micro_request.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. /*
  2. * @Date: 2021-11-27 15:27:19
  3. * @LastEditors: wanglj
  4. * @LastEditTime: 2024-03-13 16:34:10
  5. * @FilePath: \labsop_backup\frontend\packages\base\src\utils\micro_request.js
  6. * @Description: file content
  7. */
  8. import { Local, Session } from '@/utils/storage';
  9. import { CACHE_KEY } from '@/constants';
  10. const errorCode = {
  11. '401': '认证失败,无法访问系统资源',
  12. '403': '当前操作没有权限',
  13. '404': '访问资源不存在',
  14. 'default': '系统未知错误,请反馈给管理员'
  15. };
  16. const baseURL = import.meta.env.VITE_API_URL || '';
  17. function request(options) {
  18. // 拼接请求路径
  19. let url = options.url || '';
  20. if (!/^\/?http/.test(url)) {
  21. url = baseURL + url;
  22. }
  23. // 头部处理
  24. let header = Object.assign({}, options.headers, options.header);
  25. header['Content-Type'] = header['Content-Type'] || 'application/json;charset=utf-8';
  26. header['X-Secret'] = import.meta.env.VITE_SECRET;
  27. if (import.meta.env.VITE_TENANT) {
  28. header['Tenant'] = import.meta.env.VITE_TENANT;
  29. }
  30. // 是否需要设置 token
  31. const isToken = header.isToken === false;
  32. const token = Local.get(CACHE_KEY.TOKEN);
  33. if (token && !isToken) {
  34. header['Authorization'] = 'Bearer ' + token;
  35. }
  36. // uni.request 只有在 Content-Type 为 application/json 时才自动对对象执行 JSON.stringify
  37. // 对于 application/rpcx,需要手动序列化,否则会被 toString() 处理成 [object Object]
  38. let requestData = options.data;
  39. if (typeof requestData === 'object' && requestData !== null && header['Content-Type'] === 'application/rpcx') {
  40. requestData = JSON.stringify(requestData);
  41. }
  42. return new Promise((resolve, reject) => {
  43. uni.request({
  44. url: url,
  45. method: (options.method || 'GET').toUpperCase(),
  46. data: requestData,
  47. header: header,
  48. timeout: options.timeout || 60000,
  49. responseType: options.responseType || 'text',
  50. success: (res) => {
  51. // 文件流处理
  52. if (options.responseType === 'arraybuffer' || res.header?.['content-type']?.includes('application/octet-stream')) {
  53. downLoadBlobFile(res, options);
  54. resolve(res.data);
  55. return;
  56. }
  57. processResponse(res, resolve, reject);
  58. },
  59. fail: (err) => {
  60. console.log('err: ', err);
  61. uni.showToast({
  62. title: err.errMsg || '网络环境异常,请稍后重试',
  63. icon: 'none',
  64. duration: 3000
  65. });
  66. reject(err);
  67. }
  68. });
  69. });
  70. }
  71. function processResponse(res, resolve, reject) {
  72. const data = res.data || {};
  73. // 未设置状态码则默认成功状态
  74. const code = data.code || res.statusCode || 200;
  75. // 获取错误信息
  76. const message = errorCode[code] || data.message || data.msg || errorCode['default'];
  77. if (code === 401) {
  78. uni.showModal({
  79. title: '系统提示',
  80. content: '登录状态已过期,您可以继续留在该页面,或者重新登录',
  81. confirmText: '重新登录',
  82. cancelText: '取消',
  83. success: function (modalRes) {
  84. if (modalRes.confirm) {
  85. // 清除缓存/token等
  86. Session.clear();
  87. Local.remove(CACHE_KEY.TOKEN);
  88. // 重新定向到登录页
  89. uni.reLaunch({
  90. url: '/pages/login/index'
  91. });
  92. }
  93. }
  94. });
  95. reject(new Error(message));
  96. } else if (code === 500) {
  97. uni.showToast({
  98. title: message,
  99. icon: 'none',
  100. duration: 2000
  101. });
  102. reject(new Error(message));
  103. } else if (code !== 200) {
  104. uni.showToast({
  105. title: message,
  106. icon: 'none',
  107. duration: 2000
  108. });
  109. reject(new Error(message));
  110. } else {
  111. // 返回真实的业务数据,此处直接返回 data(也可根据业务需要只回 data.data)
  112. resolve(data);
  113. }
  114. }
  115. function downLoadBlobFile(res, options) {
  116. let mimeType = options.mimeType || 'application/vnd.ms-excel';
  117. let fileName = 'data.xlsx';
  118. // 尝试从header中获取文件名
  119. const header = res.header || {};
  120. let contentDisposition = header['content-disposition'] || header['Content-Disposition'];
  121. if (contentDisposition) {
  122. let patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*');
  123. let result = patt.exec(decodeURI(contentDisposition));
  124. if (result && result.length > 1) {
  125. fileName = result[1];
  126. fileName = decodeURI(escape(fileName));
  127. fileName = fileName.replace(/\"/g, '');
  128. }
  129. }
  130. // #ifdef H5
  131. const blob = new Blob([res.data], { type: mimeType });
  132. const aLink = document.createElement('a');
  133. aLink.href = URL.createObjectURL(blob);
  134. aLink.setAttribute('download', fileName);
  135. document.body.appendChild(aLink);
  136. aLink.click();
  137. document.body.removeChild(aLink);
  138. // #endif
  139. // #ifndef H5
  140. // 非H5平台(小程序、APP等)需要使用原生的文件保存及打开API
  141. const fs = uni.getFileSystemManager ? uni.getFileSystemManager() : null;
  142. if (fs && res.data) {
  143. const filePath = `${uni.env.USER_DATA_PATH}/${fileName}`;
  144. fs.writeFile({
  145. filePath: filePath,
  146. data: res.data,
  147. encoding: 'binary',
  148. success: () => {
  149. uni.openDocument({
  150. filePath: filePath,
  151. showMenu: true,
  152. success: function () {
  153. console.log('打开文档成功');
  154. }
  155. });
  156. },
  157. fail: (err) => {
  158. uni.showToast({ title: '保存文件失败', icon: 'none' });
  159. }
  160. });
  161. } else {
  162. uni.showToast({
  163. title: '文件已下载,但当前平台暂不支持直接打开',
  164. icon: 'none',
  165. duration: 3000
  166. });
  167. }
  168. // #endif
  169. }
  170. request.postRequest = function postRequest(basePath, srvName, funcName, data) {
  171. if (data == undefined) {
  172. data = { nullparam: 0 };
  173. }
  174. return request({
  175. url: basePath,
  176. method: 'post',
  177. headers: {
  178. 'Content-Type': 'application/rpcx',
  179. 'X-RPCX-SerializeType': '1',
  180. 'X-RPCX-ServicePath': srvName,
  181. 'X-RPCX-ServiceMethod': funcName,
  182. Srvenv: 'dev',
  183. },
  184. data: data,
  185. });
  186. };
  187. // 发出请求并要求把客户端信息传输给后端(IP和User-Agent)
  188. request.postRequestWithClientInfo = function postRequestWithClientInfo(basePath, srvName, funcName, data) {
  189. if (data == undefined) {
  190. data = { nullparam: 0 };
  191. }
  192. return request({
  193. url: basePath,
  194. method: 'post',
  195. headers: {
  196. 'Content-Type': 'application/rpcx',
  197. 'X-RPCX-SerializeType': '1',
  198. 'X-RPCX-ServicePath': srvName,
  199. 'X-RPCX-Meta': 'need_clint_Info=1',
  200. Srvenv: import.meta.env.VITE_SRV_ENV || 'dev',
  201. },
  202. data: data,
  203. });
  204. };
  205. // Excel文件下载(服务端生成文件流)
  206. request.downloadExcel = function downloadExcel(basePath, srvName, funcName, data) {
  207. if (data == undefined) {
  208. data = { nullparam: 0 };
  209. }
  210. let base_Path = '';
  211. // 使用 import.meta.env 替换 process.env
  212. if (basePath == import.meta.env.VITE_APP_FOSHAN_PATH) {
  213. base_Path = (import.meta.env.VITE_APP_MicroSrvProxy_foshan_API || '') + basePath;
  214. } else if (basePath == import.meta.env.VITE_APP_AdminPath) {
  215. base_Path = (import.meta.env.VITE_APP_MicroSrvProxy_API || '') + basePath;
  216. } else {
  217. base_Path = basePath;
  218. }
  219. return request({
  220. url: base_Path,
  221. method: 'post',
  222. responseType: 'arraybuffer', // uniapp中使用 arraybuffer 替代 blob下载文件
  223. headers: {
  224. 'Content-Type': 'application/rpcx',
  225. 'X-RPCX-SerializeType': '1',
  226. 'X-RPCX-ServicePath': srvName,
  227. 'X-RPCX-ServiceMethod': funcName,
  228. Srvenv: 'dev',
  229. },
  230. data: data,
  231. });
  232. };
  233. export default request;