index.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import type { Service } from "../types";
  2. import { isDev, ignoreTokens, config } from "@/config";
  3. import { locale, t } from "@/locale";
  4. import { isNull, isObject, parse, storage } from "../utils";
  5. import { useStore } from "../store";
  6. // 请求参数类型定义
  7. export type RequestOptions = {
  8. url: string; // 请求地址
  9. method?: RequestMethod; // 请求方法
  10. data?: any; // 请求体数据
  11. params?: any; // URL参数
  12. header?: any; // 请求头
  13. timeout?: number; // 超时时间
  14. withCredentials?: boolean; // 是否携带凭证
  15. firstIpv4?: boolean; // 是否优先使用IPv4
  16. enableChunked?: boolean; // 是否启用分块传输
  17. };
  18. // 响应数据类型定义
  19. export type Response = {
  20. code?: number;
  21. message?: string;
  22. data?: any;
  23. };
  24. // 请求队列(用于等待token刷新后继续请求)
  25. let requests: ((token: string) => void)[] = [];
  26. // 标记token是否正在刷新
  27. let isRefreshing = false;
  28. // 判断当前url是否忽略token校验
  29. const isIgnoreToken = (url: string) => {
  30. return ignoreTokens.some((e) => {
  31. const pattern = e.replace(/\*/g, ".*");
  32. return new RegExp(pattern).test(url);
  33. });
  34. };
  35. /**
  36. * 通用请求方法
  37. * @param options 请求参数
  38. * @returns Promise<T>
  39. */
  40. export function request<T = any>(options: RequestOptions): Promise<T> {
  41. let { url, method = "GET", data = {}, header = {}, timeout = 60000 } = options;
  42. const { user } = useStore();
  43. // 开发环境下打印请求信息
  44. if (isDev) {
  45. console.log(`[${method}] ${url}`);
  46. }
  47. // 拼接基础url
  48. if (!url.startsWith("http")) {
  49. url = config.baseUrl + url;
  50. }
  51. // 获取当前token
  52. let Authorization: string | null = user.token;
  53. // 如果是忽略token的接口,则不携带token
  54. if (isIgnoreToken(url)) {
  55. Authorization = null;
  56. }
  57. return new Promise((resolve, reject) => {
  58. // 发起请求的实际函数
  59. const next = () => {
  60. uni.request({
  61. url,
  62. method,
  63. data,
  64. header: {
  65. Authorization,
  66. language: locale.value,
  67. ...(header as UTSJSONObject)
  68. },
  69. timeout,
  70. success(res) {
  71. if (!isObject(res.data as any)) {
  72. resolve(res.data as T);
  73. return;
  74. }
  75. // 解析响应数据
  76. const { code, message, data } = parse<Response>(res.data ?? { code: 0 })!;
  77. // 401 无权限
  78. if (res.statusCode == 401) {
  79. user.logout();
  80. return reject({ message } as Response);
  81. }
  82. // 502 服务异常
  83. if (res.statusCode == 502) {
  84. return reject({
  85. message: t("服务异常")
  86. } as Response);
  87. }
  88. // 404 未找到
  89. if (res.statusCode == 404) {
  90. return reject({
  91. message: `[404] ${url}`
  92. } as Response);
  93. }
  94. // 200 正常响应
  95. if (res.statusCode == 200) {
  96. switch (code) {
  97. case 1000:
  98. resolve(data as T);
  99. break;
  100. default:
  101. reject({ message, code } as Response);
  102. break;
  103. }
  104. } else {
  105. reject({ message: t("服务异常") } as Response);
  106. }
  107. },
  108. // 网络请求失败
  109. fail(err) {
  110. reject({ message: err.errMsg } as Response);
  111. }
  112. });
  113. };
  114. // 非刷新token接口才进行token有效性校验
  115. if (!options.url.includes("/refreshToken")) {
  116. if (!isNull(Authorization)) {
  117. // 判断token是否过期
  118. if (storage.isExpired("token")) {
  119. // 判断refreshToken是否过期
  120. if (storage.isExpired("refreshToken")) {
  121. // 刷新token也过期,直接退出登录
  122. user.logout();
  123. return;
  124. }
  125. // 如果当前没有在刷新token,则发起刷新
  126. if (!isRefreshing) {
  127. isRefreshing = true;
  128. user.refreshToken()
  129. .then((token) => {
  130. // 刷新成功后,执行队列中的请求
  131. requests.forEach((cb) => cb(token));
  132. requests = [];
  133. isRefreshing = false;
  134. })
  135. .catch((err) => {
  136. reject(err);
  137. user.logout();
  138. });
  139. }
  140. // 将当前请求加入队列,等待token刷新后再执行
  141. new Promise((resolve) => {
  142. requests.push((token: string) => {
  143. // 重新设置token
  144. Authorization = token;
  145. next();
  146. resolve(true);
  147. });
  148. });
  149. // 此处return,等待token刷新
  150. return;
  151. }
  152. }
  153. }
  154. // token有效,直接发起请求
  155. next();
  156. });
  157. }
  158. // 服务对象(实际会在其他地方赋值)
  159. export const service = {} as Service;