mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 09:38:02 +00:00
feat: 完善API Keys页面多平台账户绑定信息展示
- 支持显示Claude、Gemini和OpenAI三个平台的账户绑定信息 - 添加账户状态提醒(不存在、专属、分组、共享池) - 优化UI设计,使用彩色标签区分不同平台 - 改进响应式布局,适配移动端和平板设备 - 修复OpenAI账户绑定数量统计问题 - 修复删除账户时OpenAI绑定检查逻辑 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1010,8 +1010,12 @@ const loadAccounts = async (forceReload = false) => {
|
|||||||
}
|
}
|
||||||
if (openaiData.success) {
|
if (openaiData.success) {
|
||||||
const openaiAccounts = (openaiData.data || []).map((acc) => {
|
const openaiAccounts = (openaiData.data || []).map((acc) => {
|
||||||
|
// 计算每个OpenAI账户绑定的API Key数量
|
||||||
|
const boundApiKeysCount = apiKeys.value.filter(
|
||||||
|
(key) => key.openaiAccountId === acc.id
|
||||||
|
).length
|
||||||
const groupInfo = accountGroupMap.value.get(acc.id) || null
|
const groupInfo = accountGroupMap.value.get(acc.id) || null
|
||||||
return { ...acc, platform: 'openai', boundApiKeysCount: 0, groupInfo }
|
return { ...acc, platform: 'openai', boundApiKeysCount, groupInfo }
|
||||||
})
|
})
|
||||||
allAccounts.push(...openaiAccounts)
|
allAccounts.push(...openaiAccounts)
|
||||||
}
|
}
|
||||||
@@ -1210,7 +1214,10 @@ const editAccount = (account) => {
|
|||||||
const deleteAccount = async (account) => {
|
const deleteAccount = async (account) => {
|
||||||
// 检查是否有API Key绑定到此账号
|
// 检查是否有API Key绑定到此账号
|
||||||
const boundKeysCount = apiKeys.value.filter(
|
const boundKeysCount = apiKeys.value.filter(
|
||||||
(key) => key.claudeAccountId === account.id || key.geminiAccountId === account.id
|
(key) =>
|
||||||
|
key.claudeAccountId === account.id ||
|
||||||
|
key.geminiAccountId === account.id ||
|
||||||
|
key.openaiAccountId === account.id
|
||||||
).length
|
).length
|
||||||
|
|
||||||
if (boundKeysCount > 0) {
|
if (boundKeysCount > 0) {
|
||||||
|
|||||||
@@ -206,18 +206,60 @@
|
|||||||
<div class="truncate text-xs text-gray-500" :title="key.id">
|
<div class="truncate text-xs text-gray-500" :title="key.id">
|
||||||
{{ key.id }}
|
{{ key.id }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-1 truncate text-xs text-gray-500">
|
<!-- 账户绑定信息 -->
|
||||||
<span
|
<div class="mt-1.5 space-y-1">
|
||||||
v-if="key.claudeAccountId"
|
<!-- Claude 绑定 -->
|
||||||
:title="`绑定: ${getBoundAccountName(key.claudeAccountId)}`"
|
<div
|
||||||
|
v-if="key.claudeAccountId || key.claudeConsoleAccountId"
|
||||||
|
class="flex items-center gap-1 text-xs"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
class="inline-flex items-center rounded bg-indigo-100 px-1.5 py-0.5 text-indigo-700"
|
||||||
|
>
|
||||||
|
<i class="fas fa-brain mr-1 text-[10px]" />
|
||||||
|
Claude
|
||||||
|
</span>
|
||||||
|
<span class="truncate text-gray-600">
|
||||||
|
{{ getClaudeBindingInfo(key) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<!-- Gemini 绑定 -->
|
||||||
|
<div v-if="key.geminiAccountId" class="flex items-center gap-1 text-xs">
|
||||||
|
<span
|
||||||
|
class="inline-flex items-center rounded bg-yellow-100 px-1.5 py-0.5 text-yellow-700"
|
||||||
|
>
|
||||||
|
<i class="fas fa-robot mr-1 text-[10px]" />
|
||||||
|
Gemini
|
||||||
|
</span>
|
||||||
|
<span class="truncate text-gray-600">
|
||||||
|
{{ getGeminiBindingInfo(key) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<!-- OpenAI 绑定 -->
|
||||||
|
<div v-if="key.openaiAccountId" class="flex items-center gap-1 text-xs">
|
||||||
|
<span
|
||||||
|
class="inline-flex items-center rounded bg-gray-100 px-1.5 py-0.5 text-gray-700"
|
||||||
|
>
|
||||||
|
<i class="fa-openai mr-1 text-[10px]" />
|
||||||
|
OpenAI
|
||||||
|
</span>
|
||||||
|
<span class="truncate text-gray-600">
|
||||||
|
{{ getOpenAIBindingInfo(key) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<!-- 无绑定时显示共享池 -->
|
||||||
|
<div
|
||||||
|
v-if="
|
||||||
|
!key.claudeAccountId &&
|
||||||
|
!key.claudeConsoleAccountId &&
|
||||||
|
!key.geminiAccountId &&
|
||||||
|
!key.openaiAccountId
|
||||||
|
"
|
||||||
|
class="text-xs text-gray-500"
|
||||||
>
|
>
|
||||||
<i class="fas fa-link mr-1" />
|
|
||||||
{{ getBoundAccountName(key.claudeAccountId) }}
|
|
||||||
</span>
|
|
||||||
<span v-else>
|
|
||||||
<i class="fas fa-share-alt mr-1" />
|
<i class="fas fa-share-alt mr-1" />
|
||||||
共享池
|
使用共享池
|
||||||
</span>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -712,16 +754,58 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 绑定信息 -->
|
<!-- 账户绑定信息 -->
|
||||||
<div class="mb-3 text-xs text-gray-600">
|
<div class="mb-3 space-y-1.5">
|
||||||
<span v-if="key.claudeAccountId || key.claudeConsoleAccountId">
|
<!-- Claude 绑定 -->
|
||||||
<i class="fas fa-link mr-1" />
|
<div
|
||||||
绑定: {{ getBoundAccountName(key.claudeAccountId, key.claudeConsoleAccountId) }}
|
v-if="key.claudeAccountId || key.claudeConsoleAccountId"
|
||||||
</span>
|
class="flex flex-wrap items-center gap-1 text-xs"
|
||||||
<span v-else>
|
>
|
||||||
|
<span
|
||||||
|
class="inline-flex items-center rounded bg-indigo-100 px-2 py-0.5 text-indigo-700"
|
||||||
|
>
|
||||||
|
<i class="fas fa-brain mr-1" />
|
||||||
|
Claude
|
||||||
|
</span>
|
||||||
|
<span class="text-gray-600">
|
||||||
|
{{ getClaudeBindingInfo(key) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<!-- Gemini 绑定 -->
|
||||||
|
<div v-if="key.geminiAccountId" class="flex flex-wrap items-center gap-1 text-xs">
|
||||||
|
<span
|
||||||
|
class="inline-flex items-center rounded bg-yellow-100 px-2 py-0.5 text-yellow-700"
|
||||||
|
>
|
||||||
|
<i class="fas fa-robot mr-1" />
|
||||||
|
Gemini
|
||||||
|
</span>
|
||||||
|
<span class="text-gray-600">
|
||||||
|
{{ getGeminiBindingInfo(key) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<!-- OpenAI 绑定 -->
|
||||||
|
<div v-if="key.openaiAccountId" class="flex flex-wrap items-center gap-1 text-xs">
|
||||||
|
<span class="inline-flex items-center rounded bg-gray-100 px-2 py-0.5 text-gray-700">
|
||||||
|
<i class="fa-openai mr-1" />
|
||||||
|
OpenAI
|
||||||
|
</span>
|
||||||
|
<span class="text-gray-600">
|
||||||
|
{{ getOpenAIBindingInfo(key) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<!-- 无绑定时显示共享池 -->
|
||||||
|
<div
|
||||||
|
v-if="
|
||||||
|
!key.claudeAccountId &&
|
||||||
|
!key.claudeConsoleAccountId &&
|
||||||
|
!key.geminiAccountId &&
|
||||||
|
!key.openaiAccountId
|
||||||
|
"
|
||||||
|
class="text-xs text-gray-500"
|
||||||
|
>
|
||||||
<i class="fas fa-share-alt mr-1" />
|
<i class="fas fa-share-alt mr-1" />
|
||||||
使用共享池
|
使用共享池
|
||||||
</span>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 统计信息 -->
|
<!-- 统计信息 -->
|
||||||
@@ -1300,6 +1384,12 @@ const getBoundAccountName = (accountId) => {
|
|||||||
return `分组-${geminiGroup.name}`
|
return `分组-${geminiGroup.name}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 从OpenAI分组中查找
|
||||||
|
const openaiGroup = accounts.value.openaiGroups.find((g) => g.id === groupId)
|
||||||
|
if (openaiGroup) {
|
||||||
|
return `分组-${openaiGroup.name}`
|
||||||
|
}
|
||||||
|
|
||||||
// 如果找不到分组,返回分组ID的前8位
|
// 如果找不到分组,返回分组ID的前8位
|
||||||
return `分组-${groupId.substring(0, 8)}`
|
return `分组-${groupId.substring(0, 8)}`
|
||||||
}
|
}
|
||||||
@@ -1307,17 +1397,92 @@ const getBoundAccountName = (accountId) => {
|
|||||||
// 从Claude账户列表中查找
|
// 从Claude账户列表中查找
|
||||||
const claudeAccount = accounts.value.claude.find((acc) => acc.id === accountId)
|
const claudeAccount = accounts.value.claude.find((acc) => acc.id === accountId)
|
||||||
if (claudeAccount) {
|
if (claudeAccount) {
|
||||||
return `账户-${claudeAccount.name}`
|
return `${claudeAccount.name}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从Gemini账户列表中查找
|
// 从Gemini账户列表中查找
|
||||||
const geminiAccount = accounts.value.gemini.find((acc) => acc.id === accountId)
|
const geminiAccount = accounts.value.gemini.find((acc) => acc.id === accountId)
|
||||||
if (geminiAccount) {
|
if (geminiAccount) {
|
||||||
return `账户-${geminiAccount.name}`
|
return `${geminiAccount.name}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从OpenAI账户列表中查找
|
||||||
|
const openaiAccount = accounts.value.openai.find((acc) => acc.id === accountId)
|
||||||
|
if (openaiAccount) {
|
||||||
|
return `${openaiAccount.name}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果找不到,返回账户ID的前8位
|
// 如果找不到,返回账户ID的前8位
|
||||||
return `账户-${accountId.substring(0, 8)}`
|
return `${accountId.substring(0, 8)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取Claude绑定信息
|
||||||
|
const getClaudeBindingInfo = (key) => {
|
||||||
|
if (key.claudeAccountId) {
|
||||||
|
const info = getBoundAccountName(key.claudeAccountId)
|
||||||
|
if (key.claudeAccountId.startsWith('group:')) {
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
// 检查账户是否存在
|
||||||
|
const account = accounts.value.claude.find((acc) => acc.id === key.claudeAccountId)
|
||||||
|
if (!account) {
|
||||||
|
return `⚠️ ${info} (账户不存在)`
|
||||||
|
}
|
||||||
|
if (account.accountType === 'dedicated') {
|
||||||
|
return `🔒 专属-${info}`
|
||||||
|
}
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
if (key.claudeConsoleAccountId) {
|
||||||
|
const account = accounts.value.claude.find(
|
||||||
|
(acc) => acc.id === key.claudeConsoleAccountId && acc.platform === 'claude-console'
|
||||||
|
)
|
||||||
|
if (!account) {
|
||||||
|
return `⚠️ Console账户不存在`
|
||||||
|
}
|
||||||
|
return `Console-${account.name}`
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取Gemini绑定信息
|
||||||
|
const getGeminiBindingInfo = (key) => {
|
||||||
|
if (key.geminiAccountId) {
|
||||||
|
const info = getBoundAccountName(key.geminiAccountId)
|
||||||
|
if (key.geminiAccountId.startsWith('group:')) {
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
// 检查账户是否存在
|
||||||
|
const account = accounts.value.gemini.find((acc) => acc.id === key.geminiAccountId)
|
||||||
|
if (!account) {
|
||||||
|
return `⚠️ ${info} (账户不存在)`
|
||||||
|
}
|
||||||
|
if (account.accountType === 'dedicated') {
|
||||||
|
return `🔒 专属-${info}`
|
||||||
|
}
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取OpenAI绑定信息
|
||||||
|
const getOpenAIBindingInfo = (key) => {
|
||||||
|
if (key.openaiAccountId) {
|
||||||
|
const info = getBoundAccountName(key.openaiAccountId)
|
||||||
|
if (key.openaiAccountId.startsWith('group:')) {
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
// 检查账户是否存在
|
||||||
|
const account = accounts.value.openai.find((acc) => acc.id === key.openaiAccountId)
|
||||||
|
if (!account) {
|
||||||
|
return `⚠️ ${info} (账户不存在)`
|
||||||
|
}
|
||||||
|
if (account.accountType === 'dedicated') {
|
||||||
|
return `🔒 专属-${info}`
|
||||||
|
}
|
||||||
|
return info
|
||||||
|
}
|
||||||
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查API Key是否过期
|
// 检查API Key是否过期
|
||||||
|
|||||||
Reference in New Issue
Block a user