mirror of
https://github.com/zyycn/codex-proxy-rs.git
synced 2026-11-06 08:06:19 +00:00
fix(ui): refine account settings and key displays
Show all batch account settings by default while updating only edited fields. Add ellipsis and full-text titles to API key names and labels, and adjust the token usage summary icon size and alignment. Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
import type { AccountGroup, AccountModelAccess } from '@/api'
|
||||
|
||||
import BaseButton from '@/components/base/BaseButton.vue'
|
||||
import BaseCheckbox from '@/components/base/BaseCheckbox.vue'
|
||||
import BaseModal from '@/components/base/BaseModal/index.vue'
|
||||
import AccountSettingsFields from './AccountSettingsFields.vue'
|
||||
|
||||
@@ -12,6 +11,7 @@ defineProps<{
|
||||
groups: AccountGroup[]
|
||||
groupsLoading: boolean
|
||||
saving: boolean
|
||||
hasChanges: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -22,7 +22,6 @@ const open = defineModel<boolean>({ required: true })
|
||||
const enabled = defineModel<boolean>('enabled', { required: true })
|
||||
const concurrencyLimit = defineModel<string>('concurrencyLimit', { required: true })
|
||||
const modelAccess = defineModel<AccountModelAccess | undefined>('modelAccess', { required: true })
|
||||
const updateScheduling = defineModel<boolean>('updateScheduling', { required: true })
|
||||
const weight = defineModel<string>('weight', { required: true })
|
||||
const proxyMode = defineModel<string>('proxyMode', { required: true })
|
||||
const proxyId = defineModel<string>('proxyId', { required: true })
|
||||
@@ -38,7 +37,6 @@ const selectedGroupIds = defineModel<string[]>('selectedGroupIds', { required: t
|
||||
:dismissible="!saving"
|
||||
>
|
||||
<div class="grid gap-5">
|
||||
<BaseCheckbox v-model="updateScheduling" label="同时更新调度、分组和代理设置" show-label :disabled="saving" />
|
||||
<AccountSettingsFields
|
||||
v-model:enabled="enabled"
|
||||
v-model:concurrency-limit="concurrencyLimit"
|
||||
@@ -48,7 +46,6 @@ const selectedGroupIds = defineModel<string[]>('selectedGroupIds', { required: t
|
||||
v-model:proxy-mode="proxyMode"
|
||||
v-model:proxy-id="proxyId"
|
||||
preserve-model-access
|
||||
:model-only="!updateScheduling"
|
||||
:account-id="catalogAccountId"
|
||||
:groups="groups"
|
||||
:groups-loading="groupsLoading"
|
||||
@@ -63,7 +60,7 @@ const selectedGroupIds = defineModel<string[]>('selectedGroupIds', { required: t
|
||||
<BaseButton
|
||||
variant="primary"
|
||||
:loading="saving"
|
||||
:disabled="selectedCount === 0 || (updateScheduling && groupsLoading) || (!updateScheduling && !modelAccess)"
|
||||
:disabled="selectedCount === 0 || groupsLoading || !hasChanges"
|
||||
@click="emit('save')"
|
||||
>
|
||||
保存更改
|
||||
|
||||
@@ -15,7 +15,6 @@ withDefaults(defineProps<{
|
||||
accountId?: string
|
||||
preserveProxy?: boolean
|
||||
preserveModelAccess?: boolean
|
||||
modelOnly?: boolean
|
||||
proxyError?: string
|
||||
}>(), { preserveProxy: true })
|
||||
|
||||
@@ -31,50 +30,48 @@ const selectedGroupIds = defineModel<string[]>('selectedGroupIds', { required: t
|
||||
<template>
|
||||
<div class="grid gap-5">
|
||||
<AccountModelAccessField v-model="modelAccess" :account-id="accountId" :disabled="disabled" :allow-preserve="preserveModelAccess" />
|
||||
<template v-if="!modelOnly">
|
||||
<div class="flex min-h-6 items-center justify-between gap-3">
|
||||
<span class="text-cp leading-none font-medium text-cp-text-secondary">调度</span>
|
||||
<BaseSwitch
|
||||
v-model="enabled"
|
||||
label="切换账号调度"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex min-h-6 items-center justify-between gap-3">
|
||||
<span class="text-cp leading-none font-medium text-cp-text-secondary">调度</span>
|
||||
<BaseSwitch
|
||||
v-model="enabled"
|
||||
label="切换账号调度"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 sm:grid-cols-2">
|
||||
<BaseFormItem label="并发限制">
|
||||
<BaseInput
|
||||
v-model="concurrencyLimit"
|
||||
aria-label="账号并发限制"
|
||||
type="number"
|
||||
min="1"
|
||||
max="4294967295"
|
||||
placeholder="留空使用默认值"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</BaseFormItem>
|
||||
<BaseFormItem label="权重">
|
||||
<BaseInput
|
||||
v-model="weight"
|
||||
aria-label="账号调度权重"
|
||||
type="number"
|
||||
min="1"
|
||||
max="100"
|
||||
placeholder="越高越优先,最大 100"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</BaseFormItem>
|
||||
</div>
|
||||
|
||||
<BaseFormItem label="所属分组">
|
||||
<AccountGroupCheckboxGrid
|
||||
v-model="selectedGroupIds"
|
||||
:groups="groups"
|
||||
:loading="groupsLoading"
|
||||
<div class="grid gap-4 sm:grid-cols-2">
|
||||
<BaseFormItem label="并发限制">
|
||||
<BaseInput
|
||||
v-model="concurrencyLimit"
|
||||
aria-label="账号并发限制"
|
||||
type="number"
|
||||
min="1"
|
||||
max="4294967295"
|
||||
placeholder="留空使用默认值"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</BaseFormItem>
|
||||
<AccountProxyField v-model:mode="proxyMode" v-model:proxy-id="proxyId" :preserve="preserveProxy" :error="proxyError" :endpoint="endpoint" :account-id="accountId" :disabled="disabled" />
|
||||
</template>
|
||||
<BaseFormItem label="权重">
|
||||
<BaseInput
|
||||
v-model="weight"
|
||||
aria-label="账号调度权重"
|
||||
type="number"
|
||||
min="1"
|
||||
max="100"
|
||||
placeholder="越高越优先,最大 100"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</BaseFormItem>
|
||||
</div>
|
||||
|
||||
<BaseFormItem label="所属分组">
|
||||
<AccountGroupCheckboxGrid
|
||||
v-model="selectedGroupIds"
|
||||
:groups="groups"
|
||||
:loading="groupsLoading"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</BaseFormItem>
|
||||
<AccountProxyField v-model:mode="proxyMode" v-model:proxy-id="proxyId" :preserve="preserveProxy" :error="proxyError" :endpoint="endpoint" :account-id="accountId" :disabled="disabled" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Ref } from 'vue'
|
||||
import type { AccountModelAccess, getAccounts } from '@/api'
|
||||
|
||||
import { ref, shallowRef, watch } from 'vue'
|
||||
import { computed, ref, shallowRef, watch } from 'vue'
|
||||
import { batchUpdateAccounts } from '@/api'
|
||||
import { toast } from '@/components/base/BaseToast'
|
||||
import { useAsyncAction } from '@/composables/useAsyncAction'
|
||||
@@ -22,13 +22,25 @@ export function useAccountBatchEditor(options: {
|
||||
const concurrencyLimit = shallowRef('')
|
||||
const weight = shallowRef('1')
|
||||
const modelAccess = ref<AccountModelAccess | undefined>()
|
||||
const updateScheduling = shallowRef(false)
|
||||
const editedFields = ref(new Set<'enabled' | 'concurrencyLimit' | 'weight' | 'groupIds'>())
|
||||
const catalogAccountId = shallowRef<string>()
|
||||
const proxyMode = shallowRef('preserve')
|
||||
const proxyId = shallowRef('')
|
||||
const selectedGroupIds = ref<string[]>([])
|
||||
const saveAction = useAsyncAction()
|
||||
const saving = saveAction.loading
|
||||
const hasChanges = computed(() => Boolean(modelAccess.value) || editedFields.value.size > 0 || proxyMode.value !== 'preserve')
|
||||
|
||||
// 未操作的字段保持每个账号原值,避免展开表单就覆盖混合设置。
|
||||
watch([schedulingEnabled, concurrencyLimit, weight, selectedGroupIds], (values, previous) => {
|
||||
if (!showBatchEditModal.value || saving.value)
|
||||
return
|
||||
const fields = ['enabled', 'concurrencyLimit', 'weight', 'groupIds'] as const
|
||||
fields.forEach((field, index) => {
|
||||
if (values[index] !== previous[index])
|
||||
editedFields.value.add(field)
|
||||
})
|
||||
}, { flush: 'sync' })
|
||||
|
||||
function open() {
|
||||
const accounts = selectedAccounts()
|
||||
@@ -36,7 +48,7 @@ export function useAccountBatchEditor(options: {
|
||||
return
|
||||
|
||||
modelAccess.value = undefined
|
||||
updateScheduling.value = false
|
||||
editedFields.value.clear()
|
||||
catalogAccountId.value = accounts[0]?.id
|
||||
schedulingEnabled.value = accounts.every(account => account.enabled)
|
||||
proxyMode.value = 'preserve'
|
||||
@@ -55,12 +67,12 @@ export function useAccountBatchEditor(options: {
|
||||
toast.warning(modelError)
|
||||
return
|
||||
}
|
||||
if (!updateScheduling.value && !modelAccess.value) {
|
||||
if (!hasChanges.value) {
|
||||
toast.warning('请选择需要更新的设置')
|
||||
return
|
||||
}
|
||||
const scheduling = parseAccountSchedulingForm(updateScheduling.value ? concurrencyLimit.value : '', updateScheduling.value ? weight.value : '1')
|
||||
if (updateScheduling.value && proxyMode.value === 'proxy' && !proxyId.value.trim()) {
|
||||
const scheduling = parseAccountSchedulingForm(editedFields.value.has('concurrencyLimit') ? concurrencyLimit.value : '', editedFields.value.has('weight') ? weight.value : '1')
|
||||
if (proxyMode.value === 'proxy' && !proxyId.value.trim()) {
|
||||
toast.warning('请选择已通过测试的代理')
|
||||
return
|
||||
}
|
||||
@@ -74,15 +86,11 @@ export function useAccountBatchEditor(options: {
|
||||
await batchUpdateAccounts({
|
||||
accountIds,
|
||||
modelAccess: modelAccess.value,
|
||||
...(updateScheduling.value
|
||||
? {
|
||||
outboundProxyId: proxyMode.value === 'preserve' ? undefined : proxyMode.value === 'direct' ? '' : proxyId.value.trim(),
|
||||
enabled: schedulingEnabled.value,
|
||||
concurrencyLimit: scheduling.values.concurrencyLimit,
|
||||
weight: scheduling.values.weight,
|
||||
groupIds: [...new Set(selectedGroupIds.value)],
|
||||
}
|
||||
: {}),
|
||||
outboundProxyId: proxyMode.value === 'preserve' ? undefined : proxyMode.value === 'direct' ? '' : proxyId.value.trim(),
|
||||
enabled: editedFields.value.has('enabled') ? schedulingEnabled.value : undefined,
|
||||
concurrencyLimit: editedFields.value.has('concurrencyLimit') ? scheduling.values.concurrencyLimit : undefined,
|
||||
weight: editedFields.value.has('weight') ? scheduling.values.weight : undefined,
|
||||
groupIds: editedFields.value.has('groupIds') ? [...new Set(selectedGroupIds.value)] : undefined,
|
||||
})
|
||||
showBatchEditModal.value = false
|
||||
options.selectedIds.value = new Set()
|
||||
@@ -132,7 +140,7 @@ export function useAccountBatchEditor(options: {
|
||||
concurrencyLimit,
|
||||
weight,
|
||||
modelAccess,
|
||||
updateScheduling,
|
||||
hasChanges,
|
||||
catalogAccountId,
|
||||
proxyMode,
|
||||
proxyId,
|
||||
|
||||
@@ -129,7 +129,7 @@ const {
|
||||
concurrencyLimit: batchConcurrencyLimit,
|
||||
weight: batchWeight,
|
||||
modelAccess: batchModelAccess,
|
||||
updateScheduling: batchUpdateScheduling,
|
||||
hasChanges: batchHasChanges,
|
||||
catalogAccountId: batchCatalogAccountId,
|
||||
proxyMode: batchProxyMode,
|
||||
proxyId: batchProxyId,
|
||||
@@ -375,7 +375,6 @@ const {
|
||||
v-model:concurrency-limit="batchConcurrencyLimit"
|
||||
v-model:weight="batchWeight"
|
||||
v-model:model-access="batchModelAccess"
|
||||
v-model:update-scheduling="batchUpdateScheduling"
|
||||
v-model:proxy-mode="batchProxyMode"
|
||||
v-model:proxy-id="batchProxyId"
|
||||
v-model:selected-group-ids="batchGroupIds"
|
||||
@@ -384,6 +383,7 @@ const {
|
||||
:groups="groups"
|
||||
:groups-loading="groupsLoading"
|
||||
:saving="savingBatchEdit"
|
||||
:has-changes="batchHasChanges"
|
||||
@save="saveBatchEdit"
|
||||
/>
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ const metrics = computed(() => [
|
||||
<section class="grid min-w-0 gap-4 xl:grid-cols-[minmax(340px,0.95fr)_minmax(0,3fr)]" aria-label="用量汇总">
|
||||
<BaseCard as="article" padding="compact" class="flex min-h-28 min-w-0 flex-col justify-between gap-4">
|
||||
<div class="flex min-w-0 flex-1 items-center gap-3">
|
||||
<BaseMotionIcon class="inline-flex size-8.5 shrink-0 items-center justify-center rounded-cp-lg bg-cp-info-container text-cp-info-on-container" aria-hidden="true">
|
||||
<Zap :size="18" />
|
||||
<BaseMotionIcon class="inline-flex size-9 shrink-0 items-center relative -top-0.5 justify-center rounded-cp-lg bg-cp-info-container text-cp-info-on-container" aria-hidden="true">
|
||||
<Zap :size="19" />
|
||||
</BaseMotionIcon>
|
||||
<span class="sr-only">消耗 Tokens</span>
|
||||
<div class="flex min-w-0 flex-wrap items-baseline gap-x-2 gap-y-1">
|
||||
|
||||
@@ -9,11 +9,11 @@ defineProps<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex flex-col gap-0.5">
|
||||
<span class="text-cp font-bold text-cp-text">
|
||||
<div class="flex min-w-0 flex-col gap-0.5">
|
||||
<span class="truncate text-cp font-bold text-cp-text" :title="apiKey.name">
|
||||
{{ apiKey.name }}
|
||||
</span>
|
||||
<span v-if="apiKey.label" class="text-cp-sm font-emphasis text-cp-text-tertiary">
|
||||
<span v-if="apiKey.label" class="truncate text-cp-sm font-emphasis text-cp-text-tertiary" :title="apiKey.label">
|
||||
{{ apiKey.label }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user