"use client"; import { CartesianGrid, Line, LineChart, ResponsiveContainer, Tooltip, XAxis, YAxis, } from "recharts"; import { cn } from "@/lib/utils"; export type Round = { iteration: number; runDic: number; label: string; timestamp: string; values: Record; }; export type MetricsHistory = { metrics: string[]; rounds: Round[]; }; const METRIC_LABELS: Record = { req_set_car: "需求集合(车载)", dapan_car: "大盘车载", specific_test: "Specific Test", target_subset: "目标集合", icl_test: "ICL Test", overall: "大盘整体", triage_err_rate: "Triage 错误率", bvt_nav: "导航BVT", talkable_controllable: "可聊可控", badcase_2025: "Badcase 2025", autotask: "自动任务", target_subset_count: "目标子集数量", target_subset_wrong: "目标子集错误数", target_subset_correct: "目标子集正确数", target_subset_acc: "目标子集准确率", icl_test_overall: "ICL 测试整体", specific_test_overall: "专项测试整体", overrall: "大盘集", overrall_che_zai: "大盘集车载", overrall_shou_ji: "大盘集手机", overrall_dian_shi: "大盘集电视", overrall_yan_jing: "大盘集眼镜", overrall_you_ping: "大盘集有屏", overrall_wu_ping: "大盘集无屏", }; const LINE_COLORS = [ "#34d399", // emerald "#38bdf8", // sky "#a78bfa", // violet "#fbbf24", // amber "#f472b6", // rose "#22d3ee", // cyan "#a3e635", // lime "#fb923c", // orange ]; function labelOf(key: string): string { return METRIC_LABELS[key] ?? key; } function formatValue(v: number | null | undefined): string { if (v === null || v === undefined || Number.isNaN(v)) return "—"; return v.toFixed(4); } function formatDelta(curr: number | null | undefined, base: number | null | undefined): { text: string; tone: "up" | "down" | "flat"; } { if ( curr === null || curr === undefined || base === null || base === undefined || Number.isNaN(curr) || Number.isNaN(base) ) { return { text: "—", tone: "flat" }; } const d = curr - base; if (Math.abs(d) < 1e-6) return { text: "±0", tone: "flat" }; const sign = d > 0 ? "+" : ""; return { text: `${sign}${d.toFixed(4)}`, tone: d > 0 ? "up" : "down" }; } function chartRowsFor(history: MetricsHistory, key: string): Array> { return history.rounds.map((r) => { const v = r.values[key]; return { label: r.label, [key]: v === undefined || v === null ? null : v, }; }); } export function MetricsChartPanel({ history }: { history: MetricsHistory | null }) { if (!history || history.rounds.length === 0) { return (
暂无指标数据,等首轮分析完成
); } const latest = history.rounds[history.rounds.length - 1]; const baseline = history.rounds[0]; const prev = history.rounds.length >= 2 ? history.rounds[history.rounds.length - 2] : baseline; const colorFor = (key: string): string => { const idx = history.metrics.indexOf(key); return LINE_COLORS[idx >= 0 ? idx % LINE_COLORS.length : 0]; }; return (
{history.metrics.map((k) => { const curr = latest.values[k]; const dBase = formatDelta(curr, baseline.values[k]); const dPrev = formatDelta(curr, prev.values[k]); const color = colorFor(k); const rows = chartRowsFor(history, k); return (
{labelOf(k)}
{formatValue(curr)}
Δbase {dBase.text} Δprev {dPrev.text}
typeof v === "number" ? v.toFixed(4) : v } domain={["auto", "auto"]} width={60} /> [ typeof value === "number" ? value.toFixed(4) : value, labelOf(name), ]} />
); })}
); } function deltaClass(tone: "up" | "down" | "flat"): string { if (tone === "up") return "text-emerald-400"; if (tone === "down") return "text-rose-400"; return "text-zinc-500"; }