fix:修复多语言配置,拦截函数配置,错误信息已后端为主
This commit is contained in:
1 parent
b02479e3fb
commit
838e61ee44
8 files changed
+248
-50
No files matched your search
@@ -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,8 +1,5 @@
|
||||
# 正式环境
|
||||
NODE_ENV = "production"
|
||||
|
||||
# 正式环境接口地址
|
||||
VITE_API_BASE_URL = ''
|
||||
VITE_API_BASE_URL = 'http://104.156.140.42:18888'
|
||||
|
||||
# 正式环境打包名称
|
||||
VITE_OUTDIR='production'
|
||||
|
||||
@@ -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 = ''
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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()
|
||||
Reference in new issue
Block a user