interceptor.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. import axios from 'axios';
  2. import type { AxiosRequestConfig, AxiosResponse } from 'axios';
  3. import { Message, Modal, Notification } from '@arco-design/web-vue';
  4. import { initSyncTime, fetchTime } from '../utils/syncServerTime';
  5. import { getAuthorization } from '../utils/crypto';
  6. import { DEVICE_ID, PLATFORM } from '../constants/app';
  7. import { objTypeOf } from '../utils/utils';
  8. import ls from '../utils/storage';
  9. import { useUserStore } from '../store';
  10. export interface HttpResponse<T = unknown> {
  11. message: string;
  12. code: number;
  13. data: T;
  14. }
  15. axios.defaults.timeout = 10 * 1000;
  16. let load = null;
  17. // 同一时间有多个请求时,会形成队列。在第一个请求创建loading,在最后一个响应关闭loading
  18. const queue = [];
  19. // 防止鉴权失效之后多次弹窗。
  20. let unauthMsgBoxIsShow = false;
  21. const modifyConfig = (config: AxiosRequestConfig): void => {
  22. const userStore = useUserStore();
  23. if (userStore.accessToken) {
  24. const ids = {
  25. privilegeId: ls.get('privilegeId', ''),
  26. orgId: userStore.orgInfo?.id,
  27. schoolId: userStore.curSchoolInfo?.id,
  28. userId: userStore.id,
  29. };
  30. Object.entries(ids).forEach(([key, val]) => {
  31. if (val === null || val === 'null' || val === '') return;
  32. config.headers[key] = val;
  33. });
  34. // 新版鉴权
  35. const timestamp = fetchTime();
  36. const Authorization = getAuthorization(
  37. {
  38. token: userStore.accessToken,
  39. timestamp,
  40. account: userStore.sessionId,
  41. uri: config.url.split('?')[0],
  42. method: config.method,
  43. },
  44. 'token'
  45. );
  46. config.headers.Authorization = Authorization;
  47. config.headers.time = timestamp;
  48. }
  49. config.headers.deviceId = DEVICE_ID;
  50. config.headers.platform = PLATFORM;
  51. config.headers.domain = window.location.origin;
  52. };
  53. axios.interceptors.request.use(
  54. (config: AxiosRequestConfig) => {
  55. // 显示loading提示
  56. if (!queue.length) {
  57. load = Message.loading({
  58. content: '加载中...',
  59. duration: 0,
  60. });
  61. }
  62. queue.push(1);
  63. modifyConfig(config);
  64. return config;
  65. },
  66. (error) => {
  67. // 关闭loading提示
  68. setTimeout(() => {
  69. queue.shift();
  70. if (!queue.length) load.close();
  71. }, 100);
  72. Message.error({
  73. content: error.message || '请求错误',
  74. duration: 5 * 1000,
  75. });
  76. return Promise.reject(error);
  77. }
  78. );
  79. // add response interceptors
  80. axios.interceptors.response.use(
  81. (response: AxiosResponse<HttpResponse>) => {
  82. initSyncTime(new Date(response.headers.date).getTime());
  83. // 关闭loading提示
  84. setTimeout(() => {
  85. queue.shift();
  86. if (!queue.length) load.close();
  87. }, 100);
  88. if (response.config.responseType === 'blob') return response;
  89. const res = response.data;
  90. if (res.code !== 200) {
  91. Message.error({
  92. content: res.message || '响应数据错误',
  93. duration: 5 * 1000,
  94. });
  95. return Promise.reject(res.data);
  96. }
  97. if (
  98. objTypeOf(res.data) === 'object' &&
  99. Object.prototype.hasOwnProperty.call(res.data, 'success')
  100. ) {
  101. if (res.data.success) return res.data;
  102. Message.error({
  103. content: res.message || '响应数据错误',
  104. duration: 5 * 1000,
  105. });
  106. return Promise.reject(res.data);
  107. }
  108. return res.data;
  109. },
  110. (error) => {
  111. // 关闭loading提示
  112. setTimeout(() => {
  113. queue.shift();
  114. if (!queue.length) load.close();
  115. }, 100);
  116. console.dir(error);
  117. if (!error.response) {
  118. let message = error.message || '无响应错误';
  119. if (message.indexOf('timeout') > -1) {
  120. message = '响应超时';
  121. }
  122. Message.error({
  123. content: message,
  124. duration: 5 * 1000,
  125. });
  126. return Promise.reject(error);
  127. }
  128. const { response } = error;
  129. initSyncTime(new Date(response.headers.date).getTime());
  130. if (objTypeOf(response.data) === 'blob')
  131. return Promise.reject(error.response.data);
  132. const errorData = response.data;
  133. let message = errorData.message || '响应未知错误';
  134. const unauthStatus = [401, 403];
  135. if (!unauthStatus.includes(response.status)) {
  136. Notification.error({ title: '错误提示', content: message });
  137. return Promise.reject(error);
  138. }
  139. if (errorData.code === 401001 && message === '没有权限') {
  140. Notification.error({ title: '错误提示', content: message });
  141. return Promise.reject(error);
  142. }
  143. if (unauthMsgBoxIsShow) return Promise.reject(error);
  144. const exposeMsgs = ['系统授权信息已过期,请联系系统管理员激活!'];
  145. unauthMsgBoxIsShow = true;
  146. message = exposeMsgs.includes(message)
  147. ? message
  148. : '身份验证失效,请重新登录!';
  149. Modal.confirm({
  150. title: '重新登陆?',
  151. content: message,
  152. escToClose: false,
  153. maskClosable: false,
  154. closable: false,
  155. hideCancel: true,
  156. titleAlign: 'start',
  157. onOk() {
  158. unauthMsgBoxIsShow = false;
  159. const userStore = useUserStore();
  160. userStore.logout();
  161. },
  162. });
  163. return Promise.reject(error);
  164. }
  165. );