Files
Yi.Admin/Yi.Ai.Vue3/src/routers/index.ts
2026-02-01 00:52:10 +08:00

157 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { NavigationGuardNext, RouteLocationNormalized } from 'vue-router';
import { ElMessage } from 'element-plus';
import { useNProgress } from '@vueuse/integrations/useNProgress';
import { createRouter, createWebHistory } from 'vue-router';
import { ROUTER_WHITE_LIST } from '@/config';
import { checkPagePermission } from '@/config/permission';
import { errorRouter, layoutRouter, staticRouter } from '@/routers/modules/staticRouter';
import { useUserStore } from '@/stores';
import { useDesignStore } from '@/stores/modules/design';
// 创建页面加载进度条,提升用户体验
const { start, done } = useNProgress(0, {
showSpinner: false,
trickleSpeed: 200,
minimum: 0.3,
easing: 'ease',
speed: 500,
});
// 创建路由实例
const router = createRouter({
history: createWebHistory(),
routes: [...layoutRouter, ...staticRouter, ...errorRouter],
strict: false,
scrollBehavior: () => ({ left: 0, top: 0 }),
});
// 预加载标记,防止重复预加载
const preloadedComponents = new Set();
/**
* 预加载路由组件
* 提前加载可能访问的路由组件,减少路由切换时的等待时间
*/
function preloadRouteComponents() {
// 预加载核心路由组件
const coreRoutes = [
'/chat/conversation',
'/chat/image',
'/chat/video',
'/chat/agent',
'/console/user',
'/model-library',
];
// 延迟预加载,避免影响首屏加载
setTimeout(() => {
coreRoutes.forEach(path => {
const route = router.resolve(path);
if (route.matched.length > 0) {
const component = route.matched[route.matched.length - 1].components?.default;
if (typeof component === 'function' && !preloadedComponents.has(component)) {
preloadedComponents.add(component);
// 异步预加载,不阻塞主线程
requestIdleCallback?.(() => {
(component as () => Promise<any>)().catch(() => {});
}) || setTimeout(() => {
(component as () => Promise<any>)().catch(() => {});
}, 100);
}
}
});
}, 2000);
}
// 首屏加载完成后开始预加载
if (typeof window !== 'undefined') {
window.addEventListener('load', preloadRouteComponents);
}
// 路由前置守卫
router.beforeEach(
async (
to: RouteLocationNormalized,
_from: RouteLocationNormalized,
next: NavigationGuardNext,
) => {
// 1. 开始显示进度条
start();
// 2. 设置页面标题
document.title = (to.meta.title as string) || (import.meta.env.VITE_WEB_TITLE as string);
// 3. 设置布局(使用 setTimeout 避免阻塞导航)
const layout = to.meta?.layout || 'default';
setTimeout(() => {
try {
const designStore = useDesignStore();
designStore._setLayout(layout);
} catch (e) {
// 忽略 store 初始化错误
}
}, 0);
// 4. 检查路由是否存在404 处理)
// 如果 to.matched 为空且 to.name 不存在,说明路由未匹配
if (to.matched.length === 0 || (to.matched.length === 1 && to.matched[0].path === '/:pathMatch(.*)*')) {
// 404 路由已定义在 errorRouter 中,这里不需要额外处理
}
// 5. 白名单检查(跳过权限验证)
if (ROUTER_WHITE_LIST.some(path => {
// 支持通配符匹配
if (path.includes(':')) {
const pattern = path.replace(/:\w+/g, '[^/]+');
const regex = new RegExp(`^${pattern}$`);
return regex.test(to.path);
}
return path === to.path;
})) {
return next();
}
// 6. 获取用户状态(延迟加载,避免阻塞)
let userStore;
try {
userStore = useUserStore();
} catch (e) {
// Store 未初始化,允许继续
return next();
}
// 7. Token 检查(用户认证)
if (!userStore.token) {
userStore.clearUserInfo();
return next({ path: '/', replace: true });
}
// 8. 页面权限检查
const userName = userStore.userInfo?.user?.userName;
const hasPermission = checkPagePermission(to.path, userName);
if (!hasPermission) {
ElMessage.warning('您没有权限访问该页面');
return next({ path: '/403', replace: true });
}
// 9. 放行路由
next();
},
);
// 路由跳转错误
router.onError((error) => {
// 结束全屏动画
done();
console.warn('路由错误', error.message);
});
// 后置路由
router.afterEach(() => {
// 结束全屏动画
done();
});
export default router;