import type { AxiosInstance, AxiosRequestConfig } from 'axios';
|
import axios from 'axios';
|
import { ElMessage } from 'element-plus';
|
import emitter from './mitt';
|
import { debounce } from './util';
|
import { AUTH_URL, MAIN_URL, SECONDARY_URL } from '/@/constants';
|
import { Local, Session } from '/@/utils/storage';
|
// import JSONbig from 'json-bigint';
|
|
//#region ====================== 后端 res.err_code ======================
|
export const enum ErrorCode {
|
/** @description 权限验证失败 */
|
Auth = 'AUTH',
|
}
|
//#endregion
|
|
const handleNoAuth = debounce(() => {
|
emitter.emit('logout');
|
emitter.emit('openLoginDlg');
|
});
|
const loginUrl = '/login';
|
const initRequestInterceptor = (request: AxiosInstance) => {
|
// 添加请求拦截器
|
request.interceptors.request.use(
|
(config) => {
|
// 获取本地的 token
|
const accessSession = Local.get(accessSessionKey);
|
if (accessSession) {
|
// 将 token 添加到请求报文头中
|
config.headers['hswatersession'] = accessSession;
|
} else {
|
if (config.url !== loginUrl) {
|
handleNoAuth(config.url);
|
throw '权限验证失败';
|
}
|
}
|
return config;
|
},
|
(error) => {
|
// 对请求错误做些什么
|
return Promise.reject(error);
|
}
|
);
|
|
// 添加响应拦截器
|
request.interceptors.response.use(
|
(res) => {
|
// 获取状态码和返回数据
|
const status = res.status;
|
const serveData = res.data;
|
if (!serveData) {
|
ElMessage.error('请求失败');
|
throw new Error('请求失败');
|
}
|
|
// 处理 401
|
if (status === 401) {
|
clearAccessTokens();
|
}
|
|
// 处理未进行规范化处理的
|
if (status >= 400) {
|
throw new Error(res.statusText || 'Request Error.');
|
}
|
|
// 响应拦截及自定义处理
|
if (!serveData.json_ok) {
|
switch (serveData?.err_code) {
|
case ErrorCode.Auth:
|
if (res.config.url !== loginUrl) {
|
handleNoAuth();
|
throw '权限验证失败';
|
}
|
}
|
const msg = serveData.json_msg ?? '';
|
|
const error = serveData?.err_code ? `${msg ? `【${serveData.err_code}】` : serveData.err_code}` : '';
|
const tip = error + msg || '请求失败';
|
ElMessage.error(tip);
|
const url = res.request.responseURL;
|
throw new Error(url + '\n' + tip);
|
}
|
return res.data;
|
},
|
(error) => {
|
// 处理响应错误
|
if (error.response) {
|
if (error.response.status === 401) {
|
clearAccessTokens();
|
}
|
}
|
|
// 对响应错误做点什么
|
if (error.message.indexOf('timeout') != -1) {
|
ElMessage.error('网络超时');
|
} else if (error.message == 'Network Error') {
|
ElMessage.error('网络连接错误');
|
} else {
|
if (error.response.data) ElMessage.error(error.response.statusText);
|
else ElMessage.error('接口路径找不到');
|
}
|
|
return Promise.reject(error);
|
}
|
);
|
};
|
// 配置新建一个 axios 实例
|
const createAxiosInstance = () => {
|
return axios.create({
|
baseURL: MAIN_URL,
|
timeout: 50000,
|
headers: { 'Content-Type': 'application/json;charset=utf-8 ' },
|
});
|
};
|
const service = createAxiosInstance();
|
|
export const mainRequest = service;
|
|
initRequestInterceptor(service);
|
|
export function secondaryRequest(config: AxiosRequestConfig<any>) {
|
return service({
|
...config,
|
baseURL: SECONDARY_URL,
|
});
|
}
|
|
/**
|
* 用于访问登录接口
|
*/
|
export function authRequest(config: AxiosRequestConfig<any>) {
|
return service({
|
...config,
|
baseURL: AUTH_URL,
|
});
|
}
|
/**
|
* @description 域名前缀
|
* 防止类似于 http://sqi.beng35.com/airp 和 http://sqi.beng35.com/test 公用同一个 token 或 userInfo
|
*/
|
const subDomainName = window.location.pathname
|
.split('/')
|
.filter((item) => !!item)
|
.join('-');
|
const domainPrefix = subDomainName ? `${subDomainName}-` : '';
|
// token 键定义
|
export const accessSessionKey = domainPrefix + 'access-session';
|
export const userNameKey = domainPrefix + 'userName';
|
|
export const refreshAccessTokenKey = `x-${accessSessionKey}`;
|
|
// userInfo键定义
|
export const userInfoKey = domainPrefix + 'userInfo';
|
|
// 获取 token
|
export const getSession = () => {
|
return Local.get(accessSessionKey);
|
};
|
|
// 清除 token
|
export const clearAccessTokens = async () => {
|
Local.remove(accessSessionKey);
|
// 清除用户信息(每次刷新都需要利用用户信息去请求对应权限菜单)
|
Local.remove(userInfoKey);
|
// 清除其他
|
Session.clear();
|
};
|
|
// axios 默认实例
|
export const axiosInstance: AxiosInstance = axios;
|
|
/**
|
* 参数处理
|
* @param {*} params 参数
|
*/
|
export function tansParams(params: any) {
|
let result = '';
|
for (const propName of Object.keys(params)) {
|
const value = params[propName];
|
const part = encodeURIComponent(propName) + '=';
|
if (value !== null && value !== '' && typeof value !== 'undefined') {
|
if (typeof value === 'object') {
|
for (const key of Object.keys(value)) {
|
if (value[key] !== null && value[key] !== '' && typeof value[key] !== 'undefined') {
|
const params = propName + '[' + key + ']';
|
const subPart = encodeURIComponent(params) + '=';
|
result += subPart + encodeURIComponent(value[key]) + '&';
|
}
|
}
|
} else {
|
result += part + encodeURIComponent(value) + '&';
|
}
|
}
|
}
|
return result;
|
}
|
|
/**
|
* 解密 JWT token 的信息
|
* @param token jwt token 字符串
|
* @returns <any>object
|
*/
|
export function decryptJWT(token: string): any {
|
token = token.replace(/_/g, '/').replace(/-/g, '+');
|
const json = decodeURIComponent(escape(window.atob(token.split('.')[1])));
|
return JSON.parse(json);
|
}
|
|
/**
|
* 将 JWT 时间戳转换成 Date
|
* @description 主要针对 `exp`,`iat`,`nbf`
|
* @param timestamp 时间戳
|
* @returns Date 对象
|
*/
|
export function getJWTDate(timestamp: number): Date {
|
return new Date(timestamp * 1000);
|
}
|
|
// 导出 axios 实例
|
export default service;
|