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