http.ts 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. import axios, { AxiosRequestConfig, AxiosResponse, AxiosError } from 'axios';
  2. import { ElMessage as Message, ElMessageBox as MessageBox, ElLoading as Loading } from 'element-plus';
  3. import tokenInfo from '@/stores/modules/token';
  4. import useUserInfo from "@/stores/modules/user";
  5. import pinia from "@/stores/index";
  6. import ENV_CONFIG from "@/config.json";
  7. // 加载动画的并发管理
  8. const activeRequests = new Set<string>();
  9. let loadingObj = null;
  10. /**
  11. * 关闭加载动画
  12. * 当所有请求完成时,关闭加载动画
  13. */
  14. function loadingClose(requestId: string) {
  15. activeRequests.delete(requestId);
  16. if (activeRequests.size === 0 && loadingObj) {
  17. loadingObj.close();
  18. loadingObj = null
  19. }
  20. }
  21. /**
  22. * 创建一个axios实例,用于发送HTTP请求
  23. * 配置了请求拦截器和响应拦截器,支持加载动画和错误提示
  24. */
  25. const service = axios.create({
  26. timeout: 5000, // 设置请求超时时间
  27. // baseURL: '__API__',
  28. });
  29. console.log('__API__');
  30. // 请求拦截器
  31. service.interceptors.request.use(
  32. (config: AxiosRequestConfig) => {
  33. // 动态设置baseURL
  34. const appConfig = pinia.state.value.config?.appConfig;
  35. let env = appConfig?.env || 'prod'; // 默认环境
  36. if (env) {
  37. // 从ENV_CONFIG获取对应环境的API地址
  38. const apiConfig = ENV_CONFIG[env];
  39. if (apiConfig?.api) {
  40. config.baseURL = apiConfig.api;
  41. }
  42. }
  43. console.log(`使用环境: ${env}, API地址: ${config.baseURL || '__API__'}`);
  44. // 在发送请求之前做些什么,例如添加 token
  45. const tokenInfoStore = tokenInfo();
  46. const token = tokenInfoStore.getToken; // 使用 getToken() 获取 token
  47. if (token) {
  48. config.headers['Authorization'] = `Bearer ${token}`;
  49. }
  50. // 如果配置中启用了加载动画,则显示加载动画
  51. if (config.loading) {
  52. const requestId = `${Date.now()}-${Math.random()}`;
  53. activeRequests.add(requestId);
  54. if (activeRequests.size === 1) {
  55. loadingObj = Loading.service({
  56. lock: true,
  57. text: '请稍候',
  58. background: 'rgba(0, 0, 0, 0)',
  59. });
  60. }
  61. config.requestId = requestId; // 将 requestId 挂载到 config 上
  62. }
  63. return config;
  64. },
  65. (error: AxiosError) => {
  66. // 对请求错误做些什么
  67. return Promise.reject(error);
  68. }
  69. );
  70. // 响应拦截器
  71. service.interceptors.response.use(
  72. (response: AxiosResponse) => {
  73. // 对响应数据做点什么
  74. const res = response.data;
  75. const useUserInfoStore = useUserInfo();
  76. // 如果配置中启用了加载动画,则关闭加载动画
  77. if (response.config?.loading && response.config.requestId) {
  78. loadingClose(response.config.requestId as string);
  79. }
  80. // 如果自定义状态码不为0,则判断为错误
  81. if (res.code !== 0) {
  82. switch (res.code) {
  83. case 401:
  84. Message({
  85. message: '登录状态已失效,请重新登录',
  86. type: 'error',
  87. duration: 3 * 1000,
  88. });
  89. useUserInfoStore.updateLoginShow(true)
  90. break;
  91. default:
  92. if (response.config.showErrorMessage) {
  93. if (res.message?.length > 30 || res.code === 400011) {
  94. let message = '<div style="white-space: pre-line; max-height: 500px; overflow: auto;">' + res.message + '</div>' || 'Error';
  95. MessageBox.alert(message, '提示', {
  96. dangerouslyUseHTMLString: true,
  97. });
  98. } else {
  99. Message({
  100. message: res.message || 'Error',
  101. type: 'error',
  102. duration: 5 * 1000,
  103. });
  104. }
  105. }
  106. break;
  107. }
  108. return Promise.reject(res || 'Error');
  109. } else {
  110. return res;
  111. }
  112. },
  113. (error: AxiosError) => {
  114. // 对响应错误做点什么
  115. let errMessage = '';
  116. // 如果配置中启用了加载动画,则关闭加载动画
  117. if (error.config?.loading && error.config.requestId) {
  118. loadingClose(error.config.requestId as string);
  119. }
  120. const useUserInfoStore = useUserInfo();
  121. try {
  122. if (error.response) {
  123. switch (error.response.status) {
  124. case 400: errMessage = '请求错误(400)'; break;
  125. case 401: errMessage = '登录状态已失效,请重新登录';
  126. useUserInfoStore.updateLoginShow(true)
  127. break;
  128. case 403: errMessage = '拒绝访问(403)'; break;
  129. case 404: errMessage = '请求出错(404)'; break;
  130. case 408: errMessage = '请求超时(408)'; break;
  131. case 500: errMessage = '服务器错误(500)'; break;
  132. case 501: errMessage = '服务未实现(501)'; break;
  133. case 502: errMessage = '网络错误(502)'; break;
  134. case 503: errMessage = '服务不可用(503)'; break;
  135. case 504: errMessage = '网络超时(504)'; break;
  136. case 505: errMessage = 'HTTP版本不受支持(505)'; break;
  137. default: errMessage = '连接出错!';
  138. }
  139. } else {
  140. errMessage = '未知错误';
  141. }
  142. } catch (e) {
  143. console.log(e);
  144. }
  145. // 如果配置中启用了错误提示,则显示错误信息
  146. if (error.config?.showErrorMessage) {
  147. if (error.message && error.message.includes('timeout')) {
  148. Message({
  149. message: '请求超时!',
  150. type: 'error',
  151. duration: 5 * 1000,
  152. });
  153. } else if (errMessage) {
  154. Message({
  155. message: errMessage,
  156. type: 'error',
  157. duration: 5 * 1000,
  158. });
  159. }
  160. }
  161. // 对响应错误做处理
  162. return Promise.reject(error);
  163. }
  164. );
  165. /**
  166. * 发起 GET 请求
  167. *
  168. * @template T 泛型,表示返回数据的类型
  169. * @param {string} url 请求的 URL
  170. * @param {any} [params] 请求参数
  171. * @returns {Promise<T>} 返回一个 Promise,解析为响应数据
  172. */
  173. export function GET<T>(url: string, data?: any,config): Promise<T> {
  174. return service.get(url, {
  175. params: data,
  176. loading: config?.loading ?? false,
  177. showErrorMessage: config?.showErrorMessage ?? true,
  178. });
  179. }
  180. /**
  181. * 发起 POST 请求
  182. *
  183. * @template T 泛型,表示返回数据的类型
  184. * @param {string} url 请求的 URL
  185. * @param {any} [data] 请求体
  186. * @param {any} [config] 请求配置
  187. * @returns {Promise<T>} 返回一个 Promise,解析为响应数据
  188. */
  189. export function POST<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
  190. return service.post(url, data, {
  191. ...config,
  192. loading: config?.loading ?? true,
  193. showErrorMessage: config?.showErrorMessage ?? true,
  194. });
  195. }
  196. // 导出配置好的 axios 实例
  197. export default service;