api.js 9.5 KB

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