下载模组更新,新增下载组件,下载监听改为全局,全量改为增量监听
This commit is contained in:
@@ -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
|
||||
};
|
||||
});
|
||||
Reference in New Issue
Block a user