160 lines
6.1 KiB
JavaScript
160 lines
6.1 KiB
JavaScript
import { ref } from 'vue';
|
||
import axios from 'axios';
|
||
import { ContentTypeEnum } from './httpEnum';
|
||
import { useUserStore } from '../.vuepress/store/modules/user';
|
||
|
||
// 适配VuePress服务端渲染,仅在浏览器环境执行
|
||
const isBrowser = typeof window !== 'undefined';
|
||
// 协议自动适配
|
||
const baseHref = isBrowser ? (window.location.href.includes('https:') ? 'https:' : 'http:') : 'http:';
|
||
// 创建axios实例
|
||
const service = axios.create({
|
||
baseURL: 'https://health.ufutx.com/',
|
||
withCredentials: false,
|
||
timeout: 50000,
|
||
});
|
||
|
||
// 自定义请求配置(仅保留hideLoading扩展)
|
||
export const requestConfig = {
|
||
hideLoading: false
|
||
};
|
||
|
||
// 原生加载提示(极简版,适配VuePress)
|
||
let loadingInstance = null;
|
||
const showLoading = (msg = '加载中...') => {
|
||
if (!isBrowser || loadingInstance) return;
|
||
loadingInstance = document.createElement('div');
|
||
loadingInstance.style.cssText = `position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);padding:8px 16px;background:rgba(0,0,0,0.7);color:#fff;border-radius:4px;z-index:99999;font-size:14px;`;
|
||
loadingInstance.innerText = msg;
|
||
document.body.appendChild(loadingInstance);
|
||
};
|
||
const closeLoading = () => {
|
||
if (!isBrowser || !loadingInstance) return;
|
||
document.body.removeChild(loadingInstance);
|
||
loadingInstance = null;
|
||
};
|
||
|
||
// 原生全局提示(极简版)
|
||
export const showToast = (msg = '操作失败', duration = 2000) => {
|
||
if (!isBrowser) return;
|
||
const toast = document.createElement('div');
|
||
toast.style.cssText = `position:fixed;top:80%;left:50%;transform:translate(-50%,-50%);padding:8px 16px;background:rgba(0,0,0,0.7);color:#fff;border-radius:4px;z-index:99999;font-size:14px;pointer-events:none;`;
|
||
toast.innerText = msg;
|
||
document.body.appendChild(toast);
|
||
setTimeout(() => document.body.removeChild(toast), duration);
|
||
};
|
||
|
||
// 请求拦截器:核心保留Token、Content-Type、版本号
|
||
service.interceptors.request.use(
|
||
(config) => {
|
||
// 加载提示控制
|
||
if (!config.hideLoading) showLoading();
|
||
// 初始化请求头
|
||
if (!config.headers) config.headers = {};
|
||
// 默认Content-Type为JSON
|
||
config.headers['Content-Type'] = config.headers['Content-Type'] || ContentTypeEnum.JSON;
|
||
// 携带版本号
|
||
if (isBrowser) config.headers['Version'] = import.meta.env.VITE_BASE_API_VERSION;
|
||
// 携带Token(仅保留核心rt_token,联动Pinia)
|
||
if (isBrowser) {
|
||
const userStore = useUserStore();
|
||
const token = userStore.token || localStorage.getItem('rt_token') || '';
|
||
if (token) config.headers['Authorization'] = `Bearer ${token}`;
|
||
}
|
||
// 处理POST表单参数
|
||
if (config.method?.toLocaleUpperCase() === 'POST' && config.data) {
|
||
const contentType = config.headers['Content-Type'];
|
||
if (contentType === ContentTypeEnum.FORM_URLENCODED) {
|
||
config.data = qs.stringify(config.data);
|
||
}
|
||
}
|
||
return config;
|
||
},
|
||
(error) => {
|
||
closeLoading();
|
||
console.log('请求错误:', error);
|
||
return Promise.reject(error);
|
||
}
|
||
);
|
||
|
||
// 响应拦截器:精简状态码,仅保留核心业务逻辑
|
||
service.interceptors.response.use(
|
||
(response) => {
|
||
closeLoading();
|
||
const res = response.data;
|
||
// 成功码:仅保留0(后端统一成功标识,剔除冗余兼容)
|
||
if (res.code === 0) return Promise.resolve(res.data);
|
||
// 业务错误:code!==0统一提示
|
||
showToast(res.msg || res.message || '请求失败');
|
||
return Promise.reject(res);
|
||
},
|
||
(error) => {
|
||
closeLoading();
|
||
// 网络/超时错误
|
||
if (error.message?.includes('timeout')) {
|
||
showToast('网络超时,请稍后重试');
|
||
}
|
||
// 401未授权:核心鉴权逻辑,跳登录页(适配VuePress base路径)
|
||
else if (error.response?.status === 401 && isBrowser) {
|
||
showToast('登录已过期,请重新登录');
|
||
const userStore = useUserStore();
|
||
// 清除状态,跳登录页并记录跳转地址
|
||
userStore.resetStore();
|
||
localStorage.removeItem('rt_token');
|
||
const redirect = encodeURIComponent(window.location.href);
|
||
window.location.href = `/dma_handbook/login?redirect=${redirect}`;
|
||
}
|
||
// 其他错误
|
||
else {
|
||
showToast(error.message || '网络异常');
|
||
}
|
||
console.log('响应错误:', error);
|
||
return Promise.reject(error);
|
||
}
|
||
);
|
||
|
||
// 统一请求方法:极简封装,适配VuePress
|
||
const request = (config) => {
|
||
return new Promise((resolve, reject) => {
|
||
service
|
||
.request(config)
|
||
.then((res) => resolve(res))
|
||
.catch((err) => reject(err));
|
||
});
|
||
};
|
||
|
||
|
||
// 放在request.js文件末尾,showToast导出后、request默认导出前
|
||
/**
|
||
* 全局获取用户信息接口(每次进入页面调用)
|
||
* @param {object} userStore - Pinia用户仓库实例(传入避免重复实例化)
|
||
* @returns {Promise<boolean>} - 是否获取成功
|
||
*/
|
||
export const getUserInfo = async (userStore) => {
|
||
// 非浏览器环境/未登录,直接返回失败
|
||
if (typeof window === 'undefined' || !userStore.token) return false;
|
||
try {
|
||
const res = await request({
|
||
url: '/api/app/get/service/user/info', // 你的用户信息接口地址,baseURL会自动拼接
|
||
method: 'get',
|
||
hideLoading: true, // 隐藏加载提示,避免页面闪烁
|
||
});
|
||
// 按你的接口返回格式更新仓库
|
||
if (res && res.service_user_info) {
|
||
userStore.setUserInfo(res.service_user_info); // 更新用户信息
|
||
}
|
||
if (res && res.role_list) {
|
||
userStore.setRoles(res.role_list); // 更新权限角色
|
||
}
|
||
return true;
|
||
} catch (err) {
|
||
// 接口失败轻量提示,不阻塞页面
|
||
showToast('用户信息同步失败', 1500);
|
||
console.log('获取用户信息失败:', err);
|
||
return false;
|
||
}
|
||
};
|
||
|
||
|
||
export default request;
|