| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- /**
- * 动态路由配置
- */
- import { lazy, Suspense } from "react";
- import { createBrowserRouter, Navigate, type RouteObject } from "react-router-dom";
- import MainLayout from "../layouts/MainLayout/index";
- import type { JeecgMenu } from "../types/menu";
- // 默认加载组件
- function LoadingPage() {
- return <div style={{ padding: "20px" }}>加载中...</div>;
- }
- // 默认页面(当组件路径不存在时)
- function DefaultPage({ title }: { title: string }) {
- return (
- <div style={{ padding: "20px" }}>
- <h2>{title}</h2>
- <p>页面开发中...</p>
- </div>
- );
- }
- // 使用 Vite 的 import.meta.glob 预加载所有页面组件
- // 这会在构建时扫描 src/pages 目录下的所有 .tsx 文件
- const pageModules = import.meta.glob("../pages/**/*.tsx");
- console.log("[Router] Available page modules:", Object.keys(pageModules));
- // 动态加载组件
- // 根据 component 配置动态导入 src/pages 下的组件
- // 例如: "content-model/index" -> src/pages/content-model/index.tsx
- function loadComponent(componentPath: string, title: string) {
- if (!componentPath) {
- return <DefaultPage title={title} />;
- }
- // 清理路径:移除开头的 /,移除文件后缀
- const cleanPath = componentPath
- .replace(/^\//, "")
- .replace(/\.(tsx|vue|jsx|js)$/, "");
-
- // 构建完整的模块路径
- const modulePath = `../pages/${cleanPath}.tsx`;
-
- console.log(`[Router] Loading: "${cleanPath}" (${title})`);
- console.log(`[Router] Module path: ${modulePath}`);
-
- // 检查模块是否存在
- const moduleLoader = pageModules[modulePath];
-
- if (!moduleLoader) {
- console.warn(`[Router] ✗ Module not found: ${modulePath}`);
- console.warn(`[Router] Available modules:`, Object.keys(pageModules));
- return <DefaultPage title={title} />;
- }
-
- // 使用 lazy 加载组件
- const Component = lazy(() =>
- moduleLoader()
- .then((module: any) => {
- console.log(`[Router] ✓ Loaded: ${cleanPath}`);
- return module;
- })
- .catch((error) => {
- console.error(`[Router] ✗ Failed to load: ${cleanPath}`, error);
- return {
- default: () => <DefaultPage title={title} />,
- };
- })
- );
- return (
- <Suspense fallback={<LoadingPage />}>
- <Component />
- </Suspense>
- );
- }
- // 将菜单转换为路由配置
- function menuToRoute(menu: JeecgMenu, isChild = false): RouteObject | null {
- // 跳过隐藏的菜单
- if (menu.hidden || menu.meta?.hideMenu) {
- return null;
- }
- // 处理路径
- let routePath = menu.path || "";
-
- // 如果是子路由且路径是绝对路径,只取最后一段作为相对路径
- if (isChild && routePath.startsWith("/")) {
- const segments = routePath.split("/").filter(Boolean);
- routePath = segments[segments.length - 1] || "";
- }
- const route: RouteObject = {
- path: routePath,
- };
- // 如果有子菜单
- if (menu.children && menu.children.length > 0) {
- const childRoutes = menu.children
- .map((child) => menuToRoute(child, true))
- .filter((r): r is RouteObject => r !== null);
- if (childRoutes.length > 0) {
- // 有子菜单,父路由作为容器,使用 Outlet
- // 获取第一个可见子菜单
- const firstChild = menu.children.find((child) => !child.hidden && !child.meta?.hideMenu);
-
- if (firstChild) {
- // 获取第一个子路由的相对路径
- const firstChildPath = firstChild.path || "";
- const segments = firstChildPath.split("/").filter(Boolean);
- const redirectPath = segments[segments.length - 1] || "";
- route.children = [
- // 默认重定向到第一个子菜单
- {
- index: true,
- element: <Navigate to={redirectPath} replace />,
- },
- ...childRoutes,
- ];
- } else {
- route.children = childRoutes;
- }
- } else {
- // 有 children 但都被隐藏了,当作叶子节点处理
- const title = menu.meta?.title || menu.name;
- const component = menu.component || "";
- // 跳过 layouts 组件
- if (component && !component.includes("layouts/")) {
- route.element = loadComponent(component, title);
- } else {
- route.element = <DefaultPage title={title} />;
- }
- }
- } else {
- // 没有子菜单,加载组件
- const title = menu.meta?.title || menu.name;
- const component = menu.component || "";
- // 跳过 layouts 组件
- if (component && !component.includes("layouts/")) {
- route.element = loadComponent(component, title);
- } else {
- route.element = <DefaultPage title={title} />;
- }
- }
- return route;
- }
- // 根据菜单数据生成路由
- export function generateRoutes(menus: JeecgMenu[]): RouteObject[] {
- const routes = menus
- .map((menu) => menuToRoute(menu))
- .filter((r): r is RouteObject => r !== null);
- return [
- {
- path: "/",
- element: <MainLayout />,
- children: [
- {
- index: true,
- element: <Navigate to={menus[0]?.path || "/content-model"} replace />,
- },
- ...routes,
- ],
- },
- ];
- }
- // 创建路由器(初始为空路由)
- export function createAppRouter(menus: JeecgMenu[]) {
- const routes = generateRoutes(menus);
-
- // 调试:打印路由路径结构
- console.log("=== Generated Routes ===");
- routes.forEach((route) => {
- console.log(`Route: ${route.path}`);
- if (route.children) {
- route.children.forEach((child) => {
- console.log(` - Child: ${child.path || "index"}`);
- if (child.children) {
- child.children.forEach((grandChild) => {
- console.log(` - GrandChild: ${grandChild.path || "index"}`);
- });
- }
- });
- }
- });
-
- return createBrowserRouter(routes);
- }
|