From b23e3d7359464870d21ad102372e78c0dd444926 Mon Sep 17 00:00:00 2001 From: kuaifan Date: Sat, 20 Sep 2025 12:01:14 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E4=B8=8B=E8=BD=BD?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E7=9A=84=E7=AD=89=E5=BE=85=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- electron/electron-down.js | 37 +++++++++++-- electron/electron.js | 2 +- electron/render/download/index.html | 33 ++++++++++- electron/render/download/style.css | 82 +++++++++++++++++++++++++++- resources/assets/js/store/actions.js | 5 ++ 5 files changed, 146 insertions(+), 13 deletions(-) diff --git a/electron/electron-down.js b/electron/electron-down.js index 049c3d0cf..c0621ffbb 100644 --- a/electron/electron-down.js +++ b/electron/electron-down.js @@ -2,16 +2,17 @@ const {BrowserWindow, screen, shell, ipcMain} = require('electron') const fs = require('fs'); const path = require('path'); const loger = require("electron-log"); -const {default: electronDl, download} = require("@dootask/electron-dl"); +const {default: electronDl, download, CancelError} = require("@dootask/electron-dl"); const utils = require("./lib/utils"); const {DownloadManager, DownloadStore} = require("./lib/download-manager"); const downloadManager = new DownloadManager(); let downloadWindow = null, - downloadLanguageCode = 'zh'; + downloadLanguageCode = 'zh', + downloadWaiting = false; -function initialize(onStarted= null) { +function initialize(onStarted = null) { // 下载配置 electronDl({ showBadge: false, @@ -19,6 +20,7 @@ function initialize(onStarted= null) { onStarted: (item) => { downloadManager.add(item); + downloadWaiting = false; syncDownloadItems(); if (typeof onStarted === 'function') { onStarted(item) @@ -55,7 +57,8 @@ function initialize(onStarted= null) { switch (action) { case "get": { return { - items: downloadManager.get() + items: downloadManager.get(), + waiting: downloadWaiting, }; } @@ -107,10 +110,32 @@ function initialize(onStarted= null) { }); } +async function createDownload(window_, url, options = {}) { + downloadWaiting = true; + syncDownloadItems(); + try { + return await download(window_, url, options); + } catch (error) { + // electron-dl rejects with CancelError when a download is cancelled; treat it as expected. + const isCancelError = (typeof CancelError === 'function' && error instanceof CancelError) + || error?.name === 'CancelError'; + if (!isCancelError) { + throw error; + } + return null; + } finally { + downloadWaiting = false; + syncDownloadItems(); + } +} + function syncDownloadItems() { // 同步下载项到渲染进程 if (downloadWindow) { - downloadWindow.webContents.send('download-items', downloadManager.get()); + downloadWindow.webContents.send('download-items', { + items: downloadManager.get(), + waiting: downloadWaiting, + }); } } @@ -654,7 +679,7 @@ async function updateWindow(language, theme) { module.exports = { initialize, - download, + createDownload, open, close, destroy, diff --git a/electron/electron.js b/electron/electron.js index 661af84bf..de1077349 100644 --- a/electron/electron.js +++ b/electron/electron.js @@ -2947,7 +2947,7 @@ ipcMain.on("rendererReq", async (event, args) => { ret = await electronDown.updateWindow(args.language, args.theme); break; case 'createDownload': - ret = await electronDown.download(mainWindow, args.url, args.options || {}); + ret = await electronDown.createDownload(mainWindow, args.url, args.options || {}); break; case 'watchFile': ret = await watchFile(args.path); diff --git a/electron/render/download/index.html b/electron/render/download/index.html index 0c6b0c635..c9a00e13a 100644 --- a/electron/render/download/index.html +++ b/electron/render/download/index.html @@ -35,7 +35,7 @@
-
+
@@ -44,6 +44,28 @@
{{ query ? lang.noSearchResult : lang.noItems }}
+ +
+
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
- {{ formatBytes(item.received) }} ({{ item.percent }}%) + {{ formatBytes(item.received) }} {{ formatBytes(item.total) }} @@ -135,7 +157,10 @@ data() { return { query: '', + items: [], + waiting: false, + lang: { // 语言设置 locale: 'zh-CN', @@ -193,8 +218,9 @@ this.getList(); // 监听下载任务列表 - electron?.listener('download-items', (items) => { + electron?.listener('download-items', ({items, waiting}) => { this.items = items + this.waiting = waiting }); // 接收主题 @@ -251,6 +277,7 @@ try { const data = await this.sendAsync('get'); this.items = data.items || []; + this.waiting = data.waiting || false; } catch (e) { console.error('加载下载任务失败:', e); } diff --git a/electron/render/download/style.css b/electron/render/download/style.css index 2c37c87a5..45f1aa6bc 100644 --- a/electron/render/download/style.css +++ b/electron/render/download/style.css @@ -286,6 +286,82 @@ body { color: var(--color-empty-text); } +/* 骨架屏样式 */ +.skeleton-item { + opacity: 0.7; +} + +.skeleton-item:last-child { + border-bottom: none; +} + +/* 骨架元素基础动画 */ +@keyframes skeleton-shimmer { + 0% { + background-position: -200px 0; + } + 100% { + background-position: calc(200px + 100%) 0; + } +} + +.skeleton-file-icon, +.skeleton-name, +.skeleton-size, +.skeleton-time, +.skeleton-status, +.skeleton-btn { + background: linear-gradient(90deg, var(--color-task-item-border) 25%, var(--color-task-item-hover-bg) 50%, var(--color-task-item-border) 75%); + background-size: 200px 100%; + animation: skeleton-shimmer 1.5s infinite linear; + border-radius: 4px; +} + +/* 骨架文件图标 */ +.skeleton-file-icon { + width: 32px; + height: 32px; + margin: 0 4px; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; +} + +/* 骨架文件名 */ +.skeleton-name { + height: 13px; + width: 60%; + margin: 4px 0 6px; + max-width: 200px; +} + +/* 骨架元信息 */ +.skeleton-size { + height: 12px; + width: 80px; +} + +.skeleton-time { + height: 12px; + width: 100px; +} + +.skeleton-status { + height: 12px; + width: 60px; + padding: 2px 8px; + border-radius: 12px; +} + +/* 骨架操作按钮 */ +.skeleton-btn { + width: 22px; + height: 22px; + border-radius: 50%; + margin-right: 8px; +} + /* 任务列表 */ .task-list { padding: 0; @@ -294,7 +370,8 @@ body { .task-item { display: flex; align-items: center; - padding: 16px 24px; + height: 72px; + padding: 0 24px; background: var(--color-task-item-bg); border-bottom: 1px solid var(--color-task-item-border); transition: background-color 0.15s; @@ -353,8 +430,7 @@ body { flex: 1; display: flex; flex-direction: column; - justify-content: center; - gap: 6px; + justify-content: space-between; min-width: 0; } diff --git a/resources/assets/js/store/actions.js b/resources/assets/js/store/actions.js index a92aff363..b707e5de2 100644 --- a/resources/assets/js/store/actions.js +++ b/resources/assets/js/store/actions.js @@ -465,6 +465,11 @@ export default { url = $A.urlAddParams(url, params); } if ($A.Electron) { + $A.Electron.request({ + action: 'openDownloadWindow', + language: languageName, + theme: state.themeName, + }); $A.Electron.request({ action: 'createDownload', url