mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 00:53:33 +00:00
fix: 修复 web/admin-spa 合并冲突和构建错误
- 恢复 web/admin-spa 目录到 dev 分支状态 - 修复 ApiKeysView.vue 中的重复变量声明问题 - 重新构建前端项目确保正常运行 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -288,92 +288,36 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-3">模型映射表 (可选)</label>
|
||||
<div class="bg-blue-50 p-3 rounded-lg mb-3">
|
||||
<p class="text-xs text-blue-700">
|
||||
<i class="fas fa-info-circle mr-1" />
|
||||
留空表示支持所有模型且不修改请求。配置映射后,左侧模型会被识别为支持的模型,右侧是实际发送的模型。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 模型映射表 -->
|
||||
<div class="space-y-2 mb-3">
|
||||
<div
|
||||
v-for="(mapping, index) in modelMappings"
|
||||
:key="index"
|
||||
class="flex items-center gap-2"
|
||||
>
|
||||
<input
|
||||
v-model="mapping.from"
|
||||
type="text"
|
||||
class="form-input flex-1"
|
||||
placeholder="原始模型名称"
|
||||
>
|
||||
<i class="fas fa-arrow-right text-gray-400" />
|
||||
<input
|
||||
v-model="mapping.to"
|
||||
type="text"
|
||||
class="form-input flex-1"
|
||||
placeholder="映射后的模型名称"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="p-2 text-red-500 hover:bg-red-50 rounded-lg transition-colors"
|
||||
@click="removeModelMapping(index)"
|
||||
>
|
||||
<i class="fas fa-trash" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 添加映射按钮 -->
|
||||
<button
|
||||
type="button"
|
||||
class="w-full px-4 py-2 border-2 border-dashed border-gray-300 text-gray-600 rounded-lg hover:border-gray-400 hover:text-gray-700 transition-colors"
|
||||
@click="addModelMapping"
|
||||
>
|
||||
<i class="fas fa-plus mr-2" />
|
||||
添加模型映射
|
||||
</button>
|
||||
|
||||
<!-- 快捷添加按钮 -->
|
||||
<div class="mt-3 flex flex-wrap gap-2">
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-3">支持的模型 (可选)--注意,ClaudeCode必须加上hiku模型!</label>
|
||||
<div class="mb-2 flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-blue-100 text-blue-700 rounded-lg hover:bg-blue-200 transition-colors"
|
||||
@click="addPresetMapping('claude-3-5-sonnet-20241022', 'claude-3-5-sonnet-20241022')"
|
||||
@click="addPresetModel('claude-sonnet-4-20250514')"
|
||||
>
|
||||
+ Sonnet 3.5
|
||||
+ claude-sonnet-4-20250514
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-purple-100 text-purple-700 rounded-lg hover:bg-purple-200 transition-colors"
|
||||
@click="addPresetMapping('claude-3-opus-20240229', 'claude-3-opus-20240229')"
|
||||
@click="addPresetModel('claude-opus-4-20250514')"
|
||||
>
|
||||
+ Opus 3
|
||||
+ claude-opus-4-20250514
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-green-100 text-green-700 rounded-lg hover:bg-green-200 transition-colors"
|
||||
@click="addPresetMapping('claude-3-5-haiku-20241022', 'claude-3-5-haiku-20241022')"
|
||||
class="px-3 py-1 text-xs bg-green-100 text-green-700 rounded-lg hover:bg-purple-200 transition-colors"
|
||||
@click="addPresetModel('claude-3-5-haiku-20241022')"
|
||||
>
|
||||
+ Haiku 3.5
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-orange-100 text-orange-700 rounded-lg hover:bg-orange-200 transition-colors"
|
||||
@click="addPresetMapping('claude-sonnet-4-20250514', 'claude-3-5-sonnet-20241022')"
|
||||
>
|
||||
+ Sonnet 4 → 3.5
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-red-100 text-red-700 rounded-lg hover:bg-red-200 transition-colors"
|
||||
@click="addPresetMapping('claude-opus-4-20250514', 'claude-3-opus-20240229')"
|
||||
>
|
||||
+ Opus 4 → 3
|
||||
+ claude-3-5-haiku-20241022
|
||||
</button>
|
||||
</div>
|
||||
<textarea
|
||||
v-model="form.supportedModels"
|
||||
rows="3"
|
||||
class="form-input w-full resize-none"
|
||||
placeholder="每行一个模型,留空表示支持所有模型。特别注意,ClaudeCode必须加上hiku模型!"
|
||||
/>
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
留空表示支持所有模型。如果指定模型,请求中的模型不在列表内将不会调度到此账号
|
||||
</p>
|
||||
@@ -697,92 +641,36 @@
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-3">模型映射表 (可选)</label>
|
||||
<div class="bg-blue-50 p-3 rounded-lg mb-3">
|
||||
<p class="text-xs text-blue-700">
|
||||
<i class="fas fa-info-circle mr-1" />
|
||||
留空表示支持所有模型且不修改请求。配置映射后,左侧模型会被识别为支持的模型,右侧是实际发送的模型。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 模型映射表 -->
|
||||
<div class="space-y-2 mb-3">
|
||||
<div
|
||||
v-for="(mapping, index) in modelMappings"
|
||||
:key="index"
|
||||
class="flex items-center gap-2"
|
||||
>
|
||||
<input
|
||||
v-model="mapping.from"
|
||||
type="text"
|
||||
class="form-input flex-1"
|
||||
placeholder="原始模型名称"
|
||||
>
|
||||
<i class="fas fa-arrow-right text-gray-400" />
|
||||
<input
|
||||
v-model="mapping.to"
|
||||
type="text"
|
||||
class="form-input flex-1"
|
||||
placeholder="映射后的模型名称"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="p-2 text-red-500 hover:bg-red-50 rounded-lg transition-colors"
|
||||
@click="removeModelMapping(index)"
|
||||
>
|
||||
<i class="fas fa-trash" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 添加映射按钮 -->
|
||||
<button
|
||||
type="button"
|
||||
class="w-full px-4 py-2 border-2 border-dashed border-gray-300 text-gray-600 rounded-lg hover:border-gray-400 hover:text-gray-700 transition-colors"
|
||||
@click="addModelMapping"
|
||||
>
|
||||
<i class="fas fa-plus mr-2" />
|
||||
添加模型映射
|
||||
</button>
|
||||
|
||||
<!-- 快捷添加按钮 -->
|
||||
<div class="mt-3 flex flex-wrap gap-2">
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-3">支持的模型 (可选)</label>
|
||||
<div class="mb-2 flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-blue-100 text-blue-700 rounded-lg hover:bg-blue-200 transition-colors"
|
||||
@click="addPresetMapping('claude-3-5-sonnet-20241022', 'claude-3-5-sonnet-20241022')"
|
||||
@click="addPresetModel('claude-sonnet-4-20250514')"
|
||||
>
|
||||
+ Sonnet 3.5
|
||||
+ claude-sonnet-4-20250514
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-purple-100 text-purple-700 rounded-lg hover:bg-purple-200 transition-colors"
|
||||
@click="addPresetMapping('claude-3-opus-20240229', 'claude-3-opus-20240229')"
|
||||
@click="addPresetModel('claude-opus-4-20250514')"
|
||||
>
|
||||
+ Opus 3
|
||||
+ claude-opus-4-20250514
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-green-100 text-green-700 rounded-lg hover:bg-green-200 transition-colors"
|
||||
@click="addPresetMapping('claude-3-5-haiku-20241022', 'claude-3-5-haiku-20241022')"
|
||||
class="px-3 py-1 text-xs bg-green-100 text-green-700 rounded-lg hover:bg-purple-200 transition-colors"
|
||||
@click="addPresetModel('claude-3-5-haiku-20241022')"
|
||||
>
|
||||
+ Haiku 3.5
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-orange-100 text-orange-700 rounded-lg hover:bg-orange-200 transition-colors"
|
||||
@click="addPresetMapping('claude-sonnet-4-20250514', 'claude-3-5-sonnet-20241022')"
|
||||
>
|
||||
+ Sonnet 4 → 3.5
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="px-3 py-1 text-xs bg-red-100 text-red-700 rounded-lg hover:bg-red-200 transition-colors"
|
||||
@click="addPresetMapping('claude-opus-4-20250514', 'claude-3-opus-20240229')"
|
||||
>
|
||||
+ Opus 4 → 3
|
||||
+ claude-3-5-haiku-20241022
|
||||
</button>
|
||||
</div>
|
||||
<textarea
|
||||
v-model="form.supportedModels"
|
||||
rows="3"
|
||||
class="form-input w-full resize-none"
|
||||
placeholder="每行一个模型,留空表示支持所有模型。特别注意,ClaudeCode必须加上hiku模型!"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
@@ -968,32 +856,11 @@ const form = ref({
|
||||
apiUrl: props.account?.apiUrl || '',
|
||||
apiKey: props.account?.apiKey || '',
|
||||
priority: props.account?.priority || 50,
|
||||
supportedModels: props.account?.supportedModels?.join('\n') || '',
|
||||
userAgent: props.account?.userAgent || '',
|
||||
rateLimitDuration: props.account?.rateLimitDuration || 60
|
||||
})
|
||||
|
||||
// 模型映射表数据
|
||||
const modelMappings = ref([])
|
||||
|
||||
// 初始化模型映射表
|
||||
const initModelMappings = () => {
|
||||
if (props.account?.supportedModels) {
|
||||
// 如果是对象格式(新的映射表)
|
||||
if (typeof props.account.supportedModels === 'object' && !Array.isArray(props.account.supportedModels)) {
|
||||
modelMappings.value = Object.entries(props.account.supportedModels).map(([from, to]) => ({
|
||||
from,
|
||||
to
|
||||
}))
|
||||
} else if (Array.isArray(props.account.supportedModels)) {
|
||||
// 如果是数组格式(旧格式),转换为映射表
|
||||
modelMappings.value = props.account.supportedModels.map(model => ({
|
||||
from: model,
|
||||
to: model
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 表单验证错误
|
||||
const errors = ref({
|
||||
name: '',
|
||||
@@ -1188,7 +1055,9 @@ const createAccount = async () => {
|
||||
data.apiUrl = form.value.apiUrl
|
||||
data.apiKey = form.value.apiKey
|
||||
data.priority = form.value.priority || 50
|
||||
data.supportedModels = convertMappingsToObject() || {}
|
||||
data.supportedModels = form.value.supportedModels
|
||||
? form.value.supportedModels.split('\n').filter(m => m.trim())
|
||||
: []
|
||||
data.userAgent = form.value.userAgent || null
|
||||
data.rateLimitDuration = form.value.rateLimitDuration || 60
|
||||
}
|
||||
@@ -1305,7 +1174,9 @@ const updateAccount = async () => {
|
||||
data.apiKey = form.value.apiKey
|
||||
}
|
||||
data.priority = form.value.priority || 50
|
||||
data.supportedModels = convertMappingsToObject() || {}
|
||||
data.supportedModels = form.value.supportedModels
|
||||
? form.value.supportedModels.split('\n').filter(m => m.trim())
|
||||
: []
|
||||
data.userAgent = form.value.userAgent || null
|
||||
data.rateLimitDuration = form.value.rateLimitDuration || 60
|
||||
}
|
||||
@@ -1421,44 +1292,28 @@ watch(() => form.value.groupId, (newGroupId) => {
|
||||
}
|
||||
})
|
||||
|
||||
// 添加模型映射
|
||||
const addModelMapping = () => {
|
||||
modelMappings.value.push({ from: '', to: '' })
|
||||
}
|
||||
|
||||
// 移除模型映射
|
||||
const removeModelMapping = (index) => {
|
||||
modelMappings.value.splice(index, 1)
|
||||
}
|
||||
|
||||
// 添加预设映射
|
||||
const addPresetMapping = (from, to) => {
|
||||
// 检查是否已存在相同的映射
|
||||
const exists = modelMappings.value.some(mapping => mapping.from === from)
|
||||
if (exists) {
|
||||
showToast(`模型 ${from} 的映射已存在`, 'info')
|
||||
// 添加预设模型
|
||||
const addPresetModel = (modelName) => {
|
||||
// 获取当前模型列表
|
||||
const currentModels = form.value.supportedModels
|
||||
? form.value.supportedModels.split('\n').filter(m => m.trim())
|
||||
: []
|
||||
|
||||
// 检查是否已存在
|
||||
if (currentModels.includes(modelName)) {
|
||||
showToast(`模型 ${modelName} 已存在`, 'info')
|
||||
return
|
||||
}
|
||||
|
||||
modelMappings.value.push({ from, to })
|
||||
showToast(`已添加映射: ${from} → ${to}`, 'success')
|
||||
}
|
||||
|
||||
// 将模型映射表转换为对象格式
|
||||
const convertMappingsToObject = () => {
|
||||
const mapping = {}
|
||||
modelMappings.value.forEach(item => {
|
||||
if (item.from && item.to) {
|
||||
mapping[item.from] = item.to
|
||||
}
|
||||
})
|
||||
return Object.keys(mapping).length > 0 ? mapping : null
|
||||
// 添加到列表
|
||||
currentModels.push(modelName)
|
||||
form.value.supportedModels = currentModels.join('\n')
|
||||
showToast(`已添加模型 ${modelName}`, 'success')
|
||||
}
|
||||
|
||||
// 监听账户变化,更新表单
|
||||
watch(() => props.account, (newAccount) => {
|
||||
if (newAccount) {
|
||||
initModelMappings()
|
||||
// 重新初始化代理配置
|
||||
const proxyConfig = newAccount.proxy && newAccount.proxy.host && newAccount.proxy.port
|
||||
? {
|
||||
@@ -1493,6 +1348,7 @@ watch(() => props.account, (newAccount) => {
|
||||
apiUrl: newAccount.apiUrl || '',
|
||||
apiKey: '', // 编辑模式不显示现有的 API Key
|
||||
priority: newAccount.priority || 50,
|
||||
supportedModels: newAccount.supportedModels?.join('\n') || '',
|
||||
userAgent: newAccount.userAgent || '',
|
||||
rateLimitDuration: newAccount.rateLimitDuration || 60
|
||||
}
|
||||
@@ -1514,7 +1370,4 @@ watch(() => props.account, (newAccount) => {
|
||||
}
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// 初始化时调用
|
||||
initModelMappings()
|
||||
</script>
|
||||
@@ -534,6 +534,7 @@
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { showToast } from '@/utils/toast'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useClientsStore } from '@/stores/clients'
|
||||
import { useApiKeysStore } from '@/stores/apiKeys'
|
||||
import { apiClient } from '@/config/api'
|
||||
@@ -551,6 +552,7 @@ const props = defineProps({
|
||||
|
||||
const emit = defineEmits(['close', 'success'])
|
||||
|
||||
const authStore = useAuthStore()
|
||||
const clientsStore = useClientsStore()
|
||||
const apiKeysStore = useApiKeysStore()
|
||||
const loading = ref(false)
|
||||
@@ -636,6 +638,8 @@ 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
|
||||
}
|
||||
|
||||
@@ -647,25 +651,6 @@ const updateApiKey = async () => {
|
||||
data.enableClientRestriction = form.enableClientRestriction
|
||||
data.allowedClients = form.allowedClients
|
||||
|
||||
// 处理Claude账户绑定(区分OAuth和Console)
|
||||
if (form.claudeAccountId) {
|
||||
if (form.claudeAccountId.startsWith('console:')) {
|
||||
// Claude Console账户
|
||||
data.claudeConsoleAccountId = form.claudeAccountId.substring(8);
|
||||
data.claudeAccountId = null; // 清空OAuth绑定
|
||||
} else {
|
||||
// Claude OAuth账户
|
||||
data.claudeAccountId = form.claudeAccountId;
|
||||
data.claudeConsoleAccountId = null; // 清空Console绑定
|
||||
}
|
||||
} else {
|
||||
data.claudeAccountId = null;
|
||||
data.claudeConsoleAccountId = null;
|
||||
}
|
||||
|
||||
// Gemini账户绑定
|
||||
data.geminiAccountId = form.geminiAccountId || null;
|
||||
|
||||
const result = await apiClient.put(`/admin/api-keys/${props.apiKey.id}`, data)
|
||||
|
||||
if (result.success) {
|
||||
@@ -762,15 +747,7 @@ onMounted(async () => {
|
||||
form.concurrencyLimit = props.apiKey.concurrencyLimit || ''
|
||||
form.dailyCostLimit = props.apiKey.dailyCostLimit || ''
|
||||
form.permissions = props.apiKey.permissions || 'all'
|
||||
// 处理Claude账户绑定初始化
|
||||
if (props.apiKey.claudeAccountId) {
|
||||
form.claudeAccountId = props.apiKey.claudeAccountId;
|
||||
} else if (props.apiKey.claudeConsoleAccountId) {
|
||||
form.claudeAccountId = `console:${props.apiKey.claudeConsoleAccountId}`;
|
||||
} else {
|
||||
form.claudeAccountId = '';
|
||||
}
|
||||
|
||||
form.claudeAccountId = props.apiKey.claudeAccountId || ''
|
||||
form.geminiAccountId = props.apiKey.geminiAccountId || ''
|
||||
form.restrictedModels = props.apiKey.restrictedModels || []
|
||||
form.allowedClients = props.apiKey.allowedClients || []
|
||||
|
||||
@@ -472,10 +472,10 @@
|
||||
:default-time="defaultTime"
|
||||
size="small"
|
||||
style="width: 280px;"
|
||||
@update:model-value="(value) => onApiKeyCustomDateRangeChange(key.id, value)"
|
||||
class="api-key-date-picker"
|
||||
:clearable="true"
|
||||
:unlink-panels="false"
|
||||
@update:model-value="(value) => onApiKeyCustomDateRangeChange(key.id, value)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -989,11 +989,6 @@ const accounts = ref({ claude: [], gemini: [], claudeGroups: [], geminiGroups: [
|
||||
const editingExpiryKey = ref(null)
|
||||
const expiryEditModalRef = ref(null)
|
||||
|
||||
// 分页相关
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(10)
|
||||
const pageSizeOptions = [5, 10, 20, 50, 100]
|
||||
|
||||
// 标签相关
|
||||
const selectedTagFilter = ref('')
|
||||
const availableTags = ref([])
|
||||
@@ -1017,8 +1012,8 @@ const renewingApiKey = ref(null)
|
||||
const newApiKeyData = ref(null)
|
||||
const batchApiKeyData = ref([])
|
||||
|
||||
// 计算筛选和排序后的API Keys(未分页)
|
||||
const filteredAndSortedApiKeys = computed(() => {
|
||||
// 计算排序后的API Keys
|
||||
const sortedApiKeys = computed(() => {
|
||||
// 先进行标签筛选
|
||||
let filteredKeys = apiKeys.value
|
||||
if (selectedTagFilter.value) {
|
||||
@@ -1109,17 +1104,8 @@ const loadAccounts = async () => {
|
||||
apiClient.get('/admin/account-groups')
|
||||
])
|
||||
|
||||
// 合并Claude OAuth账户和Claude Console账户
|
||||
const claudeAccounts = []
|
||||
|
||||
if (claudeData.success) {
|
||||
claudeData.data?.forEach(account => {
|
||||
claudeAccounts.push({
|
||||
...account,
|
||||
platform: 'claude-oauth',
|
||||
isDedicated: account.accountType === 'dedicated'
|
||||
})
|
||||
})
|
||||
accounts.value.claude = claudeData.data || []
|
||||
}
|
||||
|
||||
if (claudeConsoleData.success) {
|
||||
@@ -1132,10 +1118,7 @@ const loadAccounts = async () => {
|
||||
}
|
||||
|
||||
if (geminiData.success) {
|
||||
accounts.value.gemini = (geminiData.data || []).map(account => ({
|
||||
...account,
|
||||
isDedicated: account.accountType === 'dedicated'
|
||||
}))
|
||||
accounts.value.gemini = geminiData.data || []
|
||||
}
|
||||
|
||||
if (groupsData.success) {
|
||||
@@ -1165,9 +1148,6 @@ const loadApiKeys = async () => {
|
||||
}
|
||||
})
|
||||
availableTags.value = Array.from(tagsSet).sort()
|
||||
|
||||
// 重置到第一页
|
||||
currentPage.value = 1
|
||||
}
|
||||
} catch (error) {
|
||||
showToast('加载 API Keys 失败', 'error')
|
||||
@@ -1184,8 +1164,6 @@ const sortApiKeys = (field) => {
|
||||
apiKeysSortBy.value = field
|
||||
apiKeysSortOrder.value = 'asc'
|
||||
}
|
||||
// 排序时重置到第一页
|
||||
currentPage.value = 1
|
||||
}
|
||||
|
||||
// 格式化数字
|
||||
@@ -1202,28 +1180,23 @@ const calculateApiKeyCost = (usage) => {
|
||||
}
|
||||
|
||||
// 获取绑定账户名称
|
||||
const getBoundAccountName = (claudeAccountId, claudeConsoleAccountId) => {
|
||||
// 优先显示Claude OAuth账户
|
||||
if (claudeAccountId) {
|
||||
const claudeAccount = accounts.value.claude.find(acc => acc.id === claudeAccountId)
|
||||
if (claudeAccount) {
|
||||
return claudeAccount.name
|
||||
}
|
||||
// 如果找不到,返回账户ID的前8位
|
||||
return `账户-${claudeAccountId.substring(0, 8)}`
|
||||
const getBoundAccountName = (accountId) => {
|
||||
if (!accountId) return '未知账户'
|
||||
|
||||
// 从Claude账户列表中查找
|
||||
const claudeAccount = accounts.value.claude.find(acc => acc.id === accountId)
|
||||
if (claudeAccount) {
|
||||
return claudeAccount.name
|
||||
}
|
||||
|
||||
// 其次显示Claude Console账户
|
||||
if (claudeConsoleAccountId) {
|
||||
const consoleAccount = accounts.value.claude.find(acc => acc.id === claudeConsoleAccountId)
|
||||
if (consoleAccount) {
|
||||
return `${consoleAccount.name} (Console)`
|
||||
}
|
||||
// 如果找不到,返回账户ID的前8位
|
||||
return `Console-${claudeConsoleAccountId.substring(0, 8)}`
|
||||
// 从Gemini账户列表中查找
|
||||
const geminiAccount = accounts.value.gemini.find(acc => acc.id === accountId)
|
||||
if (geminiAccount) {
|
||||
return geminiAccount.name
|
||||
}
|
||||
|
||||
return '未知账户'
|
||||
// 如果找不到,返回账户ID的前8位
|
||||
return `账户-${accountId.substring(0, 8)}`
|
||||
}
|
||||
|
||||
// 检查API Key是否过期
|
||||
@@ -1504,7 +1477,7 @@ const deleteApiKey = async (keyId) => {
|
||||
const copyApiStatsLink = (apiKey) => {
|
||||
// 构建统计页面的完整URL
|
||||
const baseUrl = window.location.origin
|
||||
const statsUrl = `${baseUrl}/admin-next/api-stats?apiId=${apiKey.id}`
|
||||
const statsUrl = `${baseUrl}/admin/api-stats?apiId=${apiKey.id}`
|
||||
|
||||
// 使用传统的textarea方法复制到剪贴板
|
||||
const textarea = document.createElement('textarea')
|
||||
@@ -1673,5 +1646,4 @@ onMounted(async () => {
|
||||
.api-key-date-picker :deep(.el-range-separator) {
|
||||
@apply text-gray-500;
|
||||
}
|
||||
|
||||
</style>
|
||||
Reference in New Issue
Block a user