feat: restyle API Key quota limit UI to card/toggle format
- Redesign quota limit section with card layout and toggle switch - Add watch to clear quota value when toggle is disabled - Add i18n keys for toggle labels and hints (zh/en) - Bump version to 0.1.90.5
This commit is contained in:
@@ -762,21 +762,58 @@
|
|||||||
<!-- API Key 账号配额限制 -->
|
<!-- API Key 账号配额限制 -->
|
||||||
<div
|
<div
|
||||||
v-if="account?.type === 'apikey'"
|
v-if="account?.type === 'apikey'"
|
||||||
class="border-t border-gray-200 pt-4 dark:border-dark-600"
|
class="border-t border-gray-200 pt-4 dark:border-dark-600 space-y-4"
|
||||||
>
|
>
|
||||||
<label class="input-label">{{ t('admin.accounts.quotaLimit') }}</label>
|
<div class="mb-3">
|
||||||
<div class="flex items-center gap-2">
|
<h3 class="input-label mb-0 text-base font-semibold">{{ t('admin.accounts.quotaLimit') }}</h3>
|
||||||
<span class="text-gray-500">$</span>
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
{{ t('admin.accounts.quotaLimitHint') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rounded-lg border border-gray-200 p-4 dark:border-dark-600">
|
||||||
|
<div class="mb-3 flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<label class="input-label mb-0">{{ t('admin.accounts.quotaLimitToggle') }}</label>
|
||||||
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
{{ t('admin.accounts.quotaLimitToggleHint') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
@click="quotaLimitEnabled = !quotaLimitEnabled"
|
||||||
|
:class="[
|
||||||
|
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2',
|
||||||
|
quotaLimitEnabled ? 'bg-primary-600' : 'bg-gray-200 dark:bg-dark-600'
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
:class="[
|
||||||
|
'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out',
|
||||||
|
quotaLimitEnabled ? 'translate-x-5' : 'translate-x-0'
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="quotaLimitEnabled" class="space-y-3">
|
||||||
|
<div>
|
||||||
|
<label class="input-label">{{ t('admin.accounts.quotaLimitAmount') }}</label>
|
||||||
|
<div class="relative">
|
||||||
|
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-500 dark:text-gray-400">$</span>
|
||||||
<input
|
<input
|
||||||
v-model.number="editQuotaLimit"
|
v-model.number="editQuotaLimit"
|
||||||
type="number"
|
type="number"
|
||||||
min="0"
|
min="0"
|
||||||
step="0.01"
|
step="0.01"
|
||||||
class="input"
|
class="input pl-7"
|
||||||
:placeholder="t('admin.accounts.quotaLimitPlaceholder')"
|
:placeholder="t('admin.accounts.quotaLimitPlaceholder')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p class="input-hint">{{ t('admin.accounts.quotaLimitHint') }}</p>
|
<p class="input-hint">{{ t('admin.accounts.quotaLimitAmountHint') }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- OpenAI OAuth Codex 官方客户端限制开关 -->
|
<!-- OpenAI OAuth Codex 官方客户端限制开关 -->
|
||||||
@@ -1407,6 +1444,7 @@ const openaiAPIKeyResponsesWebSocketV2Mode = ref<OpenAIWSMode>(OPENAI_WS_MODE_OF
|
|||||||
const codexCLIOnlyEnabled = ref(false)
|
const codexCLIOnlyEnabled = ref(false)
|
||||||
const anthropicPassthroughEnabled = ref(false)
|
const anthropicPassthroughEnabled = ref(false)
|
||||||
const editQuotaLimit = ref<number | null>(null)
|
const editQuotaLimit = ref<number | null>(null)
|
||||||
|
const quotaLimitEnabled = ref(false)
|
||||||
const openAIWSModeOptions = computed(() => [
|
const openAIWSModeOptions = computed(() => [
|
||||||
{ value: OPENAI_WS_MODE_OFF, label: t('admin.accounts.openai.wsModeOff') },
|
{ value: OPENAI_WS_MODE_OFF, label: t('admin.accounts.openai.wsModeOff') },
|
||||||
// TODO: ctx_pool 选项暂时隐藏,待测试完成后恢复
|
// TODO: ctx_pool 选项暂时隐藏,待测试完成后恢复
|
||||||
@@ -1435,6 +1473,13 @@ const isOpenAIModelRestrictionDisabled = computed(() =>
|
|||||||
props.account?.platform === 'openai' && openaiPassthroughEnabled.value
|
props.account?.platform === 'openai' && openaiPassthroughEnabled.value
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// When quota limit toggle is turned off, clear the value
|
||||||
|
watch(quotaLimitEnabled, (enabled) => {
|
||||||
|
if (!enabled) {
|
||||||
|
editQuotaLimit.value = null
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Computed: current preset mappings based on platform
|
// Computed: current preset mappings based on platform
|
||||||
const presetMappings = computed(() => getPresetMappingsByPlatform(props.account?.platform || 'anthropic'))
|
const presetMappings = computed(() => getPresetMappingsByPlatform(props.account?.platform || 'anthropic'))
|
||||||
const tempUnschedPresets = computed(() => [
|
const tempUnschedPresets = computed(() => [
|
||||||
@@ -1565,8 +1610,10 @@ watch(
|
|||||||
// Load quota limit for apikey accounts
|
// Load quota limit for apikey accounts
|
||||||
if (newAccount.type === 'apikey') {
|
if (newAccount.type === 'apikey') {
|
||||||
const quotaVal = extra?.quota_limit as number | undefined
|
const quotaVal = extra?.quota_limit as number | undefined
|
||||||
|
quotaLimitEnabled.value = !!(quotaVal && quotaVal > 0)
|
||||||
editQuotaLimit.value = (quotaVal && quotaVal > 0) ? quotaVal : null
|
editQuotaLimit.value = (quotaVal && quotaVal > 0) ? quotaVal : null
|
||||||
} else {
|
} else {
|
||||||
|
quotaLimitEnabled.value = false
|
||||||
editQuotaLimit.value = null
|
editQuotaLimit.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2317,7 +2364,7 @@ const handleSubmit = async () => {
|
|||||||
const currentExtra = (updatePayload.extra as Record<string, unknown>) ||
|
const currentExtra = (updatePayload.extra as Record<string, unknown>) ||
|
||||||
(props.account.extra as Record<string, unknown>) || {}
|
(props.account.extra as Record<string, unknown>) || {}
|
||||||
const newExtra: Record<string, unknown> = { ...currentExtra }
|
const newExtra: Record<string, unknown> = { ...currentExtra }
|
||||||
if (editQuotaLimit.value != null && editQuotaLimit.value > 0) {
|
if (quotaLimitEnabled.value && editQuotaLimit.value != null && editQuotaLimit.value > 0) {
|
||||||
newExtra.quota_limit = editQuotaLimit.value
|
newExtra.quota_limit = editQuotaLimit.value
|
||||||
} else {
|
} else {
|
||||||
delete newExtra.quota_limit
|
delete newExtra.quota_limit
|
||||||
|
|||||||
@@ -1787,6 +1787,10 @@ export default {
|
|||||||
quotaLimit: 'Quota Limit',
|
quotaLimit: 'Quota Limit',
|
||||||
quotaLimitPlaceholder: '0 means unlimited',
|
quotaLimitPlaceholder: '0 means unlimited',
|
||||||
quotaLimitHint: 'Set max spending limit (USD). Account will be paused when reached. Changing limit won\'t reset usage.',
|
quotaLimitHint: 'Set max spending limit (USD). Account will be paused when reached. Changing limit won\'t reset usage.',
|
||||||
|
quotaLimitToggle: 'Enable Quota Limit',
|
||||||
|
quotaLimitToggleHint: 'When enabled, account will be paused when usage reaches the set limit',
|
||||||
|
quotaLimitAmount: 'Limit Amount',
|
||||||
|
quotaLimitAmountHint: 'Maximum spending limit (USD). Account will be auto-paused when reached. Changing limit won\'t reset usage.',
|
||||||
testConnection: 'Test Connection',
|
testConnection: 'Test Connection',
|
||||||
reAuthorize: 'Re-Authorize',
|
reAuthorize: 'Re-Authorize',
|
||||||
refreshToken: 'Refresh Token',
|
refreshToken: 'Refresh Token',
|
||||||
|
|||||||
@@ -1794,6 +1794,10 @@ export default {
|
|||||||
quotaLimit: '配额限制',
|
quotaLimit: '配额限制',
|
||||||
quotaLimitPlaceholder: '0 表示不限制',
|
quotaLimitPlaceholder: '0 表示不限制',
|
||||||
quotaLimitHint: '设置最大使用额度(美元),达到后账号暂停调度。修改限额不会重置已用额度。',
|
quotaLimitHint: '设置最大使用额度(美元),达到后账号暂停调度。修改限额不会重置已用额度。',
|
||||||
|
quotaLimitToggle: '启用配额限制',
|
||||||
|
quotaLimitToggleHint: '开启后,当账号用量达到设定额度时自动暂停调度',
|
||||||
|
quotaLimitAmount: '限额金额',
|
||||||
|
quotaLimitAmountHint: '账号最大可用额度(美元),达到后自动暂停。修改限额不会重置已用额度。',
|
||||||
testConnection: '测试连接',
|
testConnection: '测试连接',
|
||||||
reAuthorize: '重新授权',
|
reAuthorize: '重新授权',
|
||||||
refreshToken: '刷新令牌',
|
refreshToken: '刷新令牌',
|
||||||
|
|||||||
Reference in New Issue
Block a user