request.js 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import { API_BASE, TOKEN_KEY, REFRESH_TOKEN_KEY, USER_INFO_KEY } from '@/config/api'
  2. let clearingAuth = false
  3. function createUnauthorizedError(rawUrl = '') {
  4. const loginApiPattern = /\/mini\/auth\/login(?:$|\?)/
  5. const message = loginApiPattern.test(String(rawUrl))
  6. ? '用户名或密码错误,请重新输入'
  7. : '登录状态已失效,请重新登录'
  8. const error = new Error(message)
  9. error.name = 'UnauthorizedError'
  10. return error
  11. }
  12. async function onHttpUnauthorized() {
  13. if (clearingAuth) return
  14. clearingAuth = true
  15. // 先同步清理本地登录态,避免 store 动态导入失败时仍被识别为已登录
  16. uni.removeStorageSync(TOKEN_KEY)
  17. uni.removeStorageSync(REFRESH_TOKEN_KEY)
  18. uni.removeStorageSync(USER_INFO_KEY)
  19. try {
  20. const { useUserStore } = await import('@/stores/user')
  21. useUserStore().clearLoginInfo()
  22. } catch (error) {
  23. // ignore
  24. }
  25. const pages = typeof getCurrentPages === 'function' ? getCurrentPages() : []
  26. const currentRoute = pages.length ? pages[pages.length - 1].route : ''
  27. if (currentRoute === 'pages/login/login') {
  28. clearingAuth = false
  29. return
  30. }
  31. uni.showToast({ title: '登录已失效,请重新登录', icon: 'none' })
  32. uni.reLaunch({
  33. url: '/pages/login/login',
  34. fail: () => {
  35. // 某些端在请求回调中 reLaunch 可能失败,使用 redirectTo 兜底
  36. uni.redirectTo({ url: '/pages/login/login' })
  37. },
  38. complete: () => {
  39. setTimeout(() => {
  40. clearingAuth = false
  41. }, 800)
  42. }
  43. })
  44. }
  45. function isBizUnauthorized(data) {
  46. if (!data || typeof data !== 'object') return false
  47. return data.code === 401
  48. }
  49. export function request(options) {
  50. if (clearingAuth) {
  51. return Promise.reject(createUnauthorizedError(options?.url))
  52. }
  53. const rawToken = uni.getStorageSync(TOKEN_KEY)
  54. const token = typeof rawToken === 'string' ? rawToken.trim() : ''
  55. const rawUrl = options.url || ''
  56. const url = rawUrl.startsWith('http')
  57. ? rawUrl
  58. : `${API_BASE}${rawUrl.startsWith('/') ? rawUrl : `/${rawUrl}`}`
  59. return new Promise((resolve, reject) => {
  60. uni.request({
  61. ...options,
  62. url,
  63. header: {
  64. 'Content-Type': 'application/json',
  65. ...(token ? { Authorization: `Bearer ${token}` } : {}),
  66. ...(options.header || {})
  67. },
  68. success: (res) => {
  69. if (res.statusCode === 401 || isBizUnauthorized(res.data)) {
  70. onHttpUnauthorized()
  71. reject(createUnauthorizedError(rawUrl))
  72. return
  73. }
  74. resolve(res)
  75. },
  76. fail: (err) => reject(err)
  77. })
  78. })
  79. }
  80. export function parseResponseJson(res) {
  81. const d = res.data
  82. if (d == null) return d
  83. if (typeof d === 'string') {
  84. try {
  85. return JSON.parse(d)
  86. } catch (error) {
  87. return d
  88. }
  89. }
  90. return d
  91. }