# 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 检测无未解释失败。