Files
ptools/templates/auto_pt/downloading.html

807 lines
34 KiB
HTML

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>任务管理</title>
{# <link rel="stylesheet" href="https://unpkg.com/umy-ui/lib/theme-chalk/index.css">#}
<!-- 引入样式 -->
{% include 'admin/includes/css-part.html' %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vxe-table@legacy/lib/style.css">
<style>
/* 用来设置当前页面element全局table 选中某行时的背景色*/
.vxe-table--body tr.row--current > td {
background-color: #da8342 !important;
color: #fff;
}
/* 用来设置当前页面element全局table 鼠标滑过某行时的背景色*/
.vxe-table--body .vxe-body--row.row--hover td {
background-color: #df9942 !important;
}
.my-menus {
background-color: #F8F8F9;
}
.my-menus .vxe-ctxmenu--link {
width: 200px;
}
.my-copy-item {
font-weight: 700;
font-style: oblique;
}
.color-red {
color: red;
}
.color-blue {
color: blue;
}
.color-orange {
color: orange;
}
.bottom_xf {
background-color: #1D69A9;
width: 100%;
height: 28px;
margin: auto;
padding: 5px;
overflow: auto;
position: fixed;
bottom: 0;
z-index: 999999;
text-align: center;
}
</style>
</head>
<body>
<div id="app">
<div style="text-align: center">
{# <el-button-group style="margin: auto;">#}
{# <el-button size="small"#}
{# type="warning" plain#}
{# v-for="downloader in downloaders"#}
{# icon="el-icon-s-promotion"#}
{# @click="handleButtonClick(downloader.id)">#}
{# <span v-text="downloader.name"></span>#}
{# </el-button>#}
{##}
{# </el-button-group>#}
<el-tabs type="card" @tab-click="handleButtonClick">
<el-tab-pane v-for="downloader in downloaders"
:label="downloader.name"
icon="el-icon-s-promotion"
:name="downloader.id">
{# <span v-text="downloader.name"></span>#}
</el-tab-pane>
{# <el-tab-pane label="配置管理" name="second">配置管理</el-tab-pane>#}
{# <el-tab-pane label="角色管理" name="third">角色管理</el-tab-pane>#}
{# <el-tab-pane label="定时任务补偿" name="fourth">定时任务补偿</el-tab-pane>#}
</el-tabs>
</div>
<div>
<el-dialog title="删除任务" :visible.sync="deleteForm" min-width="180" width="30%" center>
<div style="text-align: center">
<el-switch v-model="delete_files" active-text="删除文件" active-color="#ff4949"
inactive-text="保留文件">
</el-switch>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="deleteForm = false">取 消</el-button>
<el-button type="danger" @click="handleDelete">确 定</el-button>
</span>
</el-dialog>
<el-dialog title="修改分类" :visible.sync="categoryForm" min-width="180" width="30%" center>
<div style="text-align: center">
<el-select v-model="category" filterable placeholder="请选择分类">
<el-option v-for="item in categories" :key="item.value" :title="item.title" :value="item.value">
</el-option>
</el-select>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="categoryForm = false">取 消</el-button>
<el-button type="danger" @click="setCategory">确 定</el-button>
</span>
</el-dialog>
</div>
<vxe-toolbar ref="xToolbar" size="mini" custom>
<template #buttons>
<el-button circle size="mini" type="primary" icon="el-icon-link"></el-button>
<el-button circle size="mini" type="primary" icon="el-icon-plus"></el-button>
<el-button circle size="mini" type="danger" icon="el-icon-delete"></el-button>
<el-button circle size="mini" type="success" icon="el-icon-video-play"></el-button>
<el-button circle size="mini" type="warning" icon="el-icon-video-pause"></el-button>
<el-dropdown split-button type="primary" size="mini" @command="handleSelected">
操作
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="resume">继续</el-dropdown-item>
<el-dropdown-item command="set_force_start">强制继续</el-dropdown-item>
<el-dropdown-item command="pause">暂停</el-dropdown-item>
<el-dropdown-item command="set_auto_management">自动管理</el-dropdown-item>
<el-dropdown-item command="set_super_seeding">超级做种</el-dropdown-item>
<el-dropdown-item command="recheck">重新校验</el-dropdown-item>
<el-dropdown-item command="reannounce">重新汇报</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<el-dropdown split-button type="success" size="mini">
操作
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>
<el-button size="mini" type="danger" @click="deleteForm = true">删除任务</el-button>
</el-dropdown-item>
<el-dropdown-item @click="setCategory">
<el-button size="mini" type="primary" @click="categoryForm = true">修改分类</el-button>
</el-dropdown-item>
{# <el-dropdown-item @click="limitSpeed">限速</el-dropdown-item>#}
{# <el-dropdown-item @click="setLocation">更改路径</el-dropdown-item>#}
</el-dropdown-menu>
</el-dropdown>
</template>
<template #tools>
<el-button size="mini" type="success" v-if="refresh" icon="el-icon-refresh" @click="startRefresh">继续刷新
</el-button>
<el-button size="mini" type="danger" v-else="refresh" icon="el-icon-pause" @click="stopRefresh">停止刷新
</el-button>
<el-button size="mini" type="warning" icon="vxe-icon-funnel-clear" @click="clearFilters">清除筛选
</el-button>
<el-button size="mini" type="warning" icon="vxe-icon-square-checked-fill" @click="clearSelection">清除选中
</el-button>
</template>
</vxe-toolbar>
<vxe-table ref="dataTable" row-height="55" id="table"
showBodyOverflow="title" showHeaderOverflow="title"
{# @row-dblclick="handleRow"#}
@selection-change="tableSelected"
:menu-config="tableMenu"
@menu-click="contextMenuClickEvent"
@cell-dblclick="showDetails"
{# @cell-click="detailClick"#}
:data="mainData.torrents"
:loading="loading"
height="700" size="mini"
auto-resize
round border stripe
:tooltip-config="{showAll: true}"
:keyboard-config="{isArrow: true, isTab: true, enterToTab: true}"
:column-config="{isCurrent: true, isHover: true, resizable: true}"
:row-config="{isCurrent: true, isHover: true, keyField: 'hash'}"
:checkbox-config="{trigger: 'row', highlight: true, range: true, reserve: true}"
highlight-current-row
style="width: 100%">
<vxe-table-column fixed="left" type="checkbox">
</vxe-table-column>
<vxe-table-column type="seq" fixed="left"></vxe-table-column>
<vxe-table-column field="name" fixed="left" sortable show-header-overflow show-overflow="tooltip"
show-footer-overflow title="种子名称" max-width="280" min-width="150">
</vxe-table-column>
<vxe-table-column field="size" sortable width="85" show-header-overflow show-overflow="tooltip"
show-footer-overflow :formatter="handleSize" title="大小">
</vxe-table-column>
<vxe-table-column field="category" sortable width="85"
:filters="[]"
:filter-method="filterCategoryMethod"
:filter-multiple="false"
title="分类"></vxe-table-column>
<vxe-table-column field="tracker" sortable
:filters="[]"
:formatter="handleTracker"
:filter-method="filterTrackerMethod"
:filter-multiple="false"
:filter-change="handlerTrackerFilter"
min-width="110"
title="Tracker"></vxe-table-column>
<vxe-table-column field="state" sortable width="85" :formatter="handleState" column-key="state"
:filter-method="filterStateMethod"
:filters="stateFilters" :filter-multiple="false" title="状态"></vxe-table-column>
<vxe-table-column field="last_activity" sortable width="105"
show-header-overflow show-overflow="tooltip"
show-footer-overflow title="最后活动"></vxe-table-column>
<vxe-table-column field="downloaded" :formatter="handleSize" min-width="90" sortable
title="已下载"></vxe-table-column>
<vxe-table-column field="uploaded" :formatter="handleSize" sortable width="85"
title="上传"></vxe-table-column>
<vxe-table-column field="dlspeed" sortable width="95" :formatter="handleSpeed"
title="下载速度"></vxe-table-column>
<vxe-table-column field="upspeed" sortable width="95" :formatter="handleSpeed"
title="上传速度"></vxe-table-column>
<vxe-table-column field="progress" sortable width="90" title="进度">
<template slot-scope="scope">
<el-progress
v-if="scope.row.progress==1"
status="success"
width="20"
:percentage="scope.row.progress * 100">
</el-progress>
<el-progress
v-else="scope.row.progress==1"
{# :text-inside="true"#}
{# type="circle"#}
width="20"
:color="customColors"
{# :stroke-width="18"#}
:percentage="scope.row.progress * 100">
</el-progress>
</template>
</vxe-table-column>
<vxe-table-column field="ratio" sortable width="85" {# :formatter="handlePercent" #}
title="分享率"></vxe-table-column>
<vxe-table-column field="seeding_time" width="100"
show-header-overflow show-overflow="tooltip"
show-footer-overflow
title="做种时间"></vxe-table-column>
<vxe-table-column field="added_on" width="100"
show-header-overflow show-overflow="tooltip"
show-footer-overflow
title="添加时间"></vxe-table-column>
{# <template #empty>#}
{# <span style="color: red;">#}
{# <img src="https://pic2.zhimg.com/50/v2-f7031359103859e1ed38559715ef5f3f_hd.gif">#}
{# <p>搜索不到数据,可能输入的关键字姿势不对!</p>#}
{# </span>#}
{# </template>#}
</vxe-table>
<div class="bottom_xf">
<el-tag size="medium" type="danger"
v-text="'总分享率:' + server_state.global_ratio"></el-tag>
<el-tag size="medium" type="success"
v-text="'剩余空间:' + renderSize(server_state.free_space_on_disk)"></el-tag>
<el-tag size="medium" type=""
v-text="'历史下载:' + renderSize(server_state.alltime_dl)"></el-tag>
<el-tag size="medium" type="warning"
v-text="'历史上传:' + renderSize(server_state.alltime_ul)"></el-tag>
<el-tag size="medium" effect="plain" color="#233223"
v-text="'本次下载:' + renderSize(server_state.dl_info_data)"></el-tag>
<el-tag size="medium" effect="plain" color="#296969"
v-text="'下载速度:' + renderSize(server_state.dl_info_speed)"></el-tag>
<el-tag size="medium" effect="plain" color="#545454"
v-text="'本次上传:' + renderSize(server_state.up_info_data)"></el-tag>
<el-tag size="medium" color="#696969"
v-text="'上传速度:' + renderSize(server_state.up_info_speed) + '/S'"></el-tag>
</div>
<div>
<el-drawer
style="margin-bottom: 35px !important;position: fixed;z-index:99999;"
:title="torrent.name"
:visible.sync="details"
direction="rtl"
:modal="false"
:wrapperClosable="false"
:destroy-on-close="true"
:close-on-press-escape="true"
:close="closeDrawer"
:with-header="false"
show-header-overflow show-overflow="tooltip"
size="40%">
<el-descriptions :title="torrent.name" border size="mini" colon :column="1">
<el-descriptions-item v-for="(value,key,index) in torrent"
:label="key">
<span v-text="value"></span>
</el-descriptions-item>
</el-descriptions>
</el-drawer>
</div>
</div>
{#<script src="https://unpkg.com/umy-ui@1.1.6/lib/index.js"></script>#}
<!-- 引入脚本 -->
{% include 'admin/includes/js-part.html' %}
<script src="https://cdn.jsdelivr.net/npm/xe-utils"></script>
<script src="https://cdn.jsdelivr.net/npm/vxe-table@legacy"></script>
<script src="{% static 'admin/simpleui-x/js/axios.min.js' %}"></script>
<script>
const download_state = {
'uploading': '正在上传',
'downloading': '正在下载',
'checkingUP': '正在校验',
'forcedDL': '强制下载',
'stalledUP': '正在做种',
'forcedUP': '强制做种',
'missingFiles': '文件丢失',
'moving': '正在移动',
'pausedDL': '暂停下载',
'pausedUP': '完成上传',
'queuedDL': '下载队列中',
'queuedUP': '上传队列中',
'error': '错误',
'unknown': '未知',
'allocating': '分配',
'stalledDL': '等待下载',
'metaDL': '下载元数据',
'checkingDL': '下载校验中',
'checkingResumeData': '校验恢复数据',
'forcedMetaDL': '强制下载元数据',
}
const vm = new Vue({
// 配置选项(option)
// element: 指定用vue来管理页面中的哪个标签区域
el: '#app',
data: {
downloaders: [],
loading: false,
mainData: {},
tracker_list: [],
dialogFormVisible: false,
timer: {},
deleteForm: false,
categoryForm: false,
refresh: false,
downloader_id: 0,
details: false,
torrent: {
{#name: '',#}
{#category: '',#}
{#size: '',#}
{#downloaded: '',#}
{#uploaded: '',#}
{#dlspeed: '',#}
{#upspeed: '',#}
{#progress: '',#}
{#ratio: '',#}
{#seeding_time: '',#}
{#state: '',#}
{#last_activity: '',#}
{#added_on: '',#}
{#tracker: '',#}
},
customColors: [
{color: '#f56c6c', percentage: 20},
{color: '#e6a23c', percentage: 40},
{color: '#5cb87a', percentage: 60},
{color: '#1989fa', percentage: 80},
{color: '#6f7ad3', percentage: 100}
],
selected_rows: [],
categoryFilters: [
{
label: '未分类',
value: ''
}
],
categories: [],
category: '',
server_state: {},
delete_files: false,
tableMenu: {
className: 'my-menus',
header: {
options: [
[]
]
},
body: {
options: [
[
{
code: 'resume',
name: '继续'
},
{
code: 'set_force_start',
name: '强制继续'
},
{
code: 'set_force_start',
name: '',
className: 'my-copy-item'
}
],
[
{
code: 'delete',
name: '删除',
prefixIcon: 'vxe-icon-delete color-red'
},
],
[
{
name: '分类',
children: [
{code: 'handleSelected', name: '添加'},
{code: 'handleSelected', name: '重置'}
]
},
{
name: '标签',
children: [
{code: '', name: '添加'},
{code: '', name: '删除全部'}
]
},
{
code: 'set_auto_management',
name: '自动管理',
},
],
[
{
code: 'set_super_seeding',
name: '超级做种',
}
],
[
{code: 'recheck', name: '重新校验'},
{code: 'reannounce', name: '重新汇报'},
{
code: 'copy',
name: '复制',
prefixIcon: 'vxe-icon-copy',
children: [
{code: 'copy', name: '名称'},
{code: 'copy', name: 'HASH', prefixIcon: 'vxe-icon-sort-alpha-asc'},
{code: 'copy', name: '链接', prefixIcon: 'vxe-icon-sort-alpha-desc'}
]
}
]
]
},
footer: {
options: [
[],
[],
[]
]
}
}
},
beforeMount() {
window.vm = this
// 任务状态过滤器数据
let data = []
for (let x in download_state) {
data.push({
'label': download_state[x],
'value': x
})
}
this.stateFilters = data
},
mounted() {
this.loading = true
{#console.log("获取下载器数量:", this.downloaders.length)#}
{#this.get_downloading(this.downloaders[0].id)#}
},
updated() {
// 十分钟后停止刷新
setTimeout(() => {
clearInterval(this.timer)
this.timer = null
}, 1000 * 60 * 10)
},
beforeUpdated() {
},
// 清除定时器,不然页面会卡死
beforeDestroy() {
this.$once('hook:beforeDestroy',
() => {
clearInterval(this.timer)
this.timer = null
}
)
},
created() {
this.$nextTick(() => {
// 将表格和工具栏进行关联
const $table = this.$refs.dataTable
{#console.log(this.$refs)#}
{#console.log($table)#}
$table.connect(this.$refs.xToolbar)
})
this.get_downloaders()
},
watch: {},
methods: {
showDetails({row, rowIndex, $rowIndex, column, columnIndex, $columnIndex, $event}) {
console.log(row)
this.torrent = row
this.details = true
},
closeDrawer() {
this.details = false
this.torrent = {}
},
clearFilters() {
//清除筛选
this.$refs.dataTable.clearFilter()
},
clearSelection() {
//清除选中
this.$refs.dataTable.clearCheckboxRow()
},
stopRefresh() {
this.refresh = true
clearInterval(this.timer)
this.timer = null
},
startRefresh() {
this.refresh = false
this.timer = setInterval(() => {
this.get_downloading(this.downloader_id)
}, 1500)
},
handleButtonClick(id) {
console.log(id.name)
{#handleButtonClick({value, $event}) {#}
this.loading = true
this.downloader_id = id.name
clearInterval(this.timer)
this.timer = null
this.mainData = []
this.get_downloader_categories(this.downloader_id)
this.timer = setInterval(() => {
this.get_downloading(this.downloader_id)
}, 1500)
this.refresh = false
},
get_downloaders() {
axios.get(
"{% url "get_downloaders" %}"
).then(res => {
this.loading = true
if (res.data.code === 0) {
console.log('获取下载器列表成功', res.data.data)
this.downloaders = res.data.data
this.downloader_id = this.downloaders[0].id
this.get_downloader_categories(this.downloader_id)
this.timer = setInterval(() => {
this.get_downloading(this.downloader_id)
}, 1500)
} else {
this.$message({
type: 'warning', message: '获取下载器列表失败!'
});
}
}).catch(res => {
console.log('获取下载器列表失败', res)
this.$message({type: 'warning', message: '获取下载器列表失败!' + res});
})
},
get_downloader_categories(downloader_id) {
axios.get(
"{% url "get_downloader_categories" %}",
{
params: {
id: downloader_id
}
}
).then(res => {
this.loading = true
if (res.data.code === 0) {
console.log('获取下载器分类列表成功', res.data.data)
this.tracker_list = res.data.data.tracker_list;
const trackerFilter = [{label: '无', value: ''}]
this.tracker_list.forEach((value, index, array) => {
{#console.log(value, index)#}
trackerFilter.push({'label': value.name, 'value': value.tracker})
})
let categoryFilters = [{label: '未分类', value: ''}]
let categories = res.data.data.categories
categories.forEach((value, index, array) => {
categoryFilters.push({'label': value, 'value': value})
this.categories.push({'title': value, 'value': value})
})
// 异步加载筛选数据
const $table = this.$refs.dataTable
{#console.log($table)#}
if ($table) {
const categoryColumn = $table.getColumnByField('category')
{#console.log(nameColumn)#}
if (categoryColumn) {
$table.setFilter(categoryColumn, categoryFilters)
}
const trackerColumn = $table.getColumnByField('tracker')
{#console.log(trackerColumn)#}
if (trackerColumn) {
$table.setFilter(trackerColumn, trackerFilter)
}
}
} else {
this.$message({
type: 'warning', message: '获取下载器列表失败!'
});
}
}).catch(res => {
console.log('获取下载器列表失败', res)
this.$message({type: 'warning', message: '获取下载器列表失败!' + res});
})
},
get_downloading(downloader_id) {
axios.get(
"{% url "downloading" %}",
{
params: {
id: downloader_id
}
}
).then(res => {
if (res.data.code === 0) {
this.mainData = res.data.data
{#console.log(res.data.data.tracker_filters)#}
this.server_state = res.data.data.server_state
this.loading = false
// 获取种子
//this.$message({
// type: 'success',
// message: '任务加载成功!!'
// })
} else {
{#console.log(res.data.data)#}
this.$message({
type: 'warning', message: '任务加载出错!!'
})
}
})
},
handlerTrackerFilter({column, property, values, datas, filterList, $event}) {
console.log(column, property, values, datas, filterList, $event)
},
handleSize({row, column, cellValue, index}) {
return this.renderSize(cellValue)
},
// 执行格式化文件大小
handleSpeed({row, column, cellValue, index}) {
return this.renderSize(cellValue) !== 0 ? this.renderSize(cellValue) + '/S' : ''
},
// 格式化文件大小
renderSize(value) {
if (null == value || value == '') {
return 0;
}
var unitArr = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
var index = 0;
var srcsize = parseFloat(value);
index = Math.floor(Math.log(srcsize) / Math.log(1024));
var size = srcsize / Math.pow(1024,
index);
size = size.toFixed(2);//保留的小数位数
return size + ' ' + unitArr[index];
},
// 格式化进度
handlePercent({row, column, cellValue, index}) {
return (cellValue * 100).toFixed(2) + '%'
},
// 格式化分享率
handleRatio({row, column, cellValue, index}) {
return cellValue.toFixed(4)
},
handleTracker({row, column, cellValue, index}) {
// 格式化输出tracker所属的站点
let site = this.tracker_list.find(site => cellValue.includes(site.tracker))
{#console.log(row)#}
{#console.log(cellValue.includes('joy'))#}
{#return cellValue#}
return null != site ? site.name : cellValue
},
handleState({row, column, cellValue, index}) {
return download_state[cellValue]
},
filterStateMethod({value, row, column}) {
const property = column['property'];
return row[property] === value;
},
filterCategoryMethod({value, row, column}) {
const property = column['property'];
return row[property] === value;
},
filterTrackerMethod({value, row, column}) {
const property = column['property'];
return row[property].includes(value);
},
handleSelected(command, category = '', delete_files = false, enable = 'True') {
let ids = []
if (this.selected_rows.length <= 0) {
this.$message({type: 'warning', message: '未选中任何种子!!'})
return
}
this.selected_rows.forEach((item, index) => {
{#console.log(item['hash'], index)#}
ids.push(item['hash'])
})
let data = new FormData()
data.append('ids', ids)
data.append('command', command)
data.append('enable', enable)
data.append('category', category)
data.append('downloader_id', this.downloader_id)
axios.post(
// "{% url "control_torrent" %}",
data
).then(res => {
console.log(res.data)
this.get_downloading(this.downloader_id)
this.$message({type: 'success', message: '指令发送成功!!'})
this.selected_rows = []
this.$refs.dataTable.clearSelection()
}).catch(() => {
this.$message({type: 'warning', message: '指令发送失败!!'})
})
},
tableSelected(rows) {
this.selected_rows = rows
console.log(this.selected_rows.length)
},
rowKey(row) {
console.log(row.hash)
return row.hash
},
handleDelete() {
console.log(this.delete_files)
// { #this.deleteForm = true# }
this.handleSelected('delete', '', this.delete_files)
this.deleteForm = false
this.delete_files = false
},
setCategory() {
this.handleSelected('set_category', this.category)
this.categoryForm = false
this.category = ''
},
limitSpeed() {
},
setLocation() {
},
setCategoryFilter(fieldOrColumn, options) {
console.log(fieldOrColumn, options)
},
contextMenuClickEvent({menu, row, column}) {
console.log(row)
console.log(column)
switch (menu.code) {
case 'copy':
// 示例
if (row && column) {
if (XEClipboard.copy(row[column.property])) {
VXETable.modal.message({content: '已复制到剪贴板!', status: 'success'})
}
}
break
default:
VXETable.modal.message(`点击了 ${menu.name} 选项`)
}
},
{#searchEvent2() {#}
{# const filterName = XEUtils.toValueString(this.filterName2).trim().toLowerCase()#}
{# if (filterName) {#}
{# const filterRE = new RegExp(filterName, 'gi')#}
{# const options = {children: 'children'}#}
{# const searchProps = ['name', 'size', 'type', 'date']#}
{# const rest = XEUtils.searchTree(this.tableData2, item => searchProps.some(key => XEUtils.toValueString(item[key]).toLowerCase().indexOf(filterName) > -1), options)#}
{# XEUtils.eachTree(rest, item => {#}
{# searchProps.forEach(key => {#}
{# item[key] = XEUtils.toValueString(item[key]).replace(filterRE, match => `<span class="keyword-lighten">${match}</span>`)#}
{# })#}
{# }, options)#}
{# this.list2 = rest#}
{# // 搜索之后默认展开所有子节点#}
{# this.$nextTick(() => {#}
{# this.$refs.xTree.setAllTreeExpand(true)#}
{# })#}
{# } else {#}
{# this.list2 = this.tableData2#}
{# }#}
//,
}
});
</script>
</body>
</html>