- 守卫先经 /profile 校验 token 有效后才放行受保护路由,无效 token 直接跳登录并带 redirect 回跳 - DashboardLayout 用户信息就绪前只渲染 spinner,不渲染后台内容 - manager 路由增加 requiresAdmin 校验(role >= 10),非管理员重定向回仪表盘 - 401 拦截器改用 router.push 软跳转,避免整页刷新与双重跳转 - pinia 先于 router 安装;登录页支持 redirect 参数回跳原目标
65 lines
2.1 KiB
TypeScript
65 lines
2.1 KiB
TypeScript
// src/api/client.ts
|
|
import axios from 'axios'
|
|
import type { AxiosError, InternalAxiosRequestConfig } from 'axios'
|
|
import { useAuthStore } from '@/stores/auth'
|
|
import router from '@/router'
|
|
|
|
const baseURL = import.meta.env.VITE_API_BASE_URL || '/api'
|
|
if (import.meta.env.DEV) { // Vite 的方式判断开发环境
|
|
console.log(`[Request] API Base URL: ${baseURL}`);
|
|
} else if (process.env.NODE_ENV === 'development') { // Vue CLI 的方式判断开发环境
|
|
console.log(`[Request] API Base URL: ${baseURL}`);
|
|
}
|
|
|
|
const service = axios.create({
|
|
baseURL: baseURL,
|
|
timeout: 6000,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
|
|
// 请求拦截器
|
|
service.interceptors.request.use(
|
|
(config: InternalAxiosRequestConfig) => {
|
|
const authStore = useAuthStore();
|
|
if (!authStore.token) {
|
|
authStore.loadTokenFromStorage();
|
|
}
|
|
if (authStore.token) {
|
|
config.headers.Authorization = `Bearer ${authStore.token}`;
|
|
}
|
|
return config;
|
|
},
|
|
(error: AxiosError) => {
|
|
console.error('Request error:', error);
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
|
|
|
|
// 响应拦截器
|
|
service.interceptors.response.use(
|
|
(response) => {
|
|
return response; // 只返回响应数据,便于后续使用
|
|
},
|
|
(error: AxiosError) => {
|
|
// 可以在这里处理响应错误的情况,例如统一处理错误信息, 提示用户等
|
|
console.error('Response error:', error);
|
|
// 这里可以做一些统一的错误处理,例如根据状态码判断是否 token 失效,并跳转到登录页面
|
|
if (error.response && error.response.status === 401) {
|
|
const authStore = useAuthStore();
|
|
authStore.clear();
|
|
// 守卫校验期间(尚未进入受保护路由)由守卫负责跳登录;
|
|
// 这里只处理已登录状态下 token 失效的情况,且不再用 location.href 硬刷新
|
|
const current = router.currentRoute.value;
|
|
if (current.matched.some(record => record.meta.requiresAuth)) {
|
|
router.push({ path: '/login', query: { redirect: current.fullPath } });
|
|
}
|
|
}
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
|
|
export default service;
|