feat(layout): 多页签 keep-alive(子模块 + openspec change)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
950e13a4a8
commit
da6edcfcfd
2
openspec/changes/multi-tab-keep-alive/.openspec.yaml
Normal file
2
openspec/changes/multi-tab-keep-alive/.openspec.yaml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
schema: spec-driven
|
||||||
|
created: 2026-10-11
|
||||||
50
openspec/changes/multi-tab-keep-alive/design.md
Normal file
50
openspec/changes/multi-tab-keep-alive/design.md
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
## Context
|
||||||
|
|
||||||
|
多页签由 `stores/modules/tabs.ts` 按 `path` 追踪(持久化),`layouts/default.vue` 的 `activePath` watch 调 `addTab`,`<router-view />` 直接渲染。菜单页面均为 `index.vue`(`<script setup>` 无显式 `name`),故 keep-alive 无法按组件名区分。动机见 proposal.md - Why。
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
**Goals:**
|
||||||
|
|
||||||
|
- 布局内菜单页面切换页签保留状态,关闭页签销毁状态。
|
||||||
|
- keep-alive 按页面可配置:默认开启,页面可经路由 meta 关闭。
|
||||||
|
|
||||||
|
**Non-Goals:**
|
||||||
|
|
||||||
|
- 不缓存空白布局页(login/register/forgot-password/force-change-password/403,均不在本布局)。
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
### D1 — 缓存标识 = 组件 name(路径末段 kebab)
|
||||||
|
给 11 个菜单页面补 `defineOptions({ name })`,name 取路径末段 kebab-case(`/`→`dashboard`、`/system/user`→`user`、`/monitor/login-log`→`login-log`…),天然唯一无冲突。
|
||||||
|
- **备选**:用 SFC 文件名派生(全是 `index`)——冲突,否决。
|
||||||
|
|
||||||
|
### D2 — 默认缓存,路由 meta 可关闭(用户已拍板 opt-out)
|
||||||
|
页面默认缓存;在 `<route>` 里声明 `meta.keepAlive: false` 即关闭缓存。`TabItem` 新增 `keepAlive` 字段(`addTab` 时取 `route.meta.keepAlive !== false`)。
|
||||||
|
|
||||||
|
### D3 — 布局 keep-alive + include 由「打开且未关闭缓存」的页签推导
|
||||||
|
`layouts/default.vue` 将 `<router-view />` 改为:
|
||||||
|
```vue
|
||||||
|
<router-view v-slot="{ Component, route }">
|
||||||
|
<keep-alive :include="cachedNames">
|
||||||
|
<component :is="Component" :key="route.path" />
|
||||||
|
</keep-alive>
|
||||||
|
</router-view>
|
||||||
|
```
|
||||||
|
`cachedNames = tabsStore.tabs.filter(t => t.keepAlive).map(t => pathToName(t.path))`,`pathToName` 从 tabs store 导出。
|
||||||
|
|
||||||
|
### D4 — 关闭页签 = 移出 include = 销毁
|
||||||
|
关闭页签即从 `tabs` 移除,`cachedNames` 不再含该 name,keep-alive 自动销毁缓存实例;重新打开时重建(重建后重新加载数据)。
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
- **[页面 name 与 pathToName 不一致 → 缓存静默失效]** → 11 个 name 与 pathToName 采用同一约定,并在 type-check 外靠冒烟验证。
|
||||||
|
- **[缓存页面数据陈旧]** → 既有 useRequest 已在进入页面时按需拉取;重建/重新打开时重新请求,不影响正确性。
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
|
||||||
|
无数据迁移;纯前端,回滚即撤销。
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
无。
|
||||||
19
openspec/changes/multi-tab-keep-alive/proposal.md
Normal file
19
openspec/changes/multi-tab-keep-alive/proposal.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
## Why
|
||||||
|
|
||||||
|
多页签已存在,但切换页签时组件会被重建,滚动位置、搜索筛选、表单输入等状态全部丢失,后台高频「列表↔详情/表单」切换体验差。需要为布局内菜单页面启用 keep-alive,切换页签保留状态、关闭页签销毁状态。
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
- 布局内菜单页面启用 keep-alive:切换页签保留组件状态,关闭页签销毁;默认缓存,页面可通过路由 meta `keepAlive: false` 关闭缓存。
|
||||||
|
- 为 11 个菜单页面补充组件 `name`(keep-alive 缓存标识),布局用 `<keep-alive :include>` 按「打开且未关闭缓存」的页签 name 集合缓存。
|
||||||
|
|
||||||
|
## Capabilities
|
||||||
|
|
||||||
|
### Modified Capabilities
|
||||||
|
|
||||||
|
- `frontend/AD/layout`: 「多页签」需求新增 keep-alive 状态保留/销毁行为。
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
- **前端**:`layouts/default.vue`(router-view 包 keep-alive)、`stores/modules/tabs.ts`(path→name 辅助 + keepAlive 字段)、11 个菜单页面加 `defineOptions({ name })`(可声明 `meta.keepAlive: false` 关闭缓存)。
|
||||||
|
- 无后端/数据变更。
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
## MODIFIED Requirements
|
||||||
|
|
||||||
|
### Requirement: 多页签
|
||||||
|
系统 SHALL 支持多页签(tabs)视图:打开的页面保留为页签,可切换与关闭;切换页签时保留各页面的组件状态(keep-alive),关闭页签时销毁其状态。页面 SHALL 默认缓存,可通过路由 meta `keepAlive: false` 关闭缓存。
|
||||||
|
|
||||||
|
#### Scenario: 页签切换与关闭
|
||||||
|
- **WHEN** 用户打开多个菜单页面
|
||||||
|
- **THEN** 各页面以页签形式保留,可切换、可关闭
|
||||||
|
|
||||||
|
#### Scenario: 切换页签保留状态
|
||||||
|
- **WHEN** 用户在多个页签间切换
|
||||||
|
- **THEN** 各页签页面的组件状态(滚动/筛选/表单)被保留,不因切换而重建
|
||||||
|
|
||||||
|
#### Scenario: 关闭页签销毁状态
|
||||||
|
- **WHEN** 用户关闭某页签
|
||||||
|
- **THEN** 该页面的组件状态被销毁,再次打开时重建
|
||||||
|
|
||||||
|
#### Scenario: 页面关闭缓存
|
||||||
|
- **WHEN** 某页面声明路由 meta `keepAlive: false`
|
||||||
|
- **THEN** 该页面切换页签时不保留状态,每次进入重新构建
|
||||||
16
openspec/changes/multi-tab-keep-alive/tasks.md
Normal file
16
openspec/changes/multi-tab-keep-alive/tasks.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
## 1. 页面 name
|
||||||
|
|
||||||
|
- [x] 1.1 给 11 个菜单页面补 `defineOptions({ name })`(`/`→dashboard、`/system/{user,role,resource,org,tenant,dict,config}`→末段、`/monitor/{audit,login-log,session}`→末段)
|
||||||
|
|
||||||
|
## 2. tabs store 辅助
|
||||||
|
|
||||||
|
- [x] 2.1 `stores/modules/tabs.ts`:`TabItem` 加 `keepAlive` 字段;导出 `pathToName(path)`(`/`→dashboard,其余取路径末段 kebab)
|
||||||
|
|
||||||
|
## 3. 布局 keep-alive
|
||||||
|
|
||||||
|
- [x] 3.1 `layouts/default.vue` 用 `<keep-alive :include="cachedNames">` 包裹 router-view;`cachedNames` = 打开且 keepAlive 的页签 name 集合;`addTab` 传 `route.meta.keepAlive !== false`;`:key="route.path"`
|
||||||
|
|
||||||
|
## 4. 验证
|
||||||
|
|
||||||
|
- [x] 4.1 前端 type-check 通过
|
||||||
|
- [x] 4.2 冒烟:默认页签切换保留状态;声明 `meta.keepAlive:false` 的页面切换重建
|
||||||
@ -1 +1 @@
|
|||||||
Subproject commit 5de298a9569027f4b40766814733bea209ccada4
|
Subproject commit ff0e1dcd27295a9cd2eb99a828c9f367aee583e9
|
||||||
Loading…
x
Reference in New Issue
Block a user