动态路由配置
隐藏侧边菜单
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),
},
};
};