request.ts 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import axios, { AxiosResponse, InternalAxiosRequestConfig } from 'axios';
  2. import { useUserStore } from '@/store/modules/user';
  3. import { getToken } from '@/utils/auth';
  4. import { tansParams, blobValidate } from '@/utils/ruoyi';
  5. import cache from '@/plugins/cache';
  6. import { HttpStatus } from '@/enums/RespEnum';
  7. import { errorCode } from '@/utils/errorCode';
  8. import { LoadingInstance } from 'element-plus/es/components/loading/src/loading';
  9. import FileSaver from 'file-saver';
  10. import { getLanguage } from '@/lang';
  11. import { encryptBase64, encryptWithAes, generateAesKey, decryptWithAes, decryptBase64 } from '@/utils/crypto';
  12. import { encrypt, decrypt } from '@/utils/jsencrypt';
  13. import { showErrorMsg, showWarningMsg } from '@/utils/notification';
  14. const encryptHeader = 'encrypt-key';
  15. let downloadLoadingInstance: LoadingInstance;
  16. // 是否显示重新登录
  17. export const isRelogin = { show: false };
  18. export const globalHeaders = () => {
  19. return {
  20. Authorization: 'Bearer ' + getToken(),
  21. clientid: import.meta.env.VITE_APP_CLIENT_ID
  22. };
  23. };
  24. axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8';
  25. axios.defaults.headers['clientid'] = import.meta.env.VITE_APP_CLIENT_ID;
  26. // 创建 axios 实例
  27. const service = axios.create({
  28. baseURL: import.meta.env.VITE_APP_BASE_API,
  29. timeout: 50000
  30. });
  31. let hideMsg = false;
  32. // 请求拦截器
  33. service.interceptors.request.use(
  34. (config: InternalAxiosRequestConfig) => {
  35. // 对应国际化资源文件后缀
  36. config.headers['Content-Language'] = getLanguage();
  37. hideMsg = !!config.hideMsg;
  38. const isToken = config.headers?.isToken === false;
  39. // 是否需要防止数据重复提交
  40. // const isRepeatSubmit = config.headers?.repeatSubmit === false;
  41. // 是否需要加密
  42. const isEncrypt = config.headers?.isEncrypt === 'true';
  43. if (getToken() && !isToken) {
  44. config.headers['Authorization'] = 'Bearer ' + getToken(); // 让每个请求携带自定义token 请根据实际情况自行修改
  45. }
  46. // get请求映射params参数
  47. if (config.method === 'get' && config.params) {
  48. let url = config.url + '?' + tansParams(config.params);
  49. url = url.slice(0, -1);
  50. config.params = {};
  51. config.url = url;
  52. }
  53. // if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) {
  54. // const requestObj = {
  55. // url: config.url,
  56. // data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
  57. // time: new Date().getTime()
  58. // };
  59. // const sessionObj = cache.session.getJSON('sessionObj');
  60. // if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
  61. // cache.session.setJSON('sessionObj', requestObj);
  62. // } else {
  63. // const s_url = sessionObj.url; // 请求地址
  64. // const s_data = sessionObj.data; // 请求数据
  65. // const s_time = sessionObj.time; // 请求时间
  66. // const interval = 500; // 间隔时间(ms),小于此时间视为重复提交
  67. // if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) {
  68. // const message = '数据正在处理,请勿重复提交';
  69. // console.warn(`[${s_url}]: ` + message);
  70. // return Promise.reject(new Error(message));
  71. // } else {
  72. // cache.session.setJSON('sessionObj', requestObj);
  73. // }
  74. // }
  75. // }
  76. if (import.meta.env.VITE_APP_ENCRYPT === 'true') {
  77. // 当开启参数加密
  78. if (isEncrypt && (config.method === 'post' || config.method === 'put')) {
  79. // 生成一个 AES 密钥
  80. const aesKey = generateAesKey();
  81. config.headers[encryptHeader] = encrypt(encryptBase64(aesKey));
  82. config.data = typeof config.data === 'object' ? encryptWithAes(JSON.stringify(config.data), aesKey) : encryptWithAes(config.data, aesKey);
  83. }
  84. }
  85. // FormData数据去请求头Content-Type
  86. if (config.data instanceof FormData) {
  87. delete config.headers['Content-Type'];
  88. }
  89. return config;
  90. },
  91. (error: any) => {
  92. return Promise.reject(error);
  93. }
  94. );
  95. // 响应拦截器
  96. service.interceptors.response.use(
  97. (res: AxiosResponse) => {
  98. if (import.meta.env.VITE_APP_ENCRYPT === 'true') {
  99. // 加密后的 AES 秘钥
  100. const keyStr = res.headers[encryptHeader];
  101. // 加密
  102. if (keyStr != null && keyStr != '') {
  103. const data = res.data;
  104. // 请求体 AES 解密
  105. const base64Str = decrypt(keyStr);
  106. // base64 解码 得到请求头的 AES 秘钥
  107. const aesKey = decryptBase64(base64Str.toString());
  108. // aesKey 解码 data
  109. const decryptData = decryptWithAes(data, aesKey);
  110. // 将结果 (得到的是 JSON 字符串) 转为 JSON
  111. res.data = JSON.parse(decryptData);
  112. }
  113. }
  114. // 未设置状态码则默认成功状态
  115. const code = res.data.code || HttpStatus.SUCCESS;
  116. // 获取错误信息
  117. const msg = errorCode[code] || res.data.msg || errorCode['default'];
  118. // 二进制数据则直接返回
  119. if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
  120. return res.data;
  121. }
  122. if (code === 401) {
  123. // prettier-ignore
  124. if (!isRelogin.show) {
  125. isRelogin.show = true;
  126. ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', {
  127. confirmButtonText: '重新登录',
  128. cancelButtonText: '取消',
  129. type: 'warning'
  130. }).then(() => {
  131. isRelogin.show = false;
  132. useUserStore().logout().then(() => {
  133. location.href = import.meta.env.VITE_APP_CONTEXT_PATH + '#' + 'index';
  134. });
  135. }).catch(() => {
  136. isRelogin.show = false;
  137. });
  138. }
  139. return Promise.reject('无效的会话,或者会话已过期,请重新登录。');
  140. } else if (code === HttpStatus.SERVER_ERROR) {
  141. if (!hideMsg) {
  142. showErrorMsg(msg);
  143. return Promise.reject(new Error(msg));
  144. }
  145. } else if (code === HttpStatus.WARN) {
  146. if (!hideMsg) {
  147. showWarningMsg(msg);
  148. return Promise.reject(new Error(msg));
  149. }
  150. } else if (code !== HttpStatus.SUCCESS) {
  151. if (!hideMsg) {
  152. showErrorMsg(msg);
  153. return Promise.reject('error');
  154. }
  155. } else {
  156. return Promise.resolve(res.data);
  157. }
  158. },
  159. (error: any) => {
  160. let { message } = error;
  161. if (message == 'Network Error') {
  162. message = '后端接口连接异常';
  163. } else if (message.includes('timeout')) {
  164. message = '系统接口请求超时';
  165. } else if (message.includes('Request failed with status code')) {
  166. message = '系统接口' + message.substr(message.length - 3) + '异常';
  167. }
  168. showErrorMsg(message, 5 * 1000);
  169. return Promise.reject(error);
  170. }
  171. );
  172. // 通用下载方法
  173. export function download(url: string, params: any, fileName: string) {
  174. downloadLoadingInstance = ElLoading.service({ text: '正在下载数据,请稍候', background: 'rgba(0, 0, 0, 0.7)' });
  175. // prettier-ignore
  176. return service.post(url, params, {
  177. transformRequest: [
  178. (params: any) => {
  179. return tansParams(params);
  180. }
  181. ],
  182. headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  183. responseType: 'blob'
  184. }).then(async (resp: any) => {
  185. const isLogin = blobValidate(resp);
  186. if (isLogin) {
  187. const blob = new Blob([resp]);
  188. FileSaver.saveAs(blob, fileName);
  189. } else {
  190. const resText = await resp.data.text();
  191. const rspObj = JSON.parse(resText);
  192. const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'];
  193. showErrorMsg(errMsg);
  194. }
  195. downloadLoadingInstance.close();
  196. }).catch((r: any) => {
  197. console.error(r);
  198. showErrorMsg('下载文件出现错误,请联系管理员!');
  199. downloadLoadingInstance.close();
  200. });
  201. }
  202. // 通用下载方法
  203. export function download2(url: string, fileName: string) {
  204. downloadLoadingInstance = ElLoading.service({ text: '正在下载数据,请稍候', background: 'rgba(0, 0, 0, 0.7)' });
  205. return service
  206. .get(url, { responseType: 'blob' })
  207. .then(async (resp: any) => {
  208. const isLogin = blobValidate(resp);
  209. if (isLogin) {
  210. const blob = new Blob([resp]);
  211. FileSaver.saveAs(blob, fileName);
  212. } else {
  213. const resText = await resp.data.text();
  214. const rspObj = JSON.parse(resText);
  215. const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'];
  216. showErrorMsg('errMsg');
  217. }
  218. downloadLoadingInstance.close();
  219. })
  220. .catch((r: any) => {
  221. console.error(r);
  222. showErrorMsg('下载文件出现错误,请联系管理员!');
  223. downloadLoadingInstance.close();
  224. });
  225. }
  226. // 导出 axios 实例
  227. export default service;