import axios from 'axios' import { ElMessage } from 'element-plus' import router from '@/router' const api = axios.create({ baseURL: '', timeout: 30000 }) /** * 解析 API 错误响应 */ function parseApiError(error) { const result = { code: 'UNKNOWN_ERROR', message: '发生了未知错误', traceId: '', status: 500 } // 网络错误(后端未启动、网络断开等) if (!error.response) { if (error.code === 'ECONNABORTED') { result.code = 'TIMEOUT_ERROR' result.message = '请求超时,请稍后重试' result.status = 0 } else if (error.message?.includes('Network Error')) { result.code = 'SERVICE_UNAVAILABLE' result.message = '服务暂时不可用,请稍后重试' result.status = 503 } else { result.code = 'NETWORK_ERROR' result.message = '网络连接失败,请检查网络后重试' result.status = 0 } return result } const { status, data, headers } = error.response result.status = status result.traceId = headers['x-trace-id'] || headers['X-Trace-ID'] || '' if (data && data.error) { result.code = data.error.code || result.code result.message = data.error.message || result.message result.traceId = data.error.trace_id || result.traceId } else if (data && data.detail) { result.message = typeof data.detail === 'string' ? data.detail : JSON.stringify(data.detail) } return result } /** * 跳转到错误页面(使用 sessionStorage + replace,不影响浏览器历史) */ function navigateToErrorPage(errorInfo) { // 记录当前页面路径(用于返回) sessionStorage.setItem('errorFromPath', router.currentRoute.value.fullPath) // 保存错误信息到 sessionStorage(不会显示在 URL 中) sessionStorage.setItem('errorInfo', JSON.stringify({ code: errorInfo.code, message: errorInfo.message, traceId: errorInfo.traceId, status: errorInfo.status, timestamp: Date.now() })) // 使用 replace 而不是 push,这样浏览器返回时不会停留在错误页 router.replace({ name: 'Error' }) } // 请求拦截器 api.interceptors.request.use( config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }, error => Promise.reject(error) ) // 响应拦截器(集成 TraceID 追踪) api.interceptors.response.use( response => response, error => { const errorInfo = parseApiError(error) const traceLog = errorInfo.traceId ? ` (trace: ${errorInfo.traceId})` : '' console.error(`[API Error] ${errorInfo.code}: ${errorInfo.message}${traceLog}`) // 严重错误列表(跳转错误页) const criticalErrors = [ 'INTERNAL_ERROR', 'SERVICE_UNAVAILABLE', 'GATEWAY_ERROR', 'NETWORK_ERROR', 'TIMEOUT_ERROR' ] if (error.response?.status === 401) { localStorage.removeItem('token') localStorage.removeItem('user') router.push('/login') ElMessage.error('登录已过期,请重新登录') } else if (error.response?.status === 403) { ElMessage.error('没有权限执行此操作') } else if (criticalErrors.includes(errorInfo.code)) { // 严重错误(包括网络错误、服务不可用)跳转到错误页面 navigateToErrorPage(errorInfo) } else { // 普通错误显示消息 ElMessage.error(errorInfo.message) } return Promise.reject(error) } ) export default api