|
|
|
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
|