axios.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';
  2. import { toast } from 'sonner';
  3. import { env } from '../config/env';
  4. // 创建 axios 实例
  5. const axiosInstance: AxiosInstance = axios.create({
  6. baseURL: env.baseUrl ? `${env.baseUrl}/api` : '/api',
  7. timeout: 30000, // 30秒超时
  8. headers: {
  9. 'Content-Type': 'application/json',
  10. },
  11. });
  12. // 请求拦截器
  13. axiosInstance.interceptors.request.use(
  14. (config) => {
  15. // 从 localStorage 获取 token
  16. const token = localStorage.getItem('token');
  17. if (token && config.headers) {
  18. config.headers.Authorization = `Bearer ${token}`;
  19. }
  20. // 添加租户信息(如果有)
  21. const tenant = localStorage.getItem('tenant');
  22. if (tenant && config.headers) {
  23. config.headers['X-Tenant-Id'] = tenant;
  24. }
  25. return config;
  26. },
  27. (error: AxiosError) => {
  28. return Promise.reject(error);
  29. }
  30. );
  31. // 响应拦截器
  32. axiosInstance.interceptors.response.use(
  33. (response: AxiosResponse) => {
  34. const { data } = response;
  35. // 如果后端返回的数据格式是 { code, data, message }
  36. if (data.code !== undefined) {
  37. if (data.code === 200 || data.code === 0) {
  38. return data.data !== undefined ? data.data : data;
  39. } else {
  40. // 业务错误
  41. const errorMessage = data.message || '请求失败';
  42. toast.error(errorMessage);
  43. return Promise.reject(new Error(errorMessage));
  44. }
  45. }
  46. // 直接返回数据
  47. return data;
  48. },
  49. (error: AxiosError) => {
  50. // 处理 HTTP 错误
  51. if (error.response) {
  52. const { status, data } = error.response;
  53. let errorMessage = '请求失败';
  54. switch (status) {
  55. case 401:
  56. errorMessage = '未授权,请重新登录';
  57. // 清除 token 并跳转到登录页
  58. localStorage.removeItem('token');
  59. localStorage.removeItem('tenant');
  60. window.location.href = '/login';
  61. break;
  62. case 403:
  63. errorMessage = '拒绝访问';
  64. break;
  65. case 404:
  66. errorMessage = '请求的资源不存在';
  67. break;
  68. case 500:
  69. errorMessage = '服务器内部错误';
  70. break;
  71. case 502:
  72. errorMessage = '网关错误';
  73. break;
  74. case 503:
  75. errorMessage = '服务不可用';
  76. break;
  77. default:
  78. errorMessage = (data as any)?.message || `请求失败 (${status})`;
  79. }
  80. toast.error(errorMessage);
  81. return Promise.reject(new Error(errorMessage));
  82. } else if (error.request) {
  83. // 请求已发出但没有收到响应
  84. toast.error('网络错误,请检查网络连接');
  85. return Promise.reject(new Error('网络错误'));
  86. } else {
  87. // 其他错误
  88. toast.error(error.message || '请求失败');
  89. return Promise.reject(error);
  90. }
  91. }
  92. );
  93. export default axiosInstance;