diff --git a/web/admin-spa/src/components/accounts/AccountForm.vue b/web/admin-spa/src/components/accounts/AccountForm.vue index eb252e55..10d469aa 100644 --- a/web/admin-spa/src/components/accounts/AccountForm.vue +++ b/web/admin-spa/src/components/accounts/AccountForm.vue @@ -870,7 +870,19 @@ const form = ref({ apiUrl: props.account?.apiUrl || '', apiKey: props.account?.apiKey || '', priority: props.account?.priority || 50, - supportedModels: props.account?.supportedModels?.join('\n') || '', + supportedModels: (() => { + const models = props.account?.supportedModels; + if (!models) return ''; + // 处理对象格式(Claude Console 的新格式) + if (typeof models === 'object' && !Array.isArray(models)) { + return Object.keys(models).join('\n'); + } + // 处理数组格式(向后兼容) + if (Array.isArray(models)) { + return models.join('\n'); + } + return ''; + })(), userAgent: props.account?.userAgent || '', rateLimitDuration: props.account?.rateLimitDuration || 60 }) @@ -1362,7 +1374,19 @@ watch(() => props.account, (newAccount) => { apiUrl: newAccount.apiUrl || '', apiKey: '', // 编辑模式不显示现有的 API Key priority: newAccount.priority || 50, - supportedModels: newAccount.supportedModels?.join('\n') || '', + supportedModels: (() => { + const models = newAccount.supportedModels; + if (!models) return ''; + // 处理对象格式(Claude Console 的新格式) + if (typeof models === 'object' && !Array.isArray(models)) { + return Object.keys(models).join('\n'); + } + // 处理数组格式(向后兼容) + if (Array.isArray(models)) { + return models.join('\n'); + } + return ''; + })(), userAgent: newAccount.userAgent || '', rateLimitDuration: newAccount.rateLimitDuration || 60 } diff --git a/web/admin-spa/src/components/apikeys/CreateApiKeyModal.vue b/web/admin-spa/src/components/apikeys/CreateApiKeyModal.vue index 4044b689..2020f74a 100644 --- a/web/admin-spa/src/components/apikeys/CreateApiKeyModal.vue +++ b/web/admin-spa/src/components/apikeys/CreateApiKeyModal.vue @@ -425,87 +425,27 @@
@@ -665,6 +605,7 @@ import { showToast } from '@/utils/toast' import { useClientsStore } from '@/stores/clients' import { useApiKeysStore } from '@/stores/apiKeys' import { apiClient } from '@/config/api' +import AccountSelector from '@/components/common/AccountSelector.vue' const props = defineProps({ accounts: { diff --git a/web/admin-spa/src/components/apikeys/EditApiKeyModal.vue b/web/admin-spa/src/components/apikeys/EditApiKeyModal.vue index 87a42f6a..9b23dbb6 100644 --- a/web/admin-spa/src/components/apikeys/EditApiKeyModal.vue +++ b/web/admin-spa/src/components/apikeys/EditApiKeyModal.vue @@ -294,87 +294,27 @@
@@ -538,6 +478,7 @@ import { useAuthStore } from '@/stores/auth'
import { useClientsStore } from '@/stores/clients'
import { useApiKeysStore } from '@/stores/apiKeys'
import { apiClient } from '@/config/api'
+import AccountSelector from '@/components/common/AccountSelector.vue'
const props = defineProps({
apiKey: {
@@ -638,11 +579,32 @@ const updateApiKey = async () => {
concurrencyLimit: form.concurrencyLimit !== '' && form.concurrencyLimit !== null ? parseInt(form.concurrencyLimit) : 0,
dailyCostLimit: form.dailyCostLimit !== '' && form.dailyCostLimit !== null ? parseFloat(form.dailyCostLimit) : 0,
permissions: form.permissions,
- claudeAccountId: form.claudeAccountId || null,
- geminiAccountId: form.geminiAccountId || null,
tags: form.tags
}
+ // 处理Claude账户绑定(区分OAuth和Console)
+ if (form.claudeAccountId) {
+ if (form.claudeAccountId.startsWith('console:')) {
+ // Claude Console账户
+ data.claudeConsoleAccountId = form.claudeAccountId.substring(8);
+ } else if (!form.claudeAccountId.startsWith('group:')) {
+ // Claude OAuth账户(非分组)
+ data.claudeAccountId = form.claudeAccountId;
+ } else {
+ // 分组
+ data.claudeAccountId = form.claudeAccountId;
+ }
+ } else {
+ data.claudeAccountId = null;
+ }
+
+ // Gemini账户绑定
+ if (form.geminiAccountId) {
+ data.geminiAccountId = form.geminiAccountId;
+ } else {
+ data.geminiAccountId = null;
+ }
+
// 模型限制 - 始终提交这些字段
data.enableModelRestriction = form.enableModelRestriction
data.restrictedModels = form.restrictedModels
@@ -747,7 +709,12 @@ onMounted(async () => {
form.concurrencyLimit = props.apiKey.concurrencyLimit || ''
form.dailyCostLimit = props.apiKey.dailyCostLimit || ''
form.permissions = props.apiKey.permissions || 'all'
- form.claudeAccountId = props.apiKey.claudeAccountId || ''
+ // 处理 Claude 账号(区分 OAuth 和 Console)
+ if (props.apiKey.claudeConsoleAccountId) {
+ form.claudeAccountId = `console:${props.apiKey.claudeConsoleAccountId}`
+ } else {
+ form.claudeAccountId = props.apiKey.claudeAccountId || ''
+ }
form.geminiAccountId = props.apiKey.geminiAccountId || ''
form.restrictedModels = props.apiKey.restrictedModels || []
form.allowedClients = props.apiKey.allowedClients || []
diff --git a/web/admin-spa/src/components/common/AccountSelector.vue b/web/admin-spa/src/components/common/AccountSelector.vue
new file mode 100644
index 00000000..da8ab6e8
--- /dev/null
+++ b/web/admin-spa/src/components/common/AccountSelector.vue
@@ -0,0 +1,468 @@
+
+