fix:修复多语言配置,拦截函数配置,错误信息已后端为主

This commit is contained in:
13895237362 committed 2025-08-13 10:16:18 +08:00
1 parent b02479e3fb
commit 838e61ee44
8 files changed
+248 -50

No files matched your search

+1 -4
View File
@@ -6,11 +6,8 @@
# @FilePath: /frontend/.env.development
# @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
###
# 开发环境
NODE_ENV = "development"
# 开发环境接口地址
VITE_API_BASE_URL = ''
VITE_API_BASE_URL = 'http://104.156.140.42:18888'
# # 开发环境打包名称
# VITE_OUTDIR='dev'
+1 -4
View File
@@ -1,8 +1,5 @@
# 正式环境
NODE_ENV = "production"
# 正式环境接口地址
VITE_API_BASE_URL = ''
VITE_API_BASE_URL = 'http://104.156.140.42:18888'
# 正式环境打包名称
VITE_OUTDIR='production'
-3
View File
@@ -6,9 +6,6 @@
# @FilePath: /frontend/.env.test
# @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
###
# 测试环境
NODE_ENV = "test"
# 测试环境接口地址
VITE_API_BASE_URL = ''
+8
View File
@@ -1,3 +1,11 @@
/*
* @Author: 13895237362 2205451508@qq.com
* @Date: 2025-07-31 14:30:20
* @LastEditors: 13895237362 2205451508@qq.com
* @LastEditTime: 2025-08-13 09:38:31
* @FilePath: /frontend/src/api/https/errorCodeType.ts
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
export const errorCodeType = function(status:Number):string{
let errMessage:string = "未知错误"
switch (status) {
+34 -23
View File
@@ -2,8 +2,9 @@ import axios from 'axios'
// http状态码
import { errorCodeType } from './errorCodeType'
//element-plus提示消息组件
import { ElMessage } from "element-plus"
// 语言管理器
import { generateAcceptLanguageHeader } from '@/utils/language'
const envUrl = import.meta.env.VITE_API_BASE_URL
//使用create方法创建axios实例
@@ -13,8 +14,8 @@ const Axios = axios.create({
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
// 跨域时候允许携带凭证
withCredentials: true
// 跨域时候允许携带凭证 (开发环境不启用,避免CORS问题)
withCredentials: false
})
// 添加请求拦截器
Axios.interceptors.request.use(config => {
@@ -23,6 +24,11 @@ Axios.interceptors.request.use(config => {
if(token){
config.headers['Authorization'] = `Bearer ${token}`
}
// 设置 Accept-Language 头,符合 RFC 7231 规范
// 优先级:用户选择;q=1.0 > 备用语言;q=0.8 > 通配符;q=0.5
config.headers['Accept-Language'] = generateAcceptLanguageHeader()
return config
})
// 添加响应拦截器
@@ -33,7 +39,7 @@ Axios.interceptors.response.use(response => {
const code = apiData.code
// 如果没有 Code, 代表这不是项目后端开发的 API
if (code === undefined) {
ElMessage.error("非本系统的接口")
// 不在拦截器中显示错误,让组件自己处理
return Promise.reject(new Error("非本系统的接口"))
} else {
switch (code) {
@@ -44,30 +50,35 @@ Axios.interceptors.response.use(response => {
// code === 0 也代表成功 (兼容)
return apiData
default:
// 不是正确的 Code
ElMessage.error(apiData.message || "Error")
return Promise.reject(new Error("Error"))
// 不在拦截器中显示错误,让组件自己处理
// 将错误信息附加到 Error 对象上,供组件使用
const error = new Error(apiData.message || "Error")
;(error as any).response = { data: apiData }
return Promise.reject(error)
}
}
}, error => {
console.log('Response的error', error)
showErrMessage(errorCodeType(error.response.status))
// 不在拦截器中显示错误消息,让具体的页面组件处理
// showErrMessage(errorCodeType(error.response?.status))
// 为错误对象添加更多信息,便于组件处理
if (error.response) {
const status = error.response.status
const data = error.response.data
// 如果后端返回了错误消息,优先使用后端消息
if (data && data.message) {
;(error as any).backendMessage = data.message
} else {
// 否则使用状态码对应的消息
;(error as any).backendMessage = errorCodeType(status)
}
}
return Promise.reject(error)
})
/**
* @description 显示错误消息
* opt 传入参数
* err 错误信息
* type 消息类型
* duration 消息持续时间
*/
function showErrMessage (errMessage: string, type:any= 'error', duration:number = 5000){
ElMessage({
message: errMessage,
type:type,
duration: duration
})
}
// 错误处理函数已移除,由页面组件统一处理错误显示
export default Axios;
+50 -14
View File
@@ -33,27 +33,39 @@ export const elementLocales = {
// 支持的语言类型
export type SupportedLocale = 'en' | 'zh' | 'ja'
// 语言代码标准映射
export const LANGUAGE_MAP = {
'zh': { code: 'zh', iso: 'zh-CN', name: '中文' },
'en': { code: 'en', iso: 'en-US', name: 'English' },
'ja': { code: 'ja', iso: 'ja-JP', name: '日本語' }
} as const
// 检测浏览器语言并返回支持的语言
function detectBrowserLanguage(): SupportedLocale {
// 优先使用本地存储中的语言偏好
// 1. 优先使用本地存储中的语言偏好(用户显式选择)
const savedLanguage = localStorage.getItem('userLanguage') as SupportedLocale
if (savedLanguage && ['en', 'zh', 'ja'].includes(savedLanguage)) {
if (savedLanguage && Object.keys(LANGUAGE_MAP).includes(savedLanguage)) {
return savedLanguage
}
// 获取浏览器语言
const browserLanguage = navigator.language.toLowerCase()
// 2. 检测浏览器语言设置
const browserLanguages = navigator.languages || [navigator.language]
// 匹配浏览器语言到支持的语言
if (browserLanguage.startsWith('en')) {
return 'en'
} else if (browserLanguage.startsWith('ja')) {
return 'ja'
} else if (browserLanguage.startsWith('zh')) {
return 'zh'
for (const lang of browserLanguages) {
const langCode = lang.toLowerCase()
// 精确匹配
if (langCode === 'zh-cn' || langCode === 'zh') return 'zh'
if (langCode === 'en-us' || langCode === 'en') return 'en'
if (langCode === 'ja-jp' || langCode === 'ja') return 'ja'
// 前缀匹配
if (langCode.startsWith('zh')) return 'zh'
if (langCode.startsWith('en')) return 'en'
if (langCode.startsWith('ja')) return 'ja'
}
// 默认回退到中文
// 3. 默认回退到中文
return 'zh'
}
@@ -67,12 +79,36 @@ const i18n = createI18n({
// 设置语言的函数
export function setI18nLanguage(locale: SupportedLocale) {
// 验证语言代码
if (!Object.keys(LANGUAGE_MAP).includes(locale)) {
console.warn(`Unsupported locale: ${locale}, falling back to zh`)
locale = 'zh'
}
// 更新 i18n 当前语言
i18n.global.locale.value = locale
// 设置 HTML 文档的 lang 属性
document.documentElement.setAttribute('lang', locale)
// 设置 HTML 文档的 lang 属性为标准 ISO 代码
const isoCode = LANGUAGE_MAP[locale].iso
document.documentElement.setAttribute('lang', isoCode)
// 保存用户语言偏好(用于下次访问和 Accept-Language 头)
localStorage.setItem('userLanguage', locale)
// 触发自定义事件,通知其他组件语言已变更
window.dispatchEvent(new CustomEvent('language-change', {
detail: { locale, isoCode, name: LANGUAGE_MAP[locale].name }
}))
return locale
}
// 获取当前语言的 ISO 代码
export function getCurrentLanguageISO(): string {
const currentLocale = i18n.global.locale.value as SupportedLocale
return LANGUAGE_MAP[currentLocale]?.iso || 'zh-CN'
}
// 获取当前Element Plus语言包
export function getCurrentElementLocale() {
const currentLocale = i18n.global.locale.value as SupportedLocale
+4 -2
View File
@@ -6,7 +6,7 @@ export const useAppStore = defineStore('app', () => {
const sidebarCollapsed = ref(false)
const isMobile = ref(false)
const theme = ref<'light' | 'dark' | 'auto'>('light')
const language = ref<'zh' | 'en'>('zh')
const language = ref<'zh' | 'en' | 'ja'>('zh')
const loading = ref(false)
// 计算属性
@@ -35,8 +35,10 @@ export const useAppStore = defineStore('app', () => {
document.documentElement.setAttribute('data-theme', newTheme)
}
const setLanguage = (lang: 'zh' | 'en') => {
const setLanguage = (lang: 'zh' | 'en' | 'ja') => {
language.value = lang
// 同时更新 localStorage 以保持一致性
localStorage.setItem('userLanguage', lang)
}
const setLoading = (isLoading: boolean) => {
+150
View File
@@ -0,0 +1,150 @@
/**
* 语言管理工具类
* 提供统一的语言切换、检测和 Accept-Language 头管理
*/
import { setI18nLanguage, LANGUAGE_MAP, type SupportedLocale } from '@/i18n'
import { useAppStore } from '@/store/modules/app'
// 导出类型供其他模块使用
export type { SupportedLocale }
export class LanguageManager {
private static instance: LanguageManager
public static getInstance(): LanguageManager {
if (!LanguageManager.instance) {
LanguageManager.instance = new LanguageManager()
}
return LanguageManager.instance
}
/**
* 切换语言并同步所有相关状态
* @param locale 目标语言代码
*/
public changeLanguage(locale: SupportedLocale): void {
try {
// 1. 更新 i18n
setI18nLanguage(locale)
// 2. 更新 app store
const appStore = useAppStore()
appStore.setLanguage(locale)
// 3. 记录日志
console.log(`Language changed to: ${locale} (${LANGUAGE_MAP[locale].iso})`)
} catch (error) {
console.error('Failed to change language:', error)
throw error
}
}
/**
* 获取当前语言信息
*/
public getCurrentLanguage(): {
code: SupportedLocale
iso: string
name: string
} {
const code = (localStorage.getItem('userLanguage') as SupportedLocale) || 'zh'
const languageInfo = LANGUAGE_MAP[code]
return {
code,
iso: languageInfo.iso,
name: languageInfo.name
}
}
/**
* 生成标准的 Accept-Language 头
* 格式: 主语言;q=1.0, 备用语言;q=0.8, *;q=0.5
*/
public generateAcceptLanguageHeader(): string {
const currentLang = this.getCurrentLanguage()
const primaryLang = currentLang.iso
const fallbackLang = primaryLang !== 'en-US' ? 'en-US' : 'zh-CN'
return `${primaryLang};q=1.0, ${fallbackLang};q=0.8, *;q=0.5`
}
/**
* 获取所有支持的语言列表
*/
public getSupportedLanguages(): Array<{
code: SupportedLocale
iso: string
name: string
}> {
return Object.entries(LANGUAGE_MAP).map(([code, info]) => ({
code: code as SupportedLocale,
iso: info.iso,
name: info.name
}))
}
/**
* 检测浏览器首选语言
*/
public detectBrowserLanguage(): SupportedLocale {
const browserLanguages = navigator.languages || [navigator.language]
for (const lang of browserLanguages) {
const langCode = lang.toLowerCase()
// 精确匹配
if (langCode === 'zh-cn' || langCode === 'zh') return 'zh'
if (langCode === 'en-us' || langCode === 'en') return 'en'
if (langCode === 'ja-jp' || langCode === 'ja') return 'ja'
// 前缀匹配
if (langCode.startsWith('zh')) return 'zh'
if (langCode.startsWith('en')) return 'en'
if (langCode.startsWith('ja')) return 'ja'
}
return 'zh' // 默认中文
}
/**
* 初始化语言设置
* 优先级:localStorage > 浏览器语言 > 默认中文
*/
public initializeLanguage(): SupportedLocale {
const savedLanguage = localStorage.getItem('userLanguage') as SupportedLocale
if (savedLanguage && Object.keys(LANGUAGE_MAP).includes(savedLanguage)) {
this.changeLanguage(savedLanguage)
return savedLanguage
}
const browserLanguage = this.detectBrowserLanguage()
this.changeLanguage(browserLanguage)
return browserLanguage
}
/**
* 监听语言变更事件
*/
public onLanguageChange(callback: (event: CustomEvent) => void): void {
window.addEventListener('language-change', callback as EventListener)
}
/**
* 移除语言变更监听器
*/
public offLanguageChange(callback: (event: CustomEvent) => void): void {
window.removeEventListener('language-change', callback as EventListener)
}
}
// 导出单例实例
export const languageManager = LanguageManager.getInstance()
// 导出便捷函数
export const changeLanguage = (locale: SupportedLocale) => languageManager.changeLanguage(locale)
export const getCurrentLanguage = () => languageManager.getCurrentLanguage()
export const generateAcceptLanguageHeader = () => languageManager.generateAcceptLanguageHeader()