mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
feat: 实现ApiStatsView页面完整国际化
- 集成vue-i18n到ApiStatsView,支持动态语言切换 - 国际化所有用户界面文本:页面标题、按钮、Tab标签、时间选择器 - 实现LogoTitle动态subtitle,根据当前tab显示对应语言的标题 - 添加语言切换组件到页面header,与主题切换并列显示 - 实现教程内容的整体替换机制,支持基于语言的动态组件选择 - 确保用户登录、管理后台、统计查询等核心功能完全本地化
This commit is contained in:
@@ -6,10 +6,15 @@
|
|||||||
<LogoTitle
|
<LogoTitle
|
||||||
:loading="oemLoading"
|
:loading="oemLoading"
|
||||||
:logo-src="oemSettings.siteIconData || oemSettings.siteIcon"
|
:logo-src="oemSettings.siteIconData || oemSettings.siteIcon"
|
||||||
:subtitle="currentTab === 'stats' ? 'API Key 使用统计' : '使用教程'"
|
:subtitle="currentTab === 'stats' ? t('apiStats.title') : t('apiStats.tutorialTitle')"
|
||||||
:title="oemSettings.siteName"
|
:title="oemSettings.siteName"
|
||||||
/>
|
/>
|
||||||
<div class="flex items-center gap-2 md:gap-4">
|
<div class="flex items-center gap-2 md:gap-4">
|
||||||
|
<!-- 语言切换按钮 -->
|
||||||
|
<div class="flex items-center">
|
||||||
|
<LanguageSwitch mode="dropdown" size="medium" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 主题切换按钮 -->
|
<!-- 主题切换按钮 -->
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<ThemeToggle mode="dropdown" />
|
<ThemeToggle mode="dropdown" />
|
||||||
@@ -28,7 +33,7 @@
|
|||||||
to="/user-login"
|
to="/user-login"
|
||||||
>
|
>
|
||||||
<i class="fas fa-user text-sm md:text-base" />
|
<i class="fas fa-user text-sm md:text-base" />
|
||||||
<span class="text-xs font-semibold tracking-wide md:text-sm">用户登录</span>
|
<span class="text-xs font-semibold tracking-wide md:text-sm">{{ t('apiStats.userLogin') }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
<!-- 管理后台按钮 -->
|
<!-- 管理后台按钮 -->
|
||||||
<router-link
|
<router-link
|
||||||
@@ -37,7 +42,7 @@
|
|||||||
to="/dashboard"
|
to="/dashboard"
|
||||||
>
|
>
|
||||||
<i class="fas fa-shield-alt text-sm md:text-base" />
|
<i class="fas fa-shield-alt text-sm md:text-base" />
|
||||||
<span class="text-xs font-semibold tracking-wide md:text-sm">管理后台</span>
|
<span class="text-xs font-semibold tracking-wide md:text-sm">{{ t('apiStats.adminPanel') }}</span>
|
||||||
</router-link>
|
</router-link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -54,14 +59,14 @@
|
|||||||
@click="currentTab = 'stats'"
|
@click="currentTab = 'stats'"
|
||||||
>
|
>
|
||||||
<i class="fas fa-chart-line mr-1 md:mr-2" />
|
<i class="fas fa-chart-line mr-1 md:mr-2" />
|
||||||
<span class="text-sm md:text-base">统计查询</span>
|
<span class="text-sm md:text-base">{{ t('apiStats.statsQuery') }}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
:class="['tab-pill-button', currentTab === 'tutorial' ? 'active' : '']"
|
:class="['tab-pill-button', currentTab === 'tutorial' ? 'active' : '']"
|
||||||
@click="currentTab = 'tutorial'"
|
@click="currentTab = 'tutorial'"
|
||||||
>
|
>
|
||||||
<i class="fas fa-graduation-cap mr-1 md:mr-2" />
|
<i class="fas fa-graduation-cap mr-1 md:mr-2" />
|
||||||
<span class="text-sm md:text-base">使用教程</span>
|
<span class="text-sm md:text-base">{{ t('apiStats.tutorial') }}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -93,7 +98,7 @@
|
|||||||
<div class="flex items-center gap-2 md:gap-3">
|
<div class="flex items-center gap-2 md:gap-3">
|
||||||
<i class="fas fa-clock text-base text-blue-500 md:text-lg" />
|
<i class="fas fa-clock text-base text-blue-500 md:text-lg" />
|
||||||
<span class="text-base font-medium text-gray-700 dark:text-gray-200 md:text-lg"
|
<span class="text-base font-medium text-gray-700 dark:text-gray-200 md:text-lg"
|
||||||
>统计时间范围</span
|
>{{ t('apiStats.timeRange') }}</span
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex w-full gap-2 md:w-auto">
|
<div class="flex w-full gap-2 md:w-auto">
|
||||||
@@ -104,7 +109,7 @@
|
|||||||
@click="switchPeriod('daily')"
|
@click="switchPeriod('daily')"
|
||||||
>
|
>
|
||||||
<i class="fas fa-calendar-day text-xs md:text-sm" />
|
<i class="fas fa-calendar-day text-xs md:text-sm" />
|
||||||
今日
|
{{ t('apiStats.today') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="flex flex-1 items-center justify-center gap-1 px-4 py-2 text-xs font-medium md:flex-none md:gap-2 md:px-6 md:text-sm"
|
class="flex flex-1 items-center justify-center gap-1 px-4 py-2 text-xs font-medium md:flex-none md:gap-2 md:px-6 md:text-sm"
|
||||||
@@ -113,7 +118,7 @@
|
|||||||
@click="switchPeriod('monthly')"
|
@click="switchPeriod('monthly')"
|
||||||
>
|
>
|
||||||
<i class="fas fa-calendar-alt text-xs md:text-sm" />
|
<i class="fas fa-calendar-alt text-xs md:text-sm" />
|
||||||
本月
|
{{ t('apiStats.thisMonth') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -140,7 +145,7 @@
|
|||||||
<!-- 教程内容 -->
|
<!-- 教程内容 -->
|
||||||
<div v-if="currentTab === 'tutorial'" class="tab-content">
|
<div v-if="currentTab === 'tutorial'" class="tab-content">
|
||||||
<div class="glass-strong rounded-3xl shadow-xl">
|
<div class="glass-strong rounded-3xl shadow-xl">
|
||||||
<TutorialView />
|
<component :is="currentTutorialComponent" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -150,21 +155,28 @@
|
|||||||
import { ref, onMounted, onUnmounted, watch, computed } from 'vue'
|
import { ref, onMounted, onUnmounted, watch, computed } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useApiStatsStore } from '@/stores/apistats'
|
import { useApiStatsStore } from '@/stores/apistats'
|
||||||
import { useThemeStore } from '@/stores/theme'
|
import { useThemeStore } from '@/stores/theme'
|
||||||
|
import { useLocaleStore } from '@/stores/locale'
|
||||||
import LogoTitle from '@/components/common/LogoTitle.vue'
|
import LogoTitle from '@/components/common/LogoTitle.vue'
|
||||||
import ThemeToggle from '@/components/common/ThemeToggle.vue'
|
import ThemeToggle from '@/components/common/ThemeToggle.vue'
|
||||||
|
import LanguageSwitch from '@/components/common/LanguageSwitch.vue'
|
||||||
import ApiKeyInput from '@/components/apistats/ApiKeyInput.vue'
|
import ApiKeyInput from '@/components/apistats/ApiKeyInput.vue'
|
||||||
import StatsOverview from '@/components/apistats/StatsOverview.vue'
|
import StatsOverview from '@/components/apistats/StatsOverview.vue'
|
||||||
import TokenDistribution from '@/components/apistats/TokenDistribution.vue'
|
import TokenDistribution from '@/components/apistats/TokenDistribution.vue'
|
||||||
import LimitConfig from '@/components/apistats/LimitConfig.vue'
|
import LimitConfig from '@/components/apistats/LimitConfig.vue'
|
||||||
import AggregatedStatsCard from '@/components/apistats/AggregatedStatsCard.vue'
|
import AggregatedStatsCard from '@/components/apistats/AggregatedStatsCard.vue'
|
||||||
import ModelUsageStats from '@/components/apistats/ModelUsageStats.vue'
|
import ModelUsageStats from '@/components/apistats/ModelUsageStats.vue'
|
||||||
import TutorialView from './TutorialView.vue'
|
import TutorialViewZhCn from './TutorialView.vue'
|
||||||
|
import TutorialViewZhTw from './TutorialView.vue'
|
||||||
|
import TutorialViewEn from './TutorialView.vue'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
const { t } = useI18n()
|
||||||
const apiStatsStore = useApiStatsStore()
|
const apiStatsStore = useApiStatsStore()
|
||||||
const themeStore = useThemeStore()
|
const themeStore = useThemeStore()
|
||||||
|
const localeStore = useLocaleStore()
|
||||||
|
|
||||||
// 当前标签页
|
// 当前标签页
|
||||||
const currentTab = ref('stats')
|
const currentTab = ref('stats')
|
||||||
@@ -172,6 +184,17 @@ const currentTab = ref('stats')
|
|||||||
// 主题相关
|
// 主题相关
|
||||||
const isDarkMode = computed(() => themeStore.isDarkMode)
|
const isDarkMode = computed(() => themeStore.isDarkMode)
|
||||||
|
|
||||||
|
// 根据当前语言选择教程组件
|
||||||
|
const currentTutorialComponent = computed(() => {
|
||||||
|
const locale = localeStore.currentLocale
|
||||||
|
const components = {
|
||||||
|
'zh-cn': TutorialViewZhCn,
|
||||||
|
'zh-tw': TutorialViewZhTw,
|
||||||
|
'en': TutorialViewEn
|
||||||
|
}
|
||||||
|
return components[locale] || TutorialViewZhCn
|
||||||
|
})
|
||||||
|
|
||||||
const {
|
const {
|
||||||
apiKey,
|
apiKey,
|
||||||
apiId,
|
apiId,
|
||||||
|
|||||||
Reference in New Issue
Block a user