下载页面卡主问题修复
This commit is contained in:
@@ -25,6 +25,12 @@ class DownloadExecutor {
|
||||
break;
|
||||
}
|
||||
|
||||
// 检查是否应该暂停
|
||||
if (this.shouldPause(task.id)) {
|
||||
console.log('任务已暂停,停止下载:', task.id);
|
||||
break;
|
||||
}
|
||||
|
||||
// 从图片对象中获取指定尺寸的URL
|
||||
const imageObj = images[index];
|
||||
let imageUrl;
|
||||
@@ -158,6 +164,12 @@ class DownloadExecutor {
|
||||
break;
|
||||
}
|
||||
|
||||
// 检查是否应该暂停
|
||||
if (this.shouldPause(task.id)) {
|
||||
console.log('批量下载任务已暂停,停止下载:', task.id);
|
||||
break;
|
||||
}
|
||||
|
||||
const batch = artworkIds.slice(i, i + concurrent);
|
||||
const batchPromises = batch.map(async artworkId => {
|
||||
try {
|
||||
@@ -503,6 +515,56 @@ class DownloadExecutor {
|
||||
const match = url.match(/\.([a-zA-Z0-9]+)(?:\?|$)/);
|
||||
return match ? match[1] : 'jpg';
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复暂停的任务
|
||||
*/
|
||||
async resumeTask(taskId) {
|
||||
const task = this.taskManager.getTask(taskId);
|
||||
if (!task) {
|
||||
throw new Error('任务不存在');
|
||||
}
|
||||
|
||||
if (task.status !== 'paused') {
|
||||
throw new Error('任务状态不是暂停状态');
|
||||
}
|
||||
|
||||
// 根据任务类型重新开始下载
|
||||
if (task.type === 'artwork') {
|
||||
// 重新获取作品信息和图片URL
|
||||
const artworkResult = await this.downloadService.artworkService.getArtwork(task.artwork_id);
|
||||
if (!artworkResult.success) {
|
||||
throw new Error(`获取作品信息失败: ${artworkResult.error}`);
|
||||
}
|
||||
|
||||
const imagesResult = await this.downloadService.artworkService.getArtworkImages(task.artwork_id, 'original');
|
||||
if (!imagesResult.success) {
|
||||
throw new Error(`获取图片URL失败: ${imagesResult.error}`);
|
||||
}
|
||||
|
||||
const artwork = artworkResult.data;
|
||||
const images = imagesResult.data.images;
|
||||
const artworkDir = await this.fileManager.getArtworkDirectory(artwork);
|
||||
|
||||
// 重新开始下载
|
||||
this.executeArtworkDownload(task, images, 'original', artworkDir, artwork);
|
||||
} else if (task.type === 'batch' || task.type === 'artist') {
|
||||
// 批量下载和作者下载的恢复逻辑
|
||||
// 这里需要根据具体实现来恢复
|
||||
console.log('恢复批量下载任务:', taskId);
|
||||
// TODO: 实现批量下载的恢复逻辑
|
||||
}
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查任务是否应该暂停
|
||||
*/
|
||||
shouldPause(taskId) {
|
||||
const task = this.taskManager.getTask(taskId);
|
||||
return task && task.status === 'paused';
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DownloadExecutor;
|
||||
|
||||
@@ -134,11 +134,17 @@ class DownloadService {
|
||||
return { success: false, error: '任务状态不是暂停状态' };
|
||||
}
|
||||
|
||||
// 更新任务状态为下载中
|
||||
await this.taskManager.updateTask(taskId, { status: 'downloading' });
|
||||
this.progressManager.notifyProgressUpdate(taskId, task);
|
||||
|
||||
// 通知进度更新
|
||||
const updatedTask = this.taskManager.getTask(taskId);
|
||||
this.progressManager.notifyProgressUpdate(taskId, updatedTask);
|
||||
|
||||
// 重新开始下载
|
||||
return this.downloadArtwork(task.artwork_id, { skipExisting: false });
|
||||
// 重新开始下载执行
|
||||
this.downloadExecutor.resumeTask(taskId);
|
||||
|
||||
return { success: true, data: updatedTask };
|
||||
}
|
||||
|
||||
// 代理方法 - 历史记录管理
|
||||
|
||||
+100
-11
@@ -62,7 +62,10 @@
|
||||
<div class="task-header">
|
||||
<div class="task-info">
|
||||
<h3 class="task-title">
|
||||
{{ getTaskTitle(task) }}
|
||||
<a v-if="task.artwork_id" :href="`/artwork/${task.artwork_id}`" class="task-link">
|
||||
{{ getTaskTitle(task) }}
|
||||
</a>
|
||||
<span v-else>{{ getTaskTitle(task) }}</span>
|
||||
</h3>
|
||||
<span class="task-status" :class="task.status">
|
||||
{{ getStatusText(task.status) }}
|
||||
@@ -72,6 +75,12 @@
|
||||
<button v-if="task.status === 'downloading'" @click="cancelTask(task.id)" class="btn btn-danger btn-sm">
|
||||
取消
|
||||
</button>
|
||||
<button v-if="task.status === 'paused'" @click="resumeTask(task.id)" class="btn btn-primary btn-sm">
|
||||
恢复
|
||||
</button>
|
||||
<button v-if="task.status === 'paused'" @click="cancelTask(task.id)" class="btn btn-danger btn-sm">
|
||||
删除
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -205,7 +214,7 @@ const history = ref<any[]>([]);
|
||||
// SSE连接管理
|
||||
const sseConnections = ref<Map<string, () => void>>(new Map());
|
||||
|
||||
// 计算属性:只显示活跃任务
|
||||
// 计算属性:显示活跃任务和暂停任务
|
||||
const activeTasks = computed(() => {
|
||||
return tasks.value.filter(task =>
|
||||
['downloading', 'paused'].includes(task.status)
|
||||
@@ -298,9 +307,9 @@ const fetchTasks = async () => {
|
||||
if (response.success) {
|
||||
tasks.value = response.data || [];
|
||||
|
||||
// 为活跃任务建立SSE连接
|
||||
// 只为正在下载的任务建立SSE连接,避免为暂停任务建立连接
|
||||
activeTasks.value.forEach(task => {
|
||||
if (!sseConnections.value.has(task.id)) {
|
||||
if (task.status === 'downloading' && !sseConnections.value.has(task.id)) {
|
||||
startTaskStreaming(task.id);
|
||||
}
|
||||
});
|
||||
@@ -337,6 +346,12 @@ const startTaskStreaming = (taskId: string) => {
|
||||
|
||||
console.log('开始SSE监听任务进度:', taskId);
|
||||
|
||||
// 添加超时处理
|
||||
const timeoutId = setTimeout(() => {
|
||||
console.warn('SSE连接超时,关闭连接:', taskId);
|
||||
stopTaskStreaming(taskId);
|
||||
}, 30000); // 30秒超时
|
||||
|
||||
const closeConnection = downloadService.streamTaskProgress(
|
||||
taskId,
|
||||
(task) => {
|
||||
@@ -348,25 +363,31 @@ const startTaskStreaming = (taskId: string) => {
|
||||
total: task.total_files
|
||||
});
|
||||
|
||||
// 清除超时
|
||||
clearTimeout(timeoutId);
|
||||
|
||||
// 更新任务状态
|
||||
const index = tasks.value.findIndex(t => t.id === taskId);
|
||||
if (index !== -1) {
|
||||
tasks.value[index] = task;
|
||||
}
|
||||
|
||||
// 如果任务完成,清理连接
|
||||
if (['completed', 'failed', 'cancelled', 'partial'].includes(task.status)) {
|
||||
console.log('任务完成,关闭SSE连接:', taskId);
|
||||
// 如果任务完成或暂停,清理连接
|
||||
if (['completed', 'failed', 'cancelled', 'partial', 'paused'].includes(task.status)) {
|
||||
console.log('任务状态变更,关闭SSE连接:', taskId);
|
||||
stopTaskStreaming(taskId);
|
||||
|
||||
// 延迟刷新历史记录
|
||||
setTimeout(() => {
|
||||
fetchHistory();
|
||||
}, 1000);
|
||||
if (['completed', 'failed', 'cancelled', 'partial'].includes(task.status)) {
|
||||
setTimeout(() => {
|
||||
fetchHistory();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
},
|
||||
() => {
|
||||
console.log('SSE连接完成:', taskId);
|
||||
clearTimeout(timeoutId);
|
||||
stopTaskStreaming(taskId);
|
||||
}
|
||||
);
|
||||
@@ -382,11 +403,35 @@ const stopTaskStreaming = (taskId: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 管理SSE连接
|
||||
const manageSSEConnections = () => {
|
||||
// 清理不需要的连接
|
||||
const currentTaskIds = new Set(activeTasks.value.map(task => task.id));
|
||||
|
||||
// 关闭已不存在的任务的连接
|
||||
sseConnections.value.forEach((closeConnection, taskId) => {
|
||||
if (!currentTaskIds.has(taskId)) {
|
||||
console.log('清理已不存在的任务连接:', taskId);
|
||||
closeConnection();
|
||||
sseConnections.value.delete(taskId);
|
||||
}
|
||||
});
|
||||
|
||||
// 为正在下载的任务建立连接
|
||||
activeTasks.value.forEach(task => {
|
||||
if (task.status === 'downloading' && !sseConnections.value.has(task.id)) {
|
||||
startTaskStreaming(task.id);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 取消任务
|
||||
const cancelTask = async (taskId: string) => {
|
||||
try {
|
||||
const response = await downloadService.cancelTask(taskId);
|
||||
if (response.success) {
|
||||
// 立即停止SSE连接
|
||||
stopTaskStreaming(taskId);
|
||||
await fetchTasks();
|
||||
} else {
|
||||
throw new Error(response.error || '取消任务失败');
|
||||
@@ -397,6 +442,23 @@ const cancelTask = async (taskId: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 恢复任务
|
||||
const resumeTask = async (taskId: string) => {
|
||||
try {
|
||||
const response = await downloadService.resumeTask(taskId);
|
||||
if (response.success) {
|
||||
await fetchTasks();
|
||||
// 重新管理SSE连接
|
||||
manageSSEConnections();
|
||||
} else {
|
||||
throw new Error(response.error || '恢复任务失败');
|
||||
}
|
||||
} catch (err) {
|
||||
error.value = err instanceof Error ? err.message : '恢复任务失败';
|
||||
console.error('恢复任务失败:', err);
|
||||
}
|
||||
};
|
||||
|
||||
// 清理历史记录
|
||||
const cleanupHistory = async () => {
|
||||
if (confirm('确定要清理下载历史吗?这将保留最新的500条记录。')) {
|
||||
@@ -455,7 +517,16 @@ const cleanupSSEConnections = () => {
|
||||
onMounted(async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
await refreshData();
|
||||
// 先获取数据,不阻塞页面渲染
|
||||
await Promise.all([
|
||||
fetchTasks(),
|
||||
fetchHistory()
|
||||
]);
|
||||
|
||||
// 数据加载完成后,异步管理SSE连接
|
||||
setTimeout(() => {
|
||||
manageSSEConnections();
|
||||
}, 100);
|
||||
} catch (err) {
|
||||
error.value = err instanceof Error ? err.message : '加载数据失败';
|
||||
} finally {
|
||||
@@ -662,6 +733,17 @@ onUnmounted(() => {
|
||||
margin: 0 0 0.25rem 0;
|
||||
}
|
||||
|
||||
.task-title .task-link {
|
||||
color: #3b82f6;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.task-title .task-link:hover {
|
||||
color: #2563eb;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.task-status,
|
||||
.history-status {
|
||||
display: inline-block;
|
||||
@@ -707,6 +789,13 @@ onUnmounted(() => {
|
||||
color: #d97706;
|
||||
}
|
||||
|
||||
.task-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.task-progress {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user