AdmSysV2【公共组件库】@前端(For Git Submodule)
edit | blame | history | raw

侧栏菜单(Side Menu)— 规格说明

1. 概述与范围

1.1 业务职能

  • 本组件承担**整站(管理后台)的主导航**:在典型布局中**常驻于视口左侧**,是用户从任意业务页跳转到其它功能模块的**一级入口**(与顶栏、中部多标签内容区共同构成「壳」;侧栏负责「去哪」,内容区负责「看什么」)。
  • 菜单项与**可打开的业务页**一一对应(具体路由、嵌入页、权限裁剪由宿主与数据层决定,见 §2、§6)。
  • 窄屏下仍承担同一职能,仅**呈现形态**改为覆盖式抽屉(§3.2),不改变「主导航」定位。

1.2 职责(能力概要)

在壳布局中提供**可折叠的纵向导航**:展示多级菜单树,标识当前选中项与已打开页签,在窄屏下以「全宽抽屉式」呈现并支持遮罩关闭。

1.3 非目标

  • 不在本组件内实现路由注册、权限过滤、菜单数据请求(由宿主提供树数据)。
  • 不重复阐述 **antd@6** 已提供的通用能力(Layout.SiderMenu 内联/深色主题等以官方文档为准)。

1.4 技术栈

实现目标:**React**,**antd@6**(自 v4.3 升级场景);SPEC 仅描述与壳子及自定义逻辑相关的约定。


2. 数据模型

2.1 菜单树(由宿主传入)

  • 顶层为**分组**列表(一级),每项包含子列表 items(二级及以下)。
  • 每个节点至少具备:
  • 标识id(优先)或 key,在整棵树中用于选中、展开状态;比较时按字符串语义相等即可。
  • 展示名name
  • 可选类型type,用于图标映射;常见取值见 §2.3;未匹配时使用默认图标。
  • 子节点:若存在非空 items,则该节点为**可展开**的父节点;否则为**叶子**(可点击打开页)。
  • 层级:须支持**至少三级**(一级分组 → 二级可展开节点 → 叶子),以合并逻辑与侧栏子菜单的递归结构为准;§2.3 所述静态配置多为「分组 → 叶子」二级,运行时以合并后的 tree 为准。

2.2 宿主提供的运行态

  • 当前选中页键:与某叶子或节点的 id/key 对应,用于菜单选中高亮及「子树选中」样式。
  • 已打开页签列表:用于在叶子上显示「已打开」样式(例如某 pane.key 与节点 id 一致则标记)。

2.3 静态路径配置(数据结构约定)

以下描述**静态路径骨架**的常见 JSON 形态,便于对齐字段语义(存放文件名与路径由项目自定)。

  • 根对象可含 projectName(工程标识)、treePaths(分组数组)、hiddenPaths(**不在侧栏展示**的页面映射,供其它入口按 pageName 打开;侧栏可不渲染此项,但应知晓 id 可能为负数等特殊键,以免选中/标签逻辑异常)。
  • treePaths 每一项(一级分组)idnameitems
  • items 内叶子(典型)idnamepageNamepath;可选 type(如 chartsetting 等)。若某节点再含非空嵌套 items,则为中间层,须按 §4.2 三级规则处理。
  • 合并后叶子可带业务/权限相关字段(由接口与宿主侧合并逻辑写入,如 forbidshow 在合并阶段已过滤);**侧栏只消费合并后的 tree**,不负责请求。

2.4 合并与权限(宿主职责,侧栏不实现)

  • 典型流程:接口返回的分组/条目与静态骨架在**宿主或数据层**合并,并按 forbidshow 等规则剔除不可见项(具体函数名与模块划分由项目自定)。
  • 合并后 type 缺省时常规范为 'normal'(与图标默认分支一致)。
  • 若沿用「静态配置 + 接口权限」形态,须在**宿主层**复现或等价实现合并;侧栏 SPEC 仍以「输入树已最终可用」为前提。

2.5 点击叶子时传递给宿主的载荷

  • 「打开页」回调应传入**完整菜单项对象**(至少包含宿主开页所需字段,如 idnamepathpageName),以便宿主增加标签、拼接构建版本等查询参数等与既有开页逻辑一致。

3. 布局与响应式

3.1 常规(宽屏)

  • 侧栏为固定**内容区宽度**的纵向区域(目标约 210px,含与滚动条占位相关的补偿时可由实现决定,但须避免内容被系统滚动条挤压错位)。
  • 侧栏可处于**展开**或**收起**状态;收起宽度为 0(不占可视内容区),由宿主控制 collapsed

3.2 断点与「固定」模式

  • 当视口宽度低于约定断点(与 lg 量级一致)时,进入**固定(fixed)**布局模式:
  • 侧栏行为接近**覆盖在内容之上的抽屉**:展开时占满可视宽度(或产品约定的全宽表现),并与宿主协作设置 collapsed
  • 宿主在断点变化时应被通知以同步折叠状态(例如 onSetMenuCollapse(broken) 语义)。

3.3 顶区标题

  • 侧栏顶部展示站点/产品标题(由宿主传入);区域宽度与下方菜单内容区对齐(含滚动条占位策略一致)。

4. 交互与状态(逻辑)

4.1 一级分组

  • 一级分组由 antd SubMenu 承载常规展示;若当前选中项落在该分组子树内,该分组须有**可区分的选中子树**样式(例如高亮父级)。

4.2 展开态(openKeys,受控扩展)

  • 内层 Menu:在 onOpenChange 中实现**同级手风琴**——用户**新展开**某节点时,**同一父级下**已展开的**兄弟**节点须关闭;用户主动收起时以传入的 openKeys 为准。(非 antd 默认,须自实现。)
  • 自定义子菜单层(三级等):展开某一 key 时从 openKeys 移除其**兄弟** key 再并入当前 key;关闭时仅移除该 key。展开/关闭后须在 DOM/动画稳定后**重新检测**是否需滚动条(允许短延迟,如 ~300–500ms)。

4.3 点击叶子

  • 宽屏:立即调用宿主的「打开页」回调。
  • 固定模式:先通知宿主**收起**侧栏,再在**短延迟**(约 300ms,与侧栏动画匹配)后调用「打开页」回调,避免动画与导航冲突。

4.4 滚动与自定义滚动条(逻辑)

  • 当菜单内容高度超过可视区域超过约 10px 时,视为**需要纵向滚动**,并切换「需滚动」状态(供 §5 样式与 §4.5 遮罩联动)。
  • 提供**自定义滚动条轨道与滑块**(非仅依赖系统滚动条):滑块高度与可视比例成正比;拖拽滑块与菜单 scrollTop 双向同步;支持鼠标拖拽与触摸拖拽;拖拽期间由 React 状态标记**滚动拖拽中**(与 §5 动效、§4.5 遮罩一致)。
  • 窗口 resize 时须更新滚动条占位宽度及滚动需求检测。

4.5 遮罩(逻辑)

  • 在**固定模式且侧栏展开**时显示遮罩;点击遮罩应通知宿主收起(若在滚动拖拽中则不应因遮罩点击关闭)。
  • 自定义滚动条拖拽过程中遮罩与 §5.3「滚动拖拽」、§5.2 遮罩层级一致;点击关闭语义以 §4.5 与 §5 联合为准。

5. 自定义样式与动效

本节描述**自研叠加层**(非 antd 默认 token 可完整表达)的视觉与动效契约;实现可用任意 class 命名,但须复现状态、时长与层级关系。

5.1 顶区标题条(Logo 区)

  • 固定高度(目标约 50px)、全宽、**主色实底**、白字加粗;过长文案 ellipsis
  • 与下方滚动区纵向衔接,滚动区高度为「剩余视高」(如 calc(100% - 顶区高度))。

5.2 固定模式与遮罩(非滚动拖拽)

  • 固定定位:侧栏容器覆盖视口;**全宽展开**时使用独立 class 将宽度拉满。
  • 遮罩层:默认隐藏;在固定模式且侧栏展开时显示,**半透明深色**(如 rgba 黑 ~0.2),铺满视口,**低于侧栏内容**的 z-index,点击触发收起(逻辑见 §4.5)。

5.3 自定义滚动条(宽屏,约 ≥992px)

出现与布局

  • 不需要滚动时:自绘轨道**不可见**(如 opacity: 0)且 pointer-events: none,避免挡操作。
  • 需要滚动时:菜单内容区增加**右侧内边距**(目标约 20px)为轨道留位;轨道贴侧栏**右缘**固定列宽(目标约 20px),**深底**与侧栏深色主题协调;轨道整体**淡入**可用 transition,并带 delay(与菜单展开/重排动画衔接,目标约 0.3s 延迟 + 0.2s 过渡量级)。
  • 实际滚动仍由内容区 overflow-y: scroll 承担;自绘滑块仅**视觉与拖拽**同步 scrollTop

滑块(thumb)

  • 默认较**细**(目标宽约 6px)、居中偏右、**圆角**条、色值深于轨道;background / width / margin 变化带过渡(目标约 0.3s),hover 时**加宽、变色**(目标宽约 12px、更高对比浅色)。
  • 滚动区域 hover(非必须点在滑块上):thumb 可先进入**中间色**态,再与滑块自身 hover 的**最亮**态区分层次。

拖拽中(与 §4.4 scrollDraging 等状态对应)

  • 容器进入「滚动拖拽」class 时:thumb 保持**展开宽度与高亮主色**,且对 background(及必要时相关属性)关闭 transition(0s),避免跟手时出现颜色/过渡迟滞。
  • 同时 遮罩若仍处于显示态:变为**全透明**,仍占位或可点,**提高 z-index**,使拖拽过程中交互意图与固定模式下的实心遮罩区分(与 §4.5 逻辑一致:不阻挡拖拽结束后的常规点击语义由实现统一)。

5.4 菜单深色主题上的局部覆盖(节选)

  • 一级 SubMenu 标题在 active/open/子树选中等态下**背景提亮**(rgba 白低不透明度阶梯)。
  • 叶子项:hover / selected 背景与右侧 Caret 箭头显隐、颜色变化带**短过渡**;**已打开**叶子的箭头常显,未打开则隐藏,逻辑见数据模型,样式与 §4 选中态一致。

5.5 可访问性与降级

  • 若产品无要求,可暂不实现 prefers-reduced-motion;迁移时建议评估:至少保证**键盘可达性**不与自定义滚动条 pointer-events 冲突。
  • 窄于 §5.3 断点时以系统滚动或全宽抽屉为主,自绘轨道可隐藏(与 §3.2 一致)。

6. 与宿主应用的契约

6.1 输入(概念)

概念 说明
菜单树 见 §2.1~§2.4
标题 顶区展示文案(常与接口站点名同源;与静态配置里的 projectName 可能不同源)
是否收起 宿主控制折叠
当前选中键 与节点 id/key 对齐;常见为**数值型菜单 id**(含 hiddenPaths 场景的负数 id)
已打开页列表 用于叶子「已打开」样式;项上 key 与菜单项 id 一致

6.2 输出(回调)

回调语义 时机
设置折叠 断点变化、用户点遮罩、叶子点击前(固定模式)等;参数可为 boolean(是否与断点「broken」对齐)或**无参**(切换/收起);宿主须兼容:**boolean 时设为对应折叠态,无参时按约定的切换语义处理**。
打开菜单项 用户点击叶子;载荷为 §2.5 菜单项对象;固定模式下在收起动画后再触发

6.3 壳布局集成(建议 props 映射,命名可改)

侧栏通常与主区域**兄弟**排列:一侧为侧栏,另一侧为 Layout(顶栏、多标签内容区等)。折叠状态应由宿主**单一数据源**驱动,并与顶栏等共享同一套折叠回调。

建议输入/输出与下列概念对齐(prop 名可重命名,语义须一致):

侧栏侧(示例名) 含义
title 顶区标题
tree 合并后的分组树
panesOnShelf 已打开页签列表(用于「已打开」样式)
collapsed 是否收起
curActivePaneKey 当前选中页键
onClickMenuItem 打开/激活标签页
onSetMenuCollapse 折叠:支持 boolean 与无参(见 §6.2)

7. 验收

验收标准见 task.md


8. 修订记录

日期 摘要
2026-04-07 初稿,抽象侧栏菜单行为契约
2026-04-07 目标栈明确为 React + antd@6;验收迁至 task.md
2026-04-07 对齐静态路径配置、壳集成与宿主合并/回调语义;去除具体仓库路径
2026-04-07 新增 §5 自定义样式与动效(滚动条/遮罩/Logo);宿主契约顺延为 §6
2026-04-07 新增 §1.1 业务职能(整站主导航与壳布局中的角色)