mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
feat: droid平台账户数据统计及调度能力
This commit is contained in:
@@ -71,7 +71,7 @@
|
||||
<!-- 平台分组选择器 -->
|
||||
<div class="space-y-3">
|
||||
<!-- 分组选择器 -->
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<div class="grid grid-cols-2 gap-2 sm:grid-cols-4">
|
||||
<!-- Claude 分组 -->
|
||||
<div
|
||||
class="group relative cursor-pointer overflow-hidden rounded-lg border-2 transition-all duration-200"
|
||||
@@ -173,6 +173,37 @@
|
||||
<p class="text-xs text-gray-600 dark:text-gray-400">Google AI</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Droid 分组 -->
|
||||
<div
|
||||
class="group relative cursor-pointer overflow-hidden rounded-lg border-2 transition-all duration-200"
|
||||
:class="[
|
||||
platformGroup === 'droid'
|
||||
? 'border-rose-500 bg-gradient-to-br from-rose-50 to-orange-50 shadow-md dark:from-rose-900/20 dark:to-orange-900/20'
|
||||
: 'border-gray-200 bg-white hover:border-rose-300 hover:shadow dark:border-gray-700 dark:bg-gray-800 dark:hover:border-rose-600'
|
||||
]"
|
||||
@click="selectPlatformGroup('droid')"
|
||||
>
|
||||
<div class="p-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div
|
||||
class="flex h-8 w-8 items-center justify-center rounded-md bg-gradient-to-br from-rose-500 to-orange-500"
|
||||
>
|
||||
<i class="fas fa-robot text-sm text-white"></i>
|
||||
</div>
|
||||
<div
|
||||
v-if="platformGroup === 'droid'"
|
||||
class="flex h-5 w-5 items-center justify-center rounded-full bg-rose-500"
|
||||
>
|
||||
<i class="fas fa-check text-xs text-white"></i>
|
||||
</div>
|
||||
</div>
|
||||
<h4 class="mt-2 text-sm font-semibold text-gray-900 dark:text-gray-100">
|
||||
Droid
|
||||
</h4>
|
||||
<p class="text-xs text-gray-600 dark:text-gray-400">Claude Droid</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 子平台选择器 -->
|
||||
@@ -447,6 +478,35 @@
|
||||
</div>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<!-- Droid 子选项 -->
|
||||
<template v-if="platformGroup === 'droid'">
|
||||
<label
|
||||
class="group relative flex cursor-pointer items-center rounded-md border p-2 transition-all"
|
||||
:class="[
|
||||
form.platform === 'droid'
|
||||
? 'border-rose-500 bg-rose-50 dark:border-rose-400 dark:bg-rose-900/30'
|
||||
: 'border-gray-300 bg-white hover:border-rose-400 hover:bg-rose-50/50 dark:border-gray-600 dark:bg-gray-700 dark:hover:border-rose-500 dark:hover:bg-rose-900/20'
|
||||
]"
|
||||
>
|
||||
<input v-model="form.platform" class="sr-only" type="radio" value="droid" />
|
||||
<div class="flex items-center gap-2">
|
||||
<i class="fas fa-robot text-sm text-rose-600 dark:text-rose-400"></i>
|
||||
<div>
|
||||
<span class="block text-xs font-medium text-gray-900 dark:text-gray-100"
|
||||
>Droid 专属</span
|
||||
>
|
||||
<span class="text-xs text-gray-500 dark:text-gray-400">官方</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="form.platform === 'droid'"
|
||||
class="absolute right-1 top-1 flex h-4 w-4 items-center justify-center rounded-full bg-rose-500"
|
||||
>
|
||||
<i class="fas fa-check text-xs text-white"></i>
|
||||
</div>
|
||||
</label>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -2992,6 +3052,8 @@ const selectPlatformGroup = (group) => {
|
||||
form.value.platform = 'openai'
|
||||
} else if (group === 'gemini') {
|
||||
form.value.platform = 'gemini'
|
||||
} else if (group === 'droid') {
|
||||
form.value.platform = 'droid'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,10 @@
|
||||
<input v-model="createForm.platform" class="mr-2" type="radio" value="openai" />
|
||||
<span class="text-sm text-gray-700">OpenAI</span>
|
||||
</label>
|
||||
<label class="flex cursor-pointer items-center">
|
||||
<input v-model="createForm.platform" class="mr-2" type="radio" value="droid" />
|
||||
<span class="text-sm text-gray-700">Droid</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -120,7 +124,9 @@
|
||||
? 'bg-purple-100 text-purple-700'
|
||||
: group.platform === 'gemini'
|
||||
? 'bg-blue-100 text-blue-700'
|
||||
: 'bg-gray-100 text-gray-700'
|
||||
: group.platform === 'openai'
|
||||
? 'bg-gray-100 text-gray-700'
|
||||
: 'bg-cyan-100 text-cyan-700'
|
||||
]"
|
||||
>
|
||||
{{
|
||||
@@ -128,7 +134,9 @@
|
||||
? 'Claude'
|
||||
: group.platform === 'gemini'
|
||||
? 'Gemini'
|
||||
: 'OpenAI'
|
||||
: group.platform === 'openai'
|
||||
? 'OpenAI'
|
||||
: 'Droid'
|
||||
}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -311,6 +311,10 @@
|
||||
<input v-model="form.permissions" class="mr-2" type="radio" value="openai" />
|
||||
<span class="text-sm text-gray-700">仅 OpenAI</span>
|
||||
</label>
|
||||
<label class="flex cursor-pointer items-center">
|
||||
<input v-model="form.permissions" class="mr-2" type="radio" value="droid" />
|
||||
<span class="text-sm text-gray-700">仅 Droid</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -345,7 +349,7 @@
|
||||
<select
|
||||
v-model="form.claudeAccountId"
|
||||
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200"
|
||||
:disabled="form.permissions === 'gemini' || form.permissions === 'openai'"
|
||||
:disabled="form.permissions && !['all', 'claude'].includes(form.permissions)"
|
||||
>
|
||||
<option value="">不修改</option>
|
||||
<option value="SHARED_POOL">使用共享账号池</option>
|
||||
@@ -380,7 +384,7 @@
|
||||
<select
|
||||
v-model="form.geminiAccountId"
|
||||
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200"
|
||||
:disabled="form.permissions === 'claude' || form.permissions === 'openai'"
|
||||
:disabled="form.permissions && !['all', 'gemini'].includes(form.permissions)"
|
||||
>
|
||||
<option value="">不修改</option>
|
||||
<option value="SHARED_POOL">使用共享账号池</option>
|
||||
@@ -411,7 +415,7 @@
|
||||
<select
|
||||
v-model="form.openaiAccountId"
|
||||
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200"
|
||||
:disabled="form.permissions === 'claude' || form.permissions === 'gemini'"
|
||||
:disabled="form.permissions && !['all', 'openai'].includes(form.permissions)"
|
||||
>
|
||||
<option value="">不修改</option>
|
||||
<option value="SHARED_POOL">使用共享账号池</option>
|
||||
@@ -442,7 +446,7 @@
|
||||
<select
|
||||
v-model="form.bedrockAccountId"
|
||||
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200"
|
||||
:disabled="form.permissions === 'gemini' || form.permissions === 'openai'"
|
||||
:disabled="form.permissions && !['all', 'openai'].includes(form.permissions)"
|
||||
>
|
||||
<option value="">不修改</option>
|
||||
<option value="SHARED_POOL">使用共享账号池</option>
|
||||
@@ -457,6 +461,37 @@
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium text-gray-600 dark:text-gray-400"
|
||||
>Droid 专属账号</label
|
||||
>
|
||||
<select
|
||||
v-model="form.droidAccountId"
|
||||
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200"
|
||||
:disabled="form.permissions && !['all', 'droid'].includes(form.permissions)"
|
||||
>
|
||||
<option value="">不修改</option>
|
||||
<option value="SHARED_POOL">使用共享账号池</option>
|
||||
<optgroup v-if="localAccounts.droidGroups.length > 0" label="账号分组">
|
||||
<option
|
||||
v-for="group in localAccounts.droidGroups"
|
||||
:key="group.id"
|
||||
:value="`group:${group.id}`"
|
||||
>
|
||||
分组 - {{ group.name }}
|
||||
</option>
|
||||
</optgroup>
|
||||
<optgroup v-if="localAccounts.droid.length > 0" label="专属账号">
|
||||
<option
|
||||
v-for="account in localAccounts.droid"
|
||||
:key="account.id"
|
||||
:value="account.id"
|
||||
>
|
||||
{{ account.name }}
|
||||
</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -497,7 +532,17 @@ const props = defineProps({
|
||||
},
|
||||
accounts: {
|
||||
type: Object,
|
||||
default: () => ({ claude: [], gemini: [], openai: [], bedrock: [] })
|
||||
default: () => ({
|
||||
claude: [],
|
||||
gemini: [],
|
||||
openai: [],
|
||||
bedrock: [],
|
||||
droid: [],
|
||||
claudeGroups: [],
|
||||
geminiGroups: [],
|
||||
openaiGroups: [],
|
||||
droidGroups: []
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -511,9 +556,11 @@ const localAccounts = ref({
|
||||
gemini: [],
|
||||
openai: [],
|
||||
bedrock: [],
|
||||
droid: [],
|
||||
claudeGroups: [],
|
||||
geminiGroups: [],
|
||||
openaiGroups: []
|
||||
openaiGroups: [],
|
||||
droidGroups: []
|
||||
})
|
||||
|
||||
// 标签相关
|
||||
@@ -542,6 +589,7 @@ const form = reactive({
|
||||
geminiAccountId: '',
|
||||
openaiAccountId: '',
|
||||
bedrockAccountId: '',
|
||||
droidAccountId: '',
|
||||
tags: [],
|
||||
isActive: null // null表示不修改
|
||||
})
|
||||
@@ -571,15 +619,23 @@ const removeTag = (index) => {
|
||||
const refreshAccounts = async () => {
|
||||
accountsLoading.value = true
|
||||
try {
|
||||
const [claudeData, claudeConsoleData, geminiData, openaiData, bedrockData, groupsData] =
|
||||
await Promise.all([
|
||||
apiClient.get('/admin/claude-accounts'),
|
||||
apiClient.get('/admin/claude-console-accounts'),
|
||||
apiClient.get('/admin/gemini-accounts'),
|
||||
apiClient.get('/admin/openai-accounts'),
|
||||
apiClient.get('/admin/bedrock-accounts'),
|
||||
apiClient.get('/admin/account-groups')
|
||||
])
|
||||
const [
|
||||
claudeData,
|
||||
claudeConsoleData,
|
||||
geminiData,
|
||||
openaiData,
|
||||
bedrockData,
|
||||
droidData,
|
||||
groupsData
|
||||
] = await Promise.all([
|
||||
apiClient.get('/admin/claude-accounts'),
|
||||
apiClient.get('/admin/claude-console-accounts'),
|
||||
apiClient.get('/admin/gemini-accounts'),
|
||||
apiClient.get('/admin/openai-accounts'),
|
||||
apiClient.get('/admin/bedrock-accounts'),
|
||||
apiClient.get('/admin/droid-accounts'),
|
||||
apiClient.get('/admin/account-groups')
|
||||
])
|
||||
|
||||
// 合并Claude OAuth账户和Claude Console账户
|
||||
const claudeAccounts = []
|
||||
@@ -627,12 +683,21 @@ const refreshAccounts = async () => {
|
||||
}))
|
||||
}
|
||||
|
||||
if (droidData.success) {
|
||||
localAccounts.value.droid = (droidData.data || []).map((account) => ({
|
||||
...account,
|
||||
platform: 'droid',
|
||||
isDedicated: account.accountType === 'dedicated'
|
||||
}))
|
||||
}
|
||||
|
||||
// 处理分组数据
|
||||
if (groupsData.success) {
|
||||
const allGroups = groupsData.data || []
|
||||
localAccounts.value.claudeGroups = allGroups.filter((g) => g.platform === 'claude')
|
||||
localAccounts.value.geminiGroups = allGroups.filter((g) => g.platform === 'gemini')
|
||||
localAccounts.value.openaiGroups = allGroups.filter((g) => g.platform === 'openai')
|
||||
localAccounts.value.droidGroups = allGroups.filter((g) => g.platform === 'droid')
|
||||
}
|
||||
|
||||
showToast('账号列表已刷新', 'success')
|
||||
@@ -720,6 +785,14 @@ const batchUpdateApiKeys = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
if (form.droidAccountId !== '') {
|
||||
if (form.droidAccountId === 'SHARED_POOL') {
|
||||
updates.droidAccountId = null
|
||||
} else {
|
||||
updates.droidAccountId = form.droidAccountId
|
||||
}
|
||||
}
|
||||
|
||||
// 激活状态
|
||||
if (form.isActive !== null) {
|
||||
updates.isActive = form.isActive
|
||||
@@ -774,9 +847,11 @@ onMounted(async () => {
|
||||
gemini: props.accounts.gemini || [],
|
||||
openai: props.accounts.openai || [],
|
||||
bedrock: props.accounts.bedrock || [],
|
||||
droid: props.accounts.droid || [],
|
||||
claudeGroups: props.accounts.claudeGroups || [],
|
||||
geminiGroups: props.accounts.geminiGroups || [],
|
||||
openaiGroups: props.accounts.openaiGroups || []
|
||||
openaiGroups: props.accounts.openaiGroups || [],
|
||||
droidGroups: props.accounts.droidGroups || []
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -616,6 +616,15 @@
|
||||
/>
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">仅 OpenAI</span>
|
||||
</label>
|
||||
<label class="flex cursor-pointer items-center">
|
||||
<input
|
||||
v-model="form.permissions"
|
||||
class="mr-2 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
|
||||
type="radio"
|
||||
value="droid"
|
||||
/>
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">仅 Droid</span>
|
||||
</label>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-gray-500 dark:text-gray-400">
|
||||
控制此 API Key 可以访问哪些服务
|
||||
@@ -653,7 +662,7 @@
|
||||
v-model="form.claudeAccountId"
|
||||
:accounts="localAccounts.claude"
|
||||
default-option-text="使用共享账号池"
|
||||
:disabled="form.permissions === 'gemini' || form.permissions === 'openai'"
|
||||
:disabled="form.permissions !== 'all' && form.permissions !== 'claude'"
|
||||
:groups="localAccounts.claudeGroups"
|
||||
placeholder="请选择Claude账号"
|
||||
platform="claude"
|
||||
@@ -667,7 +676,7 @@
|
||||
v-model="form.geminiAccountId"
|
||||
:accounts="localAccounts.gemini"
|
||||
default-option-text="使用共享账号池"
|
||||
:disabled="form.permissions === 'claude' || form.permissions === 'openai'"
|
||||
:disabled="form.permissions !== 'all' && form.permissions !== 'gemini'"
|
||||
:groups="localAccounts.geminiGroups"
|
||||
placeholder="请选择Gemini账号"
|
||||
platform="gemini"
|
||||
@@ -681,7 +690,7 @@
|
||||
v-model="form.openaiAccountId"
|
||||
:accounts="localAccounts.openai"
|
||||
default-option-text="使用共享账号池"
|
||||
:disabled="form.permissions === 'claude' || form.permissions === 'gemini'"
|
||||
:disabled="form.permissions !== 'all' && form.permissions !== 'openai'"
|
||||
:groups="localAccounts.openaiGroups"
|
||||
placeholder="请选择OpenAI账号"
|
||||
platform="openai"
|
||||
@@ -695,12 +704,26 @@
|
||||
v-model="form.bedrockAccountId"
|
||||
:accounts="localAccounts.bedrock"
|
||||
default-option-text="使用共享账号池"
|
||||
:disabled="form.permissions === 'gemini' || form.permissions === 'openai'"
|
||||
:disabled="form.permissions !== 'all' && form.permissions !== 'openai'"
|
||||
:groups="[]"
|
||||
placeholder="请选择Bedrock账号"
|
||||
platform="bedrock"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium text-gray-600 dark:text-gray-400"
|
||||
>Droid 专属账号</label
|
||||
>
|
||||
<AccountSelector
|
||||
v-model="form.droidAccountId"
|
||||
:accounts="localAccounts.droid"
|
||||
default-option-text="使用共享账号池"
|
||||
:disabled="form.permissions !== 'all' && form.permissions !== 'droid'"
|
||||
:groups="localAccounts.droidGroups"
|
||||
placeholder="请选择Droid账号"
|
||||
platform="droid"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-gray-500 dark:text-gray-400">
|
||||
选择专属账号后,此API Key将只使用该账号,不选择则使用共享账号池
|
||||
@@ -875,7 +898,17 @@ import AccountSelector from '@/components/common/AccountSelector.vue'
|
||||
const props = defineProps({
|
||||
accounts: {
|
||||
type: Object,
|
||||
default: () => ({ claude: [], gemini: [] })
|
||||
default: () => ({
|
||||
claude: [],
|
||||
gemini: [],
|
||||
openai: [],
|
||||
bedrock: [],
|
||||
droid: [],
|
||||
claudeGroups: [],
|
||||
geminiGroups: [],
|
||||
openaiGroups: [],
|
||||
droidGroups: []
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -889,10 +922,12 @@ const localAccounts = ref({
|
||||
claude: [],
|
||||
gemini: [],
|
||||
openai: [],
|
||||
bedrock: [], // 添加 Bedrock 账号列表
|
||||
bedrock: [],
|
||||
droid: [],
|
||||
claudeGroups: [],
|
||||
geminiGroups: [],
|
||||
openaiGroups: []
|
||||
openaiGroups: [],
|
||||
droidGroups: []
|
||||
})
|
||||
|
||||
// 表单验证状态
|
||||
@@ -935,7 +970,8 @@ const form = reactive({
|
||||
claudeAccountId: '',
|
||||
geminiAccountId: '',
|
||||
openaiAccountId: '',
|
||||
bedrockAccountId: '', // 添加 Bedrock 账号ID
|
||||
bedrockAccountId: '',
|
||||
droidAccountId: '',
|
||||
enableModelRestriction: false,
|
||||
restrictedModels: [],
|
||||
modelInput: '',
|
||||
@@ -973,10 +1009,15 @@ onMounted(async () => {
|
||||
claude: props.accounts.claude || [],
|
||||
gemini: props.accounts.gemini || [],
|
||||
openai: openaiAccounts,
|
||||
bedrock: props.accounts.bedrock || [], // 添加 Bedrock 账号
|
||||
bedrock: props.accounts.bedrock || [],
|
||||
droid: (props.accounts.droid || []).map((account) => ({
|
||||
...account,
|
||||
platform: 'droid'
|
||||
})),
|
||||
claudeGroups: props.accounts.claudeGroups || [],
|
||||
geminiGroups: props.accounts.geminiGroups || [],
|
||||
openaiGroups: props.accounts.openaiGroups || []
|
||||
openaiGroups: props.accounts.openaiGroups || [],
|
||||
droidGroups: props.accounts.droidGroups || []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -995,6 +1036,7 @@ const refreshAccounts = async () => {
|
||||
openaiData,
|
||||
openaiResponsesData,
|
||||
bedrockData,
|
||||
droidData,
|
||||
groupsData
|
||||
] = await Promise.all([
|
||||
apiClient.get('/admin/claude-accounts'),
|
||||
@@ -1002,7 +1044,8 @@ const refreshAccounts = async () => {
|
||||
apiClient.get('/admin/gemini-accounts'),
|
||||
apiClient.get('/admin/openai-accounts'),
|
||||
apiClient.get('/admin/openai-responses-accounts'), // 获取 OpenAI-Responses 账号
|
||||
apiClient.get('/admin/bedrock-accounts'), // 添加 Bedrock 账号获取
|
||||
apiClient.get('/admin/bedrock-accounts'),
|
||||
apiClient.get('/admin/droid-accounts'),
|
||||
apiClient.get('/admin/account-groups')
|
||||
])
|
||||
|
||||
@@ -1070,12 +1113,21 @@ const refreshAccounts = async () => {
|
||||
}))
|
||||
}
|
||||
|
||||
if (droidData.success) {
|
||||
localAccounts.value.droid = (droidData.data || []).map((account) => ({
|
||||
...account,
|
||||
platform: 'droid',
|
||||
isDedicated: account.accountType === 'dedicated'
|
||||
}))
|
||||
}
|
||||
|
||||
// 处理分组数据
|
||||
if (groupsData.success) {
|
||||
const allGroups = groupsData.data || []
|
||||
localAccounts.value.claudeGroups = allGroups.filter((g) => g.platform === 'claude')
|
||||
localAccounts.value.geminiGroups = allGroups.filter((g) => g.platform === 'gemini')
|
||||
localAccounts.value.openaiGroups = allGroups.filter((g) => g.platform === 'openai')
|
||||
localAccounts.value.droidGroups = allGroups.filter((g) => g.platform === 'droid')
|
||||
}
|
||||
|
||||
showToast('账号列表已刷新', 'success')
|
||||
@@ -1346,6 +1398,9 @@ const createApiKey = async () => {
|
||||
if (form.bedrockAccountId) {
|
||||
baseData.bedrockAccountId = form.bedrockAccountId
|
||||
}
|
||||
if (form.droidAccountId) {
|
||||
baseData.droidAccountId = form.droidAccountId
|
||||
}
|
||||
|
||||
if (form.createType === 'single') {
|
||||
// 单个创建
|
||||
|
||||
@@ -449,6 +449,15 @@
|
||||
/>
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">仅 OpenAI</span>
|
||||
</label>
|
||||
<label class="flex cursor-pointer items-center">
|
||||
<input
|
||||
v-model="form.permissions"
|
||||
class="mr-2 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
|
||||
type="radio"
|
||||
value="droid"
|
||||
/>
|
||||
<span class="text-sm text-gray-700 dark:text-gray-300">仅 Droid</span>
|
||||
</label>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-gray-500 dark:text-gray-400">
|
||||
控制此 API Key 可以访问哪些服务
|
||||
@@ -486,7 +495,7 @@
|
||||
v-model="form.claudeAccountId"
|
||||
:accounts="localAccounts.claude"
|
||||
default-option-text="使用共享账号池"
|
||||
:disabled="form.permissions === 'gemini' || form.permissions === 'openai'"
|
||||
:disabled="form.permissions !== 'all' && form.permissions !== 'claude'"
|
||||
:groups="localAccounts.claudeGroups"
|
||||
placeholder="请选择Claude账号"
|
||||
platform="claude"
|
||||
@@ -500,7 +509,7 @@
|
||||
v-model="form.geminiAccountId"
|
||||
:accounts="localAccounts.gemini"
|
||||
default-option-text="使用共享账号池"
|
||||
:disabled="form.permissions === 'claude' || form.permissions === 'openai'"
|
||||
:disabled="form.permissions !== 'all' && form.permissions !== 'gemini'"
|
||||
:groups="localAccounts.geminiGroups"
|
||||
placeholder="请选择Gemini账号"
|
||||
platform="gemini"
|
||||
@@ -514,7 +523,7 @@
|
||||
v-model="form.openaiAccountId"
|
||||
:accounts="localAccounts.openai"
|
||||
default-option-text="使用共享账号池"
|
||||
:disabled="form.permissions === 'claude' || form.permissions === 'gemini'"
|
||||
:disabled="form.permissions !== 'all' && form.permissions !== 'openai'"
|
||||
:groups="localAccounts.openaiGroups"
|
||||
placeholder="请选择OpenAI账号"
|
||||
platform="openai"
|
||||
@@ -528,12 +537,26 @@
|
||||
v-model="form.bedrockAccountId"
|
||||
:accounts="localAccounts.bedrock"
|
||||
default-option-text="使用共享账号池"
|
||||
:disabled="form.permissions === 'gemini' || form.permissions === 'openai'"
|
||||
:disabled="form.permissions !== 'all' && form.permissions !== 'openai'"
|
||||
:groups="[]"
|
||||
placeholder="请选择Bedrock账号"
|
||||
platform="bedrock"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-sm font-medium text-gray-600 dark:text-gray-400"
|
||||
>Droid 专属账号</label
|
||||
>
|
||||
<AccountSelector
|
||||
v-model="form.droidAccountId"
|
||||
:accounts="localAccounts.droid"
|
||||
default-option-text="使用共享账号池"
|
||||
:disabled="form.permissions !== 'all' && form.permissions !== 'droid'"
|
||||
:groups="localAccounts.droidGroups"
|
||||
placeholder="请选择Droid账号"
|
||||
platform="droid"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-gray-500 dark:text-gray-400">
|
||||
修改绑定账号将影响此API Key的请求路由
|
||||
@@ -717,7 +740,18 @@ const props = defineProps({
|
||||
},
|
||||
accounts: {
|
||||
type: Object,
|
||||
default: () => ({ claude: [], gemini: [] })
|
||||
default: () => ({
|
||||
claude: [],
|
||||
gemini: [],
|
||||
openai: [],
|
||||
bedrock: [],
|
||||
droid: [],
|
||||
claudeGroups: [],
|
||||
geminiGroups: [],
|
||||
openaiGroups: [],
|
||||
droidGroups: [],
|
||||
openaiResponses: []
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -732,10 +766,12 @@ const localAccounts = ref({
|
||||
claude: [],
|
||||
gemini: [],
|
||||
openai: [],
|
||||
bedrock: [], // 添加 Bedrock 账号列表
|
||||
bedrock: [],
|
||||
droid: [],
|
||||
claudeGroups: [],
|
||||
geminiGroups: [],
|
||||
openaiGroups: []
|
||||
openaiGroups: [],
|
||||
droidGroups: []
|
||||
})
|
||||
|
||||
// 支持的客户端列表
|
||||
@@ -768,7 +804,8 @@ const form = reactive({
|
||||
claudeAccountId: '',
|
||||
geminiAccountId: '',
|
||||
openaiAccountId: '',
|
||||
bedrockAccountId: '', // 添加 Bedrock 账号ID
|
||||
bedrockAccountId: '',
|
||||
droidAccountId: '',
|
||||
enableModelRestriction: false,
|
||||
restrictedModels: [],
|
||||
modelInput: '',
|
||||
@@ -930,6 +967,12 @@ const updateApiKey = async () => {
|
||||
data.bedrockAccountId = null
|
||||
}
|
||||
|
||||
if (form.droidAccountId) {
|
||||
data.droidAccountId = form.droidAccountId
|
||||
} else {
|
||||
data.droidAccountId = null
|
||||
}
|
||||
|
||||
// 模型限制 - 始终提交这些字段
|
||||
data.enableModelRestriction = form.enableModelRestriction
|
||||
data.restrictedModels = form.restrictedModels
|
||||
@@ -972,14 +1015,16 @@ const refreshAccounts = async () => {
|
||||
openaiData,
|
||||
openaiResponsesData,
|
||||
bedrockData,
|
||||
droidData,
|
||||
groupsData
|
||||
] = await Promise.all([
|
||||
apiClient.get('/admin/claude-accounts'),
|
||||
apiClient.get('/admin/claude-console-accounts'),
|
||||
apiClient.get('/admin/gemini-accounts'),
|
||||
apiClient.get('/admin/openai-accounts'),
|
||||
apiClient.get('/admin/openai-responses-accounts'), // 获取 OpenAI-Responses 账号
|
||||
apiClient.get('/admin/bedrock-accounts'), // 添加 Bedrock 账号获取
|
||||
apiClient.get('/admin/openai-responses-accounts'),
|
||||
apiClient.get('/admin/bedrock-accounts'),
|
||||
apiClient.get('/admin/droid-accounts'),
|
||||
apiClient.get('/admin/account-groups')
|
||||
])
|
||||
|
||||
@@ -1047,12 +1092,21 @@ const refreshAccounts = async () => {
|
||||
}))
|
||||
}
|
||||
|
||||
if (droidData.success) {
|
||||
localAccounts.value.droid = (droidData.data || []).map((account) => ({
|
||||
...account,
|
||||
platform: 'droid',
|
||||
isDedicated: account.accountType === 'dedicated'
|
||||
}))
|
||||
}
|
||||
|
||||
// 处理分组数据
|
||||
if (groupsData.success) {
|
||||
const allGroups = groupsData.data || []
|
||||
localAccounts.value.claudeGroups = allGroups.filter((g) => g.platform === 'claude')
|
||||
localAccounts.value.geminiGroups = allGroups.filter((g) => g.platform === 'gemini')
|
||||
localAccounts.value.openaiGroups = allGroups.filter((g) => g.platform === 'openai')
|
||||
localAccounts.value.droidGroups = allGroups.filter((g) => g.platform === 'droid')
|
||||
}
|
||||
|
||||
showToast('账号列表已刷新', 'success')
|
||||
@@ -1128,10 +1182,15 @@ onMounted(async () => {
|
||||
claude: props.accounts.claude || [],
|
||||
gemini: props.accounts.gemini || [],
|
||||
openai: openaiAccounts,
|
||||
bedrock: props.accounts.bedrock || [], // 添加 Bedrock 账号
|
||||
bedrock: props.accounts.bedrock || [],
|
||||
droid: (props.accounts.droid || []).map((account) => ({
|
||||
...account,
|
||||
platform: 'droid'
|
||||
})),
|
||||
claudeGroups: props.accounts.claudeGroups || [],
|
||||
geminiGroups: props.accounts.geminiGroups || [],
|
||||
openaiGroups: props.accounts.openaiGroups || []
|
||||
openaiGroups: props.accounts.openaiGroups || [],
|
||||
droidGroups: props.accounts.droidGroups || []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1168,7 +1227,8 @@ onMounted(async () => {
|
||||
// 处理 OpenAI 账号 - 直接使用后端传来的值(已包含 responses: 前缀)
|
||||
form.openaiAccountId = props.apiKey.openaiAccountId || ''
|
||||
|
||||
form.bedrockAccountId = props.apiKey.bedrockAccountId || '' // 添加 Bedrock 账号ID初始化
|
||||
form.bedrockAccountId = props.apiKey.bedrockAccountId || ''
|
||||
form.droidAccountId = props.apiKey.droidAccountId || ''
|
||||
form.restrictedModels = props.apiKey.restrictedModels || []
|
||||
form.allowedClients = props.apiKey.allowedClients || []
|
||||
form.tags = props.apiKey.tags || []
|
||||
|
||||
@@ -104,7 +104,9 @@
|
||||
? 'Claude OAuth 专属账号'
|
||||
: platform === 'openai'
|
||||
? 'OpenAI 专属账号'
|
||||
: 'OAuth 专属账号'
|
||||
: platform === 'droid'
|
||||
? 'Droid 专属账号'
|
||||
: 'OAuth 专属账号'
|
||||
}}
|
||||
</div>
|
||||
<div
|
||||
@@ -241,7 +243,7 @@ const props = defineProps({
|
||||
platform: {
|
||||
type: String,
|
||||
required: true,
|
||||
validator: (value) => ['claude', 'gemini', 'openai', 'bedrock'].includes(value)
|
||||
validator: (value) => ['claude', 'gemini', 'openai', 'bedrock', 'droid'].includes(value)
|
||||
},
|
||||
accounts: {
|
||||
type: Array,
|
||||
@@ -383,6 +385,8 @@ const filteredOAuthAccounts = computed(() => {
|
||||
} else if (props.platform === 'openai') {
|
||||
// 对于 OpenAI,只显示 openai 类型的账号
|
||||
accounts = sortedAccounts.value.filter((a) => a.platform === 'openai')
|
||||
} else if (props.platform === 'droid') {
|
||||
accounts = sortedAccounts.value.filter((a) => a.platform === 'droid')
|
||||
} else {
|
||||
// 其他平台显示所有非特殊类型的账号
|
||||
accounts = sortedAccounts.value.filter(
|
||||
|
||||
@@ -1740,13 +1740,15 @@ const groupOptions = computed(() => {
|
||||
accountGroups.value.forEach((group) => {
|
||||
options.push({
|
||||
value: group.id,
|
||||
label: `${group.name} (${group.platform === 'claude' ? 'Claude' : group.platform === 'gemini' ? 'Gemini' : 'OpenAI'})`,
|
||||
label: `${group.name} (${group.platform === 'claude' ? 'Claude' : group.platform === 'gemini' ? 'Gemini' : group.platform === 'openai' ? 'OpenAI' : 'Droid'})`,
|
||||
icon:
|
||||
group.platform === 'claude'
|
||||
? 'fa-brain'
|
||||
: group.platform === 'gemini'
|
||||
? 'fa-robot'
|
||||
: 'fa-openai'
|
||||
: group.platform === 'openai'
|
||||
? 'fa-openai'
|
||||
: 'fa-robot'
|
||||
})
|
||||
})
|
||||
return options
|
||||
@@ -2303,8 +2305,11 @@ const loadAccounts = async (forceReload = false) => {
|
||||
// Droid 账户
|
||||
if (droidData && droidData.success) {
|
||||
const droidAccounts = (droidData.data || []).map((acc) => {
|
||||
// Droid 不支持 API Key 绑定,固定为 0
|
||||
return { ...acc, platform: 'droid', boundApiKeysCount: 0 }
|
||||
return {
|
||||
...acc,
|
||||
platform: 'droid',
|
||||
boundApiKeysCount: acc.boundApiKeysCount ?? 0
|
||||
}
|
||||
})
|
||||
allAccounts.push(...droidAccounts)
|
||||
}
|
||||
|
||||
@@ -511,6 +511,18 @@
|
||||
{{ getBedrockBindingInfo(key) }}
|
||||
</span>
|
||||
</div>
|
||||
<!-- Droid 绑定 -->
|
||||
<div v-if="key.droidAccountId" class="flex items-center gap-1 text-xs">
|
||||
<span
|
||||
class="inline-flex items-center rounded bg-cyan-100 px-1.5 py-0.5 text-cyan-700 dark:bg-cyan-900/30 dark:text-cyan-300"
|
||||
>
|
||||
<i class="fas fa-robot mr-1 text-[10px]" />
|
||||
Droid
|
||||
</span>
|
||||
<span class="truncate text-gray-600 dark:text-gray-400">
|
||||
{{ getDroidBindingInfo(key) }}
|
||||
</span>
|
||||
</div>
|
||||
<!-- 共享池 -->
|
||||
<div
|
||||
v-if="
|
||||
@@ -518,7 +530,8 @@
|
||||
!key.claudeConsoleAccountId &&
|
||||
!key.geminiAccountId &&
|
||||
!key.openaiAccountId &&
|
||||
!key.bedrockAccountId
|
||||
!key.bedrockAccountId &&
|
||||
!key.droidAccountId
|
||||
"
|
||||
class="text-xs text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
@@ -1182,6 +1195,18 @@
|
||||
{{ getBedrockBindingInfo(key) }}
|
||||
</span>
|
||||
</div>
|
||||
<!-- Droid 绑定 -->
|
||||
<div v-if="key.droidAccountId" class="flex flex-wrap items-center gap-1 text-xs">
|
||||
<span
|
||||
class="inline-flex items-center rounded bg-cyan-100 px-2 py-0.5 text-cyan-700 dark:bg-cyan-900/30 dark:text-cyan-300"
|
||||
>
|
||||
<i class="fas fa-robot mr-1" />
|
||||
Droid
|
||||
</span>
|
||||
<span class="text-gray-600 dark:text-gray-400">
|
||||
{{ getDroidBindingInfo(key) }}
|
||||
</span>
|
||||
</div>
|
||||
<!-- 无绑定时显示共享池 -->
|
||||
<div
|
||||
v-if="
|
||||
@@ -1189,7 +1214,8 @@
|
||||
!key.claudeConsoleAccountId &&
|
||||
!key.geminiAccountId &&
|
||||
!key.openaiAccountId &&
|
||||
!key.bedrockAccountId
|
||||
!key.bedrockAccountId &&
|
||||
!key.droidAccountId
|
||||
"
|
||||
class="text-xs text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
@@ -1921,9 +1947,11 @@ const accounts = ref({
|
||||
openai: [],
|
||||
openaiResponses: [], // 添加 OpenAI-Responses 账号列表
|
||||
bedrock: [],
|
||||
droid: [],
|
||||
claudeGroups: [],
|
||||
geminiGroups: [],
|
||||
openaiGroups: []
|
||||
openaiGroups: [],
|
||||
droidGroups: []
|
||||
})
|
||||
const editingExpiryKey = ref(null)
|
||||
const expiryEditModalRef = ref(null)
|
||||
@@ -2031,12 +2059,17 @@ const getBindingDisplayStrings = (key) => {
|
||||
appendBindingRow('Bedrock', getBedrockBindingInfo(key))
|
||||
}
|
||||
|
||||
if (key.droidAccountId) {
|
||||
appendBindingRow('Droid', getDroidBindingInfo(key))
|
||||
}
|
||||
|
||||
if (
|
||||
!key.claudeAccountId &&
|
||||
!key.claudeConsoleAccountId &&
|
||||
!key.geminiAccountId &&
|
||||
!key.openaiAccountId &&
|
||||
!key.bedrockAccountId
|
||||
!key.bedrockAccountId &&
|
||||
!key.droidAccountId
|
||||
) {
|
||||
collect('共享池')
|
||||
}
|
||||
@@ -2196,6 +2229,7 @@ const loadAccounts = async () => {
|
||||
openaiData,
|
||||
openaiResponsesData,
|
||||
bedrockData,
|
||||
droidData,
|
||||
groupsData
|
||||
] = await Promise.all([
|
||||
apiClient.get('/admin/claude-accounts'),
|
||||
@@ -2204,6 +2238,7 @@ const loadAccounts = async () => {
|
||||
apiClient.get('/admin/openai-accounts'),
|
||||
apiClient.get('/admin/openai-responses-accounts'), // 加载 OpenAI-Responses 账号
|
||||
apiClient.get('/admin/bedrock-accounts'),
|
||||
apiClient.get('/admin/droid-accounts'),
|
||||
apiClient.get('/admin/account-groups')
|
||||
])
|
||||
|
||||
@@ -2260,12 +2295,21 @@ const loadAccounts = async () => {
|
||||
}))
|
||||
}
|
||||
|
||||
if (droidData.success) {
|
||||
accounts.value.droid = (droidData.data || []).map((account) => ({
|
||||
...account,
|
||||
platform: 'droid',
|
||||
isDedicated: account.accountType === 'dedicated'
|
||||
}))
|
||||
}
|
||||
|
||||
if (groupsData.success) {
|
||||
// 处理分组数据
|
||||
const allGroups = groupsData.data || []
|
||||
accounts.value.claudeGroups = allGroups.filter((g) => g.platform === 'claude')
|
||||
accounts.value.geminiGroups = allGroups.filter((g) => g.platform === 'gemini')
|
||||
accounts.value.openaiGroups = allGroups.filter((g) => g.platform === 'openai')
|
||||
accounts.value.droidGroups = allGroups.filter((g) => g.platform === 'droid')
|
||||
}
|
||||
} catch (error) {
|
||||
// console.error('加载账户列表失败:', error)
|
||||
@@ -2381,6 +2425,11 @@ const getBoundAccountName = (accountId) => {
|
||||
return `分组-${openaiGroup.name}`
|
||||
}
|
||||
|
||||
const droidGroup = accounts.value.droidGroups.find((g) => g.id === groupId)
|
||||
if (droidGroup) {
|
||||
return `分组-${droidGroup.name}`
|
||||
}
|
||||
|
||||
// 如果找不到分组,返回分组ID的前8位
|
||||
return `分组-${groupId.substring(0, 8)}`
|
||||
}
|
||||
@@ -2428,6 +2477,11 @@ const getBoundAccountName = (accountId) => {
|
||||
return `${bedrockAccount.name}`
|
||||
}
|
||||
|
||||
const droidAccount = accounts.value.droid.find((acc) => acc.id === accountId)
|
||||
if (droidAccount) {
|
||||
return `${droidAccount.name}`
|
||||
}
|
||||
|
||||
// 如果找不到,返回账户ID的前8位
|
||||
return `${accountId.substring(0, 8)}`
|
||||
}
|
||||
@@ -2530,6 +2584,24 @@ const getBedrockBindingInfo = (key) => {
|
||||
return ''
|
||||
}
|
||||
|
||||
const getDroidBindingInfo = (key) => {
|
||||
if (key.droidAccountId) {
|
||||
const info = getBoundAccountName(key.droidAccountId)
|
||||
if (key.droidAccountId.startsWith('group:')) {
|
||||
return info
|
||||
}
|
||||
const account = accounts.value.droid.find((acc) => acc.id === key.droidAccountId)
|
||||
if (!account) {
|
||||
return `⚠️ ${info} (账户不存在)`
|
||||
}
|
||||
if (account.accountType === 'dedicated') {
|
||||
return `🔒 专属-${info}`
|
||||
}
|
||||
return info
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// 检查API Key是否过期
|
||||
const isApiKeyExpired = (expiresAt) => {
|
||||
if (!expiresAt) return false
|
||||
@@ -3654,7 +3726,9 @@ const exportToExcel = () => {
|
||||
? '仅Gemini'
|
||||
: key.permissions === 'openai'
|
||||
? '仅OpenAI'
|
||||
: key.permissions || '',
|
||||
: key.permissions === 'droid'
|
||||
? '仅Droid'
|
||||
: key.permissions || '',
|
||||
|
||||
// 限制配置
|
||||
令牌限制: key.tokenLimit === '0' || key.tokenLimit === 0 ? '无限制' : key.tokenLimit || '',
|
||||
@@ -3686,6 +3760,7 @@ const exportToExcel = () => {
|
||||
OpenAI专属账户: key.openaiAccountId || '',
|
||||
'Azure OpenAI专属账户': key.azureOpenaiAccountId || '',
|
||||
Bedrock专属账户: key.bedrockAccountId || '',
|
||||
Droid专属账户: key.droidAccountId || '',
|
||||
|
||||
// 模型和客户端限制
|
||||
启用模型限制: key.enableModelRestriction ? '是' : '否',
|
||||
|
||||
Reference in New Issue
Block a user