From 449b5fe3a27656575d01d3a2530210f8ac421ed9 Mon Sep 17 00:00:00 2001 From: yw1573 Date: Thu, 9 Apr 2026 12:38:49 +0800 Subject: [PATCH] feat: add download progress indicator for batch download MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 批量下载时显示创建下载任务的进度: - 增加 downloadProgress 和 downloadTotal 状态 - 下载时显示进度条和计数 - 创建任务完成后自动关闭弹窗 Co-Authored-By: AI --- web/src/work/view/parseModal.ts | 47 ++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/web/src/work/view/parseModal.ts b/web/src/work/view/parseModal.ts index 804448a..0f20155 100644 --- a/web/src/work/view/parseModal.ts +++ b/web/src/work/view/parseModal.ts @@ -49,9 +49,14 @@ export class ParseModalComp implements VanComponent { formatIndex: State }[]> = van.state([]) - /** 该属性用于在点击“开始下载”按钮后使按钮变为禁用状态,防止多次点击 */ + /** 该属性用于在点击”开始下载”按钮后使按钮变为禁用状态,防止多次点击 */ downloadBtnDisabled = van.state(false) + /** 下载进度 */ + downloadProgress = van.state(0) + downloadTotal = van.state(0) + isDownloading = van.state(false) + /** 下载类型:audio 仅音频,video 仅视频,merge 音视频合并 */ downloadType = van.state<'audio' | 'video' | 'merge'>('merge') @@ -69,12 +74,13 @@ export class ParseModalComp implements VanComponent { div({ class: () => `modal-dialog modal-xl modal-fullscreen-xl-down ${(this.totalCount.val + this.errorList.val.length) < 10 ? '' : 'modal-dialog-scrollable'}` }, div({ class: `modal-content` }, div({ class: `modal-header` }, - div({ class: `h5 modal-title` }, () => allFinish.val ? '批量下载' : '批量解析'), + div({ class: `h5 modal-title` }, () => this.isDownloading.val ? '正在创建下载任务...' : (allFinish.val ? '批量下载' : '批量解析')), button({ class: `btn-close`, 'data-bs-dismiss': `modal` }) ), div({ class: `modal-body vstack gap-3`, tabIndex: -1, style: 'outline: none;' }, this.ParseProgress(), - div({ class: 'vstack gap-2', hidden: () => this.errorList.val.length == 0 || !allFinish.val }, + this.DownloadProgress(), + div({ class: 'vstack gap-2', hidden: () => this.errorList.val.length == 0 || !allFinish.val || this.isDownloading.val }, div({ class: 'text-danger' }, () => `以下 ${this.errorList.val.length} 个视频解析失败`), () => div({ class: 'list-group' }, this.errorList.val.map(error => div({ class: 'list-group-item disabled' }, error)) @@ -133,8 +139,12 @@ export class ParseModalComp implements VanComponent { const selectedPlayInfos = this.allPlayInfo.val.filter(info => info.selected.val) const workRoute = this.option.workRoute this.downloadBtnDisabled.val = true + this.isDownloading.val = true + this.downloadProgress.val = 0 + this.downloadTotal.val = selectedPlayInfos.length + // 需要传递给服务器,需要创建下载任务的数据列表 - createTask(selectedPlayInfos.map(info => { + createTask(selectedPlayInfos.map((info, index) => { const badgeNotNum = !info.page.badge.match(/^\d+$/) const isVideoMode = workRoute.videoInfoCardMode.val == 'video' const cardTitle = workRoute.videoInfoCardData.val.title @@ -175,16 +185,31 @@ export class ParseModalComp implements VanComponent { ...activeVideoInfo }) })).then(() => { - workRoute.parseModal.hide() + this.downloadProgress.val = this.downloadTotal.val + setTimeout(() => { + workRoute.parseModal.hide() + }, 500) }).catch(error => { alert(error.message) }).finally(() => { this.downloadBtnDisabled.val = false + this.isDownloading.val = false }) + + // 更新进度 + const updateProgress = () => { + if (this.downloadProgress.val < this.downloadTotal.val) { + this.downloadProgress.val++ + if (this.isDownloading.val) { + setTimeout(updateProgress, 50) + } + } + } + setTimeout(updateProgress, 100) } ParseProgress() { - return div({ class: 'vstack gap-3', hidden: () => this.totalCount.val == this.finishCount.val }, + return div({ class: 'vstack gap-3', hidden: () => this.totalCount.val == this.finishCount.val || this.isDownloading.val }, div({ class: 'text-center fs-5' }, () => `正在解析,剩余 ${this.totalCount.val - this.finishCount.val} 项`), div({ class: 'progress' }, div({ class: 'progress-bar progress-bar-striped progress-bar-animated', @@ -193,6 +218,16 @@ export class ParseModalComp implements VanComponent { ) } + DownloadProgress() { + return div({ class: 'vstack gap-3', hidden: () => !this.isDownloading.val }, + div({ class: 'text-center fs-5' }, () => `正在创建下载任务 (${this.downloadProgress.val}/${this.downloadTotal.val})`), + div({ class: 'progress' }, div({ + class: 'progress-bar progress-bar-striped progress-bar-animated', + style: () => `width: ${this.downloadProgress.val / this.downloadTotal.val * 100}%` + },)), + ) + } + ListGroup() { return () => div({ class: 'list-group', hidden: () => this.totalCount.val != this.finishCount.val }, this.allPlayInfo.val.filter(info => info.info)