/** token 数缩写:18289138 → "18.3M"。表格列宽有限,全宽数字读起来也更慢 */ export function formatTokens(n: number): string { if (n === 0) return "0"; if (n < 1000) return String(n); if (n < 1_000_000) { const k = n / 1000; return `${k < 10 ? k.toFixed(1) : Math.round(k)}K`; } if (n < 1_000_000_000) { const m = n / 1_000_000; return `${m < 10 ? m.toFixed(2) : m.toFixed(1)}M`; } return `${(n / 1_000_000_000).toFixed(2)}B`; } /** 命中率;null 表示分母为 0(该组没有 prompt token) */ export function formatPercent(rate: number | null): string { return rate === null ? "—" : `${(rate * 100).toFixed(1)}%`; } /** * 金额。null 表示该渠道没有单价,显示为 "—"——**不是 $0**。 * 把未知渲染成零,等于把「不知道花了多少」说成「没花钱」。 */ export function formatCost(cost: number | null): string { if (cost === null) return "—"; if (cost === 0) return "$0"; if (cost < 0.01) return "<$0.01"; return `$${cost.toFixed(2)}`; } /** 单价(每百万 token)。小到 0.003625 这种也要如实显示,不能四舍五入成 0 */ export function formatRate(rate: number): string { if (rate === 0) return "$0"; // 定长小数避免 toPrecision 在极小值上退化成科学计数法(1e-7 这种没法读) const fixed = rate.toFixed(8).replace(/\.?0+$/, ""); // 小于 1e-8 时定长表示会变成 "0",此时宁可显示原值也不能谎报成零 return `$${fixed === "0" ? String(rate) : fixed}`; } const RANGE_LABELS = { today: "今天", "7d": "7天", "30d": "30天", all: "全部", } as const; export function formatRange(range: "today" | "7d" | "30d" | "all" | { from: number; to: number }): string { if (typeof range === "object") { const d = (ms: number) => new Date(ms).toISOString().slice(0, 10); return `${d(range.from)}~${d(range.to)}`; } return RANGE_LABELS[range]; }