request.ts 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. /**
  2. * Axios封装 - HTTP请求
  3. */
  4. import axios from 'axios'
  5. import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios'
  6. import { showToast } from 'vant'
  7. import { storage } from '@/utils/storage'
  8. import { API_URL, TENANT_ID } from '@/config'
  9. import router from '@/router'
  10. import { getImageUrl } from '@/utils/image'
  11. // 创建axios实例
  12. const request: AxiosInstance = axios.create({
  13. baseURL: API_URL,
  14. timeout: 10000,
  15. headers: {
  16. 'Content-Type': 'application/json',
  17. 'tenant-id': TENANT_ID
  18. }
  19. })
  20. // 请求拦截器
  21. request.interceptors.request.use(
  22. (config: InternalAxiosRequestConfig) => {
  23. // 添加token
  24. const token = storage.get('accessToken')
  25. if (token) {
  26. config.headers.Authorization = `Bearer ${token}`
  27. }
  28. // 游客模式: 添加 deviceId header
  29. const guestDeviceId = storage.get('guestDeviceId')
  30. const guestToken = storage.get('guestToken')
  31. if (guestDeviceId) {
  32. config.headers['X-Device-Id'] = guestDeviceId
  33. }
  34. // 游客Token优先(如果已登录游客)
  35. if (guestToken && !token) {
  36. config.headers.Authorization = `Bearer ${guestToken}`
  37. }
  38. return config
  39. },
  40. (error: any) => {
  41. console.error('Request error:', error)
  42. return Promise.reject(error)
  43. }
  44. )
  45. // 响应拦截器
  46. request.interceptors.response.use(
  47. (response: AxiosResponse) => {
  48. const { data } = response
  49. // 处理图片URL
  50. if (data && typeof data === 'object') {
  51. processImageUrls(data)
  52. }
  53. // 未登录
  54. if (data.code === 401) {
  55. showToast('未登录')
  56. storage.remove('accessToken')
  57. router.push('/login')
  58. return Promise.reject(new Error('未登录'))
  59. }
  60. // 微信授权相关
  61. if (data.code === 1004004002) {
  62. // 处理微信授权
  63. const indexUrl = storage.get('index_url')
  64. if (indexUrl && typeof window !== 'undefined') {
  65. window.location.href = indexUrl
  66. }
  67. return Promise.reject(new Error('需要微信授权'))
  68. }
  69. // 请求失败
  70. if (data.code !== 0) {
  71. showToast(data.msg || '请求失败')
  72. return Promise.reject(new Error(data.msg || '请求失败'))
  73. }
  74. // 返回数据
  75. return data.data
  76. },
  77. (error: any) => {
  78. console.error('Response error:', error)
  79. // 开发环境下,如果是演示模式(无后端),不显示错误提示
  80. const isDev = import.meta.env.DEV
  81. const isNetworkError = error.code === 'ERR_NETWORK' || error.code === 'ERR_BAD_RESPONSE'
  82. if (error.response) {
  83. // 服务器返回错误
  84. const { status, data } = error.response
  85. if (status === 401) {
  86. showToast('未登录')
  87. storage.remove('accessToken')
  88. router.push('/login')
  89. } else if (status === 403) {
  90. showToast('没有权限')
  91. } else if (status === 404) {
  92. !isDev && showToast('请求的资源不存在')
  93. } else if (status === 500) {
  94. // 开发环境下的500错误不提示(演示模式)
  95. !isDev && showToast('服务器错误')
  96. } else {
  97. !isDev && showToast(data.msg || '请求失败')
  98. }
  99. } else if (error.request) {
  100. // 请求已发出但没有收到响应(开发环境下不提示)
  101. if (!isDev || !isNetworkError) {
  102. showToast('网络错误,请检查网络连接')
  103. }
  104. } else {
  105. // 其他错误
  106. !isDev && showToast(error.message || '请求失败')
  107. }
  108. return Promise.reject(error)
  109. }
  110. )
  111. /**
  112. * 递归处理对象中的图片URL
  113. */
  114. function processImageUrls(obj: any) {
  115. if (!obj || typeof obj !== 'object') return
  116. // 处理数组
  117. if (Array.isArray(obj)) {
  118. obj.forEach((item) => processImageUrls(item))
  119. return
  120. }
  121. // 处理对象
  122. for (const key in obj) {
  123. if (Object.prototype.hasOwnProperty.call(obj, key)) {
  124. // 判断是否为图片字段
  125. if (
  126. typeof obj[key] === 'string' &&
  127. (key.includes('image') ||
  128. key.includes('img') ||
  129. key.includes('pic') ||
  130. key.includes('avatar') ||
  131. key.includes('cover') ||
  132. key.includes('thumb'))
  133. ) {
  134. // 处理图片URL
  135. obj[key] = getImageUrl(obj[key])
  136. }
  137. // 递归处理嵌套对象
  138. else if (obj[key] && typeof obj[key] === 'object') {
  139. processImageUrls(obj[key])
  140. }
  141. }
  142. }
  143. }
  144. export default request