198 lines
5.0 KiB
JavaScript
198 lines
5.0 KiB
JavaScript
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
|
|
}
|
|
})
|