import type { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios' import { ContentTypeEnum, ResultEnum } from '@af-mobile-client-vue3/enums/requestEnum' import { useUserStore } from '@af-mobile-client-vue3/stores/modules/user' import { ACCESS_TOKEN } from '@af-mobile-client-vue3/stores/mutation-type' import { encryptUtil } from '@af-mobile-client-vue3/utils/EncryptUtil' import axios from 'axios' import { showToast } from 'vant' // 默认 axios 实例请求配置 const configDefault = { headers: { 'Content-Type': ContentTypeEnum.JSON, }, // 请求超时时间 timeout: 20000, // API 请求的默认前缀 baseURL: import.meta.env.VITE_APP_API_BASE_URL, data: {}, } class Http { // 当前实例 private static axiosInstance: AxiosInstance // 请求配置 private static axiosConfigDefault: AxiosRequestConfig // 请求拦截 private httpInterceptorsRequest(): void { Http.axiosInstance.interceptors.request.use( (config) => { // 发送请求前,可在此让每个请求携带自定义 token, 请根据实际情况修改 const savedToken = useUserStore().getToken() // 如果 token 存在 if (savedToken) config.headers[ACCESS_TOKEN] = savedToken const v4SessionKey = localStorage.getItem('v4-session-key') if (['post'].includes(config.method.toLowerCase()) && v4SessionKey) { if (config.data && !(config.data instanceof FormData)) { config.data = { encrypted: encryptUtil.AESEncryptCBC(config.data, v4SessionKey), } config.headers['X-Sec'] = '1' config.headers['X-Rand'] = Math.random().toString(36).substr(2, 5) config.headers['X-Ts'] = Date.now() } } return config }, (error: AxiosError) => { showToast({ message: error.message, position: 'bottom', }) return Promise.reject(error) }, ) } // 响应拦截 private httpInterceptorsResponse(): void { Http.axiosInstance.interceptors.response.use( async (response: AxiosResponse) => { // 判断是否需要解密 if (response.headers && response.headers['x-encrypted'] === '1') { const v4SessionKey = localStorage.getItem('v4-session-key') if (v4SessionKey && response.data) { const decryptedData = encryptUtil.decryptResponse(response?.data, v4SessionKey) // 如果解密成功且不等于原数据,说明解密有效 if (decryptedData !== response.data) { // 响应解密成功 response.data = decryptedData } } } const compatible = import.meta.env.VITE_APP_COMPATIBLE if (compatible !== 'V4') { return response.data } // 与后端协定的返回字段 const { code, msg, data } = response.data // 临时向v3请求上传服务,因为没有code,未来会改 if (code === undefined) { return response.data } // 判断请求是否成功 const isSuccess = code === ResultEnum.SUCCESS if (isSuccess) { // 兼容 V3 请求没有data的情况 if (data) { return data } else { return response.data } } else { if (code === 401 && response.config.url?.indexOf('af-auth/logout') === -1) { showToast({ message: '登录态已失效,请重新登录', position: 'bottom', }) await useUserStore().logout() } else { showToast({ message: msg, position: 'bottom', }) console.error(`请求失败,返回结果:${msg}`) } return Promise.reject(response.data) } }, (error: AxiosError) => { // 处理 HTTP 网络错误 let message: string = error.response?.data as string if (!message) { // HTTP 状态码 const status = error.response?.status switch (status) { case 400: message = '请求错误' break case 401: message = '登录态已失效,请重新登录' // 401状态码处理:自动登出并跳转到登录页 useUserStore().logout() break case 403: message = '拒绝访问' break case 404: message = `请求地址出错: ${error.response?.config?.url}` break case 408: message = '请求超时' break case 500: message = '服务器内部错误' break case 501: message = '服务未实现' break case 502: message = '网关错误' break case 503: message = '服务不可用' break case 504: message = '网关超时' break case 505: message = 'HTTP版本不受支持' break default: message = '网络连接故障' } } showToast({ message, position: 'bottom', }) return Promise.reject(error) }, ) } constructor(config: AxiosRequestConfig) { Http.axiosConfigDefault = config Http.axiosInstance = axios.create(config) this.httpInterceptorsRequest() this.httpInterceptorsResponse() } // 通用请求函数 public request(paramConfig: AxiosRequestConfig): Promise { const config = { ...Http.axiosConfigDefault, ...paramConfig } return new Promise((resolve, reject) => { Http.axiosInstance .request(config) .then((response: any) => { resolve(response) }) .catch((error) => { reject(error) }) }) } } const METHOD = { GET: 'get', POST: 'post', PUT: 'put', DELETE: 'delete', } /** * axios请求 * @param url 请求地址 * @param method {METHOD} http method * @param params 请求参数 * @param config * @returns {Promise>} */ async function request(url, method, params, config) { switch (method) { case METHOD.GET: return axios.get(url, { params, ...config }) case METHOD.POST: return axios.post(url, params, config) case METHOD.PUT: return axios.put(url, params, config) case METHOD.DELETE: return axios.delete(url, { params, ...config }) default: return axios.get(url, { params, ...config }) } } export const http = new Http(configDefault) export { METHOD, request, }