Files
tms-erp-web/.workbuddy/memory/2026-08-06.md
赵忠林 02b2e3b673 refactor(transportCapacity): 使用 section-card 重构司机管理弹窗布局
- 将司机管理页面新增/编辑弹窗中四个分组改为使用标准 section-card 组件包裹
- 删除手写的扁平样式和红色竖条,统一使用全局 element-ui 样式维护弹窗视觉风格
- 调整司机证件上传区尺寸为 240x151 px,保持身份证比例并左对齐显示
- 优化弹窗中各表单项布局,提高视觉层次与一致性
- 更新 scoped 样式,移除无用的 section-title 样式,保持代码清洁
2026-08-06 16:53:23 +08:00

18 KiB
Raw Blame History

2026-08-06

复盘 src/views/business/project-apply.vue 弹窗 UI 规范

  • 主弹窗为自定义 el-dialog + el-form(非 Avue option 驱动width 1440px、top 4vh、append-to-body、destroy-on-close。
  • 分区标题复用全局 .dialog-section-title4px 主色竖条 + 8px 间距);页面将其改造为「白卡头 + 下方白内容卡」拼合方案radius 6px + 淡阴影 0 1px 2px rgba(0,0,0,.04))。
  • 表单label-position="right" / label-width="auto";两列 gridcolumn-gap: 72px);表单项 margin-bottom 16pxlabel min-width 160px。
  • 备注类 textarea 整行展示 + 200 字上限 + show-word-limit金额/天数有非负与范围校验。
  • 底部按钮:非变更弹窗=取消/提交(右对齐);变更弹窗=提交/保存/关闭(左对齐)。
  • 与设计规范(AGENTS.md)对照:基本符合 4.8 弹窗表单规范;但本页 scoped 用 white-space: normal 覆盖了全局 .el-dialog .el-form-item__label { white-space: nowrap }允许长标签换行与全局「label 不换行」取向不同);两列 grid 统一 min-width 160px未严格「按列分别计算 label 宽度」。

将 project-apply 弹窗标准推广为「全项目通用标准」

用户要求把 project-apply 弹窗视觉(灰底 + 分组白卡 + 标题在卡内)复制到其他页面。落地方式(已执行):

  • 新增可复用组件 src/components/section-card/main.vue:白卡 + 标题在卡头4px 主色竖条 + 8px 间距),支持 title prop 与 #title/#extra slot已在 src/main.js 全局注册为 <section-card>
  • src/styles/element-ui.scss 末尾新增全局标准:① .el-dialog__body 统一灰底 #f5f6fa(影响全项目所有弹窗);② .el-dialog .avue-form 自动包白卡Avue 默认增删改弹窗无需改 option 即统一);③ .el-dialog .avue-form__group 分组白卡option.group 渲染为标题在卡内);④ 弹窗内 .el-form-item 默认 16px 间距;⑤ .section-card 全局样式;⑥ .form-grid-2 两列栅格工具类column-gap 72px
  • 应用落地business 模块自定义弹窗——common-route.vue 的「选择常用地址」弹窗用 section-card 包成「查询条件/常用地址」两组;loading-manage.vue 主弹窗 6 个分区全部改用 <section-card>common-cargo.vue 导入弹窗经全局 .el-dialog .avue-form 规则自动获得白卡。project-apply.vue 本身作为参照未改(其 scoped 样式已符合,全局灰底与其一致)。
  • 验证:vite build 失败为仓库既有问题(waybill-import-dialog.vue 引入的 xlsx 依赖缺失),与本次改动无关;改用 @vue/compiler-sfc 编译三个改动 .vue均 OK+ sass 编译 element-ui.scssOK
  • 注意:其他模块的自定义弹窗需按同样方式用 <section-card> 包裹各组(灰底已由全局规则自动生效);如需继续推进其余模块可照此模式批量处理。

继续推广vehicle/customer-archive.vue 弹窗

  • 用户反馈 /vehicle/customer-archive 新增客商档案弹窗需与 /business/project-apply 一致。
  • 已将该页主弹窗内的表单分组由旧版「灰底分组块 + 内层白卡 + 标题在卡内」全部替换为 <section-card>
    • 工商信息 / 联系信息 / 财务/信用信息 / 其他信息 四个分组改为 section-card删除外层浮于灰底的「基本信息」大标题分组已足够清晰避免嵌套
    • 客商材料、变更记录两个非表单分区也改用 <section-card>,其中客商材料的操作按钮通过 #extra 插槽放在卡头右侧。
  • 清理了失效的 .archive-form__group.customer-material-section.change-record-section.section-title 等 scoped 样式。
  • 验证:@vue/compiler-sfc 编译 customer-archive.vue OKsass 编译 element-ui.scss OKsection-card 标签开合平衡。

继续推广transportCapacity/driver.vue 弹窗

  • 用户反馈司机管理页面(/transportCapacity/driver)新增/编辑弹窗的 UI 与「重大项目补录」等不一致,仍是红色竖条 + 字段直接落在灰底上的扁平样式,要求统一为「灰底弹窗 + 白底分组卡片」。
  • 改造 src/views/transportCapacity/driver.vue
    • 4 个分组(基础身份信息 / 驾驶证信息 / 从业资格证信息 / 司机联系信息)由原 <div class="section-title"> + 扁平 <el-row> 改为标准 <section-card title="..."> 包裹。
    • 删除手写的 .section-title(红色竖条)、.driver-form__block(白底分组卡)、自定义 .driver-dialog { :deep(.el-dialog__body) { background: #f0f2f5; ... } } ——以上视觉均由 src/styles/element-ui.scss 的全局 .el-dialog__body { background: #f5f6fa; padding: 16px; } + .section-card { ... } 自动覆盖。
    • 弹窗内 4 个分组的二级 row 网格不变3列/4列 12栏布局由每张白卡的 body 承载。
  • 教训:第一次改动按 project-apply.vue 的手写方式做了一版(手写 dialog-section-title override + __block),与已落的「全项目通用标准」重复。今后新页面/新弹窗直接用 <section-card>,不要再手写卡片样式

调整 /base/fee-item 弹窗左右表单项间距

  • 用户反馈费用项弹窗中「费用类型」与「费用项代码」左右列太挤。
  • 修改 src/views/base/fee-item.vue 的 scoped 样式:将弹窗内 .avue-form__row / .el-row 的负边距与 .el-col 的水平 padding 从 36px 加大到 48px,等效 gutter 由 72px 增至 96px。
  • 该样式限定在 .avue-crud__dialog 内,仅影响新增/编辑弹窗,不影响列表搜索栏。

调整 /vehicle/violation-record 弹窗两栏横向间距

  • 用户反馈违章记录新增弹窗左右两列表单项间距太小,列与列之间距离过近。
  • 修改 src/views/vehicle/violation-record.vue 的 scoped 样式:限定在 .el-dialog__body .avue-form 内,将 .el-row 负边距与 .el-col 水平 padding 均设为 24px,等效 gutter 由默认 20px 增至 48px
  • 仅作用于弹窗内的 Avue 表单,不影响列表搜索栏;保持表单项纵向间距不变。

/business/process-config 弹窗改为 section-card 卡片式布局

  • 用户反馈过程配置新增弹窗需将「基本信息」「现行过程节点」「节点设置」等分组改为白卡(参考重大项目补录)。
  • 改造 src/views/business/process-config.vue + src/option/business/process-config.js
    • 取消本页 .avue-crud__dialog .avue-form 的全局自动白卡(避免白底套白底),改为灰底上直接叠 <section-card>
    • basicInfo-form<section-card title="基本信息"> 包裹;将原独立 Avue 字段 includedNodes 改为 formslot 并移入该卡片内部,用 el-select multiple 自定义渲染,保留 searchLabel 使搜索栏不受影响。
    • nodeConfigJson-form 拆分为 <section-card title="现行过程节点"><section-card title="节点设置"> 两张白卡。
    • 新增 includedNodeValues 计算属性,实现下拉框与 nodeRows.enabled / form.includedNodes 的双向同步。
    • 添加 .process-config-page__field-label--required 样式给必填项标红 *
    • 通过 scoped 覆盖消除 formslot 外层 .el-form-item 的额外 margin-bottom,由 section-card 自身控制卡片间距。
  • 验证:@vue/compiler-sfc 编译 process-config.vue template/script OKsass 编译其 scoped style OKsection-card 标签开合平衡。

修正 process-config 弹窗「卡片看不见」的真正根因(重要坑)

  • 现象:用 <section-card> 包了基本信息/现行过程节点/节点设置三张白卡,但弹窗里看不出卡片,用户反馈"还是不对"。
  • 排查发现:之前方向的"灰底缺失"是错的——全局 .el-dialog__body { background:#f5f6fa }.el-dialog .avue-form { 白卡 } 早就定义在 src/styles/element-ui.scss(216/222 行),灰底一直有。
  • 真正根因Avue CRUD 弹窗默认 append-to-body:trueDOM 挂在 <body> 下、在组件模板之外。组件 scoped 样式里的 :deep(.avue-crud__dialog .avue-form) 编译为 [data-v-x] .avue-form,要求祖先带 scoped 属性,而 append-to-body 弹窗没有该祖先 → 规则不命中 → 全局 .el-dialog .avue-form 白卡未被取消 → 白卡套白卡section-card 被白色表单卡盖住看不见。
  • 修正:作用于 append-to-body 弹窗的样式必须用 :global(...)(完全全局、不依赖 scoped 祖先)。给 option 加 dialogCustomClass: 'process-config-dialog'dialogCustomClass 由 Avue 直接挂在 .el-dialog 根元素上),把所有弹窗内覆盖规则改为 :global(.process-config-dialog .avue-form) + !important(取消全局 .el-dialog .avue-form 白卡,露出灰底),并清零 formslot 的 .el-form-item/.el-col/.el-form-item__content/.avue-form__row 占位与内边距让卡片满宽贴边。列表内的 :deep(.el-table) 仍有效(列表在组件 DOM 内),弹窗内表格需用 :global(.process-config-dialog .el-table)
  • 二级坑(本次才真正修对)DOM 路径里不要写 .avue-crud__dialog。该 class 只存在于 avue 默认 index.css,而 Avue 组件 JSdialog-form3.mjs实际只把 dialogCustomClass 加到 .el-dialog 根、内部是 .el-dialog__body > .avue-form.avue-crud__dialog 在运行时根本不渲染。之前写 .process-config-dialog .avue-crud__dialog .avue-form 整体不命中,白卡一直没被取消 → 卡片照样看不见。正确路径:dialogCustomClass(=.el-dialog) 直接作祖先,下接 .avue-form/.el-form-item/.el-col 等。
  • 三级坑includedNodes 列虽已改 formslot 且在 basicInfo 卡片内手动渲染但它没有关掉表单显示Avue 仍会为其渲染一个空的 .el-form-item(无对应 #includedNodes-form 槽),在「基本信息」与「节点设置」卡片间留下空隙。修正:给 includedNodesaddDisplay:false, editDisplay:false(保留 search:true+searchLabel 不影响搜索栏)。
  • 教训(可复用):凡要给 Avue/Element 的 append-to-body 弹窗el-dialog 默认 append-to-body写 scoped 覆盖样式,一律用 :global():deep() 在弹窗 DOM 上不生效;且选择器祖先只用 dialogCustomClass(挂在 .el-dialog),不要误加 .avue-crud__dialog(运行时不存在)。 之前 vehicle/customer-archive、loading-manage 等自定义弹窗若也用 append-to-body弹窗内 :deep 覆盖同样可能失效(它们当时多靠全局 .el-dialog .avue-form/.section-card 兜底,未暴露)。

继续推广transportCapacity 三页面弹窗 + 上传区整改

  • 用户确认 /transportCapacity/driver 改造效果正确,并要求:

    • /transportCapacity/vehicle/transportCapacity/ship 两个页面的弹窗也按同样 <section-card> 标准整改。
    • 上传证件区域整改为:① 宽高比 = 中国二代身份证 1.586:185.6 × 53.98 mm② 区域大小取真实身份证的 50%,固定 165 × 104 px③ 适用范围 = 全部上传区(含机动车行驶证主页正/反/副页正/反、道路运输证、机动车登记本、船舶所有权/安全环保/国籍/最低安全配员/光船租赁/营业运输 共 6 张 + 行驶证 4 张),居中显示不再 100% 占栏。
  • 已落地:src/views/transportCapacity/driver.vue + vehicle.vue + ship.vue 三页面:

    • vehicle.vue: 2 个分组 <div class="section-title"> 替换为 <section-card title="基础车辆信息/车辆资质图片">;删除 .section-title.section-title:first-child scoped 规则。
    • ship.vue: 2 个分组替换为 <section-card title="基础船舶信息/船舶证书信息">6 个子证书仍保留 .cert-block + .cert-block__title 做内部子分段(外层白卡 + 内层 1px border-top 分隔线,视觉层次合理);删除 .section-title.section-title--wide.section-title:first-child scoped 规则。
    • driver.vue: 之前已用 <section-card> 完成,本次仅收尾上传区尺寸。
    • 三页面的 uploader 样式统一改造:.xxx-uploader 取消强制 width:100%;新增 .xxx-uploader--large { display:block; width:165px; margin:0 auto }.xxx-uploader--large .el-upload { width:100%; height:104px }--large 选择器特定性 0,0,1,0 写在最后覆盖默认规则,源码位置在 .xxx-uploader 之后,确保 CSS 顺序覆盖生效)。
    • 保留每个 class-prefix 的非 large 默认高度170px、placeholder 边框/背景/居中等样式。
  • 验证:@vue/compiler-sfc 编译三个 .vue 均 OK<section-card> 标签数量 driver 4/4、vehicle 2/2、ship 2/2 平衡vite dev 服务对 /transportCapacity/{driver,vehicle,ship} 均返回 200prettier 通过。

  • 沉淀到 MEMORY.md 的设计约定:「上传证件区域 = aspect-ratio: 1.586 + 固定 165 × 104 px + 居中」为全 transportCapacity 模块统一的硬规范。后续若其他模块也使用 <image-upload-field large> 应套用同款规范。

上调 transportCapacity 三页面上传证件区域尺寸165×104 → 240×151并左对齐

  • 用户反馈上一轮的 165×104真实身份证 50%)偏小、看不清楚。
  • 三页面(driver/vehicle/ship.vue)的 .xxx-uploader--large 尺寸统一上调到 240 × 151 px(约真实身份证 75%,保持 1.586:1对齐方式由 margin: 0 auto 居中改为 margin: 0 左对齐。
  • 仅改三页面 scoped 样式里的 width / height / margin 三处数值,未动模板与组件逻辑;非 large 默认高度 170px 等样式保留。
  • 同步更新 MEMORY.md「上传证件区域尺寸规范」为 240×151 + 左对齐。

规划:将 process-config.vue 弹窗布局对标 project-apply.vue尚未落地

  • 用户要求参考 /business/project-apply.vue 重构 process-config.vue主要是 UI 布局
  • 现状回顾process-config 弹窗卡片已用 <section-card> 包裹basicInfo / 现行过程节点 / 节点设置),但弹窗机制仍是 Avue avue-crud 表单槽(#basicInfo-form/#nodeConfigJson-form+ before-open/row-save/row-update;字段用 el-row/el-col :span=12 + 手写 .process-config-page__fieldflex 左标签右控件)。
  • 目标对标 project-apply① 列表只做表格/搜索,新增/编辑/查看改为自定义 el-dialog + 手写 el-form(去掉 Avue 表单槽与 before-open/row-save/row-update② 字段布局从 el-row/el-col + 手写 label 改为 CSS 两列网格 .__gridcolumn-gap 72px+ 原生 el-form-item labelright/auto;③ 卡片继续用 <section-card>(项目统一标准,不回头手写白卡);④ 底部「取消/提交」(查看态隐藏提交)。
  • 逻辑NODE_TEMPLATES、parseNodeConfig、validateRow、loadProjectOptions、includedNodeValues 计算属性)全部保留,仅把 Avue 钩子改为自定义 openConfigDialog/submitConfigapi.submit/copy/enable/disable/remove/getDetail 不变。
  • 待确认决策:卡片实现用 <section-card>(推荐,符合项目标准)还是 1:1 还原 project-apply 的手写白卡(与项目标准不一致)。用户仅要求先规划,未授权落地。

已落地process-config.vue 弹窗重构为自定义 el-dialog对标 project-apply卡片用 section-card

  • 已按方案执行:src/views/business/process-config.vue 改为「列表 avue-crud仅表格/搜索)+ 自定义 el-dialog 新增/编辑/查看」结构。
  • 关键改造:① 删除 @before-open/@row-save/@row-update#basicInfo-form/#nodeConfigJson-form 表单槽;② 新增 el-dialog.process-config-dialog1440px / top4vh / append-to-body / destroy-on-close+ 手写 el-form.ref=configFormlabel right/auto+ 两列 CSS 网格 .process-config-form__gridcolumn-gap 72px③ 三张白卡仍用 <section-card>(基本信息 / 现行过程节点 / 节点设置);④ 底部「取消 / 提交」(查看态隐藏提交)。
  • 逻辑保留NODE_TEMPLATES、parseNodeConfig、validateRow、loadProjectOptions、includedNodeValues 计算属性、节点表格/时间轴渲染均不变;仅把 Avue 钩子换成 openConfigDialog/resetConfigDialog/submitConfig,新增 formRules + configDialogTitle + handleValidateFail
  • 样式:删除原 :global(.process-config-dialog .avue-form …) 及其 .el-form-item/.el-col/.avue-form__row 覆盖(不再用 Avue 表单);新增 :global(.process-config-dialog .el-dialog__body){ max-height:76vh; overflow-y:auto }(灰底 16px 已由全局规则提供);.process-config-form 作用域样式grid/label min-width160/节点表头 #f5f7fa/偶数行 #fafafa/timeline/require-*)。
  • 注意:自定义 el-dialog 是 append-to-body,作用于弹窗的覆盖样式仍须 :global().el-dialog__body.process-config-form 及其 :deep() 因内容由本组件渲染、带 scoped 属性可命中。
  • 验证:@vue/compiler-sfc 编译 template+script OK、scoped style 括号配平 OK无 beforeOpen/rowSave/forms lot 残留引用。

全局组件 image-upload-field 增加点击放大预览

  • 用户要求证件图片可点击放大查看。确认方案:用 Element Plus 原生 el-imagepreview-src-list+ 改全局组件一次、driver/vehicle/ship 三页自动生效。
  • 改造 src/components/image-upload-field/main.vue:① 两模式form / card<img> 都换成 el-imagepreview-src-list=[value], fit=contain, preview-teleported, z-index=3000, @click.stop② form 模式图片下方加 el-link「更换」、card 模式 head 的 actionText 改 el-link 可点;③ 新增 triggerUpload()el-upload 隐藏 input.el-upload__input.click() 触发文件框el-upload 仅 expose abort/submit/clearFiles/handleStart/handleRemove无 openFileDialog须 DOM 方式);④ @click.stop 阻止图片点击冒泡到 el-upload 误触发重传;⑤ readonly 时仅预览、无上传入口。
  • 兼容性:三页 scoped 样式同时写了 .xxx-uploader .el-upload(后代)与 .xxx-uploader.el-upload(同元素)两种选择器,把 .xxx-uploader 从 el-upload 移到外层 div 后高度样式仍生效;.xxx-uploader--large 同理。
  • 验证:@vue/compiler-sfc 解析 main.vue + 三页均 OKvite dev 对三页均返回 200确认 el-upload 内部隐藏 input 类名为 .el-upload__input(源码 ns.e('input'))。