fix: 修正 Codex free 账号用量显示到每周窗口

This commit is contained in:
reed
2026-03-18 19:11:07 +08:00
parent a1a92c1918
commit 5bb8fe6af5

View File

@@ -43,6 +43,68 @@ const pickStrokeColor = (percent) => {
return '#3b82f6';
};
const normalizePlanType = (value) => {
if (value == null) return '';
return String(value).trim().toLowerCase();
};
const getWindowDurationSeconds = (windowData) => {
const value = Number(windowData?.limit_window_seconds);
if (!Number.isFinite(value) || value <= 0) return null;
return value;
};
const classifyWindowByDuration = (windowData) => {
const seconds = getWindowDurationSeconds(windowData);
if (seconds == null) return null;
return seconds >= 24 * 60 * 60 ? 'weekly' : 'fiveHour';
};
const resolveRateLimitWindows = (data) => {
const rateLimit = data?.rate_limit ?? {};
const primary = rateLimit?.primary_window ?? null;
const secondary = rateLimit?.secondary_window ?? null;
const windows = [primary, secondary].filter(Boolean);
const planType = normalizePlanType(data?.plan_type ?? rateLimit?.plan_type);
let fiveHourWindow = null;
let weeklyWindow = null;
for (const windowData of windows) {
const bucket = classifyWindowByDuration(windowData);
if (bucket === 'fiveHour' && !fiveHourWindow) {
fiveHourWindow = windowData;
continue;
}
if (bucket === 'weekly' && !weeklyWindow) {
weeklyWindow = windowData;
}
}
if (planType === 'free') {
if (!weeklyWindow) {
weeklyWindow = primary ?? secondary ?? null;
}
return { fiveHourWindow: null, weeklyWindow };
}
if (!fiveHourWindow && !weeklyWindow) {
return {
fiveHourWindow: primary ?? null,
weeklyWindow: secondary ?? null,
};
}
if (!fiveHourWindow) {
fiveHourWindow = windows.find((windowData) => windowData !== weeklyWindow) ?? null;
}
if (!weeklyWindow) {
weeklyWindow = windows.find((windowData) => windowData !== fiveHourWindow) ?? null;
}
return { fiveHourWindow, weeklyWindow };
};
const formatDurationSeconds = (seconds, t) => {
const tt = typeof t === 'function' ? t : (v) => v;
const s = Number(seconds);
@@ -68,6 +130,10 @@ const formatUnixSeconds = (unixSeconds) => {
const RateLimitWindowCard = ({ t, title, windowData }) => {
const tt = typeof t === 'function' ? t : (v) => v;
const hasWindowData =
!!windowData &&
typeof windowData === 'object' &&
Object.keys(windowData).length > 0;
const percent = clampPercent(windowData?.used_percent ?? 0);
const resetAt = windowData?.reset_at;
const resetAfterSeconds = windowData?.reset_after_seconds;
@@ -83,26 +149,30 @@ const RateLimitWindowCard = ({ t, title, windowData }) => {
</Text>
</div>
<div className='mt-2'>
<Progress
percent={percent}
stroke={pickStrokeColor(percent)}
showInfo={true}
/>
</div>
{hasWindowData ? (
<div className='mt-2'>
<Progress
percent={percent}
stroke={pickStrokeColor(percent)}
showInfo={true}
/>
</div>
) : (
<div className='mt-3 text-sm text-semi-color-text-2'>-</div>
)}
<div className='mt-1 flex flex-wrap items-center gap-2 text-xs text-semi-color-text-2'>
<div>
{tt('已使用:')}
{percent}%
{hasWindowData ? `${percent}%` : '-'}
</div>
<div>
{tt('距离重置:')}
{formatDurationSeconds(resetAfterSeconds, tt)}
{hasWindowData ? formatDurationSeconds(resetAfterSeconds, tt) : '-'}
</div>
<div>
{tt('窗口:')}
{formatDurationSeconds(limitWindowSeconds, tt)}
{hasWindowData ? formatDurationSeconds(limitWindowSeconds, tt) : '-'}
</div>
</div>
</div>
@@ -113,9 +183,7 @@ const CodexUsageView = ({ t, record, payload, onCopy, onRefresh }) => {
const tt = typeof t === 'function' ? t : (v) => v;
const data = payload?.data ?? null;
const rateLimit = data?.rate_limit ?? {};
const primary = rateLimit?.primary_window ?? null;
const secondary = rateLimit?.secondary_window ?? null;
const { fiveHourWindow, weeklyWindow } = resolveRateLimitWindows(data);
const allowed = !!rateLimit?.allowed;
const limitReached = !!rateLimit?.limit_reached;
@@ -163,12 +231,12 @@ const CodexUsageView = ({ t, record, payload, onCopy, onRefresh }) => {
<RateLimitWindowCard
t={tt}
title={tt('5小时窗口')}
windowData={primary}
windowData={fiveHourWindow}
/>
<RateLimitWindowCard
t={tt}
title={tt('每周窗口')}
windowData={secondary}
windowData={weeklyWindow}
/>
</div>