diff --git a/ui/src/components/common/DownloadProgressWidget.vue b/ui/src/components/common/DownloadProgressWidget.vue index 58d9f2d..1bda39e 100644 --- a/ui/src/components/common/DownloadProgressWidget.vue +++ b/ui/src/components/common/DownloadProgressWidget.vue @@ -151,7 +151,10 @@ const getStatusText = (status: string) => { 'failed': '失败', 'cancelled': '已取消', 'partial': '部分完成', - 'paused': '已暂停' + 'paused': '已暂停', + 'pausing': '暂停中', + 'resuming': '恢复中', + 'cancelling': '取消中' }; return statusMap[status] || status; }; diff --git a/ui/src/components/download/DownloadProgress.vue b/ui/src/components/download/DownloadProgress.vue index 386d47c..75fe268 100644 --- a/ui/src/components/download/DownloadProgress.vue +++ b/ui/src/components/download/DownloadProgress.vue @@ -3,15 +3,17 @@

{{ getTaskTitle(task) }}

- - -
@@ -74,7 +76,7 @@ diff --git a/ui/src/stores/download.ts b/ui/src/stores/download.ts index 3ea11a5..eac1e94 100644 --- a/ui/src/stores/download.ts +++ b/ui/src/stores/download.ts @@ -156,7 +156,22 @@ export const useDownloadStore = defineStore('download', () => { // 更新任务状态 const index = tasks.value.findIndex(t => t.id === taskId); if (index !== -1) { - tasks.value[index] = task; + // 保留临时状态(如pausing, resuming, cancelling) + const currentTask = tasks.value[index]; + const isTemporaryStatus = ['pausing', 'resuming', 'cancelling'].includes(currentTask.status); + + if (!isTemporaryStatus) { + tasks.value[index] = task; + } else { + // 只更新进度相关字段,保留临时状态 + tasks.value[index] = { + ...currentTask, + progress: task.progress, + completed_files: task.completed_files, + failed_files: task.failed_files, + recent_completed: task.recent_completed + }; + } } else { // 如果是新任务,添加到列表 tasks.value.push(task); @@ -285,18 +300,42 @@ export const useDownloadStore = defineStore('download', () => { // 取消任务 const cancelTask = async (taskId: string) => { try { + // 立即停止SSE连接 + stopTaskStreaming(taskId); + + // 立即更新本地状态为取消中 + updateTask(taskId, { status: 'cancelling' as any }); + const response = await downloadService.cancelTask(taskId); if (response.success) { - // 立即停止SSE连接 - stopTaskStreaming(taskId); - await fetchTasks(); + // 立即从任务列表中移除 + removeTask(taskId); + // 异步刷新任务列表以确保同步 + setTimeout(() => fetchTasks(), 500); } else { + // 如果取消失败,恢复原状态 + await fetchTasks(); throw new Error(response.error || '取消任务失败'); } } catch (err) { - error.value = err instanceof Error ? err.message : '取消任务失败'; + // 如果是网络错误或超时,提供更友好的错误信息 + let errorMessage = '取消任务失败'; + if (err instanceof Error) { + if (err.message.includes('timeout') || err.message.includes('network')) { + errorMessage = '网络连接超时,请检查网络连接后重试'; + } else if (err.message.includes('404')) { + errorMessage = '任务不存在或已被删除'; + } else { + errorMessage = err.message; + } + } + + error.value = errorMessage; console.error('取消任务失败:', err); - throw err; + + // 恢复任务状态 + await fetchTasks(); + throw new Error(errorMessage); } }; @@ -305,6 +344,10 @@ export const useDownloadStore = defineStore('download', () => { try { // 获取任务信息以确定类型 const task = getTask(taskId); + + // 立即更新本地状态为恢复中 + updateTask(taskId, { status: 'resuming' as any }); + let response; // 判断是否为批量下载任务(batch、artist、art类型都是批量下载) @@ -317,16 +360,38 @@ export const useDownloadStore = defineStore('download', () => { } if (response.success) { - await fetchTasks(); - // 重新管理SSE连接 - manageSSEConnections(); + // 立即更新状态为下载中 + updateTask(taskId, { status: 'downloading' }); + // 立即建立SSE连接 + startTaskStreaming(taskId); + // 异步刷新任务列表以确保同步 + setTimeout(() => fetchTasks(), 500); } else { + // 如果恢复失败,恢复原状态 + await fetchTasks(); throw new Error(response.error || '恢复任务失败'); } } catch (err) { - error.value = err instanceof Error ? err.message : '恢复任务失败'; + // 提供更友好的错误信息 + let errorMessage = '恢复任务失败'; + if (err instanceof Error) { + if (err.message.includes('timeout') || err.message.includes('network')) { + errorMessage = '网络连接超时,请检查网络连接后重试'; + } else if (err.message.includes('404')) { + errorMessage = '任务不存在或已被删除'; + } else if (err.message.includes('already running')) { + errorMessage = '任务已在运行中'; + } else { + errorMessage = err.message; + } + } + + error.value = errorMessage; console.error('恢复任务失败:', err); - throw err; + + // 恢复任务状态 + await fetchTasks(); + throw new Error(errorMessage); } }; @@ -335,6 +400,10 @@ export const useDownloadStore = defineStore('download', () => { try { // 获取任务信息以确定类型 const task = getTask(taskId); + + // 立即更新本地状态为暂停中 + updateTask(taskId, { status: 'pausing' as any }); + let response; // 判断是否为批量下载任务(batch、artist、art类型都是批量下载) @@ -347,14 +416,38 @@ export const useDownloadStore = defineStore('download', () => { } if (response.success) { - await fetchTasks(); + // 立即更新状态为已暂停 + updateTask(taskId, { status: 'paused' }); + // 停止SSE连接 + stopTaskStreaming(taskId); + // 异步刷新任务列表以确保同步 + setTimeout(() => fetchTasks(), 500); } else { + // 如果暂停失败,恢复原状态 + await fetchTasks(); throw new Error(response.error || '暂停任务失败'); } } catch (err) { - error.value = err instanceof Error ? err.message : '暂停任务失败'; + // 提供更友好的错误信息 + let errorMessage = '暂停任务失败'; + if (err instanceof Error) { + if (err.message.includes('timeout') || err.message.includes('network')) { + errorMessage = '网络连接超时,请检查网络连接后重试'; + } else if (err.message.includes('404')) { + errorMessage = '任务不存在或已被删除'; + } else if (err.message.includes('already paused')) { + errorMessage = '任务已暂停'; + } else { + errorMessage = err.message; + } + } + + error.value = errorMessage; console.error('暂停任务失败:', err); - throw err; + + // 恢复任务状态 + await fetchTasks(); + throw new Error(errorMessage); } }; diff --git a/ui/src/types/index.ts b/ui/src/types/index.ts index 64138a2..fd05278 100644 --- a/ui/src/types/index.ts +++ b/ui/src/types/index.ts @@ -99,7 +99,7 @@ export interface LoginStatus { export interface DownloadTask { id: string; type: 'artwork' | 'batch' | 'artist' | 'art'; - status: 'downloading' | 'completed' | 'failed' | 'partial' | 'cancelled' | 'paused'; + status: 'downloading' | 'completed' | 'failed' | 'partial' | 'cancelled' | 'paused' | 'pausing' | 'resuming' | 'cancelling'; progress: number; total_files: number; completed_files: number; diff --git a/ui/src/views/DownloadsView.vue b/ui/src/views/DownloadsView.vue index 2eb5ee8..04f3697 100644 --- a/ui/src/views/DownloadsView.vue +++ b/ui/src/views/DownloadsView.vue @@ -62,17 +62,21 @@
- - - -
@@ -242,7 +246,10 @@ const getStatusText = (status: string) => { 'failed': '失败', 'cancelled': '已取消', 'partial': '部分完成', - 'paused': '已暂停' + 'paused': '已暂停', + 'pausing': '暂停中', + 'resuming': '恢复中', + 'cancelling': '取消中' }; return statusMap[status] || status; }; @@ -360,6 +367,10 @@ const cancelTask = async (taskId: string) => { } catch (err) { error.value = err instanceof Error ? err.message : '取消任务失败'; console.error('取消任务失败:', err); + // 显示用户友好的错误提示 + if (err instanceof Error) { + alert(`取消失败: ${err.message}`); + } } }; @@ -370,6 +381,10 @@ const resumeTask = async (taskId: string) => { } catch (err) { error.value = err instanceof Error ? err.message : '恢复任务失败'; console.error('恢复任务失败:', err); + // 显示用户友好的错误提示 + if (err instanceof Error) { + alert(`恢复失败: ${err.message}`); + } } }; @@ -380,6 +395,10 @@ const pauseTask = async (taskId: string) => { } catch (err) { error.value = err instanceof Error ? err.message : '暂停任务失败'; console.error('暂停任务失败:', err); + // 显示用户友好的错误提示 + if (err instanceof Error) { + alert(`暂停失败: ${err.message}`); + } } };