mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
feat: 完成多个组件的国际化支持与文本替换
- 更新 AccountForm.vue 中的占位符文本为 i18n 语言包中的键 - 修改 ConfirmModal.vue 中的确认和取消按钮文本为 i18n 语言包中的键 - 更新 CustomDropdown.vue 中的占位符文本为 i18n 语言包中的键 - 修改 app.js 中的应用标题为英文版本 - 更新 router/index.js 中的日志输出为英文 - 在 accounts.js 和 apiKeys.js 中的错误处理信息中引入 i18n 键以提升多语言一致性 - 更新 dashboard.js 中的系统状态和错误日志为 i18n 键 - 在 DashboardView.vue 中的多个文本替换为 i18n 语言包中的键
This commit is contained in:
@@ -1847,14 +1847,14 @@
|
|||||||
<input
|
<input
|
||||||
v-model="mapping.from"
|
v-model="mapping.from"
|
||||||
class="form-input flex-1"
|
class="form-input flex-1"
|
||||||
placeholder="原始模型名称"
|
:placeholder="t('accountForm.originalModelName')"
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
<i class="fas fa-arrow-right text-gray-400" />
|
<i class="fas fa-arrow-right text-gray-400" />
|
||||||
<input
|
<input
|
||||||
v-model="mapping.to"
|
v-model="mapping.to"
|
||||||
class="form-input flex-1"
|
class="form-input flex-1"
|
||||||
placeholder="映射后的模型名称"
|
:placeholder="t('accountForm.mappedModelName')"
|
||||||
type="text"
|
type="text"
|
||||||
/>
|
/>
|
||||||
<button
|
<button
|
||||||
@@ -1874,7 +1874,7 @@
|
|||||||
@click="addModelMapping"
|
@click="addModelMapping"
|
||||||
>
|
>
|
||||||
<i class="fas fa-plus mr-2" />
|
<i class="fas fa-plus mr-2" />
|
||||||
添加模型映射
|
{{ t('accountForm.addModelMapping') }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- 快捷添加按钮 -->
|
<!-- 快捷添加按钮 -->
|
||||||
|
|||||||
@@ -25,13 +25,13 @@
|
|||||||
class="flex-1 rounded-xl bg-gray-100 px-4 py-2.5 font-medium text-gray-700 transition-colors hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600"
|
class="flex-1 rounded-xl bg-gray-100 px-4 py-2.5 font-medium text-gray-700 transition-colors hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600"
|
||||||
@click="$emit('cancel')"
|
@click="$emit('cancel')"
|
||||||
>
|
>
|
||||||
{{ cancelText }}
|
{{ cancelLabel }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="flex-1 rounded-xl bg-gradient-to-r from-yellow-500 to-orange-500 px-4 py-2.5 font-medium text-white shadow-sm transition-colors hover:from-yellow-600 hover:to-orange-600"
|
class="flex-1 rounded-xl bg-gradient-to-r from-yellow-500 to-orange-500 px-4 py-2.5 font-medium text-white shadow-sm transition-colors hover:from-yellow-600 hover:to-orange-600"
|
||||||
@click="$emit('confirm')"
|
@click="$emit('confirm')"
|
||||||
>
|
>
|
||||||
{{ confirmText }}
|
{{ confirmLabel }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -40,11 +40,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
|
|
||||||
defineProps({
|
const props = defineProps({
|
||||||
show: {
|
show: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true
|
required: true
|
||||||
@@ -59,14 +60,17 @@ defineProps({
|
|||||||
},
|
},
|
||||||
confirmText: {
|
confirmText: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => t('common.confirmModal.continue')
|
default: ''
|
||||||
},
|
},
|
||||||
cancelText: {
|
cancelText: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => t('common.confirmModal.cancel')
|
default: ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const confirmLabel = computed(() => props.confirmText || t('common.confirmModal.continue'))
|
||||||
|
const cancelLabel = computed(() => props.cancelText || t('common.confirmModal.cancel'))
|
||||||
|
|
||||||
defineEmits(['confirm', 'cancel'])
|
defineEmits(['confirm', 'cancel'])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<span
|
<span
|
||||||
class="select-none whitespace-nowrap text-sm font-medium text-gray-700 dark:text-gray-200"
|
class="select-none whitespace-nowrap text-sm font-medium text-gray-700 dark:text-gray-200"
|
||||||
>
|
>
|
||||||
{{ selectedLabel || placeholder }}
|
{{ selectedLabel || placeholderText }}
|
||||||
</span>
|
</span>
|
||||||
<i
|
<i
|
||||||
:class="[
|
:class="[
|
||||||
@@ -80,7 +80,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
placeholder: {
|
placeholder: {
|
||||||
type: String,
|
type: String,
|
||||||
default: () => t('common.customDropdown.placeholder')
|
default: ''
|
||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -99,6 +99,8 @@ const triggerRef = ref(null)
|
|||||||
const dropdownRef = ref(null)
|
const dropdownRef = ref(null)
|
||||||
const dropdownStyle = ref({})
|
const dropdownStyle = ref({})
|
||||||
|
|
||||||
|
const placeholderText = computed(() => props.placeholder || t('common.customDropdown.placeholder'))
|
||||||
|
|
||||||
const selectedLabel = computed(() => {
|
const selectedLabel = computed(() => {
|
||||||
const selected = props.options.find((opt) => opt.value === props.modelValue)
|
const selected = props.options.find((opt) => opt.value === props.modelValue)
|
||||||
return selected ? selected.label : ''
|
return selected ? selected.label : ''
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export const APP_CONFIG = {
|
|||||||
basePath: import.meta.env.VITE_APP_BASE_URL || (import.meta.env.DEV ? '/admin/' : '/web/admin/'),
|
basePath: import.meta.env.VITE_APP_BASE_URL || (import.meta.env.DEV ? '/admin/' : '/web/admin/'),
|
||||||
|
|
||||||
// 应用标题
|
// 应用标题
|
||||||
title: import.meta.env.VITE_APP_TITLE || 'Claude Relay Service - 管理后台',
|
title: import.meta.env.VITE_APP_TITLE || 'Claude Relay Service - Admin Panel',
|
||||||
|
|
||||||
// 是否为开发环境
|
// 是否为开发环境
|
||||||
isDev: import.meta.env.DEV,
|
isDev: import.meta.env.DEV,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { createRouter, createWebHistory } from 'vue-router'
|
|||||||
import { useAuthStore } from '@/stores/auth'
|
import { useAuthStore } from '@/stores/auth'
|
||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
import { APP_CONFIG } from '@/config/app'
|
import { APP_CONFIG } from '@/config/app'
|
||||||
|
import { showToast } from '@/utils/toast'
|
||||||
|
|
||||||
// 路由懒加载
|
// 路由懒加载
|
||||||
const LoginView = () => import('@/views/LoginView.vue')
|
const LoginView = () => import('@/views/LoginView.vue')
|
||||||
@@ -150,7 +151,7 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
|
||||||
console.log('路由导航:', {
|
console.log('Router navigation:', {
|
||||||
to: to.path,
|
to: to.path,
|
||||||
from: from.path,
|
from: from.path,
|
||||||
fullPath: to.fullPath,
|
fullPath: to.fullPath,
|
||||||
@@ -177,8 +178,6 @@ router.beforeEach(async (to, from, next) => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If the error is about disabled account, redirect to login with error
|
// If the error is about disabled account, redirect to login with error
|
||||||
if (error.message && error.message.includes('disabled')) {
|
if (error.message && error.message.includes('disabled')) {
|
||||||
// Import showToast to display the error
|
|
||||||
const { showToast } = await import('@/utils/toast')
|
|
||||||
showToast(error.message, 'error')
|
showToast(error.message, 'error')
|
||||||
}
|
}
|
||||||
return next('/user-login')
|
return next('/user-login')
|
||||||
|
|||||||
@@ -204,7 +204,9 @@ export const useAccountsStore = defineStore('accounts', () => {
|
|||||||
await fetchClaudeConsoleAccounts()
|
await fetchClaudeConsoleAccounts()
|
||||||
return response.data
|
return response.data
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.message || '创建Claude Console账户失败')
|
throw new Error(
|
||||||
|
response.message || i18n.global.t('common.errors.createClaudeConsoleAccountFailed')
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error.value = err.message
|
error.value = err.message
|
||||||
@@ -284,7 +286,9 @@ export const useAccountsStore = defineStore('accounts', () => {
|
|||||||
await fetchAzureOpenAIAccounts()
|
await fetchAzureOpenAIAccounts()
|
||||||
return response.data
|
return response.data
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.message || '创建Azure OpenAI账户失败')
|
throw new Error(
|
||||||
|
response.message || i18n.global.t('common.errors.createAzureOpenAIAccountFailed')
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error.value = err.message
|
error.value = err.message
|
||||||
@@ -344,7 +348,9 @@ export const useAccountsStore = defineStore('accounts', () => {
|
|||||||
await fetchClaudeConsoleAccounts()
|
await fetchClaudeConsoleAccounts()
|
||||||
return response
|
return response
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.message || '更新Claude Console账户失败')
|
throw new Error(
|
||||||
|
response.message || i18n.global.t('common.errors.updateClaudeConsoleAccountFailed')
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error.value = err.message
|
error.value = err.message
|
||||||
@@ -424,7 +430,9 @@ export const useAccountsStore = defineStore('accounts', () => {
|
|||||||
await fetchAzureOpenAIAccounts()
|
await fetchAzureOpenAIAccounts()
|
||||||
return response
|
return response
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.message || '更新Azure OpenAI账户失败')
|
throw new Error(
|
||||||
|
response.message || i18n.global.t('common.errors.updateAzureOpenAIAccountFailed')
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error.value = err.message
|
error.value = err.message
|
||||||
@@ -624,7 +632,9 @@ export const useAccountsStore = defineStore('accounts', () => {
|
|||||||
if (response.success) {
|
if (response.success) {
|
||||||
return response.data // 返回整个对象,包含authUrl和sessionId
|
return response.data // 返回整个对象,包含authUrl和sessionId
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.message || '生成Setup Token URL失败')
|
throw new Error(
|
||||||
|
response.message || i18n.global.t('common.errors.generateSetupTokenUrlFailed')
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error.value = err.message
|
error.value = err.message
|
||||||
@@ -642,7 +652,7 @@ export const useAccountsStore = defineStore('accounts', () => {
|
|||||||
if (response.success) {
|
if (response.success) {
|
||||||
return response.data
|
return response.data
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.message || '交换Setup Token授权码失败')
|
throw new Error(response.message || i18n.global.t('common.errors.exchangeSetupTokenFailed'))
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
error.value = err.message
|
error.value = err.message
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ export const useApiKeysStore = defineStore('apiKeys', () => {
|
|||||||
throw new Error(response.message || i18n.global.t('apiKeys.operationFailed'))
|
throw new Error(response.message || i18n.global.t('apiKeys.operationFailed'))
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('获取API Key统计失败:', err)
|
console.error(i18n.global.t('common.errors.getApiKeyStatsFailed'), err)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,7 +170,7 @@ export const useApiKeysStore = defineStore('apiKeys', () => {
|
|||||||
throw new Error(response.message || i18n.global.t('apiKeys.operationFailed'))
|
throw new Error(response.message || i18n.global.t('apiKeys.operationFailed'))
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('获取标签失败:', err)
|
console.error(i18n.global.t('common.errors.getTagsFailed'), err)
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -367,7 +367,7 @@ export const useApiStatsStore = defineStore('apistats', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (validIds.length === 0) {
|
if (validIds.length === 0) {
|
||||||
throw new Error('所有 API Key 都无效')
|
throw new Error(i18n.global.t('common.errors.allApiKeysInvalid'))
|
||||||
}
|
}
|
||||||
|
|
||||||
apiIds.value = validIds
|
apiIds.value = validIds
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载OEM设置失败:', error)
|
console.error(i18n.global.t('common.errors.loadOemSettingsFailed'), error)
|
||||||
} finally {
|
} finally {
|
||||||
oemLoading.value = false
|
oemLoading.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { ref, computed } from 'vue'
|
|||||||
import { apiClient } from '@/config/api'
|
import { apiClient } from '@/config/api'
|
||||||
import { showToast } from '@/utils/toast'
|
import { showToast } from '@/utils/toast'
|
||||||
import i18n from '@/i18n'
|
import i18n from '@/i18n'
|
||||||
import i18n from '@/i18n'
|
|
||||||
|
|
||||||
export const useDashboardStore = defineStore('dashboard', () => {
|
export const useDashboardStore = defineStore('dashboard', () => {
|
||||||
// 状态
|
// 状态
|
||||||
@@ -43,7 +42,7 @@ export const useDashboardStore = defineStore('dashboard', () => {
|
|||||||
realtimeTPM: 0,
|
realtimeTPM: 0,
|
||||||
metricsWindow: 5,
|
metricsWindow: 5,
|
||||||
isHistoricalMetrics: false,
|
isHistoricalMetrics: false,
|
||||||
systemStatus: '正常',
|
systemStatus: i18n.global.t('system.status.normal'),
|
||||||
uptime: 0,
|
uptime: 0,
|
||||||
systemTimezone: 8 // 默认 UTC+8
|
systemTimezone: 8 // 默认 UTC+8
|
||||||
})
|
})
|
||||||
@@ -200,7 +199,9 @@ export const useDashboardStore = defineStore('dashboard', () => {
|
|||||||
realtimeTPM: realtimeMetrics.tpm || 0,
|
realtimeTPM: realtimeMetrics.tpm || 0,
|
||||||
metricsWindow: realtimeMetrics.windowMinutes || 5,
|
metricsWindow: realtimeMetrics.windowMinutes || 5,
|
||||||
isHistoricalMetrics: realtimeMetrics.isHistorical || false,
|
isHistoricalMetrics: realtimeMetrics.isHistorical || false,
|
||||||
systemStatus: systemHealth.redisConnected ? '正常' : '异常',
|
systemStatus: systemHealth.redisConnected
|
||||||
|
? i18n.global.t('system.status.normal')
|
||||||
|
: i18n.global.t('system.status.abnormal'),
|
||||||
uptime: systemHealth.uptime || 0,
|
uptime: systemHealth.uptime || 0,
|
||||||
systemTimezone: dashboardResponse.data.systemTimezone || 8
|
systemTimezone: dashboardResponse.data.systemTimezone || 8
|
||||||
}
|
}
|
||||||
@@ -220,7 +221,7 @@ export const useDashboardStore = defineStore('dashboard', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载仪表板数据失败:', error)
|
console.error(i18n.global.t('common.errors.loadDashboardFailed'), error)
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
}
|
}
|
||||||
@@ -308,7 +309,7 @@ export const useDashboardStore = defineStore('dashboard', () => {
|
|||||||
trendData.value = response.data
|
trendData.value = response.data
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载使用趋势失败:', error)
|
console.error(i18n.global.t('common.errors.loadUsageTrendFailed'), error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -399,7 +400,7 @@ export const useDashboardStore = defineStore('dashboard', () => {
|
|||||||
dashboardModelStats.value = response.data
|
dashboardModelStats.value = response.data
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载模型统计失败:', error)
|
console.error(i18n.global.t('common.errors.loadModelStatsFailed'), error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,7 +502,7 @@ export const useDashboardStore = defineStore('dashboard', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载API Keys趋势失败:', error)
|
console.error(i18n.global.t('common.errors.loadApiKeysTrendFailed'), error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -309,13 +309,13 @@
|
|||||||
}}</span></span
|
}}</span></span
|
||||||
>
|
>
|
||||||
<span v-if="(dashboardData.totalCacheCreateTokens || 0) > 0" class="text-purple-600"
|
<span v-if="(dashboardData.totalCacheCreateTokens || 0) > 0" class="text-purple-600"
|
||||||
>缓存创建:
|
>{{ t('dashboard.cacheCreateTokens') }}:
|
||||||
<span class="font-medium">{{
|
<span class="font-medium">{{
|
||||||
formatNumber(dashboardData.totalCacheCreateTokens || 0)
|
formatNumber(dashboardData.totalCacheCreateTokens || 0)
|
||||||
}}</span></span
|
}}</span></span
|
||||||
>
|
>
|
||||||
<span v-if="(dashboardData.totalCacheReadTokens || 0) > 0" class="text-purple-600"
|
<span v-if="(dashboardData.totalCacheReadTokens || 0) > 0" class="text-purple-600"
|
||||||
>缓存读取:
|
>{{ t('dashboard.cacheReadTokens') }}:
|
||||||
<span class="font-medium">{{
|
<span class="font-medium">{{
|
||||||
formatNumber(dashboardData.totalCacheReadTokens || 0)
|
formatNumber(dashboardData.totalCacheReadTokens || 0)
|
||||||
}}</span></span
|
}}</span></span
|
||||||
@@ -930,11 +930,14 @@ function createUsageTrendChart() {
|
|||||||
const bLabel = b.dataset.label || ''
|
const bLabel = b.dataset.label || ''
|
||||||
|
|
||||||
// 费用和请求数使用不同的轴,单独处理
|
// 费用和请求数使用不同的轴,单独处理
|
||||||
if (aLabel === '费用 (USD)' || bLabel === '费用 (USD)') {
|
if (aLabel === t('dashboard.costLabel') || bLabel === t('dashboard.costLabel')) {
|
||||||
return aLabel === '费用 (USD)' ? -1 : 1
|
return aLabel === t('dashboard.costLabel') ? -1 : 1
|
||||||
}
|
}
|
||||||
if (aLabel === '请求数' || bLabel === '请求数') {
|
if (
|
||||||
return aLabel === '请求数' ? 1 : -1
|
aLabel === t('dashboard.requestsLabel') ||
|
||||||
|
bLabel === t('dashboard.requestsLabel')
|
||||||
|
) {
|
||||||
|
return aLabel === t('dashboard.requestsLabel') ? 1 : -1
|
||||||
}
|
}
|
||||||
|
|
||||||
// 其他按token值倒序
|
// 其他按token值倒序
|
||||||
@@ -945,15 +948,15 @@ function createUsageTrendChart() {
|
|||||||
const label = context.dataset.label || ''
|
const label = context.dataset.label || ''
|
||||||
let value = context.parsed.y
|
let value = context.parsed.y
|
||||||
|
|
||||||
if (label === '费用 (USD)') {
|
if (label === t('dashboard.costLabel')) {
|
||||||
// 格式化费用显示
|
// 格式化费用显示
|
||||||
if (value < 0.01) {
|
if (value < 0.01) {
|
||||||
return label + ': $' + value.toFixed(6)
|
return label + ': $' + value.toFixed(6)
|
||||||
} else {
|
} else {
|
||||||
return label + ': $' + value.toFixed(4)
|
return label + ': $' + value.toFixed(4)
|
||||||
}
|
}
|
||||||
} else if (label === '请求数') {
|
} else if (label === t('dashboard.requestsLabel')) {
|
||||||
return label + ': ' + value.toLocaleString() + ' 次'
|
return label + ': ' + value.toLocaleString()
|
||||||
} else {
|
} else {
|
||||||
// 格式化token数显示
|
// 格式化token数显示
|
||||||
if (value >= 1000000) {
|
if (value >= 1000000) {
|
||||||
|
|||||||
Reference in New Issue
Block a user