修复:顶部标签栏切换丢失页面状态(Vue Router 4 RouterView 缓存结构) #187

Open
opened 2026-09-01 09:57:01 +08:00 by ila · 2 comments
Owner

Gitea MCP 未向当前会话暴露,按仓库规则回退项目根目录安全配置与 Gitea API 更新本工单;凭据未写入工单、代码或日志。

原始需求摘要

来源:用户于 2026-09-01 反馈,Admin 顶部标签栏(TagsView)切换标签页时,GoAuto 列表页的搜索条件会丢失(例如 tab A 搜索后打开 tab B,再切回 tab A,搜索状态被重置)。用户要求建单;2026-09-01 评审后确认原工单对 router-view key 的根因判断不正确,本版按当前 Vue 3 / Vue Router 4 实现修订。

目的:让顶部标签栏切换时,GoAuto 列表页及其他启用缓存的页面能够复用原组件实例,保留搜索、分页和选中状态;同时保证动态路径页面(字典数据页)按实际 path 隔离实例,不串数据。

基线与已核实事实

代码基线:3aab1f0(2026-09-01,当前 main)。核验日期 2026-09-01。

  • web/package.json 当前使用 Vue 3.5.41、Vue Router 4.6.4。
  • web/src/layout/components/AppMain.vue 仍采用 Vue 2 风格结构:<keep-alive :include="cachedViews"><router-view :key="key" /></keep-alive>,并把 router-view 直接放在 keep-alive 内。
  • 当前安装的 Vue Router 4.6.4 运行时代码明确警告:<router-view> can no longer be used directly inside <transition> or <keep-alive>,要求使用 v-slot="{ Component }" 后把真实路由组件交给 keep-alive。因此当前结构缓存的是不受支持的承载方式,不能可靠缓存业务页面实例;这是本单的正确根因。
  • 原工单认为 $route.path 作为 key 会在切换标签时绕过缓存,该判断不成立。Vue 3.5.41 KeepAlive 以 vnode.key 查找缓存;从页面 A 切到 B 再回 A 时,A 的 path/key 仍相同,应命中原缓存。route.path 反而能让 /dict/data/1 与 /dict/data/2 形成独立缓存实例。
  • cachedViews 仍由 store/modules/tagsView.js 按路由 name 维护;GoAuto 组件名与菜单名一致,no_cache=0,无需修改。
  • 字典数据管理页使用同一路由名、不同 params.dictId 形成不同实际 path;必须继续使用实际 path 作为组件 key。
  • GoAuto 商品定位等参数使用 query。对同一 path 的 query 变化应复用同一缓存实例;需要响应 query 的页面不能依赖组件重新挂载,必须通过已有 watcher/路由钩子或在对应业务工单中补充,不在本单中用强制 remount 掩盖。

目标

  1. 使用 Vue Router 4 官方支持的 RouterView 插槽结构,让 keep-alive 缓存真实路由业务组件。
  2. 顶部标签栏在 GoAuto 列表页及其他启用缓存页面之间切换时,搜索、分页、选中行等组件内部状态正确保留。
  3. 字典数据管理页按不同实际 path 保持独立缓存实例,切换不同 dictId 时不串数据。
  4. 消除 RouterView 直接嵌套 KeepAlive/Transition 的运行时警告。

非目标

  • 不修改 tagsView.js 按 name/path 维护标签与缓存列表的既有逻辑。
  • 不修改 sys_menu 的 menu_name、no_cache 配置。
  • 不把 key 改成“有 params 用 path、无 params 用 route name”的条件表达式;统一保留 route.path,由 KeepAlive 正确使用它区分缓存实例。
  • 不在本单批量修改业务页面的 query 响应逻辑;如发现某页只在 created() 读取 query 且确有 SPA 内同 path 跳转需求,单独记录或纳入直接受影响工单。
  • 不涉及权限、安全、并发、数据库结构、接口或业务状态变化。

前置依赖与并行性

  • 无服务端依赖,纯前端公共布局组件改动。
  • 影响全站,实施时不得与其他修改 AppMain.vue、TagsView 或 tagsView.js 的任务并行。
  • 建议先完成本单,再实施 #186,以便 #186 在正确缓存基础上验收来源列表状态保持。

固定实施方案

仅修改 web/src/layout/components/AppMain.vue 的路由承载结构,使用 RouterView 插槽取得真实组件:

<router-view v-slot="{ Component, route }">
  <transition name="fade-transform" mode="out-in">
    <keep-alive :include="cachedViews">
      <component :is="Component" :key="route.path" />
    </keep-alive>
  </transition>
</router-view>
  • 删除原来给 router-view 使用的 key() 计算属性;cachedViews 和 appMainStyle 保持不变。
  • 不修改 tagsView.js、菜单配置和业务页面。
  • 如实际渲染验证发现 Transition/KeepAlive 的顺序需要按 Vue Router 4 警告示例调整,以“真实业务组件处于 KeepAlive 直接缓存边界内、无运行时警告”为硬约束,不得回退到直接缓存 router-view。

设计证据

修复不改变可见 UI 结构或控件,只纠正组件缓存行为,不需要视觉设计稿。工单验收记录必须列出验证页面、状态字段、组件挂载次数和动态 path 隔离结果。

验收标准

  • GoAuto 列表页经顶部标签切换后,搜索、分页和选中行保持不变。
  • 状态页 A → B → A 时,A 的业务组件只挂载一次,切回使用缓存实例。
  • 字典 /dict/data/1、/dict/data/2、/dict/data/3 分别形成独立实例,来回切换不串数据且各自状态保留。
  • 不再出现 RouterView 直接放入 KeepAlive/Transition 的 Vue Router 运行时警告。
  • 直接刷新浏览器仍执行正常初始化,不错误复用刷新前的内存组件实例。
  • sys-config、sys-api 等静态路径页面切换后状态正常保留。
  • 未修改 tagsView.js、sys_menu 配置和 GoAuto 业务页面。
  • Web 单元测试、既有 e2e 测试与生产构建通过。

必测场景

  • 自动化组件测试:构造有名称的状态组件 A/B;修改 A 的输入值,导航 B 后返回 A,断言值保留且 A mount 次数为 1。
  • 自动化动态 path 测试:同一路由名、不同 dictId 的三个 path 各自修改本地状态,来回切换后分别恢复,断言没有复用错误实例。
  • 自动化告警检查:挂载 AppMain 并导航,断言不产生 RouterView/KeepAlive 直接嵌套警告。
  • 浏览器验收:pdd-products、shopee-products、syb-products 各验证搜索、分页、选中行。
  • 浏览器验收:连续切换三个字典数据标签,验证标题、dictId、表格内容和本地状态一致。
  • 直接刷新当前页面,确认重新加载;关闭标签再重新打开,确认按新实例初始化。

风险与安全门禁

  • AppMain.vue 是全站公共容器,回归面广;不能只验证 GoAuto 页面。
  • 动态 path 必须继续使用 route.path 作为 key,不能退化为仅按 route name 缓存。
  • 正确启用 KeepAlive 后,同 path query 变化不再靠 remount 触发 created();实施中发现的业务页 query 响应缺口必须记录,不能通过恢复错误缓存结构规避。
  • 不涉及权限、安全、并发、数据库结构或高风险业务动作。

文档影响

无长期文档影响。 未新增或修改接口、数据结构、业务规则、启动、部署或运维方式;属于前端公共容器行为修复。因此跳过 Wiki 更新与同步。

状态

待确认(2026-09-01 已按评审意见修正根因与方案,等待用户确认后实施)。

> Gitea MCP 未向当前会话暴露,按仓库规则回退项目根目录安全配置与 Gitea API 更新本工单;凭据未写入工单、代码或日志。 ## 原始需求摘要 来源:用户于 2026-09-01 反馈,Admin 顶部标签栏(TagsView)切换标签页时,GoAuto 列表页的搜索条件会丢失(例如 tab A 搜索后打开 tab B,再切回 tab A,搜索状态被重置)。用户要求建单;2026-09-01 评审后确认原工单对 `router-view key` 的根因判断不正确,本版按当前 Vue 3 / Vue Router 4 实现修订。 目的:让顶部标签栏切换时,GoAuto 列表页及其他启用缓存的页面能够复用原组件实例,保留搜索、分页和选中状态;同时保证动态路径页面(字典数据页)按实际 path 隔离实例,不串数据。 ## 基线与已核实事实 代码基线:`3aab1f0`(2026-09-01,当前 `main`)。核验日期 2026-09-01。 - `web/package.json` 当前使用 Vue `3.5.41`、Vue Router `4.6.4`。 - `web/src/layout/components/AppMain.vue` 仍采用 Vue 2 风格结构:`<keep-alive :include="cachedViews"><router-view :key="key" /></keep-alive>`,并把 `router-view` 直接放在 `keep-alive` 内。 - 当前安装的 Vue Router 4.6.4 运行时代码明确警告:`<router-view> can no longer be used directly inside <transition> or <keep-alive>`,要求使用 `v-slot="{ Component }"` 后把真实路由组件交给 `keep-alive`。因此当前结构缓存的是不受支持的承载方式,不能可靠缓存业务页面实例;这是本单的正确根因。 - 原工单认为 `$route.path` 作为 key 会在切换标签时绕过缓存,该判断不成立。Vue 3.5.41 `KeepAlive` 以 `vnode.key` 查找缓存;从页面 A 切到 B 再回 A 时,A 的 path/key 仍相同,应命中原缓存。`route.path` 反而能让 `/dict/data/1` 与 `/dict/data/2` 形成独立缓存实例。 - `cachedViews` 仍由 `store/modules/tagsView.js` 按路由 `name` 维护;GoAuto 组件名与菜单名一致,`no_cache=0`,无需修改。 - 字典数据管理页使用同一路由名、不同 `params.dictId` 形成不同实际 path;必须继续使用实际 path 作为组件 key。 - GoAuto 商品定位等参数使用 query。对同一 path 的 query 变化应复用同一缓存实例;需要响应 query 的页面不能依赖组件重新挂载,必须通过已有 watcher/路由钩子或在对应业务工单中补充,不在本单中用强制 remount 掩盖。 ## 目标 1. 使用 Vue Router 4 官方支持的 RouterView 插槽结构,让 `keep-alive` 缓存真实路由业务组件。 2. 顶部标签栏在 GoAuto 列表页及其他启用缓存页面之间切换时,搜索、分页、选中行等组件内部状态正确保留。 3. 字典数据管理页按不同实际 path 保持独立缓存实例,切换不同 `dictId` 时不串数据。 4. 消除 RouterView 直接嵌套 KeepAlive/Transition 的运行时警告。 ## 非目标 - 不修改 `tagsView.js` 按 name/path 维护标签与缓存列表的既有逻辑。 - 不修改 `sys_menu` 的 `menu_name`、`no_cache` 配置。 - 不把 key 改成“有 params 用 path、无 params 用 route name”的条件表达式;统一保留 `route.path`,由 KeepAlive 正确使用它区分缓存实例。 - 不在本单批量修改业务页面的 query 响应逻辑;如发现某页只在 `created()` 读取 query 且确有 SPA 内同 path 跳转需求,单独记录或纳入直接受影响工单。 - 不涉及权限、安全、并发、数据库结构、接口或业务状态变化。 ## 前置依赖与并行性 - 无服务端依赖,纯前端公共布局组件改动。 - 影响全站,实施时不得与其他修改 `AppMain.vue`、`TagsView` 或 `tagsView.js` 的任务并行。 - 建议先完成本单,再实施 #186,以便 #186 在正确缓存基础上验收来源列表状态保持。 ## 固定实施方案 仅修改 `web/src/layout/components/AppMain.vue` 的路由承载结构,使用 RouterView 插槽取得真实组件: ```vue <router-view v-slot="{ Component, route }"> <transition name="fade-transform" mode="out-in"> <keep-alive :include="cachedViews"> <component :is="Component" :key="route.path" /> </keep-alive> </transition> </router-view> ``` - 删除原来给 `router-view` 使用的 `key()` 计算属性;`cachedViews` 和 `appMainStyle` 保持不变。 - 不修改 `tagsView.js`、菜单配置和业务页面。 - 如实际渲染验证发现 Transition/KeepAlive 的顺序需要按 Vue Router 4 警告示例调整,以“真实业务组件处于 KeepAlive 直接缓存边界内、无运行时警告”为硬约束,不得回退到直接缓存 `router-view`。 ## 设计证据 修复不改变可见 UI 结构或控件,只纠正组件缓存行为,不需要视觉设计稿。工单验收记录必须列出验证页面、状态字段、组件挂载次数和动态 path 隔离结果。 ## 验收标准 - [ ] GoAuto 列表页经顶部标签切换后,搜索、分页和选中行保持不变。 - [ ] 状态页 A → B → A 时,A 的业务组件只挂载一次,切回使用缓存实例。 - [ ] 字典 `/dict/data/1`、`/dict/data/2`、`/dict/data/3` 分别形成独立实例,来回切换不串数据且各自状态保留。 - [ ] 不再出现 RouterView 直接放入 KeepAlive/Transition 的 Vue Router 运行时警告。 - [ ] 直接刷新浏览器仍执行正常初始化,不错误复用刷新前的内存组件实例。 - [ ] `sys-config`、`sys-api` 等静态路径页面切换后状态正常保留。 - [ ] 未修改 `tagsView.js`、`sys_menu` 配置和 GoAuto 业务页面。 - [ ] Web 单元测试、既有 e2e 测试与生产构建通过。 ## 必测场景 - 自动化组件测试:构造有名称的状态组件 A/B;修改 A 的输入值,导航 B 后返回 A,断言值保留且 A mount 次数为 1。 - 自动化动态 path 测试:同一路由名、不同 `dictId` 的三个 path 各自修改本地状态,来回切换后分别恢复,断言没有复用错误实例。 - 自动化告警检查:挂载 AppMain 并导航,断言不产生 RouterView/KeepAlive 直接嵌套警告。 - 浏览器验收:`pdd-products`、`shopee-products`、`syb-products` 各验证搜索、分页、选中行。 - 浏览器验收:连续切换三个字典数据标签,验证标题、dictId、表格内容和本地状态一致。 - 直接刷新当前页面,确认重新加载;关闭标签再重新打开,确认按新实例初始化。 ## 风险与安全门禁 - `AppMain.vue` 是全站公共容器,回归面广;不能只验证 GoAuto 页面。 - 动态 path 必须继续使用 `route.path` 作为 key,不能退化为仅按 route name 缓存。 - 正确启用 KeepAlive 后,同 path query 变化不再靠 remount 触发 `created()`;实施中发现的业务页 query 响应缺口必须记录,不能通过恢复错误缓存结构规避。 - 不涉及权限、安全、并发、数据库结构或高风险业务动作。 ## 文档影响 **无长期文档影响。** 未新增或修改接口、数据结构、业务规则、启动、部署或运维方式;属于前端公共容器行为修复。因此跳过 Wiki 更新与同步。 ## 状态 待确认(2026-09-01 已按评审意见修正根因与方案,等待用户确认后实施)。
ila changed title from 修复:顶部标签栏切换丢失页面状态(AppMain 的 router-view key 绑定问题) to 修复:顶部标签栏切换丢失页面状态(Vue Router 4 RouterView 缓存结构) 2026-09-01 10:23:45 +08:00
Author
Owner

已按用户指令开始实施。基线为 3aab1f0,范围严格限定为 AppMain.vue 的 Vue Router 4 scoped-slot 缓存结构及对应回归测试;保留 route.path 作为缓存 key,不修改 tagsView、业务页面或视觉样式。Gitea MCP 当前会话未提供,按项目规则回退 Gitea API。无长期文档影响,实施完成后跳过 Wiki 同步并回写验证证据。

已按用户指令开始实施。基线为 3aab1f0,范围严格限定为 AppMain.vue 的 Vue Router 4 scoped-slot 缓存结构及对应回归测试;保留 route.path 作为缓存 key,不修改 tagsView、业务页面或视觉样式。Gitea MCP 当前会话未提供,按项目规则回退 Gitea API。无长期文档影响,实施完成后跳过 Wiki 同步并回写验证证据。
Author
Owner

实施完成,等待用户验收。

实现:

  • AppMain 改为 Vue Router 4 scoped slot,KeepAlive 缓存实际路由组件。
  • 保留 route.path 作为 vnode key,确保同名动态路由按 path 隔离缓存。
  • 删除已无用途的 key 计算属性。
  • 新增 3 个回归测试:普通标签状态恢复/单次挂载、同名动态路由多实例隔离、无 RouterView 直接嵌套警告。
  • 未修改 tagsView、业务页面、视觉样式或状态管理。

验证:

  • pnpm exec jest tests/unit/layout/AppMain.spec.js --runInBand:3/3 通过。
  • pnpm exec jest --runInBand:11 suites、39 tests 全部通过;既有 Pagination 测试仍输出未注册 Element Plus 组件警告,不影响通过。
  • pnpm exec eslint src/layout/components/AppMain.vue:通过。
  • pnpm exec eslint tests/unit/layout/AppMain.spec.js:通过。
  • pnpm build:prod:通过;保留既有 CSS/minify 与 chunk-size 警告。
  • Chrome 本地登录态真机浏览器验收:PDD、虾皮、SYB 商品页分别填写未提交筛选条件,切换标签后均恢复;测试值已清空,未触发查询或数据写入;RouterView 直接嵌套警告为 0。
  • pnpm lint:未通过,唯一 errors 来自未改动的 web/src/views/goauto/purchase-tasks/index.vue(24 个既有 tabs/spaces/indent errors);#187 改动文件 lint 均通过,未将相邻问题混入本工单。
  • git diff --cached --check:通过。
  • 远端 main 已核对为本提交。

提交:b31896ef752dfae0bff9b58a447d7520a9949db3
推送:origin/main 成功。

文档影响:无长期文档影响。本次仅恢复既有标签缓存行为,不改变 API、配置、数据结构、业务规则或运维方式,因此按工单约定跳过 Wiki 更新与同步。

未执行:未发布线上;发布属于独立高风险动作,#187 当前停在待验收。

实施完成,等待用户验收。 实现: - AppMain 改为 Vue Router 4 scoped slot,KeepAlive 缓存实际路由组件。 - 保留 route.path 作为 vnode key,确保同名动态路由按 path 隔离缓存。 - 删除已无用途的 key 计算属性。 - 新增 3 个回归测试:普通标签状态恢复/单次挂载、同名动态路由多实例隔离、无 RouterView 直接嵌套警告。 - 未修改 tagsView、业务页面、视觉样式或状态管理。 验证: - pnpm exec jest tests/unit/layout/AppMain.spec.js --runInBand:3/3 通过。 - pnpm exec jest --runInBand:11 suites、39 tests 全部通过;既有 Pagination 测试仍输出未注册 Element Plus 组件警告,不影响通过。 - pnpm exec eslint src/layout/components/AppMain.vue:通过。 - pnpm exec eslint tests/unit/layout/AppMain.spec.js:通过。 - pnpm build:prod:通过;保留既有 CSS/minify 与 chunk-size 警告。 - Chrome 本地登录态真机浏览器验收:PDD、虾皮、SYB 商品页分别填写未提交筛选条件,切换标签后均恢复;测试值已清空,未触发查询或数据写入;RouterView 直接嵌套警告为 0。 - pnpm lint:未通过,唯一 errors 来自未改动的 web/src/views/goauto/purchase-tasks/index.vue(24 个既有 tabs/spaces/indent errors);#187 改动文件 lint 均通过,未将相邻问题混入本工单。 - git diff --cached --check:通过。 - 远端 main 已核对为本提交。 提交:b31896ef752dfae0bff9b58a447d7520a9949db3 推送:origin/main 成功。 文档影响:无长期文档影响。本次仅恢复既有标签缓存行为,不改变 API、配置、数据结构、业务规则或运维方式,因此按工单约定跳过 Wiki 更新与同步。 未执行:未发布线上;发布属于独立高风险动作,#187 当前停在待验收。
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OPC/goauto#187