map/ui/dasadmin/src/utils/axios.ts
2024-06-19 16:32:11 +08:00

362 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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'
import { useUserInfo } from '/@/stores/userInfo'
import { encrypt_aes, decrypt_aes, generateRandomNumber } from './crypto'
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 userInfo = useUserInfo()
const Axios = axios.create({
baseURL: getUrl(),
timeout: 1000 * 10,
headers: {
'think-lang': config.lang.defaultLang,
server: true,
},
responseType: 'json',
})
// // 自定义后台入口
// if (adminBaseRoute.path != '/admin' && isAdminApp() && /^\/admin\//.test(axiosConfig.url!)) {
// axiosConfig.url = axiosConfig.url!.replace(/^\/admin\//, adminBaseRoute.path + '.php/')
// }
// alert(axiosConfig.data)
// 合并默认请求选项
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
},
options
)
// 请求拦截
Axios.interceptors.request.use(
(config) => {
const v = generateRandomNumber(16)
removePending(config)
options.CancelDuplicateRequest && addPending(config)
// 创建loading实例
if (options.loading) {
loadingInstance.count++
if (loadingInstance.count === 1) {
loadingInstance.target = ElLoading.service(loading)
}
}
if (config.method === 'post' && config.data) {
// 对data进行加密
const formData = new FormData()
formData.append('param', encrypt_aes(config.data, v))
config.data = formData
} else if (config.method === 'get' && config.params) {
// 对params进行加密
const formData = new FormData()
formData.append('param', encrypt_aes(config.params, v))
config.params = formData
}
// 自动携带token
if (config.headers) {
config.headers.v = v
const token = adminInfo.getToken()
if (token) (config.headers as anyObj).token = encrypt_aes(token, v)
}
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') {
if (response.data && response.data.code !== 200) {
// 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.token}`
// window.requests.forEach((cb) => cb(res.data.token, '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) => {
// response.headers.token = `${token}`
// resolve(Axios(response.config))
// })
// })
// }
// }
// 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 ?? '' })
// }
if (response.data.code == 400) {
let routerPath = adminBaseRoute.path
userInfo.removeToken()
routerPath += '/login'
router.push({ path: routerPath })
}
if (response.data.code == 401) {
let routerPath = adminBaseRoute.path
userInfo.removeToken()
routerPath += '/login'
router.push({ path: routerPath })
}
// code不等于1, 页面then内的具体逻辑就不执行了
return Promise.reject(response.data)
} else if (options.showSuccessMessage && response.data && response.data.code == 1) {
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
}
/*
* 感谢掘金@橙某人提供的思路和分享
* 本axios封装详细解释请参考https://juejin.cn/post/6968630178163458084?share_token=7831c9e0-bea0-469e-8028-b587e13681a8#heading-27
*/