西藏巴青项目

request.js 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. import { joinApiUrl } from '@/config'
  2. import { getToken } from '@/utils/auth'
  3. import errorCode from '@/utils/errorCode'
  4. import { useUserStore } from '@/store/user'
  5. /** 是否正在处理 401 跳转,避免重复提示 */
  6. export const isRelogin = { show: false }
  7. const SESSION_EXPIRED_MSG = '登录过期,即将登录'
  8. const LOGIN_PAGE_URL = '/pages/login/index'
  9. const LOGIN_REDIRECT_DELAY_MS = 400
  10. /** 与 ruoyi-ui tansParams 一致:null / undefined / 空字符串不参与序列化 */
  11. function isEmptyParam(value) {
  12. return value === null || value === undefined || value === ''
  13. }
  14. /**
  15. * 剔除空值形参(GET 查询用)
  16. */
  17. export function sanitizeParams(params) {
  18. if (params == null || typeof params !== 'object') {
  19. return params
  20. }
  21. if (Array.isArray(params)) {
  22. return params
  23. }
  24. const result = {}
  25. Object.keys(params).forEach((key) => {
  26. const value = params[key]
  27. if (isEmptyParam(value)) {
  28. return
  29. }
  30. if (typeof value === 'object' && !Array.isArray(value)) {
  31. const nested = sanitizeParams(value)
  32. if (nested && Object.keys(nested).length > 0) {
  33. result[key] = nested
  34. }
  35. return
  36. }
  37. result[key] = value
  38. })
  39. return result
  40. }
  41. function appendQuery(url, params) {
  42. const parts = []
  43. const build = (obj, prefix) => {
  44. Object.keys(obj).forEach((key) => {
  45. const value = obj[key]
  46. const name = prefix ? `${prefix}[${key}]` : key
  47. if (value !== null && value !== '' && typeof value !== 'undefined') {
  48. if (typeof value === 'object' && !Array.isArray(value)) {
  49. build(value, name)
  50. } else {
  51. parts.push(`${encodeURIComponent(name)}=${encodeURIComponent(value)}`)
  52. }
  53. }
  54. })
  55. }
  56. build(params, '')
  57. if (!parts.length) {
  58. return url
  59. }
  60. const qs = parts.join('&')
  61. return url + (url.indexOf('?') >= 0 ? '&' : '?') + qs
  62. }
  63. /**
  64. * 封装 uni.request,响应格式与 ruoyi-ui axios 拦截器一致
  65. */
  66. export function request(options = {}) {
  67. const header = { ...(options.header || {}) }
  68. const skipToken = header.isToken === false
  69. delete header.isToken
  70. delete header.repeatSubmit
  71. if (skipToken) {
  72. delete header.Authorization
  73. } else if (getToken()) {
  74. header.Authorization = 'Bearer ' + getToken()
  75. }
  76. const method = (options.method || 'GET').toUpperCase()
  77. let url = (options.url || '').startsWith('http')
  78. ? options.url
  79. : joinApiUrl(options.url)
  80. let requestData = options.data !== undefined ? options.data : options.params
  81. if (method === 'GET' && requestData && typeof requestData === 'object' && !Array.isArray(requestData)) {
  82. const cleaned = sanitizeParams(requestData)
  83. const keys = Object.keys(cleaned || {})
  84. if (keys.length) {
  85. url = appendQuery(url, cleaned)
  86. }
  87. requestData = undefined
  88. }
  89. return new Promise((resolve, reject) => {
  90. uni.request({
  91. url,
  92. method,
  93. data: requestData,
  94. header: {
  95. 'Content-Type': 'application/json;charset=utf-8',
  96. ...header
  97. },
  98. timeout: options.timeout || 10000,
  99. success: (res) => {
  100. const httpStatus = res.statusCode || 200
  101. const data = parseResponseData(res.data)
  102. const bizCode = data.code !== undefined && data.code !== null ? Number(data.code) : null
  103. const code = bizCode !== null && !Number.isNaN(bizCode) ? bizCode : httpStatus >= 200 && httpStatus < 300 ? 200 : httpStatus
  104. const msg = resolveErrorMessage(data, code)
  105. if (code === 401) {
  106. if (skipToken) {
  107. showErrorToast(msg)
  108. reject(new Error(msg))
  109. } else {
  110. handle401SessionExpired()
  111. reject(new Error(SESSION_EXPIRED_MSG))
  112. }
  113. return
  114. }
  115. if (code !== 200) {
  116. if (!options.silent) {
  117. showErrorToast(msg)
  118. }
  119. reject(new Error(msg || 'error'))
  120. return
  121. }
  122. resolve(data)
  123. },
  124. fail: (err) => {
  125. let message = err.errMsg || '网络异常'
  126. if (message.includes('timeout')) {
  127. message = '系统接口请求超时'
  128. } else if (message.includes('fail')) {
  129. message = '后端接口连接异常'
  130. }
  131. showErrorToast(message)
  132. reject(err)
  133. }
  134. })
  135. })
  136. }
  137. function parseResponseData(raw) {
  138. if (raw == null || raw === '') {
  139. return {}
  140. }
  141. if (typeof raw === 'object') {
  142. return raw
  143. }
  144. if (typeof raw === 'string') {
  145. try {
  146. return JSON.parse(raw)
  147. } catch (e) {
  148. return { msg: raw }
  149. }
  150. }
  151. return {}
  152. }
  153. function resolveErrorMessage(data, code) {
  154. const serverMsg = data && data.msg != null ? String(data.msg).trim() : ''
  155. if (serverMsg) {
  156. return serverMsg
  157. }
  158. const key = String(code)
  159. if (errorCode[key]) {
  160. return errorCode[key]
  161. }
  162. return errorCode.default || '请求失败'
  163. }
  164. function showErrorToast(message) {
  165. const title = (message && String(message).trim()) || errorCode.default || '请求失败'
  166. uni.showToast({
  167. title: title.length > 40 ? title.slice(0, 40) + '…' : title,
  168. icon: 'none',
  169. duration: 3000
  170. })
  171. }
  172. function handle401SessionExpired() {
  173. if (isRelogin.show) {
  174. return
  175. }
  176. isRelogin.show = true
  177. uni.showToast({
  178. title: SESSION_EXPIRED_MSG,
  179. icon: 'none',
  180. duration: 2000
  181. })
  182. setTimeout(() => {
  183. const userStore = useUserStore()
  184. userStore.fedLogOut().finally(() => {
  185. isRelogin.show = false
  186. uni.reLaunch({ url: LOGIN_PAGE_URL })
  187. })
  188. }, LOGIN_REDIRECT_DELAY_MS)
  189. }
  190. export default request