Skyroc Web Kit
Admin Layouts

静态路由菜单生成

从 TanStack Router staticData 生成 Admin Layouts 菜单树、快速索引和首页

静态模式适合菜单由前端路由文件维护的后台应用。路由文件声明 staticData,布局包读取 routeTree 后生成菜单树、快速索引和首页。

启用静态模式

setupAdminLayouts({
  defaultHome: '/home',
  defaultIcon: 'mdi:menu',
  menuCategories: {
    admin: {
      key: 'admin',
      layout: '/(admin)'
    }
  },
  routeMode: 'static',
  routeTree,
  storage: localStg
});

menuCategories 决定哪些 layout route 会参与菜单生成。上面的配置表示:

  • admin 是菜单分类 key。
  • /(admin) 是 TanStack Router 的 layout route id。
  • 该 layout route 下的子路由会被扫描并转换成菜单。

路由声明

每个需要进入菜单或快速索引的 route 都应该声明 staticData

export const Route = createFileRoute('/(admin)/home/')({
  component: Home,
  staticData: {
    i18nKey: 'route.home',
    title: 'home',
    menu: {
      badge: {
        type: 'normal',
        valueKey: 'home.updates'
      },
      icon: 'mdi:monitor-dashboard',
      order: 1
    }
  }
});

隐藏详情页也要声明 staticData。区别只是 menu.hidetrue,这样页面不会显示在菜单树里,但仍会进入快速索引,布局可以根据它找到 tab 信息、父级菜单和激活菜单。

export const Route = createFileRoute('/(admin)/system/user/$id')({
  component: UserDetail,
  staticData: {
    title: 'userDetail',
    menu: {
      activeMenu: '/system/user',
      hide: true
    }
  }
});

生成流程

useMenus().initMenus(userInfo)
  └─ menuGenerator.generate({ userInfo })
       └─ generateStaticMenus()
            ├─ createEmptyCategoryMaps()
            ├─ findLayoutRoute(menuCategories[*].layout)
            ├─ generateStaticLayoutMenus(layoutRoute)
            │    ├─ transformStaticRouteToMenu(route)
            │    ├─ generateStaticChildMenus(route)
            │    └─ menuNodeCallback(layoutRoute.id)
            └─ return { allMenus, quickReferenceMenus, home: defaultHome }

initMenus(userInfo) 是静态菜单的入口。认证完成后传入当前用户信息,生成器会在转换 route 时做权限过滤。

分类和 layout route

静态菜单不会扫描整棵 routeTree。它只会找 routeTree.children 中 id 命中 menuCategories[*].layout 的 layout route。

menuCategories: {
  admin: {
    key: 'admin',
    layout: '/(admin)'
  },
  monitor: {
    key: 'monitor',
    layout: '/(monitor)'
  }
}

生成结果会按分类写入:

Map {
  'admin' => GeneratedMenu[],
  'monitor' => GeneratedMenu[]
}

如果某个 layout route 没有配置到 menuCategories,它下面的路由不会参与该菜单系统。

staticData 转换规则

transformStaticRouteToMenu 会先读取 route.options.staticData。没有 staticData 的 route 会直接跳过。

输入字段输出到 GeneratedMenu
staticData.titletitle
staticData.i18nKeyi18nKey
staticData.menu.iconicon
staticData.menu.localIconlocalIcon
staticData.menu.orderorder
staticData.menu.typetype,默认 item
staticData.menu.badgebadge
staticData.menu.extraextra
route.fullPathkeypath,会先经过 normalizePath

normalizePath 会去掉非根路径末尾的 /。例如 /home/ 会变成 /home/ 会保持不变。

权限和隐藏菜单

静态模式会在生成菜单时调用:

hasRoutePermission(staticData, userInfo)

如果 route 没有权限,它不会进入菜单,也不会进入快速索引。父 route 没有权限时,它的子路由也不会继续生成。

menu.hide 的处理顺序不同:生成器会先把 route 写入 quickReferenceMenus,再判断是否从菜单树隐藏。因此隐藏页仍可用于:

  • 打开对应 tab。
  • 根据 activeMenu 激活左侧菜单。
  • 找到父级菜单链路。

children 和排序

静态菜单会递归读取当前 route 的 children。每一层都会按 menu.order 升序排序,未声明 order 时按 0 处理。

/(admin)
  ├─ /home        order: 1
  ├─ /system      order: 10
  │    └─ /user   order: 1
  └─ /about       order: 22

生成后同级菜单顺序只由同级 order 决定,父子层级不会互相比较。

静态模式下,menuNodeCallback 会在两个位置执行:

  • layout route 级别:给整个分类追加一级菜单或 divider。
  • 普通 route 级别:给某个菜单节点追加子菜单或 divider。

它只适合追加路由树之外的补充节点。普通页面菜单仍应放在 route 的 staticData.menu 中。没有补充节点时返回空数组。

import type { MenuNodeCallback } from '@skyroc/web-admin-layouts';

export const menuNodeCallback: MenuNodeCallback = routeId => {
  if (routeId !== '/(admin)') return [];

  return [
    {
      id: 'admin-divider',
      menu: {
        order: 9,
        type: 'divider'
      }
    }
  ];
};

回调返回的节点会走 createExtraMenu,支持 badgeextrachildren 和排序。非 divider 节点必须提供 path,否则不会生成菜单。

常见问题

问题原因和处理
路由能访问但菜单不显示检查 route 是否有 staticData.menu,以及父 route 是否被权限过滤。
隐藏详情页无法激活父菜单详情页需要声明 staticData.menu.hidestaticData.menu.activeMenu
某个 layout 下的路由都不显示检查 menuCategories[*].layout 是否等于 TanStack Router 的 layout route id。
菜单顺序不符合预期检查同级 menu.order,未声明时会按 0 排序。
badge 数字不会变化路由只声明 valueKey,数量更新要通过 useAdminMenuBadges 或导出的 badge action 写入。

On this page