index.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. import { isDev, ignoreTokens, config } from "@/config";
  2. import { locale, t } from "../locale";
  3. import { isNull, isObject, parse, storage } from "../utils";
  4. import { useStore } from "../store";
  5. import { useUi } from "@/uni_modules/cool-ui";
  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. const ui = useUi();
  25. // 请求队列(用于等待token刷新后继续请求)
  26. let requests: ((token: string) => void)[] = [];
  27. // 标记token是否正在刷新
  28. let isRefreshing = false;
  29. // 判断当前url是否忽略token校验
  30. const isIgnoreToken = (url: string) => {
  31. return ignoreTokens.some((e) => {
  32. const pattern = e.replace(/\*/g, ".*");
  33. return new RegExp(pattern).test(url);
  34. });
  35. };
  36. /**
  37. * 通用请求方法
  38. * @param options 请求参数
  39. * @returns Promise<T>
  40. */
  41. export function request(options: RequestOptions): Promise<any | null> {
  42. let { url, method = "GET", data = {}, header = {}, timeout = 60000 } = options;
  43. const { user } = useStore();
  44. // 拼接基础url
  45. if (!url.startsWith("http")) {
  46. url = config.baseUrl + url;
  47. }
  48. // 开发环境下打印请求信息
  49. if (isDev) {
  50. console.log(`[${method}] ${url}`);
  51. }
  52. // 获取当前token
  53. let Authorization: string | null = user.token;
  54. // 如果是忽略token的接口,则不携带token
  55. if (isIgnoreToken(url)) {
  56. Authorization = null;
  57. }
  58. return new Promise((resolve, reject) => {
  59. let isRetry = false;
  60. // 发起请求的实际函数
  61. function next() {
  62. uni.request({
  63. url,
  64. method,
  65. data,
  66. header: {
  67. Authorization,
  68. language: locale.value,
  69. ...(header as UTSJSONObject)
  70. },
  71. timeout,
  72. success(res) {
  73. if (isNull(res.data)) {
  74. res.data = {};
  75. }
  76. const { code, message, data } = parse<Response>(
  77. (res.data as Response)
  78. )!
  79. // 401 无权限
  80. if (res.statusCode == 401) {
  81. // 不对token接口本身做刷新,防止死循环
  82. if (!isRetry && url.indexOf('/oauth/token') === -1) {
  83. isRetry = true;
  84. user.refreshToken()
  85. .then(() => {
  86. Authorization = user.token;
  87. next();
  88. })
  89. .catch(() => {
  90. user.logout();
  91. ui.showToast({
  92. message: "无权限",
  93. type: "warn",
  94. })
  95. reject({ message: t("无权限") } as Response);
  96. });
  97. return;
  98. }
  99. // 刷新后仍然401,强制登出
  100. user.logout();
  101. ui.showToast({
  102. message: "无权限",
  103. type: "warn",
  104. })
  105. reject({ message: t("无权限") } as Response);
  106. }
  107. // 502 服务异常
  108. else if (res.statusCode == 502 || res.statusCode == 405) {
  109. ui.showToast({
  110. message: message || "服务异常",
  111. type: "warn",
  112. })
  113. reject({
  114. message: message || t("服务异常")
  115. } as Response);
  116. }
  117. // 404 未找到
  118. else if (res.statusCode == 404) {
  119. ui.showToast({
  120. message: `[404] ${url}`,
  121. type: "warn",
  122. })
  123. return reject({
  124. message: `[404] ${url}`
  125. } as Response);
  126. }
  127. // 200 正常响应
  128. else if (res.statusCode == 200) {
  129. if (res.data == null) {
  130. resolve(null);
  131. } else if (!isObject(res.data as any)) {
  132. resolve(res.data);
  133. } else {
  134. // 解析响应数据
  135. if (code == 200 || code == 0) {
  136. resolve(data);
  137. } else if (code == 1) {
  138. reject(res.data);
  139. }
  140. else {
  141. ui.showToast({
  142. message: message ?? t("服务异常"),
  143. type: "warn",
  144. })
  145. reject({ message: message } as Response);
  146. }
  147. }
  148. } else {
  149. ui.showToast({
  150. message: message || "服务异常",
  151. type: "warn",
  152. })
  153. reject({ message: message || "服务异常" } as Response);
  154. }
  155. },
  156. // 网络请求失败
  157. fail(err) {
  158. reject({ message: err.errMsg } as Response);
  159. }
  160. });
  161. };
  162. next();
  163. });
  164. }
  165. export function useGet(url: string, data?: any, config?: RequestOptions): Promise<any> {
  166. const options: RequestOptions = {
  167. url,
  168. data,
  169. method: 'GET',
  170. ...config,
  171. }
  172. return request(options)
  173. }
  174. export function usePost(url: string, data?: any, config?: RequestOptions): Promise<any> {
  175. const options: RequestOptions = {
  176. url,
  177. data,
  178. method: 'POST',
  179. ...config,
  180. }
  181. return request(options)
  182. }
  183. export function usePut(url: string, data?: any, config?: RequestOptions): Promise<any> {
  184. const options: RequestOptions = {
  185. url,
  186. data,
  187. method: 'PUT',
  188. ...config,
  189. }
  190. return request(options)
  191. }
  192. export function useDelete(url: string, data?: any, config?: RequestOptions): Promise<any> {
  193. const options: RequestOptions = {
  194. url,
  195. data,
  196. method: 'DELETE',
  197. ...config,
  198. }
  199. return request(options)
  200. }