"use client"; import { ResponsiveContainer, LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend } from "recharts"; import { formatTokens, formatUSD } from "@/lib/utils"; import { useI18n } from "@/lib/i18n"; interface TrendPoint { date: string; calls: number; total_tokens: number; prompt_tokens: number; completion_tokens: number; quota?: number; } export function TrendChart({ data, metric = "total_tokens" }: { data: TrendPoint[]; metric?: "total_tokens" | "calls" | "quota" }) { const { t, locale } = useI18n(); if (!data.length) return
{t("common.noData")}
; const parseTrendDate = (dateStr: string) => { const [datePart, timePart] = dateStr.replace("T", " ").split(" "); const [year, month, day] = datePart.split("-").map(Number); const hour = timePart ? timePart.slice(0, 5) : ""; return { year, month, day, hour }; }; const formatDateLabel = (dateStr: string) => { const { year, month, day, hour } = parseTrendDate(dateStr); const d = new Date(year, month - 1, day); if (locale === "zh") { return hour ? `${month}/${day} ${hour}` : `${month}/${day}`; } const dateLabel = d.toLocaleDateString("en-US", { month: "short", day: "numeric" }); return hour ? `${dateLabel}, ${hour}` : dateLabel; }; const formatTooltipLabel = (label: string) => { const { year, month, day, hour } = parseTrendDate(label); const d = new Date(year, month - 1, day); if (locale === "zh") { return hour ? `${year}/${month}/${day} ${hour}` : `${year}/${month}/${day}`; } const dateLabel = d.toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric" }); return hour ? `${dateLabel}, ${hour}` : dateLabel; }; const tooltipStyle = { background: "var(--surface)", border: "1px solid var(--surface-border)", borderRadius: "8px", color: "var(--foreground)", fontSize: "12px", backdropFilter: "blur(16px)", }; // Token 模式:输入和输出数量级差距大,用双 Y 轴 if (metric === "total_tokens") { return ( {/* 左 Y 轴:输入 */} formatTokens(v)} tick={{ fontSize: 10 }} stroke="var(--chart-grid)" label={{ value: t("th.input"), angle: -90, position: "insideLeft", style: { fontSize: 10, fill: "var(--text-muted)" } }} /> {/* 右 Y 轴:输出 */} formatTokens(v)} tick={{ fontSize: 10 }} stroke="var(--chart-grid)" label={{ value: t("th.output"), angle: 90, position: "insideRight", style: { fontSize: 10, fill: "var(--text-muted)" } }} /> formatTooltipLabel(String(label))} formatter={(value, name) => [ formatTokens(Number(value)), name === t("th.input") ? t("th.input") : t("th.output"), ]} /> ); } // 金额模式:单 Y 轴 if (metric === "quota") { return ( formatUSD(v / 500000)} tick={{ fontSize: 11 }} stroke="var(--chart-grid)" /> formatTooltipLabel(String(label))} formatter={(value) => [formatUSD(Number(value) / 500000), t("th.cost")]} /> ); } // 调用量模式:单 Y 轴 return ( formatTokens(v)} tick={{ fontSize: 11 }} stroke="var(--chart-grid)" /> formatTooltipLabel(String(label))} formatter={(value, name) => [ formatTokens(Number(value)), name === t("th.calls") ? t("th.calls") : String(name), ]} /> ); }