diff --git a/frontend/src/components/account/AccountUsageCell.vue b/frontend/src/components/account/AccountUsageCell.vue
index 3291b9b30d..966b0a5e08 100644
--- a/frontend/src/components/account/AccountUsageCell.vue
+++ b/frontend/src/components/account/AccountUsageCell.vue
@@ -397,12 +397,16 @@
class="flex flex-wrap items-center gap-1 text-[10px] text-gray-500 dark:text-gray-400"
>
{{ t('admin.accounts.usageWindow.grokPrepaid') }} ${{ grokPrepaidMoneyLine.prepaid }}
-
+
{{ t('admin.accounts.usageWindow.grokUsed') }}
{{ grokPrepaidMoneyLine.used }}/{{ grokPrepaidMoneyLine.limit }}
@@ -1164,30 +1168,33 @@ const formatGrokMoney = (value?: number | null) => {
if (value >= 10) return value.toFixed(1)
return value.toFixed(2)
}
-// Prepaid money line for paid Grok: show when prepaid_balance is present.
-// Monthly used/limit numbers are optional context; primary progress is the 30d bar.
+// Prepaid chip only when there is a positive prepaid balance.
+// Used/limit only when monthly limit is a positive number (0 means unlimited / unset).
const grokPrepaidMoneyLine = computed(() => {
const billing = grokBilling.value
if (!billing) return null
const prepaid = billing.prepaid_balance
- // "只针对预付": only render when prepaid field exists (including $0.00).
- if (prepaid == null || !Number.isFinite(prepaid)) return null
+ const showPrepaid = prepaid != null && Number.isFinite(prepaid) && prepaid > 0
+ const limitRaw =
+ billing.monthly_limit != null
+ ? billing.monthly_limit
+ : billing.monthly_limit_cents != null
+ ? billing.monthly_limit_cents / 100
+ : null
+ const showUsedLimit = limitRaw != null && Number.isFinite(limitRaw) && limitRaw > 0
+ if (!showPrepaid && !showUsedLimit) return null
const used =
billing.monthly_used != null
? billing.monthly_used
: billing.used_cents != null
? billing.used_cents / 100
: 0
- const limit =
- billing.monthly_limit != null
- ? billing.monthly_limit
- : billing.monthly_limit_cents != null
- ? billing.monthly_limit_cents / 100
- : 0
return {
- prepaid: formatGrokMoney(prepaid),
- used: formatGrokMoney(used),
- limit: formatGrokMoney(limit)
+ showPrepaid,
+ showUsedLimit,
+ prepaid: showPrepaid ? formatGrokMoney(prepaid) : null,
+ used: showUsedLimit ? formatGrokMoney(used) : null,
+ limit: showUsedLimit ? formatGrokMoney(limitRaw) : null
}
})
const grokPlanLabelIsFree = (value: string) => value.includes('free') || value.includes('basic')
diff --git a/frontend/src/components/account/__tests__/AccountUsageCell.spec.ts b/frontend/src/components/account/__tests__/AccountUsageCell.spec.ts
index 3e048a5346..1ad6866dd7 100644
--- a/frontend/src/components/account/__tests__/AccountUsageCell.spec.ts
+++ b/frontend/src/components/account/__tests__/AccountUsageCell.spec.ts
@@ -1168,6 +1168,94 @@ describe('AccountUsageCell', () => {
expect(wrapper.text()).toContain('30d|8|4400000')
})
+ it('Grok paid hides zero prepaid and hides used/limit when monthly limit is 0', async () => {
+ getUsage.mockResolvedValue({
+ subscription_tier: 'SuperGrok',
+ grok_billing: {
+ period_type: 'weekly',
+ usage_percent: 20,
+ prepaid_balance: 0,
+ monthly_limit: 0,
+ monthly_used: 3.5,
+ plan: 'SuperGrok'
+ }
+ })
+
+ const wrapper = mount(AccountUsageCell, {
+ props: {
+ account: makeAccount({ id: 4413, platform: 'grok', type: 'oauth', extra: {} })
+ },
+ global: {
+ stubs: {
+ UsageProgressBar: true,
+ AccountQuotaInfo: true
+ }
+ }
+ })
+
+ await flushPromises()
+ expect(wrapper.text()).not.toContain('admin.accounts.usageWindow.grokPrepaid')
+ expect(wrapper.text()).not.toContain('admin.accounts.usageWindow.grokUsed')
+ expect(wrapper.text()).not.toContain('3.5/0')
+ })
+
+ it('Grok paid shows used/limit without prepaid, and prepaid without a zero monthly limit', async () => {
+ getUsage.mockResolvedValueOnce({
+ subscription_tier: 'SuperGrok',
+ grok_billing: {
+ period_type: 'weekly',
+ usage_percent: 20,
+ monthly_limit: 25,
+ monthly_used: 3.5,
+ plan: 'SuperGrok'
+ }
+ })
+
+ const usedOnly = mount(AccountUsageCell, {
+ props: {
+ account: makeAccount({ id: 4414, platform: 'grok', type: 'oauth', extra: {} })
+ },
+ global: {
+ stubs: {
+ UsageProgressBar: true,
+ AccountQuotaInfo: true
+ }
+ }
+ })
+ await flushPromises()
+ expect(usedOnly.text()).not.toContain('admin.accounts.usageWindow.grokPrepaid')
+ expect(usedOnly.text()).toContain('admin.accounts.usageWindow.grokUsed')
+ expect(usedOnly.text()).toContain('3.50/25.0')
+
+ getUsage.mockResolvedValueOnce({
+ subscription_tier: 'SuperGrok Heavy',
+ grok_billing: {
+ period_type: 'weekly',
+ usage_percent: 20,
+ prepaid_balance: 12.5,
+ monthly_limit: 0,
+ monthly_used: 8,
+ plan: 'SuperGrok Heavy'
+ }
+ })
+ const prepaidOnly = mount(AccountUsageCell, {
+ props: {
+ account: makeAccount({ id: 4415, platform: 'grok', type: 'oauth', extra: {} })
+ },
+ global: {
+ stubs: {
+ UsageProgressBar: true,
+ AccountQuotaInfo: true
+ }
+ }
+ })
+ await flushPromises()
+ expect(prepaidOnly.text()).toContain('admin.accounts.usageWindow.grokPrepaid')
+ expect(prepaidOnly.text()).toContain('$12.5')
+ expect(prepaidOnly.text()).not.toContain('admin.accounts.usageWindow.grokUsed')
+ expect(prepaidOnly.text()).not.toContain('8.00/0')
+ })
+
it('Key 账号在 today stats loading 时显示骨架屏', async () => {
const wrapper = mount(AccountUsageCell, {
props: {