alova.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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. // 处理动态域名
  79. if (config.meta?.domain) {
  80. method.baseURL = config.meta.domain
  81. console.log('当前域名', method.baseURL)
  82. }
  83. },
  84. responded: {
  85. onSuccess: (response, method) => {
  86. const { config } = method
  87. const { requestType } = config
  88. const {
  89. statusCode,
  90. data: rawData,
  91. errMsg,
  92. } = response as UniNamespace.RequestSuccessCallbackResult
  93. // 处理特殊请求类型(上传/下载)
  94. if (requestType === 'upload' || requestType === 'download') {
  95. return response
  96. }
  97. // 特殊处理:返回原始响应
  98. if (config.meta?.returnResponse) {
  99. return response
  100. }
  101. // 处理 HTTP 状态码错误
  102. if (statusCode !== 200) {
  103. // 直接处理401错误,跳转到登录页面
  104. if (statusCode === 401) {
  105. if (!loginModalShown) {
  106. loginModalShown = true
  107. uni.showModal({
  108. title: '提示',
  109. content: '登录已过期,请重新登录',
  110. confirmText: '去登录',
  111. cancelText: '回首页',
  112. success: ({ confirm, cancel }) => {
  113. loginModalShown = false // 重置标志
  114. if (confirm) {
  115. toLoginPage({ mode: 'reLaunch' })
  116. }
  117. else if (cancel) {
  118. uni.reLaunch({ url: '/pages/home/home' })
  119. }
  120. },
  121. fail: () => {
  122. loginModalShown = false // 重置标志
  123. }
  124. })
  125. useTokenStore().cleanToken()
  126. }
  127. // setTimeout(() => {
  128. // toLoginPage({ mode: 'reLaunch' })
  129. // }, 1500)
  130. throw new Error('登录已过期')
  131. }
  132. const errorMessage = ShowMessage(statusCode) || `HTTP请求错误[${statusCode}]`
  133. console.error('errorMessage===>', errorMessage)
  134. uni.showToast({
  135. title: errorMessage,
  136. icon: 'error',
  137. })
  138. throw new Error(`${errorMessage}:${errMsg}`)
  139. }
  140. // 处理业务逻辑错误
  141. const { code, message, result: data } = rawData as IResponse
  142. // 0和200当做成功都很普遍,这里直接兼容两者,见 ResultEnum
  143. if (code !== ResultEnum.Success0 && code !== ResultEnum.Success200) {
  144. if (config.meta?.toast !== false) {
  145. uni.showToast({
  146. title: message,
  147. icon: 'none',
  148. })
  149. }
  150. throw new Error(`请求错误[${code}]:${message}`)
  151. }
  152. // 处理成功响应,返回业务数据
  153. return data
  154. },
  155. // 添加网络错误处理
  156. onError: (error, method) => {
  157. // 处理网络错误(断网、超时、DNS解析失败等)
  158. console.error('网络错误:', error)
  159. // 避免重复提示
  160. if (error.message?.includes('HTTP请求错误') || error.message?.includes('请求错误[')) {
  161. return
  162. }
  163. let errorMessage = '网络错误,请检查网络连接'
  164. if (error.message?.includes('timeout')) {
  165. errorMessage = '请求超时,请稍后重试'
  166. }
  167. uni.showToast({
  168. title: errorMessage,
  169. icon: 'error',
  170. })
  171. }
  172. },
  173. })
  174. export const http = alovaInstance