import { useCallback, useEffect, useState } from "react"; import { Activity, X } from "lucide-react"; import { adminApi } from "../../api"; import { Pager } from "../../components/pager"; import { IconKitSvg } from "../../components/IconKitSvg"; import type { AdminTask, AdminTaskDetail } from "../../types"; import { pts } from "../stage-config"; type Notify = (type: "success" | "error" | "info", text: string) => void; const PAGE_SIZE = 10; const TABS = [ { key: "", label: "全部" }, { key: "failed", label: "失败" }, { key: "succeeded", label: "成功" } ]; function fmtDate(iso: string) { if (!iso) return "—"; const d = new Date(iso); const p = (n: number) => String(n).padStart(2, "0"); return `${d.getFullYear()}/${p(d.getMonth() + 1)}/${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`; } function statusPill(status: string) { if (status === "succeeded") return 成功; if (status === "failed") return 失败; if (["cancelled", "compensating"].includes(status)) return {status}; return 进行中; } export function AdminTasksPage({ notify }: { notify: Notify }) { const [tasks, setTasks] = useState([]); const [count, setCount] = useState(0); const [loading, setLoading] = useState(true); const [page, setPage] = useState(1); const [tab, setTab] = useState(""); const [anomalyOnly, setAnomalyOnly] = useState(false); const [detail, setDetail] = useState(null); const [detailLoading, setDetailLoading] = useState(false); const [busy, setBusy] = useState(false); const load = useCallback(async () => { setLoading(true); try { const res = await adminApi.tasks({ status: tab || undefined, anomaly: anomalyOnly ? "1" : undefined, page, page_size: PAGE_SIZE }); if (res.results.length === 0 && res.count > 0 && page > 1) { setPage((p) => Math.max(1, p - 1)); return; } setTasks(res.results); setCount(res.count); } catch { notify("error", "加载任务失败"); } finally { setLoading(false); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [tab, anomalyOnly, page]); useEffect(() => { void load(); }, [load]); async function openDetail(id: string) { setDetailLoading(true); setDetail(null); try { setDetail(await adminApi.taskDetail(id)); } catch { notify("error", "加载详情失败"); } finally { setDetailLoading(false); } } async function retry(t: AdminTask) { if (busy) return; setBusy(true); try { await adminApi.retryTask(t.id); notify("success", "已重投,稍后刷新查看"); await load(); } catch (e) { notify("error", e instanceof Error ? e.message : "重投失败"); } finally { setBusy(false); } } return ( <>

任务监控

// {count} 个任务 · 全局 AI 任务 · 成本异常 · 失败重投
{TABS.map((t) => ( ))}
{loading ? (

加载中…

// fetching tasks

) : tasks.length === 0 ? (

暂无任务

// no tasks

) : (
{tasks.map((t) => ( ))}
类型团队模型状态成本(预估→实际)时间操作
{t.task_type} {t.team_name || } {t.model_name || } {statusPill(t.status)} {pts(t.estimated_cost)} → {pts(t.actual_cost)} 积分{t.margin_yuan != null ? ` · 毛利 ¥${t.margin_yuan}` : ""} {t.cost_anomaly && 异常} {fmtDate(t.created_at)} {t.status === "failed" && ( )}
)} {(detail || detailLoading) && (
{ if (e.target === e.currentTarget) setDetail(null); }}>

任务详情

{detailLoading || !detail ? (

加载中…

) : ( <>
状态{statusPill(detail.status)}
团队{detail.team_name || "—"}
模型{detail.model_name || "—"}
计价{pts(detail.estimated_cost)} → {pts(detail.actual_cost)} 积分{detail.cost_anomaly ? " ⚠" : ""}
平台成本{Number(detail.base_cost || 0) > 0 ? `¥${detail.base_cost} · 毛利 ${detail.margin_yuan != null ? `¥${detail.margin_yuan}` : "—"}` : "未知"}
{detail.error_message && ( <>
错误
{detail.error_message}
)}
请求 payload
{JSON.stringify(detail.request_payload, null, 2)}
响应 payload
{JSON.stringify(detail.response_payload, null, 2)}
)}
)} ); }