feat: add portal home launcher, favorites UI, and WorkTab overflow eviction
Replace top-bar module navigation with a card home page, persist favorites via API, reopen favorite tabs on cold login, and enable portal multi-tabs with overflow-based eviction. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
7898d65437
commit
2df2ef0808
17 files changed
+2242
-157
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,251 @@
|
||||
# 门户菜单首页启动器 + 多页签(方案 1)
|
||||
|
||||
## 背景与目标
|
||||
|
||||
当前门户(`MenuTypeEnum.PORTAL`)将业务菜单放在顶栏(一级下拉 + 二级平铺/三级下拉),横向空间不足。本设计将导航改为:
|
||||
|
||||
1. **菜单首页**:按一级分区的可点卡片启动器,顶部为收藏区。
|
||||
2. **WorkTab 多页签**:打开的业务页以 Tab 管理;首页 Tab 固定不可关。
|
||||
3. **瘦顶栏**:不再展示二/三级业务菜单。
|
||||
|
||||
权威视觉仍遵循 Fresh Mint(`docs/web-admin-ui-spec-fresh-mint.md`)。本设计**取代**门户顶栏平铺/大面板作为主导航权威;`docs/portal-l1-select-flat-nav-demo.html`、`2026-08-01-portal-mega-menu-design.md` 不再作为目标交互。
|
||||
|
||||
## 已确认决策
|
||||
|
||||
| 项 | 决策 |
|
||||
|----|------|
|
||||
| 业务页顶栏菜单 | 不保留二/三级(方案 A) |
|
||||
| Tab 淘汰 | Tab 栏出现横向滚动后再开新页时,静默关掉最早打开的可关 Tab |
|
||||
| 未保存内容 | 淘汰/关闭时静默丢弃(与现 KeepAlive 一致) |
|
||||
| 首页辅助区 | 收藏菜单(非最近访问),置顶,上限 **10** |
|
||||
| 收藏操作 | 仅首页卡片星标收藏/取消 |
|
||||
| 登录预开 | 按收藏自动打开对应 Tab,**落点仍为首页** |
|
||||
| 总体方案 | 方案 1:瘦顶栏 + 首页启动器 + 重开 WorkTab + 后端持久化收藏 |
|
||||
|
||||
---
|
||||
|
||||
## §1 整体布局与导航收敛
|
||||
|
||||
### 壳层
|
||||
|
||||
```
|
||||
┌─ 顶栏 56px:Logo(→ 首页) 全屏(可选) | 用户 ─┐
|
||||
├─ WorkTab:🏠首页(固定) | 业务… × [▾ 批量关闭] ─┤
|
||||
└─ 内容区:菜单首页 / 业务页(keep-alive)──────────────────┘
|
||||
```
|
||||
|
||||
### SHALL
|
||||
|
||||
1. 默认布局仍为 `PORTAL`,导航模式改为「启动器 + 多页签」。
|
||||
2. 顶栏去掉 `ArtPortalModuleSelect`、`ArtPortalMegaMenu`(及仅服务于顶栏导航的移动端等价入口)。
|
||||
3. 顶栏保留:品牌 Logo、系统名(视宽度)、用户区;Logo 点击进入菜单首页(不关闭其它 Tab)。
|
||||
4. 强制开启 WorkTab;撤销门户下 `setWorkTab(false)` 与 `enforceSinglePageTabs()`。
|
||||
5. 菜单首页为新固定路由,作为门户 `homePath`;对应 Tab `fixedTab=true`。
|
||||
6. 业务页不再提供二/三级顶栏导航;跨功能仅通过回首页点卡片或切换已开 Tab。
|
||||
|
||||
### 与现状差异
|
||||
|
||||
| 项 | 现状 | 本设计 |
|
||||
|----|------|--------|
|
||||
| 顶栏菜单 | 一级下拉 + 二级平铺 | 无业务菜单 |
|
||||
| WorkTab | 门户强制关闭 | 强制开启 |
|
||||
| 默认首页 | 某模块首个业务页 | 菜单启动器页 |
|
||||
| 关全部后落点 | 模块首个二级 | 菜单首页 |
|
||||
|
||||
### 非目标(§1)
|
||||
|
||||
- 不改小程序导航。
|
||||
- 不恢复左侧栏为默认布局。
|
||||
- Demo 工作台(`/#/demo/console`)可保留,但不作为门户登录落点。
|
||||
|
||||
---
|
||||
|
||||
## §2 菜单首页(卡片拍平 + 收藏区)
|
||||
|
||||
### 页面结构
|
||||
|
||||
自上而下:
|
||||
|
||||
1. **收藏**(有数据才展示整块;空则隐藏):最多 10 张已收藏卡片。
|
||||
2. **按一级模块分区**:一级标题(不可点)+ 其下可点菜单卡片网格。
|
||||
|
||||
一级模块顺序:`PORTAL_TOP_MODULE_ORDER`(`filterPortalTopModules`)。
|
||||
|
||||
### 卡片拍平规则
|
||||
|
||||
数据源:当前用户权限菜单树。对每个一级下的二级节点:
|
||||
|
||||
| 情况 | 展示 |
|
||||
|------|------|
|
||||
| 二级不可导航且有可点子级 | **不展示二级**;其下可导航叶子直接作为卡片 |
|
||||
| 二级可导航且无子级 | 一张二级卡片 |
|
||||
| 二级可导航且仍有子级 | 各可导航叶子一张卡;若二级本身也可导航则额外一张二级卡 |
|
||||
| `isHide` / 无权限 | 不出现 |
|
||||
|
||||
实现复用并抽出与 `resolveMegaPanelL3Links`(`web-admin/src/utils/router/portal-menu.ts`)同语义的叶子收集。
|
||||
|
||||
卡片字段:标题、可选 `meta.icon`、路由 path、是否已收藏。
|
||||
|
||||
### 交互
|
||||
|
||||
1. 点击卡片主体 → 打开或激活对应 WorkTab。
|
||||
2. 点击星标 → 收藏/取消(`stopPropagation`);上限 10,超出 `ElMessage.warning`。
|
||||
3. 收藏顺序:按收藏时间升序;取消再藏视为新时间排到末尾。
|
||||
4. 同一菜单可同时出现在收藏区与原分区,星标状态同步。
|
||||
5. 无任何可点叶子:空态「暂无可用菜单」。
|
||||
|
||||
### 视觉
|
||||
|
||||
- Fresh Mint;一级标题使用 `h2.title-bar-heading`(必要时在 `App.vue` `panelTitleBarTextRules` 注册)。
|
||||
- 卡片:约 10px 圆角、浅边框、hover 薄荷绿底;星标区分已藏/未藏。
|
||||
- **不做**统计、图表、仪表盘指标——首页只做启动器。
|
||||
|
||||
### 路由
|
||||
|
||||
- 建议 path:`/home`,名称如 `PortalHome`,组件如 `views/portal/home/index.vue`。
|
||||
- 设为门户 `homePath`;登录成功与关全部 Tab 后均落此页。
|
||||
- **不**在业务菜单树中再挂「首页」菜单项(由布局固定注入 Tab)。
|
||||
|
||||
---
|
||||
|
||||
## §3 WorkTab、溢出淘汰与登录预开
|
||||
|
||||
### WorkTab
|
||||
|
||||
1. 门户开启 `showWorkTab`。
|
||||
2. 首页 Tab 固定:无关闭按钮;关闭全部/其他后仍保留并回首页。
|
||||
3. 保留:单关、右键关左/右/其他/全部、同 path 复用。
|
||||
4. 门户下 **隐藏 Tab「固定」**,避免与「收藏」概念混淆。
|
||||
5. KeepAlive:关页/淘汰时 exclude;静默丢缓存。
|
||||
|
||||
### 溢出淘汰(替换门户硬上限 10)
|
||||
|
||||
触发:准备 **新开** 可关 Tab 时(激活已有 Tab 不触发)。
|
||||
|
||||
1. 加入 `opened` 后测量:若 Tab 条 `scrollWidth > clientWidth`(出现横向滚动)。
|
||||
2. `while` 溢出:`evictOldestClosableTab`(`opened` 中最靠前的非 `fixedTab`)。
|
||||
3. 若仅剩固定 Tab 仍溢出:停止,允许极端窄屏轻微溢出。
|
||||
4. 窗口变窄导致溢出时 **不主动踢页**,仅在新开 Tab 时淘汰。
|
||||
5. 淘汰静默,不确认、不 toast。
|
||||
6. 门户路径下不再使用 `MAX_OPEN_TABS=10` 作为硬上限。
|
||||
|
||||
「一次性关闭」:WorkTab 提供关闭全部(保留首页)。
|
||||
|
||||
### 登录后自动打开收藏
|
||||
|
||||
```
|
||||
登录成功 → 菜单权限就绪 → GET 收藏列表(≤10,有序)
|
||||
→ 固定打开首页 Tab,路由落在首页
|
||||
→ 按收藏顺序依次 openTab(不切换 current)
|
||||
→ 每开一个后执行溢出淘汰(窄屏可能开不全,预期内)
|
||||
→ 最终 current / 路由停在首页
|
||||
```
|
||||
|
||||
补充:
|
||||
|
||||
| 项 | 约定 |
|
||||
|----|------|
|
||||
| path 无权限或路由无效 | 跳过,并可从收藏软删 |
|
||||
| 同用户刷新 | 优先恢复 WorkTab 本地 `opened`,**不再**按收藏全量重开 |
|
||||
| 冷登录 / 换用户 | 按收藏预开 |
|
||||
| 退出登录 | 清空会话 Tab;收藏留在服务端 |
|
||||
| 取消收藏 | 不自动关闭已打开的对应 Tab |
|
||||
|
||||
星标写入收藏成功后 **不**自动开 Tab;仅登录预开与用户点击卡片/其它导航会开 Tab。
|
||||
|
||||
---
|
||||
|
||||
## §4 收藏持久化与改造范围
|
||||
|
||||
### 表 `sys_user_menu_favorite`
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `id` | VARCHAR(64) UUID PK |
|
||||
| `user_id` | 当前用户 |
|
||||
| `menu_path` | 路由 path |
|
||||
| `order_num` | 顺序(越小越靠前;也可用 `create_time`) |
|
||||
| 公共字段 | `tenant_no`, `remark`, `create_by`, `create_dept_id`, `create_time`, `update_by`, `update_time`, `del_flag` |
|
||||
|
||||
- 默认 `del_flag=0`;按 `tenant_no` + 当前用户隔离。
|
||||
- 有效收藏唯一:`(tenant_no, user_id, menu_path)`(软删后可再藏)。
|
||||
- 上限 10:服务端写入前校验。
|
||||
- DDL:`docs/sql/migrations/` 增量 + 基线评估。
|
||||
|
||||
### API(示意,均需鉴权)
|
||||
|
||||
| 方法 | 路径 | 行为 |
|
||||
|------|------|------|
|
||||
| GET | `/web/admin/menu-favorites` | 当前用户有序收藏列表 |
|
||||
| POST | `/web/admin/menu-favorites` | `{ path }`;已存在幂等;超 10 拒绝 |
|
||||
| DELETE | `/web/admin/menu-favorites` | 按 `path` 软删 |
|
||||
|
||||
- 不信任客户端 `user_id` / `tenant_no`。
|
||||
- 写入宜校验 path 仍在用户菜单权限内。
|
||||
- 标题由前端菜单树解析,不必强依赖接口回填。
|
||||
|
||||
路由注册:`backend/app/api/__init__.py` 与 `app/main.py`。
|
||||
|
||||
### 前端模块
|
||||
|
||||
| 模块 | 职责 |
|
||||
|------|------|
|
||||
| `api/.../menuFavorite.ts` | 三个接口 |
|
||||
| Pinia `menuFavorite` | 列表、toggle、上限提示 |
|
||||
| `views/portal/home` | 收藏区 + 分区卡片 |
|
||||
| 登录/路由初始化 | 拉收藏并预开 Tab |
|
||||
| `worktab` | 溢出淘汰;取消门户单页强制 |
|
||||
| `ArtPortalHeader` | 瘦身 |
|
||||
| `views/index` | 开 WorkTab;`homePath` → `/home` |
|
||||
|
||||
### 必做清单
|
||||
|
||||
1. 后端表 + model/crud/schema + API。
|
||||
2. 前端首页、收藏 store/API、星标 UI。
|
||||
3. 门户瘦顶栏 + 开 WorkTab + 固定首页 Tab。
|
||||
4. 溢出淘汰;关全部回首页。
|
||||
5. 登录预开收藏,落点首页。
|
||||
6. SQL migration;`homePath` / 权限放行 `/home`。
|
||||
7. 视需要更新 OpenSpec `ui-conventions` / 工程说明中门户导航描述。
|
||||
|
||||
### 可停用(门户引用)
|
||||
|
||||
- 头栏对 `ArtPortalModuleSelect`、`ArtPortalMegaMenu` 等的引用(组件文件可暂留)。
|
||||
|
||||
### 非目标
|
||||
|
||||
- 小程序、默认改回左侧菜单布局。
|
||||
- 首页塞工作台图表。
|
||||
- Tab 右键收藏、最近访问。
|
||||
|
||||
### 风险
|
||||
|
||||
| 风险 | 处理 |
|
||||
|------|------|
|
||||
| 窄屏收藏 10 个开不全 | 接受;溢出静默淘汰 |
|
||||
| 菜单 path 变更 | 预开失败则剔收藏 |
|
||||
| worktab 本地持久化 vs 收藏预开 | 冷登录预开;刷新恢复 opened |
|
||||
| 顶栏去掉后找入口 | 固定首页 Tab + Logo 回首页 |
|
||||
|
||||
### 验收
|
||||
|
||||
1. 登录落首页;顶栏无二/三级业务菜单。
|
||||
2. 卡片仅为可点叶子;不可点二级不展示。
|
||||
3. 星标收藏 ≤10;同账号跨浏览器(服务端)仍在。
|
||||
4. 再登录:收藏对应 Tab 已开,当前在首页。
|
||||
5. Tab 可单关/全关;全关后仅剩首页。
|
||||
6. Tab 条出现横向滚动后再开新页,最早可关 Tab 被静默关闭。
|
||||
|
||||
---
|
||||
|
||||
## 界面模式
|
||||
|
||||
- 菜单首页为新型启动器页,可对照工作台轻量入口骨架,但内容仅为卡片分区(非 P01 标准业务列表)。
|
||||
- 业务列表页本身不因本变更改模式编号。
|
||||
|
||||
## 实现注意
|
||||
|
||||
- 最小必要改动;门户路径收敛导航,避免无关重构。
|
||||
- 字段一致性:若新增实体,CRUD/`_row_to_dict`/前端按 `docs/dev-field-consistency.md` 对齐。
|
||||
- 完成后按仓库约定更新 graphify(backend / web-admin 各自改动范围)。
|
||||
|
||||
Reference in new issue
Block a user