request.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import Vue from 'vue'
  2. import axios from 'axios'
  3. import { MessageBox } from 'element-ui'
  4. import {
  5. baseURL,
  6. contentType,
  7. debounce,
  8. invalidCode,
  9. noPermissionCode,
  10. requestTimeout,
  11. successCode,
  12. recordRoute,
  13. loginInterception,
  14. } from '@/config'
  15. import router from '@/router'
  16. import store from '@/store'
  17. import qs from 'qs'
  18. import { isArray } from '@/utils/validate'
  19. let loadingInstance
  20. /**
  21. * 处理code异常
  22. * @param {*} code
  23. * @param {*} msg
  24. */
  25. const handleCode = (code, msg) => {
  26. switch (code) {
  27. case invalidCode:
  28. MessageBox.alert(
  29. `<div style="max-height:60vh;overflow:auto">${
  30. msg || `后端接口${code}异常`
  31. }</div>`,
  32. '错误',
  33. {
  34. type: 'error',
  35. dangerouslyUseHTMLString: true,
  36. showClose: true,
  37. }
  38. )
  39. store.dispatch('user/resetAccessToken')
  40. if (loginInterception) {
  41. try {
  42. const fullPath = router.currentRoute.fullPath
  43. if (recordRoute && fullPath) {
  44. router.push(`/login?redirect=${fullPath}`)
  45. } else {
  46. router.push('/login')
  47. }
  48. } catch (e) {
  49. // 兜底:直接跳转登录页
  50. window.location.href = '/login'
  51. }
  52. }
  53. break
  54. case noPermissionCode:
  55. store
  56. .dispatch('user/logout')
  57. .then(() => {
  58. if (recordRoute) {
  59. const fullPath = router.currentRoute.fullPath
  60. router.push(`/login?redirect=${fullPath}`)
  61. } else {
  62. router.push('/login')
  63. }
  64. })
  65. .catch(() => {
  66. // logout时可能因为token过期报401错误,此时用href跳转到login页面
  67. window.location.href = '/login'
  68. })
  69. break
  70. default:
  71. if (Number(code) === 500) {
  72. // 500 保持原来的轻提示样式
  73. Vue.prototype.$baseMessage(msg || `后端接口${code}异常`, 'error')
  74. } else if (Number(code) === 250) {
  75. // 250 使用现在的可滚动模态框
  76. MessageBox.alert(
  77. `<div style="max-height:60vh;overflow:auto">${
  78. msg || `后端接口${code}异常`
  79. }</div>`,
  80. '错误',
  81. {
  82. type: 'error',
  83. dangerouslyUseHTMLString: true,
  84. showClose: true,
  85. }
  86. )
  87. } else {
  88. // 其他按当前默认(模态框)处理
  89. MessageBox.alert(`${msg || `后端接口${code}异常`}`, '错误', {
  90. type: 'error',
  91. dangerouslyUseHTMLString: true,
  92. showClose: true,
  93. })
  94. }
  95. break
  96. }
  97. }
  98. const instance = axios.create({
  99. baseURL,
  100. timeout: requestTimeout,
  101. // headers: {
  102. // 'Content-Type': contentType,
  103. // },
  104. headers: {
  105. 'Content-Type': contentType,
  106. 'ngrok-skip-browser-warning': 'true',
  107. },
  108. })
  109. instance.interceptors.request.use(
  110. (config) => {
  111. if (config.headers && config.headers.constructor == String) {
  112. try {
  113. config.headers = JSON.parse(config.headers)
  114. } catch (e) {
  115. MessageBox.alert(
  116. `请求头部不是有效的JSON格式:${config.headers}`,
  117. '请求错误',
  118. { type: 'error', dangerouslyUseHTMLString: true }
  119. )
  120. throw e
  121. }
  122. }
  123. if (!config.headers || !config.headers.Authorization) {
  124. const accessToken = store.getters['user/accessToken']
  125. if (accessToken) {
  126. config.headers['Authorization'] = `Bearer ${accessToken}`
  127. }
  128. }
  129. if (
  130. config.data &&
  131. config.headers['Content-Type'] ===
  132. 'application/x-www-form-urlencoded;charset=UTF-8'
  133. )
  134. config.data = qs.stringify(config.data)
  135. config.headers['Accept-Language'] = localStorage.getItem('lang') || 'zh-CN'
  136. if (debounce.some((item) => config.url && config.url.includes(item)))
  137. loadingInstance = Vue.prototype.$baseLoading()
  138. return config
  139. },
  140. (error) => {
  141. return Promise.reject(error)
  142. }
  143. )
  144. instance.interceptors.response.use(
  145. (response) => {
  146. if (loadingInstance) loadingInstance.close()
  147. const { data, config, headers } = response
  148. const { code, msg, message } = data
  149. if (config) {
  150. const { responseType } = config
  151. // 请求的返回值类型为arraybuffer时返回headers
  152. if (responseType && responseType == 'arraybuffer') {
  153. return { data, headers }
  154. }
  155. }
  156. if (headers) {
  157. // 附件下载,需要返回 headers
  158. const content = headers['content-disposition']
  159. if (content && content.startsWith('attachment;')) {
  160. return { data, headers }
  161. }
  162. }
  163. // TODO:暂时允许请求不返回code
  164. if (code === undefined) {
  165. return data
  166. }
  167. // 操作正常Code数组
  168. const codeVerificationArray = isArray(successCode)
  169. ? [...successCode]
  170. : [...[successCode]]
  171. // 是否操作正常
  172. if (codeVerificationArray.includes(code)) {
  173. return data
  174. } else {
  175. handleCode(code, message)
  176. return Promise.reject(
  177. '请求异常拦截:' + JSON.stringify({ url: config.url, code, message }) ||
  178. 'Error'
  179. )
  180. }
  181. },
  182. (error) => {
  183. if (loadingInstance) loadingInstance.close()
  184. const { response, message } = error
  185. if (error.response && error.response.data) {
  186. const { status, data } = response
  187. //data.msg
  188. handleCode(status, data.message || message)
  189. return Promise.reject(error)
  190. } else {
  191. let { message } = error
  192. if (message === 'Network Error') {
  193. message = '后端接口连接异常'
  194. }
  195. if (message.includes('timeout')) {
  196. message = '后端接口请求超时'
  197. }
  198. if (message.includes('Request failed with status code')) {
  199. const code = message.substr(message.length - 3)
  200. message = '后端接口' + code + '异常'
  201. }
  202. MessageBox.alert(message || `后端接口未知异常`, '错误', {
  203. type: 'error',
  204. dangerouslyUseHTMLString: true,
  205. showClose: true,
  206. })
  207. return Promise.reject(error)
  208. }
  209. }
  210. )
  211. export default instance