diff --git a/apps/web/src/pages/tasks/components/TaskTable.tsx b/apps/web/src/pages/tasks/components/TaskTable.tsx old mode 100644 new mode 100755 index 84a331f0e..fb751fc25 --- a/apps/web/src/pages/tasks/components/TaskTable.tsx +++ b/apps/web/src/pages/tasks/components/TaskTable.tsx @@ -1,11 +1,8 @@ import React from "react" -import { Table, Tag, Button, Popconfirm, Tooltip } from "antd" -import { RedoOutlined, InfoCircleOutlined, ClockCircleOutlined } from "@ant-design/icons" -import type { ColumnsType } from "antd/es/table" -import type { TaskItem, TaskStatus } from "@/api/tasks" -import { STATUS_CONFIG, TYPE_LABELS } from "../constants" -import { formatDuration, formatTime } from "../utils" +import { Table } from "antd" +import type { TaskItem } from "@/api/tasks" import { TaskErrorDetail } from "./TaskErrorDetail" +import { useTaskTableColumns, TaskEmptyState } from "./task-table" interface TaskTableProps { dataSource: TaskItem[] @@ -24,7 +21,6 @@ interface TaskTableProps { /** * 任务列表表格 - * 含列定义、分页、展开行 */ export const TaskTable: React.FC = ({ dataSource, @@ -40,116 +36,7 @@ export const TaskTable: React.FC = ({ onRetry, onViewDetail, }) => { - // 表格列定义 - 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 ( - onRetry(record.id)} - okText="确定" - cancelText="取消" - > - - - ) - } - if (record.status === "failed") { - return ( - - ) - } - return - - }, - }, - ] + const columns = useTaskTableColumns({ retryLoading, onRetry, onViewDetail }) return ( = ({ scroll={{ x: 800 }} className="task-table" locale={{ - emptyText: ( -
- -

暂无任务记录

-
- ), + emptyText: , }} /> ) diff --git a/apps/web/src/pages/tasks/components/task-table/TaskEmptyState.tsx b/apps/web/src/pages/tasks/components/task-table/TaskEmptyState.tsx new file mode 100755 index 000000000..de5e4682e --- /dev/null +++ b/apps/web/src/pages/tasks/components/task-table/TaskEmptyState.tsx @@ -0,0 +1,9 @@ +import React from "react" +import { ClockCircleOutlined } from "@ant-design/icons" + +export const TaskEmptyState: React.FC = () => ( +
+ +

暂无任务记录

+
+) diff --git a/apps/web/src/pages/tasks/components/task-table/index.ts b/apps/web/src/pages/tasks/components/task-table/index.ts new file mode 100755 index 000000000..50a42ac1b --- /dev/null +++ b/apps/web/src/pages/tasks/components/task-table/index.ts @@ -0,0 +1,2 @@ +export { useTaskTableColumns } from "./useTaskTableColumns" +export { TaskEmptyState } from "./TaskEmptyState" diff --git a/apps/web/src/pages/tasks/components/task-table/useTaskTableColumns.tsx b/apps/web/src/pages/tasks/components/task-table/useTaskTableColumns.tsx new file mode 100755 index 000000000..018437d61 --- /dev/null +++ b/apps/web/src/pages/tasks/components/task-table/useTaskTableColumns.tsx @@ -0,0 +1,128 @@ +import { Tag, Button, Popconfirm, Tooltip } from "antd" +import { RedoOutlined, InfoCircleOutlined } from "@ant-design/icons" +import type { ColumnsType } from "antd/es/table" +import type { TaskItem, TaskStatus } from "@/api/tasks" +import { STATUS_CONFIG, TYPE_LABELS } from "../../constants" +import { formatDuration, formatTime } from "../../utils" + +interface UseTaskTableColumnsOptions { + retryLoading: boolean + onRetry: (id: string) => void + onViewDetail: (record: TaskItem) => void +} + +export function useTaskTableColumns({ + retryLoading, + onRetry, + onViewDetail, +}: UseTaskTableColumnsOptions): ColumnsType { + return [ + { + 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 ( + onRetry(record.id)} + okText="确定" + cancelText="取消" + > + + + ) + } + if (record.status === "failed") { + return ( + + ) + } + return - + }, + }, + ] +} diff --git a/apps/web/src/test/pages/tasks/smoke.test.tsx b/apps/web/src/test/pages/tasks/smoke.test.tsx new file mode 100644 index 000000000..f1b2a72d3 --- /dev/null +++ b/apps/web/src/test/pages/tasks/smoke.test.tsx @@ -0,0 +1,17 @@ +/** + * Tasks 模块 smoke test + * 建立依赖链,确保 vitest related 能匹配到 tasks 目录下的改动 + */ +import { describe, it, expect } from "vitest" + +import "@/pages/tasks/components/TaskTable" +import "@/pages/tasks/components/task-table/useTaskTableColumns" +import "@/pages/tasks/components/task-table/TaskEmptyState" +import "@/pages/tasks/components/TaskFilterBar" +import "@/pages/tasks/components/TaskErrorDetail" + +describe("Tasks module smoke test", () => { + it("should load all task modules", () => { + expect(true).toBe(true) + }) +})