- 修改选择器为后代选择器以适配 avue 表单项的嵌套结构 - 统一弹窗内表单标签宽度,避免 labelWidth: 'auto' 导致控件宽度不一 - 新增全局样式规则,固定标签宽度为 calc(4em + 24px) - 删除用于调试控件宽度的临时文件 cascader-width-check.html - 记录并说明 avue 弹窗宽度调整相关坑点和解决方案
6.1 KiB
项目长期记忆(tms-erp-web-ws / Saber3)
后端:/Users/gxwebsoft/JAVA/tms-api(tms-erp-api-ws 滞后,别看错仓库)。
列表 / 表单 / 详情三态同组件分流
/xxx、/xxx/form、/xxx/detail 指向同一 .vue;form/detail 在 src/router/views/index.js 是 component: Layout + 空 children.path + meta:{keepAlive:false}(detail 另带 activeMenu)。组件内按 $route.path 分流,必须 watch:{$route} 重新 init;标签标题取 query.name;独立页容器固定 <div v-if>,不用 el-dialog。
⚠️ keep-alive 三坑
背景(router/tab.js + page/index/index.vue):按 fullPath 建 wrapper,<keep-alive :include="tagsKeep"> 缓存,每标签一实例、永不销毁,deactivate 后仍随全局 $route 重渲染。
坑一:独立页容器禁用 <component :is> 在 div / el-dialog 间切换。 append-to-body 走 Teleport,TeleportImpl.move 忽略 moveType,不会把 DOM 搬回缓存容器 → 切菜单冒出旧弹窗。修法:固定 <div v-if> + deactivated(){closeInnerDialogs()} + beforeUnmount()(写在子组件也生效)。已修 loading-manage / project-apply / waybill-manage-page;未修 settlement/components/{pre,formal}-settlement-editor.vue、transport-reconciliation-editor.vue、waybill-import-dialog.vue。
坑二:宿主 view 直绑全局 $route.query.* 给子组件 prop。 缓存实例被灌入当前页 id → 拿错 id 查后端(批量弹「运单管理不存在」),live computed 还会误 push 标签。修法:routePathLocked 锁路径 + watcher / push 前判 isOwnedRouteActive + 宿主绑定加路径守卫。⚠️ routePathLocked 必须放 data()(Options API 顺序 data → computed → watch(immediate) → created;放 created 会读到空值、挡掉首次加载)。已修 waybill-manage-page / transport-plan-page + 三个宿主 view。
坑四:avue-crud 弹窗 append-to-body 默认 true(dialog-form3.js:validData(tableOption.dialogAppendToBody, true)),DOM Teleport 到 body。 于是页面里以页面容器为祖先的 scoped 选择器(.xxx-page :deep(.yyy))命中不到弹窗内容,尤其 el-cascader(inline-block、宽度按内容收缩,不像 el-input/el-autocomplete 有 --el-input-width:100% 兜底)会明显比同行 input 窄。修法:option 里配 dialogCustomClass: 'xxx-dialog',规则写到 src/styles/element-ui.scss 的 .xxx-dialog ...(项目既有约定,10+ 处同款);或把基准类放在 Teleport 内部元素上(如 ship.vue 的 .ship-form 挂在 el-form 上,自身带 scope 属性,照样生效)。
坑五:avue 表单项控件不是 .el-form-item__content 的直接子级。 form/index3.js 渲染为 content > div > [div|elTooltip] > 控件,所以 .el-form-item__content > .el-cascader 这类直接子级选择器对 avue 表单全部失效(手写 <el-form-item> 才生效),一律用后代选择器。
坑六:labelWidth: 'auto' 在 Element Plus 里逐项计算,不是全表单共享。 实测同一弹窗:3 字 54/64px、2 字 40px、4 字 68px、4 字带必填星号 78px;label 越宽 → 控件可用宽度越小,同级控件宽度就不一致(车牌号 input 486 vs 使用部门 cascader 472)。要让同一行控件等宽必须统一 label 宽度:.xxx-dialog .el-form-item__label { flex: 0 0 calc(Nem + 24px) !important; width: calc(Nem + 24px) !important }(N=本列最长汉字数,与 ship.vue、.dialog-form--carded 同款)。
验证 avue 弹窗 DOM/宽度的可复用办法: 项目根建临时 html(Vite 直接服务)→ 引真实 option 与 src/styles/element-ui.scss,挂 avue-crud 并 mounted(){ this.$refs.crud.rowAdd() } 弹出真弹窗;用 agent-browser(/Users/gxwebsoft/.workbuddy/binaries/node/workspace/node_modules/.bin/agent-browser)open + eval 读 DOM 链路与 getBoundingClientRect,用完删文件。
坑三:路由表重复定义 → 内容区整页空白(2026-09-18)。 /business/contract-manage/detail 在 router/views/index.js 定义两次、子路由同名 合同详情。重名 addRoute 会 removeRoute(name) 移除旧子记录,但不从父记录 children 里摘除,第一条 Layout 壳记录留在 matcher 且靠前 → resolve() 命中它 → $route.matched.length === 1 → 二级 router-view 无匹配 → 侧栏/标签正常但内容空白。修法:删重复定义。排查:扫「顶层 path + 首个子路由 name」重复项。
样式硬规则
- 底栏
flex; justify-content:flex-end,不写 gap(靠 EP 默认.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;中间步骤=
primary plain;主操作=primary(禁绿、禁两个蓝实心)。顺序[辅助][取消][保存草稿][提交]。 - 独立页标题
.archive-page-form__title;分组用全局<section-card>;上传证件区width:100%; max-width:240px+.el-upload{height:151px}。 - ⚠️
<style scoped lang="scss">顶层禁//注释(Vite5 + sassUnexpected '/'→ 500),顶层用/* */。其余见src/styles/element-ui.scss与AGENTS.md4.4。
表单 / 自检 / 导出
- placeholder 只写
请输入/请选择;例外:请输入或选择车辆、日期区间起/止、示例值。校验 message 须带字段名。 business-crud-page.vue:option 经cloneOption克隆,独立表单页走PageAvueForm。- 自检:
curl "localhost:2889/src/xxx.vue"与"...?vue&type=style&index=0&scoped=true&lang.scss"(200 / 500)。vite build在facelogin.vue被沙箱中断,不能作唯一校验。devVITE_APP_API=/api代理172.16.203.228:8000。 - 导出:前端
exportColumns后端未用,以XxxExportExcel.java为准;BladeXBeanUtil类型不兼容静默跳过(Date→LocalDateTime丢值,用DateUtil.fromDate)。