You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
235 lines
6.8 KiB
235 lines
6.8 KiB
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<string, AxiosInstance> = {} |
|
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<AxiosError> => { |
|
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<any> => { |
|
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<T = any>(config: AxiosRequestConfig): Promise<Result<T>> { |
|
try { |
|
const result = (await service(config)) as Result<T> |
|
return result |
|
} catch (err: any) { |
|
const result: Result<T> = { |
|
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
|
|
|