diff --git a/frontend/src/api/admin/accounts.ts b/frontend/src/api/admin/accounts.ts index a146f1f7..8a127793 100644 --- a/frontend/src/api/admin/accounts.ts +++ b/frontend/src/api/admin/accounts.ts @@ -370,8 +370,8 @@ export async function batchUpdateCredentials(request: { * @returns Success confirmation */ export async function bulkUpdate( - accountIds: number[], - updates: Record + accountIdsOrPayload: number[] | Record, + updates?: Record ): Promise<{ success: number failed: number @@ -379,16 +379,19 @@ export async function bulkUpdate( failed_ids?: number[] results: Array<{ account_id: number; success: boolean; error?: string }> }> { + const payload = Array.isArray(accountIdsOrPayload) + ? { + account_ids: accountIdsOrPayload, + ...(updates ?? {}) + } + : accountIdsOrPayload const { data } = await apiClient.post<{ success: number failed: number success_ids?: number[] failed_ids?: number[] results: Array<{ account_id: number; success: boolean; error?: string }> - }>('/admin/accounts/bulk-update', { - account_ids: accountIds, - ...updates - }) + }>('/admin/accounts/bulk-update', payload) return data } diff --git a/frontend/src/components/account/BulkEditAccountModal.vue b/frontend/src/components/account/BulkEditAccountModal.vue index 13c30cf9..b55456ff 100644 --- a/frontend/src/components/account/BulkEditAccountModal.vue +++ b/frontend/src/components/account/BulkEditAccountModal.vue @@ -17,7 +17,7 @@ d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> - {{ t('admin.accounts.bulkEdit.selectionInfo', { count: accountIds.length }) }} + {{ t('admin.accounts.bulkEdit.selectionInfo', { count: targetMode === 'filtered' ? targetPreviewCount : accountIds.length }) }}

@@ -27,7 +27,7 @@ - {{ t('admin.accounts.bulkEdit.mixedPlatformWarning', { platforms: selectedPlatforms.join(', ') }) }} + {{ t('admin.accounts.bulkEdit.mixedPlatformWarning', { platforms: targetSelectedPlatforms.join(', ') }) }}

@@ -227,7 +227,7 @@

@@ -933,6 +933,13 @@ interface Props { accountIds: number[] selectedPlatforms: AccountPlatform[] selectedTypes: AccountType[] + target?: { + mode: 'selected' | 'filtered' + filters?: Record + previewCount?: number + selectedPlatforms?: AccountPlatform[] + selectedTypes?: AccountType[] + } proxies: ProxyConfig[] groups: AdminGroup[] } @@ -947,40 +954,53 @@ const { t } = useI18n() const appStore = useAppStore() // Platform awareness -const isMixedPlatform = computed(() => props.selectedPlatforms.length > 1) +const targetMode = computed(() => props.target?.mode ?? 'selected') +const targetPreviewCount = computed(() => props.target?.previewCount ?? props.accountIds.length) +const targetSelectedPlatforms = computed(() => props.target?.selectedPlatforms ?? props.selectedPlatforms) +const targetSelectedTypes = computed(() => props.target?.selectedTypes ?? props.selectedTypes) +const isMixedPlatform = computed(() => targetSelectedPlatforms.value.length > 1) const allOpenAIPassthroughCapable = computed(() => { return ( - props.selectedPlatforms.length === 1 && - props.selectedPlatforms[0] === 'openai' && - props.selectedTypes.length > 0 && - props.selectedTypes.every(t => t === 'oauth' || t === 'apikey') + targetSelectedPlatforms.value.length === 1 && + targetSelectedPlatforms.value[0] === 'openai' && + targetSelectedTypes.value.length > 0 && + targetSelectedTypes.value.every(t => t === 'oauth' || t === 'apikey') ) }) const allOpenAIOAuth = computed(() => { return ( - props.selectedPlatforms.length === 1 && - props.selectedPlatforms[0] === 'openai' && - props.selectedTypes.length > 0 && - props.selectedTypes.every(t => t === 'oauth') + targetSelectedPlatforms.value.length === 1 && + targetSelectedPlatforms.value[0] === 'openai' && + targetSelectedTypes.value.length > 0 && + targetSelectedTypes.value.every(t => t === 'oauth') + ) +}) + +const allOpenAIAPIKey = computed(() => { + return ( + targetSelectedPlatforms.value.length === 1 && + targetSelectedPlatforms.value[0] === 'openai' && + targetSelectedTypes.value.length > 0 && + targetSelectedTypes.value.every(t => t === 'apikey') ) }) // 是否全部为 Anthropic OAuth/SetupToken(RPM 配置仅在此条件下显示) const allAnthropicOAuthOrSetupToken = computed(() => { return ( - props.selectedPlatforms.length === 1 && - props.selectedPlatforms[0] === 'anthropic' && - props.selectedTypes.every(t => t === 'oauth' || t === 'setup-token') + targetSelectedPlatforms.value.length === 1 && + targetSelectedPlatforms.value[0] === 'anthropic' && + targetSelectedTypes.value.every(t => t === 'oauth' || t === 'setup-token') ) }) const filteredPresets = computed(() => { - if (props.selectedPlatforms.length === 0) return [] + if (targetSelectedPlatforms.value.length === 0) return [] const dedupedPresets = new Map[number]>() - for (const platform of props.selectedPlatforms) { + for (const platform of targetSelectedPlatforms.value) { for (const preset of getPresetMappingsByPlatform(platform)) { const key = `${preset.from}=>${preset.to}` if (!dedupedPresets.has(key)) { @@ -1291,8 +1311,8 @@ const mixedChannelConfirmed = ref(false) const canPreCheck = () => enableGroups.value && groupIds.value.length > 0 && - props.selectedPlatforms.length === 1 && - (props.selectedPlatforms[0] === 'antigravity' || props.selectedPlatforms[0] === 'anthropic') + targetSelectedPlatforms.value.length === 1 && + (targetSelectedPlatforms.value[0] === 'antigravity' || targetSelectedPlatforms.value[0] === 'anthropic') const handleClose = () => { showMixedChannelWarning.value = false @@ -1309,7 +1329,7 @@ const preCheckMixedChannelRisk = async (built: Record): Promise try { const result = await adminAPI.accounts.checkMixedChannelRisk({ - platform: props.selectedPlatforms[0], + platform: targetSelectedPlatforms.value[0], group_ids: groupIds.value }) if (!result.has_risk) return true @@ -1325,7 +1345,7 @@ const preCheckMixedChannelRisk = async (built: Record): Promise } const handleSubmit = async () => { - if (props.accountIds.length === 0) { + if (targetMode.value === 'selected' && props.accountIds.length === 0) { appStore.showError(t('admin.accounts.bulkEdit.noSelection')) return } @@ -1373,7 +1393,12 @@ const submitBulkUpdate = async (baseUpdates: Record) => { submitting.value = true try { - const res = await adminAPI.accounts.bulkUpdate(props.accountIds, updates) + const res = targetMode.value === 'filtered' && props.target?.filters + ? await adminAPI.accounts.bulkUpdate({ + filters: props.target.filters, + ...updates + }) + : await adminAPI.accounts.bulkUpdate(props.accountIds, updates) const success = res.success || 0 const failed = res.failed || 0 diff --git a/frontend/src/components/admin/account/AccountBulkActionsBar.vue b/frontend/src/components/admin/account/AccountBulkActionsBar.vue index 3b987bd0..a632bdd4 100644 --- a/frontend/src/components/admin/account/AccountBulkActionsBar.vue +++ b/frontend/src/components/admin/account/AccountBulkActionsBar.vue @@ -1,9 +1,13 @@ diff --git a/frontend/src/views/admin/AccountsView.vue b/frontend/src/views/admin/AccountsView.vue index bc4c6215..2f061118 100644 --- a/frontend/src/views/admin/AccountsView.vue +++ b/frontend/src/views/admin/AccountsView.vue @@ -141,7 +141,17 @@