mirror of
https://github.com/kuaifan/dootask.git
synced 2025-12-11 18:42:54 +00:00
perf: 优化下载工具
This commit is contained in:
parent
5bbffc4f5c
commit
a8a54593e2
125
electron/electron-down.js
vendored
125
electron/electron-down.js
vendored
@ -20,87 +20,88 @@ function initialize(options = {}) {
|
||||
...options,
|
||||
|
||||
onStarted: (item) => {
|
||||
downloadManager.addDownloadItem(item);
|
||||
syncDownloadItems();
|
||||
},
|
||||
onProgress: (item) => {
|
||||
downloadManager.updateDownloadItem(item.path);
|
||||
downloadManager.add(item);
|
||||
syncDownloadItems();
|
||||
},
|
||||
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();
|
||||
},
|
||||
onCompleted: (item) => {
|
||||
downloadManager.updateDownloadItem(item.path);
|
||||
downloadManager.refresh(item.path);
|
||||
syncDownloadItems();
|
||||
}
|
||||
});
|
||||
|
||||
// IPC - 获取下载任务
|
||||
ipcMain.handle('getDownloadTasks', () => {
|
||||
return {
|
||||
items: downloadManager.getDownloadItems()
|
||||
};
|
||||
});
|
||||
// IPC
|
||||
ipcMain.handle('downloadManager', async (event, {action, path}) => {
|
||||
switch (action) {
|
||||
case "get": {
|
||||
return {
|
||||
items: downloadManager.get()
|
||||
};
|
||||
}
|
||||
|
||||
// IPC - 暂停下载任务
|
||||
ipcMain.handle('pauseDownloadTask', async (event, {path}) => {
|
||||
downloadManager.pauseDownloadItem(path);
|
||||
syncDownloadItems();
|
||||
return true;
|
||||
});
|
||||
case "pause": {
|
||||
downloadManager.pause(path);
|
||||
syncDownloadItems();
|
||||
return true;
|
||||
}
|
||||
|
||||
// IPC - 恢复下载任务
|
||||
ipcMain.handle('resumeDownloadTask', async (event, {path}) => {
|
||||
downloadManager.resumeDownloadItem(path);
|
||||
syncDownloadItems();
|
||||
return true;
|
||||
});
|
||||
case "resume": {
|
||||
downloadManager.resume(path);
|
||||
syncDownloadItems();
|
||||
return true;
|
||||
}
|
||||
|
||||
// IPC - 取消下载任务
|
||||
ipcMain.handle('cancelDownloadTask', async (event, {path}) => {
|
||||
downloadManager.cancelDownloadItem(path);
|
||||
syncDownloadItems();
|
||||
return true;
|
||||
});
|
||||
case "cancel": {
|
||||
downloadManager.cancel(path);
|
||||
syncDownloadItems();
|
||||
return true;
|
||||
}
|
||||
|
||||
// IPC - 从下载历史中移除下载项
|
||||
ipcMain.handle('removeFromDownloadHistory', async (event, {path}) => {
|
||||
downloadManager.removeFromDownloadHistory(path);
|
||||
syncDownloadItems();
|
||||
return true;
|
||||
});
|
||||
case "remove": {
|
||||
downloadManager.remove(path);
|
||||
syncDownloadItems();
|
||||
return true;
|
||||
}
|
||||
|
||||
// IPC - 清理下载历史
|
||||
ipcMain.handle('clearDownloadHistory', async () => {
|
||||
downloadManager.clearHistory();
|
||||
syncDownloadItems();
|
||||
return true;
|
||||
});
|
||||
case "removeAll": {
|
||||
downloadManager.removeAll();
|
||||
syncDownloadItems();
|
||||
return true;
|
||||
}
|
||||
|
||||
// IPC - 打开下载文件
|
||||
ipcMain.handle('openDownloadedFile', async (event, {path}) => {
|
||||
if (fs.existsSync(path)) {
|
||||
return shell.openPath(path);
|
||||
case "openFile": {
|
||||
if (!fs.existsSync(path)) {
|
||||
throw new Error('file not found');
|
||||
}
|
||||
return shell.openPath(path);
|
||||
}
|
||||
|
||||
case "showFolder": {
|
||||
if (!fs.existsSync(path)) {
|
||||
throw new Error('file not found');
|
||||
}
|
||||
shell.showItemInFolder(path);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
throw new Error('file not found');
|
||||
});
|
||||
|
||||
// IPC - 显示下载文件
|
||||
ipcMain.handle('showDownloadedFileInFolder', async (event, {path}) => {
|
||||
if (fs.existsSync(path)) {
|
||||
shell.showItemInFolder(path);
|
||||
return true;
|
||||
}
|
||||
throw new Error('file not found');
|
||||
});
|
||||
}
|
||||
|
||||
function syncDownloadItems() {
|
||||
// 同步下载项到渲染进程
|
||||
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) {
|
||||
// 更新窗口数据
|
||||
await updateDownloadWindow(language, theme)
|
||||
await updateWindow(language, theme)
|
||||
// 显示窗口并聚焦
|
||||
downloadWindow.show();
|
||||
downloadWindow.focus();
|
||||
@ -215,7 +216,7 @@ async function open(language = 'zh', theme = 'light') {
|
||||
|
||||
// 将语言包发送到渲染进程
|
||||
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) {
|
||||
try {
|
||||
const finalLanguage = getLanguagePack(language);
|
||||
@ -256,5 +257,5 @@ module.exports = {
|
||||
open,
|
||||
close,
|
||||
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':
|
||||
ret = await electronDown.open(args.language || 'zh', args.theme || 'light');
|
||||
break;
|
||||
case 'createDownloadTask':
|
||||
ret = await electronDown.download(mainWindow, args.url, args.options || {});
|
||||
break;
|
||||
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;
|
||||
case 'watchFile':
|
||||
ret = await watchFile(args.path);
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
"url": "https://github.com/kuaifan/dootask.git"
|
||||
},
|
||||
"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/maker-deb": "^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>
|
||||
|
||||
644
electron/render/download/style.css
vendored
644
electron/render/download/style.css
vendored
@ -1,527 +1,543 @@
|
||||
/* 下载管理器样式 - Chrome 风格 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--color-bg: #fff;
|
||||
--color-text: #202124;
|
||||
--color-bg: #fff;
|
||||
--color-text: #202124;
|
||||
|
||||
--color-toolbar-bg: #fff;
|
||||
--color-toolbar-border: #dadce0;
|
||||
--color-toolbar-bg: #fff;
|
||||
--color-toolbar-border: #dadce0;
|
||||
|
||||
--color-input-bg: #efefef;
|
||||
--color-input-text: #202124;
|
||||
--color-input-placeholder: #5f6368;
|
||||
--color-input-focus-border: #1a73e8;
|
||||
--color-input-focus-ring: rgba(26, 115, 232, .2);
|
||||
--color-input-bg: #efefef;
|
||||
--color-input-text: #202124;
|
||||
--color-input-placeholder: #5f6368;
|
||||
--color-input-focus-border: #1a73e8;
|
||||
--color-input-focus-ring: rgba(26, 115, 232, .2);
|
||||
|
||||
--color-action-btn-bg: #fff;
|
||||
--color-action-btn-border: #dadce0;
|
||||
--color-action-btn-text: #1a73e8;
|
||||
--color-action-btn-hover-bg: #f8f9fa;
|
||||
--color-action-btn-hover-border: #c8c9ca;
|
||||
--color-action-btn-danger-text: #d93025;
|
||||
--color-action-btn-danger-hover-bg: #fce8e6;
|
||||
--color-action-btn-bg: #fff;
|
||||
--color-action-btn-border: #dadce0;
|
||||
--color-action-btn-text: #1a73e8;
|
||||
--color-action-btn-hover-bg: #f8f9fa;
|
||||
--color-action-btn-hover-border: #c8c9ca;
|
||||
--color-action-btn-danger-text: #d93025;
|
||||
--color-action-btn-danger-hover-bg: #fce8e6;
|
||||
|
||||
--color-icon-btn: #5f6368;
|
||||
--color-icon-btn-hover-bg: #f8f9fa;
|
||||
--color-icon-btn-hover-color: #202124;
|
||||
--color-icon-btn-danger: #d93025;
|
||||
--color-icon-btn-danger-hover-bg: #fce8e6;
|
||||
--color-icon-btn-danger-hover-color: #d93025;
|
||||
--color-icon-btn: #5f6368;
|
||||
--color-icon-btn-hover-bg: #f8f9fa;
|
||||
--color-icon-btn-hover-color: #202124;
|
||||
--color-icon-btn-danger: #d93025;
|
||||
--color-icon-btn-danger-hover-bg: #fce8e6;
|
||||
--color-icon-btn-danger-hover-color: #d93025;
|
||||
|
||||
--color-content-bg: #fff;
|
||||
--color-task-item-bg: #fff;
|
||||
--color-task-item-border: #e8eaed;
|
||||
--color-task-item-hover-bg: #f8f9fa;
|
||||
--color-task-name: #202124;
|
||||
--color-task-name-clickable: #1a73e8;
|
||||
--color-content-bg: #fff;
|
||||
--color-task-item-bg: #fff;
|
||||
--color-task-item-border: #e8eaed;
|
||||
--color-task-item-hover-bg: #f8f9fa;
|
||||
--color-task-name: #202124;
|
||||
--color-task-name-clickable: #1a73e8;
|
||||
|
||||
--color-progress-bar: #e8eaed;
|
||||
--color-progress-fill: #1a73e8;
|
||||
--color-progress-text: #5f6368;
|
||||
--color-task-meta: #5f6368;
|
||||
--color-speed: #1a73e8;
|
||||
--color-progress-bar: #e8eaed;
|
||||
--color-progress-fill: #1a73e8;
|
||||
--color-progress-text: #5f6368;
|
||||
--color-task-meta: #5f6368;
|
||||
--color-speed: #1a73e8;
|
||||
|
||||
--color-empty-state: #5f6368;
|
||||
--color-empty-text: #5f6368;
|
||||
--color-status-completed-bg: #e8f5e8;
|
||||
--color-status-completed-text: #137333;
|
||||
--color-status-failed-bg: #fce8e6;
|
||||
--color-status-failed-text: #d93025;
|
||||
--color-status-cancelled-bg: #e8eaed;
|
||||
--color-status-cancelled-text: #5f6368;
|
||||
--color-empty-state: #5f6368;
|
||||
--color-empty-text: #5f6368;
|
||||
--color-state-completed-bg: #e8f5e8;
|
||||
--color-state-completed-text: #137333;
|
||||
--color-state-failed-bg: #fce8e6;
|
||||
--color-state-failed-text: #d93025;
|
||||
--color-state-cancelled-bg: #e8eaed;
|
||||
--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-hover: rgba(0, 0, 0, 0.3);
|
||||
--scrollbar-thumb: rgba(0, 0, 0, 0.2);
|
||||
--scrollbar-thumb-hover: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.dark {
|
||||
--color-bg: #202124;
|
||||
--color-text: #e8eaed;
|
||||
--color-bg: #202124;
|
||||
--color-text: #e8eaed;
|
||||
|
||||
--color-toolbar-bg: #2d2e30;
|
||||
--color-toolbar-border: #3c4043;
|
||||
--color-toolbar-bg: #2d2e30;
|
||||
--color-toolbar-border: #3c4043;
|
||||
|
||||
--color-input-bg: #282828;
|
||||
--color-input-text: #e8eaed;
|
||||
--color-input-placeholder: #9aa0a6;
|
||||
--color-input-focus-border: #8ab4f8;
|
||||
--color-input-focus-ring: rgba(138, 180, 248, .12);
|
||||
--color-input-bg: #282828;
|
||||
--color-input-text: #e8eaed;
|
||||
--color-input-placeholder: #9aa0a6;
|
||||
--color-input-focus-border: #8ab4f8;
|
||||
--color-input-focus-ring: rgba(138, 180, 248, .12);
|
||||
|
||||
--color-action-btn-bg: #2d2e30;
|
||||
--color-action-btn-border: #5f6368;
|
||||
--color-action-btn-text: #8ab4f8;
|
||||
--color-action-btn-hover-bg: #35363a;
|
||||
--color-action-btn-hover-border: #70757a;
|
||||
--color-action-btn-danger-text: #f28b82;
|
||||
--color-action-btn-danger-hover-bg: #35363a;
|
||||
--color-action-btn-bg: #2d2e30;
|
||||
--color-action-btn-border: #5f6368;
|
||||
--color-action-btn-text: #8ab4f8;
|
||||
--color-action-btn-hover-bg: #35363a;
|
||||
--color-action-btn-hover-border: #70757a;
|
||||
--color-action-btn-danger-text: #f28b82;
|
||||
--color-action-btn-danger-hover-bg: #35363a;
|
||||
|
||||
--color-icon-btn: #9aa0a6;
|
||||
--color-icon-btn-hover-bg: #35363a;
|
||||
--color-icon-btn-hover-color: #e8eaed;
|
||||
--color-icon-btn-danger: #f28b82;
|
||||
--color-icon-btn-danger-hover-bg: #3d1a1a;
|
||||
--color-icon-btn-danger-hover-color: #f28b82;
|
||||
--color-icon-btn: #9aa0a6;
|
||||
--color-icon-btn-hover-bg: #35363a;
|
||||
--color-icon-btn-hover-color: #e8eaed;
|
||||
--color-icon-btn-danger: #f28b82;
|
||||
--color-icon-btn-danger-hover-bg: #3d1a1a;
|
||||
--color-icon-btn-danger-hover-color: #f28b82;
|
||||
|
||||
--color-content-bg: #2d2e30;
|
||||
--color-task-item-bg: #2d2e30;
|
||||
--color-task-item-border: #3c4043;
|
||||
--color-task-item-hover-bg: #35363a;
|
||||
--color-task-name: #e8eaed;
|
||||
--color-task-name-clickable: #8ab4f8;
|
||||
--color-content-bg: #2d2e30;
|
||||
--color-task-item-bg: #2d2e30;
|
||||
--color-task-item-border: #3c4043;
|
||||
--color-task-item-hover-bg: #35363a;
|
||||
--color-task-name: #e8eaed;
|
||||
--color-task-name-clickable: #8ab4f8;
|
||||
|
||||
--color-progress-bar: #3c4043;
|
||||
--color-progress-fill: #8ab4f8;
|
||||
--color-progress-text: #9aa0a6;
|
||||
--color-task-meta: #9aa0a6;
|
||||
--color-speed: #8ab4f8;
|
||||
--color-progress-bar: #3c4043;
|
||||
--color-progress-fill: #8ab4f8;
|
||||
--color-progress-text: #9aa0a6;
|
||||
--color-task-meta: #9aa0a6;
|
||||
--color-speed: #8ab4f8;
|
||||
|
||||
--color-empty-state: #9aa0a6;
|
||||
--color-empty-text: #9aa0a6;
|
||||
--color-status-completed-bg: #1e3a1e;
|
||||
--color-status-completed-text: #81c995;
|
||||
--color-status-failed-bg: #3d1a1a;
|
||||
--color-status-failed-text: #f28b82;
|
||||
--color-status-cancelled-bg: #3c4043;
|
||||
--color-status-cancelled-text: #9aa0a6;
|
||||
--color-empty-state: #9aa0a6;
|
||||
--color-empty-text: #9aa0a6;
|
||||
--color-state-completed-bg: #1e3a1e;
|
||||
--color-state-completed-text: #81c995;
|
||||
--color-state-failed-bg: #3d1a1a;
|
||||
--color-state-failed-text: #f28b82;
|
||||
--color-state-cancelled-bg: #3c4043;
|
||||
--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-hover: rgba(255, 255, 255, 0.3);
|
||||
--scrollbar-thumb: rgba(255, 255, 255, 0.2);
|
||||
--scrollbar-thumb-hover: rgba(255, 255, 255, 0.3);
|
||||
|
||||
body {
|
||||
color-scheme: dark;
|
||||
}
|
||||
body {
|
||||
color-scheme: dark;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
font-size: 13px;
|
||||
overflow: hidden;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
font-size: 13px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.download-manager {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
/* 工具栏 */
|
||||
.toolbar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 32px;
|
||||
padding: 0 24px;
|
||||
background: var(--color-toolbar-bg);
|
||||
border-bottom: 1px solid var(--color-toolbar-border);
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 32px;
|
||||
padding: 0 24px;
|
||||
background: var(--color-toolbar-bg);
|
||||
border-bottom: 1px solid var(--color-toolbar-border);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.search {
|
||||
flex: 1;
|
||||
padding: 8px 0;
|
||||
flex: 1;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
max-width: 380px;
|
||||
height: 32px;
|
||||
padding: 0 12px 0 32px;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
margin: 1px 0;
|
||||
font-size: 13px;
|
||||
outline: none;
|
||||
color: var(--color-input-text);
|
||||
background: var(--color-input-bg) url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"%235f6368\"><path d=\"M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z\"/></svg>') no-repeat 8px center;
|
||||
background-size: 18px 18px;
|
||||
transition: all 0.3s;
|
||||
width: 100%;
|
||||
max-width: 380px;
|
||||
height: 32px;
|
||||
padding: 0 12px 0 32px;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
margin: 1px 0;
|
||||
font-size: 13px;
|
||||
outline: none;
|
||||
color: var(--color-input-text);
|
||||
background: var(--color-input-bg) url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"%235f6368\"><path d=\"M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z\"/></svg>') no-repeat 8px center;
|
||||
background-size: 18px 18px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.search-input::placeholder {
|
||||
color: var(--color-input-placeholder);
|
||||
color: var(--color-input-placeholder);
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
border-color: var(--color-input-focus-border);
|
||||
box-shadow: 0 0 0 2px var(--color-input-focus-ring);
|
||||
border-color: var(--color-input-focus-border);
|
||||
box-shadow: 0 0 0 2px var(--color-input-focus-ring);
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
padding: 6px 12px;
|
||||
border: 1px solid var(--color-action-btn-border);
|
||||
background: var(--color-action-btn-bg);
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
color: var(--color-action-btn-text);
|
||||
transition: all 0.15s;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
padding: 6px 12px;
|
||||
border: 1px solid var(--color-action-btn-border);
|
||||
background: var(--color-action-btn-bg);
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
color: var(--color-action-btn-text);
|
||||
transition: all 0.15s;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.action-btn:hover:not(:disabled) {
|
||||
background: var(--color-action-btn-hover-bg);
|
||||
border-color: var(--color-action-btn-hover-border);
|
||||
background: var(--color-action-btn-hover-bg);
|
||||
border-color: var(--color-action-btn-hover-border);
|
||||
}
|
||||
|
||||
.action-btn:disabled {
|
||||
opacity: 0.38;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.38;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.action-btn.small {
|
||||
padding: 4px 8px;
|
||||
font-size: 11px;
|
||||
padding: 4px 8px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.action-btn.danger {
|
||||
color: var(--color-action-btn-danger-text);
|
||||
color: var(--color-action-btn-danger-text);
|
||||
}
|
||||
|
||||
.action-btn.danger:hover:not(:disabled) {
|
||||
background: var(--color-action-btn-danger-hover-bg);
|
||||
background: var(--color-action-btn-danger-hover-bg);
|
||||
}
|
||||
|
||||
/* 图标按钮样式 */
|
||||
.icon-btn {
|
||||
padding: 6px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
color: var(--color-icon-btn);
|
||||
transition: all 0.15s;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 6px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
color: var(--color-icon-btn);
|
||||
transition: all 0.15s;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.icon-btn svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.icon-btn:hover {
|
||||
background: var(--color-icon-btn-hover-bg);
|
||||
color: var(--color-icon-btn-hover-color);
|
||||
background: var(--color-icon-btn-hover-bg);
|
||||
color: var(--color-icon-btn-hover-color);
|
||||
}
|
||||
|
||||
.icon-btn.danger {
|
||||
color: var(--color-icon-btn-danger);
|
||||
color: var(--color-icon-btn-danger);
|
||||
}
|
||||
|
||||
.icon-btn.danger:hover {
|
||||
background: var(--color-icon-btn-danger-hover-bg);
|
||||
color: var(--color-icon-btn-danger-hover-color);
|
||||
background: var(--color-icon-btn-danger-hover-bg);
|
||||
color: var(--color-icon-btn-danger-hover-color);
|
||||
}
|
||||
|
||||
/* 内容区 */
|
||||
.content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
background: var(--color-content-bg);
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
background: var(--color-content-bg);
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* 空状态 */
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
padding: 48px 24px;
|
||||
color: var(--color-empty-state);
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
padding: 48px 24px;
|
||||
color: var(--color-empty-state);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
margin-bottom: 20px;
|
||||
opacity: 0.6;
|
||||
margin-bottom: 20px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 14px;
|
||||
color: var(--color-empty-text);
|
||||
font-size: 14px;
|
||||
color: var(--color-empty-text);
|
||||
}
|
||||
|
||||
/* 任务列表 */
|
||||
.task-list {
|
||||
padding: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.task-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px 24px;
|
||||
background: var(--color-task-item-bg);
|
||||
border-bottom: 1px solid var(--color-task-item-border);
|
||||
transition: background-color 0.15s;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px 24px;
|
||||
background: var(--color-task-item-bg);
|
||||
border-bottom: 1px solid var(--color-task-item-border);
|
||||
transition: background-color 0.15s;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.task-item > * {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.task-item:hover {
|
||||
background: var(--color-task-item-hover-bg);
|
||||
background: var(--color-task-item-hover-bg);
|
||||
}
|
||||
|
||||
.task-item:last-child {
|
||||
border-bottom: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* 整块背景进度条(下载中样式) */
|
||||
.task-item.downloading-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: var(--progress, 0%);
|
||||
background: var(--color-progress-fill);
|
||||
opacity: 0.12;
|
||||
pointer-events: none;
|
||||
transition: width 0.3s ease;
|
||||
z-index: 0;
|
||||
.task-item.progressing-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: var(--progress, 0%);
|
||||
background: var(--color-progress-fill);
|
||||
opacity: 0.12;
|
||||
pointer-events: none;
|
||||
transition: width 0.3s ease;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.task-icon {
|
||||
margin-right: 16px;
|
||||
flex-shrink: 0;
|
||||
margin-right: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.file-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.file-icon svg {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.task-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.task-name {
|
||||
min-width: 0;
|
||||
width: auto;
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
color: var(--color-task-name);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
min-width: 0;
|
||||
width: auto;
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
color: var(--color-task-name);
|
||||
}
|
||||
|
||||
.task-name-clickable {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
color: var(--color-task-name-clickable);
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
color: var(--color-task-name-clickable);
|
||||
text-decoration: none;
|
||||
max-width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.task-name-clickable:hover {
|
||||
text-decoration: underline;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.task-progress {
|
||||
margin-bottom: 4px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background: var(--color-progress-bar);
|
||||
border-radius: 2px;
|
||||
margin-bottom: 6px;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
background: var(--color-progress-bar);
|
||||
border-radius: 2px;
|
||||
margin-bottom: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.progress-fill {
|
||||
height: 100%;
|
||||
background: var(--color-progress-fill);
|
||||
border-radius: 2px;
|
||||
transition: width 0.3s ease;
|
||||
height: 100%;
|
||||
background: var(--color-progress-fill);
|
||||
border-radius: 2px;
|
||||
transition: width 0.3s ease;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
font-size: 11px;
|
||||
color: var(--color-progress-text);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 11px;
|
||||
color: var(--color-progress-text);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.speed {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--color-speed);
|
||||
font-weight: 400;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--color-speed);
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.task-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
height: 20px;
|
||||
font-size: 12px;
|
||||
color: var(--color-task-meta);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
height: 20px;
|
||||
font-size: 12px;
|
||||
color: var(--color-task-meta);
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
.task-meta > span {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.status.completed {
|
||||
background: var(--color-status-completed-bg);
|
||||
color: var(--color-status-completed-text);
|
||||
.state {
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.status.failed {
|
||||
background: var(--color-status-failed-bg);
|
||||
color: var(--color-status-failed-text);
|
||||
.state.completed {
|
||||
background: var(--color-state-completed-bg);
|
||||
color: var(--color-state-completed-text);
|
||||
}
|
||||
|
||||
.status.cancelled {
|
||||
background: var(--color-status-cancelled-bg);
|
||||
color: var(--color-status-cancelled-text);
|
||||
.state.cancelled {
|
||||
background: var(--color-state-cancelled-bg);
|
||||
color: var(--color-state-cancelled-text);
|
||||
}
|
||||
|
||||
.state.interrupted {
|
||||
background: var(--color-state-failed-bg);
|
||||
color: var(--color-state-failed-text);
|
||||
}
|
||||
|
||||
.state.paused {
|
||||
background: var(--color-state-paused-bg);
|
||||
color: var(--color-state-paused-text);
|
||||
}
|
||||
|
||||
.task-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-left: 16px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin-left: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
|
||||
/* 平台特定样式 */
|
||||
body.darwin {
|
||||
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
|
||||
}
|
||||
|
||||
body.win32 {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
.tab-content::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.tab-content::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.tab-content::-webkit-scrollbar-thumb {
|
||||
background: var(--scrollbar-thumb);
|
||||
border-radius: 4px;
|
||||
background: var(--scrollbar-thumb);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.tab-content::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--scrollbar-thumb-hover);
|
||||
background: var(--scrollbar-thumb-hover);
|
||||
}
|
||||
|
||||
/* 深色模式通过 .dark 类启用变量,不再依赖系统配色偏好 */
|
||||
|
||||
/* Toast 提示框样式 */
|
||||
.toast {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 1000;
|
||||
animation: toast-slide-up 0.3s ease-out;
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 1000;
|
||||
animation: toast-slide-up 0.3s ease-out;
|
||||
}
|
||||
|
||||
.toast-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
background: #323232;
|
||||
color: #fff;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
font-size: 13px;
|
||||
min-width: 200px;
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 12px 16px;
|
||||
background: #323232;
|
||||
color: #fff;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
font-size: 13px;
|
||||
min-width: 200px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.toast.success .toast-content {
|
||||
background: #4caf50;
|
||||
background: #4caf50;
|
||||
}
|
||||
|
||||
.toast.error .toast-content {
|
||||
background: #f44336;
|
||||
background: #f44336;
|
||||
}
|
||||
|
||||
.toast-message {
|
||||
flex: 1;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
@keyframes toast-slide-up {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translate(-50%, 20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translate(-50%, 20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translate(-50%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
46
electron/utils/download.js
vendored
46
electron/utils/download.js
vendored
@ -30,7 +30,7 @@ class DownloadManager {
|
||||
* 转换下载项格式
|
||||
* @param {Electron.DownloadItem} downloadItem
|
||||
*/
|
||||
convertItem(downloadItem) {
|
||||
convert(downloadItem) {
|
||||
return {
|
||||
filename: downloadItem.getFilename(),
|
||||
path: downloadItem.getSavePath(),
|
||||
@ -52,27 +52,26 @@ class DownloadManager {
|
||||
* 添加下载项
|
||||
* @param {Electron.DownloadItem} downloadItem
|
||||
*/
|
||||
addDownloadItem(downloadItem) {
|
||||
add(downloadItem) {
|
||||
// 根据保存路径,如果下载项已存在,则取消下载(避免重复下载)
|
||||
this.cancelDownloadItem(downloadItem.getSavePath());
|
||||
this.cancel(downloadItem.getSavePath());
|
||||
|
||||
// 添加下载项
|
||||
this.downloadHistory.unshift({
|
||||
...this.convertItem(downloadItem),
|
||||
...this.convert(downloadItem),
|
||||
_source: downloadItem,
|
||||
});
|
||||
if (this.downloadHistory.length > 1000) {
|
||||
this.downloadHistory = this.downloadHistory.slice(0, 1000);
|
||||
}
|
||||
store.set('downloadHistory', this.downloadHistory);
|
||||
loger.info(`Download item added: ${downloadItem.getSavePath()}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下载列表
|
||||
* @returns {*}
|
||||
*/
|
||||
getDownloadItems() {
|
||||
get() {
|
||||
return this.downloadHistory.map(item => {
|
||||
return {
|
||||
...item,
|
||||
@ -87,7 +86,7 @@ class DownloadManager {
|
||||
* 更新下载项
|
||||
* @param {string} path
|
||||
*/
|
||||
updateDownloadItem(path) {
|
||||
refresh(path) {
|
||||
const item = this.downloadHistory.find(d => d.path === path)
|
||||
if (!item) {
|
||||
return;
|
||||
@ -97,16 +96,15 @@ class DownloadManager {
|
||||
loger.warn(`Download item not found for path: ${path}`);
|
||||
return;
|
||||
}
|
||||
Object.assign(item, this.convertItem(downloadItem))
|
||||
Object.assign(item, this.convert(downloadItem))
|
||||
store.set('downloadHistory', this.downloadHistory);
|
||||
loger.info(`Download item updated: ${path} - ${item.state} (${item.percent}%)`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停下载项
|
||||
* @param {string} path
|
||||
*/
|
||||
pauseDownloadItem(path) {
|
||||
pause(path) {
|
||||
const item = this.downloadHistory.find(d => d.path === path)
|
||||
if (!item) {
|
||||
return;
|
||||
@ -117,14 +115,14 @@ class DownloadManager {
|
||||
return;
|
||||
}
|
||||
downloadItem.pause();
|
||||
this.updateDownloadItem(path);
|
||||
this.refresh(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复下载项
|
||||
* @param {string} path
|
||||
*/
|
||||
resumeDownloadItem(path) {
|
||||
resume(path) {
|
||||
const item = this.downloadHistory.find(d => d.path === path)
|
||||
if (!item) {
|
||||
return;
|
||||
@ -135,14 +133,14 @@ class DownloadManager {
|
||||
return;
|
||||
}
|
||||
downloadItem.resume();
|
||||
this.updateDownloadItem(path);
|
||||
this.refresh(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消下载项
|
||||
* @param {string} path
|
||||
*/
|
||||
cancelDownloadItem(path) {
|
||||
cancel(path) {
|
||||
const item = this.downloadHistory.find(d => d.path === path)
|
||||
if (!item) {
|
||||
return;
|
||||
@ -153,39 +151,39 @@ class DownloadManager {
|
||||
return;
|
||||
}
|
||||
downloadItem.cancel();
|
||||
this.updateDownloadItem(path);
|
||||
this.refresh(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消所有下载项
|
||||
*/
|
||||
cancelAllDownloadItems() {
|
||||
cancelAll() {
|
||||
this.downloadHistory.forEach(item => {
|
||||
this.cancelDownloadItem(item.path);
|
||||
this.cancel(item.path);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 从下载历史中移除下载项
|
||||
* 删除下载项
|
||||
* @param {string} path
|
||||
*/
|
||||
removeFromDownloadHistory(path) {
|
||||
remove(path) {
|
||||
const index = this.downloadHistory.findIndex(item => item.path === path);
|
||||
if (index > -1) {
|
||||
this.cancelDownloadItem(path);
|
||||
this.cancel(path);
|
||||
this.downloadHistory.splice(index, 1);
|
||||
store.set('downloadHistory', this.downloadHistory);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空下载历史
|
||||
* 清空下载项
|
||||
*/
|
||||
clearHistory() {
|
||||
this.cancelAllDownloadItems();
|
||||
removeAll() {
|
||||
this.cancelAll();
|
||||
this.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"/>
|
||||
</div>
|
||||
</FormItem>
|
||||
<FormItem :label="$L('下载内容')" prop="download_key">
|
||||
<FormItem :label="$L('下载内容')">
|
||||
<div class="input-box">
|
||||
{{mateName}}<div class="input-box-push">+</div>{{altName}}<div class="input-box-push">+</div>L
|
||||
</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) {
|
||||
$A.Electron.request({
|
||||
action: 'createDownloadTask',
|
||||
action: 'createDownload',
|
||||
url
|
||||
});
|
||||
$A.Electron.request({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user