Files
docs/superpowers/specs/2026-07-27-client-navigation-design.md
T

169 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Client Web 导航分级与移动侧栏设计
## 背景
`apps/client-web` 当前把业务总览、企业资料、企业成员、角色管理、菜单权限和头程订单全部渲染为同级侧栏链接。权威原型 `docs/悦/头程下单/柏悦客户端.html` 采用“一级业务域 + 二级具体任务”的导航结构,现有实现没有延续这一信息架构,也无法承接后续业务扩展。
Impeccable 验收还发现一个阻断性问题:720px 以下侧栏被隐藏,但页面上的菜单按钮没有打开移动导航的行为,导致移动端无法切换模块。
## 目标
- 将客户工作台的平铺菜单改为可扩展的两级分组导航。
- 保留现有路由、页面、文案、品牌颜色与 Element Plus 技术体系。
- 修复移动端导航不可用的问题。
- 让菜单节点显式携带图标与权限信息,避免依赖数组下标。
- 为后续真实权限集合接入保留清晰边界,不在本次伪造授权数据。
## 非目标
- 不新增原型中尚未实现的一键导入、快速下单、海外仓或财务结算入口。
- 不改变现有路由地址,不拆分当前 `ModuleView.vue` 页面。
- 不照搬原型的紫色渐变视觉,不重做登录、注册或业务内容页。
- 不实现服务端权限校验,也不把前端菜单隐藏当作安全控制。
- 不新增测试框架;当前项目尚未声明测试脚本。
## 导航信息架构
采用两级分组,业务总览保留直接入口:
```text
业务总览 /
头程业务 可展开分组
└── 头程订单 /orders/first-leg
企业管理 可展开分组
├── 企业资料 /company/profile
├── 企业成员 /organization/members
├── 角色管理 /organization/roles
└── 菜单权限 /organization/permissions
```
分组遵循原型的结构原则,但只呈现当前真实存在的客户端路由。后续功能只有在页面与路由实现后,才加入对应业务分组。
## 组件与数据结构
### 导航配置
新增客户端本地导航配置 `src/navigation/clientNavigation.ts`。定义客户端专用的 `ClientNavigationNode`,包含:
- 稳定 `key`;
- 用户可见 `title`;
- Lucide 图标组件;
- 可选 `path`;
- 可选 `permission`;
- 可选 `children`。
共享包中的 `NavigationItem` 仍保持单层公共模型,避免为了单个应用的壳层结构扩大跨项目契约。
### 侧栏菜单组件
新增独立的客户端侧栏菜单组件,使用 Element Plus 的 `el-menu`、`el-sub-menu` 与 `el-menu-item`:
- `active-path` 由当前路由提供;
- 父分组可展开和收起;
- 当前路由所在分组默认展开;
- 叶子菜单负责实际路由跳转;
- 图标来自节点配置,不依赖渲染顺序;
- 桌面与移动端复用同一组件和同一份导航数据。
本轮不额外实现展开状态持久化;Element Plus 在当前会话中维护用户开合状态,且当前路由所在分组始终可识别。持久化不是修复平铺结构与移动阻断所必需的能力。
### 应用壳层
`AppLayout.vue` 继续负责品牌、租户、用户和页面标题,同时增加:
- `mobileMenuOpen` 状态;
- 移动菜单按钮的 `aria-expanded`;
- 遮罩关闭;
- 路由切换后自动关闭;
- Escape 键关闭;
- 打开时阻止背景页面滚动;
- 退出确认取消时不产生未处理异常。
桌面侧栏保持固定宽度和现有绿色服务型视觉,不引入后台的深色侧栏,也不增加桌面折叠模式。
## 交互与状态流
### 桌面端
1. 路由加载后,菜单以 `route.path` 高亮对应叶子。
2. “业务总览”为直接入口。
3. “头程业务”和“企业管理”为分组触发器。
4. 当前页面属于某分组时,该分组保持展开并显示父级激活语义。
5. 用户可以独立展开或收起其他分组。
### 移动端
1. 720px 以下侧栏移出视口,而不是使用 `display: none` 永久移除。
2. 点击页面标题前的菜单按钮打开抽屉并显示遮罩。
3. 点击遮罩、按 Escape 或选择任一叶子路由后关闭抽屉。
4. 抽屉打开时禁止背景滚动,关闭或组件卸载时恢复。
5. 抽屉沿用完整租户导航,不维护第二份移动菜单。
### 权限
导航节点继续携带权限编码。当前模拟用户没有可靠的权限集合,因此静态阶段显示全部已实现菜单。真实认证接口返回权限集合后,再增加叶子过滤;父分组没有可访问子项时隐藏。服务端必须继续执行租户与权限校验。
## 视觉与可访问性
- 保留现有 `--primary`、`--navy`、`--border` 和 `--muted` 设计令牌。
- 一级分组与二级叶子通过缩进、字号、颜色和激活背景形成清晰层级。
- 分组触发器与移动菜单按钮使用真实可聚焦控件。
- 保持可见 `:focus-visible` 状态,不只依赖 hover 或颜色表达当前状态。
- 菜单触控目标不低于约 44px。
- 二级菜单文本允许单行省略,避免长租户业务名称破坏侧栏宽度。
- 侧栏导航区域允许纵向滚动;底部帮助区保持可达。
- 320px、720px 与桌面宽度均不能依赖水平滚动完成导航。
## 错误与边界处理
- 用户取消退出确认时保持当前页面,不产生控制台未处理异常。
- 移动抽屉在快速路由切换、浏览器后退和组件卸载后不得残留锁定滚动状态。
- 不存在或被移除的路由继续走现有兜底重定向。
- 当前不虚构客户经理电话、微信或帮助中心链接;帮助区保留为信息提示,后续有真实渠道后再做可执行入口。
- 权限为空或数据尚未加载时,本轮不提前隐藏菜单,避免模拟阶段出现无法恢复的空侧栏。
## 实施范围
预计修改或新增:
- `apps/client-web/src/navigation/clientNavigation.ts`
- `apps/client-web/src/components/ClientSidebarMenu.vue`
- `apps/client-web/src/layouts/AppLayout.vue`
- `apps/client-web/src/router/index.ts`
- `apps/client-web/src/styles/index.css`
仅当实现需要声明自动导入组件时,依赖现有插件自动生成声明,不手工编辑 `auto-imports.d.ts` 或 `components.d.ts`。
## 验收与验证
### 自动验证
在 `apps/client-web` 执行:
```bash
pnpm typecheck
pnpm build
```
项目当前没有 test 或 lint 脚本,因此不声称相应检查通过,也不为本次导航改动引入整套测试设施。
### Impeccable 验收
- 运行一次机械检测,目标覆盖本次变更的 Vue 文件。
- 桌面检查:一级分组、二级缩进、当前路由高亮、可见焦点和侧栏滚动。
- 720px 检查:断点切换时导航仍可达。
- 320px 检查:抽屉、遮罩、触控目标与页面内容不发生水平溢出。
- 浏览器可用时检查菜单打开、选择路由、遮罩关闭和 Escape 关闭;若浏览器不可用,明确记录回退证据,不伪装成已完成视觉实测。
## 完成标准
- 侧栏不再把全部功能放在一级菜单。
- 当前六个页面入口全部可达,路由地址不变。
- 桌面端当前页面与父分组状态清晰。
- 720px 以下菜单按钮能够打开和关闭真实导航抽屉。
- 导航图标不会因菜单调序或新增分组而错位。
- 未实现的原型功能不会作为无效入口出现。
- 类型检查、生产构建与 Impeccable 检测无未解释失败。