import { createRouter, createWebHashHistory } from "vue-router"; import { convertMenusToRoutes } from "./dynamicRoutes"; // 静态子路由:需要在 Main 框架内显示的页面 const staticMainChildren = [ { path: "/home", name: "Home", component: () => import("@/views/home/index.vue"), meta: { requiresAuth: true, title: "首页" } }, { path: "/user/userProfile", name: "userProfile", component: () => import("@/views/user/userProfile.vue"), meta: { requiresAuth: true, title: "用户中心" } }, { path: "/system/email", name: "SystemEmail", component: () => import("@/views/system/email/index.vue"), meta: { requiresAuth: true, title: "邮箱管理" } }, // 统一认证中心:接入应用(OIDC Client)注册管理 { path: "/system/authClient", name: "AuthClientManage", component: () => import("@/views/system/authClient/index.vue"), meta: { requiresAuth: true, title: "应用注册" } }, // 统一认证中心:第三方登录配置(全局 / 租户自带) { path: "/system/authIdp", name: "AuthIdpManage", component: () => import("@/views/system/authIdp/index.vue"), meta: { requiresAuth: true, title: "第三方登录" } }, // 统一认证中心:租户登录策略(验证方式/会话时长/1号1机) { path: "/system/authConfig", name: "AuthConfigManage", component: () => import("@/views/system/authConfig/index.vue"), meta: { requiresAuth: true, title: "登录策略" } }, // 审批流配置(平台侧:域名申请等管控场景) { path: "/system/approval", name: "PlatformApprovalFlow", component: () => import("@/views/system/approval/index.vue"), meta: { requiresAuth: true, title: "审批流配置" } }, { path: "/tools/passwordStore", name: "PlatformPasswordStore", component: () => import("@/views/tools/passwordStore/index.vue"), meta: { requiresAuth: true, title: "密码存储" } }, { path: "/website/setting", name: "PlatformWebsiteSetting", component: () => import("@/views/website/site/setting/index.vue"), meta: { requiresAuth: true, title: "站点设置" } }, { path: "/template/index", name: "PlatformTemplateManage", component: () => import("@/views/template/index.vue"), meta: { requiresAuth: true, title: "官网模板管理" } }, { path: "/template/tags", name: "PlatformTemplateTags", component: () => import("@/views/template/tags.vue"), meta: { requiresAuth: true, title: "标签调用说明" } }, // 兼容拼写错误的路径重定向 { path: "/apps/erp/dashborad", redirect: "/apps/erp/dashboard" } ]; // 静态路由:登录页独立、home 导航门户独立、404 页面独立 const staticRoutes = [ { path: "/login", name: "Login", component: () => import("@/views/login/index.vue"), meta: { requiresAuth: false } }, { path: "/register", name: "Register", component: () => import("@/views/login/register.vue"), meta: { requiresAuth: false } }, { path: "/forget", name: "ForgetPassword", component: () => import("@/views/login/forget.vue"), meta: { requiresAuth: false } }, // 兼容路径拼写错误:dashborad -> dashboard { path: "/apps/erp/dashborad", redirect: "/apps/erp/dashboard" }, { path: "/:pathMatch(.*)*", name: "NotFound", component: () => import("@/views/404/404.vue"), meta: { requiresAuth: false } } ]; const router = createRouter({ history: createWebHashHistory(), routes: staticRoutes }); let dynamicRoutesAdded = false; let dynamicRoutesData = []; let routesLoadingPromise = null; export function resetDynamicRoutes() { dynamicRoutesAdded = false; routesLoadingPromise = null; } export async function loadAndAddDynamicRoutes() { if (routesLoadingPromise) { return routesLoadingPromise; } if (dynamicRoutesAdded) { return Promise.resolve(); } routesLoadingPromise = (async () => { try { const { useMenuStore } = await import("@/stores/menu"); const menuStore = useMenuStore(); const menuData = await menuStore.fetchMenus(); if (menuData && menuData.length > 0) { addDynamicRoutes(menuData); dynamicRoutesAdded = true; routesLoadingPromise = null; return Promise.resolve(); } else { dynamicRoutesAdded = true; routesLoadingPromise = null; return Promise.resolve(); } } catch (error) { console.error('加载动态路由失败:', error); dynamicRoutesAdded = true; routesLoadingPromise = null; throw error; } })(); return routesLoadingPromise; } // 核心修改:移除扁平化,直接使用嵌套菜单生成路由 function addDynamicRoutes(menus) { if (!menus?.length) { return; } // 直接转换嵌套菜单为嵌套路由(不再扁平化) const dynamicRoutes = convertMenusToRoutes(menus); if (router.hasRoute('Main')) { router.removeRoute('Main'); } // 重新添加主路由,合并静态子路由和动态路由 router.addRoute({ path: "/", name: "Main", component: () => import("@/views/Main.vue"), redirect: "/home", meta: { requiresAuth: true }, children: [...staticMainChildren, ...dynamicRoutes] // 合并静态和动态子路由 }); dynamicRoutesAdded = true; } function findRouteByName(routes, routeName) { for (const route of routes) { if (route.name === routeName) { return route; } if (route.children) { const found = findRouteByName(route.children, routeName); if (found) { return found; } } } return null; } function findFirstValidRoute(routes) { for (const route of routes) { if (route.component) { return route; } if (route.children && route.children.length > 0) { const childRoute = findFirstValidRoute(route.children); if (childRoute) { return childRoute; } } } return null; } router.beforeEach(async (to, from, next) => { const token = localStorage.getItem("token"); const publicPaths = ["/login", "/register", "/forget"]; if (publicPaths.includes(to.path)) { if (token) { if (!dynamicRoutesAdded) { await loadAndAddDynamicRoutes(); } next({ path: "/home" }); } else { next(); } return; } if (!token) { next({ path: "/login", query: { redirect: to.path } }); return; } if (!dynamicRoutesAdded) { try { await loadAndAddDynamicRoutes(); } catch (error) { console.error('动态路由加载失败:', error); // token 无效时跳转登录 if (error?.message === 'token无效' || error?.response?.status === 401) { next({ path: '/login' }); return; } } // 路由加载后重新导航,确保路由匹配正确 next({ path: to.path, replace: true }); return; } next(); }); export default router;