fix(providers): hide billing fields in provider form

This commit is contained in:
elky
2026-09-02 23:17:01 +08:00
parent 7323d41fbe
commit 214f3d6406
5 changed files with 82 additions and 129 deletions
@@ -123,39 +123,11 @@
</div>
</div>
<!-- 计费与限流 / 请求配置 -->
<!-- 请求配置 -->
<div class="space-y-3">
<div class="grid grid-cols-2 gap-4">
<h3 class="text-sm font-medium border-b pb-2">
{{ legacyT('计费与限流') }}
</h3>
<h3 class="text-sm font-medium border-b pb-2">
{{ legacyT('请求配置') }}
</h3>
</div>
<div class="grid grid-cols-2 gap-4">
<div class="space-y-1.5">
<Label>{{ legacyT('计费类型') }}</Label>
<Select
v-model="form.billing_type"
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="monthly_quota">
{{ legacyT('月卡额度') }}
</SelectItem>
<SelectItem value="pay_as_you_go">
{{ legacyT('按量付费') }}
</SelectItem>
<SelectItem value="free_tier">
{{ legacyT('免费套餐') }}
</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<h3 class="text-sm font-medium border-b pb-2">
{{ legacyT('请求配置') }}
</h3>
<!-- 超时配置 -->
<div class="grid grid-cols-2 gap-4">
@@ -229,49 +201,6 @@
/>
</div>
</div>
<!-- 月卡配置 -->
<div
v-if="form.billing_type === 'monthly_quota'"
class="grid grid-cols-2 gap-4 p-3 border rounded-lg bg-muted/50"
>
<div class="space-y-1.5">
<Label class="text-xs">{{ legacyT('周期额度 (USD)') }}</Label>
<Input
:model-value="form.monthly_quota_usd ?? ''"
type="number"
step="0.01"
min="0"
@update:model-value="(v) => form.monthly_quota_usd = parseNumberInput(v, { allowFloat: true })"
/>
</div>
<div class="space-y-1.5">
<Label class="text-xs">{{ legacyT('重置周期 (天)') }}</Label>
<Input
:model-value="form.quota_reset_day ?? ''"
type="number"
min="1"
max="365"
@update:model-value="(v) => form.quota_reset_day = parseNumberInput(v) ?? 30"
/>
</div>
<div class="space-y-1.5">
<Label class="text-xs">
{{ legacyT('周期开始时间') }} <span class="text-red-500">*</span>
</Label>
<Input
v-model="form.quota_last_reset_at"
type="datetime-local"
/>
</div>
<div class="space-y-1.5">
<Label class="text-xs">{{ legacyT('过期时间') }}</Label>
<Input
v-model="form.quota_expires_at"
type="datetime-local"
/>
</div>
</div>
</div>
<!-- 功能开关 -->
@@ -426,7 +355,6 @@ import {
} from '@/api/endpoints'
import { parseApiError } from '@/utils/errorParser'
import { parseNumberInput } from '@/utils/form'
import { dateTimeLocalToRfc3339, formatDateTimeLocalInput } from '@/utils/date'
const props = defineProps<{
modelValue: boolean
@@ -468,12 +396,6 @@ const form = ref({
provider_type: 'custom' as ProviderType,
description: '',
website: '',
// 计费配置
billing_type: 'pay_as_you_go' as 'monthly_quota' | 'pay_as_you_go' | 'free_tier',
monthly_quota_usd: undefined as number | undefined,
quota_reset_day: 30,
quota_last_reset_at: '', // 周期开始时间
quota_expires_at: '',
provider_priority: 100,
keep_priority_on_conversion: false, // 格式转换时是否保持优先级
// 状态配置
@@ -504,11 +426,6 @@ function resetForm() {
provider_type: 'custom',
description: '',
website: '',
billing_type: 'pay_as_you_go',
monthly_quota_usd: undefined,
quota_reset_day: 30,
quota_last_reset_at: '',
quota_expires_at: '',
provider_priority: defaultPriority.value,
keep_priority_on_conversion: false,
is_active: true,
@@ -542,11 +459,6 @@ function loadProviderData() {
provider_type: props.provider.provider_type || 'custom',
description: props.provider.description || '',
website: props.provider.website || '',
billing_type: (props.provider.billing_type as 'monthly_quota' | 'pay_as_you_go' | 'free_tier') || 'pay_as_you_go',
monthly_quota_usd: props.provider.monthly_quota_usd || undefined,
quota_reset_day: props.provider.quota_reset_day || 30,
quota_last_reset_at: formatDateTimeLocalInput(props.provider.quota_last_reset_at),
quota_expires_at: formatDateTimeLocalInput(props.provider.quota_expires_at),
provider_priority: props.provider.provider_priority || 999,
keep_priority_on_conversion: props.provider.keep_priority_on_conversion ?? false,
is_active: props.provider.is_active,
@@ -595,23 +507,6 @@ watch(() => form.value.provider_type, () => {
// 提交表单
const handleSubmit = async () => {
// 月卡类型必须设置周期开始时间
if (form.value.billing_type === 'monthly_quota' && !form.value.quota_last_reset_at) {
showError(legacyT('月卡类型必须设置周期开始时间'), legacyT('验证失败'))
return
}
const quotaLastResetAt = dateTimeLocalToRfc3339(form.value.quota_last_reset_at)
if (form.value.billing_type === 'monthly_quota' && !quotaLastResetAt) {
showError(legacyT('周期开始时间必须是合法时间'), legacyT('验证失败'))
return
}
const quotaExpiresAt = dateTimeLocalToRfc3339(form.value.quota_expires_at)
if (form.value.quota_expires_at && !quotaExpiresAt) {
showError(legacyT('过期时间必须是合法时间'), legacyT('验证失败'))
return
}
loading.value = true
try {
const currentPoolAdvanced = normalizePoolAdvancedConfig(props.provider?.pool_advanced)
@@ -620,11 +515,6 @@ const handleSubmit = async () => {
provider_type: form.value.provider_type,
description: form.value.description || undefined,
website: form.value.website || undefined,
billing_type: form.value.billing_type,
monthly_quota_usd: form.value.monthly_quota_usd,
quota_reset_day: form.value.quota_reset_day,
quota_last_reset_at: quotaLastResetAt,
quota_expires_at: quotaExpiresAt,
keep_priority_on_conversion: form.value.keep_priority_on_conversion,
responses_websocket_enabled: form.value.responses_websocket_enabled,
is_active: form.value.is_active,
@@ -137,6 +137,36 @@ function clickButton(text: string) {
button.click()
}
const billingFieldNames = [
'billing_type',
'monthly_quota_usd',
'quota_reset_day',
'quota_last_reset_at',
'quota_expires_at',
] as const
function expectBillingConfigurationHidden() {
for (const text of [
'计费类型',
'月卡额度',
'按量付费',
'免费套餐',
'周期额度 (USD)',
'重置周期 (天)',
'周期开始时间',
'过期时间',
]) {
expect(document.body.textContent).not.toContain(text)
}
}
function expectBillingFieldsOmitted(payload: unknown) {
expect(payload).toEqual(expect.any(Object))
for (const field of billingFieldNames) {
expect(payload).not.toHaveProperty(field)
}
}
beforeEach(() => {
endpointMocks.createProvider.mockReset()
endpointMocks.createProvider.mockResolvedValue({ id: 'provider-new', name: 'New Provider' })
@@ -226,6 +256,41 @@ describe('ProviderFormDialog transfer limits', () => {
})
})
describe('ProviderFormDialog billing configuration', () => {
it('hides billing configuration and omits billing fields when creating', async () => {
mountDialog(null)
await settle()
expectBillingConfigurationHidden()
await setInput('#name', 'New Provider')
clickButton('创建')
await settle()
expect(endpointMocks.createProvider).toHaveBeenCalledTimes(1)
expectBillingFieldsOmitted(endpointMocks.createProvider.mock.calls[0]?.[0])
})
it('hides existing monthly quota configuration and preserves it when editing', async () => {
mountDialog(makeProvider({
billing_type: 'monthly_quota',
monthly_quota_usd: 200,
quota_reset_day: 30,
quota_last_reset_at: '2026-08-01T00:00:00Z',
quota_expires_at: '2026-09-01T00:00:00Z',
}))
await settle()
expectBillingConfigurationHidden()
clickButton('保存')
await settle()
expect(endpointMocks.updateProvider).toHaveBeenCalledTimes(1)
expectBillingFieldsOmitted(endpointMocks.updateProvider.mock.calls[0]?.[1])
})
})
describe('ProviderFormDialog provider types', () => {
it('creates an experimental Claude Code provider from the add dialog', async () => {
mountDialog(null)