UI Design System
build dev

Menu

在模块或功能之间导航。中文惯用名:菜单。
稳定本期新增已接入

元信息

字段值
规范 IDcomponent.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 结构

DslNode
{
  "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 属性

属性类型默认值说明
itemsMenuItem[][]菜单项(可含表达式):{ key, label, icon?, badge?, dot?, group?, disabled?, children? }
mode`'inline''horizontal'`'inline'
selectedKeystring—受控选中项 key(可绑 ${state.x})
collapsiblebooleanfalse仅 inline:含 children 的目录项可展开 / 收起(点击不触发 onSelect)
defaultExpandedKeysstring[]—初始展开的目录 key(与 selectedKey 祖先链合并;展开状态非受控)
onSelect(key: string) => void—选中变化事件,传出选中项 key(Renderer 绑定 events.onSelect);目录项 toggle 不触发
className / stylestring / 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 接入后回填。