alova.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import type { uniappRequestAdapter } from '@alova/adapter-uniapp'
  2. import type { IResponse } from './types'
  3. import AdapterUniapp from '@alova/adapter-uniapp'
  4. import { createAlova } from 'alova'
  5. import { createServerTokenAuthentication } from 'alova/client'
  6. import VueHook from 'alova/vue'
  7. import { useTokenStore } from '@/store/token'
  8. import { toLoginPage } from '@/utils/toLoginPage'
  9. import { ContentTypeEnum, ResultEnum, ShowMessage } from './tools/enum'
  10. // 配置动态Tag
  11. export const API_DOMAINS = {
  12. DEFAULT: import.meta.env.VITE_SERVER_BASEURL,
  13. SECONDARY: import.meta.env.VITE_SERVER_BASEURL_SECONDARY,
  14. }
  15. // 添加全局标志,防止重复弹窗
  16. let loginModalShown = false
  17. /**
  18. * 创建请求实例
  19. */
  20. const { onAuthRequired, onResponseRefreshToken } = createServerTokenAuthentication<
  21. typeof VueHook,
  22. typeof uniappRequestAdapter
  23. >({
  24. // 如果下面拦截不到,请使用 refreshTokenOnSuccess by 群友@琛
  25. refreshTokenOnError: {
  26. isExpired: (error) => {
  27. console.log('error', error)
  28. return error.response?.status === ResultEnum.Unauthorized
  29. },
  30. handler: async () => {
  31. try {
  32. // await authLogin();
  33. console.log('拦截token失效')
  34. }
  35. catch (error) {
  36. // 切换到登录页
  37. // toLoginPage({ mode: 'reLaunch' })
  38. console.log('刷新token失败', error)
  39. throw error
  40. }
  41. },
  42. },
  43. })
  44. /**
  45. * alova 请求实例
  46. */
  47. const alovaInstance = createAlova({
  48. baseURL: API_DOMAINS.DEFAULT,
  49. ...AdapterUniapp(),
  50. // timeout: 7000,
  51. statesHook: VueHook,
  52. cacheFor: null,
  53. beforeRequest: (method) => {
  54. // 设置默认 Content-Type
  55. method.config.headers = {
  56. ContentType: method.type === 'POST' ? ContentTypeEnum.FORM_URLENCODED : ContentTypeEnum.JSON,
  57. // // #ifndef MP-WEIXIN
  58. // responseType: 'json',
  59. // // #endif
  60. Accept: 'application/json, text/plain, */*',
  61. ...method.config.headers,
  62. }
  63. console.log('method===>', method)
  64. const { config } = method
  65. const ignoreAuth = !config.meta?.ignoreAuth
  66. // 处理认证信息 自行处理认证问题
  67. if (ignoreAuth) {
  68. const tokenStore = useTokenStore()
  69. const token = tokenStore.validToken
  70. // const token = 'getToken()'
  71. if (!token) {
  72. throw new Error('[请求错误]:未登录')
  73. }
  74. method.config.headers['X-Access-Token'] = `${token}`
  75. method.config.headers.AppType = '7'
  76. // method.config.headers.token = token;
  77. }
  78. const isShowLoading = !!config.meta?.showLoading
  79. if (isShowLoading) {
  80. const loadingText = config.meta?.loadingText || ''
  81. const loadingMask = !!config.meta?.loadingMask
  82. uni.showLoading({
  83. title: loadingText,
  84. mask: loadingMask,
  85. })
  86. }
  87. // 处理动态域名
  88. if (config.meta?.domain) {
  89. method.baseURL = config.meta.domain
  90. console.log('当前域名', method.baseURL)
  91. }
  92. },
  93. responded: {
  94. onSuccess: (response, method) => {
  95. const { config } = method
  96. const { requestType } = config
  97. const {
  98. statusCode,
  99. data: rawData,
  100. errMsg,
  101. } = response as UniNamespace.RequestSuccessCallbackResult
  102. // 关闭loading
  103. const isShowLoading = !!config.meta?.showLoading
  104. if (isShowLoading) {
  105. uni.hideLoading()
  106. }
  107. // 处理特殊请求类型(上传/下载)
  108. if (requestType === 'upload' || requestType === 'download') {
  109. return response
  110. }
  111. // 特殊处理:返回原始响应
  112. if (config.meta?.returnResponse) {
  113. return response
  114. }
  115. // 处理 HTTP 状态码错误
  116. if (statusCode !== 200) {
  117. // 直接处理401错误,跳转到登录页面
  118. if (statusCode === 401) {
  119. if (!loginModalShown) {
  120. loginModalShown = true
  121. uni.showModal({
  122. title: '提示',
  123. content: '登录已过期,请重新登录',
  124. confirmText: '去登录',
  125. cancelText: '回首页',
  126. success: ({ confirm, cancel }) => {
  127. loginModalShown = false // 重置标志
  128. if (confirm) {
  129. toLoginPage({ mode: 'reLaunch' })
  130. }
  131. else if (cancel) {
  132. uni.reLaunch({ url: '/pages/home/home' })
  133. }
  134. },
  135. fail: () => {
  136. loginModalShown = false // 重置标志
  137. }
  138. })
  139. useTokenStore().cleanToken()
  140. }
  141. // setTimeout(() => {
  142. // toLoginPage({ mode: 'reLaunch' })
  143. // }, 1500)
  144. throw new Error('登录已过期')
  145. }
  146. const errorMessage = ShowMessage(statusCode) || `HTTP请求错误[${statusCode}]`
  147. console.error('errorMessage===>', errorMessage)
  148. uni.showToast({
  149. title: errorMessage,
  150. icon: 'error',
  151. })
  152. throw new Error(`${errorMessage}:${errMsg}`)
  153. }
  154. // 处理业务逻辑错误
  155. const { code, message, result: data } = rawData as IResponse
  156. // 0和200当做成功都很普遍,这里直接兼容两者,见 ResultEnum
  157. if (code !== ResultEnum.Success0 && code !== ResultEnum.Success200) {
  158. if (config.meta?.toast !== false) {
  159. uni.showToast({
  160. title: message,
  161. icon: 'none',
  162. })
  163. }
  164. throw new Error(`请求错误[${code}]:${message}`)
  165. }
  166. // 处理成功响应,返回业务数据
  167. return data
  168. },
  169. // 添加网络错误处理
  170. onError: (error, method) => {
  171. const { config } = method
  172. // 处理网络错误(断网、超时、DNS解析失败等)
  173. console.error('网络错误:', error)
  174. // 关闭loading
  175. const isShowLoading = !!config.meta?.showLoading
  176. if (isShowLoading) {
  177. uni.hideLoading()
  178. }
  179. // 避免重复提示
  180. if (error.message?.includes('HTTP请求错误') || error.message?.includes('请求错误[')) {
  181. return
  182. }
  183. let errorMessage = '网络正在迷路中'
  184. if (error.message?.includes('timeout')) {
  185. errorMessage = '服务器好像睡着了'
  186. }
  187. uni.showToast({
  188. title: errorMessage,
  189. icon: 'error',
  190. })
  191. throw new Error(errorMessage)
  192. }
  193. },
  194. })
  195. export const http = alovaInstance