import type { AxiosInstance, AxiosRequestConfig, AxiosRequestHeaders, AxiosResponse, InternalAxiosRequestConfig, } from 'axios' import qs from 'qs' import Keys from '../Keys' import { useMessage } from '@/hooks/useMessage' import axios, { AxiosError, isCancel } from 'axios' import { getRefreshToken, getTenantId, getToken, removeToken, setToken, } from '@/utils/auth' import { authErrMap, ignoreMsgs, networkErrMap } from '../basic/utils' import type { Result } from '../basic/httpTypes' import type { APIConfigKeys } from './config' import { openSocket, closeSocket } from '@/pages/socket_server/index' import { RouterView, useRouter } from 'vue-router' import { deleteUserCache } from '@/hooks/useCache' const router = useRouter() const VITE_BASE_API_SYSTEM = import.meta.env.VITE_BASE_API_SYSTEM const message = useMessage() const basicHeader = { tenant() { const tenant = getTenantId() if (tenant) return tenant return '' }, token() { const token = getToken() if (token) return 'Bearer ' + token return '' }, testToken: 'Bearer test', } interface Config { baseURL?: string baseAPI: string } const instances: Record = {} let isRefreshToken = false // 是否正在刷新中 let requestList: any[] = [] // 请求队列 const createAxiosInstance = (module: APIConfigKeys, config: Config) => { if (!config || !config.baseAPI) { throw new Error(`Invalid configuration for module: ${module}`) } const { baseAPI } = config if (!instances[module]) { const instance = axios.create({ baseURL: `${baseAPI}`, timeout: 10000, headers: { 'Content-Type': 'application/json', }, }) instances[module] = instance } const service = instances[module] service.interceptors.request.use( (config: InternalAxiosRequestConfig): InternalAxiosRequestConfig => { config.headers.Authorization = basicHeader.token() // config.headers['tenant-id'] = basicHeader.tenant() const params = config.params || {} const data = config.data || false if ( config.method?.toUpperCase() === 'POST' && (config.headers as AxiosRequestHeaders)['Content-Type'] === 'application/x-www-form-urlencoded' ) { config.data = qs.stringify(data) } if (config.method?.toUpperCase() === 'GET' && params) { config.params = {} const paramsStr = qs.stringify(params, { allowDots: true }) if (paramsStr) { config.url = config.url + '?' + paramsStr } } return config }, (error: AxiosError): Promise => { return Promise.reject(error) } ) service.interceptors.response.use( async (res: AxiosResponse) => { const config = res.config let { data } = res if (!res.data) { throw new Error('返回“[HTTP]请求没有返回值”') } if ( res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer' ) { // 注意:如果导出的响应为 json,说明可能失败了,不直接返回进行下载 if (res.data.type !== 'application/json') { return Promise.resolve({ code: res.status, data: res.data }) } data = await new Response(res.data).json() } const code = data.code || 200 const msg = data.msg || authErrMap[code] || authErrMap['default'] if (ignoreMsgs.indexOf(msg) !== -1) { // 如果是忽略的错误码,直接返回 msg 异常 return Promise.reject({ code: null, msg }) } else if (code === 401) { if (!isRefreshToken) { isRefreshToken = true // if (!getRefreshToken()) logout() try { const refreshTokenRes = await refreshToken() if (refreshTokenRes.data.code !== 0) { // logout() return Promise.reject({ code, msg }) } setToken(refreshTokenRes.data.data.accessToken) config.headers!.Authorization = 'Bearer ' + getToken() requestList.forEach((cb: any) => { cb() }) requestList = [] // 重连接socket // openSocket() return service(config) } catch (e) { // logout() return Promise.reject({ code, msg }) } finally { requestList = [] isRefreshToken = false } } else { return new Promise(resolve => { requestList.push(() => { config.headers!.Authorization = 'Bearer ' + getToken() resolve(service(config)) }) }) } } else if (code === 500) { return Promise.reject({ code, msg }) } else if (code !== 200) { // if (msg === '无效的刷新令牌') { // // hard coding:忽略这个提示,直接登出 // console.log(msg) // } else { // ElMessage.error(msg) // } return Promise.reject({ code, msg }) } return data }, (error: any): Promise => { if (isCancel(error)) { return Promise.resolve() } const message = '请求错误' if (error.code === 'ECONNABORTED') { return Promise.reject({ code: null, msg: '服务器响应超时' }) } if (!error.response) { return Promise.reject({ code: null, msg: message }) } const status = error.response?.status const unKnowError = `连接出错(${error.response.status})!` if (status === 401) { localStorage.removeItem(Keys.STORAGE_TOKEN) router.push('/login') } const msg = networkErrMap[status] ? networkErrMap[status] : unKnowError return Promise.reject({ code: status || null, msg: msg }) } ) if (!service) { throw new Error(`Failed to create Axios instance for module: ${module}`) } const refreshToken = async () => { // axios.defaults.headers.common['tenant-id'] = getTenantId() return await axios.post( `${VITE_BASE_API_SYSTEM}/auth/refresh-token?refreshToken=` + getRefreshToken() ) } async function request(config: AxiosRequestConfig): Promise> { try { const result = (await service(config)) as Result return result } catch (err: any) { const result: Result = { code: err?.code || -1, msg: err.msg || err.message, data: err.data || null, } return result } } return request } let isShowLogout = false async function logout() { if (isShowLogout) return isShowLogout = true await message.forceConfirm('登录状态已失效,请重新登录', '系统提示', '重新登录') removeToken() isShowLogout = false deleteUserCache() window.location.href = '/login' } export default createAxiosInstance