409 lines
13 KiB
JavaScript
409 lines
13 KiB
JavaScript
import axios from "axios";
|
||
import { ElMessage } from "element-plus";
|
||
import dayjs from 'dayjs'
|
||
import useRequestLoading from "../hooks/useRequestLoading/index.js";
|
||
import { getBaseUrl } from "../utils/index.js";
|
||
import CryptoJS from "crypto-js";
|
||
import {aesDecrypt} from "../aesSecret/index.js";
|
||
|
||
/**
|
||
* Axios服务类
|
||
*/
|
||
class AxiosService {
|
||
constructor() {
|
||
/**
|
||
* Axios配置选项
|
||
*/
|
||
this.config = {
|
||
router: null,
|
||
store: null,
|
||
tokenRefreshUrl: '/sys/refreshToken',
|
||
loginPath: '/login',
|
||
tokenRefreshInterval: 5, // 分钟
|
||
baseURL: getBaseUrl(),
|
||
timeout: 1000 * 60 * 10,
|
||
requestParamsSign: {
|
||
use: false,
|
||
key: ''
|
||
},
|
||
responseParamsDecrypt: {
|
||
use: false,
|
||
}
|
||
};
|
||
|
||
this.loading = useRequestLoading();
|
||
this.isTipTokenFailure = false;
|
||
}
|
||
|
||
/**
|
||
* 配置axios插件
|
||
* @param {Object} [config] - 配置选项
|
||
* @param {Object} config.router - Vue Router实例
|
||
* @param {Object} config.store - token的piniaStore
|
||
* @param {string} [config.tokenRefreshUrl="/sys/refreshToken"] - token刷新接口URL
|
||
* @param {string} [config.loginPath="/login"] - 登录页面路由
|
||
* @param {number} [config.tokenRefreshInterval=5] - token刷新间隔(分钟)
|
||
* @param {string} [config.baseURL=getBaseUrl()] - API基础URL
|
||
* @param {number} [config.timeout=60000] - 请求超时时间(毫秒)
|
||
* @param {Object} [config.requestParamsSign] - 请求参数是否签名
|
||
* @param {boolean} [config.requestParamsSign.use=false] - 是否使用签名
|
||
* @param {string} [config.requestParamsSign.key] - 签名密钥
|
||
* @param {Object} [config.responseParamsDecrypt] - 响应参数是否需要解密
|
||
* @param {boolean} [config.responseParamsDecrypt.use=false] - 是否需要解密
|
||
*/
|
||
configure(config = {}) {
|
||
if (!config.router) throw new Error('router 参数必传');
|
||
if (!config.store) throw new Error('store 参数必传');
|
||
this.config = { ...this.config, ...config };
|
||
|
||
// 设置默认配置
|
||
axios.defaults.baseURL = this.config.baseURL;
|
||
axios.defaults.timeout = this.config.timeout;
|
||
|
||
// 清除现有拦截器
|
||
axios.interceptors.request.clear();
|
||
axios.interceptors.response.clear();
|
||
|
||
// 重新设置拦截器
|
||
this.setupInterceptors();
|
||
}
|
||
|
||
/**
|
||
* token刷新逻辑
|
||
*/
|
||
async refreshToken() {
|
||
const currentRoute = this.config.router.currentRoute?.value;
|
||
if (currentRoute?.meta?.isLogin) {
|
||
const store = this.config.store;
|
||
if (store.getTokenTime) {
|
||
const diffMinutes = dayjs().diff(dayjs(store.getTokenTime), "minute");
|
||
if (diffMinutes >= this.config.tokenRefreshInterval) {
|
||
await store.setTokenTime(dayjs().format("YYYY-MM-DD HH:mm:ss"));
|
||
await this.postRequest(this.config.tokenRefreshUrl);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 请求参数签名
|
||
*/
|
||
requestParamsSign(params) {
|
||
if (!this.config.requestParamsSign.key) throw new Error('请传入签名密钥');
|
||
const Timestamp = new Date().getTime();
|
||
const cloneParams = { ...params };
|
||
const keys = Object.keys(cloneParams).sort();
|
||
const sortData = {};
|
||
for (let i = 0; i < keys.length; i++) {
|
||
if (cloneParams[keys[i]] !== null && cloneParams[keys[i]] !== undefined) {
|
||
sortData[keys[i]] = cloneParams[keys[i]];
|
||
}
|
||
}
|
||
const Sign = CryptoJS.MD5(
|
||
Timestamp +
|
||
JSON.stringify(sortData) +
|
||
this.config.requestParamsSign.key
|
||
).toString();
|
||
return {
|
||
Sign,
|
||
Timestamp,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 设置拦截器
|
||
*/
|
||
setupInterceptors() {
|
||
// 请求拦截器
|
||
axios.interceptors.request.use(
|
||
async (request) => {
|
||
request.headers.Token = this.config.store.getToken;
|
||
let params = request.data || request.params || {};
|
||
// 根据配置决定是否启用参数签名
|
||
if (this.config.requestParamsSign.use) {
|
||
const { Timestamp, Sign } = this.requestParamsSign(params);
|
||
request.headers.Timestamp = Timestamp;
|
||
request.headers.Sign = Sign;
|
||
}
|
||
|
||
this.loading.value = true;
|
||
return request;
|
||
},
|
||
(error) => Promise.reject(error)
|
||
);
|
||
|
||
// 响应拦截器
|
||
axios.interceptors.response.use(
|
||
(response) => {
|
||
if (this.config.responseParamsDecrypt.use) response.data = { ...response.data, ...aesDecrypt(response.data.data)}
|
||
this.loading.value = false;
|
||
if (response.data.code === 401) {
|
||
if (!this.isTipTokenFailure) {
|
||
this.isTipTokenFailure = true;
|
||
ElMessage.error("登录失效,请重新登录");
|
||
this.config.router.push(this.config.loginPath);
|
||
this.isTipTokenFailure = false;
|
||
}
|
||
return Promise.reject(response.data.msg);
|
||
} else {
|
||
this.refreshToken().then();
|
||
}
|
||
return response;
|
||
},
|
||
(error) => {
|
||
if (error && error.response) {
|
||
if (import.meta.env.DEV) ElMessage.error(`连接错误${error.response.status}`);
|
||
} else ElMessage.error(error.message);
|
||
return Promise.reject(error.message);
|
||
}
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 发送POST请求
|
||
* @param {string} url - 请求URL
|
||
* @param {Object} params - 请求参数
|
||
* @returns {Promise} - 返回Promise对象,包含请求结果
|
||
*/
|
||
postRequest(url, params = {}) {
|
||
return new Promise((resolve, reject) => {
|
||
axios
|
||
.post(url, params)
|
||
.then((res) => {
|
||
if (res.data.result === "success") {
|
||
resolve(res.data);
|
||
} else {
|
||
ElMessage.error(res.data.msg || "系统开小差了");
|
||
reject(res.data);
|
||
}
|
||
})
|
||
.catch((err) => {
|
||
reject(err);
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 获取GET请求
|
||
* @param {string} url - 请求URL
|
||
* @param {Object} params - 请求参数
|
||
* @param {string} splicingURL - URL拼接字符串
|
||
* @returns {Promise} - 返回Promise对象,包含请求结果
|
||
*/
|
||
getRequest(url, params = {}, splicingURL = "") {
|
||
return new Promise((resolve, reject) => {
|
||
axios
|
||
.get(url + (splicingURL ? "/" + splicingURL : ""), {
|
||
headers: {
|
||
"Content-Type": "application/x-www-form-urlencoded",
|
||
},
|
||
params,
|
||
})
|
||
.then((res) => {
|
||
if (res.data.result === "success") {
|
||
resolve(res.data);
|
||
} else {
|
||
ElMessage.error(res.data.msg || "系统开小差了");
|
||
reject(res.data);
|
||
}
|
||
})
|
||
.catch((err) => {
|
||
reject(err);
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 获取PUT请求
|
||
* @param {string} url - 请求URL
|
||
* @param {Object} params - 请求参数
|
||
* @returns {Promise} - 返回Promise对象,包含请求结果
|
||
*/
|
||
putRequest(url, params = {}) {
|
||
return new Promise((resolve, reject) => {
|
||
axios
|
||
.put(url, params)
|
||
.then((res) => {
|
||
if (res.data.result === "success") {
|
||
resolve(res.data);
|
||
} else {
|
||
ElMessage.error(res.data.msg || "系统开小差了");
|
||
reject(res.data);
|
||
}
|
||
})
|
||
.catch((err) => {
|
||
reject(err);
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 获取DELETE请求
|
||
* @param {string} url - 请求URL
|
||
* @param {Object} params - 删除参数
|
||
* @param {string} splicingURL - URL拼接字符串
|
||
* @returns {Promise} - 返回Promise对象,包含请求结果
|
||
*/
|
||
deleteRequest(url, params = {}, splicingURL = "") {
|
||
return new Promise((resolve, reject) => {
|
||
axios
|
||
.delete(url + (splicingURL ? "/" + splicingURL : ""), { data: params })
|
||
.then((res) => {
|
||
if (res.data.result === "success") {
|
||
resolve(res.data);
|
||
} else {
|
||
ElMessage.error(res.data.msg || "系统开小差了");
|
||
reject(res.data);
|
||
}
|
||
})
|
||
.catch((err) => {
|
||
reject(err);
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 获取PATCH请求
|
||
* @param {string} url - 请求URL
|
||
* @param {Object} params - 请求参数
|
||
* @param {string} splicingURL - URL拼接字符串
|
||
* @returns {Promise} - 返回Promise对象,包含请求结果
|
||
*/
|
||
patchRequest(url, params = {}, splicingURL = "") {
|
||
return new Promise((resolve, reject) => {
|
||
axios
|
||
.patch(url + (splicingURL ? "/" + splicingURL : ""), params)
|
||
.then((res) => {
|
||
if (res.data.result === "success") {
|
||
resolve(res.data);
|
||
} else {
|
||
ElMessage.error(res.data.msg || "系统开小差了")
|
||
reject(res.data);
|
||
}
|
||
})
|
||
.catch((err) => {
|
||
reject(err);
|
||
});
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 上传文件请求
|
||
* @param {string} url - 请求URL
|
||
* @param {Object} params - 请求参数
|
||
* @returns {Promise} - 返回Promise对象,包含请求结果
|
||
*/
|
||
uploadRequest(url, params = {}) {
|
||
return new Promise((resolve, reject) => {
|
||
axios
|
||
.post(url, params, {
|
||
headers: {
|
||
"Content-Type": "multipart/form-data",
|
||
},
|
||
})
|
||
.then((res) => {
|
||
if (res.data.result === "success") {
|
||
resolve(res.data);
|
||
} else {
|
||
ElMessage.error(res.data.msg || "系统开小差了");
|
||
reject(res.data);
|
||
}
|
||
})
|
||
.catch((err) => {
|
||
reject(err);
|
||
});
|
||
});
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 配置axios插件
|
||
* @param {Object} [config] - 配置选项
|
||
* @param {Object} config.router - Vue Router实例
|
||
* @param {Object} config.store - token的piniaStore
|
||
* @param {string} [config.tokenRefreshUrl="/sys/refreshToken"] - token刷新接口URL
|
||
* @param {string} [config.loginPath="/login"] - 登录页面路由
|
||
* @param {number} [config.tokenRefreshInterval=5] - token刷新间隔(分钟)
|
||
* @param {string} [config.baseURL=getBaseUrl()] - API基础URL
|
||
* @param {number} [config.timeout=60000] - 请求超时时间(毫秒)
|
||
* @param {Object} [config.requestParamsSign] - 请求参数是否签名
|
||
* @param {boolean} [config.requestParamsSign.use=false] - 是否使用签名
|
||
* @param {string} [config.requestParamsSign.key] - 签名密钥
|
||
* @param {Object} [config.responseParamsDecrypt] - 响应参数是否需要解密
|
||
* @param {boolean} [config.responseParamsDecrypt.use=false] - 是否需要解密
|
||
*/
|
||
export function configureAxios(config = {}) {
|
||
if(!window.axiosService) window.axiosService = new AxiosService();
|
||
return axiosService.configure(config);
|
||
}
|
||
|
||
/**
|
||
* 发送POST请求
|
||
* @param {string} url - 请求URL
|
||
* @param {Object} params - 请求参数
|
||
* @returns {Promise} - 返回Promise对象,包含请求结果
|
||
*/
|
||
export function postRequest(url, params = {}) {
|
||
if (!window.axiosService) throw new Error('未配置Axios,请先调用 configureAxios');
|
||
return window.axiosService.postRequest(url, params);
|
||
}
|
||
|
||
/**
|
||
* 获取GET请求
|
||
* @param {string} url - 请求URL
|
||
* @param {Object} params - 请求参数
|
||
* @param {string} splicingURL - URL拼接字符串
|
||
* @returns {Promise} - 返回Promise对象,包含请求结果
|
||
*/
|
||
export function getRequest(url, params = {}, splicingURL = "") {
|
||
if (!window.axiosService) throw new Error('未配置Axios,请先调用 configureAxios');
|
||
return window.axiosService.getRequest(url, params, splicingURL);
|
||
}
|
||
|
||
/**
|
||
* 获取PUT请求
|
||
* @param {string} url - 请求URL
|
||
* @param {Object} params - 请求参数
|
||
* @returns {Promise} - 返回Promise对象,包含请求结果
|
||
*/
|
||
export function putRequest(url, params = {}) {
|
||
if (!window.axiosService) throw new Error('未配置Axios,请先调用 configureAxios');
|
||
return window.axiosService.putRequest(url, params);
|
||
}
|
||
|
||
/**
|
||
* 获取DELETE请求
|
||
* @param {string} url - 请求URL
|
||
* @param {Object} params - 删除参数
|
||
* @param {string} splicingURL - URL拼接字符串
|
||
* @returns {Promise} - 返回Promise对象,包含请求结果
|
||
*/
|
||
export function deleteRequest(url, params = {}, splicingURL = "") {
|
||
if (!window.axiosService) throw new Error('未配置Axios,请先调用 configureAxios');
|
||
return window.axiosService.deleteRequest(url, params, splicingURL);
|
||
}
|
||
|
||
/**
|
||
* 获取PATCH请求
|
||
* @param {string} url - 请求URL
|
||
* @param {Object} params - 请求参数
|
||
* @param {string} splicingURL - URL拼接字符串
|
||
* @returns {Promise} - 返回Promise对象,包含请求结果
|
||
*/
|
||
export function patchRequest(url, params = {}, splicingURL = "") {
|
||
if (!window.axiosService) throw new Error('未配置Axios,请先调用 configureAxios');
|
||
return window.axiosService.patchRequest(url, params, splicingURL);
|
||
}
|
||
|
||
/**
|
||
* 上传文件请求
|
||
* @param {string} url - 请求URL
|
||
* @param {Object} params - 请求参数
|
||
* @returns {Promise} - 返回Promise对象,包含请求结果
|
||
*/
|
||
export function uploadRequest(url, params = {}) {
|
||
if (!window.axiosService) throw new Error('未配置Axios,请先调用 configureAxios');
|
||
return window.axiosService.uploadRequest(url, params);
|
||
}
|
||
|