mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-13 20:12:48 +00:00
perf: 优化下载工具
This commit is contained in:
parent
5bbffc4f5c
commit
a8a54593e2
91
electron/electron-down.js
vendored
91
electron/electron-down.js
vendored
@ -20,87 +20,88 @@ function initialize(options = {}) {
|
|||||||
...options,
|
...options,
|
||||||
|
|
||||||
onStarted: (item) => {
|
onStarted: (item) => {
|
||||||
downloadManager.addDownloadItem(item);
|
downloadManager.add(item);
|
||||||
syncDownloadItems();
|
|
||||||
},
|
|
||||||
onProgress: (item) => {
|
|
||||||
downloadManager.updateDownloadItem(item.path);
|
|
||||||
syncDownloadItems();
|
syncDownloadItems();
|
||||||
},
|
},
|
||||||
onCancel: (item) => {
|
onCancel: (item) => {
|
||||||
downloadManager.updateDownloadItem(item.getSavePath())
|
downloadManager.refresh(item.getSavePath())
|
||||||
|
syncDownloadItems();
|
||||||
|
},
|
||||||
|
onInterrupted: (item) => {
|
||||||
|
downloadManager.refresh(item.getSavePath());
|
||||||
|
syncDownloadItems();
|
||||||
|
},
|
||||||
|
onProgress: (item) => {
|
||||||
|
downloadManager.refresh(item.path);
|
||||||
syncDownloadItems();
|
syncDownloadItems();
|
||||||
},
|
},
|
||||||
onCompleted: (item) => {
|
onCompleted: (item) => {
|
||||||
downloadManager.updateDownloadItem(item.path);
|
downloadManager.refresh(item.path);
|
||||||
syncDownloadItems();
|
syncDownloadItems();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// IPC - 获取下载任务
|
// IPC
|
||||||
ipcMain.handle('getDownloadTasks', () => {
|
ipcMain.handle('downloadManager', async (event, {action, path}) => {
|
||||||
|
switch (action) {
|
||||||
|
case "get": {
|
||||||
return {
|
return {
|
||||||
items: downloadManager.getDownloadItems()
|
items: downloadManager.get()
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
|
||||||
// IPC - 暂停下载任务
|
case "pause": {
|
||||||
ipcMain.handle('pauseDownloadTask', async (event, {path}) => {
|
downloadManager.pause(path);
|
||||||
downloadManager.pauseDownloadItem(path);
|
|
||||||
syncDownloadItems();
|
syncDownloadItems();
|
||||||
return true;
|
return true;
|
||||||
});
|
}
|
||||||
|
|
||||||
// IPC - 恢复下载任务
|
case "resume": {
|
||||||
ipcMain.handle('resumeDownloadTask', async (event, {path}) => {
|
downloadManager.resume(path);
|
||||||
downloadManager.resumeDownloadItem(path);
|
|
||||||
syncDownloadItems();
|
syncDownloadItems();
|
||||||
return true;
|
return true;
|
||||||
});
|
}
|
||||||
|
|
||||||
// IPC - 取消下载任务
|
case "cancel": {
|
||||||
ipcMain.handle('cancelDownloadTask', async (event, {path}) => {
|
downloadManager.cancel(path);
|
||||||
downloadManager.cancelDownloadItem(path);
|
|
||||||
syncDownloadItems();
|
syncDownloadItems();
|
||||||
return true;
|
return true;
|
||||||
});
|
}
|
||||||
|
|
||||||
// IPC - 从下载历史中移除下载项
|
case "remove": {
|
||||||
ipcMain.handle('removeFromDownloadHistory', async (event, {path}) => {
|
downloadManager.remove(path);
|
||||||
downloadManager.removeFromDownloadHistory(path);
|
|
||||||
syncDownloadItems();
|
syncDownloadItems();
|
||||||
return true;
|
return true;
|
||||||
});
|
}
|
||||||
|
|
||||||
// IPC - 清理下载历史
|
case "removeAll": {
|
||||||
ipcMain.handle('clearDownloadHistory', async () => {
|
downloadManager.removeAll();
|
||||||
downloadManager.clearHistory();
|
|
||||||
syncDownloadItems();
|
syncDownloadItems();
|
||||||
return true;
|
return true;
|
||||||
});
|
}
|
||||||
|
|
||||||
// IPC - 打开下载文件
|
case "openFile": {
|
||||||
ipcMain.handle('openDownloadedFile', async (event, {path}) => {
|
if (!fs.existsSync(path)) {
|
||||||
if (fs.existsSync(path)) {
|
throw new Error('file not found');
|
||||||
|
}
|
||||||
return shell.openPath(path);
|
return shell.openPath(path);
|
||||||
}
|
}
|
||||||
throw new Error('file not found');
|
|
||||||
});
|
|
||||||
|
|
||||||
// IPC - 显示下载文件
|
case "showFolder": {
|
||||||
ipcMain.handle('showDownloadedFileInFolder', async (event, {path}) => {
|
if (!fs.existsSync(path)) {
|
||||||
if (fs.existsSync(path)) {
|
throw new Error('file not found');
|
||||||
|
}
|
||||||
shell.showItemInFolder(path);
|
shell.showItemInFolder(path);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
throw new Error('file not found');
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function syncDownloadItems() {
|
function syncDownloadItems() {
|
||||||
// 同步下载项到渲染进程
|
// 同步下载项到渲染进程
|
||||||
if (downloadWindow) {
|
if (downloadWindow) {
|
||||||
downloadWindow.webContents.send('download-items', downloadManager.getDownloadItems());
|
downloadWindow.webContents.send('download-items', downloadManager.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +124,7 @@ async function open(language = 'zh', theme = 'light') {
|
|||||||
// 如果窗口已存在,直接显示
|
// 如果窗口已存在,直接显示
|
||||||
if (downloadWindow) {
|
if (downloadWindow) {
|
||||||
// 更新窗口数据
|
// 更新窗口数据
|
||||||
await updateDownloadWindow(language, theme)
|
await updateWindow(language, theme)
|
||||||
// 显示窗口并聚焦
|
// 显示窗口并聚焦
|
||||||
downloadWindow.show();
|
downloadWindow.show();
|
||||||
downloadWindow.focus();
|
downloadWindow.focus();
|
||||||
@ -215,7 +216,7 @@ async function open(language = 'zh', theme = 'light') {
|
|||||||
|
|
||||||
// 将语言包发送到渲染进程
|
// 将语言包发送到渲染进程
|
||||||
downloadWindow.webContents.once('dom-ready', () => {
|
downloadWindow.webContents.once('dom-ready', () => {
|
||||||
updateDownloadWindow(language, theme)
|
updateWindow(language, theme)
|
||||||
});
|
});
|
||||||
|
|
||||||
// 显示窗口
|
// 显示窗口
|
||||||
@ -236,7 +237,7 @@ function destroy() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateDownloadWindow(language, theme) {
|
async function updateWindow(language, theme) {
|
||||||
if (downloadWindow) {
|
if (downloadWindow) {
|
||||||
try {
|
try {
|
||||||
const finalLanguage = getLanguagePack(language);
|
const finalLanguage = getLanguagePack(language);
|
||||||
@ -256,5 +257,5 @@ module.exports = {
|
|||||||
open,
|
open,
|
||||||
close,
|
close,
|
||||||
destroy,
|
destroy,
|
||||||
updateDownloadWindow
|
updateWindow
|
||||||
}
|
}
|
||||||
|
|||||||
8
electron/electron.js
vendored
8
electron/electron.js
vendored
@ -2802,11 +2802,11 @@ ipcMain.on("rendererReq", async (event, args) => {
|
|||||||
case 'openDownloadWindow':
|
case 'openDownloadWindow':
|
||||||
ret = await electronDown.open(args.language || 'zh', args.theme || 'light');
|
ret = await electronDown.open(args.language || 'zh', args.theme || 'light');
|
||||||
break;
|
break;
|
||||||
case 'createDownloadTask':
|
|
||||||
ret = await electronDown.download(mainWindow, args.url, args.options || {});
|
|
||||||
break;
|
|
||||||
case 'updateDownloadWindow':
|
case 'updateDownloadWindow':
|
||||||
ret = await electronDown.updateDownloadWindow(args.language, args.theme);
|
ret = await electronDown.updateWindow(args.language, args.theme);
|
||||||
|
break;
|
||||||
|
case 'createDownload':
|
||||||
|
ret = await electronDown.download(mainWindow, args.url, args.options || {});
|
||||||
break;
|
break;
|
||||||
case 'watchFile':
|
case 'watchFile':
|
||||||
ret = await watchFile(args.path);
|
ret = await watchFile(args.path);
|
||||||
|
|||||||
@ -26,7 +26,7 @@
|
|||||||
"url": "https://github.com/kuaifan/dootask.git"
|
"url": "https://github.com/kuaifan/dootask.git"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@dootask/electron-dl": "^4.0.0-rc.1",
|
"@dootask/electron-dl": "^4.0.0-rc.2",
|
||||||
"@electron-forge/cli": "^7.8.3",
|
"@electron-forge/cli": "^7.8.3",
|
||||||
"@electron-forge/maker-deb": "^7.8.3",
|
"@electron-forge/maker-deb": "^7.8.3",
|
||||||
"@electron-forge/maker-rpm": "^7.8.3",
|
"@electron-forge/maker-rpm": "^7.8.3",
|
||||||
|
|||||||
@ -1 +1,502 @@
|
|||||||
123
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Download</title>
|
||||||
|
<link rel="stylesheet" href="./style.css">
|
||||||
|
<script>
|
||||||
|
const getQueryParam = (name) => {
|
||||||
|
const url = window.location.href;
|
||||||
|
const match = url.match(new RegExp('[?&]' + name + '=([^&#]*)'));
|
||||||
|
return match ? decodeURIComponent(match[1]) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateTheme = (theme) => {
|
||||||
|
const root = document.documentElement;
|
||||||
|
root.classList.toggle('dark', theme === 'dark');
|
||||||
|
root.classList.toggle('light', theme === 'light');
|
||||||
|
};
|
||||||
|
|
||||||
|
updateTheme(getQueryParam('theme'))
|
||||||
|
</script>
|
||||||
|
<script src="../tabs/assets/js/vue.global.min.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app" class="download-manager">
|
||||||
|
<div class="toolbar">
|
||||||
|
<label class="search">
|
||||||
|
<input class="search-input" v-model.trim="query" :placeholder="lang.searchPlaceholder"></input>
|
||||||
|
</label>
|
||||||
|
<div class="actions">
|
||||||
|
<button class="action-btn" @click="onRemoveAll" :disabled="items.length === 0">{{ lang.removeAll }}</button>
|
||||||
|
<button class="action-btn" @click="onRefresh">{{ lang.refresh }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<div class="tab-content all-tasks">
|
||||||
|
<div v-if="list.length === 0" class="empty-state">
|
||||||
|
<div class="empty-icon">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="48" height="48" fill="currentColor">
|
||||||
|
<path d="M5 20h14v-2H5v2zM19 9h-4V3H9v6H5l7 7 7-7z"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="empty-text">{{ query ? lang.noSearchResult : lang.noItems }}</div>
|
||||||
|
</div>
|
||||||
|
<div v-else class="task-list">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in list"
|
||||||
|
:key="index"
|
||||||
|
class="task-item"
|
||||||
|
:class="{'progressing-item': item.state === 'progressing'}"
|
||||||
|
:style="item.state === 'progressing' ? {'--progress': item.percent + '%'} : {'--progress': '0%'}">
|
||||||
|
<div class="task-icon">
|
||||||
|
<div class="file-icon" :class="getFileTypeClass(item)" v-html="getFileIcon(item)"></div>
|
||||||
|
</div>
|
||||||
|
<div class="task-info">
|
||||||
|
<div class="task-name">
|
||||||
|
<div class="task-name-clickable" :title="item.filename" @click="onOpenFile(item)">{{ item.filename }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="task-meta">
|
||||||
|
<!-- 大小 -->
|
||||||
|
<span v-if="item.state === 'progressing'" class="file-size">
|
||||||
|
{{ formatBytes(item.received) }}<template v-if="item.total > 0"> / {{ formatBytes(item.total) }}</template> ({{ item.percent }}%)
|
||||||
|
</span>
|
||||||
|
<span v-else class="file-size">
|
||||||
|
{{ formatBytes(item.total) }}
|
||||||
|
</span>
|
||||||
|
<!-- 时间 -->
|
||||||
|
<span v-if="item.state === 'completed'" class="download-time">{{ formatTime(item.endTime) }}</span>
|
||||||
|
<span v-else class="download-time">{{ formatTime(item.startTime) }}</span>
|
||||||
|
<!-- 状态 -->
|
||||||
|
<span v-if="item.state !== 'progressing' || item.paused" class="state" :class="getStateClass(item)">
|
||||||
|
{{ getStateText(item) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="task-actions">
|
||||||
|
<!-- 下载速度 -->
|
||||||
|
<span v-if="item.state === 'progressing' && item.speed" class="speed">{{ formatBytes(item.speed) }}/s</span>
|
||||||
|
<!-- 复制链接 -->
|
||||||
|
<button @click="copyUrl(item)" class="icon-btn" :title="lang.copyLink">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" focusable="false" viewBox="0 0 24 24" width="16" height="16" fill="currentColor">
|
||||||
|
<path d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<!-- 暂停和继续 -->
|
||||||
|
<template v-if="item.state === 'progressing'">
|
||||||
|
<button v-if="item.paused" @click="onResume(item)" class="icon-btn" :title="lang.resume">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M8 5v14l11-7z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button v-else @click="onPause(item)" class="icon-btn" :title="lang.pause">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
<!-- 显示文件夹 -->
|
||||||
|
<button v-if="item.state === 'completed'" @click="onShowFolder(item)" class="icon-btn" :title="lang.showInFolder">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 12H4V8h16v10z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<!-- 删除 -->
|
||||||
|
<button @click="onRemove(item)" class="icon-btn danger" :title="lang.remove">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 信息提示框 -->
|
||||||
|
<div v-if="toast.show" class="toast" :class="toast.type">
|
||||||
|
<div class="toast-content">
|
||||||
|
<svg v-if="toast.type === 'success'" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
|
||||||
|
</svg>
|
||||||
|
<svg v-else-if="toast.type === 'error'" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="toast-message">{{ toast.message }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const {createApp} = Vue;
|
||||||
|
|
||||||
|
createApp({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
query: '',
|
||||||
|
items: [],
|
||||||
|
lang: {
|
||||||
|
// 语言设置
|
||||||
|
locale: 'zh',
|
||||||
|
title: '下载管理器',
|
||||||
|
|
||||||
|
// 界面文本
|
||||||
|
searchPlaceholder: '搜索文件名或链接...',
|
||||||
|
noItems: '暂无任务',
|
||||||
|
noSearchResult: '未找到匹配的结果',
|
||||||
|
|
||||||
|
// 操作按钮
|
||||||
|
refresh: '刷新',
|
||||||
|
removeAll: "清空历史",
|
||||||
|
copyLink: '复制链接',
|
||||||
|
resume: '继续',
|
||||||
|
pause: '暂停',
|
||||||
|
cancel: '取消',
|
||||||
|
remove: '删除',
|
||||||
|
showInFolder: '显示在文件夹',
|
||||||
|
|
||||||
|
// 状态文本
|
||||||
|
progressing: '下载中',
|
||||||
|
completed: '已完成',
|
||||||
|
cancelled: '已取消',
|
||||||
|
interrupted: '失败',
|
||||||
|
paused: '已暂停',
|
||||||
|
|
||||||
|
// 成功消息
|
||||||
|
copied: "已复制",
|
||||||
|
refreshSuccess: '刷新成功',
|
||||||
|
|
||||||
|
// 确认对话框
|
||||||
|
confirmCancel: '确定要取消此下载任务并删除记录吗?',
|
||||||
|
confirmRemove: '确定要从历史记录中删除此项吗?',
|
||||||
|
confirmRemoveAll: '确定要清空下载历史吗?',
|
||||||
|
|
||||||
|
// 错误消息
|
||||||
|
copyFailed: '复制失败: ',
|
||||||
|
pauseFailed: '暂停失败: ',
|
||||||
|
resumeFailed: '继续失败: ',
|
||||||
|
removeFailed: '删除失败: ',
|
||||||
|
removeAllFailed: '清空失败: ',
|
||||||
|
openFailed: '打开文件失败: ',
|
||||||
|
showFailed: '显示文件失败: ',
|
||||||
|
},
|
||||||
|
toast: {
|
||||||
|
show: false,
|
||||||
|
type: 'success', // success, error
|
||||||
|
message: '',
|
||||||
|
timer: null
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getList();
|
||||||
|
|
||||||
|
// 监听下载任务列表
|
||||||
|
electron?.listener('download-items', (items) => {
|
||||||
|
this.items = items
|
||||||
|
});
|
||||||
|
|
||||||
|
// 接收主题
|
||||||
|
electron?.listener('download-theme', (theme) => {
|
||||||
|
updateTheme(theme)
|
||||||
|
});
|
||||||
|
|
||||||
|
// 接收语言包
|
||||||
|
electron?.listener('download-language', (lang) => {
|
||||||
|
if (lang && typeof lang === 'object') {
|
||||||
|
this.lang = {...this.lang, ...lang};
|
||||||
|
document.title = this.lang.title || document.title;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
beforeUnmount() {
|
||||||
|
if (this.toast.timer) {
|
||||||
|
clearTimeout(this.toast.timer);
|
||||||
|
this.toast.timer = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
list() {
|
||||||
|
const q = (this.query || '').toLowerCase();
|
||||||
|
return q
|
||||||
|
? this.items.filter(t => (t.filename || '').toLowerCase().includes(q) || (t.url || '').toLowerCase().includes(q))
|
||||||
|
: this.items;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
async copyUrl({url, urls}) {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(urls.length > 0 ? urls[0] : url);
|
||||||
|
this.showToast(this.lang.copied);
|
||||||
|
} catch (e) {
|
||||||
|
this.errorToast(this.lang.copyFailed + e.message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async getList() {
|
||||||
|
try {
|
||||||
|
const data = await this.sendAsync('get');
|
||||||
|
this.items = data.items || [];
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载下载任务失败:', e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async onRefresh() {
|
||||||
|
await this.getList();
|
||||||
|
this.showToast(this.lang.refreshSuccess, 'success');
|
||||||
|
},
|
||||||
|
|
||||||
|
async onPause({path}) {
|
||||||
|
try {
|
||||||
|
await this.sendAsync('pause', {path});
|
||||||
|
} catch (e) {
|
||||||
|
this.errorToast(this.lang.pauseFailed + e.message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async onResume({path}) {
|
||||||
|
try {
|
||||||
|
await this.sendAsync('resume', {path});
|
||||||
|
} catch (e) {
|
||||||
|
this.errorToast(this.lang.resumeFailed + e.message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async onRemove({state, path}) {
|
||||||
|
if (!confirm(state === 'progressing' ? this.lang.confirmCancel : this.lang.confirmRemove)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await this.sendAsync('remove', {path});
|
||||||
|
} catch (e) {
|
||||||
|
this.errorToast(this.lang.removeFailed + e.message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async onRemoveAll() {
|
||||||
|
if (!confirm(this.lang.confirmRemoveAll)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await this.sendAsync('removeAll');
|
||||||
|
} catch (e) {
|
||||||
|
this.errorToast(this.lang.removeAllFailed + e.message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async onOpenFile({path}) {
|
||||||
|
try {
|
||||||
|
await this.sendAsync('openFile', {path});
|
||||||
|
} catch (e) {
|
||||||
|
this.errorToast(this.lang.openFailed + e.message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async onShowFolder({path}) {
|
||||||
|
try {
|
||||||
|
await this.sendAsync('showFolder', {path});
|
||||||
|
} catch (e) {
|
||||||
|
this.errorToast(this.lang.showFailed + e.message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
isPaused({state, paused}) {
|
||||||
|
return state === 'progressing' && paused;
|
||||||
|
},
|
||||||
|
|
||||||
|
getStateText({state, paused}) {
|
||||||
|
if (this.isPaused({state, paused})) {
|
||||||
|
return this.lang.paused;
|
||||||
|
}
|
||||||
|
const stateMap = {
|
||||||
|
'progressing': this.lang.progressing,
|
||||||
|
'completed': this.lang.completed,
|
||||||
|
'cancelled': this.lang.cancelled,
|
||||||
|
'interrupted': this.lang.interrupted,
|
||||||
|
};
|
||||||
|
return stateMap[state] || state;
|
||||||
|
},
|
||||||
|
|
||||||
|
getStateClass({state, paused}) {
|
||||||
|
if (this.isPaused({state, paused})) {
|
||||||
|
return 'paused';
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
},
|
||||||
|
|
||||||
|
getFileTypeClass({filename}) {
|
||||||
|
const typeMap = {
|
||||||
|
'pdf': 'file-pdf',
|
||||||
|
'doc': 'file-word',
|
||||||
|
'docx': 'file-word',
|
||||||
|
'xls': 'file-excel',
|
||||||
|
'xlsx': 'file-excel',
|
||||||
|
'ppt': 'file-powerpoint',
|
||||||
|
'pptx': 'file-powerpoint',
|
||||||
|
'jpg': 'file-image',
|
||||||
|
'jpeg': 'file-image',
|
||||||
|
'png': 'file-image',
|
||||||
|
'gif': 'file-image',
|
||||||
|
'svg': 'file-image',
|
||||||
|
'webp': 'file-image',
|
||||||
|
'bmp': 'file-image',
|
||||||
|
'mp4': 'file-video',
|
||||||
|
'avi': 'file-video',
|
||||||
|
'mov': 'file-video',
|
||||||
|
'mkv': 'file-video',
|
||||||
|
'webm': 'file-video',
|
||||||
|
'wmv': 'file-video',
|
||||||
|
'mp3': 'file-audio',
|
||||||
|
'wav': 'file-audio',
|
||||||
|
'flac': 'file-audio',
|
||||||
|
'aac': 'file-audio',
|
||||||
|
'm4a': 'file-audio',
|
||||||
|
'zip': 'file-archive',
|
||||||
|
'rar': 'file-archive',
|
||||||
|
'7z': 'file-archive',
|
||||||
|
'tar': 'file-archive',
|
||||||
|
'gz': 'file-archive',
|
||||||
|
'txt': 'file-text',
|
||||||
|
'md': 'file-text',
|
||||||
|
'rtf': 'file-text',
|
||||||
|
'js': 'file-code',
|
||||||
|
'ts': 'file-code',
|
||||||
|
'css': 'file-code',
|
||||||
|
'html': 'file-code',
|
||||||
|
'php': 'file-code',
|
||||||
|
'py': 'file-code',
|
||||||
|
'java': 'file-code',
|
||||||
|
'cpp': 'file-code',
|
||||||
|
'c': 'file-code',
|
||||||
|
'exe': 'file-exe',
|
||||||
|
'msi': 'file-exe',
|
||||||
|
'dmg': 'file-exe',
|
||||||
|
'deb': 'file-exe',
|
||||||
|
'rpm': 'file-exe',
|
||||||
|
'_': 'file-unknown'
|
||||||
|
};
|
||||||
|
|
||||||
|
return typeMap[this.getFileExt(filename)] || typeMap['_'];
|
||||||
|
},
|
||||||
|
|
||||||
|
getFileIcon({filename}) {
|
||||||
|
const iconMap = {
|
||||||
|
'pdf': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#ea4335"><path d="M20 2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-8.5 7.5c0 .83-.67 1.5-1.5 1.5H9v2H7.5V7H10c.83 0 1.5.67 1.5 1.5v1zm5 2c0 .83-.67 1.5-1.5 1.5h-2.5V7H15c.83 0 1.5.67 1.5 1.5v3zm4-3H19v1h1.5V11H19v1h1.5v1.5H17.5V7h4v1.5z"/></svg>',
|
||||||
|
|
||||||
|
'doc': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#4285f4"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.89 2 2 2h12c1.11 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/></svg>',
|
||||||
|
'docx': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#4285f4"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.89 2 2 2h12c1.11 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/></svg>',
|
||||||
|
|
||||||
|
'xls': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#0f9d58"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.89 2 2 2h12c1.11 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/></svg>',
|
||||||
|
'xlsx': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#0f9d58"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.89 2 2 2h12c1.11 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/></svg>',
|
||||||
|
|
||||||
|
'ppt': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#ff6d01"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.89 2 2 2h12c1.11 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/></svg>',
|
||||||
|
'pptx': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#ff6d01"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.89 2 2 2h12c1.11 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/></svg>',
|
||||||
|
|
||||||
|
'jpg': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#9c27b0"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></svg>',
|
||||||
|
'jpeg': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#9c27b0"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></svg>',
|
||||||
|
'png': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#9c27b0"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></svg>',
|
||||||
|
'gif': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#9c27b0"><path d="M21 19V5c0-1.1-.9-2-2-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2zM8.5 13.5l2.5 3.01L14.5 12l4.5 6H5l3.5-4.5z"/></svg>',
|
||||||
|
|
||||||
|
'mp4': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#607d8b"><path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"/></svg>',
|
||||||
|
'avi': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#607d8b"><path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"/></svg>',
|
||||||
|
'mov': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#607d8b"><path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z"/></svg>',
|
||||||
|
|
||||||
|
'mp3': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#ff9800"><path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21s4.5-2.01 4.5-4.5V7h4V3h-7z"/></svg>',
|
||||||
|
'wav': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#ff9800"><path d="M12 3v9.28c-.47-.17-.97-.28-1.5-.28C8.01 12 6 14.01 6 16.5S8.01 21 10.5 21s4.5-2.01 4.5-4.5V7h4V3h-7z"/></svg>',
|
||||||
|
|
||||||
|
'zip': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#795548"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.89 2 2 2h12c1.11 0 2-.9 2-2V8l-6-6zm-1 7V3.5L18.5 9H13z"/></svg>',
|
||||||
|
'rar': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#795548"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.89 2 2 2h12c1.11 0 2-.9 2-2V8l-6-6zm-1 7V3.5L18.5 9H13z"/></svg>',
|
||||||
|
|
||||||
|
'txt': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#757575"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.89 2 2 2h12c1.11 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/></svg>',
|
||||||
|
|
||||||
|
'js': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#f7df1e"><rect width="24" height="24" fill="#323330"/><path d="M12 12v8h2c2 0 3-1 3-3s-1-3-3-3h-2zm-2 0h-2v8h2v-3c0-1 1-2 2-2s2 1 2 2v3h2v-3c0-2-1-4-4-4s-4 2-4 4z" fill="#f7df1e"/></svg>',
|
||||||
|
|
||||||
|
'exe': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#424242"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.89 2 2 2h12c1.11 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/></svg>',
|
||||||
|
|
||||||
|
'_': '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#9e9e9e"><path d="M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.89 2 2 2h12c1.11 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11z"/></svg>'
|
||||||
|
};
|
||||||
|
|
||||||
|
return iconMap[this.getFileExt(filename)] || iconMap['_'];
|
||||||
|
},
|
||||||
|
|
||||||
|
getFileExt(value) {
|
||||||
|
return `${value}`.split('.').pop()?.toLowerCase();
|
||||||
|
},
|
||||||
|
|
||||||
|
formatBytes(bytes) {
|
||||||
|
if (!bytes || bytes === 0) return '0 B';
|
||||||
|
const k = 1024;
|
||||||
|
const sizes = ['B', 'KB', 'MB', 'GB'];
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||||
|
},
|
||||||
|
|
||||||
|
formatTime(timestamp) {
|
||||||
|
if (!timestamp) return '';
|
||||||
|
const date = new Date(timestamp * 1000);
|
||||||
|
const locales = {
|
||||||
|
"zh": "zh-CN",
|
||||||
|
"zh-CHT": "zh-TW",
|
||||||
|
"en": "en-US",
|
||||||
|
"ko": "ko-KR",
|
||||||
|
"ja": "ja-JP",
|
||||||
|
"de": "de-DE",
|
||||||
|
"fr": "fr-FR",
|
||||||
|
"id": "id-ID",
|
||||||
|
"ru": "ru-RU"
|
||||||
|
};
|
||||||
|
const locale = locales[this.lang?.locale] || this.lang?.locale || locales['en'];
|
||||||
|
return date.toLocaleString(locale, {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
sendAsync(action, args = {}) {
|
||||||
|
return electron?.sendAsync("downloadManager", {
|
||||||
|
action,
|
||||||
|
...args
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
showToast(message, type = 'success') {
|
||||||
|
if (this.toast.timer) {
|
||||||
|
clearTimeout(this.toast.timer);
|
||||||
|
this.toast.timer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.toast.show) {
|
||||||
|
this.toast.show = false;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.displayToast(message, type);
|
||||||
|
}, 100);
|
||||||
|
} else {
|
||||||
|
this.displayToast(message, type);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
errorToast(message) {
|
||||||
|
this.showToast(message, 'error');
|
||||||
|
},
|
||||||
|
|
||||||
|
displayToast(message, type) {
|
||||||
|
this.toast.message = message;
|
||||||
|
this.toast.type = type;
|
||||||
|
this.toast.show = true;
|
||||||
|
|
||||||
|
this.toast.timer = setTimeout(() => {
|
||||||
|
this.toast.show = false;
|
||||||
|
this.toast.timer = null;
|
||||||
|
}, 4000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).mount('#app');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|||||||
68
electron/render/download/style.css
vendored
68
electron/render/download/style.css
vendored
@ -48,12 +48,14 @@
|
|||||||
|
|
||||||
--color-empty-state: #5f6368;
|
--color-empty-state: #5f6368;
|
||||||
--color-empty-text: #5f6368;
|
--color-empty-text: #5f6368;
|
||||||
--color-status-completed-bg: #e8f5e8;
|
--color-state-completed-bg: #e8f5e8;
|
||||||
--color-status-completed-text: #137333;
|
--color-state-completed-text: #137333;
|
||||||
--color-status-failed-bg: #fce8e6;
|
--color-state-failed-bg: #fce8e6;
|
||||||
--color-status-failed-text: #d93025;
|
--color-state-failed-text: #d93025;
|
||||||
--color-status-cancelled-bg: #e8eaed;
|
--color-state-cancelled-bg: #e8eaed;
|
||||||
--color-status-cancelled-text: #5f6368;
|
--color-state-cancelled-text: #5f6368;
|
||||||
|
--color-state-paused-bg: #fff3e0;
|
||||||
|
--color-state-paused-text: #f57c00;
|
||||||
|
|
||||||
--scrollbar-thumb: rgba(0, 0, 0, 0.2);
|
--scrollbar-thumb: rgba(0, 0, 0, 0.2);
|
||||||
--scrollbar-thumb-hover: rgba(0, 0, 0, 0.3);
|
--scrollbar-thumb-hover: rgba(0, 0, 0, 0.3);
|
||||||
@ -102,12 +104,14 @@
|
|||||||
|
|
||||||
--color-empty-state: #9aa0a6;
|
--color-empty-state: #9aa0a6;
|
||||||
--color-empty-text: #9aa0a6;
|
--color-empty-text: #9aa0a6;
|
||||||
--color-status-completed-bg: #1e3a1e;
|
--color-state-completed-bg: #1e3a1e;
|
||||||
--color-status-completed-text: #81c995;
|
--color-state-completed-text: #81c995;
|
||||||
--color-status-failed-bg: #3d1a1a;
|
--color-state-failed-bg: #3d1a1a;
|
||||||
--color-status-failed-text: #f28b82;
|
--color-state-failed-text: #f28b82;
|
||||||
--color-status-cancelled-bg: #3c4043;
|
--color-state-cancelled-bg: #3c4043;
|
||||||
--color-status-cancelled-text: #9aa0a6;
|
--color-state-cancelled-text: #9aa0a6;
|
||||||
|
--color-state-paused-bg: #522f2f;
|
||||||
|
--color-state-paused-text: #f57c00;
|
||||||
|
|
||||||
--scrollbar-thumb: rgba(255, 255, 255, 0.2);
|
--scrollbar-thumb: rgba(255, 255, 255, 0.2);
|
||||||
--scrollbar-thumb-hover: rgba(255, 255, 255, 0.3);
|
--scrollbar-thumb-hover: rgba(255, 255, 255, 0.3);
|
||||||
@ -312,7 +316,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 整块背景进度条(下载中样式) */
|
/* 整块背景进度条(下载中样式) */
|
||||||
.task-item.downloading-item::before {
|
.task-item.progressing-item::before {
|
||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
@ -360,9 +364,6 @@ body {
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: var(--color-task-name);
|
color: var(--color-task-name);
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-name-clickable {
|
.task-name-clickable {
|
||||||
@ -370,6 +371,10 @@ body {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: var(--color-task-name-clickable);
|
color: var(--color-task-name-clickable);
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
max-width: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-name-clickable:hover {
|
.task-name-clickable:hover {
|
||||||
@ -420,26 +425,37 @@ body {
|
|||||||
color: var(--color-task-meta);
|
color: var(--color-task-meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status {
|
.task-meta > span {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.state {
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status.completed {
|
.state.completed {
|
||||||
background: var(--color-status-completed-bg);
|
background: var(--color-state-completed-bg);
|
||||||
color: var(--color-status-completed-text);
|
color: var(--color-state-completed-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status.failed {
|
.state.cancelled {
|
||||||
background: var(--color-status-failed-bg);
|
background: var(--color-state-cancelled-bg);
|
||||||
color: var(--color-status-failed-text);
|
color: var(--color-state-cancelled-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status.cancelled {
|
.state.interrupted {
|
||||||
background: var(--color-status-cancelled-bg);
|
background: var(--color-state-failed-bg);
|
||||||
color: var(--color-status-cancelled-text);
|
color: var(--color-state-failed-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.state.paused {
|
||||||
|
background: var(--color-state-paused-bg);
|
||||||
|
color: var(--color-state-paused-text);
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-actions {
|
.task-actions {
|
||||||
|
|||||||
46
electron/utils/download.js
vendored
46
electron/utils/download.js
vendored
@ -30,7 +30,7 @@ class DownloadManager {
|
|||||||
* 转换下载项格式
|
* 转换下载项格式
|
||||||
* @param {Electron.DownloadItem} downloadItem
|
* @param {Electron.DownloadItem} downloadItem
|
||||||
*/
|
*/
|
||||||
convertItem(downloadItem) {
|
convert(downloadItem) {
|
||||||
return {
|
return {
|
||||||
filename: downloadItem.getFilename(),
|
filename: downloadItem.getFilename(),
|
||||||
path: downloadItem.getSavePath(),
|
path: downloadItem.getSavePath(),
|
||||||
@ -52,27 +52,26 @@ class DownloadManager {
|
|||||||
* 添加下载项
|
* 添加下载项
|
||||||
* @param {Electron.DownloadItem} downloadItem
|
* @param {Electron.DownloadItem} downloadItem
|
||||||
*/
|
*/
|
||||||
addDownloadItem(downloadItem) {
|
add(downloadItem) {
|
||||||
// 根据保存路径,如果下载项已存在,则取消下载(避免重复下载)
|
// 根据保存路径,如果下载项已存在,则取消下载(避免重复下载)
|
||||||
this.cancelDownloadItem(downloadItem.getSavePath());
|
this.cancel(downloadItem.getSavePath());
|
||||||
|
|
||||||
// 添加下载项
|
// 添加下载项
|
||||||
this.downloadHistory.unshift({
|
this.downloadHistory.unshift({
|
||||||
...this.convertItem(downloadItem),
|
...this.convert(downloadItem),
|
||||||
_source: downloadItem,
|
_source: downloadItem,
|
||||||
});
|
});
|
||||||
if (this.downloadHistory.length > 1000) {
|
if (this.downloadHistory.length > 1000) {
|
||||||
this.downloadHistory = this.downloadHistory.slice(0, 1000);
|
this.downloadHistory = this.downloadHistory.slice(0, 1000);
|
||||||
}
|
}
|
||||||
store.set('downloadHistory', this.downloadHistory);
|
store.set('downloadHistory', this.downloadHistory);
|
||||||
loger.info(`Download item added: ${downloadItem.getSavePath()}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取下载列表
|
* 获取下载列表
|
||||||
* @returns {*}
|
* @returns {*}
|
||||||
*/
|
*/
|
||||||
getDownloadItems() {
|
get() {
|
||||||
return this.downloadHistory.map(item => {
|
return this.downloadHistory.map(item => {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
@ -87,7 +86,7 @@ class DownloadManager {
|
|||||||
* 更新下载项
|
* 更新下载项
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
*/
|
*/
|
||||||
updateDownloadItem(path) {
|
refresh(path) {
|
||||||
const item = this.downloadHistory.find(d => d.path === path)
|
const item = this.downloadHistory.find(d => d.path === path)
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
@ -97,16 +96,15 @@ class DownloadManager {
|
|||||||
loger.warn(`Download item not found for path: ${path}`);
|
loger.warn(`Download item not found for path: ${path}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Object.assign(item, this.convertItem(downloadItem))
|
Object.assign(item, this.convert(downloadItem))
|
||||||
store.set('downloadHistory', this.downloadHistory);
|
store.set('downloadHistory', this.downloadHistory);
|
||||||
loger.info(`Download item updated: ${path} - ${item.state} (${item.percent}%)`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 暂停下载项
|
* 暂停下载项
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
*/
|
*/
|
||||||
pauseDownloadItem(path) {
|
pause(path) {
|
||||||
const item = this.downloadHistory.find(d => d.path === path)
|
const item = this.downloadHistory.find(d => d.path === path)
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
@ -117,14 +115,14 @@ class DownloadManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
downloadItem.pause();
|
downloadItem.pause();
|
||||||
this.updateDownloadItem(path);
|
this.refresh(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 恢复下载项
|
* 恢复下载项
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
*/
|
*/
|
||||||
resumeDownloadItem(path) {
|
resume(path) {
|
||||||
const item = this.downloadHistory.find(d => d.path === path)
|
const item = this.downloadHistory.find(d => d.path === path)
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
@ -135,14 +133,14 @@ class DownloadManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
downloadItem.resume();
|
downloadItem.resume();
|
||||||
this.updateDownloadItem(path);
|
this.refresh(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取消下载项
|
* 取消下载项
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
*/
|
*/
|
||||||
cancelDownloadItem(path) {
|
cancel(path) {
|
||||||
const item = this.downloadHistory.find(d => d.path === path)
|
const item = this.downloadHistory.find(d => d.path === path)
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
@ -153,39 +151,39 @@ class DownloadManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
downloadItem.cancel();
|
downloadItem.cancel();
|
||||||
this.updateDownloadItem(path);
|
this.refresh(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 取消所有下载项
|
* 取消所有下载项
|
||||||
*/
|
*/
|
||||||
cancelAllDownloadItems() {
|
cancelAll() {
|
||||||
this.downloadHistory.forEach(item => {
|
this.downloadHistory.forEach(item => {
|
||||||
this.cancelDownloadItem(item.path);
|
this.cancel(item.path);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从下载历史中移除下载项
|
* 删除下载项
|
||||||
* @param {string} path
|
* @param {string} path
|
||||||
*/
|
*/
|
||||||
removeFromDownloadHistory(path) {
|
remove(path) {
|
||||||
const index = this.downloadHistory.findIndex(item => item.path === path);
|
const index = this.downloadHistory.findIndex(item => item.path === path);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
this.cancelDownloadItem(path);
|
this.cancel(path);
|
||||||
this.downloadHistory.splice(index, 1);
|
this.downloadHistory.splice(index, 1);
|
||||||
store.set('downloadHistory', this.downloadHistory);
|
store.set('downloadHistory', this.downloadHistory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 清空下载历史
|
* 清空下载项
|
||||||
*/
|
*/
|
||||||
clearHistory() {
|
removeAll() {
|
||||||
this.cancelAllDownloadItems();
|
this.cancelAll();
|
||||||
this.downloadHistory = [];
|
this.downloadHistory = [];
|
||||||
store.set('downloadHistory', []);
|
store.set('downloadHistory', []);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { DownloadManager };
|
module.exports = {DownloadManager};
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
{{mateName}}<div class="input-box-push">+</div>Shift<div class="input-box-push">+</div><Input class="input-box-key" v-model="formData.screenshot_key" :maxlength="2"/>
|
{{mateName}}<div class="input-box-push">+</div>Shift<div class="input-box-push">+</div><Input class="input-box-key" v-model="formData.screenshot_key" :maxlength="2"/>
|
||||||
</div>
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem :label="$L('下载内容')" prop="download_key">
|
<FormItem :label="$L('下载内容')">
|
||||||
<div class="input-box">
|
<div class="input-box">
|
||||||
{{mateName}}<div class="input-box-push">+</div>{{altName}}<div class="input-box-push">+</div>L
|
{{mateName}}<div class="input-box-push">+</div>{{altName}}<div class="input-box-push">+</div>L
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
2
resources/assets/js/store/actions.js
vendored
2
resources/assets/js/store/actions.js
vendored
@ -466,7 +466,7 @@ export default {
|
|||||||
}
|
}
|
||||||
if ($A.Electron) {
|
if ($A.Electron) {
|
||||||
$A.Electron.request({
|
$A.Electron.request({
|
||||||
action: 'createDownloadTask',
|
action: 'createDownload',
|
||||||
url
|
url
|
||||||
});
|
});
|
||||||
$A.Electron.request({
|
$A.Electron.request({
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user