From b4e12a671f1da47164f28645258178644a3fce33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Fri, 18 Sep 2026 10:17:48 +0800 Subject: [PATCH] =?UTF-8?q?fix(views):=20=E4=BF=AE=E5=A4=8D=E7=8B=AC?= =?UTF-8?q?=E7=AB=8B=E9=A1=B5=E5=88=87=E6=8D=A2=E6=97=B6=E5=BC=B9=E7=AA=97?= =?UTF-8?q?=E6=AE=8B=E7=95=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 独立整页固定使用普通 div 容器,取消动态切换 el-dialog 避免切换时弹窗 DOM 残留 - 删除相关 dialogVisible、dialogTitle、页脚弹窗动态分支代码以简化逻辑 - watch $route 调整,优化独立页初始化流程,避免不必要的关闭弹窗操作 - 新增 closeInnerDialogs 方法,页面卸载或 keep-alive deactivate 时关闭所有二级弹窗 - 对 loading-manage 与 project-apply 两个模块同步改造,统一处理弹窗状态和容器渲染 - 解决 keep-alive 缓存导致旧弹窗一直挂载在 body 上的问题,改善用户切换体验 --- .workbuddy/memory/2026-09-18.md | 24 +++++ .workbuddy/memory/MEMORY.md | 53 +++++------ src/views/business/loading-manage.vue | 122 +++++++++----------------- src/views/business/project-apply.vue | 90 ++++++------------- 4 files changed, 119 insertions(+), 170 deletions(-) create mode 100644 .workbuddy/memory/2026-09-18.md diff --git a/.workbuddy/memory/2026-09-18.md b/.workbuddy/memory/2026-09-18.md new file mode 100644 index 0000000..7d5557d --- /dev/null +++ b/.workbuddy/memory/2026-09-18.md @@ -0,0 +1,24 @@ +# 2026-09-18 + +## 修复:改成独立整页后点左侧菜单仍弹出旧弹窗(loading-manage / project-apply) + +**现象**:配载单管理、项目管理已改为「新增/编辑/详情走独立整页」,但从独立页返回或直接点左侧菜单时,页面上仍会盖出一个老的 `el-dialog`(新增项目管理 / 配载单)。 + +**根因**(已读 Vue runtime-core 源码确认): +- `src/router/tab.js` 按 `tabKey`(默认 fullPath)为每个标签建一个具名 wrapper 组件放进 `wrapperMap`,`src/page/index/layout.vue` 用 `` 缓存 —— 每个标签页一个实例,全部缓存不销毁。 +- 组件被 deactivate 后**仍会随 `$route`(全局响应式)重新渲染**;此时 `isStandalonePage` 变 false,`` 由 div 翻回 `el-dialog`,而 `v-model`(`projectBox`/`dialogVisible`)仍是 true,`append-to-body` 使弹窗 Teleport 到 body。 +- `KeepAlive.deactivate` 调 `move(vnode, storageContainer, ..., moveType=OUT)`,但 `TeleportImpl.move`(`moveTeleport`)忽略传入的 moveType、一律按 REORDER 处理 → **teleport 出去的 DOM 不会被搬回 storage container**,永久残留在 `document.body` 上并显示出来。 + +**修法**(不回退 tab.js 的 `return wrapper` 提交 c1c4fa1,改修根因): +- 独立页容器固定为 `
`,不再按路由回退成 `el-dialog`。 +- 页内其它 `append-to-body` 二级弹窗加 `deactivated()` / `beforeUnmount()` → `closeInnerDialogs()` 把 v-model 置 false。 + +**改动文件**: +- `src/views/business/loading-manage.vue`:容器改 `
`;删 `dialogVisible`、`dialogTitle`、`resetLoadingDialog`、`clearLoadingDialog`、页脚弹窗分支(含「清空」);`watch.$route` 简化;新增 `closeInnerDialogs()`(routeChange / commonAddress / candidateSearch)+ `deactivated`/`beforeUnmount`。 +- `src/views/business/project-apply.vue`:容器改 `
`;删 `projectFormContainer`/`projectFormContainerProps`、`projectBox`、`resetProjectDialog`、`handleCancelProject`;`closeProjectForm` 简化为直接 push 列表;新增 `closeInnerDialogs()`(changeRecordDetail / 附件文档预览 / 图片预览 / 选人)+ `deactivated`/`beforeUnmount`。 + +**校验**:dev server(2889) curl 两个 .vue 及其 scoped style 模块,均 HTTP 200(500 才是编译失败)。`vite build` 因沙箱敏感内容保护会在 facelogin.vue 中断,未用。 + +**遗留同类风险(未改,已告知用户)**:`business/components/waybill-manage-page.vue`(`detailContainer` 在 PageDetail / el-dialog 间切)、`settlement/components/{pre,formal}-settlement-editor.vue`、`transport-reconciliation-editor.vue`(`pageMode ? 'div':'el-dialog'`)、`business/components/waybill-import-dialog.vue`(`standalone`/`createPage`)。统一修法:容器类型在 `created()` 锁定,不随路由/prop 翻转。 + +**未提交**:上述改动 + 上一轮 loading-manage 独立页改造均未 commit。 diff --git a/.workbuddy/memory/MEMORY.md b/.workbuddy/memory/MEMORY.md index 79fd549..db7e494 100644 --- a/.workbuddy/memory/MEMORY.md +++ b/.workbuddy/memory/MEMORY.md @@ -2,37 +2,38 @@ 配套后端:`/Users/gxwebsoft/JAVA/tms-api`(不是 tms-erp-api-ws,后者功能滞后)。 -## 页面结构:列表 + 独立表单页 + 独立详情页(同一组件分流) -- `src/router/views/index.js` 里三条静态/菜单路由指向同一个 .vue:`/xxx`(后端菜单)、`/xxx/form`、`/xxx/detail`;form/detail 都是 `component: Layout` + 空 children,`meta: { keepAlive: false }`,detail 额外带 `activeMenu: '/xxx'`。 -- 组件内按 `$route.path` 分流(如 `isFormPage` / `isDetailPage` / `isListPage`),列表部分统一 `v-if="!isStandalonePage"` 隐藏搜索栏/工具栏/表格/分页。 -- 路由记录不同但组件相同 → Vue 复用实例,所以**必须**在 `watch: { $route(to, from) }` 里重新 init;`mounted` 只覆盖「直接打开/刷新」场景。切换详情与表单时不要只比 id,还要看当前 `dialogMode` 是否匹配。 -- 跳转:`this.$router.push({ path: '/xxx/form', query: { mode, id, name: '新增xxx' } })`;标签标题由 avue-router 取 `query.name`。关闭:`this.$router.$avueRouter?.closeTag?.()` + push 列表。 -- 已按此模式:contract-manage(form+detail)、waybill-manage、loading-manage(新增/编辑/重新派单/详情,2026-09-17 完成)。 -- 底栏文案:只读详情页用「关闭」;表单页沿用「关闭/暂存/确认」。 +## 列表 + 独立表单页 + 独立详情页(同一组件分流) +- 路由:`/xxx`(菜单)、`/xxx/form`、`/xxx/detail` 指向同一 .vue;form/detail 为 `component: Layout` + 空 children,`meta:{keepAlive:false}`,detail 带 `activeMenu:'/xxx'`。 +- 组件内按 `$route.path` 分流,列表部分统一 `v-if="!isStandalonePage"` 隐藏。 +- 路由记录不同但组件相同 → 必须 `watch:{$route}` 里重新 init;`mounted` 只覆盖直接打开/刷新。 +- 跳转 `push({path:'/xxx/form',query:{mode,id,name:'新增xxx'}})`,标签标题取 `query.name`。 +- 底栏:只读详情页「关闭」;表单页「关闭/暂存/确认」。 + +## ⚠️ 独立页容器禁止用 `` 在 div 与 el-dialog 间切换 +- **现象**:改成独立整页后,点左侧菜单又弹出旧弹窗,且永久盖在页面上。 +- **根因**:`src/router/tab.js` 按 fullPath 建 wrapper 组件,`layout.vue` 用 `` 缓存,每个标签一个实例。实例被 deactivate 后**仍会随 `$route` 重新渲染**;此时 `:is` 由 div 变回 `el-dialog`,`v-model` 仍为 true,`append-to-body` 的弹窗经 Teleport 渲染进 body——而 `KeepAlive.deactivate → Teleport.move` 只做 reorder,**不会把 teleport 出去的 DOM 搬回 storage container**,于是弹窗永久残留。 +- **正确写法**:独立页固定 `
` 普通容器,绝不按路由回退成 el-dialog;列表页分支另写或只保留弹窗一种形态。 +- **兜底**:页面内其它 `append-to-body` 的二级弹窗,加 `deactivated(){closeInnerDialogs()}` + `beforeUnmount(){closeInnerDialogs()}` 把 v-model 置 false。 +- 同类风险未修:`business/components/waybill-manage-page.vue`(`detailContainer` 在 PageDetail/el-dialog 间切)、`settlement/components/{pre,formal}-settlement-editor.vue`、`transport-reconciliation-editor.vue`(`pageMode ? 'div':'el-dialog'`)、`business/components/waybill-import-dialog.vue`(`standalone`/`createPage`)。修法同上:容器类型在 `created()` 锁定,不随路由/prop 翻转。 ## 样式硬规则 -- 底部操作栏:`display:flex; justify-content:flex-end;`,**不写 gap**(间距由 Element Plus 默认 `.el-button+.el-button{margin-left:12px}` 提供)。⚠️ 浮动底栏必须 `position:fixed`(祖先 overflow:hidden 让 sticky 失效),配套 `:global(.avue--collapse .x){left:60px}`、`:global(.avue-layout--horizontal .x){left:0}`,`:global()` 要包住整个选择器。 -- 按钮层级:次要 = 不写 type;中间步骤 = `type="primary" plain`;主操作 = `type="primary"`(禁绿色 success,禁两个蓝实心)。顺序 `[辅助工具][取消][保存草稿][提交]`。 -- ⚠️ **`