"use client"; import Link from "next/link"; import { useEffect, useMemo, useState } from "react"; import { SendToBack } from "lucide-react"; import { AppShell } from "@/components/AppShell"; import { EmptyState, ErrorState, LoadingState } from "@/components/StateBlocks"; import { listRuns } from "@/lib/api/client"; import type { RunListItem } from "@/lib/api/types"; import { platformLabel } from "@/lib/flow-ledger/business"; function statusLabel(value?: string | null): string { const labels: Record = { completed: "已完成", running: "运行中", failed: "运行失败", interrupted: "已中断", blocked: "已拦截", pending: "等待处理", success: "已完成", partial_success: "已完成" }; return value ? labels[value] || "状态待确认" : "状态待确认"; } function statusChipClass(value?: string | null): string { if (value === "failed" || value === "interrupted") return "red"; if (value === "running" || value === "pending") return "blue"; if (value === "blocked") return "yellow"; return "green"; } function formatBeijingTime(value?: string | null): string { if (!value) return "未记录时间"; const iso = /z$|[+-]\d{2}:\d{2}$/i.test(value) ? value : `${value}Z`; const date = new Date(iso); if (Number.isNaN(date.getTime())) return "时间待确认"; return `北京时间 ${new Intl.DateTimeFormat("zh-CN", { timeZone: "Asia/Shanghai", year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", hour12: false }).format(date)}`; } type DemandGroup = { key: string; id: number | null; name: string; runs: RunListItem[]; latest: string; }; const PLATFORM_ORDER = ["douyin", "kuaishou", "shipinhao"]; function platformsSorted(runs: RunListItem[]): string[] { const unique = [...new Set(runs.map((run) => run.platform || "unknown"))]; return unique.sort((a, b) => { const ia = PLATFORM_ORDER.indexOf(a); const ib = PLATFORM_ORDER.indexOf(b); return (ia < 0 ? 99 : ia) - (ib < 0 ? 99 : ib); }); } function byStartedDesc(a: RunListItem, b: RunListItem): number { return (b.started_at || "") > (a.started_at || "") ? 1 : (b.started_at || "") < (a.started_at || "") ? -1 : 0; } function buildDemands(runs: RunListItem[]): DemandGroup[] { const map = new Map(); for (const run of runs) { const id = run.demand_content_id ?? null; const key = id != null ? `id:${id}` : "unknown"; const name = run.demand_name || (id != null ? `需求 ${id}` : "未知需求"); let group = map.get(key); if (!group) { group = { key, id, name, runs: [], latest: "" }; map.set(key, group); } group.runs.push(run); if ((run.started_at || "") > group.latest) group.latest = run.started_at || ""; } return [...map.values()].sort((a, b) => (b.latest > a.latest ? 1 : b.latest < a.latest ? -1 : 0)); } export function RunListPage() { const [runs, setRuns] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [query, setQuery] = useState(""); const [selectedKey, setSelectedKey] = useState(null); async function load() { setLoading(true); setError(null); try { // 现规模(几十条 run)一次取够,前端按需求分组;>100 再加分页。 const data = await listRuns(new URLSearchParams({ page_size: "100" })); setRuns(data.items || []); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { setLoading(false); } } useEffect(() => { void load(); }, []); const demands = useMemo(() => buildDemands(runs), [runs]); const filtered = useMemo(() => { const q = query.trim().toLowerCase(); if (!q) return demands; return demands.filter((d) => d.name.toLowerCase().includes(q) || (d.id != null && String(d.id).includes(q))); }, [demands, query]); useEffect(() => { if (filtered.length && !filtered.some((d) => d.key === selectedKey)) { setSelectedKey(filtered[0].key); } }, [filtered, selectedKey]); const selected = demands.find((d) => d.key === selectedKey) || null; return ( 推送 AIGC 记录 } > {loading ? : null} {error ? : null} {!loading && !error && !runs.length ? : null} {runs.length ? (
{selected ? ( <>
{selected.name} {selected.id != null ? `需求 #${selected.id} · ` : ""} {selected.runs.length} 次运行 · {platformsSorted(selected.runs).length} 个渠道
{platformsSorted(selected.runs).map((p) => { const platformRuns = selected.runs.filter((r) => (r.platform || "unknown") === p).sort(byStartedDesc); return (
{platformLabel(p)} {platformRuns.length} 次运行
{platformRuns.map((run) => ( {formatBeijingTime(run.started_at)} {statusLabel(run.status)} {run.run_id} ))}
); })} ) : ( )}
) : null}
); }