feat: simplify dashboard usage stats

This commit is contained in:
2026-08-14 15:36:03 +08:00
parent 5f50669b98
commit 45ceeee936
3 changed files with 4 additions and 26 deletions
+2 -2
View File
@@ -6,7 +6,7 @@
- 支持在同一总览页查看多个 Codex 账号或工作区(包括同一邮箱的个人订阅与 Team 工作区) - 支持在同一总览页查看多个 Codex 账号或工作区(包括同一邮箱的个人订阅与 Team 工作区)
- 展示 Codex 账户、套餐、剩余额度和下次重置时间 - 展示 Codex 账户、套餐、剩余额度和下次重置时间
- 展示当前重置周期 Token 合计、本周期单日峰值、连续使用天数及每日 Token 趋势 - 展示当前重置周期 Token 合计、本周期单日峰值及每日 Token 趋势
- 在本地 SQLite 中保留历史用量和限额快照 - 在本地 SQLite 中保留历史用量和限额快照
- 在限额重置前、重置后发送 Telegram 或邮件提醒 - 在限额重置前、重置后发送 Telegram 或邮件提醒
- 通过 Telegram 菜单查询当前用量、重置时间、历史概览和账户信息 - 通过 Telegram 菜单查询当前用量、重置时间、历史概览和账户信息
@@ -171,7 +171,7 @@ Codex Helper 展示的是 Codex app-server 实际返回的数据。当前接口
- ChatGPT/Codex 账户和套餐类型 - ChatGPT/Codex 账户和套餐类型
- 限额窗口使用百分比、窗口长度和重置时间 - 限额窗口使用百分比、窗口长度和重置时间
- 当前重置周期 Token 合计、本周期单日峰值、连续使用天数、最长任务时长等摘要 - 当前重置周期 Token 合计、本周期单日峰值等摘要
- 每日总 Token 桶 - 每日总 Token 桶
当前总览的 Token 合计按 app-server 返回的当前有效最长限额窗口汇总每日 Token bucket,通常对应 secondary 周窗口;周期边界所在日期按整日统计。系统不根据 Token 推算 Credits、美元价值或订阅价格。部分摘要或每日数据也可能因账户或服务端暂未返回而显示为“暂无”。根据 OpenAI 官方文档,`account/usage/read` 需要 Codex 服务支持的身份认证;仅 API Key 或 Bedrock 登录不能读取这些 ChatGPT 用量数据。 当前总览的 Token 合计按 app-server 返回的当前有效最长限额窗口汇总每日 Token bucket,通常对应 secondary 周窗口;周期边界所在日期按整日统计。系统不根据 Token 推算 Credits、美元价值或订阅价格。部分摘要或每日数据也可能因账户或服务端暂未返回而显示为“暂无”。根据 OpenAI 官方文档,`account/usage/read` 需要 Codex 服务支持的身份认证;仅 API Key 或 Bedrock 登录不能读取这些 ChatGPT 用量数据。
-24
View File
@@ -15,7 +15,6 @@ import {
ChevronDown, ChevronDown,
Clock, Clock,
Coins, Coins,
Flame,
Github, Github,
Gauge, Gauge,
LogIn, LogIn,
@@ -27,7 +26,6 @@ import {
Sun, Sun,
Trash2, Trash2,
TrendingUp, TrendingUp,
Timer,
Zap, Zap,
Wifi, Wifi,
WifiOff, WifiOff,
@@ -753,20 +751,6 @@ function AccountDashboardDetails({
label="本周期单日峰值" label="本周期单日峰值"
v={num(dashboard.summary.peakDailyTokens)} v={num(dashboard.summary.peakDailyTokens)}
/> />
<Stat
icon={<Flame />}
label="连续使用"
v={
dashboard.summary.currentStreakDays == null
? "暂无"
: dashboard.summary.currentStreakDays + " 天"
}
/>
<Stat
icon={<Timer />}
label="最长任务时长"
v={duration(dashboard.summary.longestRunningTurnSec)}
/>
</div> </div>
<Suspense fallback={<SettingsLoading />}> <Suspense fallback={<SettingsLoading />}>
<UsageChart data={dashboard.usage} accountId={dashboard.accountId} /> <UsageChart data={dashboard.usage} accountId={dashboard.accountId} />
@@ -1660,14 +1644,6 @@ const num = (v?: number | null) =>
notation: "compact", notation: "compact",
maximumFractionDigits: 1, maximumFractionDigits: 1,
}).format(v); }).format(v);
const duration = (v?: number | null) =>
v == null
? "暂无"
: v < 60
? `${v} 秒`
: v < 3600
? `${Math.floor(v / 60)} 分 ${v % 60} 秒`
: `${(v / 3600).toFixed(1)} 小时`;
const cycleTimestamp = (value: number) => const cycleTimestamp = (value: number) =>
new Date(value * 1000).toLocaleString(); new Date(value * 1000).toLocaleString();
const cycleDuration = (minutes: number) => const cycleDuration = (minutes: number) =>
+2
View File
@@ -175,6 +175,8 @@ test("shows every account as a collapsible summary on the overview", async ({
).toBeVisible(); ).toBeVisible();
await expect(first.getByText("本周期 Tokens")).toBeVisible(); await expect(first.getByText("本周期 Tokens")).toBeVisible();
await expect(first.getByText("本周期单日峰值")).toBeVisible(); await expect(first.getByText("本周期单日峰值")).toBeVisible();
await expect(first.getByText("连续使用", { exact: true })).toHaveCount(0);
await expect(first.getByText("最长任务时长", { exact: true })).toHaveCount(0);
await expect(first.getByText("123.5K", { exact: true })).toBeVisible(); await expect(first.getByText("123.5K", { exact: true })).toBeVisible();
await expect(first.getByText("按每日数据汇总")).toBeVisible(); await expect(first.getByText("按每日数据汇总")).toBeVisible();
await expect(page.locator(".account-detail")).toHaveCount(1); await expect(page.locator(".account-detail")).toHaveCount(1);