api.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  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. // 清除登录缓存
  149. $app.popup
  150. .confirm(`${err.data.msg},点击确定去登录`, "提示", {
  151. showCancel: true,
  152. })
  153. .then((confirmed) => {
  154. isLoginPromptShown = false;
  155. if (confirmed) {
  156. uni.clearStorage();
  157. uni.navigateTo({ url: "/pages/login/login" });
  158. }
  159. });
  160. }
  161. }
  162. } catch (err) {
  163. reject(err);
  164. }
  165. } else {
  166. $app.popup.alert(`服务器响应失败\r\n${err.data.msg}`, "数据请求");
  167. }
  168. /*失败处理,此处为非200状态码引起的错误*/
  169. options.hideLoading();
  170. });
  171. });
  172. };
  173. // 新增:获取accessToken的方法
  174. const getAccessToken = function () {
  175. return uni.getStorageSync($config.keyname.accessToken);
  176. };
  177. // 新增:刷新token的方法
  178. const refreshToken = async function () {
  179. const refreshToken = uni.getStorageSync($config.keyname.refreshToken);
  180. console.log("刷新token", refreshToken);
  181. if (!refreshToken) return false;
  182. try {
  183. const res = await Request({
  184. url: pathJoin($config.url.request, $config.api.refreshToken),
  185. method: "post",
  186. data: { refreshToken },
  187. header: {
  188. "content-type": "application/json",
  189. },
  190. });
  191. if (res.code === "00000") {
  192. uni.setStorageSync($config.keyname.accessToken, res.data.accessToken);
  193. uni.setStorageSync($config.keyname.refreshToken, res.data.refreshToken);
  194. return true;
  195. }
  196. return false;
  197. } catch (e) {
  198. return false;
  199. }
  200. };
  201. /**
  202. * POST请求
  203. * @param {String} url 请求地址
  204. * @param {Object} data 请求数据
  205. * @param {Object} opts 配置参数
  206. */
  207. api.post = function (url, data, opts) {
  208. return api.base("post", url, data, Object.assign({}, opts));
  209. };
  210. /**
  211. * GET请求
  212. * @param {String} url 请求地址
  213. * @param {Object} data 请求数据
  214. * @param {Object} opts 配置参数
  215. */
  216. api.get = function (url, data, opts) {
  217. return api.base("get", url, data, Object.assign({}, opts));
  218. };
  219. /**
  220. * PUT请求
  221. * @param {String} url 请求地址
  222. * @param {Object} data 请求数据
  223. * @param {Object} opts 配置参数
  224. */
  225. api.put = function (url, data, opts) {
  226. return api.base("put", url, data, Object.assign({}, opts));
  227. };
  228. /**
  229. * 获取静态数据
  230. * @param {String} url 静态文件地址
  231. * @param {Object} data 参数数据
  232. * @param {Object} opts 配置参数
  233. */
  234. api.static = function (url, data, opts) {
  235. return new Promise(async (resolve, reject) => {
  236. // 设置选项
  237. var _opts = Object.assign(
  238. {
  239. url: /^http/.test(url) ? url : API_BASE + (url || ""),
  240. data: Object.assign({ cache: Date.now() }, data),
  241. },
  242. opts || {}
  243. );
  244. uni.request({
  245. ..._opts,
  246. success: (res) => {
  247. resolve(res.data);
  248. },
  249. });
  250. });
  251. };
  252. /**
  253. * 文件上传
  254. * @param {String} file 文件临时路径
  255. * @param {Object} opts 上传参数
  256. */
  257. api.upload = function (file, opts) {
  258. return new Promise((resolve, reject) => {
  259. const options = Object.assign(
  260. {
  261. // 请求地址
  262. url: pathJoin($config.url.upload, opts?.url || ""),
  263. // 请求数据
  264. filePath: file,
  265. // 自动显示LOADING
  266. loading: true,
  267. // 启用错误自动拦截
  268. error: true,
  269. // 请求头设定
  270. header: {
  271. token: uni.getStorageSync($config.keyname.userToken),
  272. },
  273. // 设置显示LOADING的方法
  274. showLoading: showLoading,
  275. // 设置关闭LOADING的方法
  276. hideLoading: hideLoading,
  277. },
  278. opts || {}
  279. );
  280. });
  281. };
  282. /**
  283. * 获取登录code
  284. */
  285. api.userCode = function () {
  286. return new Promise((resolve, reject) => {
  287. uni.login({
  288. provider: "weixin",
  289. success: (res) => resolve(res.code),
  290. fail: (err) => reject(err),
  291. });
  292. });
  293. };
  294. /**
  295. * 自动登录
  296. * @param {Object} data 登录数据(非必填)
  297. */
  298. api.login = function (data) {
  299. return new Promise(async (resolve, reject) => {
  300. const code = await api.userCode();
  301. api
  302. .post($config.api.login, { ...(data || {}), code })
  303. .then(async (res) => {
  304. // 保存 accessToken 和 refreshToken
  305. uni.setStorageSync($config.keyname.accessToken, res?.data?.accessToken);
  306. uni.setStorageSync(
  307. $config.keyname.refreshToken,
  308. res?.data?.refreshToken
  309. );
  310. // 保留原有的 userInfo 存储
  311. uni.setStorageSync($config.keyname.userInfo, res?.data?.userInfo);
  312. // 返回 accessToken 保持向后兼容
  313. resolve(res.data.accessToken);
  314. })
  315. .catch((err) => reject(err));
  316. });
  317. };
  318. export default api;