diff --git a/frontend/src/components/account/AccountUsageCell.vue b/frontend/src/components/account/AccountUsageCell.vue index e548be8c..131d82b2 100644 --- a/frontend/src/components/account/AccountUsageCell.vue +++ b/frontend/src/components/account/AccountUsageCell.vue @@ -82,6 +82,7 @@ :utilization="usageInfo.five_hour.utilization" :resets-at="usageInfo.five_hour.resets_at" :window-stats="usageInfo.five_hour.window_stats" + :show-now-when-idle="true" color="indigo" /> diff --git a/frontend/src/components/account/UsageProgressBar.vue b/frontend/src/components/account/UsageProgressBar.vue index 506071fa..52f0ecbb 100644 --- a/frontend/src/components/account/UsageProgressBar.vue +++ b/frontend/src/components/account/UsageProgressBar.vue @@ -48,7 +48,7 @@ - + {{ formatResetTime }} @@ -68,6 +68,7 @@ const props = defineProps<{ resetsAt?: string | null color: 'indigo' | 'emerald' | 'purple' | 'amber' windowStats?: WindowStats | null + showNowWhenIdle?: boolean }>() const { t } = useI18n() @@ -139,9 +140,20 @@ const displayPercent = computed(() => { return percent > 999 ? '>999%' : `${percent}%` }) +const shouldShowResetTime = computed(() => { + if (props.resetsAt) return true + return Boolean(props.showNowWhenIdle && props.utilization <= 0) +}) + // Format reset time const formatResetTime = computed(() => { + // For rolling windows, when utilization is 0%, treat as immediately available. + if (props.showNowWhenIdle && props.utilization <= 0) { + return '现在' + } + if (!props.resetsAt) return '-' + const date = new Date(props.resetsAt) const diffMs = date.getTime() - now.value.getTime() diff --git a/frontend/src/components/account/__tests__/UsageProgressBar.spec.ts b/frontend/src/components/account/__tests__/UsageProgressBar.spec.ts new file mode 100644 index 00000000..9def052c --- /dev/null +++ b/frontend/src/components/account/__tests__/UsageProgressBar.spec.ts @@ -0,0 +1,69 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' +import { mount } from '@vue/test-utils' +import UsageProgressBar from '../UsageProgressBar.vue' + +vi.mock('vue-i18n', async () => { + const actual = await vi.importActual('vue-i18n') + return { + ...actual, + useI18n: () => ({ + t: (key: string) => key + }) + } +}) + +describe('UsageProgressBar', () => { + beforeEach(() => { + vi.useFakeTimers() + vi.setSystemTime(new Date('2026-03-17T00:00:00Z')) + }) + + afterEach(() => { + vi.useRealTimers() + }) + + it('showNowWhenIdle=true 且利用率为 0 时显示“现在”', () => { + const wrapper = mount(UsageProgressBar, { + props: { + label: '5h', + utilization: 0, + resetsAt: '2026-03-17T02:30:00Z', + showNowWhenIdle: true, + color: 'indigo' + } + }) + + expect(wrapper.text()).toContain('现在') + expect(wrapper.text()).not.toContain('2h 30m') + }) + + it('showNowWhenIdle=true 但利用率大于 0 时显示倒计时', () => { + const wrapper = mount(UsageProgressBar, { + props: { + label: '7d', + utilization: 12, + resetsAt: '2026-03-17T02:30:00Z', + showNowWhenIdle: true, + color: 'emerald' + } + }) + + expect(wrapper.text()).toContain('2h 30m') + expect(wrapper.text()).not.toContain('现在') + }) + + it('showNowWhenIdle=false 时保持原有倒计时行为', () => { + const wrapper = mount(UsageProgressBar, { + props: { + label: '1d', + utilization: 0, + resetsAt: '2026-03-17T02:30:00Z', + showNowWhenIdle: false, + color: 'indigo' + } + }) + + expect(wrapper.text()).toContain('2h 30m') + expect(wrapper.text()).not.toContain('现在') + }) +})