map/ui/dasadmin/src/utils/axios.ts

359 lines
13 KiB
TypeScript
Raw Normal View History

2024-06-13 11:30:23 +08:00
import type { AxiosRequestConfig, Method } from 'axios'
import axios from 'axios'
import { ElLoading, ElNotification, type LoadingOptions } from 'element-plus'
import { refreshToken } from '/@/api/common'
import { i18n } from '/@/lang/index'
import router from '/@/router/index'
import adminBaseRoute from '/@/router/static/adminBase'
import { useAdminInfo } from '/@/stores/adminInfo'
import { useConfig } from '/@/stores/config'
2024-06-17 17:10:54 +08:00
import { encrypt_aes, decrypt_aes, generateRandomNumber } from './crypto'
2024-06-21 09:27:02 +08:00
import { json } from 'stream/consumers'
2024-06-13 11:30:23 +08:00
window.requests = []
window.tokenRefreshing = false
const pendingMap = new Map()
const loadingInstance: LoadingInstance = {
target: null,
count: 0,
}
/**
* URL
*/
export const getUrl = (): string => {
const value: string = import.meta.env.VITE_AXIOS_BASE_URL as string
return value == 'getCurrentDomain' ? window.location.protocol + '//' + window.location.host : value
}
/**
* URL的端口
*/
export const getUrlPort = (): string => {
const url = getUrl()
return new URL(url).port
}
/**
* `Axios`
* `reductDataFormat(简洁响应)`,`ApiPromise`
* `reductDataFormat`,`AxiosPromise`
*/
function createAxios<Data = any, T = ApiPromise<Data>>(axiosConfig: AxiosRequestConfig, options: Options = {}, loading: LoadingOptions = {}): T {
const config = useConfig()
const adminInfo = useAdminInfo()
const Axios = axios.create({
baseURL: getUrl(),
timeout: 1000 * 10,
headers: {
'think-lang': config.lang.defaultLang,
server: true,
2024-06-21 09:27:02 +08:00
'Content-Type': 'application/json',
2024-06-13 11:30:23 +08:00
},
responseType: 'json',
})
// // 自定义后台入口
// if (adminBaseRoute.path != '/admin' && isAdminApp() && /^\/admin\//.test(axiosConfig.url!)) {
// axiosConfig.url = axiosConfig.url!.replace(/^\/admin\//, adminBaseRoute.path + '.php/')
// }
2024-06-17 18:03:10 +08:00
// alert(axiosConfig.data)
2024-06-13 11:30:23 +08:00
// 合并默认请求选项
options = Object.assign(
{
CancelDuplicateRequest: true, // 是否开启取消重复请求, 默认为 true
loading: false, // 是否开启loading层效果, 默认为false
reductDataFormat: true, // 是否开启简洁的数据结构响应, 默认为true
showErrorMessage: true, // 是否开启接口错误信息展示,默认为true
showCodeMessage: true, // 是否开启code不为1时的信息提示, 默认为true
showSuccessMessage: false, // 是否开启code为1时的信息提示, 默认为false
anotherToken: '', // 当前请求使用另外的用户token
2024-07-04 16:44:12 +08:00
customEncrypt: false, // 是否开启自定义加密
2024-06-13 11:30:23 +08:00
},
options
)
// 请求拦截
Axios.interceptors.request.use(
(config) => {
2024-06-17 18:03:10 +08:00
const v = generateRandomNumber(16)
2024-06-13 11:30:23 +08:00
removePending(config)
options.CancelDuplicateRequest && addPending(config)
// 创建loading实例
if (options.loading) {
loadingInstance.count++
if (loadingInstance.count === 1) {
loadingInstance.target = ElLoading.service(loading)
}
}
2024-07-04 16:44:12 +08:00
if (!options.customEncrypt) {
if (config.method === 'post' && config.data) {
// 对data进行加密
console.log(config.data)
config.data = encrypt_aes(config.data, v)
} else if (config.method === 'get' && config.params) {
// 对params进行加密
console.log(config.params)
config.params = encrypt_aes(config.params, v)
}
2024-06-13 11:30:23 +08:00
2024-07-04 16:44:12 +08:00
// 自动携带token
if (config.headers) {
config.headers.v = v
const token = adminInfo.getToken()
if (token) (config.headers as anyObj).token = encrypt_aes(token, v)
}
2024-06-13 11:30:23 +08:00
}
return config
},
(error) => {
return Promise.reject(error)
}
)
// 响应拦截
Axios.interceptors.response.use(
(response) => {
removePending(response.config)
options.loading && closeLoading(options) // 关闭loading
if (response.config.responseType == 'json') {
2024-06-19 18:05:52 +08:00
if (response.data && response.data.code == 401) {
2024-06-21 14:02:09 +08:00
if (response.data.code == 409) {
if (!window.tokenRefreshing) {
window.tokenRefreshing = true
return refreshToken()
.then((res) => {
adminInfo.setToken(res.data.token, 'auth')
response.headers.token = `${res.data.accessToken}`
window.requests.forEach((cb) => cb(res.data.accessToken, 'admin-refresh'))
window.requests = []
return Axios(response.config)
})
.catch((err) => {
adminInfo.removeToken()
router.push({ name: 'adminLogin' })
return Promise.reject(err)
})
.finally(() => {
window.tokenRefreshing = false
})
} else {
return new Promise((resolve) => {
// 用函数形式将 resolve 存入,等待刷新后再执行
window.requests.push((token: string, type: string) => {
const v = response.headers.v
response.headers.token = encrypt_aes(token, v)
resolve(Axios(response.config))
})
})
}
}
2024-06-17 17:10:54 +08:00
// if (options.showCodeMessage) {
// ElNotification({
// type: 'error',
// message: response.data.msg,
// })
// }
// // 自动跳转到路由name或path
// if (response.data.code == 302) {
// router.push({ path: response.data.data.routePath ?? '', name: response.data.data.routeName ?? '' })
// }
2024-06-19 18:05:52 +08:00
// if (response.data.code == 401) {
let routerPath = adminBaseRoute.path
2024-06-20 14:57:02 +08:00
adminInfo.removeToken()
2024-06-19 18:05:52 +08:00
routerPath += '/login'
router.push({ path: routerPath })
// }
2024-06-13 11:30:23 +08:00
// code不等于1, 页面then内的具体逻辑就不执行了
return Promise.reject(response.data)
2024-06-19 18:05:52 +08:00
} else if (options.showSuccessMessage && response.data && response.data.code == 200) {
2024-06-13 11:30:23 +08:00
ElNotification({
message: response.data.msg ? response.data.msg : i18n.global.t('axios.Operation successful'),
type: 'success',
})
}
}
return options.reductDataFormat ? response.data : response
},
(error) => {
error.config && removePending(error.config)
options.loading && closeLoading(options) // 关闭loading
options.showErrorMessage && httpErrorStatusHandle(error) // 处理错误状态码
return Promise.reject(error) // 错误继续返回给到具体页面
}
)
return Axios(axiosConfig) as T
}
export default createAxios
/**
*
* @param {*} error
*/
function httpErrorStatusHandle(error: any) {
// 处理被取消的请求
if (axios.isCancel(error)) return console.error(i18n.global.t('axios.Automatic cancellation due to duplicate request:') + error.message)
let message = ''
if (error && error.response) {
switch (error.response.status) {
case 302:
message = i18n.global.t('axios.Interface redirected!')
break
case 400:
message = i18n.global.t('axios.Incorrect parameter!')
break
case 401:
message = i18n.global.t('axios.You do not have permission to operate!')
break
case 403:
message = i18n.global.t('axios.You do not have permission to operate!')
break
case 404:
message = i18n.global.t('axios.Error requesting address:') + error.response.config.url
break
case 408:
message = i18n.global.t('axios.Request timed out!')
break
case 409:
message = i18n.global.t('axios.The same data already exists in the system!')
break
case 500:
message = i18n.global.t('axios.Server internal error!')
break
case 501:
message = i18n.global.t('axios.Service not implemented!')
break
case 502:
message = i18n.global.t('axios.Gateway error!')
break
case 503:
message = i18n.global.t('axios.Service unavailable!')
break
case 504:
message = i18n.global.t('axios.The service is temporarily unavailable Please try again later!')
break
case 505:
message = i18n.global.t('axios.HTTP version is not supported!')
break
default:
message = i18n.global.t('axios.Abnormal problem, please contact the website administrator!')
break
}
}
if (error.message.includes('timeout')) message = i18n.global.t('axios.Network request timeout!')
if (error.message.includes('Network'))
message = window.navigator.onLine ? i18n.global.t('axios.Server exception!') : i18n.global.t('axios.You are disconnected!')
ElNotification({
type: 'error',
message,
})
}
/**
* Loading层实例
*/
function closeLoading(options: Options) {
if (options.loading && loadingInstance.count > 0) loadingInstance.count--
if (loadingInstance.count === 0) {
loadingInstance.target.close()
loadingInstance.target = null
}
}
/**
* cancel回调,
*/
function addPending(config: AxiosRequestConfig) {
const pendingKey = getPendingKey(config)
config.cancelToken =
config.cancelToken ||
new axios.CancelToken((cancel) => {
if (!pendingMap.has(pendingKey)) {
pendingMap.set(pendingKey, cancel)
}
})
}
/**
*
*/
function removePending(config: AxiosRequestConfig) {
const pendingKey = getPendingKey(config)
if (pendingMap.has(pendingKey)) {
const cancelToken = pendingMap.get(pendingKey)
cancelToken(pendingKey)
pendingMap.delete(pendingKey)
}
}
/**
* key
*/
function getPendingKey(config: AxiosRequestConfig) {
let { data } = config
const { url, method, params, headers } = config
if (typeof data === 'string') {
try {
data = JSON.parse(data)
} catch (e) {}
} // response里面返回的config.data是个字符串对象
return [
url,
method,
headers && (headers as anyObj).token ? (headers as anyObj).token : '',
headers && (headers as anyObj)['token'] ? (headers as anyObj)['token'] : '',
JSON.stringify(params),
JSON.stringify(data),
].join('&')
}
/**
* /
*/
export function requestPayload(method: Method, data: anyObj) {
if (method == 'GET') {
return {
params: data,
}
} else if (method == 'POST') {
return {
data: data,
}
}
}
interface LoadingInstance {
target: any
count: number
}
interface Options {
// 是否开启取消重复请求, 默认为 true
CancelDuplicateRequest?: boolean
// 是否开启loading层效果, 默认为false
loading?: boolean
// 是否开启简洁的数据结构响应, 默认为true
reductDataFormat?: boolean
// 是否开启接口错误信息展示,默认为true
showErrorMessage?: boolean
// 是否开启code不为0时的信息提示, 默认为true
showCodeMessage?: boolean
// 是否开启code为0时的信息提示, 默认为false
showSuccessMessage?: boolean
// 当前请求使用另外的用户token
anotherToken?: string
2024-07-04 16:44:12 +08:00
// 是否开启自定义加密
customEncrypt?: boolean
2024-06-13 11:30:23 +08:00
}
/*
* @橙某人提供的思路和分享
* axios封装详细解释请参考https://juejin.cn/post/6968630178163458084?share_token=7831c9e0-bea0-469e-8028-b587e13681a8#heading-27
*/