mirror of
https://gitee.com/ccnetcore/Yi
synced 2026-04-12 04:06:37 +08:00
157 lines
4.4 KiB
TypeScript
157 lines
4.4 KiB
TypeScript
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;
|