tj
2025-06-05 bba272999cc546f65781bf3d20245a3f819af67f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/**
 * 封装 axios
 */
import axios from 'axios'
import { ElMessage } from 'element-plus'
 
import store from '@/store'
import router from '@/router'
import Config from '@/config'
import autoJump from '@/lin/util/auto-jump'
import ErrorCode from '@/config/error-code'
import { getToken, saveAccessToken } from '@/lin/util/token'
 
const config = {
  baseURL: Config.baseURL || '',
  timeout: 50 * 1000, // 请求超时时间设置
  crossDomain: true,
  // withCredentials: true, // Check cross-site Access-Control
  // 定义可获得的http响应状态码
  // return true、设置为null或者undefined,promise将resolved,否则将rejected
  validateStatus(status) {
    return status >= 200 && status < 510
  },
}
 
/**
 * 错误码是否是refresh相关
 * @param { number } code 错误码
 */
function refreshTokenException(code) {
  const codes = [10000, 10042, 10050, 10052, 10012]
  return codes.includes(code)
}
 
// 创建请求实例
const _axios = axios.create(config)
 
_axios.interceptors.request.use(
  originConfig => {
    // 有 API 请求重新计时
    autoJump(router)
 
    const reqConfig = { ...originConfig }
 
    // step1: 容错处理
    if (!reqConfig.url) {
      console.error('request need url')
    }
 
    reqConfig.method = reqConfig.method.toLowerCase() // 大小写容错
 
    // 参数容错
    if (reqConfig.method === 'get') {
      if (!reqConfig.params) {
        reqConfig.params = reqConfig.data || {}
      }
    } else if (reqConfig.method === 'post') {
      if (!reqConfig.data) {
        reqConfig.data = reqConfig.params || {}
      }
 
      // 检测是否包含文件类型, 若包含则进行 formData 封装
      let hasFile = false
      Object.keys(reqConfig.data).forEach(key => {
        if (typeof reqConfig.data[key] === 'object') {
          const item = reqConfig.data[key]
          if (item instanceof FileList || item instanceof File || item instanceof Blob) {
            hasFile = true
          }
        }
      })
 
      // 检测到存在文件使用 FormData 提交数据
      if (hasFile) {
        const formData = new FormData()
        Object.keys(reqConfig.data).forEach(key => {
          formData.append(key, reqConfig.data[key])
        })
        reqConfig.data = formData
      }
    }
 
    // step2: permission 处理
    if (reqConfig.url === 'cms/user/refresh') {
      const refreshToken = getToken('refresh_token')
      if (refreshToken) {
        reqConfig.headers.Authorization = refreshToken
      }
    } else {
      const accessToken = getToken('access_token')
      if (accessToken) {
        reqConfig.headers.Authorization = accessToken
      }
    }
 
    return reqConfig
  },
  error => Promise.reject(error),
)
 
// Add a response interceptor
_axios.interceptors.response.use(
  async res => {
 
     // 如果是文件下载响应,直接处理文件
     if (res.config.responseType === 'blob') {
      return res; // 直接返回文件流,交给调用方法处理
    }
 
    if (res.status.toString().charAt(0) === '2') {
      return res.data
    }
 
    const { code, message } = res.data
 
    return new Promise(async (resolve, reject) => {
      let tipMessage = ''
      const { url } = res.config
 
      // refresh_token 异常,直接登出
      if (refreshTokenException(code)) {
        setTimeout(() => {
          store.dispatch('loginOut')
          const { origin } = window.location
          window.location.href = origin
        }, 1500)
        return resolve(null)
      }
      // assessToken相关,刷新令牌
      if (code === 10041 || code === 10051) {
        const cache = {}
        if (cache.url !== url) {
          cache.url = url
          const refreshResult = await _axios('cms/user/refresh')
          saveAccessToken(refreshResult.access_token)
          // 将上次失败请求重发
          const result = await _axios(res.config)
          return resolve(result)
        }
      }
 
      // 弹出信息提示的第一种情况:直接提示后端返回的异常信息(框架默认为此配置);
      // 特殊情况:如果本次请求添加了 handleError: true,用户自行通过 try catch 处理,框架不做额外处理
      if (res.config.handleError) {
        return reject(res)
      }
 
      // 弹出信息提示的第二种情况:采用前端自己定义的一套异常提示信息(需自行在配置项开启);
      // 特殊情况:如果本次请求添加了 showBackend: true, 弹出后端返回错误信息。
      if (Config.useFrontEndErrorMsg && !res.config.showBackend) {
        // 弹出前端自定义错误信息
        const errorArr = Object.entries(ErrorCode).filter(v => v[0] === code.toString())
        // 匹配到前端自定义的错误码
        if (errorArr.length > 0 && errorArr[0][1] !== '') {
          ;[[, tipMessage]] = errorArr
        } else {
          tipMessage = ErrorCode['777']
        }
      }
 
      if (typeof message === 'string') {
        tipMessage = message
      }
      if (Object.prototype.toString.call(message) === '[object Object]') {
        ;[tipMessage] = Object.values(message).flat()
      }
      if (Object.prototype.toString.call(message) === '[object Array]') {
        ;[tipMessage] = message
      }
      ElMessage.error(tipMessage)
      reject(res)
    })
  },
  error => {
    if (!error.response) {
      ElMessage.error('请检查 API 是否异常')
      console.log('error', error)
    }
 
    // 判断请求超时
    if (error.code === 'ECONNABORTED' && error.message.indexOf('timeout') !== -1) {
      ElMessage.warning('请求超时')
    }
    return Promise.reject(error)
  },
)
 
// 导出常用函数
 
/**
 * @param {string} url
 * @param {object} data
 * @param {object} params
 */
export function post(url, data = {}, params = {}) {
  return _axios({
    method: 'post',
    url,
    data,
    params,
  })
}
 
/**
 * @param {string} url
 * @param {object} params
 */
export function get(url, params = {}) {
  return _axios({
    method: 'get',
    url,
    params,
  })
}
 
/**
 * @param {string} url
 * @param {object} data
 * @param {object} params
 */
export function put(url, data = {}, params = {}) {
  return _axios({
    method: 'put',
    url,
    params,
    data,
  })
}
 
/**
 * @param {string} url
 * @param {object} params
 */
export function _delete(url, params = {}) {
  return _axios({
    method: 'delete',
    url,
    params,
  })
}
 
export default _axios