下载模组更新,新增下载组件,下载监听改为全局,全量改为增量监听

This commit is contained in:
2025-08-31 06:41:46 +08:00
parent aa04f9d03f
commit ad5dfc64cb
17 changed files with 1662 additions and 285 deletions
+413
View File
@@ -0,0 +1,413 @@
import { defineStore } from 'pinia';
import { ref, computed } from 'vue';
import downloadService from '@/services/download';
import type { DownloadTask } from '@/types';
export const useDownloadStore = defineStore('download', () => {
// 状态
const tasks = ref<DownloadTask[]>([]);
const loading = ref(false);
const error = ref<string | null>(null);
const lastUpdate = ref<number>(0);
// SSE连接管理
const sseConnections = ref<Map<string, () => void>>(new Map());
// 计算属性:显示活跃任务和暂停任务
const activeTasks = computed(() => {
return tasks.value.filter(task =>
['downloading', 'paused'].includes(task.status)
);
});
// 计算属性:正在下载的任务
const downloadingTasks = computed(() => {
return tasks.value.filter(task => task.status === 'downloading');
});
// 计算属性:暂停的任务
const pausedTasks = computed(() => {
return tasks.value.filter(task => task.status === 'paused');
});
// 获取指定任务
const getTask = (taskId: string) => {
return tasks.value.find(task => task.id === taskId) || null;
};
// 获取指定作品的任务
const getArtworkTask = (artworkId: number) => {
return tasks.value.find(task =>
task.artwork_id === artworkId &&
['downloading', 'paused'].includes(task.status)
) || null;
};
// 获取任务列表(优化版本)
const fetchTasks = async () => {
try {
loading.value = true;
error.value = null;
// 使用增量更新API
const response = await downloadService.getTasksChanges(lastUpdate.value);
if (response.success) {
const { tasks: changedTasks, lastUpdate: newLastUpdate } = response.data;
// 更新任务列表
changedTasks.forEach((changedTask: DownloadTask) => {
const index = tasks.value.findIndex((t: DownloadTask) => t.id === changedTask.id);
if (index !== -1) {
tasks.value[index] = changedTask;
} else {
tasks.value.push(changedTask);
}
});
lastUpdate.value = newLastUpdate;
// 管理SSE连接
manageSSEConnections();
} else {
throw new Error(response.error || '获取任务列表失败');
}
} catch (err) {
error.value = err instanceof Error ? err.message : '获取任务列表失败';
console.error('获取任务列表失败:', err);
} finally {
loading.value = false;
}
};
// 获取活跃任务(轻量级)
const fetchActiveTasks = async () => {
try {
const response = await downloadService.getActiveTasks();
if (response.success) {
// 只更新活跃任务
const activeTaskIds = new Set(response.data.map((t: DownloadTask) => t.id));
// 移除已完成的活跃任务
tasks.value = tasks.value.filter((task: DownloadTask) =>
!activeTaskIds.has(task.id) || ['downloading', 'paused'].includes(task.status)
);
// 更新或添加活跃任务
response.data.forEach((activeTask: DownloadTask) => {
const index = tasks.value.findIndex((t: DownloadTask) => t.id === activeTask.id);
if (index !== -1) {
tasks.value[index] = activeTask;
} else {
tasks.value.push(activeTask);
}
});
// 管理SSE连接
manageSSEConnections();
}
} catch (err) {
console.error('获取活跃任务失败:', err);
}
};
// 获取任务摘要(用于快速状态检查)
const fetchTasksSummary = async () => {
try {
const response = await downloadService.getTasksSummary();
if (response.success) {
// 可以用于快速检查是否有新任务完成
return response.data;
}
} catch (err) {
console.error('获取任务摘要失败:', err);
}
return null;
};
// 开始SSE监听任务进度
const startTaskStreaming = (taskId: string) => {
// 如果已经有连接,先关闭
if (sseConnections.value.has(taskId)) {
sseConnections.value.get(taskId)!();
}
console.log('开始SSE监听任务进度:', taskId);
// 添加超时处理
const timeoutId = setTimeout(() => {
console.warn('SSE连接超时,关闭连接:', taskId);
stopTaskStreaming(taskId);
}, 30000); // 30秒超时
const closeConnection = downloadService.streamTaskProgress(
taskId,
(task) => {
// console.log('收到SSE进度更新:', {
// taskId,
// status: task.status,
// progress: task.progress,
// completed: task.completed_files,
// total: task.total_files
// });
// 清除超时
clearTimeout(timeoutId);
// 更新任务状态
const index = tasks.value.findIndex(t => t.id === taskId);
if (index !== -1) {
tasks.value[index] = task;
} else {
// 如果是新任务,添加到列表
tasks.value.push(task);
}
// 如果任务完成或暂停,清理连接
if (['completed', 'failed', 'cancelled', 'partial', 'paused'].includes(task.status)) {
console.log('任务状态变更,关闭SSE连接:', taskId);
stopTaskStreaming(taskId);
}
},
() => {
console.log('SSE连接完成:', taskId);
clearTimeout(timeoutId);
stopTaskStreaming(taskId);
}
);
sseConnections.value.set(taskId, closeConnection);
};
// 停止SSE监听
const stopTaskStreaming = (taskId: string) => {
if (sseConnections.value.has(taskId)) {
sseConnections.value.get(taskId)!();
sseConnections.value.delete(taskId);
}
};
// 管理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);
}
});
};
// 清理所有SSE连接
const cleanupSSEConnections = () => {
sseConnections.value.forEach(closeConnection => {
closeConnection();
});
sseConnections.value.clear();
};
// 定期刷新任务列表
let refreshInterval: number | null = null;
let summaryInterval: number | null = null;
const startRefreshInterval = () => {
if (refreshInterval) return;
// 主要刷新:每5秒获取活跃任务(轻量级)
refreshInterval = window.setInterval(() => {
fetchActiveTasks();
}, 5000);
// 摘要检查:每30秒检查一次任务摘要,如果有变化则获取详细信息
summaryInterval = window.setInterval(async () => {
const summary = await fetchTasksSummary();
if (summary && summary.active > 0) {
// 如果有活跃任务,确保获取最新状态
fetchActiveTasks();
}
}, 30000);
};
const stopRefreshInterval = () => {
if (refreshInterval) {
clearInterval(refreshInterval);
refreshInterval = null;
}
if (summaryInterval) {
clearInterval(summaryInterval);
summaryInterval = null;
}
};
// 添加新任务(用于立即显示)
const addTask = (task: DownloadTask) => {
// 检查是否已存在
const existingIndex = tasks.value.findIndex(t => t.id === task.id);
if (existingIndex !== -1) {
tasks.value[existingIndex] = task;
} else {
tasks.value.push(task);
}
// 如果是下载中的任务,立即建立SSE连接
if (task.status === 'downloading') {
startTaskStreaming(task.id);
}
};
// 更新任务状态
const updateTask = (taskId: string, updates: Partial<DownloadTask>) => {
const index = tasks.value.findIndex(t => t.id === taskId);
if (index !== -1) {
tasks.value[index] = { ...tasks.value[index], ...updates };
}
};
// 移除任务
const removeTask = (taskId: string) => {
const index = tasks.value.findIndex(t => t.id === taskId);
if (index !== -1) {
tasks.value.splice(index, 1);
}
stopTaskStreaming(taskId);
};
// 取消任务
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 || '取消任务失败');
}
} catch (err) {
error.value = err instanceof Error ? err.message : '取消任务失败';
console.error('取消任务失败:', err);
throw err;
}
};
// 恢复任务
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);
throw err;
}
};
// 暂停任务
const pauseTask = async (taskId: string) => {
try {
const response = await downloadService.pauseTask(taskId);
if (response.success) {
await fetchTasks();
} else {
throw new Error(response.error || '暂停任务失败');
}
} catch (err) {
error.value = err instanceof Error ? err.message : '暂停任务失败';
console.error('暂停任务失败:', err);
throw err;
}
};
// 清理已完成的任务
const cleanupCompletedTasks = async (keepCount = 100) => {
try {
const response = await downloadService.cleanupTasks(true, keepCount);
if (response.success) {
await fetchTasks();
} else {
throw new Error(response.error || '清理任务失败');
}
} catch (err) {
error.value = err instanceof Error ? err.message : '清理任务失败';
console.error('清理任务失败:', err);
throw err;
}
};
// 清理历史记录
const cleanupHistory = async (keepCount = 500) => {
try {
const response = await downloadService.cleanupHistory(keepCount);
if (response.success) {
// 历史记录清理不影响当前任务状态
return response.data;
} else {
throw new Error(response.error || '清理历史失败');
}
} catch (err) {
error.value = err instanceof Error ? err.message : '清理历史失败';
console.error('清理历史失败:', err);
throw err;
}
};
// 清除错误
const clearError = () => {
error.value = null;
};
return {
// 状态
tasks,
loading,
error,
lastUpdate,
// 计算属性
activeTasks,
downloadingTasks,
pausedTasks,
// 方法
getTask,
getArtworkTask,
fetchTasks,
fetchActiveTasks,
fetchTasksSummary,
addTask,
updateTask,
removeTask,
cancelTask,
resumeTask,
pauseTask,
cleanupCompletedTasks,
cleanupHistory,
clearError,
// SSE管理
startTaskStreaming,
stopTaskStreaming,
manageSSEConnections,
cleanupSSEConnections,
// 定期刷新管理
startRefreshInterval,
stopRefreshInterval
};
});