后台系统常有一组面向开发者 / 运维的辅助入口——语言词条、在线用户、跟踪日志之类。它们不属于业务导航, 却又需要随取随用。Known 把这类入口收进侧栏一个**「开发中心」分组**:内置项由框架提供,宿主项加一行特性即可注入。
这一组没有任何专属机制——它就是 Config.AddKnown 预先声明的一个普通菜单分组,页面用和其余侧栏菜单
完全相同的 [Menu] 特性、把 Group 指向它的 id 即可挂进来。声明一次、自动扫描、按角色裁剪,
理念与 [Menu] / [AppMenu] 一脉相承。
早期版本把它做成顶栏右上角的图标下拉,并配了一套
[DevMenu]特性 +IDevCenterProvider汇总器。[DevMenu]已删除:现在它既迁到了左侧菜单,分组本身又是普通菜单节点,那套专属机制就没有存在必要了。 手上还有[DevMenu]的代码改成[Menu(Group = "development")]即可,见下节。
它长什么样
登录进后台后,侧栏菜单树里会出现一个「开发中心」分组(图标 code),点开是一组子菜单,
默认包含三个框架内置项:
| 菜单项 | 路由 | 图标 | Order |
|---|---|---|---|
| 语言管理 | /dev/languages |
language |
1 |
| 在线用户 | /dev/online-users |
users |
2 |
| 跟踪日志 | /dev/trace-logs |
activity |
3 |
这三页是内置通用功能,落在框架的 Known/Platforms/ 下、各自标注了 [Menu(..., Group = "development")],
宿主无需实现、也不会去改它们。分组下没有任何当前用户可见项时,整组不渲染——空分组会被裁掉。
分组排序 order: 100,因此默认排在宿主自声明分组(如 system 用 90)之后、贴近菜单末尾。
往「开发中心」加一个页面
和加任何侧栏菜单项一模一样:给页面类加一行 [Menu],Group 填 development。
Config.AddModule 扫描到它,AuthProvider.BuildMenus 就把它归进该分组:
@page "/dev/demo-tools"
@attribute [Menu("示例自定义工具", Icon = "wrench", Group = "development", Order = 10)]
<div class="page-header">
<h2>示例自定义工具</h2>
</div>
@* 你的开发中心页面内容 *@
Order = 10 让它排在三个内置项(Order 为 1 / 2 / 3)之后。[Menu] 不可重复标注
(不同于可标多条的 [AppMenu]),一个页面就是菜单里的一个条目。
[Menu] 各字段语义:
name:菜单文字(构造参数,必填)。可作为国际化 key,经LocalService解析,无译文时回退原文。Icon:对应Icon组件的Type;留空回退circle。Group:所属上级分组。填Config.AddMenuGroup声明过的分组 id(这里是development);留空为根级菜单项; 也兼容旧的斜杠路径写法(如"系统管理/权限"),未命中已声明 id 时按路径自动归并、分组无自定义图标。Order:组内排序,越小越靠前。Roles:逗号分隔的可见角色(如"超级管理员,运维")——留空则所有已登录用户可见。Hidden:为true时不进菜单(页面路由仍可直达)。
地址不在
[Menu]上:菜单项的 URL 取自页面自身的@page(Route)路由。页面没有@page就没有链接。
按角色显隐
角色过滤发生在 AuthProvider.BuildMenus 里,和其余菜单项同一条路径:Roles 留空的项人人可见,
填了的只有命中当前用户角色的才可见。AdminLayout 鉴权拿到当前 User 后才装配菜单,
因此未登录或角色不匹配的项自然不会出现在分组里。
@attribute [Menu("运维控制台", Icon = "terminal", Roles = "超级管理员,运维", Order = 20)]
匹配是拿 Roles 拆出的每一项与 user.Role 精确相等比较(UserInfo.Role 是单个角色名,不是角色列表),
所以填的角色名要和登录时签发的那个字符串一致。
改名、换图标与整体接管
「开发中心」这个名字、code 图标、order: 100,都来自 Config.AddKnown 里的一次普通声明:
// Known/Config.cs —— AddKnown 内部
AddMenuGroup("development", "开发中心", icon: "code", order: 100);
AddMenuGroup 对相同 id 是后写覆盖,所以宿主想调整这一组,不必接管整套菜单,在 AddKnown 之后再声明一次即可:
services.AddKnown(option => { /* ... */ });
Config.AddModule(assembly);
// 改名换图标,仍挂在原位置
Config.AddMenuGroup("development", "运维工具", icon: "terminal", order: 100);
要完全主导这一组(混入自己的排序、拆并子分组),也可以给 AdminLayout 显式传 Menus——
一旦传了,框架就不再走 [Menu] 扫描装配,整棵菜单树由宿主说了算。
更细粒度的权限(数据库 / 配置驱动)则继承 AuthProvider 覆写 BuildMenus 或 CanSee(两者皆为 virtual),
页面与布局无需改动。
顶栏标题与命令面板
开发中心页面不需要逐页传 PageTitle:AdminLayout 会拿当前 URL 在整棵菜单树里匹配出链路
(ActiveTrail),取末位名称作顶栏标题、其余作面包屑。因为开发中心项本就是菜单树的普通节点,
这条推导对它们和业务页完全一致——进 /dev/languages 标题显示「语言管理」、面包屑显示「开发中心 ›」。
同理,Ctrl+K 命令面板的数据源也是同一棵菜单树,开发中心的项自动可检索跳转,无需另行登记。
装配链路
理解这条链有助于排查「加了特性却不显示」:
Config.AddModule<T>()扫描T所在程序集,凡带[Menu]的类型收进Config.MenuTypes。Config.AddKnown登记分组development(宿主自己的分组用Config.AddMenuGroup声明)。AuthProvider.BuildMenus(user)遍历MenuTypes:跳过Hidden与角色不匹配项,读页面@page作 URL, 按Group归入对应分组(未声明的按斜杠路径归并),本地化名称,组内按Order→名称排序, 子树为空的分组直接裁掉。AdminLayout鉴权拿到当前用户后调用它,把结果交给侧栏与命令面板渲染。
常见坑
- 加了
[Menu(Group = "development")]却不显示 → 标注所在程序集必须被Config.AddModule扫描到。 放在未登记的程序集里,特性收集不到。 - 分组整个不见了 → 该组下当前用户可见项为空,空分组会被裁掉。先确认至少一项的
Roles/Hidden放行。 Group填了"开发中心"→ 无效。Group填的是分组 iddevelopment;填中文会被当成未声明的路径分组另起一节点。- 图标空白 / 回退成圆点 →
Icon用了IconData未收录的名字会回退circle;用已存在的图标名(如language/users/activity/wrench/terminal)。 - 想给一个页面配多个菜单入口 →
[Menu]不可重复标注,一个页面一个条目。确要多入口就拆成多个页面,或显式传Menus。 Roles写了却不生效 / 一直不显示 → 角色是与user.Role精确相等的单值比较,不是集合包含;确认角色字符串与登录时一致。- 内置三页想删掉 / 改掉 → 它们是框架内置通用功能,落在
Known/Platforms/;一般不动。真要精简, 用各页[Menu]的Hidden/Roles让可见项清空、分组自然消失,或显式传Menus接管,不建议直接改框架内置页。 - 代码里还留着
[DevMenu]→ 该特性已从框架移除,编译不过。改成[Menu(..., Group = "development")], 原来的Url字段也不再有——地址一律取页面@page。
扫码关注公众号
扫码加入 QQ 群