import router from '@/router'; import { auth } from '@/hooks/useAuth'; import { useMenuStore } from '@/stores/menuStore'; import { usePermissionStore } from '@/stores/permissionStore'; import { useTabsStore } from '@/stores/tabsStore'; const appTitle = import.meta.env.VITE_APP_TITLE || 'CPMS 运营平台'; /** 导航完成后设置页面标题,避免动态路由加载前误匹配 404 导致 title 闪烁 */ function updateDocumentTitle(title?: string) { document.title = title ? `${title} - ${appTitle}` : appTitle; } function getRedirectPath(queryValue: unknown): string | undefined { if (Array.isArray(queryValue)) return queryValue.find((item) => typeof item === 'string'); return typeof queryValue === 'string' ? queryValue : undefined; } /** * 全局路由守卫 * 核心流程: * 1. /login → 已登录则跳首页,未登录则放行 * 2. 未登录 → 跳转 /login * 3. 已登录但菜单未加载 → 加载菜单+权限 → 重新导航(确保动态路由生效) * 4. 已登录且菜单已加载 → 放行 * * 关键:当 loadMenu 动态注册路由后,必须用 next({ path, replace: true }) * 重新触发导航,否则当前导航仍使用旧路由表,会匹配到 404。 * 注意:不能用 next({ ...to, replace: true }),因为展开 to 会把旧的 * matched 信息带过去,导致仍然匹配到 /:pathMatch(.*)*。 */ router.beforeEach(async (to, _from, next) => { // ── /login 特殊处理 ── if (to.path === '/login') { if (auth.isLoggedIn()) { const { loadMenu, loaded, homePath, isRoutePathAvailable } = useMenuStore(); if (!loaded.value) { const { loadPermissions } = usePermissionStore(); await Promise.all([loadMenu(), loadPermissions()]); } if (!auth.isLoggedIn()) { next(); return; } const redirectPath = getRedirectPath(to.query.redirect); const targetPath = redirectPath && isRoutePathAvailable(redirectPath) ? redirectPath : homePath.value || '/404'; next(targetPath); } else { next(); } return; } // ── 未登录 → 跳转登录页 ── if (!auth.isLoggedIn()) { next({ path: '/login', query: { redirect: to.fullPath } }); return; } // ── 已登录:确保菜单和动态路由已加载 ── const { loadMenu, loaded } = useMenuStore(); if (!loaded.value) { const { loadPermissions } = usePermissionStore(); await Promise.all([loadMenu(), loadPermissions()]); // 权限获取失败时会清空会话,此时回登录页而不是继续进入业务页 if (!auth.isLoggedIn()) { next({ path: '/login', replace: true }); return; } // 动态路由刚注册,需要用新路由表重新匹配当前路径 next({ path: to.fullPath, replace: true }); return; } next(); }); router.afterEach((to) => { updateDocumentTitle(to.meta.title as string | undefined); // ── 路由跳转 → tab 来源 ── // 隐藏的路由(如详情页、编辑页)不进 tab,避免标签栏被噪声污染 // /login 也不进 tab if (to.meta?.hideInMenu || to.path === '/login') return; if (typeof to.meta?.title !== 'string') return; const componentName = (to.meta.componentName as string) || (to.name as string); if (!componentName) return; const { isRoutePathAvailable } = useMenuStore(); if (!isRoutePathAvailable(to.path)) return; const { addTab } = useTabsStore(); addTab({ path: to.path, name: componentName, title: to.meta.title, icon: to.meta.icon as string | undefined, }); });