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

Context

admin2-components 是管理后台组件库,位于 src/framework/sideMenu/。组件需要:

  • 基于 antd@6 Menu 实现三级菜单(antd 原生支持内嵌三级)
  • 实现全局手风琴展开(最多同时展开一个分支,避免滚动条过长)
  • 响应式固定模式(窄屏下覆盖式抽屉 + 遮罩,仅移动端生效)
  • 受控组件模式,菜单数据由外部注入

详细行为规范见 openspec/docs/old-refactors/side-menu/ 下的 spec.md、adr.md、task.md。

Goals / Non-Goals

Goals:
- 实现 P0:基础三级菜单 + 全局手风琴展开
- 实现 P1:响应式固定模式 + 遮罩

Non-Goals:
- P2 自定义滚动条、P3 动效细节留作后续迭代
- 不实现菜单数据请求(由宿主提供已合并的树结构)
- PC 端不启用覆盖模式(固定模式仅移动端生效)

Decisions

1. 三级菜单采用 antd 原生实现

决定:利用 antd@6 Menu 的 items + children 嵌套结构,支持 key > label > children > key > label > children 三级结构。

原因:antd@6 已原生支持多级菜单展开,自定义第三级增加了不必要的复杂度。

2. 手风琴逻辑为全局互斥

决定:在 onOpenChange 中实现全局互斥——当用户展开一个新分支时,关闭之前展开的分支。

原因:避免多分支同时展开导致滚动条过长,影响导航效率。

实现:维护单一 openKeys 数组,展开时用新 key 替换数组。

3. 响应式固定模式仅移动端生效,使用样式自定义覆盖

决定
- 移动端(断点 < lg):侧边栏以 position: fixed 悬浮覆盖在内容之上,配合半透明遮罩
- PC 端:侧边栏固定在内容左侧,不覆盖

原因:使用 Drawer 组件会增加复杂性,且 PC 端不需要覆盖效果。

实现:使用 antd Layout.Sider 的 breakpoint 检测断点,配合 CSS 媒体查询控制覆盖样式。

4. 组件 Props 设计

interface CSideMenuProps {
  title: string;                          // 顶区标题
  tree: MenuTree;                         // 合并后的菜单树
  collapsed: boolean;                      // 是否收起
  curActivePaneKey?: string | number;    // 当前选中键
  panesOnShelf?: Array<{ key: string }>; // 已打开页签列表
  onClickMenuItem: (item: MenuItem) => void;
  onSetMenuCollapse: (collapsed: boolean | void) => void;
}

5. 统一组件导出入口

决定:创建 src/index.ts 作为组件库的统一导出入口。

结构
typescript export { CSideMenu } from './framework/sideMenu/CSideMenu'; // 后续组件陆续添加

6. example 极简结构

决定:example 保持极简,仅作为组件演示用途。

结构
example/ ├── App.tsx # 组件列表页 ├── main.tsx # 入口 ├── index.css # 全局样式 └── pages/ └── side-menu/ └── SideMenuPage.tsx # 组件示例页

原因:极简结构减少维护成本,重点在组件本身,Playwright 测试也依赖此示例。

7. vitest 测试文件放在 test 文件夹

决定:组件的 vitest 测试文件放在 test/unit/ 目录。

结构
test/ ├── setup.ts └── unit/ └── CSideMenu.test.tsx

Risks / Trade-offs

Risk Mitigation
全局手风琴限制了多分支展开的灵活性 当前业务场景以导航效率优先,后续可扩展为可配置
移动端覆盖模式可能影响内容区交互 遮罩层 z-index 正确设置,确保点击遮罩可关闭侧栏

Open Questions

无。所有决策已确认。