request.js 5.56 KB
import Vue from 'vue'
import axios from 'axios'
import {
  baseURL,
  contentType,
  debounce,
  invalidCode,
  noPermissionCode,
  requestTimeout,
  successCode,
  recordRoute,
  loginInterception,
} from '@/config'
import router from '@/router'
import store from '@/store'
import qs from 'qs'
import { isArray } from '@/utils/validate'

let loadingInstance

/**
 * 处理code异常
 * @param {*} code
 * @param {*} msg
 */
const handleCode = (code, msg) => {
  switch (code) {
    case invalidCode:
      Vue.prototype.$baseMessage(msg || `后端接口${code}异常`, 'error')
      store.dispatch('user/resetAccessToken')
      if (loginInterception) {
        location.reload()
      }
      break
    case noPermissionCode:
      store.getters['user/accessToken'] &&
        store
          .dispatch('user/logout')
          .then(() => {
            if (recordRoute) {
              const fullPath = router.currentRoute.fullPath
              router.push(`/login?redirect=${fullPath}`)
            } else {
              router.push('/login')
            }
          })
          .catch(() => {
            // logout时可能因为token过期报401错误,此时用href跳转到login页面
            store.dispatch('user/resetAccessToken')
            router.push('/login')
          })
      break
    default:
      Vue.prototype.$baseMessage(msg || `后端接口${code}异常`, 'error')
      break
  }
}

const instance = axios.create({
  baseURL,
  timeout: requestTimeout,
  headers: {
    'Content-Type': contentType,
  },
})

instance.interceptors.request.use(
  (config) => {
    if (config.headers && config.headers.constructor == String) {
      try {
        config.headers = JSON.parse(config.headers)
      } catch (e) {
        Message.error(`请求头部不是有效的JSON格式:${config.headers}`)
        throw e
      }
    }
    if (!config.headers || !config.headers.Authorization) {
      const accessToken = store.getters['user/accessToken']
      if (accessToken) {
        config.headers['Authorization'] = `Bearer ${accessToken}`
      }
    }
    if (
      config.data &&
      config.headers['Content-Type'] ===
        'application/x-www-form-urlencoded;charset=UTF-8'
    )
      config.data = qs.stringify(config.data)
    config.headers['Accept-Language'] = localStorage.getItem('lang') || 'zh-CN'
    if (debounce.some((item) => config.url && config.url.includes(item)))
      loadingInstance = Vue.prototype.$baseLoading()
    return config
  },
  (error) => {
    return Promise.reject(error)
  }
)

instance.interceptors.response.use(
  (response) => {
    if (loadingInstance) loadingInstance.close()
    const { data, config, headers } = response
    const { code, msg, message, state, value } = data

    if (config) {
      const { responseType } = config
      // 请求的返回值类型为arraybuffer时返回headers
      if (responseType && responseType == 'arraybuffer') {
        return { data, headers }
      }
    }

    if (headers) {
      // 附件下载,需要返回 headers
      const content = headers['content-disposition']
      if (content && content.startsWith('attachment;')) {
        return { data, headers }
      }
    }

    // TODO:暂时允许请求不返回code
    if (code === undefined) {
      return data
    }
    //如果返回结果不符合EIP后端统一接口格式,直接返回data
    var isCommonResult = code && state && message
    if (!isCommonResult) {
      return data
    }

    // 操作正常Code数组
    const codeVerificationArray = isArray(successCode)
      ? [...successCode]
      : [...[successCode]]
    // 是否操作正常
    if (codeVerificationArray.includes(code)) {
      return data
    } else {
      handleCode(code, message)
      if (code == '401' && ['4016', '4017'].includes(data.errorCode)) {
        window.location.href = '/login'
      }
      return Promise.reject(
        '请求异常拦截:' + JSON.stringify({ url: config.url, code, message }) ||
          'Error'
      )
    }
  },
  (error) => {
    if (loadingInstance) loadingInstance.close()
    let { response, message } = error
    if (error.response && error.response.data) {
      if (
        error.response.status === 500 &&
        error.response.config.url.indexOf(
          'dataTemplate/v1/exportByBtnSetting'
        ) != -1
      ) {
        return Promise.reject(error)
      } else {
        const { status, data } = response
        // 是 arraybuffer 请求
        if (
          response.request &&
          response.request.responseType === 'arraybuffer'
        ) {
          // 格式化返回错误数据,用于提示
          try {
            let enc = new TextDecoder('utf-8')
            let result = JSON.parse(enc.decode(new Uint8Array(response.data)))
            if (result) {
              message = result.msg || result.message
              return Promise.reject(message)
            }
          } catch (error) {
            console.log(error, 'error')
          }
        }
        //data.msg
        handleCode(status, data.message || message)
        return Promise.reject(error)
      }
    } else {
      let { message } = error
      if (message === 'Network Error') {
        message = '后端接口连接异常'
      }
      if (message.includes('timeout')) {
        message = '后端接口请求超时'
      }
      if (message.includes('Request failed with status code')) {
        const code = message.substr(message.length - 3)
        message = '后端接口' + code + '异常'
      }
      Vue.prototype.$baseMessage(message || `后端接口未知异常`, 'error')
      return Promise.reject(error)
    }
  }
)

export default instance