fix(usage): 修按天分桶时区错位 + 区分账号额度/maestro用量口径 + 成本说明文档 (tsk_JKAzHuXvSw7h)
全局面板「用量」数据问题修复与澄清: 1. 时区分桶根因(store.ts):按天/周/月分桶与今天/本月窗口起点原全用 UTC, UTC+8 用户本地 00:00–08:00 的 run 被算进前一天、当天窗口晚 8h → 柱状图偏移 一天、当天偏小。新增 localDay/localMonth,periodStartISO/weekStartLabel/ usageDetail 改用 daemon 本地时区(SQL 比绝对时刻仍正确)。 2. UI 口径区分(design/ui_kits/console):额度条标注账号级含交互会话,AGENT 卡/ 详情弹框标注仅统计 maestro 任务执行,$ 加 API 列表价估算非实际扣费提示并以 ≈ 前缀,模型列加近似角标。新增 4 个 i18n key×5 语言。 3. 文档:新增 docs/usage-cost-explained.html(两数据源/为何对不上/cost 公式/ 单价表/本地时区分桶/已知近似),登记进 docs/index.html 知识库调研。 4. 测试:budget.test.ts 新增 UTC+8 跨日分桶用例。typecheck + 268 测试全绿。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -258,16 +258,18 @@ function AgentCard({ global, summary, t, collapsed, projects }) {
|
||||
<span style={{ color: 'var(--cyan)', display: 'inline-flex' }}><IcoAgents /></span>{t.gAgents.toUpperCase()}
|
||||
<span style={{ marginLeft: 'auto', fontWeight: 400, letterSpacing: '.04em', color: 'var(--faint)' }}>{t.apWeek}</span>
|
||||
</div>
|
||||
{/* 口径说明:本卡仅统计 maestro 任务执行,与账号级额度条不同源 */}
|
||||
<div style={{ fontSize: 9.5, color: 'var(--faint)', letterSpacing: '.04em', marginBottom: 8, marginTop: -4 }}>{t.apScopeMaestro}</div>
|
||||
{/* 概览 2×2 */}
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 1, background: 'var(--line-soft)', border: '1px solid var(--line-soft)', borderRadius: 4, overflow: 'hidden', marginBottom: 10 }}>
|
||||
{[
|
||||
[fmtTokens(summary.tokensWeek), t.apTokens, 'var(--cyan)'],
|
||||
['$' + summary.costWeek.toFixed(1), t.apCost, 'var(--green)'],
|
||||
[summary.runsWeek, t.apRuns, 'var(--ink)'],
|
||||
[summary.activeNow + ' / ' + global.maxAgents, t.apActive, summary.activeNow ? 'var(--cyan)' : 'var(--faint)'],
|
||||
].map(([v, k, c], i) => (
|
||||
[fmtTokens(summary.tokensWeek), t.apTokens, 'var(--cyan)', null],
|
||||
['≈$' + summary.costWeek.toFixed(1), t.apCost, 'var(--green)', t.apCostEstimateHint],
|
||||
[summary.runsWeek, t.apRuns, 'var(--ink)', null],
|
||||
[summary.activeNow + ' / ' + global.maxAgents, t.apActive, summary.activeNow ? 'var(--cyan)' : 'var(--faint)', null],
|
||||
].map(([v, k, c, hint], i) => (
|
||||
<div key={i} style={{ background: 'var(--bg-deep)', padding: '7px 9px' }}>
|
||||
<div style={{ fontSize: 15, fontWeight: 700, color: c, letterSpacing: '.02em' }}>{v}</div>
|
||||
<div title={hint || undefined} style={{ fontSize: 15, fontWeight: 700, color: c, letterSpacing: '.02em' }}>{v}</div>
|
||||
<div style={{ fontSize: 9.5, color: 'var(--muted)', letterSpacing: '.06em', marginTop: 1 }}>{k}</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -319,6 +321,7 @@ function AgentDetailModal({ projects, t, onClose }) {
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 14 }}>
|
||||
<span style={{ color: 'var(--cyan)', display: 'inline-flex' }}><IcoAgents /></span>
|
||||
<span style={{ fontSize: 14, fontWeight: 700, letterSpacing: '.04em', color: 'var(--ink)' }}>{t.apDetailTitle}</span>
|
||||
<span style={{ fontSize: 9.5, color: 'var(--faint)', letterSpacing: '.04em' }}>· {t.apScopeMaestro}</span>
|
||||
<div style={{ marginLeft: 'auto', display: 'flex', gap: 1, background: 'var(--line-soft)', border: '1px solid var(--line-soft)', borderRadius: 4, overflow: 'hidden' }}>
|
||||
{[['day', t.apByDay], ['week', t.apByWeek], ['month', t.apByMonth]].map(([g, label]) => (
|
||||
<button key={g} type="button" onClick={() => setGran(g)} style={{
|
||||
@@ -334,13 +337,13 @@ function AgentDetailModal({ projects, t, onClose }) {
|
||||
{/* total 概览 */}
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: 1, background: 'var(--line-soft)', border: '1px solid var(--line-soft)', borderRadius: 4, overflow: 'hidden', marginBottom: 14 }}>
|
||||
{[
|
||||
[fmtUsd(tot.costUsd), t.apCost, 'var(--green)'],
|
||||
[fmtTokens(tot.tokens), t.apTokens, 'var(--cyan)'],
|
||||
[tot.runs, t.apRuns, 'var(--ink)'],
|
||||
[tot.tasks, t.apColTasks, 'var(--ink)'],
|
||||
].map(([v, k, c], i) => (
|
||||
[fmtUsd(tot.costUsd), t.apCost, 'var(--green)', t.apCostEstimateHint],
|
||||
[fmtTokens(tot.tokens), t.apTokens, 'var(--cyan)', null],
|
||||
[tot.runs, t.apRuns, 'var(--ink)', null],
|
||||
[tot.tasks, t.apColTasks, 'var(--ink)', null],
|
||||
].map(([v, k, c, hint], i) => (
|
||||
<div key={i} style={{ background: 'var(--bg-deep)', padding: '8px 10px' }}>
|
||||
<div style={{ fontSize: 15, fontWeight: 700, color: c }}>{v}</div>
|
||||
<div title={hint || undefined} style={{ fontSize: 15, fontWeight: 700, color: c }}>{v}</div>
|
||||
<div style={{ fontSize: 9.5, color: 'var(--muted)', letterSpacing: '.06em', marginTop: 1 }}>{k}</div>
|
||||
</div>
|
||||
))}
|
||||
@@ -372,7 +375,7 @@ function AgentDetailModal({ projects, t, onClose }) {
|
||||
<th style={{ padding: '4px 6px', fontWeight: 600, textAlign: 'right' }}>{t.apTokens}</th>
|
||||
<th style={{ padding: '4px 6px', fontWeight: 600, textAlign: 'right' }}>{t.apCost}</th>
|
||||
<th style={{ padding: '4px 6px', fontWeight: 600, textAlign: 'right' }}>{t.apColTasks}</th>
|
||||
<th style={{ padding: '4px 6px', fontWeight: 600, textAlign: 'left' }}>{t.apColModels}</th>
|
||||
<th title={t.apModelApprox} style={{ padding: '4px 6px', fontWeight: 600, textAlign: 'left', cursor: 'help' }}>{t.apColModels} <span style={{ color: 'var(--faint)' }}>≈</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
Reference in New Issue
Block a user