Skip to content

动态路由配置 ​

隐藏侧边菜单 ​

Umi Max 的 ProLayout 是根据routes 里的name 字段来生成侧边栏菜单的,没有name 的路由不会出现在菜单里,但页面本身仍然可以访问(比如直接输 URL 或通过代码跳转)

  • 下面table不写了name,这样侧边栏就不渲染了

  • 里面appConfig 这段代码的作用就是 开启 antd 插件,并让 message 组件通过<App> 上下文工作,同时限制同时只弹 1 条提示 。

tsx
import { defineConfig } from "@umijs/max";

export default defineConfig({
  antd: {
    appConfig: {
      message: {
        maxCount: 1,
      },
    },
  },
  access: {},
  model: {},
  initialState: {},
  request: {},
  layout: {
    title: "@umijs/max",
  },
  routes: [
    {
      path: "/login",
      component: "./Login",
      layout: false,
    },
    {
      path: "/",
      redirect: "/home",
    },
    {
      name: "首页",
      path: "/home",
      component: "./Home",
      wrappers: ["@/wrappers/auth"],
    },
    {
      name: "权限演示",
      path: "/access",
      component: "./Access",
      wrappers: ["@/wrappers/auth"],
    },
    {
      path: "/table",
      component: "./Table",
      wrappers: ["@/wrappers/auth"],
    },
  ],
  npmClient: "pnpm",
  utoopack: {},
});

动态侧边菜单 ​

(1) 新建 src/config/routes.ts ​

ts
// 路由配置(纯数据,供 .umirc.ts 与运行时代码共用)
// 注意:此文件不能引入 @umijs/max 等仅构建期可用的模块
const routes = [
  {
    path: "/login",
    component: "./Login",
    layout: false,
  },
  {
    path: "/",
    redirect: "/home",
  },
  {
    name: "首页",
    path: "/home",
    component: "./Home",
    wrappers: ["@/wrappers/auth"],
  },
  {
    name: "权限演示",
    path: "/access",
    component: "./Access",
    wrappers: ["@/wrappers/auth"],
  },
  {
    name: "CRUD 示例",
    path: "/table",
    component: "./Table",
    wrappers: ["@/wrappers/auth"],
  },
  {
    name: "用户中心",
    path: "/user",
    component: "./User",
    wrappers: ["@/wrappers/auth"],
  },
  {
    path: "/blog",
    name: "博客系统",
    wrappers: ["@/wrappers/auth"],
    routes: [
      {
        path: "/blog/article",
        name: "文章管理",
        component: "./Blog/Article",
      },
      {
        path: "/blog/tags",
        name: "标签管理",
        component: "./Blog/Tags",
      },
    ],
  },
];

export default routes;

(2) 修改.umirc.ts ​

  • 导出路由配置
tsx
import { defineConfig } from "@umijs/max";
import routes from "./src/config/routes";

export default defineConfig({
  antd: {
    appConfig: {
      message: {
        maxCount: 1,
      },
    },
  },
  access: {},
  model: {},
  initialState: {},
  request: {},
  layout: {
    title: "@umijs/max",
  },
  routes,
  npmClient: "pnpm",
  utoopack: {},
});

(3) 修改app.ts ​

tsx
// 运行时配置
import routes from "@/config/routes";
import mockMenuData from "@/mock/data";

type RouteConfig = {
  path?: string;
  name?: string;
  routes?: RouteConfig[];
};

// 从路由配置中递归提取 路径 -> 菜单名称 映射
function buildMenuNameMap(routes: RouteConfig[] = []): Record<string, string> {
  const map: Record<string, string> = {};
  routes.forEach((route) => {
    if (route.path && route.name) {
      map[route.path.replace(/^\//, "")] = route.name.trim();
    }
    if (route.routes) {
      Object.assign(map, buildMenuNameMap(route.routes));
    }
  });
  return map;
}

const menuNameMap = buildMenuNameMap(routes as RouteConfig[]);

type MenuItem = {
  path: string;
  name: string;
  children?: MenuItem[];
};

// 将平铺的路径数组转换为菜单树
function buildMenu(paths: string[]): MenuItem[] {
  const root: MenuItem[] = [];
  const nodeMap = new Map<string, MenuItem>();

  paths.forEach((p) => {
    const segments = p.split("/");
    let parentChildren = root;
    let currentPath = "";

    segments.forEach((seg, idx) => {
      currentPath = currentPath ? `${currentPath}/${seg}` : seg;
      let node = nodeMap.get(currentPath);
      if (!node) {
        node = {
          path: `/${currentPath}`,
          name: menuNameMap[currentPath] || seg,
        };
        nodeMap.set(currentPath, node);
        parentChildren.push(node);
      }
      if (idx < segments.length - 1) {
        node.children = node.children || [];
        parentChildren = node.children;
      }
    });
  });

  return root;
}

// 全局初始化数据配置,用于 Layout 用户信息和权限初始化
// 更多信息见文档:https://umijs.org/docs/api/runtime-config#getinitialstate
export async function getInitialState(): Promise<{ name: string }> {
  return { name: "@umijs/max" };
}

export const layout = () => {
  return {
    logo: "https://img.alicdn.com/tfs/TB1YHEpwUT1gK0jSZFhXXaAtVXa-28-27.svg",
    menu: {
      locale: false,
      // 从 mock 数据动态生成菜单
      request: async () => buildMenu(mockMenuData),
    },
  };
};