feat: 语言切换
This commit is contained in:
@@ -108,3 +108,20 @@ export const verifyEmailCode = (verifyCode: string): Promise<ApiResponse> => {
|
||||
data: { verifyCode }
|
||||
})
|
||||
}
|
||||
|
||||
// 获取用户语言设置
|
||||
export const getUserLanguage = (): Promise<ApiResponse> => {
|
||||
return request({
|
||||
url: '/buyer/profile/getLanguage',
|
||||
method: 'post'
|
||||
})
|
||||
}
|
||||
|
||||
// 设置语言
|
||||
export const setUserLanguage = (language: string): Promise<ApiResponse> => {
|
||||
return request({
|
||||
url: '/buyer/profile/setLanguage',
|
||||
method: 'post',
|
||||
data: { language }
|
||||
})
|
||||
}
|
||||
@@ -1,4 +1,16 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import { useUserInfoStore } from '@/stores/userInfo'
|
||||
import { getUserLanguage } from '@/api/user'
|
||||
import i18n from '@/lang/index'
|
||||
|
||||
// 语言映射:后端格式 -> i18n 格式
|
||||
const backendToI18nLanguage: Record<string, string> = {
|
||||
'en': 'ENGLISH',
|
||||
'zh-CN': 'CHINESE_SIMPLIFIED'
|
||||
}
|
||||
|
||||
// 语言同步状态缓存(避免每次路由切换都请求)
|
||||
let languageSynced = false
|
||||
|
||||
/**
|
||||
* 路由缓存机制:
|
||||
@@ -83,6 +95,52 @@ router.beforeEach((to, from, next) => {
|
||||
next()
|
||||
})
|
||||
|
||||
router.afterEach(() => {})
|
||||
router.afterEach(async () => {
|
||||
// 检查用户是否已登录
|
||||
const userInfoStore = useUserInfoStore()
|
||||
const token = userInfoStore.state.token
|
||||
|
||||
if (!token) {
|
||||
languageSynced = false // 未登录时重置同步状态
|
||||
return
|
||||
}
|
||||
|
||||
// 如果已经同步过,跳过
|
||||
if (languageSynced) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取用户语言设置
|
||||
const response = await getUserLanguage()
|
||||
const userLanguage = (response as any)?.language // 后端返回 'en' 或 'zh-CN'
|
||||
|
||||
if (!userLanguage) {
|
||||
return
|
||||
}
|
||||
|
||||
// 转换为 i18n 格式
|
||||
const i18nLanguage = backendToI18nLanguage[userLanguage]
|
||||
|
||||
if (!i18nLanguage) {
|
||||
return
|
||||
}
|
||||
|
||||
// 获取当前 i18n 语言
|
||||
const currentLocale = i18n.global.locale.value
|
||||
|
||||
// 如果用户语言和本地 i18n 不一致,更新 i18n
|
||||
if (i18nLanguage !== currentLocale) {
|
||||
i18n.global.locale.value = i18nLanguage as 'ENGLISH' | 'CHINESE_SIMPLIFIED'
|
||||
localStorage.setItem('language', i18nLanguage)
|
||||
}
|
||||
|
||||
// 标记已同步
|
||||
languageSynced = true
|
||||
} catch (error) {
|
||||
// 静默失败,不影响页面正常加载
|
||||
console.warn('Failed to sync user language:', error)
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
|
||||
@@ -87,6 +87,8 @@
|
||||
import myEvent from '@/utils/myEvent'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useUserInfoStore } from '@/stores/userInfo'
|
||||
import { setUserLanguage } from '@/api/user'
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
const userInfoStore = useUserInfoStore()
|
||||
const router = useRouter()
|
||||
@@ -153,6 +155,12 @@
|
||||
const onLanguageClick = () => {
|
||||
locale.value = locale.value === 'ENGLISH' ? 'CHINESE_SIMPLIFIED' : 'ENGLISH'
|
||||
localStorage.setItem('language', locale.value)
|
||||
console.log(locale.value)
|
||||
const localeMap: Record<string, string> = {
|
||||
ENGLISH: 'en',
|
||||
CHINESE_SIMPLIFIED: 'zh-CN'
|
||||
}
|
||||
setUserLanguage(localeMap[locale.value])
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -26,11 +26,24 @@ interface UseSettingsFormOptions {
|
||||
locale: Ref<string>
|
||||
}
|
||||
|
||||
// 前端 UI 使用的语言值(用于下拉选择)
|
||||
const languageLocaleMap: Record<LanguageValue, 'ENGLISH' | 'CHINESE_SIMPLIFIED'> = {
|
||||
english: 'ENGLISH',
|
||||
chinese: 'CHINESE_SIMPLIFIED'
|
||||
}
|
||||
|
||||
// 后端 API 使用的语言值
|
||||
const backendLanguageMap: Record<'ENGLISH' | 'CHINESE_SIMPLIFIED', 'en' | 'zh-CN'> = {
|
||||
ENGLISH: 'en',
|
||||
CHINESE_SIMPLIFIED: 'zh-CN'
|
||||
}
|
||||
|
||||
// 后端语言值转换为前端语言值
|
||||
const backendToFrontendLanguage: Record<'en' | 'zh-CN', LanguageValue> = {
|
||||
en: 'english',
|
||||
'zh-CN': 'chinese'
|
||||
}
|
||||
|
||||
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
||||
|
||||
const createDefaultData = (): SettingsData => ({
|
||||
@@ -58,8 +71,15 @@ const normalizeLanguage = (language: string | null | undefined): LanguageValue =
|
||||
return '' as LanguageValue
|
||||
}
|
||||
|
||||
const normalized = language.trim().toLowerCase()
|
||||
return normalized.includes('chinese') ? 'chinese' : 'english'
|
||||
// 后端返回 'en' 或 'zh-CN'
|
||||
const trimmed = language.trim()
|
||||
if (trimmed === 'en') {
|
||||
return 'english'
|
||||
} else if (trimmed === 'zh-CN') {
|
||||
return 'chinese'
|
||||
}
|
||||
|
||||
return '' as LanguageValue
|
||||
}
|
||||
|
||||
const buildSettingsDataFromProfile = (profile: Partial<UserProfile>): SettingsData => ({
|
||||
@@ -308,14 +328,20 @@ export function useSettingsForm({ t, locale }: UseSettingsFormOptions) {
|
||||
return
|
||||
}
|
||||
|
||||
const nextEmail = securityDraft.value.newEmail.trim() || draftData.value.email || ''
|
||||
// 将前端语言值转换为后端格式
|
||||
let backendLanguage = ''
|
||||
if (draftData.value.language) {
|
||||
const i18nLocale = languageLocaleMap[draftData.value.language as LanguageValue]
|
||||
backendLanguage = backendLanguageMap[i18nLocale]
|
||||
}
|
||||
|
||||
const nextData: UserProfile = {
|
||||
firstName: draftData.value.firstName.trim(),
|
||||
lastName: draftData.value.lastName.trim(),
|
||||
username: draftData.value.username.trim(),
|
||||
email: nextEmail,
|
||||
email: securityDraft.value.newEmail.trim(),
|
||||
roles: draftData.value.roles as string[],
|
||||
language: draftData.value.language,
|
||||
language: backendLanguage, // 发送后端格式:'en' 或 'zh-CN'
|
||||
region: draftData.value.region,
|
||||
newPassword: '',
|
||||
oldPassword: '',
|
||||
@@ -337,26 +363,34 @@ export function useSettingsForm({ t, locale }: UseSettingsFormOptions) {
|
||||
|
||||
try {
|
||||
await updateUserProfile(nextData)
|
||||
|
||||
// 将后端返回的语言值转换为前端格式
|
||||
const frontendLanguage = backendLanguage
|
||||
? backendToFrontendLanguage[backendLanguage as 'en' | 'zh-CN']
|
||||
: ''
|
||||
|
||||
const settingsData: SettingsData = {
|
||||
firstName: nextData.firstName,
|
||||
lastName: nextData.lastName,
|
||||
username: nextData.username,
|
||||
email: nextData.email,
|
||||
roles: nextData.roles as RoleValue[],
|
||||
language: nextData.language as LanguageValue | '',
|
||||
language: frontendLanguage,
|
||||
region: nextData.region as any
|
||||
}
|
||||
sourceData.value = cloneSettingsData(settingsData)
|
||||
console.log(nextData)
|
||||
|
||||
if (nextData.language && nextData.language !== previousLanguage) {
|
||||
syncAppLanguage(nextData.language as LanguageValue)
|
||||
if (frontendLanguage && frontendLanguage !== previousLanguage) {
|
||||
syncAppLanguage(frontendLanguage as LanguageValue)
|
||||
}
|
||||
|
||||
draftData.value = cloneSettingsData(sourceData.value)
|
||||
securityDraft.value = createEmptySecurityDraft()
|
||||
resetEmailVerificationState()
|
||||
isEditing.value = false
|
||||
isEditingEmail.value = false
|
||||
isEditingPassword.value = false
|
||||
ElMessage.success(t('Settings.messages.settingsUpdated'))
|
||||
} catch (error) {
|
||||
console.warn(error)
|
||||
|
||||
Reference in New Issue
Block a user