api.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. import $app from "./app";
  2. import $config from "./config";
  3. const api = {};
  4. // 标志位:控制登录提示弹窗只显示一次
  5. let isLoginPromptShown = false;
  6. // 网络请求封装
  7. const Request = function (opts) {
  8. const originalComplete = opts.complete;
  9. return new Promise((resolve, reject) => {
  10. uni.request(
  11. Object.assign({}, opts, {
  12. complete: (res) => {
  13. const hasSuccess = Boolean(
  14. res.errMsg == "request:ok" &&
  15. res.statusCode >= 200 &&
  16. res.statusCode < 300
  17. );
  18. hasSuccess ? resolve(res.data) : reject(res);
  19. if (originalComplete && typeof originalComplete == "function")
  20. originalComplete(res);
  21. },
  22. })
  23. );
  24. });
  25. };
  26. // 路径拼接
  27. const pathJoin = function (...args) {
  28. return args.join("/").replace(/(?<!:)(\/{2,})/g, "/");
  29. };
  30. // 显示LOADING
  31. const showLoading = function () {
  32. uni.showLoading({ title: "", mask: true });
  33. };
  34. // 关闭LOADING
  35. const hideLoading = function () {
  36. uni.hideLoading();
  37. };
  38. // 获取用户TOKEN
  39. const getUserToken = function () {
  40. return uni.getStorageSync($config.keyname.userToken);
  41. };
  42. /**
  43. * 基础请求
  44. * @param {String} method 请求类型
  45. * @param {String} url 请求地址
  46. * @param {Object} data 请求数据
  47. * @param {Object} opts 配置参数
  48. */
  49. api.base = function (method, url, data, opts) {
  50. return new Promise(async (resolve, reject) => {
  51. const options = Object.assign(
  52. {
  53. // 请求地址
  54. url: pathJoin($config.url.request, url),
  55. // 请求数据
  56. data: data || {},
  57. // 请求类型
  58. method: method || "post",
  59. // 数据类型
  60. dataType: "json",
  61. // 自动显示LOADING
  62. loading: true,
  63. // 启用错误自动拦截
  64. error: true,
  65. // 请求头设定
  66. header: {
  67. "content-type": "application/json",
  68. Authorization: "Bearer " + getAccessToken(), // 修改这里,使用新的获取token方法
  69. },
  70. // 设置显示LOADING的方法
  71. showLoading: showLoading,
  72. // 设置关闭LOADING的方法
  73. hideLoading: hideLoading,
  74. },
  75. opts || {}
  76. );
  77. if (options.loading && typeof options.showLoading == "function") {
  78. options.showLoading();
  79. }
  80. Request(options)
  81. .then(async (res) => {
  82. if (options.loading && typeof options.hideLoading == "function") {
  83. options.hideLoading();
  84. }
  85. switch (res.code) {
  86. // 请求正常
  87. case "00000":
  88. resolve(res);
  89. break;
  90. // 约定401为未登录状态
  91. case "A0230":
  92. try {
  93. // 尝试刷新token
  94. console.log("进入刷新请求");
  95. const refreshResult = await refreshToken();
  96. if (refreshResult) {
  97. // 刷新成功,更新header中的token并重新请求
  98. options.header.token = getAccessToken();
  99. try {
  100. const db = await Request(options);
  101. resolve(db);
  102. } catch (err) {
  103. reject(err);
  104. }
  105. } else {
  106. // 刷新失败,执行登录
  107. // await api.login();
  108. options.header.token = getAccessToken();
  109. try {
  110. const db = await Request(options);
  111. resolve(db);
  112. } catch (err) {
  113. reject(err);
  114. }
  115. }
  116. } catch (err) {
  117. reject(err);
  118. }
  119. break;
  120. // 其他错误请求
  121. default:
  122. if (options.error) {
  123. $app.popup.alert(res.msg || `${url}\r\n未知错误`, "数据请求");
  124. } else {
  125. reject(res);
  126. }
  127. break;
  128. }
  129. })
  130. .catch(async (err) => {
  131. if (err.data.code == "A0230") {
  132. try {
  133. // 尝试刷新token
  134. const refreshResult = await refreshToken();
  135. if (refreshResult) {
  136. // 刷新成功,更新header中的token并重新请求
  137. options.header.token = getAccessToken();
  138. try {
  139. const db = await Request(options);
  140. resolve(db);
  141. } catch (err) {
  142. reject(err);
  143. }
  144. } else {
  145. // 刷新失败,执行登录
  146. if (!isLoginPromptShown) {
  147. isLoginPromptShown = true;
  148. $app.popup
  149. .confirm(
  150. `服务器响应失败\r\n${err.data.msg},点击确定去登录`,
  151. "提示",
  152. {
  153. showCancel: true,
  154. }
  155. )
  156. .then((confirmed) => {
  157. isLoginPromptShown = false;
  158. if (confirmed) {
  159. uni.navigateTo({ url: "/pages/login/login" });
  160. }
  161. });
  162. }
  163. }
  164. } catch (err) {
  165. reject(err);
  166. }
  167. } else {
  168. $app.popup.alert(`服务器响应失败\r\n${err.data.msg}`, "数据请求");
  169. }
  170. /*失败处理,此处为非200状态码引起的错误*/
  171. options.hideLoading();
  172. });
  173. });
  174. };
  175. // 新增:获取accessToken的方法
  176. const getAccessToken = function () {
  177. return uni.getStorageSync($config.keyname.accessToken);
  178. };
  179. // 新增:刷新token的方法
  180. const refreshToken = async function () {
  181. const refreshToken = uni.getStorageSync($config.keyname.refreshToken);
  182. console.log("刷新token", refreshToken);
  183. if (!refreshToken) return false;
  184. try {
  185. const res = await Request({
  186. url: pathJoin($config.url.request, $config.api.refreshToken),
  187. method: "post",
  188. data: { refreshToken },
  189. header: {
  190. "content-type": "application/json",
  191. },
  192. });
  193. if (res.code === "00000") {
  194. uni.setStorageSync($config.keyname.accessToken, res.data.accessToken);
  195. uni.setStorageSync($config.keyname.refreshToken, res.data.refreshToken);
  196. return true;
  197. }
  198. return false;
  199. } catch (e) {
  200. return false;
  201. }
  202. };
  203. /**
  204. * POST请求
  205. * @param {String} url 请求地址
  206. * @param {Object} data 请求数据
  207. * @param {Object} opts 配置参数
  208. */
  209. api.post = function (url, data, opts) {
  210. return api.base("post", url, data, Object.assign({}, opts));
  211. };
  212. /**
  213. * GET请求
  214. * @param {String} url 请求地址
  215. * @param {Object} data 请求数据
  216. * @param {Object} opts 配置参数
  217. */
  218. api.get = function (url, data, opts) {
  219. return api.base("get", url, data, Object.assign({}, opts));
  220. };
  221. /**
  222. * PUT请求
  223. * @param {String} url 请求地址
  224. * @param {Object} data 请求数据
  225. * @param {Object} opts 配置参数
  226. */
  227. api.put = function (url, data, opts) {
  228. return api.base("put", url, data, Object.assign({}, opts));
  229. };
  230. /**
  231. * 获取静态数据
  232. * @param {String} url 静态文件地址
  233. * @param {Object} data 参数数据
  234. * @param {Object} opts 配置参数
  235. */
  236. api.static = function (url, data, opts) {
  237. return new Promise(async (resolve, reject) => {
  238. // 设置选项
  239. var _opts = Object.assign(
  240. {
  241. url: /^http/.test(url) ? url : API_BASE + (url || ""),
  242. data: Object.assign({ cache: Date.now() }, data),
  243. },
  244. opts || {}
  245. );
  246. uni.request({
  247. ..._opts,
  248. success: (res) => {
  249. resolve(res.data);
  250. },
  251. });
  252. });
  253. };
  254. /**
  255. * 文件上传
  256. * @param {String} file 文件临时路径
  257. * @param {Object} opts 上传参数
  258. */
  259. api.upload = function (file, opts) {
  260. return new Promise((resolve, reject) => {
  261. const options = Object.assign(
  262. {
  263. // 请求地址
  264. url: pathJoin($config.url.upload, opts?.url || ""),
  265. // 请求数据
  266. filePath: file,
  267. // 自动显示LOADING
  268. loading: true,
  269. // 启用错误自动拦截
  270. error: true,
  271. // 请求头设定
  272. header: {
  273. token: uni.getStorageSync($config.keyname.userToken),
  274. },
  275. // 设置显示LOADING的方法
  276. showLoading: showLoading,
  277. // 设置关闭LOADING的方法
  278. hideLoading: hideLoading,
  279. },
  280. opts || {}
  281. );
  282. });
  283. };
  284. /**
  285. * 获取登录code
  286. */
  287. api.userCode = function () {
  288. return new Promise((resolve, reject) => {
  289. uni.login({
  290. provider: "weixin",
  291. success: (res) => resolve(res.code),
  292. fail: (err) => reject(err),
  293. });
  294. });
  295. };
  296. /**
  297. * 自动登录
  298. * @param {Object} data 登录数据(非必填)
  299. */
  300. api.login = function (data) {
  301. return new Promise(async (resolve, reject) => {
  302. const code = await api.userCode();
  303. api
  304. .post($config.api.login, { ...(data || {}), code })
  305. .then(async (res) => {
  306. // 保存 accessToken 和 refreshToken
  307. uni.setStorageSync($config.keyname.accessToken, res?.data?.accessToken);
  308. uni.setStorageSync(
  309. $config.keyname.refreshToken,
  310. res?.data?.refreshToken
  311. );
  312. // 保留原有的 userInfo 存储
  313. uni.setStorageSync($config.keyname.userInfo, res?.data?.userInfo);
  314. // 返回 accessToken 保持向后兼容
  315. resolve(res.data.accessToken);
  316. })
  317. .catch((err) => reject(err));
  318. });
  319. };
  320. export default api;