admin2-components 是管理后台组件库,位于 src/framework/sideMenu/。组件需要:
详细行为规范见 openspec/docs/old-refactors/side-menu/ 下的 spec.md、adr.md、task.md。
Goals:
- 实现 P0:基础三级菜单 + 全局手风琴展开
- 实现 P1:响应式固定模式 + 遮罩
Non-Goals:
- P2 自定义滚动条、P3 动效细节留作后续迭代
- 不实现菜单数据请求(由宿主提供已合并的树结构)
- PC 端不启用覆盖模式(固定模式仅移动端生效)
决定:利用 antd@6 Menu 的 items + children 嵌套结构,支持 key > label > children > key > label > children 三级结构。
原因:antd@6 已原生支持多级菜单展开,自定义第三级增加了不必要的复杂度。
决定:在 onOpenChange 中实现全局互斥——当用户展开一个新分支时,关闭之前展开的分支。
原因:避免多分支同时展开导致滚动条过长,影响导航效率。
实现:维护单一 openKeys 数组,展开时用新 key 替换数组。
决定:
- 移动端(断点 < lg):侧边栏以 position: fixed 悬浮覆盖在内容之上,配合半透明遮罩
- PC 端:侧边栏固定在内容左侧,不覆盖
原因:使用 Drawer 组件会增加复杂性,且 PC 端不需要覆盖效果。
实现:使用 antd Layout.Sider 的 breakpoint 检测断点,配合 CSS 媒体查询控制覆盖样式。
interface CSideMenuProps {
title: string; // 顶区标题
tree: MenuTree; // 合并后的菜单树
collapsed: boolean; // 是否收起
curActivePaneKey?: string | number; // 当前选中键
panesOnShelf?: Array<{ key: string }>; // 已打开页签列表
onClickMenuItem: (item: MenuItem) => void;
onSetMenuCollapse: (collapsed: boolean | void) => void;
}
决定:创建 src/index.ts 作为组件库的统一导出入口。
结构:typescript export { CSideMenu } from './framework/sideMenu/CSideMenu'; // 后续组件陆续添加
决定:example 保持极简,仅作为组件演示用途。
结构: example/ ├── App.tsx # 组件列表页 ├── main.tsx # 入口 ├── index.css # 全局样式 └── pages/ └── side-menu/ └── SideMenuPage.tsx # 组件示例页
原因:极简结构减少维护成本,重点在组件本身,Playwright 测试也依赖此示例。
决定:组件的 vitest 测试文件放在 test/unit/ 目录。
结构: test/ ├── setup.ts └── unit/ └── CSideMenu.test.tsx
| Risk | Mitigation |
|---|---|
| 全局手风琴限制了多分支展开的灵活性 | 当前业务场景以导航效率优先,后续可扩展为可配置 |
| 移动端覆盖模式可能影响内容区交互 | 遮罩层 z-index 正确设置,确保点击遮罩可关闭侧栏 |
无。所有决策已确认。