/** * 任务中心页面 * 展示用户的所有任务(生成任务、素材导入等),支持状态筛选、类型筛选、分页、重试 */ import { useState } from "react" import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query" import { Table, Tabs, Select, Tag, Button, message, Popconfirm, Tooltip } from "antd" import { CheckCircleOutlined, ClockCircleOutlined, SyncOutlined, CloseCircleOutlined, ExclamationCircleOutlined, MinusCircleOutlined, RedoOutlined, InfoCircleOutlined, } from "@ant-design/icons" import type { ColumnsType } from "antd/es/table" import { getTasks, retryTask, type TaskItem, type TaskStatus, type TaskListParams, } from "@/api/tasks" import "./tasks.css" /* ──────────── 常量 ──────────── */ /** 状态 Tab 配置 */ const STATUS_TABS: { key: TaskStatus | "all"; label: string }[] = [ { key: "all", label: "全部" }, { key: "waiting", label: "等待中" }, { key: "running", label: "进行中" }, { key: "completed", label: "已完成" }, { key: "failed", label: "失败" }, { key: "cancelled", label: "已取消" }, ] /** 类型筛选选项 */ const TYPE_OPTIONS = [ { value: "all", label: "全部类型" }, { value: "generation", label: "生成任务" }, { value: "ingest", label: "素材导入" }, ] /** 状态标签配置 */ const STATUS_CONFIG: Record = { pending: { label: "等待中", color: "default", icon: , }, waiting: { label: "排队中", color: "processing", icon: , }, running: { label: "进行中", color: "processing", icon: , }, completed: { label: "已完成", color: "success", icon: , }, failed: { label: "失败", color: "error", icon: , }, cancelled: { label: "已取消", color: "default", icon: , }, } /** 任务类型标签 */ const TYPE_LABELS: Record = { generation: { label: "生成任务", color: "blue" }, ingest: { label: "素材导入", color: "green" }, } /* ──────────── 工具函数 ──────────── */ /** 格式化耗时 */ const formatDuration = (seconds?: number): string => { if (!seconds) return "-" if (seconds < 60) return `${Math.round(seconds)}秒` const minutes = Math.floor(seconds / 60) const secs = Math.round(seconds % 60) if (minutes < 60) return `${minutes}分${secs}秒` const hours = Math.floor(minutes / 60) const mins = minutes % 60 return `${hours}小时${mins}分` } /** 格式化时间 */ const formatTime = (dateStr?: string | null): string => { if (!dateStr) return "-" const date = new Date(dateStr) return date.toLocaleString("zh-CN", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", }) } /* ──────────── 主组件 ──────────── */ export default function TaskCenter() { const queryClient = useQueryClient() // 筛选状态 const [statusFilter, setStatusFilter] = useState("all") const [typeFilter, setTypeFilter] = useState("all") const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(20) const [expandedTaskId, setExpandedTaskId] = useState(null) const [expandedTaskDetail, setExpandedTaskDetail] = useState(null) // 查询参数 const queryParams: TaskListParams = { page, page_size: pageSize, ...(statusFilter !== "all" && { status: statusFilter }), ...(typeFilter !== "all" && { task_type: typeFilter }), } // 获取任务列表 const { data, isLoading, error } = useQuery({ queryKey: ["tasks", queryParams], queryFn: () => getTasks(queryParams), refetchInterval: (query) => { // 有进行中的任务时自动刷新 const tasks = query.state.data?.items ?? [] const hasRunning = tasks.some((t) => t.status === "running" || t.status === "waiting") return hasRunning ? 5000 : false }, }) // 重试任务 const retryMutation = useMutation({ mutationFn: retryTask, onSuccess: () => { message.success("任务已重新提交") queryClient.invalidateQueries({ queryKey: ["tasks"] }) }, onError: () => { message.error("重试失败,请检查任务状态") }, }) // 展开查看详情 const handleExpand = async (expanded: boolean, record: TaskItem) => { if (!expanded) { setExpandedTaskId(null) setExpandedTaskDetail(null) return } setExpandedTaskId(record.id) // 如果是失败任务,获取详情(含 error_info) if (record.status === "failed" && record.error_info) { setExpandedTaskDetail(record) } } // 表格列定义 const columns: ColumnsType = [ { title: "任务ID", dataIndex: "id", key: "id", width: 120, ellipsis: true, render: (id: string) => ( {id.slice(0, 8)}... ), }, { title: "类型", dataIndex: "task_type", key: "task_type", width: 100, render: (type: string) => { const config = TYPE_LABELS[type] || { label: type, color: "default" } return {config.label} }, }, { title: "状态", dataIndex: "status", key: "status", width: 120, render: (status: TaskStatus, record: TaskItem) => { const config = STATUS_CONFIG[status] || { label: status, color: "default", icon: null, } return ( {config.label} {status === "running" && record.progress > 0 && ( {record.progress}% )} ) }, }, { title: "当前步骤", dataIndex: "current_step", key: "current_step", width: 150, ellipsis: true, render: (step: string) => {step || "-"}, }, { title: "耗时", dataIndex: "duration_seconds", key: "duration_seconds", width: 100, render: (seconds: number) => {formatDuration(seconds)}, }, { title: "创建时间", dataIndex: "created_at", key: "created_at", width: 120, render: (time: string) => {formatTime(time)}, }, { title: "操作", key: "action", width: 100, fixed: "right", render: (_: unknown, record: TaskItem) => { if (record.status === "failed" && record.retryable) { return ( retryMutation.mutate(record.id)} okText="确定" cancelText="取消" > ) } if (record.status === "failed") { return ( ) } return - }, }, ] // 展开行渲染(错误详情) const expandedRowRender = (record: TaskItem) => { const detail = expandedTaskDetail || record const errorInfo = detail.error_info if (!errorInfo && !detail.error_message) { return
暂无错误详情
} return (
错误详情
{errorInfo?.error_type && (
错误类型: {errorInfo.error_type}
)} {(errorInfo?.error_message || detail.error_message) && (
错误信息: {errorInfo?.error_message || detail.error_message}
)} {errorInfo?.failed_step && (
失败阶段: {errorInfo.failed_step}
)} {errorInfo?.stack_trace && (
堆栈信息:
{errorInfo.stack_trace}
)}
) } // 错误处理 if (error) { return (

加载任务列表失败

) } return (
{/* 页面标题 */}

任务中心

查看和管理所有生成任务与素材导入任务

{/* 筛选栏 */}
{/* 状态 Tab */} { setStatusFilter(key as TaskStatus | "all") setPage(1) }} items={STATUS_TABS.map((tab) => ({ key: tab.key, label: tab.label, }))} className="task-status-tabs" /> {/* 类型筛选 */}