mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
feat: 实现DashboardView.vue完整国际化支持
- 完成DashboardView.vue全面国际化 * 主要统计卡片:总API Keys、服务账户、今日请求、系统状态全部多语言化 * Token统计模块:今日Token、总消耗量、实时RPM/TPM指标完整国际化 * 实时性能监控:请求数/Token数每分钟指标、历史数据标识多语言化 * 图表组件完整国际化:饼图、趋势图、API Keys使用趋势图 * 交互控件全面支持:日期选择器、粒度切换、自动刷新等 - 图表系统深度国际化 * Chart.js图表标签完全多语言化:Token使用趋势、模型分布等 * 工具提示和坐标轴标签支持动态语言切换 * 表格头部和数据展示完整国际化支持 - 扩展三语言dashboard翻译组 * zh-cn.js: 简体中文专业术语翻译 * zh-tw.js: 繁体中文技术翻译(快取、即時等台湾用词) * en.js: 英文专业技术术语标准翻译 * 总计90+个翻译键值,涵盖所有用户可见文本 - 平台账户工具提示国际化 * Claude/Console/Gemini/Bedrock/OpenAI/Azure OpenAI账户状态 * 支持参数化翻译,动态显示账户数量和状态 - 提升复杂业务场景多语言体验 * 管理后台核心数据可视化页面完全国际化 * 60+个硬编码中文字符串全部替换 * 确保不同语言环境下数据展示的专业性
This commit is contained in:
@@ -215,5 +215,106 @@ export default {
|
|||||||
passwordPlaceholder: 'Please enter password',
|
passwordPlaceholder: 'Please enter password',
|
||||||
loginButton: 'Login',
|
loginButton: 'Login',
|
||||||
loggingIn: 'Logging in...'
|
loggingIn: 'Logging in...'
|
||||||
|
},
|
||||||
|
|
||||||
|
// Dashboard page
|
||||||
|
dashboard: {
|
||||||
|
// Main stats cards
|
||||||
|
totalApiKeys: 'Total API Keys',
|
||||||
|
activeApiKeys: 'Active',
|
||||||
|
serviceAccounts: 'Service Accounts',
|
||||||
|
normalAccounts: 'Normal',
|
||||||
|
abnormalAccounts: 'Abnormal',
|
||||||
|
pausedAccounts: 'Paused',
|
||||||
|
rateLimitedAccounts: 'Rate Limited',
|
||||||
|
todayRequests: 'Today Requests',
|
||||||
|
totalRequests: 'Total Requests',
|
||||||
|
systemStatus: 'System Status',
|
||||||
|
uptime: 'Uptime',
|
||||||
|
|
||||||
|
// Platform accounts tooltip
|
||||||
|
claudeAccount: 'Claude: {total} accounts (Normal: {normal})',
|
||||||
|
consoleAccount: 'Console: {total} accounts (Normal: {normal})',
|
||||||
|
geminiAccount: 'Gemini: {total} accounts (Normal: {normal})',
|
||||||
|
bedrockAccount: 'Bedrock: {total} accounts (Normal: {normal})',
|
||||||
|
openaiAccount: 'OpenAI: {total} accounts (Normal: {normal})',
|
||||||
|
azureOpenaiAccount: 'Azure OpenAI: {total} accounts (Normal: {normal})',
|
||||||
|
|
||||||
|
// Token stats cards
|
||||||
|
todayToken: 'Today Tokens',
|
||||||
|
totalTokenConsumption: 'Total Token Consumption',
|
||||||
|
inputTokens: 'Input',
|
||||||
|
outputTokens: 'Output',
|
||||||
|
cacheCreateTokens: 'Cache Create',
|
||||||
|
cacheReadTokens: 'Cache Read',
|
||||||
|
|
||||||
|
// Real-time metrics
|
||||||
|
realtimeRPM: 'Realtime RPM',
|
||||||
|
realtimeTPM: 'Realtime TPM',
|
||||||
|
requestsPerMinute: 'Requests per Minute',
|
||||||
|
tokensPerMinute: 'Tokens per Minute',
|
||||||
|
historicalData: 'Historical Data',
|
||||||
|
minutes: 'minutes',
|
||||||
|
|
||||||
|
// Charts section
|
||||||
|
modelDistributionAndTrend: 'Model Usage Distribution & Token Usage Trends',
|
||||||
|
|
||||||
|
// Date filter presets
|
||||||
|
today: 'Today',
|
||||||
|
yesterday: 'Yesterday',
|
||||||
|
last7Days: 'Last 7 Days',
|
||||||
|
last30Days: 'Last 30 Days',
|
||||||
|
thisWeek: 'This Week',
|
||||||
|
lastWeek: 'Last Week',
|
||||||
|
thisMonth: 'This Month',
|
||||||
|
lastMonth: 'Last Month',
|
||||||
|
|
||||||
|
// Granularity buttons
|
||||||
|
byDay: 'By Day',
|
||||||
|
byHour: 'By Hour',
|
||||||
|
|
||||||
|
// Date picker
|
||||||
|
startDatePlaceholder: 'Start Date',
|
||||||
|
endDatePlaceholder: 'End Date',
|
||||||
|
dateSeparator: 'to',
|
||||||
|
maxHours24: 'Maximum 24 hours',
|
||||||
|
|
||||||
|
// Auto refresh controls
|
||||||
|
autoRefresh: 'Auto Refresh',
|
||||||
|
refresh: 'Refresh',
|
||||||
|
refreshing: 'Refreshing',
|
||||||
|
refreshDataNow: 'Refresh data now',
|
||||||
|
|
||||||
|
// Charts
|
||||||
|
tokenUsageDistribution: 'Token Usage Distribution',
|
||||||
|
detailedStatistics: 'Detailed Statistics',
|
||||||
|
noModelUsageData: 'No model usage data available',
|
||||||
|
|
||||||
|
// Table headers
|
||||||
|
model: 'Model',
|
||||||
|
requestCount: 'Requests',
|
||||||
|
totalTokens: 'Total Tokens',
|
||||||
|
cost: 'Cost',
|
||||||
|
percentage: 'Percentage',
|
||||||
|
|
||||||
|
// Trend charts
|
||||||
|
tokenUsageTrend: 'Token Usage Trend',
|
||||||
|
apiKeysUsageTrend: 'API Keys Usage Trend',
|
||||||
|
requestsCount: 'Requests Count',
|
||||||
|
tokenCount: 'Token Count',
|
||||||
|
totalApiKeysCount: 'Total {count} API Keys',
|
||||||
|
showingTop10: 'Total {count} API Keys, showing top 10 by usage',
|
||||||
|
|
||||||
|
// Chart labels
|
||||||
|
inputTokensLabel: 'Input Tokens',
|
||||||
|
outputTokensLabel: 'Output Tokens',
|
||||||
|
cacheCreateTokensLabel: 'Cache Create Tokens',
|
||||||
|
cacheReadTokensLabel: 'Cache Read Tokens',
|
||||||
|
costLabel: 'Cost (USD)',
|
||||||
|
requestsLabel: 'Requests',
|
||||||
|
time: 'Time',
|
||||||
|
date: 'Date',
|
||||||
|
tokenQuantity: 'Token Quantity',
|
||||||
|
requestsQuantity: 'Requests Count'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -215,5 +215,106 @@ export default {
|
|||||||
passwordPlaceholder: '请输入密码',
|
passwordPlaceholder: '请输入密码',
|
||||||
loginButton: '登录',
|
loginButton: '登录',
|
||||||
loggingIn: '登录中...'
|
loggingIn: '登录中...'
|
||||||
|
},
|
||||||
|
|
||||||
|
// Dashboard page
|
||||||
|
dashboard: {
|
||||||
|
// Main stats cards
|
||||||
|
totalApiKeys: '总 API Keys',
|
||||||
|
activeApiKeys: '活跃',
|
||||||
|
serviceAccounts: '服务账户',
|
||||||
|
normalAccounts: '正常',
|
||||||
|
abnormalAccounts: '异常',
|
||||||
|
pausedAccounts: '停止调度',
|
||||||
|
rateLimitedAccounts: '限流',
|
||||||
|
todayRequests: '今日请求',
|
||||||
|
totalRequests: '总请求',
|
||||||
|
systemStatus: '系统状态',
|
||||||
|
uptime: '运行时间',
|
||||||
|
|
||||||
|
// Platform accounts tooltip
|
||||||
|
claudeAccount: 'Claude: {total} 个 (正常: {normal})',
|
||||||
|
consoleAccount: 'Console: {total} 个 (正常: {normal})',
|
||||||
|
geminiAccount: 'Gemini: {total} 个 (正常: {normal})',
|
||||||
|
bedrockAccount: 'Bedrock: {total} 个 (正常: {normal})',
|
||||||
|
openaiAccount: 'OpenAI: {total} 个 (正常: {normal})',
|
||||||
|
azureOpenaiAccount: 'Azure OpenAI: {total} 个 (正常: {normal})',
|
||||||
|
|
||||||
|
// Token stats cards
|
||||||
|
todayToken: '今日Token',
|
||||||
|
totalTokenConsumption: '总Token消耗',
|
||||||
|
inputTokens: '输入',
|
||||||
|
outputTokens: '输出',
|
||||||
|
cacheCreateTokens: '缓存创建',
|
||||||
|
cacheReadTokens: '缓存读取',
|
||||||
|
|
||||||
|
// Real-time metrics
|
||||||
|
realtimeRPM: '实时RPM',
|
||||||
|
realtimeTPM: '实时TPM',
|
||||||
|
requestsPerMinute: '每分钟请求数',
|
||||||
|
tokensPerMinute: '每分钟Token数',
|
||||||
|
historicalData: '历史数据',
|
||||||
|
minutes: '分钟',
|
||||||
|
|
||||||
|
// Charts section
|
||||||
|
modelDistributionAndTrend: '模型使用分布与Token使用趋势',
|
||||||
|
|
||||||
|
// Date filter presets (would be populated from dateFilter.presetOptions)
|
||||||
|
today: '今日',
|
||||||
|
yesterday: '昨日',
|
||||||
|
last7Days: '迗 7 天',
|
||||||
|
last30Days: '迗 30 天',
|
||||||
|
thisWeek: '本周',
|
||||||
|
lastWeek: '上周',
|
||||||
|
thisMonth: '本月',
|
||||||
|
lastMonth: '上月',
|
||||||
|
|
||||||
|
// Granularity buttons
|
||||||
|
byDay: '按天',
|
||||||
|
byHour: '按小时',
|
||||||
|
|
||||||
|
// Date picker
|
||||||
|
startDatePlaceholder: '开始日期',
|
||||||
|
endDatePlaceholder: '结束日期',
|
||||||
|
dateSeparator: '至',
|
||||||
|
maxHours24: '最多24小时',
|
||||||
|
|
||||||
|
// Auto refresh controls
|
||||||
|
autoRefresh: '自动刷新',
|
||||||
|
refresh: '刷新',
|
||||||
|
refreshing: '刷新中',
|
||||||
|
refreshDataNow: '立即刷新数据',
|
||||||
|
|
||||||
|
// Charts
|
||||||
|
tokenUsageDistribution: 'Token使用分布',
|
||||||
|
detailedStatistics: '详细统计数据',
|
||||||
|
noModelUsageData: '暂无模型使用数据',
|
||||||
|
|
||||||
|
// Table headers
|
||||||
|
model: '模型',
|
||||||
|
requestCount: '请求数',
|
||||||
|
totalTokens: '总Token',
|
||||||
|
cost: '费用',
|
||||||
|
percentage: '占比',
|
||||||
|
|
||||||
|
// Trend charts
|
||||||
|
tokenUsageTrend: 'Token使用趋势',
|
||||||
|
apiKeysUsageTrend: 'API Keys 使用趋势',
|
||||||
|
requestsCount: '请求次数',
|
||||||
|
tokenCount: 'Token 数量',
|
||||||
|
totalApiKeysCount: '共 {count} 个 API Key',
|
||||||
|
showingTop10: '共 {count} 个 API Key,显示使用量前 10 个',
|
||||||
|
|
||||||
|
// Chart labels
|
||||||
|
inputTokensLabel: '输入Token',
|
||||||
|
outputTokensLabel: '输出Token',
|
||||||
|
cacheCreateTokensLabel: '缓存创建Token',
|
||||||
|
cacheReadTokensLabel: '缓存读取Token',
|
||||||
|
costLabel: '费用 (USD)',
|
||||||
|
requestsLabel: '请求数',
|
||||||
|
time: '时间',
|
||||||
|
date: '日期',
|
||||||
|
tokenQuantity: 'Token数量',
|
||||||
|
requestsQuantity: '请求次数'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -215,5 +215,106 @@ export default {
|
|||||||
passwordPlaceholder: '請輸入密碼',
|
passwordPlaceholder: '請輸入密碼',
|
||||||
loginButton: '登錄',
|
loginButton: '登錄',
|
||||||
loggingIn: '登錄中...'
|
loggingIn: '登錄中...'
|
||||||
|
},
|
||||||
|
|
||||||
|
// Dashboard page
|
||||||
|
dashboard: {
|
||||||
|
// Main stats cards
|
||||||
|
totalApiKeys: '總 API Keys',
|
||||||
|
activeApiKeys: '活躍',
|
||||||
|
serviceAccounts: '服務帳戶',
|
||||||
|
normalAccounts: '正常',
|
||||||
|
abnormalAccounts: '異常',
|
||||||
|
pausedAccounts: '停止調度',
|
||||||
|
rateLimitedAccounts: '限流',
|
||||||
|
todayRequests: '今日請求',
|
||||||
|
totalRequests: '總請求',
|
||||||
|
systemStatus: '系統狀態',
|
||||||
|
uptime: '運行時間',
|
||||||
|
|
||||||
|
// Platform accounts tooltip
|
||||||
|
claudeAccount: 'Claude: {total} 個 (正常: {normal})',
|
||||||
|
consoleAccount: 'Console: {total} 個 (正常: {normal})',
|
||||||
|
geminiAccount: 'Gemini: {total} 個 (正常: {normal})',
|
||||||
|
bedrockAccount: 'Bedrock: {total} 個 (正常: {normal})',
|
||||||
|
openaiAccount: 'OpenAI: {total} 個 (正常: {normal})',
|
||||||
|
azureOpenaiAccount: 'Azure OpenAI: {total} 個 (正常: {normal})',
|
||||||
|
|
||||||
|
// Token stats cards
|
||||||
|
todayToken: '今日Token',
|
||||||
|
totalTokenConsumption: '總Token消耗',
|
||||||
|
inputTokens: '輸入',
|
||||||
|
outputTokens: '輸出',
|
||||||
|
cacheCreateTokens: '快取建立',
|
||||||
|
cacheReadTokens: '快取讀取',
|
||||||
|
|
||||||
|
// Real-time metrics
|
||||||
|
realtimeRPM: '即時RPM',
|
||||||
|
realtimeTPM: '即時TPM',
|
||||||
|
requestsPerMinute: '每分钟請求數',
|
||||||
|
tokensPerMinute: '每分钟Token數',
|
||||||
|
historicalData: '歷史資料',
|
||||||
|
minutes: '分钟',
|
||||||
|
|
||||||
|
// Charts section
|
||||||
|
modelDistributionAndTrend: '模型使用分佈與Token使用趋勢',
|
||||||
|
|
||||||
|
// Date filter presets
|
||||||
|
today: '今日',
|
||||||
|
yesterday: '昨日',
|
||||||
|
last7Days: '近 7 天',
|
||||||
|
last30Days: '近 30 天',
|
||||||
|
thisWeek: '本週',
|
||||||
|
lastWeek: '上週',
|
||||||
|
thisMonth: '本月',
|
||||||
|
lastMonth: '上月',
|
||||||
|
|
||||||
|
// Granularity buttons
|
||||||
|
byDay: '按日',
|
||||||
|
byHour: '按小時',
|
||||||
|
|
||||||
|
// Date picker
|
||||||
|
startDatePlaceholder: '開始日期',
|
||||||
|
endDatePlaceholder: '結束日期',
|
||||||
|
dateSeparator: '至',
|
||||||
|
maxHours24: '最多24小時',
|
||||||
|
|
||||||
|
// Auto refresh controls
|
||||||
|
autoRefresh: '自動刷新',
|
||||||
|
refresh: '刷新',
|
||||||
|
refreshing: '刷新中',
|
||||||
|
refreshDataNow: '立即刷新資料',
|
||||||
|
|
||||||
|
// Charts
|
||||||
|
tokenUsageDistribution: 'Token使用分佈',
|
||||||
|
detailedStatistics: '詳細統計資料',
|
||||||
|
noModelUsageData: '暫無模型使用資料',
|
||||||
|
|
||||||
|
// Table headers
|
||||||
|
model: '模型',
|
||||||
|
requestCount: '請求數',
|
||||||
|
totalTokens: '總Token',
|
||||||
|
cost: '費用',
|
||||||
|
percentage: '占比',
|
||||||
|
|
||||||
|
// Trend charts
|
||||||
|
tokenUsageTrend: 'Token使用趋勢',
|
||||||
|
apiKeysUsageTrend: 'API Keys 使用趋勢',
|
||||||
|
requestsCount: '請求次數',
|
||||||
|
tokenCount: 'Token 數量',
|
||||||
|
totalApiKeysCount: '共 {count} 個 API Key',
|
||||||
|
showingTop10: '共 {count} 個 API Key,顯示使用量前 10 個',
|
||||||
|
|
||||||
|
// Chart labels
|
||||||
|
inputTokensLabel: '輸入Token',
|
||||||
|
outputTokensLabel: '輸出Token',
|
||||||
|
cacheCreateTokensLabel: '快取建立Token',
|
||||||
|
cacheReadTokensLabel: '快取讀取Token',
|
||||||
|
costLabel: '費用 (USD)',
|
||||||
|
requestsLabel: '請求數',
|
||||||
|
time: '時間',
|
||||||
|
date: '日期',
|
||||||
|
tokenQuantity: 'Token數量',
|
||||||
|
requestsQuantity: '請求次數'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,13 +8,13 @@
|
|||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p class="mb-1 text-xs font-semibold text-gray-600 dark:text-gray-400 sm:text-sm">
|
<p class="mb-1 text-xs font-semibold text-gray-600 dark:text-gray-400 sm:text-sm">
|
||||||
总API Keys
|
{{ t('dashboard.totalApiKeys') }}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-2xl font-bold text-gray-900 dark:text-gray-100 sm:text-3xl">
|
<p class="text-2xl font-bold text-gray-900 dark:text-gray-100 sm:text-3xl">
|
||||||
{{ dashboardData.totalApiKeys }}
|
{{ dashboardData.totalApiKeys }}
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
活跃: {{ dashboardData.activeApiKeys || 0 }}
|
{{ t('dashboard.activeApiKeys') }}: {{ dashboardData.activeApiKeys || 0 }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-icon flex-shrink-0 bg-gradient-to-br from-blue-500 to-blue-600">
|
<div class="stat-icon flex-shrink-0 bg-gradient-to-br from-blue-500 to-blue-600">
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<p class="mb-1 text-xs font-semibold text-gray-600 dark:text-gray-400 sm:text-sm">
|
<p class="mb-1 text-xs font-semibold text-gray-600 dark:text-gray-400 sm:text-sm">
|
||||||
服务账户
|
{{ t('dashboard.serviceAccounts') }}
|
||||||
</p>
|
</p>
|
||||||
<div class="flex flex-wrap items-baseline gap-x-2">
|
<div class="flex flex-wrap items-baseline gap-x-2">
|
||||||
<p class="text-2xl font-bold text-gray-900 dark:text-gray-100 sm:text-3xl">
|
<p class="text-2xl font-bold text-gray-900 dark:text-gray-100 sm:text-3xl">
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
dashboardData.accountsByPlatform.claude.total > 0
|
dashboardData.accountsByPlatform.claude.total > 0
|
||||||
"
|
"
|
||||||
class="inline-flex items-center gap-0.5"
|
class="inline-flex items-center gap-0.5"
|
||||||
:title="`Claude: ${dashboardData.accountsByPlatform.claude.total} 个 (正常: ${dashboardData.accountsByPlatform.claude.normal})`"
|
:title="t('dashboard.claudeAccount', { total: dashboardData.accountsByPlatform.claude.total, normal: dashboardData.accountsByPlatform.claude.normal })"
|
||||||
>
|
>
|
||||||
<i class="fas fa-brain text-xs text-indigo-600" />
|
<i class="fas fa-brain text-xs text-indigo-600" />
|
||||||
<span class="text-xs font-medium text-gray-700 dark:text-gray-300">{{
|
<span class="text-xs font-medium text-gray-700 dark:text-gray-300">{{
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
dashboardData.accountsByPlatform['claude-console'].total > 0
|
dashboardData.accountsByPlatform['claude-console'].total > 0
|
||||||
"
|
"
|
||||||
class="inline-flex items-center gap-0.5"
|
class="inline-flex items-center gap-0.5"
|
||||||
:title="`Console: ${dashboardData.accountsByPlatform['claude-console'].total} 个 (正常: ${dashboardData.accountsByPlatform['claude-console'].normal})`"
|
:title="t('dashboard.consoleAccount', { total: dashboardData.accountsByPlatform['claude-console'].total, normal: dashboardData.accountsByPlatform['claude-console'].normal })"
|
||||||
>
|
>
|
||||||
<i class="fas fa-terminal text-xs text-purple-600" />
|
<i class="fas fa-terminal text-xs text-purple-600" />
|
||||||
<span class="text-xs font-medium text-gray-700 dark:text-gray-300">{{
|
<span class="text-xs font-medium text-gray-700 dark:text-gray-300">{{
|
||||||
@@ -70,7 +70,7 @@
|
|||||||
dashboardData.accountsByPlatform.gemini.total > 0
|
dashboardData.accountsByPlatform.gemini.total > 0
|
||||||
"
|
"
|
||||||
class="inline-flex items-center gap-0.5"
|
class="inline-flex items-center gap-0.5"
|
||||||
:title="`Gemini: ${dashboardData.accountsByPlatform.gemini.total} 个 (正常: ${dashboardData.accountsByPlatform.gemini.normal})`"
|
:title="t('dashboard.geminiAccount', { total: dashboardData.accountsByPlatform.gemini.total, normal: dashboardData.accountsByPlatform.gemini.normal })"
|
||||||
>
|
>
|
||||||
<i class="fas fa-robot text-xs text-yellow-600" />
|
<i class="fas fa-robot text-xs text-yellow-600" />
|
||||||
<span class="text-xs font-medium text-gray-700 dark:text-gray-300">{{
|
<span class="text-xs font-medium text-gray-700 dark:text-gray-300">{{
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
dashboardData.accountsByPlatform.bedrock.total > 0
|
dashboardData.accountsByPlatform.bedrock.total > 0
|
||||||
"
|
"
|
||||||
class="inline-flex items-center gap-0.5"
|
class="inline-flex items-center gap-0.5"
|
||||||
:title="`Bedrock: ${dashboardData.accountsByPlatform.bedrock.total} 个 (正常: ${dashboardData.accountsByPlatform.bedrock.normal})`"
|
:title="t('dashboard.bedrockAccount', { total: dashboardData.accountsByPlatform.bedrock.total, normal: dashboardData.accountsByPlatform.bedrock.normal })"
|
||||||
>
|
>
|
||||||
<i class="fab fa-aws text-xs text-orange-600" />
|
<i class="fab fa-aws text-xs text-orange-600" />
|
||||||
<span class="text-xs font-medium text-gray-700 dark:text-gray-300">{{
|
<span class="text-xs font-medium text-gray-700 dark:text-gray-300">{{
|
||||||
@@ -98,7 +98,7 @@
|
|||||||
dashboardData.accountsByPlatform.openai.total > 0
|
dashboardData.accountsByPlatform.openai.total > 0
|
||||||
"
|
"
|
||||||
class="inline-flex items-center gap-0.5"
|
class="inline-flex items-center gap-0.5"
|
||||||
:title="`OpenAI: ${dashboardData.accountsByPlatform.openai.total} 个 (正常: ${dashboardData.accountsByPlatform.openai.normal})`"
|
:title="t('dashboard.openaiAccount', { total: dashboardData.accountsByPlatform.openai.total, normal: dashboardData.accountsByPlatform.openai.normal })"
|
||||||
>
|
>
|
||||||
<i class="fas fa-openai text-xs text-gray-100" />
|
<i class="fas fa-openai text-xs text-gray-100" />
|
||||||
<span class="text-xs font-medium text-gray-700 dark:text-gray-300">{{
|
<span class="text-xs font-medium text-gray-700 dark:text-gray-300">{{
|
||||||
@@ -112,7 +112,7 @@
|
|||||||
dashboardData.accountsByPlatform.azure_openai.total > 0
|
dashboardData.accountsByPlatform.azure_openai.total > 0
|
||||||
"
|
"
|
||||||
class="inline-flex items-center gap-0.5"
|
class="inline-flex items-center gap-0.5"
|
||||||
:title="`Azure OpenAI: ${dashboardData.accountsByPlatform.azure_openai.total} 个 (正常: ${dashboardData.accountsByPlatform.azure_openai.normal})`"
|
:title="t('dashboard.azureOpenaiAccount', { total: dashboardData.accountsByPlatform.azure_openai.total, normal: dashboardData.accountsByPlatform.azure_openai.normal })"
|
||||||
>
|
>
|
||||||
<i class="fab fa-microsoft text-xs text-blue-600" />
|
<i class="fab fa-microsoft text-xs text-blue-600" />
|
||||||
<span class="text-xs font-medium text-gray-700 dark:text-gray-300">{{
|
<span class="text-xs font-medium text-gray-700 dark:text-gray-300">{{
|
||||||
@@ -122,18 +122,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
正常: {{ dashboardData.normalAccounts || 0 }}
|
{{ t('dashboard.normalAccounts') }}: {{ dashboardData.normalAccounts || 0 }}
|
||||||
<span v-if="dashboardData.abnormalAccounts > 0" class="text-red-600">
|
<span v-if="dashboardData.abnormalAccounts > 0" class="text-red-600">
|
||||||
| 异常: {{ dashboardData.abnormalAccounts }}
|
| {{ t('dashboard.abnormalAccounts') }}: {{ dashboardData.abnormalAccounts }}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
v-if="dashboardData.pausedAccounts > 0"
|
v-if="dashboardData.pausedAccounts > 0"
|
||||||
class="text-gray-600 dark:text-gray-400"
|
class="text-gray-600 dark:text-gray-400"
|
||||||
>
|
>
|
||||||
| 停止调度: {{ dashboardData.pausedAccounts }}
|
| {{ t('dashboard.pausedAccounts') }}: {{ dashboardData.pausedAccounts }}
|
||||||
</span>
|
</span>
|
||||||
<span v-if="dashboardData.rateLimitedAccounts > 0" class="text-yellow-600">
|
<span v-if="dashboardData.rateLimitedAccounts > 0" class="text-yellow-600">
|
||||||
| 限流: {{ dashboardData.rateLimitedAccounts }}
|
| {{ t('dashboard.rateLimitedAccounts') }}: {{ dashboardData.rateLimitedAccounts }}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -147,13 +147,13 @@
|
|||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p class="mb-1 text-xs font-semibold text-gray-600 dark:text-gray-400 sm:text-sm">
|
<p class="mb-1 text-xs font-semibold text-gray-600 dark:text-gray-400 sm:text-sm">
|
||||||
今日请求
|
{{ t('dashboard.todayRequests') }}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-2xl font-bold text-gray-900 dark:text-gray-100 sm:text-3xl">
|
<p class="text-2xl font-bold text-gray-900 dark:text-gray-100 sm:text-3xl">
|
||||||
{{ dashboardData.todayRequests }}
|
{{ dashboardData.todayRequests }}
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
总请求: {{ formatNumber(dashboardData.totalRequests || 0) }}
|
{{ t('dashboard.totalRequests') }}: {{ formatNumber(dashboardData.totalRequests || 0) }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-icon flex-shrink-0 bg-gradient-to-br from-purple-500 to-purple-600">
|
<div class="stat-icon flex-shrink-0 bg-gradient-to-br from-purple-500 to-purple-600">
|
||||||
@@ -166,13 +166,13 @@
|
|||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p class="mb-1 text-xs font-semibold text-gray-600 dark:text-gray-400 sm:text-sm">
|
<p class="mb-1 text-xs font-semibold text-gray-600 dark:text-gray-400 sm:text-sm">
|
||||||
系统状态
|
{{ t('dashboard.systemStatus') }}
|
||||||
</p>
|
</p>
|
||||||
<p class="text-2xl font-bold text-green-600 sm:text-3xl">
|
<p class="text-2xl font-bold text-green-600 sm:text-3xl">
|
||||||
{{ dashboardData.systemStatus }}
|
{{ dashboardData.systemStatus }}
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
运行时间: {{ formattedUptime }}
|
{{ t('dashboard.uptime') }}: {{ formattedUptime }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-icon flex-shrink-0 bg-gradient-to-br from-yellow-500 to-orange-500">
|
<div class="stat-icon flex-shrink-0 bg-gradient-to-br from-yellow-500 to-orange-500">
|
||||||
@@ -190,7 +190,7 @@
|
|||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="mr-8 flex-1">
|
<div class="mr-8 flex-1">
|
||||||
<p class="mb-1 text-xs font-semibold text-gray-600 dark:text-gray-400 sm:text-sm">
|
<p class="mb-1 text-xs font-semibold text-gray-600 dark:text-gray-400 sm:text-sm">
|
||||||
今日Token
|
{{ t('dashboard.todayToken') }}
|
||||||
</p>
|
</p>
|
||||||
<div class="mb-2 flex flex-wrap items-baseline gap-2">
|
<div class="mb-2 flex flex-wrap items-baseline gap-2">
|
||||||
<p class="text-xl font-bold text-blue-600 sm:text-2xl md:text-3xl">
|
<p class="text-xl font-bold text-blue-600 sm:text-2xl md:text-3xl">
|
||||||
@@ -210,25 +210,25 @@
|
|||||||
<div class="text-xs text-gray-500 dark:text-gray-400">
|
<div class="text-xs text-gray-500 dark:text-gray-400">
|
||||||
<div class="flex flex-wrap items-center justify-between gap-x-4">
|
<div class="flex flex-wrap items-center justify-between gap-x-4">
|
||||||
<span
|
<span
|
||||||
>输入:
|
>{{ t('dashboard.inputTokens') }}:
|
||||||
<span class="font-medium">{{
|
<span class="font-medium">{{
|
||||||
formatNumber(dashboardData.todayInputTokens || 0)
|
formatNumber(dashboardData.todayInputTokens || 0)
|
||||||
}}</span></span
|
}}</span></span
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
>输出:
|
>{{ t('dashboard.outputTokens') }}:
|
||||||
<span class="font-medium">{{
|
<span class="font-medium">{{
|
||||||
formatNumber(dashboardData.todayOutputTokens || 0)
|
formatNumber(dashboardData.todayOutputTokens || 0)
|
||||||
}}</span></span
|
}}</span></span
|
||||||
>
|
>
|
||||||
<span v-if="(dashboardData.todayCacheCreateTokens || 0) > 0" class="text-purple-600"
|
<span v-if="(dashboardData.todayCacheCreateTokens || 0) > 0" class="text-purple-600"
|
||||||
>缓存创建:
|
>{{ t('dashboard.cacheCreateTokens') }}:
|
||||||
<span class="font-medium">{{
|
<span class="font-medium">{{
|
||||||
formatNumber(dashboardData.todayCacheCreateTokens || 0)
|
formatNumber(dashboardData.todayCacheCreateTokens || 0)
|
||||||
}}</span></span
|
}}</span></span
|
||||||
>
|
>
|
||||||
<span v-if="(dashboardData.todayCacheReadTokens || 0) > 0" class="text-purple-600"
|
<span v-if="(dashboardData.todayCacheReadTokens || 0) > 0" class="text-purple-600"
|
||||||
>缓存读取:
|
>{{ t('dashboard.cacheReadTokens') }}:
|
||||||
<span class="font-medium">{{
|
<span class="font-medium">{{
|
||||||
formatNumber(dashboardData.todayCacheReadTokens || 0)
|
formatNumber(dashboardData.todayCacheReadTokens || 0)
|
||||||
}}</span></span
|
}}</span></span
|
||||||
@@ -246,7 +246,7 @@
|
|||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div class="mr-8 flex-1">
|
<div class="mr-8 flex-1">
|
||||||
<p class="mb-1 text-xs font-semibold text-gray-600 dark:text-gray-400 sm:text-sm">
|
<p class="mb-1 text-xs font-semibold text-gray-600 dark:text-gray-400 sm:text-sm">
|
||||||
总Token消耗
|
{{ t('dashboard.totalTokenConsumption') }}
|
||||||
</p>
|
</p>
|
||||||
<div class="mb-2 flex flex-wrap items-baseline gap-2">
|
<div class="mb-2 flex flex-wrap items-baseline gap-2">
|
||||||
<p class="text-xl font-bold text-emerald-600 sm:text-2xl md:text-3xl">
|
<p class="text-xl font-bold text-emerald-600 sm:text-2xl md:text-3xl">
|
||||||
@@ -266,13 +266,13 @@
|
|||||||
<div class="text-xs text-gray-500 dark:text-gray-400">
|
<div class="text-xs text-gray-500 dark:text-gray-400">
|
||||||
<div class="flex flex-wrap items-center justify-between gap-x-4">
|
<div class="flex flex-wrap items-center justify-between gap-x-4">
|
||||||
<span
|
<span
|
||||||
>输入:
|
>{{ t('dashboard.inputTokens') }}:
|
||||||
<span class="font-medium">{{
|
<span class="font-medium">{{
|
||||||
formatNumber(dashboardData.totalInputTokens || 0)
|
formatNumber(dashboardData.totalInputTokens || 0)
|
||||||
}}</span></span
|
}}</span></span
|
||||||
>
|
>
|
||||||
<span
|
<span
|
||||||
>输出:
|
>{{ t('dashboard.outputTokens') }}:
|
||||||
<span class="font-medium">{{
|
<span class="font-medium">{{
|
||||||
formatNumber(dashboardData.totalOutputTokens || 0)
|
formatNumber(dashboardData.totalOutputTokens || 0)
|
||||||
}}</span></span
|
}}</span></span
|
||||||
@@ -302,16 +302,16 @@
|
|||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p class="mb-1 text-xs font-semibold text-gray-600 dark:text-gray-400 sm:text-sm">
|
<p class="mb-1 text-xs font-semibold text-gray-600 dark:text-gray-400 sm:text-sm">
|
||||||
实时RPM
|
{{ t('dashboard.realtimeRPM') }}
|
||||||
<span class="text-xs text-gray-400">({{ dashboardData.metricsWindow }}分钟)</span>
|
<span class="text-xs text-gray-400">({{ dashboardData.metricsWindow }}{{ t('dashboard.minutes') }})</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="text-2xl font-bold text-orange-600 sm:text-3xl">
|
<p class="text-2xl font-bold text-orange-600 sm:text-3xl">
|
||||||
{{ dashboardData.realtimeRPM || 0 }}
|
{{ dashboardData.realtimeRPM || 0 }}
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
每分钟请求数
|
{{ t('dashboard.requestsPerMinute') }}
|
||||||
<span v-if="dashboardData.isHistoricalMetrics" class="text-yellow-600">
|
<span v-if="dashboardData.isHistoricalMetrics" class="text-yellow-600">
|
||||||
<i class="fas fa-exclamation-circle" /> 历史数据
|
<i class="fas fa-exclamation-circle" /> {{ t('dashboard.historicalData') }}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -325,16 +325,16 @@
|
|||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<p class="mb-1 text-xs font-semibold text-gray-600 dark:text-gray-400 sm:text-sm">
|
<p class="mb-1 text-xs font-semibold text-gray-600 dark:text-gray-400 sm:text-sm">
|
||||||
实时TPM
|
{{ t('dashboard.realtimeTPM') }}
|
||||||
<span class="text-xs text-gray-400">({{ dashboardData.metricsWindow }}分钟)</span>
|
<span class="text-xs text-gray-400">({{ dashboardData.metricsWindow }}{{ t('dashboard.minutes') }})</span>
|
||||||
</p>
|
</p>
|
||||||
<p class="text-2xl font-bold text-rose-600 sm:text-3xl">
|
<p class="text-2xl font-bold text-rose-600 sm:text-3xl">
|
||||||
{{ formatNumber(dashboardData.realtimeTPM || 0) }}
|
{{ formatNumber(dashboardData.realtimeTPM || 0) }}
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
每分钟Token数
|
{{ t('dashboard.tokensPerMinute') }}
|
||||||
<span v-if="dashboardData.isHistoricalMetrics" class="text-yellow-600">
|
<span v-if="dashboardData.isHistoricalMetrics" class="text-yellow-600">
|
||||||
<i class="fas fa-exclamation-circle" /> 历史数据
|
<i class="fas fa-exclamation-circle" /> {{ t('dashboard.historicalData') }}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -349,7 +349,7 @@
|
|||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<div class="mb-4 flex flex-col gap-4 sm:mb-6">
|
<div class="mb-4 flex flex-col gap-4 sm:mb-6">
|
||||||
<h3 class="text-lg font-bold text-gray-900 dark:text-gray-100 sm:text-xl">
|
<h3 class="text-lg font-bold text-gray-900 dark:text-gray-100 sm:text-xl">
|
||||||
模型使用分布与Token使用趋势
|
{{ t('dashboard.modelDistributionAndTrend') }}
|
||||||
</h3>
|
</h3>
|
||||||
<div class="flex flex-col gap-2 lg:flex-row lg:items-center lg:justify-end">
|
<div class="flex flex-col gap-2 lg:flex-row lg:items-center lg:justify-end">
|
||||||
<!-- 快捷日期选择 -->
|
<!-- 快捷日期选择 -->
|
||||||
@@ -382,7 +382,7 @@
|
|||||||
]"
|
]"
|
||||||
@click="setTrendGranularity('day')"
|
@click="setTrendGranularity('day')"
|
||||||
>
|
>
|
||||||
<i class="fas fa-calendar-day mr-1" />按天
|
<i class="fas fa-calendar-day mr-1" />{{ t('dashboard.byDay') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:class="[
|
:class="[
|
||||||
@@ -393,7 +393,7 @@
|
|||||||
]"
|
]"
|
||||||
@click="setTrendGranularity('hour')"
|
@click="setTrendGranularity('hour')"
|
||||||
>
|
>
|
||||||
<i class="fas fa-clock mr-1" />按小时
|
<i class="fas fa-clock mr-1" />{{ t('dashboard.byHour') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -404,18 +404,18 @@
|
|||||||
class="custom-date-picker w-full lg:w-auto"
|
class="custom-date-picker w-full lg:w-auto"
|
||||||
:default-time="defaultTime"
|
:default-time="defaultTime"
|
||||||
:disabled-date="disabledDate"
|
:disabled-date="disabledDate"
|
||||||
end-placeholder="结束日期"
|
:end-placeholder="t('dashboard.endDatePlaceholder')"
|
||||||
format="YYYY-MM-DD HH:mm:ss"
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
range-separator="至"
|
:range-separator="t('dashboard.dateSeparator')"
|
||||||
size="default"
|
size="default"
|
||||||
start-placeholder="开始日期"
|
:start-placeholder="t('dashboard.startDatePlaceholder')"
|
||||||
style="max-width: 400px"
|
style="max-width: 400px"
|
||||||
type="datetimerange"
|
type="datetimerange"
|
||||||
value-format="YYYY-MM-DD HH:mm:ss"
|
value-format="YYYY-MM-DD HH:mm:ss"
|
||||||
@change="onCustomDateRangeChange"
|
@change="onCustomDateRangeChange"
|
||||||
/>
|
/>
|
||||||
<span v-if="trendGranularity === 'hour'" class="text-xs text-orange-600">
|
<span v-if="trendGranularity === 'hour'" class="text-xs text-orange-600">
|
||||||
<i class="fas fa-info-circle" /> 最多24小时
|
<i class="fas fa-info-circle" /> {{ t('dashboard.maxHours24') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -433,7 +433,7 @@
|
|||||||
class="ml-2.5 flex select-none items-center gap-1 text-sm font-medium text-gray-600 dark:text-gray-300"
|
class="ml-2.5 flex select-none items-center gap-1 text-sm font-medium text-gray-600 dark:text-gray-300"
|
||||||
>
|
>
|
||||||
<i class="fas fa-redo-alt text-xs text-gray-500 dark:text-gray-400" />
|
<i class="fas fa-redo-alt text-xs text-gray-500 dark:text-gray-400" />
|
||||||
<span>自动刷新</span>
|
<span>{{ t('dashboard.autoRefresh') }}</span>
|
||||||
<span
|
<span
|
||||||
v-if="autoRefreshEnabled"
|
v-if="autoRefreshEnabled"
|
||||||
class="ml-1 font-mono text-xs text-blue-600 transition-opacity"
|
class="ml-1 font-mono text-xs text-blue-600 transition-opacity"
|
||||||
@@ -449,11 +449,11 @@
|
|||||||
<button
|
<button
|
||||||
class="flex items-center gap-1 rounded-md border border-gray-300 bg-white px-3 py-1 text-sm font-medium text-blue-600 shadow-sm transition-colors hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-600 dark:bg-gray-800 dark:hover:bg-gray-700 sm:gap-2"
|
class="flex items-center gap-1 rounded-md border border-gray-300 bg-white px-3 py-1 text-sm font-medium text-blue-600 shadow-sm transition-colors hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-50 dark:border-gray-600 dark:bg-gray-800 dark:hover:bg-gray-700 sm:gap-2"
|
||||||
:disabled="isRefreshing"
|
:disabled="isRefreshing"
|
||||||
title="立即刷新数据"
|
:title="t('dashboard.refreshDataNow')"
|
||||||
@click="refreshAllData()"
|
@click="refreshAllData()"
|
||||||
>
|
>
|
||||||
<i :class="['fas fa-sync-alt text-xs', { 'animate-spin': isRefreshing }]" />
|
<i :class="['fas fa-sync-alt text-xs', { 'animate-spin': isRefreshing }]" />
|
||||||
<span class="hidden sm:inline">{{ isRefreshing ? '刷新中' : '刷新' }}</span>
|
<span class="hidden sm:inline">{{ isRefreshing ? t('dashboard.refreshing') : t('dashboard.refresh') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -463,7 +463,7 @@
|
|||||||
<!-- 饼图 -->
|
<!-- 饼图 -->
|
||||||
<div class="card p-4 sm:p-6">
|
<div class="card p-4 sm:p-6">
|
||||||
<h4 class="mb-4 text-base font-semibold text-gray-800 dark:text-gray-200 sm:text-lg">
|
<h4 class="mb-4 text-base font-semibold text-gray-800 dark:text-gray-200 sm:text-lg">
|
||||||
Token使用分布
|
{{ t('dashboard.tokenUsageDistribution') }}
|
||||||
</h4>
|
</h4>
|
||||||
<div class="relative" style="height: 250px">
|
<div class="relative" style="height: 250px">
|
||||||
<canvas ref="modelUsageChart" />
|
<canvas ref="modelUsageChart" />
|
||||||
@@ -473,10 +473,10 @@
|
|||||||
<!-- 详细数据表格 -->
|
<!-- 详细数据表格 -->
|
||||||
<div class="card p-4 sm:p-6">
|
<div class="card p-4 sm:p-6">
|
||||||
<h4 class="mb-4 text-base font-semibold text-gray-800 dark:text-gray-200 sm:text-lg">
|
<h4 class="mb-4 text-base font-semibold text-gray-800 dark:text-gray-200 sm:text-lg">
|
||||||
详细统计数据
|
{{ t('dashboard.detailedStatistics') }}
|
||||||
</h4>
|
</h4>
|
||||||
<div v-if="dashboardModelStats.length === 0" class="py-8 text-center">
|
<div v-if="dashboardModelStats.length === 0" class="py-8 text-center">
|
||||||
<p class="text-sm text-gray-500 sm:text-base">暂无模型使用数据</p>
|
<p class="text-sm text-gray-500 sm:text-base">{{ t('dashboard.noModelUsageData') }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="max-h-[250px] overflow-auto sm:max-h-[300px]">
|
<div v-else class="max-h-[250px] overflow-auto sm:max-h-[300px]">
|
||||||
<table class="min-w-full">
|
<table class="min-w-full">
|
||||||
@@ -485,27 +485,27 @@
|
|||||||
<th
|
<th
|
||||||
class="px-2 py-2 text-left text-xs font-medium text-gray-700 dark:text-gray-300 sm:px-4"
|
class="px-2 py-2 text-left text-xs font-medium text-gray-700 dark:text-gray-300 sm:px-4"
|
||||||
>
|
>
|
||||||
模型
|
{{ t('dashboard.model') }}
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
class="hidden px-2 py-2 text-right text-xs font-medium text-gray-700 dark:text-gray-300 sm:table-cell sm:px-4"
|
class="hidden px-2 py-2 text-right text-xs font-medium text-gray-700 dark:text-gray-300 sm:table-cell sm:px-4"
|
||||||
>
|
>
|
||||||
请求数
|
{{ t('dashboard.requestCount') }}
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
class="px-2 py-2 text-right text-xs font-medium text-gray-700 dark:text-gray-300 sm:px-4"
|
class="px-2 py-2 text-right text-xs font-medium text-gray-700 dark:text-gray-300 sm:px-4"
|
||||||
>
|
>
|
||||||
总Token
|
{{ t('dashboard.totalTokens') }}
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
class="px-2 py-2 text-right text-xs font-medium text-gray-700 dark:text-gray-300 sm:px-4"
|
class="px-2 py-2 text-right text-xs font-medium text-gray-700 dark:text-gray-300 sm:px-4"
|
||||||
>
|
>
|
||||||
费用
|
{{ t('dashboard.cost') }}
|
||||||
</th>
|
</th>
|
||||||
<th
|
<th
|
||||||
class="hidden px-2 py-2 text-right text-xs font-medium text-gray-700 dark:text-gray-300 sm:table-cell sm:px-4"
|
class="hidden px-2 py-2 text-right text-xs font-medium text-gray-700 dark:text-gray-300 sm:table-cell sm:px-4"
|
||||||
>
|
>
|
||||||
占比
|
{{ t('dashboard.percentage') }}
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@@ -566,7 +566,7 @@
|
|||||||
<div class="card p-4 sm:p-6">
|
<div class="card p-4 sm:p-6">
|
||||||
<div class="mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
<div class="mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||||
<h3 class="text-base font-semibold text-gray-900 dark:text-gray-100 sm:text-lg">
|
<h3 class="text-base font-semibold text-gray-900 dark:text-gray-100 sm:text-lg">
|
||||||
API Keys 使用趋势
|
{{ t('dashboard.apiKeysUsageTrend') }}
|
||||||
</h3>
|
</h3>
|
||||||
<!-- 维度切换按钮 -->
|
<!-- 维度切换按钮 -->
|
||||||
<div class="flex gap-1 rounded-lg bg-gray-100 p-1 dark:bg-gray-700">
|
<div class="flex gap-1 rounded-lg bg-gray-100 p-1 dark:bg-gray-700">
|
||||||
@@ -579,8 +579,8 @@
|
|||||||
]"
|
]"
|
||||||
@click="((apiKeysTrendMetric = 'requests'), updateApiKeysUsageTrendChart())"
|
@click="((apiKeysTrendMetric = 'requests'), updateApiKeysUsageTrendChart())"
|
||||||
>
|
>
|
||||||
<i class="fas fa-exchange-alt mr-1" /><span class="hidden sm:inline">请求次数</span
|
<i class="fas fa-exchange-alt mr-1" /><span class="hidden sm:inline">{{ t('dashboard.requestsCount') }}</span
|
||||||
><span class="sm:hidden">请求</span>
|
><span class="sm:hidden">{{ t('dashboard.requestsCount').split(' ')[0] }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:class="[
|
:class="[
|
||||||
@@ -591,16 +591,16 @@
|
|||||||
]"
|
]"
|
||||||
@click="((apiKeysTrendMetric = 'tokens'), updateApiKeysUsageTrendChart())"
|
@click="((apiKeysTrendMetric = 'tokens'), updateApiKeysUsageTrendChart())"
|
||||||
>
|
>
|
||||||
<i class="fas fa-coins mr-1" /><span class="hidden sm:inline">Token 数量</span
|
<i class="fas fa-coins mr-1" /><span class="hidden sm:inline">{{ t('dashboard.tokenCount') }}</span
|
||||||
><span class="sm:hidden">Token</span>
|
><span class="sm:hidden">Token</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4 text-xs text-gray-600 dark:text-gray-400 sm:text-sm">
|
<div class="mb-4 text-xs text-gray-600 dark:text-gray-400 sm:text-sm">
|
||||||
<span v-if="apiKeysTrendData.totalApiKeys > 10">
|
<span v-if="apiKeysTrendData.totalApiKeys > 10">
|
||||||
共 {{ apiKeysTrendData.totalApiKeys }} 个 API Key,显示使用量前 10 个
|
{{ t('dashboard.showingTop10', { count: apiKeysTrendData.totalApiKeys }) }}
|
||||||
</span>
|
</span>
|
||||||
<span v-else> 共 {{ apiKeysTrendData.totalApiKeys }} 个 API Key </span>
|
<span v-else>{{ t('dashboard.totalApiKeysCount', { count: apiKeysTrendData.totalApiKeys }) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="sm:h-[350px]" style="height: 300px">
|
<div class="sm:h-[350px]" style="height: 300px">
|
||||||
<canvas ref="apiKeysUsageTrendChart" />
|
<canvas ref="apiKeysUsageTrendChart" />
|
||||||
@@ -613,12 +613,14 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted, onUnmounted, watch, nextTick, computed } from 'vue'
|
import { ref, onMounted, onUnmounted, watch, nextTick, computed } from 'vue'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useDashboardStore } from '@/stores/dashboard'
|
import { useDashboardStore } from '@/stores/dashboard'
|
||||||
import { useThemeStore } from '@/stores/theme'
|
import { useThemeStore } from '@/stores/theme'
|
||||||
import Chart from 'chart.js/auto'
|
import Chart from 'chart.js/auto'
|
||||||
|
|
||||||
const dashboardStore = useDashboardStore()
|
const dashboardStore = useDashboardStore()
|
||||||
const themeStore = useThemeStore()
|
const themeStore = useThemeStore()
|
||||||
|
const { t } = useI18n()
|
||||||
const { isDarkMode } = storeToRefs(themeStore)
|
const { isDarkMode } = storeToRefs(themeStore)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -804,35 +806,35 @@ function createUsageTrendChart() {
|
|||||||
labels: labels,
|
labels: labels,
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: '输入Token',
|
label: t('dashboard.inputTokensLabel'),
|
||||||
data: inputData,
|
data: inputData,
|
||||||
borderColor: 'rgb(102, 126, 234)',
|
borderColor: 'rgb(102, 126, 234)',
|
||||||
backgroundColor: 'rgba(102, 126, 234, 0.1)',
|
backgroundColor: 'rgba(102, 126, 234, 0.1)',
|
||||||
tension: 0.3
|
tension: 0.3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '输出Token',
|
label: t('dashboard.outputTokensLabel'),
|
||||||
data: outputData,
|
data: outputData,
|
||||||
borderColor: 'rgb(240, 147, 251)',
|
borderColor: 'rgb(240, 147, 251)',
|
||||||
backgroundColor: 'rgba(240, 147, 251, 0.1)',
|
backgroundColor: 'rgba(240, 147, 251, 0.1)',
|
||||||
tension: 0.3
|
tension: 0.3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '缓存创建Token',
|
label: t('dashboard.cacheCreateTokensLabel'),
|
||||||
data: cacheCreateData,
|
data: cacheCreateData,
|
||||||
borderColor: 'rgb(59, 130, 246)',
|
borderColor: 'rgb(59, 130, 246)',
|
||||||
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
backgroundColor: 'rgba(59, 130, 246, 0.1)',
|
||||||
tension: 0.3
|
tension: 0.3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '缓存读取Token',
|
label: t('dashboard.cacheReadTokensLabel'),
|
||||||
data: cacheReadData,
|
data: cacheReadData,
|
||||||
borderColor: 'rgb(147, 51, 234)',
|
borderColor: 'rgb(147, 51, 234)',
|
||||||
backgroundColor: 'rgba(147, 51, 234, 0.1)',
|
backgroundColor: 'rgba(147, 51, 234, 0.1)',
|
||||||
tension: 0.3
|
tension: 0.3
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '费用 (USD)',
|
label: t('dashboard.costLabel'),
|
||||||
data: costData,
|
data: costData,
|
||||||
borderColor: 'rgb(34, 197, 94)',
|
borderColor: 'rgb(34, 197, 94)',
|
||||||
backgroundColor: 'rgba(34, 197, 94, 0.1)',
|
backgroundColor: 'rgba(34, 197, 94, 0.1)',
|
||||||
@@ -840,7 +842,7 @@ function createUsageTrendChart() {
|
|||||||
yAxisID: 'y2'
|
yAxisID: 'y2'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '请求数',
|
label: t('dashboard.requestsLabel'),
|
||||||
data: requestsData,
|
data: requestsData,
|
||||||
borderColor: 'rgb(16, 185, 129)',
|
borderColor: 'rgb(16, 185, 129)',
|
||||||
backgroundColor: 'rgba(16, 185, 129, 0.1)',
|
backgroundColor: 'rgba(16, 185, 129, 0.1)',
|
||||||
@@ -863,7 +865,7 @@ function createUsageTrendChart() {
|
|||||||
plugins: {
|
plugins: {
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: 'Token使用趋势',
|
text: t('dashboard.tokenUsageTrend'),
|
||||||
font: {
|
font: {
|
||||||
size: 16,
|
size: 16,
|
||||||
weight: 'bold'
|
weight: 'bold'
|
||||||
@@ -929,7 +931,7 @@ function createUsageTrendChart() {
|
|||||||
display: true,
|
display: true,
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: trendGranularity === 'hour' ? '时间' : '日期',
|
text: trendGranularity === 'hour' ? t('dashboard.time') : t('dashboard.date'),
|
||||||
color: chartColors.value.text
|
color: chartColors.value.text
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
@@ -945,7 +947,7 @@ function createUsageTrendChart() {
|
|||||||
position: 'left',
|
position: 'left',
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: 'Token数量',
|
text: t('dashboard.tokenQuantity'),
|
||||||
color: chartColors.value.text
|
color: chartColors.value.text
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
@@ -964,7 +966,7 @@ function createUsageTrendChart() {
|
|||||||
position: 'right',
|
position: 'right',
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: '请求数',
|
text: t('dashboard.requestsQuantity'),
|
||||||
color: chartColors.value.text
|
color: chartColors.value.text
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
@@ -1148,7 +1150,7 @@ function createApiKeysUsageTrendChart() {
|
|||||||
display: true,
|
display: true,
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: trendGranularity === 'hour' ? '时间' : '日期',
|
text: trendGranularity === 'hour' ? t('dashboard.time') : t('dashboard.date'),
|
||||||
color: chartColors.value.text
|
color: chartColors.value.text
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
@@ -1162,7 +1164,7 @@ function createApiKeysUsageTrendChart() {
|
|||||||
beginAtZero: true,
|
beginAtZero: true,
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: apiKeysTrendMetric.value === 'tokens' ? 'Token 数量' : '请求次数',
|
text: apiKeysTrendMetric.value === 'tokens' ? t('dashboard.tokenQuantity') : t('dashboard.requestsQuantity'),
|
||||||
color: chartColors.value.text
|
color: chartColors.value.text
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
|
|||||||
Reference in New Issue
Block a user