import { defineStore } from 'pinia' import { computed, ref } from 'vue' import { message } from 'ant-design-vue' import { taskerApi } from '@/apis/tasker' import { parseToShanghai } from '@/utils/time' const ACTIVE_STATUSES = new Set(['pending', 'running', 'queued']) const FAILED_STATUSES = new Set(['failed', 'cancelled']) const createDefaultSummary = () => ({ total: 0, filtered_total: 0, status_counts: {}, type_counts: {} }) const toTask = (raw = {}) => ({ id: raw.id, name: raw.name || '后台任务', type: raw.type || 'general', status: raw.status || 'pending', progress: raw.progress ?? 0, message: raw.message || '', created_at: raw.created_at, updated_at: raw.updated_at, started_at: raw.started_at, completed_at: raw.completed_at, payload: raw.payload || {}, result: raw.result, error: raw.error, cancel_requested: raw.cancel_requested || false }) export const useTaskerStore = defineStore('tasker', () => { const tasks = ref([]) const loading = ref(false) const lastError = ref(null) const isPolling = ref(false) const isDrawerOpen = ref(false) const summary = ref(createDefaultSummary()) let pollingTimer = null const sortedTasks = computed(() => { return [...tasks.value].sort((a, b) => { const timeA = parseToShanghai(a.created_at) const timeB = parseToShanghai(b.created_at) if (!timeA && !timeB) return 0 if (!timeA) return 1 if (!timeB) return -1 return timeB.valueOf() - timeA.valueOf() }) }) const statusCounts = computed(() => summary.value?.status_counts || {}) const activeCount = computed(() => Array.from(ACTIVE_STATUSES).reduce((count, status) => count + (statusCounts.value?.[status] || 0), 0) ) const failedCount = computed(() => Array.from(FAILED_STATUSES).reduce((count, status) => count + (statusCounts.value?.[status] || 0), 0) ) const successCount = computed(() => statusCounts.value?.success || 0) const totalCount = computed(() => summary.value?.total || 0) function upsertTask(rawTask) { if (!rawTask || !rawTask.id) return const task = toTask(rawTask) const index = tasks.value.findIndex(item => item.id === task.id) if (index >= 0) { tasks.value.splice(index, 1, { ...tasks.value[index], ...task }) } else { tasks.value.unshift(task) } } async function loadTasks(params = {}) { loading.value = true lastError.value = null try { const response = await taskerApi.fetchTasks(params) const taskList = response?.tasks || [] summary.value = { ...createDefaultSummary(), ...(response?.summary || {}) } tasks.value = taskList.map(toTask) } catch (error) { console.error('加载任务列表失败', error) lastError.value = error summary.value = createDefaultSummary() } finally { loading.value = false } } async function refreshTask(taskId) { if (!taskId) return try { const response = await taskerApi.fetchTaskDetail(taskId) if (response?.task) { upsertTask(response.task) } } catch (error) { console.error(`刷新任务 ${taskId} 详情失败`, error) lastError.value = error } } async function cancelTask(taskId) { if (!taskId) return try { await taskerApi.cancelTask(taskId) message.success('取消任务成功') await refreshTask(taskId) } catch (error) { console.error(`取消任务 ${taskId} 失败`, error) message.error(error?.message || '取消任务失败') } } function registerQueuedTask({ task_id, name, task_type, message: msg, payload } = {}) { if (!task_id) return const now = new Date().toISOString() upsertTask({ id: task_id, name: name || '后台任务', type: task_type || 'manual', status: 'queued', progress: 0, message: msg || '任务已排队', created_at: now, updated_at: now, payload: payload || {} }) } function openDrawer() { isDrawerOpen.value = true } function closeDrawer() { isDrawerOpen.value = false } function toggleDrawer() { isDrawerOpen.value = !isDrawerOpen.value } function startPolling(interval = 5000) { if (pollingTimer) return isPolling.value = true pollingTimer = setInterval(() => { loadTasks() }, interval) } function stopPolling() { if (pollingTimer) { clearInterval(pollingTimer) pollingTimer = null } isPolling.value = false } function reset() { stopPolling() tasks.value = [] lastError.value = null isDrawerOpen.value = false summary.value = createDefaultSummary() } return { isDrawerOpen, tasks, sortedTasks, summary, statusCounts, totalCount, successCount, failedCount, loading, lastError, activeCount, isPolling, loadTasks, refreshTask, cancelTask, registerQueuedTask, startPolling, stopPolling, reset, openDrawer, closeDrawer, toggleDrawer } })