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); }