本规格描述壳层侧栏的**技术契约**:数据树、受控展开、响应式侧栏、antd Layout.Sider / Menu 的组合,以及分组内多级由**自研子菜单层**承载(现网实现可拆为独立组件,迁移时可替换实现,语义须一致)。业务目标与用户叙事以 prod.md 为准。
Layout.Sider、Menu(dark、inline)。写法:以行为、谓词与可验收参数为主,不锁定现网内部 class/state/函数名;实现可重命名,须保持等价语义。下列 antd/React 对外概念(如 openKeys、onBreakpoint)保留。
Menu.SubMenu 承载,含:id(优先)或 key,全树用于选中与展开(与 openKeys、selectedKeys 字符串化一致)。name(作分组标题)。items;其内为二级及以下节点。items → 可展开父节点,由自研子菜单层渲染,可递归;selectedKeys(与窗格 key 字符串一致)。id 一致(若项目统一用 id/key 之一,须全树一致)。可选:静态配置与接口合并、hiddenPaths、负数 id 等由宿主与数据层约定,侧栏以输入树已最终可用为前提。
Layout.Sider(须可观测)外层根容器高度随壳层占满;下列语义由内部 state、宿主 collapsed、Layout.Sider 的 breakpoint 与拖拽态共同决定(实现可用任意 class,须满足谓词):
| 语义 | 谓词(逻辑合取) |
|---|---|
| 窄屏固定覆盖态 | Sider 的 breakpoint 已打破(现网为 lg,与 ≥992px 媒体自绘轨大致同量级);内部记录与 onBreakpoint(true) 一致 |
| 窄屏且抽屉展开为全宽 | 窄屏固定覆盖态且宿主 collapsed === false |
| 窄屏且收起不占全宽 | 窄屏固定覆盖态且 collapsed === true(侧栏收至零宽量级,主内容区占满) |
| 自绘滚动条拖拽中 | 用户正在拖拽自绘滑块(与 §5 联动);根容器进入「滚动拖拽」语义,z-index 抬升 |
| 宽屏侧栏并排 | 非窄屏固定覆盖态;侧栏为常规并排,不施加「全宽抽屉压在主内容上」 |
断点联动(顺序与契约):
onBreakpoint(broken) 触发时,须调用 onSetMenuCollapse(broken),并使内部「窄屏固定覆盖」与 broken 同步。collapsed 与断点共同决定「全宽展开」是否出现。onBreakpoint → onSetMenuCollapse(broken) 与内部窄屏态同一布尔。openKeys(受控)Menu 的 onOpenChange(仅作用于一级分组 SubMenu);② 自研子菜单层的展开/收起回调(现网用独立回调名,避免与 Menu 内部拦截冲突)。_checkScrolling 等价逻辑,避免滑块高度为 0 或永久不可滚。selectedKeys 与当前选中键字符串形式一致。onClickMenuItem(item)(载荷为完整 item)。onSetMenuCollapse()(或等价收起),约 300ms 后 onClickMenuItem(item)。scrollTop 双向同步;支持鼠标与触摸(按下/移动/结束)。scrollTop(避免抖动);拖拽时按位移比例写回 scrollTop。用户感知与操作时序全貌见 prod.md §2–§3。本节给状态、时长、层级与参数目标,实现可用任意样式方案,须可验收。
| 项目 | 约定 |
|---|---|
| 可见(半透明) | 窄屏且侧栏展开且非拖拽滚动条的典型态;背景透明度目标约 0.2(黑底) |
| 近透明 | 自绘滚动条拖拽中:背景约完全透明,仍占据全屏命中区时需配合 z-index(见下)避免误点关闭 |
| 点击 | 非拖拽滚动条时点击遮罩 → onSetMenuCollapse()(或等价);拖拽中点击不收拢 |
层级(目标关系):窄屏下固定覆盖容器整体 z-index 高于主内容;拖拽中侧栏区域须高于近透明遮罩,以免滑块被挡。现网量级:容器拖拽态约 50、遮罩约 10、侧栏内容区约 2(实现可调整,须保持相对关系)。
出现条件(合取):视口 ≥ 约 992px(与 Sider 的 lg 断点同量级)且判定内容超高(§3 容差规则)且进入「显示轨」状态。
| 阶梯 | 命中谓词(语义) | 视觉与过渡目标 |
|---|---|---|
| 外围上下文 | 宽屏且溢出,未 hover 可滚菜单区;轨可 opacity 0→1 渐入(轨整体可带 delay,如约 0.3s delay + 0.2s 过渡量级) | 轨上滑块「细、深」,低对比 |
| 控件邻近带 | 指针进入可滚菜单区(.c-menu-scroll-show 等价语义),尚未指向滑块主热区 |
滑块条先变为中间强调色(如灰蓝),宽度可仍为窄条 |
| 主操作面 | 指针落在滑块热区(轨内滑块可拖区域) | 滑块加宽(如约 6px→12px)、更亮;background / width 过渡约 0.3s |
| 激活 / 拖拽中 | mousedown / touch 拖拽滑块 | 保持加宽与高亮;background-color 等 transition: 0s;遮罩按上表近透明 |
不需要滚动时:轨不可见且 pointer-events: none(或等价),不形成右侧死区;菜单区无为轨预留的额外右内边距。
需要滚动时:内容区保留纵向滚动(overflow-y: scroll 语义),右侧内边距(目标约 20px)为轨留位;轨宽约 20px,贴于可滚区右侧;滑块在轨内用 transform: translateY 与 scrollTop 同步。
与 JS 的边界:「是否溢出」「是否宽屏」由检测与媒体查询共同决定;拖拽中由 React state 切换根容器「拖拽」语义并联动遮罩样式。
prefers-reduced-motion:产品未强制时可注明「当前不约束」;若实现,应同步缩短或关闭展开与轨渐入。| 概念 / 常见 prop 名 | 说明 |
|---|---|
| 菜单树 | §2 |
| 顶区标题 | title |
collapsed |
宿主驱动;与 Sider collapsed 绑定 |
| 当前选中键 | curActivePaneKey → selectedKeys |
| 已打开列表 | panesOnShelf;叶子「已打开」判定见 §2 |
onSetMenuCollapse |
断点传入 boolean;遮罩、窄屏点叶子前等可为无参收起;语义见 §3–§4 |
onClickMenuItem |
叶子;载荷为完整 item;窄屏时序见 §4 |
验收标准见 task.md。