Files
shanxia2.0/docs/superpowers/specs/2026-06-20-web-admin-portal-layout-design.md
T
2026-06-21 09:19:07 +08:00

286 lines
13 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.
# web-admin 门户化布局改造 — 设计文档
> **日期**: 2026-06-20
> **状态**: 已实现(2026-06-20)
> **范围**: `web-admin` 管理端框架(布局 + 菜单展示 + 权限联动)
> **UI 规范**: `docs/web-admin-ui-spec-fresh-mint.md`(主色 `#10b981`、Fresh Mint Token)
## 目标
将 web-admin 从「左侧全量菜单 + 顶栏工具条」改为 **一体化门户布局**:
1. 顶栏展示 Logo、「一体化门户」、6 个一级模块卡片(icon 上 / 名称下),选中态绿底白字。
2. 下方分左右:左侧为当前模块的二/三级菜单;右侧为 WorkTab + 内容区。
3. 移除顶栏的菜单折叠、面包屑、刷新按钮;移除【控制台】一级菜单。
4. 一级 / 二级 / 三级菜单仍按角色与 `menuCode` 权限过滤显示。
---
## 已确认决策
| # | 决策 | 说明 |
|---|------|------|
| D1 | **实现方式** | 新增 `MenuTypeEnum.PORTAL` 专用布局模式(方案 A),不魔改 TOP_LEFT / DUAL_MENU |
| D2 | **权限边界(0/1 模块)** | **始终保留顶栏卡片区域**:0 个可见模块时展示「暂无可用模块」空态;1 个模块时仍展示 1 张卡片 |
| D3 | **一级菜单清单** | CRM 管理、报销管理、地磅管理、运输管理、工作流管理、系统管理(共 6 个,不含控制台) |
| D4 | **一级展示顺序** | CRM → 报销 → 地磅 → 运输 → 工作流 → 系统(前端常量排序,不依赖路由文件注册顺序) |
| D5 | **控制台** | 从侧栏与顶栏卡片中移除;默认首页改为第一个有权限模块的首个可访问叶子页 |
| D6 | **顶栏右侧** | 保留用户菜单;全屏按钮保留;折叠 / 面包屑 / 刷新移除 |
| D7 | **侧栏** | 去掉 Logo 与「一体化门户」品牌区;去掉顶栏式折叠 icon;侧栏本身不新增折叠按钮(固定展开,宽度沿用 `menuOpenWidth` 默认 230px) |
| D8 | **OpenSpec 侧栏 icon** | 一级卡片有 icon;左侧二/三级菜单仍遵守「仅一级有 icon、更深层仅文字」(`SidebarSubmenu` 现有 `level === 0` 规则) |
---
## 非目标(本期不做)
- 设置面板中保留/切换 Left、Top、Mixed、Dual 等旧布局(PORTAL 为唯一产品布局,设置项可隐藏或锁定)
- 小程序端布局改造
- 权限后端 API 改造(沿用 `MenuProcessor` + `fetchGetMenuCodes`)
- 移动端完整适配(本期仅保证 `<800px` 不严重错位;横滑卡片 + 抽屉侧栏可作为 follow-up)
- Demo 菜单独立提升为一级模块
---
## 一、目标布局
```
┌────────────────────────────────────────────────────────────────────────┐
│ [Logo] 一体化门户 [CRM] [报销] [地磅] [运输] [工作流] [系统] [用户] │ ← ArtPortalHeader(全宽)
│ ↑ 卡片:icon 上 / 名称下;选中 #10b981 底 + 白字 │
├──────────────┬─────────────────────────────────────────────────────────┤
│ 二级 / 三级 │ ArtWorkTab(标签页) │
│ 菜单树 ├─────────────────────────────────────────────────────────┤
│ ArtPortal │ ArtPageContent(业务页面) │
│ Sidebar │ │
└──────────────┴─────────────────────────────────────────────────────────┘
```
**DOM 结构(`views/index/index.vue`)**
```
.app-layout-portal
├── #app-portal-header → ArtPortalHeader
└── #app-portal-body → flex row
├── #app-sidebar → ArtPortalSidebar(改造 art-sidebar-menu)
└── #app-workspace → flex column
├── #app-worktab → ArtWorkTab(从 art-header-bar 迁出)
└── #app-content → ArtPageContent
```
---
## 二、一级模块卡片(ArtPortalTopNav)
### 2.1 数据来源
- 输入:`menuStore.menuList` 经 **门户一级过滤器** 处理后的列表。
- 过滤器职责:
1. 排除控制台(`path === '/dashboard'` 或 `meta.title === '控制台'`)。
2. 排除 `meta.isHide === true` 的项。
3. 按 D4 常量顺序排序。
4. 若项无可见子节点且自身不可导航 → 不渲染卡片(与 `MenuProcessor.filterEmptyMenus` 结果一致)。
### 2.2 卡片 UI
| 属性 | 值 |
|------|-----|
| 布局 | 纵向 flex,居中;icon 在上(20–24px),标题在下(12–13px) |
| 间距 | 卡片之间 `gap: 8–12px`;卡片内 padding 约 `8px 12px` |
| 圆角 | `8px`(与规范按钮/输入圆角一致) |
| 默认 | 透明底;icon/文字 `--text` / `--text-muted` |
| 选中 | `background: var(--primary)`;icon 与文字 `#fff` |
| 激活判定 | 复用 `ArtMixedMenu.isMenuItemActive`:当前路由(或 `meta.activePath`)落在该模块子树内 |
### 2.3 交互
- 点击卡片:`router.push(module.redirect || firstAccessibleChildPath(module))`。
- 若当前路由已属于该模块:仅更新选中态,不强制跳转。
- **0 个可见模块**:顶栏卡片区展示 muted 文案「暂无可用模块」(不隐藏顶栏)。
- **1 个可见模块**:仍展示 1 张卡片(D2)。
### 2.4 一级模块与路由前缀映射
| 卡片名称 | 路由 path 前缀 | 路由模块 |
|----------|----------------|----------|
| CRM 管理 | `/crm` | `sqxp.ts` |
| 报销管理 | `/bx` | `sqxp.ts` |
| 地磅管理 | `/db` | `sqxp.ts` |
| 运输管理 | `/pl` | `pl.ts` |
| 工作流管理 | `/workflow` | `sqxp.ts` |
| 系统管理 | `/system` | `sqxp.ts` |
---
## 三、左侧菜单(ArtPortalSidebar)
### 3.1 渲染范围
- 仅渲染 **当前激活一级模块** 的 `children`(二/三级)。
- 激活模块解析:`const top = '/' + route.path.split('/')[1]`,在过滤后的一级列表中匹配。
- iframe 路由:沿用 `art-sidebar-menu` 现有 `findIframeMenuList` 逻辑。
### 3.2 移除内容
- 侧栏 header 区(Logo + 系统名)。
- 双列菜单 / 折叠按钮 / `menu-model` 遮罩(PORTAL 模式不启用 `DUAL_MENU` / `LEFT` 分支)。
- 顶栏菜单折叠联动(`visibleMenu` / `menuOpen` 切换对 PORTAL 无 UI 入口;`menuOpen` 保持 `true`)。
### 3.3 子菜单组件
- 继续复用 `SidebarSubmenu` + `filterSidebarRoutes`。
- `ElMenu` 默认展开:保留现有 `syncExpandedMenus` / `defaultOpenedMenus` 逻辑。
---
## 四、顶栏(ArtPortalHeader)
### 4.1 左侧
- `ArtLogo`(点击跳转默认首页,见 §五)。
- 文案「一体化门户」(`AppConfig.systemInfo.name`,已为该值)。
### 4.2 中间
- `ArtPortalTopNav`(flex-1,卡片可横向滚动;模块多时可加 `ElScrollbar`)。
### 4.3 右侧
- 全屏按钮(可选保留)。
- `ArtUserMenu`。
- **不渲染**:折叠、刷新、面包屑、快速入口、通知、语言、主题切换(与现 header 隐藏项一致)。
### 4.4 WorkTab 迁移
- 从 `art-header-bar/index.vue` 移除 `<ArtWorkTab />`。
- 挂载到 `#app-workspace` 顶部(`ArtWorkTab` 组件本身不改业务逻辑)。
- 样式:`worktab-strip` 的 `mb-3` 与 workspace padding 对齐 Fresh Mint 内容区 rhythm。
---
## 五、菜单数据与权限
### 5.1 现有链路(保持不变)
```
beforeEach → MenuProcessor.getMenuList()
→ filterMenuByRoles / filterMenuByCodes
→ filterEmptyMenus
→ menuStore.setMenuList
→ RouteRegistry.register
```
### 5.2 前端增强
| 位置 | 增强 |
|------|------|
| `MenuProcessor.processFrontendMenu` | 过滤掉 Dashboard / 控制台节点 |
| 新建 `utils/router/portal-menu.ts` | `PORTAL_TOP_MODULE_ORDER`、`filterPortalTopModules()`、`resolveActivePortalModule()`、`getFirstAccessiblePath()` |
| `menuStore.setMenuList` | 设置 `homePath` 时跳过 `/dashboard`,使用 `getFirstSecondLevelMenuPath`(已有) |
### 5.3 三级权限行为
| 层级 | 机制 |
|------|------|
| 一级卡片 | `MenuProcessor` 输出 + `filterPortalTopModules`;`filterMenuByCodes` level=1 保留「有可见子节点」的父级 |
| 二级 | `filterSidebarRoutes` |
| 三级 | `SidebarSubmenu` 递归 + `filterSidebarRoutes` |
### 5.4 后端菜单(backend 模式)
- SQL 隐藏或删除 Dashboard 对应 `sys_menu` 记录(与前端路由同步)。
- 确认 6 个一级菜单在 `sys_menu` 中 `parent_id` 指向门户 Web 根节点;运输管理已更名见 `docs/sql/20260620_pl_menu_rename_hide.sql`。
- 新增迁移脚本:`docs/sql/20260620_portal_hide_dashboard.sql`(建议)。
---
## 六、配置与枚举
| 文件 | 变更 |
|------|------|
| `enums/appEnum.ts` | 新增 `MenuTypeEnum.PORTAL = 'portal'` |
| `config/setting.ts` | 默认 `menuType: MenuTypeEnum.PORTAL`;`showMenuButton/showRefreshButton/showCrumbs: false` |
| `config/modules/headerBar.ts` | `menuButton/refreshButton/breadcrumb.enabled: false` |
| `store/modules/setting.ts` | 支持 PORTAL 分支(若 layout 判断分散在各组件,统一用 `isPortalLayout` computed) |
| `views/index/index.vue` | PORTAL 时使用新 DOM 结构;旧 LEFT 结构可保留供模板升级对比或后续删除 |
---
## 七、路由与首页
### 7.1 控制台处理
- `sqxp.ts`:Dashboard 路由设 `meta.isHide: true` 或整段注释/删除(与 SQL 同步)。
- 登录后默认跳转:第一个有权限模块的首个叶子页(如 `/crm/...`)。
### 7.2 路由顺序
- `routeModules` 合并顺序不影响 UI(由 `PORTAL_TOP_MODULE_ORDER` 控制)。
- 文档注释更新:`sqxp.ts` 头部说明去掉「控制台」。
---
## 八、样式要点
- 顶栏高度:建议 `64–72px`(略高于现 `h-15` 以容纳卡片)。
- 顶栏背景:`var(--default-bg-color)` / 白底 + 底部分割线 `var(--art-card-border)`。
- 一级卡片选中色:`var(--primary)` `#10b981`,**勿**使用小程序色 `#0a9f6d`。
- 下方 body:`height: calc(100vh - 顶栏高度)`;workspace 内容区 padding 沿用 `--mint-content-padding`。
- 侧栏宽度:230px(`menuOpenWidth` 默认值)。
---
## 九、测试计划
| 场景 | 预期 |
|------|------|
| super_admin | 6 张卡片 + 对应侧栏全量 |
| admin 仅有 bx 权限 | 顶栏仅 1 张「报销管理」卡片;侧栏仅 bx 子菜单 |
| admin 无任何业务 menuCode | 顶栏卡片区显示「暂无可用模块」 |
| 点击 CRM 卡片 | 路由进入 CRM 首个可访问页;卡片选中;侧栏切换为 CRM 树 |
| 从 CRM 页内跳转 bx 页 | 顶栏选中态自动切到报销 |
| 直接 URL 访问 `/pl/plan` | 运输卡片选中;侧栏为 pl 子树 |
| 控制台 `/dashboard` | 不可从菜单进入;直接访问按路由守卫处理(404 或重定向首页) |
| WorkTab | 开/关/切换标签正常;位置在内容区上方 |
| 侧栏二/三级 | 无 icon,仅文字(OpenSpec) |
---
## 十、影响文件清单
**新建**
- `components/core/layouts/art-portal-header/index.vue`
- `components/core/layouts/art-portal-top-nav/index.vue`
- `utils/router/portal-menu.ts`
- `docs/sql/20260620_portal_hide_dashboard.sql`(建议)
**修改**
- `views/index/index.vue`、`views/index/style.scss`
- `components/core/layouts/art-menus/art-sidebar-menu/index.vue`(PORTAL 分支)
- `components/core/layouts/art-header-bar/index.vue`(移除 WorkTab;或 PORTAL 下不再使用此组件)
- `enums/appEnum.ts`、`config/setting.ts`、`config/modules/headerBar.ts`
- `router/core/MenuProcessor.ts`
- `router/modules/sqxp.ts`
**可选**
- `components/core/layouts/art-settings-panel`:隐藏菜单布局切换或锁定 PORTAL
---
## 十一、风险
1. **WorkTab 样式错位**:迁移后需目视检查 tab-card / tab-google 两种 `tabStyle`。
2. **跨模块 deep link**:书签直达子页时,顶栏选中态依赖 `isMenuItemActive`,需覆盖 `meta.activePath`。
3. **模板升级**:Art Design Pro 上游更新 layout 时,PORTAL 为独立分支,合并冲突集中在 `index.vue`。
4. **窄屏**:6 卡片可能溢出,顶栏需横向滚动;侧栏 `<800px` 现有 fixed 策略待 follow-up。
---
## 十二、后续(非本期)
- 移动端:顶栏卡片横滑 + 侧栏抽屉。
- 设置面板清理旧 layout 选项。
- OpenSpec `ui-conventions` 补充「门户布局」章节(若需长期规范)。