Menu
在模块或功能之间导航。中文惯用名:菜单。
稳定本期新增已接入
元信息
| 字段 | 值 |
|---|---|
| 规范 ID | component.menu |
| 名称 | Menu |
| 二级分类 | 导航(navigation) |
| 用途 | 在模块或功能之间导航 |
| 描述 | 中文惯用名:菜单。 |
预览
静态结构:渲染为 <nav data-slot="menu" data-mode>,每项外包 data-slot="menu-group-entry";inline 模式为纵向 flex-col(gap-0.5 p-2,子项按 depth 缩进 paddingLeft = 12 + depth × 16),horizontal 模式为横向 flex items-center gap-1。菜单项为 <button data-slot="menu-item">:选中项带 aria-current="page" 与 --menu-active-background / --menu-active-foreground 令牌底色,未选中为 text-muted-foreground + hover 变色,disabled 项置灰(opacity-50)且点击不触发 onSelect;icon 渲染于文字前(Icon size="sm"),badge 为右对齐圆角计数(menu-item-badge),dot 为危险色圆点(menu-item-dot),group 为分组标题(menu-group,相邻同组仅渲染一次);collapsible 的目录项点击只 toggle、右端 chevron(menu-item-chevron)展开时旋转 180°;在 Sidebar 内且侧栏折叠时自动降级为 icon rail(仅图标 + RailTooltip,含子项的目录经右侧浮层展示)。本预览为受控用法:selectedKey 由 state 驱动,点击经 events.onSelect 回写。
DSL 结构
{
"type": "Menu",
"props": {
"mode": "inline",
"selectedKey": "${state.selected}",
"items": [
{
"key": "dashboard",
"label": "工作台",
"icon": "home"
},
{
"key": "orders",
"label": "订单管理",
"icon": "list",
"badge": 12
},
{
"key": "stats",
"label": "数据统计",
"icon": "chart"
},
{
"key": "settings",
"label": "系统设置",
"icon": "settings",
"disabled": true
}
]
},
"events": {
"onSelect": {
"action": "setState",
"params": {
"selected": "${event}"
}
}
}
}何时用
何时使用
- 在模块或功能之间导航:Sider 内的纵向一级菜单、Header 内的横向菜单。
- 需要一个受控的「当前所在位置」:selectedKey 与路由路径同步,高亮当前项。
- 菜单项需要附带信息:图标、待办角标(badge)、危险状态圆点(dot)、分组标题(group)。
- 目录可折叠:collapsible 让含子项的目录点击展开 / 收起,层级不超过两级。
何时不用
- 同层内容视图切换(不换页,只换内容区):使用 Tabs。
- 表达当前位置与上级路径:使用 Breadcrumb。
- 临时操作集合(重命名 / 分享 / 删除):使用 DropdownMenu。
变体
| 变体 | 视觉形态 | 适用场景 |
|---|---|---|
| inline 纵向 | Sider 内纵向列表,选中项主色底 | 后台侧边主导航 |
| horizontal 横向 | Header 内横向排列 | 顶部模块导航 |
| 分组 | 分组标题 + 组内项(相邻同组只出一次标题) | 功能较多的后台菜单分区 |
| 带角标 / 状态点 | 右对齐计数徽标 / 危险色圆点 | 待办数量、异常提醒 |
| 可折叠目录 | collapsible 展开收起 + chevron 旋转 | 带二级目录的菜单 |
| icon rail | 侧栏折叠后仅图标 + hover 提示 | 给内容区让出宽度的紧凑模式 |
API 属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
items | MenuItem[] | [] | 菜单项(可含表达式):{ key, label, icon?, badge?, dot?, group?, disabled?, children? } |
mode | `'inline' | 'horizontal'` | 'inline' |
selectedKey | string | — | 受控选中项 key(可绑 ${state.x}) |
collapsible | boolean | false | 仅 inline:含 children 的目录项可展开 / 收起(点击不触发 onSelect) |
defaultExpandedKeys | string[] | — | 初始展开的目录 key(与 selectedKey 祖先链合并;展开状态非受控) |
onSelect | (key: string) => void | — | 选中变化事件,传出选中项 key(Renderer 绑定 events.onSelect);目录项 toggle 不触发 |
className / style | string / CSSProperties | — | 通用:根节点 class 合并 / 内联样式 |
使用规范
- Menu 是哑渲染组件:items 经表达式注入(如 ${data.menus}),不自动读取路由;选中态用 selectedKey + onSelect 闭环接 navigate 动作。
- 层级不超过两级;需要第三级说明信息架构需要重构,而不是继续嵌套。
- 菜单文案用名词短语(「订单管理」),与页面标题保持一致,便于用户建立位置感。
- 角标只用于需要即时处理的计数;纯装饰信息不放 badge。
- 折叠侧栏场景下菜单自动降级为 icon rail,此时必须保证每个一级项都有 icon。
正例
- ✓ 后台侧栏:Menu inline + selectedKey 绑路由,onSelect 触发 navigate。
- ✓ 带待办的后台菜单:订单项 badge={12} 展示待处理数量,配置项 disabled 表示无权限。
反例
- ✕ 用 Menu 做同页内容切换(应使用 Tabs)。
- ✕ 把层级做到三级以上、靠缩进表达(应拆分信息架构或改用 Tree)。
Design Token 映射
不适用:组件 tokenRefs 为空:样式 Token 数据尚未接入,三层映射(Primitive → Semantic → Component)待样式 Token 接入后回填。