/** * Axios HTTP 请求封装 * 统一处理请求、响应、错误和Token */ import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios' import { message } from 'antd' import type { ApiResponse } from '@/types' // 创建axios实例 const request: AxiosInstance = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:18099', timeout: 30000, headers: { 'Content-Type': 'application/json', }, }) // Token存储键(统一使用 hrt-token,与后端约定一致) const TOKEN_KEY = 'hrt-token' // 获取Token export const getToken = (): string | null => { return localStorage.getItem(TOKEN_KEY) } // 设置Token export const setToken = (token: string): void => { localStorage.setItem(TOKEN_KEY, token) } // 移除Token export const removeToken = (): void => { localStorage.removeItem(TOKEN_KEY) } // 请求拦截器 request.interceptors.request.use( (config) => { // 自动添加Token到请求头(前台用户使用hrt-token) const token = getToken() if (token && config.headers) { config.headers['hrt-token'] = token } return config }, (error: AxiosError) => { console.error('请求错误:', error) return Promise.reject(error) } ) // 响应拦截器 request.interceptors.response.use( (response: AxiosResponse) => { const { data } = response // 后端使用 mica 框架的 R 类包装响应 // R.success() 格式:{ code: 1, success: true, data: ..., msg: "操作成功" } // R.error() 格式:{ code: xxx, success: false, data: null, msg: "错误信息" } if (data && typeof data === 'object') { // 检查 code 字段(mica 框架使用 code) if ('code' in data) { // 成功响应判断(支持多种成功标识) // 1. success === true(最可靠的成功标志) // 2. code === 1(后端实际使用 - 重要!) // 3. code === 0(部分框架默认) // 4. code === 200(HTTP标准) const isSuccess = data.success === true || data.success === 'true' || data.code === 1 || // ✅ 后端实际使用 code: 1 表示成功 data.code === 0 || data.code === 200 if (isSuccess) { return data.data } // 业务错误 const errorMessage = data.msg || data.message || '请求失败' // 🔥 识别未登录相关的错误,自动跳转到登录页 const needLoginKeywords = [ '未提供token', '未提供Token', 'token无效', 'Token无效', '未登录', '请登录', '登录已过期', '身份验证失败', '未授权' ] const needLogin = needLoginKeywords.some(keyword => errorMessage.toLowerCase().includes(keyword.toLowerCase()) ) if (needLogin) { message.warning('请先登录') removeToken() // 延迟跳转,让用户看到提示 setTimeout(() => { window.location.href = '/login' }, 500) } return Promise.reject(new Error(errorMessage)) } // 兼容其他格式:{ status, msg, data } if ('status' in data) { if (data.status === 200 || data.status === 0) { return data.data } const errorMessage = data.msg || data.message || '请求失败' return Promise.reject(new Error(errorMessage)) } } // 如果后端直接返回数据(非标准格式) return data }, (error: AxiosError) => { // HTTP错误处理 if (error.response) { const { status, data } = error.response let errorMessage = '请求失败' // 优先从 data.msg 或 data.message 提取错误信息 if (data && typeof data === 'object') { errorMessage = data.msg || data.message || errorMessage } // 针对特定 HTTP 状态码的处理 switch (status) { case 400: errorMessage = errorMessage || '请求参数错误' break case 401: errorMessage = '未授权,请重新登录' removeToken() window.location.href = '/login' break case 403: errorMessage = errorMessage || '没有权限访问' break case 404: errorMessage = '请求的资源不存在' break case 500: errorMessage = errorMessage || '服务器错误' break } // 将错误信息附加到error对象上 const enhancedError = new Error(errorMessage) as any enhancedError.status = status enhancedError.response = error.response return Promise.reject(enhancedError) } else if (error.request) { // 请求已发出但没有收到响应 return Promise.reject(new Error('网络连接失败,请检查网络')) } else { // 请求配置出错 return Promise.reject(new Error('请求配置错误')) } } ) // 导出请求方法 export default request // 常用请求方法封装 export const http = { get(url: string, config?: AxiosRequestConfig): Promise { return request.get(url, config) }, post(url: string, data?: any, config?: AxiosRequestConfig): Promise { return request.post(url, data, config) }, put(url: string, data?: any, config?: AxiosRequestConfig): Promise { return request.put(url, data, config) }, delete(url: string, config?: AxiosRequestConfig): Promise { return request.delete(url, config) }, patch(url: string, data?: any, config?: AxiosRequestConfig): Promise { return request.patch(url, data, config) }, }