react动态路由

发布于:2025-07-17 ⋅ 阅读:(16) ⋅ 点赞:(0)
router
// components/DynamicRouter.tsx
import { Suspense, useEffect, useState, lazy, Children } from "react";
import { useRoutes, Navigate } from "react-router-dom";

interface RouteItem {
  path: string;
  element: React.ReactNode;
  meta?: {
    title: string;
    roles?: string[]; // 权限控制
    requiresAuth?: boolean;
    hideInMenu?: boolean;
  };
  children?: RouteItem[];
}
interface MenuItem {
  path?: string;
  name: string;
  component?: string; // 组件路径(如:"@/pages/Dashboard")
  meta?: Omit<RouteItem["meta"], "hideInMenu">;
  children?: MenuItem[];
  index?: boolean;
}

// 模拟从后端获取菜单数据
const fetchMenuData = async (): Promise<MenuItem[]> => {
  // 实际项目中替换为 API 请求
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([
        {
          index: true,
          name: "home",
          component: "../views/Home",
          meta: { title: "控制台", requiresAuth: true,},
        },
        {
          path: "/admin",
          name: "Admin",
          meta: { title: "管理页", roles: ["admin"] },
          children: [
            {
              path: "/admin/user",
              name: "User",
              component: "@/pages/Admin/User",
              meta: { title: "用户管理" },
            },
          ],
        },
      ]);
    }, 500);
  });
};

// 权限校验组件
const AuthGuard = ({
  children,
  roles,
}: {
  children: any;
  roles?: string[];
}) => {
  const isAuthenticated = true; // 替换为实际登录状态
  const userRoles = ["user"]; // 替换为实际用户角色

  if (!isAuthenticated) return <Navigate to="/login" replace />;
  if (roles && !roles.some((role) => userRoles.includes(role)))
    return <Navigate to="/403" replace />;

  return children;
};

const Layout = lazy(() => import("../views/Layout"));
const Login = lazy(() => import("../views/Login"));

// 动态路由组件
export const DynamicRouter = () => {
  const [routes, setRoutes] = useState<RouteItem[]>([]);
  useEffect(() => {
    const initRoutes = async () => {
      // 1. 获取菜单数据
      const menuData = await fetchMenuData();
      // 2. 转换为路由配置
      const dynamicRoutes = menuData.map((menu) => convertMenuToRoute(menu));
      // 3. 添加静态路由(如登录页、404)
      const allRoutes = [
        { path: "/login", element: <Login></Login> },
        { path: "/", element: <Layout></Layout>, children: [...dynamicRoutes] },
        { path: "*", element: <div>404</div> },
      ];

      setRoutes(allRoutes);
    };
    initRoutes();
  }, []);

  // 递归转换菜单项为路由
  const convertMenuToRoute = (menu: any): RouteItem => {
    let element: React.ReactNode = <div>Default</div>;
    if (menu.component) {
      // 动态导入组件(懒加载)
      const Component = lazy(() => import(`${menu.component}`));
      element = (
        <Suspense fallback={<div>Loading...</div>}>
          <Component />
        </Suspense>
      );
    }
    const route: any = {
      element: <AuthGuard roles={menu.meta?.roles}>{element}</AuthGuard>,
      meta: menu.meta,
    };

    // 3. 处理 index 路由(不能有 path)
    if (menu.index) {
      route.index = true;
    } else {
      route.path = menu.path; // 非 index 路由才设置 path
    }

    // 4. 递归处理子路由
    if (menu.children) {
      route.children = menu.children.map((child: any) =>
        convertMenuToRoute(child)
      );
    }

    return route;
  };

  // 渲染路由
  const element = useRoutes(routes);
  console.log(routes);

  return <div className="router-view">{element}</div>;
};
app.tsx
// App.tsx
import { BrowserRouter } from 'react-router-dom';
import { DynamicRouter } from './router/index';

const App = () => {
  return (
    <BrowserRouter>
      <DynamicRouter>
      </DynamicRouter>
    </BrowserRouter>
  );
};

export default App;
main.tsx

import { createRoot } from 'react-dom/client'
import './App.css'
import App from './App'

createRoot(document.getElementById('root')!).render(
  <App></App>
)


网站公告

今日签到

点亮在社区的每一天
去签到