mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-21 06:56:58 +00:00
Merge pull request #6384 from alfadb/fix/unify-usage-window-style
fix(frontend): unify usage window rendering and tighten alert thresholds
This commit is contained in:
@@ -4,38 +4,18 @@
|
||||
data-test="cn-provider-quota"
|
||||
class="min-w-[220px] space-y-1"
|
||||
>
|
||||
<!-- Tier rows: 5h + weekly utilization bars (snapshot renders on mount) -->
|
||||
<!-- Tier rows: 5h + weekly utilization bars (snapshot renders on mount).
|
||||
复用账号页 UsageProgressBar:同阈值配色、同倒计时格式。 -->
|
||||
<div v-if="data?.success && data.tiers?.length" class="space-y-1">
|
||||
<div
|
||||
<UsageProgressBar
|
||||
v-for="tier in data.tiers"
|
||||
:key="tier.window"
|
||||
data-test="cn-provider-quota-tier"
|
||||
class="flex min-w-0 items-center gap-1.5 text-[10px] leading-4"
|
||||
>
|
||||
<span
|
||||
data-test="cn-provider-quota-label"
|
||||
class="w-14 shrink-0 whitespace-nowrap text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{{ windowLabel(tier.window) }}
|
||||
</span>
|
||||
<div class="h-1.5 w-16 shrink-0 overflow-hidden rounded-full bg-gray-200 dark:bg-dark-600">
|
||||
<div
|
||||
class="h-full rounded-full transition-all"
|
||||
:class="utilizationColor(tier.used_percent)"
|
||||
:style="{ width: `${Math.min(100, Math.max(0, tier.used_percent))}%` }"
|
||||
/>
|
||||
</div>
|
||||
<span :class="['shrink-0 font-medium', utilizationTextColor(tier.used_percent)]">
|
||||
{{ Math.round(tier.used_percent) }}%
|
||||
</span>
|
||||
<span
|
||||
v-if="tier.reset_at"
|
||||
class="min-w-0 truncate text-gray-400 dark:text-gray-500"
|
||||
:title="tier.reset_at"
|
||||
>
|
||||
· {{ formatReset(tier.reset_at) }}
|
||||
</span>
|
||||
</div>
|
||||
:label="windowLabel(tier.window)"
|
||||
:color="tier.window === 'weekly' ? 'emerald' : 'indigo'"
|
||||
:utilization="tier.used_percent"
|
||||
:resets-at="tier.reset_at"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Explicit refresh action (aligned with the OpenAI "Query" / Grok "Probe"
|
||||
@@ -86,6 +66,7 @@ import { adminAPI } from '@/api/admin'
|
||||
import type { CNProviderQuotaProbeResult } from '@/api/admin/cnProviders'
|
||||
import type { Account } from '@/types'
|
||||
import { cnQuotaCellVisible } from './credentialsBuilder'
|
||||
import UsageProgressBar from './UsageProgressBar.vue'
|
||||
|
||||
const props = defineProps<{
|
||||
account: Account
|
||||
@@ -185,33 +166,6 @@ const windowLabel = (window: string) =>
|
||||
? t('admin.accounts.cnProviders.windowWeekly')
|
||||
: t('admin.accounts.cnProviders.window5h')
|
||||
|
||||
const utilizationColor = (pct: number) => {
|
||||
if (pct >= 90) return 'bg-red-500'
|
||||
if (pct >= 75) return 'bg-amber-500'
|
||||
return 'bg-emerald-500'
|
||||
}
|
||||
|
||||
const utilizationTextColor = (pct: number) => {
|
||||
if (pct >= 90) return 'text-red-600 dark:text-red-400'
|
||||
if (pct >= 75) return 'text-amber-600 dark:text-amber-400'
|
||||
return 'text-emerald-600 dark:text-emerald-400'
|
||||
}
|
||||
|
||||
// 重置时间相对/绝对简短显示。
|
||||
const formatReset = (iso: string) => {
|
||||
const d = new Date(iso)
|
||||
if (isNaN(d.getTime())) return iso
|
||||
const now = Date.now()
|
||||
const diffMs = d.getTime() - now
|
||||
if (diffMs <= 0) return t('admin.accounts.cnProviders.resetSoon')
|
||||
if (diffMs < 3_600_000) return `${Math.max(1, Math.round(diffMs / 60_000))}m`
|
||||
const hours = Math.round(diffMs / 3_600_000)
|
||||
if (hours < 48) return `${hours}h`
|
||||
const mm = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const dd = String(d.getDate()).padStart(2, '0')
|
||||
return `${mm}-${dd}`
|
||||
}
|
||||
|
||||
const handleProbe = async () => {
|
||||
if (loading.value) return
|
||||
loading.value = true
|
||||
|
||||
@@ -27,10 +27,8 @@
|
||||
|
||||
<!-- Progress bar row -->
|
||||
<div class="flex items-center gap-1">
|
||||
<!-- Label badge (fixed width for alignment) -->
|
||||
<span
|
||||
:class="['w-[32px] shrink-0 rounded px-1 text-center text-[10px] font-medium', labelClass]"
|
||||
>
|
||||
<!-- Label badge (label-width: fixed = 定宽居中, auto = 限宽截断左对齐) -->
|
||||
<span :class="[labelSizeClass, labelClass]">
|
||||
{{ label }}
|
||||
</span>
|
||||
|
||||
@@ -62,15 +60,20 @@ import { useI18n } from 'vue-i18n'
|
||||
import type { WindowStats } from '@/types'
|
||||
import { formatCompactNumber } from '@/utils/format'
|
||||
|
||||
const props = defineProps<{
|
||||
label: string
|
||||
utilization: number // Percentage (0-100+)
|
||||
resetsAt?: string | null
|
||||
color: 'indigo' | 'emerald' | 'purple' | 'amber'
|
||||
windowStats?: WindowStats | null
|
||||
showNowWhenIdle?: boolean
|
||||
remainingCapacity?: boolean
|
||||
}>()
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
label: string
|
||||
utilization: number // Percentage (0-100+)
|
||||
resetsAt?: string | null
|
||||
color: 'indigo' | 'emerald' | 'purple' | 'amber'
|
||||
windowStats?: WindowStats | null
|
||||
showNowWhenIdle?: boolean
|
||||
remainingCapacity?: boolean
|
||||
/** fixed: 定宽居中徽章(账号页纵向对齐);auto: 限宽截断左对齐(监控页组合标签) */
|
||||
labelWidth?: 'fixed' | 'auto'
|
||||
}>(),
|
||||
{ labelWidth: 'fixed' }
|
||||
)
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -108,6 +111,14 @@ const labelClass = computed(() => {
|
||||
return colors[props.color]
|
||||
})
|
||||
|
||||
// Label badge width mode: fixed 定宽保证账号页纵向对齐;auto 限宽截断适配
|
||||
// 监控页「Pro/7 天」类组合标签。百分比列在两种模式下保持不变。
|
||||
const labelSizeClass = computed(() =>
|
||||
props.labelWidth === 'auto'
|
||||
? 'max-w-[72px] shrink-0 truncate rounded px-1 text-left text-[10px] font-medium'
|
||||
: 'w-[32px] shrink-0 rounded px-1 text-center text-[10px] font-medium'
|
||||
)
|
||||
|
||||
// Progress bar color based on utilization
|
||||
const barClass = computed(() => {
|
||||
if (props.remainingCapacity) {
|
||||
@@ -118,9 +129,9 @@ const barClass = computed(() => {
|
||||
}
|
||||
return 'bg-green-500'
|
||||
}
|
||||
if (props.utilization >= 100) {
|
||||
if (props.utilization >= 90) {
|
||||
return 'bg-red-500'
|
||||
} else if (props.utilization >= 80) {
|
||||
} else if (props.utilization >= 75) {
|
||||
return 'bg-amber-500'
|
||||
} else {
|
||||
return 'bg-green-500'
|
||||
@@ -137,9 +148,9 @@ const textClass = computed(() => {
|
||||
}
|
||||
return 'text-gray-600 dark:text-gray-400'
|
||||
}
|
||||
if (props.utilization >= 100) {
|
||||
if (props.utilization >= 90) {
|
||||
return 'text-red-600 dark:text-red-400'
|
||||
} else if (props.utilization >= 80) {
|
||||
} else if (props.utilization >= 75) {
|
||||
return 'text-amber-600 dark:text-amber-400'
|
||||
} else {
|
||||
return 'text-gray-600 dark:text-gray-400'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { flushPromises, mount } from '@vue/test-utils'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import CNProviderQuotaCell from '../CNProviderQuotaCell.vue'
|
||||
import UsageProgressBar from '../UsageProgressBar.vue'
|
||||
import type { Account } from '@/types'
|
||||
|
||||
const { queryQuota } = vi.hoisted(() => ({
|
||||
@@ -13,11 +14,17 @@ vi.mock('@/api/admin', () => ({
|
||||
}
|
||||
}))
|
||||
|
||||
vi.mock('vue-i18n', () => ({
|
||||
useI18n: () => ({
|
||||
t: (key: string) => key
|
||||
})
|
||||
}))
|
||||
// 保留 vue-i18n 真实导出:UsageProgressBar 依赖 @/utils/format → @/i18n,
|
||||
// 其模块级 createI18n 需要真实 createI18n 存在。
|
||||
vi.mock('vue-i18n', async () => {
|
||||
const actual = await vi.importActual<typeof import('vue-i18n')>('vue-i18n')
|
||||
return {
|
||||
...actual,
|
||||
useI18n: () => ({
|
||||
t: (key: string) => key
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const account = {
|
||||
id: 7,
|
||||
@@ -38,7 +45,7 @@ describe('CNProviderQuotaCell', () => {
|
||||
queryQuota.mockReset()
|
||||
})
|
||||
|
||||
it('keeps the compact quota stack readable inside the account table cell', async () => {
|
||||
it('renders tier rows through the shared UsageProgressBar inside the account table cell', async () => {
|
||||
queryQuota.mockResolvedValue({
|
||||
success: true,
|
||||
tiers: [
|
||||
@@ -51,27 +58,30 @@ describe('CNProviderQuotaCell', () => {
|
||||
const root = wrapper.get('[data-test="cn-provider-quota"]')
|
||||
expect(root.classes()).toContain('min-w-[220px]')
|
||||
|
||||
const probeButton = root.get('button')
|
||||
expect(probeButton.classes()).toContain('whitespace-nowrap')
|
||||
expect(probeButton.classes()).toContain('leading-4')
|
||||
// 新鲜快照:挂载即渲染条形图,不触发探测
|
||||
await flushPromises()
|
||||
expect(queryQuota).not.toHaveBeenCalled()
|
||||
expect(wrapper.text()).toContain('27%')
|
||||
|
||||
// probe 按钮文案是动词 key(i18n mock 返回 key 本身),点击触发查询
|
||||
const probeButton = root.get('[data-test="cn-provider-quota-probe"]')
|
||||
expect(probeButton.text()).toBe('admin.accounts.cnProviders.probe')
|
||||
await probeButton.trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
const tiers = root.findAll('[data-test="cn-provider-quota-tier"]')
|
||||
expect(tiers).toHaveLength(2)
|
||||
for (const tier of tiers) {
|
||||
expect(tier.classes()).toContain('min-w-0')
|
||||
expect(tier.classes()).toContain('leading-4')
|
||||
}
|
||||
|
||||
const labels = root.findAll('[data-test="cn-provider-quota-label"]')
|
||||
expect(labels).toHaveLength(2)
|
||||
for (const label of labels) {
|
||||
expect(label.classes()).toContain('w-14')
|
||||
expect(label.classes()).toContain('whitespace-nowrap')
|
||||
}
|
||||
|
||||
expect(queryQuota).toHaveBeenCalledWith(account.id)
|
||||
|
||||
// tier 行由 UsageProgressBar 渲染:数量、label/color/utilization/reset 逐行对齐
|
||||
expect(root.findAll('[data-test="cn-provider-quota-tier"]')).toHaveLength(2)
|
||||
const bars = root.findAllComponents(UsageProgressBar)
|
||||
expect(bars).toHaveLength(2)
|
||||
expect(bars[0].props('label')).toBe('admin.accounts.cnProviders.window5h')
|
||||
expect(bars[0].props('utilization')).toBe(0)
|
||||
expect(bars[0].props('color')).toBe('indigo')
|
||||
expect(bars[0].props('resetsAt')).toBe('2026-08-18T12:30:00+08:00')
|
||||
expect(bars[1].props('label')).toBe('admin.accounts.cnProviders.windowWeekly')
|
||||
expect(bars[1].props('utilization')).toBe(27)
|
||||
expect(bars[1].props('color')).toBe('emerald')
|
||||
expect(bars[1].props('resetsAt')).toBe('2026-08-22T00:00:00+08:00')
|
||||
})
|
||||
|
||||
it('labels the refresh control with an explicit action verb, not a data caption', async () => {
|
||||
|
||||
@@ -146,4 +146,61 @@ describe('UsageProgressBar', () => {
|
||||
expect(wrapper.get('.h-1\\.5 > div').attributes('style')).toContain('width: 100%')
|
||||
expect(wrapper.get('.h-1\\.5 > div').classes()).toContain('bg-red-500')
|
||||
})
|
||||
|
||||
it('默认利用率模式按 75/90 阈值提前预警分级', () => {
|
||||
const mountAt = (utilization: number) =>
|
||||
mount(UsageProgressBar, {
|
||||
props: { label: '5h', utilization, color: 'indigo' }
|
||||
})
|
||||
|
||||
// 条形配色:74 绿 / 75 与 89 黄 / 90 红
|
||||
expect(mountAt(74).get('.h-1\\.5 > div').classes()).toContain('bg-green-500')
|
||||
expect(mountAt(75).get('.h-1\\.5 > div').classes()).toContain('bg-amber-500')
|
||||
expect(mountAt(89).get('.h-1\\.5 > div').classes()).toContain('bg-amber-500')
|
||||
expect(mountAt(90).get('.h-1\\.5 > div').classes()).toContain('bg-red-500')
|
||||
|
||||
// 百分比文本同步分级
|
||||
expect(mountAt(74).get('.h-1\\.5 + span').classes()).toContain('text-gray-600')
|
||||
expect(mountAt(75).get('.h-1\\.5 + span').classes()).toContain('text-amber-600')
|
||||
expect(mountAt(89).get('.h-1\\.5 + span').classes()).toContain('text-amber-600')
|
||||
expect(mountAt(90).get('.h-1\\.5 + span').classes()).toContain('text-red-600')
|
||||
})
|
||||
|
||||
it('labelWidth 默认 fixed:标签保持定宽居中,百分比列不变', () => {
|
||||
const wrapper = mount(UsageProgressBar, {
|
||||
props: { label: '5h', utilization: 30, color: 'indigo' }
|
||||
})
|
||||
|
||||
const label = wrapper.get('.gap-1 > span')
|
||||
expect(label.classes()).toContain('w-[32px]')
|
||||
expect(label.classes()).toContain('text-center')
|
||||
expect(label.classes()).not.toContain('max-w-[72px]')
|
||||
|
||||
const percent = wrapper.get('.h-1\\.5 + span')
|
||||
expect(percent.classes()).toContain('w-[32px]')
|
||||
expect(percent.classes()).toContain('text-right')
|
||||
})
|
||||
|
||||
it('labelWidth=auto 时标签限宽截断左对齐,百分比列保持不变', () => {
|
||||
const wrapper = mount(UsageProgressBar, {
|
||||
props: {
|
||||
label: 'Pro/7 天',
|
||||
utilization: 30,
|
||||
color: 'purple',
|
||||
labelWidth: 'auto'
|
||||
}
|
||||
})
|
||||
|
||||
const label = wrapper.get('.gap-1 > span')
|
||||
expect(label.text()).toBe('Pro/7 天')
|
||||
expect(label.classes()).toContain('max-w-[72px]')
|
||||
expect(label.classes()).toContain('truncate')
|
||||
expect(label.classes()).toContain('text-left')
|
||||
expect(label.classes()).not.toContain('w-[32px]')
|
||||
expect(label.classes()).not.toContain('text-center')
|
||||
|
||||
const percent = wrapper.get('.h-1\\.5 + span')
|
||||
expect(percent.classes()).toContain('w-[32px]')
|
||||
expect(percent.classes()).toContain('text-right')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -7,26 +7,19 @@
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- 用量窗口条形图(样式/阈值对齐账号页 CNProviderQuotaCell) -->
|
||||
<!-- 用量窗口条形图(复用账号页 UsageProgressBar:同阈值配色、同倒计时格式) -->
|
||||
<div v-if="snapshot.success && tierRows.length" class="space-y-1">
|
||||
<div v-for="row in tierRows" :key="row.key" class="flex items-center gap-1.5 text-[10px]">
|
||||
<span class="w-14 shrink-0 truncate text-gray-500 dark:text-gray-400" :title="row.title">
|
||||
{{ row.label }}
|
||||
</span>
|
||||
<div class="h-1.5 w-16 shrink-0 overflow-hidden rounded-full bg-gray-200 dark:bg-dark-600">
|
||||
<div
|
||||
class="h-full rounded-full transition-all"
|
||||
:class="utilizationColor(row.tier.used_percent)"
|
||||
:style="{ width: `${Math.min(100, Math.max(0, row.tier.used_percent))}%` }"
|
||||
/>
|
||||
</div>
|
||||
<span :class="['shrink-0 font-medium', utilizationTextColor(row.tier.used_percent)]">
|
||||
{{ Math.round(row.tier.used_percent) }}%
|
||||
</span>
|
||||
<span v-if="row.tier.reset_at" class="truncate text-gray-400 dark:text-gray-500" :title="row.tier.reset_at">
|
||||
· {{ formatReset(row.tier.reset_at) }}
|
||||
</span>
|
||||
</div>
|
||||
<UsageProgressBar
|
||||
v-for="row in tierRows"
|
||||
:key="row.key"
|
||||
data-testid="monitor-quota-tier"
|
||||
:label="row.label"
|
||||
:title="row.title"
|
||||
label-width="auto"
|
||||
:color="row.color"
|
||||
:utilization="row.tier.used_percent"
|
||||
:resets-at="row.tier.reset_at ?? null"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 余额(国产 payg;支持多币种) -->
|
||||
@@ -50,12 +43,14 @@
|
||||
import { computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type { MonitorQuotaSnapshot, MonitorQuotaTier } from '@/api/admin/channelMonitor'
|
||||
import UsageProgressBar from '@/components/account/UsageProgressBar.vue'
|
||||
|
||||
/**
|
||||
* 配额快照渲染(管理端监控列表/运行结果 + 用户端监控卡片共用)。
|
||||
* 展示形态对齐账号管理侧的用量视图(CNProviderQuotaCell:同阈值配色、
|
||||
* 同倒计时格式);tier 的 Window/Label 是后端约定的机器 token,
|
||||
* 已知 token 走 i18n,未知 token 原样展示(前向兼容)。
|
||||
* 用量条直接复用账号页的 UsageProgressBar(与 CNProviderQuotaCell、
|
||||
* AccountUsageCell 同一组件:同阈值配色、同倒计时格式);tier 的
|
||||
* Window/Label 是后端约定的机器 token,已知 token 走 i18n,未知 token
|
||||
* 原样展示(前向兼容)。
|
||||
*/
|
||||
const props = defineProps<{
|
||||
snapshot?: MonitorQuotaSnapshot | null
|
||||
@@ -63,10 +58,13 @@ const props = defineProps<{
|
||||
|
||||
const { t, te } = useI18n()
|
||||
|
||||
type TierColor = 'indigo' | 'emerald' | 'purple' | 'amber'
|
||||
|
||||
interface QuotaTierRow {
|
||||
key: string
|
||||
label: string
|
||||
title: string
|
||||
color: TierColor
|
||||
tier: MonitorQuotaTier
|
||||
}
|
||||
|
||||
@@ -103,11 +101,15 @@ function tierLabel(tier: MonitorQuotaTier): string {
|
||||
return `${label}/${window}`
|
||||
}
|
||||
|
||||
// tier 配色按数组顺序轮转(UsageProgressBar 支持的色板)。
|
||||
const tierColors: TierColor[] = ['indigo', 'emerald', 'purple', 'amber']
|
||||
|
||||
const tierRows = computed<QuotaTierRow[]>(() =>
|
||||
(props.snapshot?.tiers || []).map((tier, idx) => ({
|
||||
key: `${tier.window}-${tier.label || ''}-${idx}`,
|
||||
label: tierLabel(tier),
|
||||
title: tierLabel(tier),
|
||||
color: tierColors[idx % tierColors.length],
|
||||
tier,
|
||||
})),
|
||||
)
|
||||
@@ -126,31 +128,4 @@ const truncatedError = computed(() => {
|
||||
const error = props.snapshot?.error || t('monitorCommon.quota.unavailable')
|
||||
return error.length > 48 ? `${error.slice(0, 48)}…` : error
|
||||
})
|
||||
|
||||
const utilizationColor = (pct: number) => {
|
||||
if (pct >= 90) return 'bg-red-500'
|
||||
if (pct >= 75) return 'bg-amber-500'
|
||||
return 'bg-emerald-500'
|
||||
}
|
||||
|
||||
const utilizationTextColor = (pct: number) => {
|
||||
if (pct >= 90) return 'text-red-600 dark:text-red-400'
|
||||
if (pct >= 75) return 'text-amber-600 dark:text-amber-400'
|
||||
return 'text-emerald-600 dark:text-emerald-400'
|
||||
}
|
||||
|
||||
// 重置时间相对/绝对简短显示(与账号页一致)。
|
||||
const formatReset = (iso: string) => {
|
||||
const d = new Date(iso)
|
||||
if (isNaN(d.getTime())) return iso
|
||||
const now = Date.now()
|
||||
const diffMs = d.getTime() - now
|
||||
if (diffMs <= 0) return t('monitorCommon.quota.resetSoon')
|
||||
if (diffMs < 3_600_000) return `${Math.max(1, Math.round(diffMs / 60_000))}m`
|
||||
const hours = Math.round(diffMs / 3_600_000)
|
||||
if (hours < 48) return `${hours}h`
|
||||
const mm = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const dd = String(d.getDate()).padStart(2, '0')
|
||||
return `${mm}-${dd}`
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -42,9 +42,9 @@ describe('MonitorQuotaView', () => {
|
||||
},
|
||||
})
|
||||
|
||||
const rows = wrapper.findAll('[data-testid="monitor-quota-view"] .flex.items-center')
|
||||
const rows = wrapper.findAll('[data-testid="monitor-quota-tier"]')
|
||||
// tier 行只在 success 且有数据时渲染
|
||||
expect(rows.length).toBeGreaterThanOrEqual(3)
|
||||
expect(rows).toHaveLength(3)
|
||||
const text = wrapper.text()
|
||||
// 已知 window token 走 i18n
|
||||
expect(text).toContain('monitorCommon.quota.windows.5h')
|
||||
@@ -59,8 +59,9 @@ describe('MonitorQuotaView', () => {
|
||||
expect(text).toContain('95%')
|
||||
|
||||
const html = wrapper.html()
|
||||
// 阈值配色:≥90 红 / ≥75 黄 / 其余绿(与账号页 CNProviderQuotaCell 对齐)
|
||||
expect(html).toContain('bg-emerald-500')
|
||||
// 阈值配色(三处共用的 UsageProgressBar 统一):≥90 红 / ≥75 黄 / 其余绿
|
||||
// 42.4 → 绿、80 → 黄、95 → 红
|
||||
expect(html).toContain('bg-green-500')
|
||||
expect(html).toContain('bg-amber-500')
|
||||
expect(html).toContain('bg-red-500')
|
||||
})
|
||||
|
||||
@@ -146,14 +146,13 @@ export default {
|
||||
},
|
||||
},
|
||||
balance: 'Balance --',
|
||||
window5h: '5-hour window',
|
||||
windowWeekly: 'Weekly window',
|
||||
window5h: '5h',
|
||||
windowWeekly: '7d',
|
||||
probe: 'Query',
|
||||
probeTooltip: 'Query the provider quota endpoint for 5-hour / weekly rolling window usage',
|
||||
balanceProbeTooltip: 'Query the provider balance endpoint for the account balance',
|
||||
balanceLow: 'Insufficient balance',
|
||||
noBalanceEndpoint: 'This platform has no balance query endpoint',
|
||||
resetSoon: 'reset soon',
|
||||
},
|
||||
types: {
|
||||
oauth: 'OAuth',
|
||||
|
||||
@@ -472,7 +472,6 @@ export default {
|
||||
// Quota snapshot rendering (MonitorQuotaView, shared by admin + user views)
|
||||
quota: {
|
||||
unavailable: 'Quota unavailable',
|
||||
resetSoon: 'resetting',
|
||||
windows: {
|
||||
'5h': '5h',
|
||||
'7d': '7d',
|
||||
|
||||
@@ -349,14 +349,13 @@ export default {
|
||||
},
|
||||
},
|
||||
balance: '余额 --',
|
||||
window5h: '5 小时窗口',
|
||||
windowWeekly: '每周窗口',
|
||||
window5h: '5h',
|
||||
windowWeekly: '7d',
|
||||
probe: '查询',
|
||||
probeTooltip: '请求供应商额度端点,查询 5 小时 / 每周滚动窗口用量',
|
||||
balanceProbeTooltip: '请求供应商余额端点,查询账户余额',
|
||||
balanceLow: '余额不足',
|
||||
noBalanceEndpoint: '该平台暂无余额查询接口',
|
||||
resetSoon: '即将重置',
|
||||
},
|
||||
types: {
|
||||
oauth: 'OAuth',
|
||||
|
||||
@@ -477,7 +477,6 @@ export default {
|
||||
// 配额快照展示(MonitorQuotaView,管理端与用户端共用)
|
||||
quota: {
|
||||
unavailable: '配额信息不可用',
|
||||
resetSoon: '即将重置',
|
||||
windows: {
|
||||
'5h': '5 小时',
|
||||
'7d': '7 天',
|
||||
|
||||
Reference in New Issue
Block a user