239 lines
5.4 KiB
JavaScript
239 lines
5.4 KiB
JavaScript
import axios from "axios";
|
||
import crypto from "@/utils/crypto";
|
||
import Cookies from 'js-cookie';
|
||
import { ElMessage, ElLoading } from 'element-plus';
|
||
|
||
// 获取基础 API 地址
|
||
const API_BASE_URL = import.meta.env.VITE_APP_API_BASE_URL;
|
||
|
||
if (!API_BASE_URL) {
|
||
console.warn('API基础地址未配置,请在环境变量中设置VITE_APP_API_BASE_URL');
|
||
}
|
||
|
||
// 创建axios实例
|
||
const api = axios.create({
|
||
baseURL: API_BASE_URL,
|
||
timeout: 15000, // 请求超时时间
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
}
|
||
});
|
||
|
||
// 加载状态管理
|
||
let loadingInstance = null;
|
||
let requestCount = 0;
|
||
|
||
// 显示加载状态
|
||
const showLoading = () => {
|
||
if (requestCount === 0) {
|
||
loadingInstance = ElLoading.service({
|
||
lock: true,
|
||
text: '加载中...',
|
||
background: 'rgba(0, 0, 0, 0.1)'
|
||
});
|
||
}
|
||
requestCount++;
|
||
};
|
||
|
||
// 隐藏加载状态
|
||
const hideLoading = () => {
|
||
requestCount--;
|
||
if (requestCount <= 0 && loadingInstance) {
|
||
loadingInstance.close();
|
||
loadingInstance = null;
|
||
requestCount = 0;
|
||
}
|
||
};
|
||
|
||
// 请求拦截器
|
||
api.interceptors.request.use(
|
||
(config) => {
|
||
// 如果需要显示加载状态
|
||
if (config.showLoading !== false) {
|
||
showLoading();
|
||
}
|
||
|
||
// 添加认证token
|
||
const accessToken = Cookies.get('access_token');
|
||
if (accessToken) {
|
||
config.headers['Authorization'] = `Bearer ${accessToken}`;
|
||
}
|
||
|
||
// 处理POST请求加密
|
||
if (config.method?.toLowerCase() === 'post' && !config.isEncrypted) {
|
||
try {
|
||
// 添加用户信息
|
||
let requestData = { ...config.data };
|
||
const storedUserInfo = localStorage.getItem('info');
|
||
|
||
if (storedUserInfo) {
|
||
const info = JSON.parse(crypto.decrypt(storedUserInfo));
|
||
requestData['user_id'] = info.id;
|
||
}
|
||
|
||
// 加密数据
|
||
config.data = {
|
||
postdata: crypto.encrypt(JSON.stringify(requestData))
|
||
};
|
||
|
||
// 标记为已加密
|
||
config.isEncrypted = true;
|
||
} catch (error) {
|
||
console.error('请求数据加密失败:', error);
|
||
}
|
||
}
|
||
|
||
return config;
|
||
},
|
||
(error) => {
|
||
hideLoading();
|
||
ElMessage.error('请求配置错误');
|
||
return Promise.reject(error);
|
||
}
|
||
);
|
||
|
||
// 响应拦截器
|
||
api.interceptors.response.use(
|
||
(response) => {
|
||
hideLoading();
|
||
|
||
// 处理业务逻辑错误
|
||
const { data } = response;
|
||
|
||
// 根据实际后端规范调整
|
||
if (data.code && data.code !== 200) {
|
||
ElMessage.error(data.message || '操作失败');
|
||
return Promise.reject(new Error(data.message || '请求失败'));
|
||
}
|
||
|
||
return data;
|
||
},
|
||
async (error) => {
|
||
hideLoading();
|
||
|
||
// 处理网络错误
|
||
if (!error.response) {
|
||
ElMessage.error('网络连接异常,请检查网络');
|
||
return Promise.reject(error);
|
||
}
|
||
|
||
const { status, data } = error.response;
|
||
|
||
// 处理不同状态码
|
||
switch (status) {
|
||
case 401:
|
||
// 未授权,处理token过期逻辑
|
||
ElMessage.error('登录已过期,请重新登录');
|
||
// 清除登录状态
|
||
Cookies.remove('access_token');
|
||
localStorage.removeItem('info');
|
||
// 跳转到登录页(假设使用vue-router)
|
||
if (window.router) {
|
||
window.router.push('/');
|
||
}
|
||
break;
|
||
case 403:
|
||
ElMessage.error('没有权限执行此操作');
|
||
break;
|
||
case 404:
|
||
ElMessage.error('请求的资源不存在');
|
||
break;
|
||
case 500:
|
||
ElMessage.error('服务器内部错误,请稍后再试');
|
||
break;
|
||
default:
|
||
ElMessage.error(data?.message || `请求错误 (${status})`);
|
||
}
|
||
|
||
return Promise.reject(error);
|
||
}
|
||
);
|
||
|
||
// 封装请求方法
|
||
const request = {
|
||
/**
|
||
* GET请求
|
||
* @param {string} url 请求地址
|
||
* @param {object} params 请求参数
|
||
* @param {object} config 额外配置
|
||
* @returns {Promise}
|
||
*/
|
||
get: (url, params = {}, config = {}) => {
|
||
return api({
|
||
url,
|
||
method: 'get',
|
||
params,
|
||
...config
|
||
});
|
||
},
|
||
|
||
/**
|
||
* POST请求
|
||
* @param {string} url 请求地址
|
||
* @param {object} data 请求数据
|
||
* @param {object} config 额外配置
|
||
* @returns {Promise}
|
||
*/
|
||
post: (url, data = {}, config = {}) => {
|
||
return api({
|
||
url,
|
||
method: 'post',
|
||
data,
|
||
...config
|
||
});
|
||
},
|
||
|
||
/**
|
||
* PUT请求
|
||
* @param {string} url 请求地址
|
||
* @param {object} data 请求数据
|
||
* @param {object} config 额外配置
|
||
* @returns {Promise}
|
||
*/
|
||
put: (url, data = {}, config = {}) => {
|
||
return api({
|
||
url,
|
||
method: 'put',
|
||
data,
|
||
...config
|
||
});
|
||
},
|
||
|
||
/**
|
||
* DELETE请求
|
||
* @param {string} url 请求地址
|
||
* @param {object} params 请求参数
|
||
* @param {object} config 额外配置
|
||
* @returns {Promise}
|
||
*/
|
||
delete: (url, params = {}, config = {}) => {
|
||
return api({
|
||
url,
|
||
method: 'delete',
|
||
params,
|
||
...config
|
||
});
|
||
},
|
||
|
||
/**
|
||
* 不加密的POST请求
|
||
* @param {string} url 请求地址
|
||
* @param {object} data 请求数据
|
||
* @param {object} config 额外配置
|
||
* @returns {Promise}
|
||
*/
|
||
postWithoutEncryption: (url, data = {}, config = {}) => {
|
||
return api({
|
||
url,
|
||
method: 'post',
|
||
data,
|
||
isEncrypted: true, // 标记为已加密,跳过加密处理
|
||
...config
|
||
});
|
||
}
|
||
};
|
||
|
||
// 暴露axios实例和请求方法
|
||
export { api, request };
|
||
export default request;
|