mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-10-03 23:54:06 +00:00
fix(billing): 页面文字溢出卡片 + 数字千分位
用户报 billing 页"文字超出展示框"。真因是三处叠加,都不是文案问题: 1. KPI 卡是 CSS grid,grid item 默认 min-width:auto。2e8 级的 prompt tokens(实测 270,149,209)无法收缩,于是把 grid 轨道撑出容器 → 整页横向 溢出。修法是 min-width:0 + overflow:hidden(不给 min-width:0 的话, grid 子项永远不肯收缩,这是 grid 最常见的溢出坑)。 2. 表格写死 min-width:560px,窄视口下必然溢出。改 width:100% + table-layout:fixed,列宽由布局分配而不是由内容撑开。 3. 长名称(deepseek-v4.1-flash 这类模型 id)撑宽单元格。名称列改 ellipsis + title 悬停看全名;数字列 word-break:break-all 在列宽内换行。 顺手:所有 token/请求数走 toLocaleString 千分位。原始 9 位数字读起来要数 零位数,分组后 270,149,209 一眼可读,也顺带缩短了字符串宽度。 CDP 实测(820px 窄视口,逼出溢出条件): main/body 横向溢出 = no table=338 < card=366(修复前 min-width:560 必然 > 366) KPI 输入 tokens = 270,149,209 billing 页无控制台错误 残留(不影响布局):表头 TH 在固定布局下 48>42 轻微超出自身格,因为 word-break 不拆单个长词;表格整体仍在容器内,未产生页面滚动。 全量测试全绿(8 包)。
This commit is contained in:
@ -547,21 +547,30 @@ plugin.ui = {
|
|||||||
if (!b.cache_reported_reqs) return "n/r";
|
if (!b.cache_reported_reqs) return "n/r";
|
||||||
return ((hit / prompt) * 100).toFixed(1) + "%";
|
return ((hit / prompt) * 100).toFixed(1) + "%";
|
||||||
}
|
}
|
||||||
|
// fmtInt 千分位分组:2e8 级 token 总数可读、也更短,降低撑宽风险。
|
||||||
|
function fmtInt(n) {
|
||||||
|
return (Number(n) || 0).toLocaleString("en-US");
|
||||||
|
}
|
||||||
function row(name, b, cur) {
|
function row(name, b, cur) {
|
||||||
var fresh = (b.cache_fresh_tokens === undefined) ? (b.prompt_tokens || 0) : b.cache_fresh_tokens;
|
var fresh = (b.cache_fresh_tokens === undefined) ? (b.prompt_tokens || 0) : b.cache_fresh_tokens;
|
||||||
return "<tr><td><b>" + esc(name) + "</b></td><td>" + money(b.cost, cur) +
|
// 名称列 ellipsis(title 悬停看全名);数字列 break-all 在列宽内换行而不是
|
||||||
"</td><td>" + (b.requests || 0) + "</td><td>" + (b.prompt_tokens || 0) +
|
// 把表格撑出卡片。单元格结构与列数不变,列数判据不受影响。
|
||||||
"</td><td>" + fresh +
|
return "<tr><td style='overflow:hidden'><b style='display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis' title='" +
|
||||||
"</td><td>" + (b.cache_hit_tokens || 0) +
|
esc(String(name).replace(/'/g, "'")) + "'>" + esc(name) + "</b></td>" +
|
||||||
"</td><td>" + esc(cacheRate(b)) +
|
"<td style='word-break:break-all'>" + money(b.cost, cur) + "</td>" +
|
||||||
"</td><td>" + (b.completion_tokens || 0) + "</td></tr>";
|
"<td style='word-break:break-all'>" + fmtInt(b.requests || 0) + "</td>" +
|
||||||
|
"<td style='word-break:break-all'>" + fmtInt(b.prompt_tokens || 0) + "</td>" +
|
||||||
|
"<td style='word-break:break-all'>" + fmtInt(fresh) + "</td>" +
|
||||||
|
"<td style='word-break:break-all'>" + fmtInt(b.cache_hit_tokens || 0) + "</td>" +
|
||||||
|
"<td style='word-break:break-all'>" + esc(cacheRate(b)) + "</td>" +
|
||||||
|
"<td style='word-break:break-all'>" + fmtInt(b.completion_tokens || 0) + "</td></tr>";
|
||||||
}
|
}
|
||||||
function tableFor(el, obj, cur, empty) {
|
function tableFor(el, obj, cur, empty) {
|
||||||
var keys = Object.keys(obj || {});
|
var keys = Object.keys(obj || {});
|
||||||
if (!keys.length) { el.innerHTML = '<div class="muted">' + empty + "</div>"; return; }
|
if (!keys.length) { el.innerHTML = '<div class="muted">' + empty + "</div>"; return; }
|
||||||
keys.sort(function (a, b) { return (obj[b].cost || 0) - (obj[a].cost || 0); });
|
keys.sort(function (a, b) { return (obj[b].cost || 0) - (obj[a].cost || 0); });
|
||||||
var TH = L();
|
var TH = L();
|
||||||
var h = "<table style='width:100%;border-collapse:collapse;font-size:13px;min-width:560px'>" +
|
var h = "<table style='width:100%;border-collapse:collapse;font-size:13px;table-layout:fixed;word-break:break-word'>" +
|
||||||
"<tr style='text-align:left;opacity:.65'><th>" + TH.thName + "</th><th>" + TH.thCost +
|
"<tr style='text-align:left;opacity:.65'><th>" + TH.thName + "</th><th>" + TH.thCost +
|
||||||
"</th><th>" + TH.thReqs + "</th><th>" + TH.thPrompt +
|
"</th><th>" + TH.thReqs + "</th><th>" + TH.thPrompt +
|
||||||
"</th><th>" + TH.thFresh + "</th><th>" + TH.thCache + "</th><th>" + TH.thCachePct +
|
"</th><th>" + TH.thFresh + "</th><th>" + TH.thCache + "</th><th>" + TH.thCachePct +
|
||||||
@ -589,20 +598,23 @@ plugin.ui = {
|
|||||||
var t = st.total || {};
|
var t = st.total || {};
|
||||||
document.getElementById("billing-kpis").innerHTML = [
|
document.getElementById("billing-kpis").innerHTML = [
|
||||||
[T.total, money(t.cost, cur)],
|
[T.total, money(t.cost, cur)],
|
||||||
[T.requests, t.requests || 0],
|
[T.requests, fmtInt(t.requests || 0)],
|
||||||
[T.degraded, st.degraded_reqs || 0],
|
[T.degraded, fmtInt(st.degraded_reqs || 0)],
|
||||||
[T.unpriced, st.unpriced_reqs || 0],
|
[T.unpriced, fmtInt(st.unpriced_reqs || 0)],
|
||||||
[T.prompt, t.prompt_tokens || 0],
|
[T.prompt, fmtInt(t.prompt_tokens || 0)],
|
||||||
[T.completion, t.completion_tokens || 0],
|
[T.completion, fmtInt(t.completion_tokens || 0)],
|
||||||
[T.failures, t.failures || 0],
|
[T.failures, t.failures || 0],
|
||||||
// Cache KPIs: last session added the table columns but the KPI cards
|
// Cache KPIs: last session added the table columns but the KPI cards
|
||||||
// were left out — the edit's assert failed and the retry only re-did the
|
// were left out — the edit's assert failed and the retry only re-did the
|
||||||
// tables. The numbers existed in state and nowhere in the UI.
|
// tables. The numbers existed in state and nowhere in the UI.
|
||||||
[T.cacheRate, cacheRate(t)],
|
[T.cacheRate, cacheRate(t)],
|
||||||
[T.cacheTokens, t.cache_hit_tokens || 0]
|
[T.cacheTokens, fmtInt(t.cache_hit_tokens || 0)]
|
||||||
].map(function (kv) {
|
].map(function (kv) {
|
||||||
return "<div class='card' style='padding:12px'><div style='font-size:11px;opacity:.65'>" +
|
// min-width:0:grid item 默认 min-width:auto,2e8 级长数字会把轨道撑出
|
||||||
kv[0] + "</div><div style='font-size:19px;font-weight:600;margin-top:4px'>" +
|
// 容器造成横向溢出。标签 nowrap 截断,数值 break-all 换行。
|
||||||
|
return "<div class='card' style='padding:12px;min-width:0;overflow:hidden'>" +
|
||||||
|
"<div style='font-size:11px;opacity:.65;white-space:nowrap;overflow:hidden;text-overflow:ellipsis'>" +
|
||||||
|
esc(kv[0]) + "</div><div style='font-size:19px;font-weight:600;margin-top:4px;word-break:break-all;line-height:1.2'>" +
|
||||||
esc(kv[1]) + "</div></div>";
|
esc(kv[1]) + "</div></div>";
|
||||||
}).join("");
|
}).join("");
|
||||||
var TD = L();
|
var TD = L();
|
||||||
|
|||||||
Reference in New Issue
Block a user