feat: 优化移动端响应式设计

- 优化所有页面的移动端适配(手机、平板、PC)
- 修复AccountsView移动端状态显示和按钮功能问题
- 修复ApiKeysView移动端详情展开显示问题
- 移除ApiKeysView不必要的查看按钮
- 修复Dashboard页面PC版时间筛选按钮布局
- 改进所有组件的响应式设计
- 删除dist目录避免构建文件冲突

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
shaw
2025-08-03 01:09:26 +08:00
parent f22c8cbfcc
commit ecfc1050d3
23 changed files with 2775 additions and 697 deletions

View File

@@ -1,17 +1,18 @@
<template>
<div class="tab-content">
<div class="card p-6">
<div class="flex flex-col md:flex-row justify-between items-center gap-4 mb-6">
<div class="card p-4 sm:p-6">
<div class="flex flex-col gap-4 mb-4 sm:mb-6">
<div>
<h3 class="text-xl font-bold text-gray-900 mb-2">
<h3 class="text-lg sm:text-xl font-bold text-gray-900 mb-1 sm:mb-2">
API Keys 管理
</h3>
<p class="text-gray-600">
<p class="text-sm sm:text-base text-gray-600">
管理和监控您的 API 密钥
</p>
</div>
<div class="flex items-center gap-3">
<div class="flex flex-col sm:flex-row gap-3 sm:items-center sm:justify-between">
<!-- Token统计时间范围选择 -->
<<<<<<< Updated upstream
<select
v-model="apiKeyStatsTimeRange"
class="px-2 py-1 text-sm text-gray-700 bg-white border border-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent hover:border-gray-300 transition-colors"
@@ -43,12 +44,51 @@
v-for="tag in availableTags"
:key="tag"
:value="tag"
=======
<div class="flex flex-wrap gap-2 items-center">
<select
v-model="apiKeyStatsTimeRange"
class="px-3 py-2 text-sm text-gray-700 bg-white border border-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent hover:border-gray-300 transition-colors"
@change="loadApiKeys()"
>>>>>>> Stashed changes
>
{{ tag }}
</option>
</select>
<option value="today">
今日
</option>
<option value="7days">
最近7天
</option>
<option value="monthly">
本月
</option>
<option value="all">
全部时间
</option>
</select>
<!-- 标签筛选器 -->
<select
v-model="selectedTagFilter"
class="px-3 py-2 text-sm text-gray-700 bg-white border border-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent hover:border-gray-300 transition-colors"
@change="currentPage = 1"
>
<option value="">
所有标签
</option>
<option
v-for="tag in availableTags"
:key="tag"
:value="tag"
>
{{ tag }}
</option>
</select>
</div>
<button
class="btn btn-primary px-4 py-1.5 text-sm flex items-center gap-2"
<<<<<<< Updated upstream
class="btn btn-primary px-6 py-3 flex items-center gap-2"
=======
class="btn btn-primary px-4 py-2 text-sm flex items-center gap-2 w-full sm:w-auto justify-center"
>>>>>>> Stashed changes
@click.stop="openCreateApiKeyModal"
>
<i class="fas fa-plus" />创建新 Key
@@ -81,9 +121,10 @@
</p>
</div>
<!-- 桌面端表格视图 -->
<div
v-else
class="table-container"
class="hidden md:block table-container"
>
<table class="min-w-full">
<thead class="bg-gray-50/80 backdrop-blur-sm">
@@ -105,9 +146,6 @@
<th class="px-6 py-4 text-left text-xs font-bold text-gray-700 uppercase tracking-wider">
标签
</th>
<th class="px-6 py-4 text-left text-xs font-bold text-gray-700 uppercase tracking-wider">
API Key
</th>
<th
class="px-6 py-4 text-left text-xs font-bold text-gray-700 uppercase tracking-wider cursor-pointer hover:bg-gray-100"
@click="sortApiKeys('status')"
@@ -219,11 +257,6 @@
>无标签</span>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-mono text-gray-600 bg-gray-50 px-3 py-1 rounded-lg">
{{ (key.apiKey || '').substring(0, 20) }}...
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span
:class="['inline-flex items-center px-3 py-1 rounded-full text-xs font-semibold',
@@ -337,34 +370,43 @@
{{ new Date(key.createdAt).toLocaleDateString() }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
<div v-if="key.expiresAt">
<div
v-if="isApiKeyExpired(key.expiresAt)"
class="text-red-600"
>
<i class="fas fa-exclamation-circle mr-1" />
已过期
</div>
<div
v-else-if="isApiKeyExpiringSoon(key.expiresAt)"
class="text-orange-600"
>
<i class="fas fa-clock mr-1" />
{{ formatExpireDate(key.expiresAt) }}
</div>
<div
<div class="inline-flex items-center gap-1 group">
<span v-if="key.expiresAt">
<span
v-if="isApiKeyExpired(key.expiresAt)"
class="text-red-600"
>
<i class="fas fa-exclamation-circle mr-1" />
已过期
</span>
<span
v-else-if="isApiKeyExpiringSoon(key.expiresAt)"
class="text-orange-600"
>
<i class="fas fa-clock mr-1" />
{{ formatExpireDate(key.expiresAt) }}
</span>
<span
v-else
class="text-gray-600"
>
{{ formatExpireDate(key.expiresAt) }}
</span>
</span>
<span
v-else
class="text-gray-600"
class="text-gray-400"
>
{{ formatExpireDate(key.expiresAt) }}
</div>
</div>
<div
v-else
class="text-gray-400"
>
<i class="fas fa-infinity mr-1" />
永不过期
<i class="fas fa-infinity mr-1" />
永不过期
</span>
<button
class="opacity-0 group-hover:opacity-100 p-0.5 text-gray-400 hover:text-blue-600 rounded transition-all duration-200"
title="快速修改过期时间"
@click.stop="startEditExpiry(key)"
>
<i class="fas fa-pencil-alt text-xs" />
</button>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
@@ -402,7 +444,7 @@
<!-- 模型统计展开区域 -->
<tr v-if="key && key.id && expandedApiKeys[key.id]">
<td
colspan="7"
colspan="6"
class="px-6 py-4 bg-gray-50"
>
<div
@@ -604,21 +646,228 @@
</tbody>
</table>
</div>
<<<<<<< Updated upstream
=======
<!-- 移动端卡片视图 -->
<div
v-if="!apiKeysLoading && sortedApiKeys.length > 0"
class="md:hidden space-y-3"
>
<div
v-for="key in sortedApiKeys"
:key="key.id"
class="card p-4 hover:shadow-lg transition-shadow"
>
<!-- 卡片头部 -->
<div class="flex items-start justify-between mb-3">
<div class="flex items-center gap-3">
<div class="w-10 h-10 bg-gradient-to-br from-blue-500 to-blue-600 rounded-lg flex items-center justify-center flex-shrink-0">
<i class="fas fa-key text-white text-sm" />
</div>
<div>
<h4 class="text-sm font-semibold text-gray-900">
{{ key.name }}
</h4>
<p class="text-xs text-gray-500 mt-0.5">
{{ key.id }}
</p>
</div>
</div>
<span
:class="['inline-flex items-center px-2 py-1 rounded-full text-xs font-semibold',
key.isActive ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800']"
>
<div
:class="['w-1.5 h-1.5 rounded-full mr-1.5',
key.isActive ? 'bg-green-500' : 'bg-red-500']"
/>
{{ key.isActive ? '活跃' : '已停用' }}
</span>
</div>
<!-- 绑定信息 -->
<div class="mb-3 text-xs text-gray-600">
<span v-if="key.claudeAccountId || key.claudeConsoleAccountId">
<i class="fas fa-link mr-1" />
绑定: {{ getBoundAccountName(key.claudeAccountId, key.claudeConsoleAccountId) }}
</span>
<span v-else>
<i class="fas fa-share-alt mr-1" />
使用共享池
</span>
</div>
<!-- 统计信息 -->
<div class="grid grid-cols-2 gap-3 mb-3">
<div>
<p class="text-xs text-gray-500">
使用量
</p>
<p class="text-sm font-semibold text-gray-900">
{{ formatNumber((key.usage && key.usage.total && key.usage.total.requests) || 0) }} 次
</p>
<p class="text-xs text-gray-500 mt-0.5">
{{ formatNumber((key.usage && key.usage.total && key.usage.total.tokens) || 0) }} tokens
</p>
</div>
<div>
<p class="text-xs text-gray-500">
费用
</p>
<p class="text-sm font-semibold text-green-600">
{{ calculateApiKeyCost(key.usage) }}
</p>
</div>
</div>
<!-- 时间信息 -->
<div class="text-xs text-gray-500 mb-3">
<div class="flex justify-between mb-1">
<span>创建时间</span>
<span>{{ formatDate(key.createdAt) }}</span>
</div>
<div class="flex justify-between">
<span>过期时间</span>
<span :class="isApiKeyExpiringSoon(key.expiresAt) ? 'text-orange-600 font-semibold' : ''">
{{ key.expiresAt ? formatDate(key.expiresAt) : '永不过期' }}
</span>
</div>
</div>
<!-- 标签 -->
<div
v-if="key.tags && key.tags.length > 0"
class="flex flex-wrap gap-1 mb-3"
>
<span
v-for="tag in key.tags"
:key="tag"
class="inline-flex items-center px-2 py-0.5 bg-blue-100 text-blue-800 text-xs rounded-full"
>
{{ tag }}
</span>
</div>
<!-- 操作按钮 -->
<div class="flex gap-2 mt-3 pt-3 border-t border-gray-100">
<button
class="flex-1 px-3 py-2 text-xs text-blue-600 bg-blue-50 rounded-lg hover:bg-blue-100 transition-colors flex items-center justify-center gap-1"
@click="toggleExpanded(key.id)"
>
<i :class="['fas', expandedKeys.includes(key.id) ? 'fa-chevron-up' : 'fa-chevron-down']" />
{{ expandedKeys.includes(key.id) ? '收起' : '详情' }}
</button>
<button
class="flex-1 px-3 py-2 text-xs text-gray-600 bg-gray-50 rounded-lg hover:bg-gray-100 transition-colors"
@click="openEditApiKeyModal(key)"
>
<i class="fas fa-edit mr-1" />
编辑
</button>
<button
v-if="key.expiresAt && (isApiKeyExpired(key.expiresAt) || isApiKeyExpiringSoon(key.expiresAt))"
class="flex-1 px-3 py-2 text-xs text-orange-600 bg-orange-50 rounded-lg hover:bg-orange-100 transition-colors"
@click="openRenewApiKeyModal(key)"
>
<i class="fas fa-clock mr-1" />
续期
</button>
<button
class="px-3 py-2 text-xs text-red-600 bg-red-50 rounded-lg hover:bg-red-100 transition-colors"
@click="deleteApiKey(key.id)"
>
<i class="fas fa-trash" />
</button>
</div>
<!-- 展开的详细统计 -->
<div
v-if="expandedKeys.includes(key.id)"
class="mt-3 pt-3 border-t border-gray-100"
>
<h5 class="text-xs font-semibold text-gray-700 mb-2">
详细信息
</h5>
<!-- 更多统计数据 -->
<div class="space-y-2 text-xs">
<div class="flex justify-between">
<span class="text-gray-600">并发限制:</span>
<span class="font-medium text-purple-600">{{ key.concurrencyLimit > 0 ? key.concurrencyLimit : '无限制' }}</span>
</div>
<div class="flex justify-between">
<span class="text-gray-600">当前并发:</span>
<span :class="['font-medium', key.currentConcurrency > 0 ? 'text-orange-600' : 'text-gray-600']">
{{ key.currentConcurrency || 0 }}
<span v-if="key.concurrencyLimit > 0" class="text-xs text-gray-500">/ {{ key.concurrencyLimit }}</span>
</span>
</div>
<div v-if="key.dailyCostLimit > 0" class="flex justify-between">
<span class="text-gray-600">今日费用:</span>
<span :class="['font-medium', (key.dailyCost || 0) >= key.dailyCostLimit ? 'text-red-600' : 'text-blue-600']">
${{ (key.dailyCost || 0).toFixed(2) }} / ${{ key.dailyCostLimit.toFixed(2) }}
</span>
</div>
<div v-if="key.rateLimitWindow > 0" class="flex justify-between">
<span class="text-gray-600">时间窗口:</span>
<span class="font-medium text-indigo-600">{{ key.rateLimitWindow }} 分钟</span>
</div>
<div v-if="key.rateLimitRequests > 0" class="flex justify-between">
<span class="text-gray-600">请求限制:</span>
<span class="font-medium text-indigo-600">{{ key.rateLimitRequests }} 次/窗口</span>
</div>
<!-- Token 细节 -->
<div class="pt-2 mt-2 border-t border-gray-100">
<div class="flex justify-between">
<span class="text-gray-600">输入 Token:</span>
<span class="font-medium">{{ formatNumber((key.usage && key.usage.total && key.usage.total.inputTokens) || 0) }}</span>
</div>
<div class="flex justify-between">
<span class="text-gray-600">输出 Token:</span>
<span class="font-medium">{{ formatNumber((key.usage && key.usage.total && key.usage.total.outputTokens) || 0) }}</span>
</div>
<div v-if="((key.usage && key.usage.total && key.usage.total.cacheCreateTokens) || 0) > 0" class="flex justify-between">
<span class="text-gray-600">缓存创建:</span>
<span class="font-medium text-purple-600">{{ formatNumber((key.usage && key.usage.total && key.usage.total.cacheCreateTokens) || 0) }}</span>
</div>
<div v-if="((key.usage && key.usage.total && key.usage.total.cacheReadTokens) || 0) > 0" class="flex justify-between">
<span class="text-gray-600">缓存读取:</span>
<span class="font-medium text-purple-600">{{ formatNumber((key.usage && key.usage.total && key.usage.total.cacheReadTokens) || 0) }}</span>
</div>
</div>
<!-- 今日统计 -->
<div class="pt-2 mt-2 border-t border-gray-100">
<div class="flex justify-between">
<span class="text-gray-600">今日请求:</span>
<span class="font-medium text-green-600">{{ formatNumber((key.usage && key.usage.daily && key.usage.daily.requests) || 0) }} 次</span>
</div>
<div class="flex justify-between">
<span class="text-gray-600">今日 Token:</span>
<span class="font-medium text-green-600">{{ formatNumber((key.usage && key.usage.daily && key.usage.daily.tokens) || 0) }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 分页组件 -->
<div
v-if="filteredAndSortedApiKeys.length > 0"
class="mt-6 flex flex-col sm:flex-row justify-between items-center gap-4"
class="mt-4 sm:mt-6 flex flex-col sm:flex-row justify-between items-center gap-4"
>
<div class="flex items-center gap-3">
<span class="text-sm text-gray-600">
<div class="flex flex-col sm:flex-row items-center gap-3 w-full sm:w-auto">
<span class="text-xs sm:text-sm text-gray-600">
共 {{ filteredAndSortedApiKeys.length }} 条记录
</span>
<div class="flex items-center gap-2">
<span class="text-sm text-gray-600">每页显示</span>
<span class="text-xs sm:text-sm text-gray-600">每页显示</span>
<select
v-model="pageSize"
class="px-2 py-1 text-sm text-gray-700 bg-white border border-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent hover:border-gray-300 transition-colors"
class="px-2 py-1 text-xs sm:text-sm text-gray-700 bg-white border border-gray-200 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent hover:border-gray-300 transition-colors"
@change="currentPage = 1"
>
<option
@@ -629,14 +878,14 @@
{{ size }}
</option>
</select>
<span class="text-sm text-gray-600">条</span>
<span class="text-xs sm:text-sm text-gray-600">条</span>
</div>
</div>
<div class="flex items-center gap-2">
<!-- 上一页 -->
<button
class="px-3 py-1 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
class="px-3 py-1.5 sm:py-1 text-xs sm:text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
:disabled="currentPage === 1"
@click="currentPage--"
>
@@ -648,14 +897,14 @@
<!-- 第一页 -->
<button
v-if="currentPage > 3"
class="px-3 py-1 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50"
class="hidden sm:block px-3 py-1 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50"
@click="currentPage = 1"
>
1
</button>
<span
v-if="currentPage > 4"
class="px-2 text-gray-500"
class="hidden sm:inline px-2 text-gray-500"
>...</span>
<!-- 中间页码 -->
@@ -663,7 +912,7 @@
v-for="page in pageNumbers"
:key="page"
:class="[
'px-3 py-1 text-sm font-medium rounded-md',
'px-2 sm:px-3 py-1 text-xs sm:text-sm font-medium rounded-md',
page === currentPage
? 'bg-blue-600 text-white'
: 'text-gray-700 bg-white border border-gray-300 hover:bg-gray-50'
@@ -676,11 +925,11 @@
<!-- 最后一页 -->
<span
v-if="currentPage < totalPages - 3"
class="px-2 text-gray-500"
class="hidden sm:inline px-2 text-gray-500"
>...</span>
<button
v-if="totalPages > 1 && currentPage < totalPages - 2"
class="px-3 py-1 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50"
class="hidden sm:block px-3 py-1 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50"
@click="currentPage = totalPages"
>
{{ totalPages }}
@@ -689,7 +938,7 @@
<!-- 下一页 -->
<button
class="px-3 py-1 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
class="px-3 py-1.5 sm:py-1 text-xs sm:text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
:disabled="currentPage === totalPages || totalPages === 0"
@click="currentPage++"
>
@@ -697,6 +946,7 @@
</button>
</div>
</div>
>>>>>>> Stashed changes
</div>
<!-- 模态框组件 -->
@@ -705,6 +955,7 @@
:accounts="accounts"
@close="showCreateApiKeyModal = false"
@success="handleCreateSuccess"
@batch-success="handleBatchCreateSuccess"
/>
<EditApiKeyModal
@@ -727,6 +978,21 @@
:api-key="newApiKeyData"
@close="showNewApiKeyModal = false"
/>
<BatchApiKeyModal
v-if="showBatchApiKeyModal"
:api-keys="batchApiKeyData"
@close="showBatchApiKeyModal = false"
/>
<!-- 过期时间编辑弹窗 -->
<ExpiryEditModal
ref="expiryEditModalRef"
:show="!!editingExpiryKey"
:api-key="editingExpiryKey || { id: null, expiresAt: null, name: '' }"
@close="closeExpiryEdit"
@save="handleSaveExpiry"
/>
</div>
</template>
@@ -739,6 +1005,8 @@ import CreateApiKeyModal from '@/components/apikeys/CreateApiKeyModal.vue'
import EditApiKeyModal from '@/components/apikeys/EditApiKeyModal.vue'
import RenewApiKeyModal from '@/components/apikeys/RenewApiKeyModal.vue'
import NewApiKeyModal from '@/components/apikeys/NewApiKeyModal.vue'
import BatchApiKeyModal from '@/components/apikeys/BatchApiKeyModal.vue'
import ExpiryEditModal from '@/components/apikeys/ExpiryEditModal.vue'
// 响应式数据
const clientsStore = useClientsStore()
@@ -752,6 +1020,8 @@ const apiKeyModelStats = ref({})
const apiKeyDateFilters = ref({})
const defaultTime = ref([new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)])
const accounts = ref({ claude: [], gemini: [] })
const editingExpiryKey = ref(null)
const expiryEditModalRef = ref(null)
// 分页相关
const currentPage = ref(1)
@@ -762,14 +1032,19 @@ const pageSizeOptions = [5, 10, 20, 50, 100]
const selectedTagFilter = ref('')
const availableTags = ref([])
// 移动端展开状态
const expandedKeys = ref([])
// 模态框状态
const showCreateApiKeyModal = ref(false)
const showEditApiKeyModal = ref(false)
const showRenewApiKeyModal = ref(false)
const showNewApiKeyModal = ref(false)
const showBatchApiKeyModal = ref(false)
const editingApiKey = ref(null)
const renewingApiKey = ref(null)
const newApiKeyData = ref(null)
const batchApiKeyData = ref([])
// 计算筛选和排序后的API Keys未分页
const filteredAndSortedApiKeys = computed(() => {
@@ -1155,12 +1430,16 @@ const resetApiKeyDateFilter = (keyId) => {
}
// 打开创建模态框
const openCreateApiKeyModal = () => {
const openCreateApiKeyModal = async () => {
// 重新加载账号数据,确保显示最新的专属账号
await loadAccounts()
showCreateApiKeyModal.value = true
}
// 打开编辑模态框
const openEditApiKeyModal = (apiKey) => {
const openEditApiKeyModal = async (apiKey) => {
// 重新加载账号数据,确保显示最新的专属账号
await loadAccounts()
editingApiKey.value = apiKey
showEditApiKeyModal.value = true
}
@@ -1179,6 +1458,14 @@ const handleCreateSuccess = (data) => {
loadApiKeys()
}
// 处理批量创建成功
const handleBatchCreateSuccess = (data) => {
showCreateApiKeyModal.value = false
batchApiKeyData.value = data
showBatchApiKeyModal.value = true
loadApiKeys()
}
// 处理编辑成功
const handleEditSuccess = () => {
showEditApiKeyModal.value = false
@@ -1258,6 +1545,90 @@ const copyApiStatsLink = (apiKey) => {
}
}
// 开始编辑过期时间
const startEditExpiry = (apiKey) => {
editingExpiryKey.value = apiKey
}
// 关闭过期时间编辑
const closeExpiryEdit = () => {
editingExpiryKey.value = null
}
// 保存过期时间
const handleSaveExpiry = async ({ keyId, expiresAt }) => {
try {
const data = await apiClient.put(`/admin/api-keys/${keyId}`, {
expiresAt: expiresAt || null
})
if (data.success) {
showToast('过期时间已更新', 'success')
// 更新本地数据
const key = apiKeys.value.find(k => k.id === keyId)
if (key) {
key.expiresAt = expiresAt || null
}
closeExpiryEdit()
} else {
showToast(data.message || '更新失败', 'error')
// 重置保存状态
if (expiryEditModalRef.value) {
expiryEditModalRef.value.resetSaving()
}
}
} catch (error) {
showToast('更新失败', 'error')
// 重置保存状态
if (expiryEditModalRef.value) {
expiryEditModalRef.value.resetSaving()
}
}
}
// 切换移动端卡片展开状态
const toggleExpanded = (keyId) => {
const index = expandedKeys.value.indexOf(keyId)
if (index > -1) {
expandedKeys.value.splice(index, 1)
} else {
expandedKeys.value.push(keyId)
}
}
// 格式化日期时间
const formatDate = (dateString) => {
if (!dateString) return ''
const date = new Date(dateString)
return date.toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
}).replace(/\//g, '-')
}
// 显示API Key详情
const showApiKey = async (apiKey) => {
try {
// 重新获取API Key的完整信息包含实际的key值
const response = await apiClient.get(`/admin/api-keys/${apiKey.id}`)
if (response.success && response.data) {
newApiKeyData.value = {
...response.data,
key: response.data.key || response.data.apiKey // 兼容不同的字段名
}
showNewApiKeyModal.value = true
} else {
showToast('获取API Key信息失败', 'error')
}
} catch (error) {
console.error('Error fetching API key:', error)
showToast('获取API Key信息失败', 'error')
}
}
onMounted(async () => {
// 并行加载所有需要的数据
await Promise.all([