Files
tms-erp-web/.workbuddy/memory/2026-08-21.md
T
gxwebsoft 4edaa8486d style(settlement): 优化表格列居中及单据号列固定,调整审批状态展示
- payable-detail 单据号列新增固定左侧配置,保障滚动时可见
- receivable-payable-detail 组件表格动态列透传 fixed 属性
- project-apply 列表所有列单元格及表头居中显示,审批状态列改为纯文字
- 全站表格列(含操作列按钮)全局居中,样式统一覆盖 el-table 组件
- 修复 AVUE 依赖表单 label 冒号默认显示,pnpm patch 修改 es 与 lib 版本同步
- 解决 Vite optimizeDeps 缓存问题,patch 依赖后需删除 vite 缓存并重启 dev server
- railway-station 弹窗表单全局 labelSuffix 设为空,去除 label 后冒号
2026-08-21 18:04:59 +08:00

247 lines
37 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-08-21
## temporary-credit-limit 详情弹窗上下居中(align-center
- 需求:`/business/temporary-credit-limit` 详情弹窗(detailBox,走 business-crud-page 自定义详情)上下居中。
- 机制:Element Plus el-dialog 的 `align-center` prop —— use-dialog 源码确认:align-center 时 `.el-overlay-dialog``display:flex``.el-dialog.is-align-center { margin:auto }`,实现垂直+水平居中;此时 top prop 设置的 `--el-dialog-margin-top` 被更高特异性的 `margin:auto` 覆盖,不生效。
- 改动(3 处,开关式不影响其他页):
1. `src/views/business/components/business-crud-page.vue` 详情 el-dialog2504 行)加 `:align-center="detailAlignCenter"`
2. 同文件新增 computed `detailAlignCenter()``this.config.detailAlignCenter === true`(默认 falsecontract/waybill 等共用详情弹窗不受影响)。
3. `src/option/business/temporary-credit-limit.js` config 加 `detailAlignCenter: true`
- 安全性:全局 `.el-dialog__body { max-height: calc(100vh - 200px); overflow-y:auto }`element-ui.scss 182 行)保证内容超高时在 body 内滚动,dialog 总高 < 视口,flex 居中不会顶部裁切。
- 注意:element-plus 实际版本是 2.14.2package.json 声明 ^2.10.1,锁到 2.14.2)。el-dialog 的 class 经 $attrs 透传最终落在 `div.el-dialog` 根元素(ElDialogContent 未设 inheritAttrs:false);因此 scoped 里 `&__detail-dialog.is-transport-plan-detail { :deep(.el-dialog){...} }` 的 max-width/margin 规则实际选不到自身(仅 header/body 后代规则生效)——历史遗留,未处理。
- 验证:`node --check` 通过;Vite 2889 编译两个改动文件均 HTTP 200。
## temporary-credit-limit 弹窗宽度 1400px → 96%
- 需求:`/business/temporary-credit-limit` 新增/编辑弹窗加宽到 96%。
- 改动(单文件 `src/option/business/temporary-credit-limit.js`):`dialogWidth: 1400``dialogWidth: '96%'`
- 先例:waybill-manage / transport-plan 均为 `dialogWidth: '96%'`shipping-template 为 `'92%'`。business-crud-page 对 dialogWidth 无特殊处理(仅独立表单页强制 100%),百分比直接透传 avue-crudelement-ui.scss 的 `.temporary-credit-limit-dialog` 只覆 label 宽度、不覆盖 el-dialog 宽度,无冲突。
## process-config 弹窗卡片间隔(对齐 project-apply 风格)
- 需求:process-config 新增/编辑弹窗内三张 `<section-card>`(基本信息 / 现行过程节点 / 节点设置)要像 project-apply 表单页一样独立成卡、卡间露出灰底(12px)。
- 根因:全站规则 `.el-dialog .avue-form { background:#fff; padding:8px 16px 4px }`element-ui.scss ~246 行)把 Avue 弹窗表单整体包成一张白卡,section-card 白卡叠在其上看不出间隔。
- 改动(仅 2 文件):
1. `src/option/business/process-config.js`option 顶层加 `dialogCustomClass: 'process-config-dialog'`
2. `src/styles/element-ui.scss`contract-manage-dialog 规则块后新增):
- `.process-config-dialog .avue-form { background:transparent; box-shadow:none; padding:0 }` —— 白卡透明化,灰底(.el-dialog__body 全局 #f2f2f2)露出,section-card 自带 margin-bottom:12px 形成卡距。
- `.process-config-dialog .avue-form__group > .el-col > .el-form-item { margin-bottom:0 }` —— formslot 外层 form-item margin 与卡片 margin 叠加会拉大卡距,归零统一 12px。
- `.process-config-dialog .avue-dialog__footer` + `.avue-crud__dialog .process-config-dialog .avue-dialog__footer { margin-top:0 !important }` —— 抵消全局 947 行 `.avue-crud__dialog .avue-dialog__footer { margin-top:-30px !important }`(form 透明化后会上移压卡)。双选择器兼顾 dialog 未 teleport / teleport 两种挂载。
- 已验证:Vite 2889 编译通过、HMR 生效。
- 备注:`menu-form-before` 插槽(保存草稿/保存并启用按钮)渲染在 `.avue-dialog__footer` 内(avue 源码 dialog-form3.mjs 确认),不受 form 透明化影响,仍处白底底栏。
## 合同管理表单页:多分组卡片风格(对齐 project-apply 风格)
- 需求:合同管理新增/编辑表单页(`/business/contract-manage/form?mode=add|edit`)的 7 个业务分组(基本信息 / 合同文件 / 计费信息 / 结算单规则 / 付款比例设置 / 其它附件 / 变更记录)要像 project-apply 一样独立成白底卡、卡间 12px 间距、标题 4px 主色蓝条。
- 关键事实(容易踩坑):
- 合同表单走 `PageAvueForm` 组件(`src/views/business/components/business-crud-page.vue:5577`),render 函数 `h('div', { class: option.dialogCustomClass }, [h('avue-form', ...)])`**不包 el-dialog**(所以底部按钮天然不浮动贴底,会随内容滚动——这点与 project-apply 的 el-dialog #footer 浮动有差异)。
- `option` 顶层在 `isStandaloneFormPage` 时已设 `dialogCustomClass: 'contract-manage-form-dialog'``dialogAppendToBody: false``dialogModal: false``dialogTop: '0'``dialogWidth: '100%'`5897-5914 行)。
- 7 个分区标题列在 option.column 里以 `order` 升序分块(`basicInfoTitle=200``contractFileTitle=30``billingInfoTitle=10``settlementRuleTitle=-10``paymentRatioTitle=-30``attachmentTitle=-50``changeRecordTitle=-70`),其余字段 order 在每对标题列之间。
- 实施方案(已提交 63256a9):
1. `buildContractFormGroupOption(option)`business-crud-page.vue 新增方法):按 order 降序遍历 option.column,遇到 7 个 `*Title` 列就切片分组;`groups.length<=1` 时回退原 option(不影响其他业务弹窗)。返回 `{ ...option, column: groups[0], group: groups.slice(1).map(g=>({ label:'', arrow:false, column:g })) }`
2. `pageFormOption`6205 行)`isContractFormPage` 时调用上述方法。
3. CSSbusiness-crud-page.vue scoped 末尾):
- `.contract-manage-form-dialog``padding:20px 24px 96px; background:#f5f6fa; box-sizing:border-box;`(外层灰底)
- `.business-crud-page--contract-form .avue-form { background:transparent; padding:0 }`(表单白卡透明化)
- `.business-crud-page--contract-form .avue-form > .el-form > .avue-form__group``.avue-group` 都设白底 + 6px 圆角 + 0 1px 2px 阴影 + 12px margin-bottom + 14px 16px 4px padding(两张选择器分别覆盖第 1 组 column 与第 2-7 组 group 渲染)
- 验证(agent-browser + 系统 Chrome,已登录后截图核对):
- 合同表单 7 张白底卡清晰分隔,每张卡左侧 4px 主色蓝条 + 标题(基本信息 / 合同文件 / 计费信息 / 结算单规则 / 付款比例设置 / 其它附件 / 变更记录),卡间约 12px 灰底露白。
- 底部 5 个按钮(提交 / 关闭 / 保存 / 提交临时合同 / 提交正式合同)随内容滚动(非浮动贴底)。
- 与 project-apply 滚动后的"客户信息 / 承运商信息"卡风格完全一致。
- 与 project-apply 的已知差异(用户本次未要求改):
- 合同表单底部按钮非浮动贴底(PageAvueForm 不包 el-dialogavue-form 的 form_menu 渲染在内部)
- 合同表单无"返回"按钮(用"关闭"代替)
- 合同表单无独立页面标题(page-header 区域在 breadcrumb 处,不在 main 标题区)
- 行为验证码:登录页拼图/旋转求解基础设施已搭好——`/tmp/captcha_solve.py`CORS 修复后支持 puzzle+rotate 双模式)、`/tmp/ab-install/node_modules/.bin/agent-browser --browser-channel chrome`。登录流程:访问 → 切"账号密码登录" → 点登录 → 检测 `.puzzle-stage`/`.rotate-image` → POST 至 http://127.0.0.1:8765 → 取 xDisp/angle → JS PointerEvent 模拟拖动 .track-handle 即可。
- 提交:63256a9 feat(business-crud-page): 合同管理表单页改为多分组卡片风格(1 file, +86/-2)。未提交改动(process-config.js、element-ui.scss、master-order-editor.vue、loading-manage.vue、project-apply.vue、formal-settlement.vue、customer-archive.vue)与本任务无关,未纳入。
## shipping-template 独立表单页加 archive-page-form__title(参考 customer-archive
- 需求:独立表单页 `/business/shipping-template/form?mode=add&name=新运费发货模板` 顶部缺页面标题,参考 customer-archive 的 `.archive-page-form__title` 加标题。
- 改动(单文件 `src/views/business/components/business-crud-page.vue`):
1. `PageAvueForm`5578 行)声明 `props.formPageTitle`render 时若非空则先 push `<div class="archive-page-form__title">{{ title }}</div>` 再渲染 avue-formtitle 在表单容器 `shipping-template-dialog` 内、avue-form 之前)。
2. 模板 `<component :is="crudContainer">` 新增 `:form-page-title="isStandaloneFormPage ? formPageTitle : undefined"`
3. 新增 computed `formPageTitle()``isStandaloneFormPage && isShippingTemplatePage` 时返回 `this.$route.query.name || '新增'/'编辑' + config.title`,否则 ''。
4. 样式(scoped 末尾,用 `:global` 因为 title div 由 PageAvueForm 函数式组件渲染、无 scoped 属性):`:global(.business-crud-page--form-page .archive-page-form__title)` 复制 customer-archive 的 18px/600/#303133 + margin-bottom:20px::before 为空,与参考一致,仅纯文字标题)。
- 仅作用于 shipping-template 独立表单页;其他独立表单页(waybill/transport-plan/contract`formPageTitle` 返回 '',不受影响。
## transport-plan 独立表单页也加 archive-page-form__title
- 需求:`/business/transport-plan/form?mode=add&name=新运费运输计划` 也要同样的页面标题。
- 改动:`formPageTitle()` 条件由 `isStandaloneFormPage && isShippingTemplatePage` 改为 `isStandaloneFormPage && (isShippingTemplatePage || isTransportPlanPage)`(复用已有 `isTransportPlanPage` computed`config.permission==='transport_plan'`)。标题文案取 `this.$route.query.name`(「新运费运输计划」)或回退「新增/编辑运输计划」。
- 验证:Vite 2889 编译通过(HTTP 200,无错误)。
- 验证:Vite 2889 编译通过(HTTP 200,无 transform/syntax 错误),HMR 生效。如需扩展到其他独立表单页,去掉 `isShippingTemplatePage` 限定即可。
## 将 .archive-page-form__title 全局化,并再加 waybill / loading-manage / master-order 三页
- 需求:把页面标题扩展到 `/business/waybill-manage/form``/business/loading-manage/form``/business/master-order?mode=editor` 三页,并与 customer-archive / shipping-template / transport-plan 视觉统一。
- **全局化样式**`.archive-page-form__title` 原本分散在 `customer-archive.vue`scoped)与 `business-crud-page.vue``:global`,::before 为空纯文字)。现统一提取到 `src/styles/element-ui.scss`5b 段):18px/600/#303133 + margin-bottom:20px + `::before` 4px 主色竖条 + 8px 间距(补全竖条,与 section-card 卡头风格一致)。并删除上述两处本地重复定义。
- **waybill-manage/form**:在 `business-crud-page.vue``formPageTitle()` 条件再并入 `isWaybillDetailLayout``config.permission==='waybill_manage'`),复用既有 computed。标题取 `query.name || '新增/编辑运单管理'`
- **loading-manage/form**:该路由直接映射到 `loading-manage.vue`(非 business-crud-page)。新增 computed `formPageTitle()``query.name || '新增/编辑配载管理'`),并在独立表单页的 `<component>` 内、body 之前加 `<div v-if="isStandaloneFormPage" class="archive-page-form__title">{{ formPageTitle }}</div>`
- **master-order?mode=editor**`master-order.vue` 渲染 `master-order-editor.vue`。将 `<master-order-editor>` 包进 `<template v-else-if="mode==='editor'">`,前置 `<div class="archive-page-form__title">{{ masterEditorTitle }}</div>`;新增 computed `masterEditorTitle``routeId ? '编辑总单' : '新增总单'`)。
- 验证:4 个 SFC + element-ui.scss 经 Vite 2889 编译均 HTTP 200、无 transform/syntax 错误。
## 综合台账弹窗 & 临时额度查看弹窗 布局调整
- vehicle-ledger / ship-ledger/transportCapacity 车辆·船舶综合台账弹窗):把 `<el-table>`+分页从 `el-tabs` 外移入每个 `<el-tab-pane>` 内(切换 tab 表格跟着切换);ship-ledger 同步把三个蓝标内容块(船舶基本信息/船舶证书信息/证书附件)由 `<section>`+`.dialog-section-title` 改为全局 `<section-card :title>`,卡片间距 12px。两文件均加 `:deep(.el-tabs__content){padding:0}` 去 border-card 默认内边距。
- 临时额度查看弹窗改两列(/business/temporary-credit-limit):
- 原"查看"调 Avue 内置 `rowView`(列数复用 column.span,呈 3 列)。改用 business-crud-page 内置自定义详情弹窗。
- `src/option/business/temporary-credit-limit.js` config 加 `detailButton:true``detailButtonIgnoreDraftStatus:true``detailSections:[申请信息(申请单号/备注 span:2 整行,其余两列)、额度信息]`
- `business-crud-page.vue``showDetailButton``if(this.config.detailButtonIgnoreDraftStatus) return true`(仅放开草稿态也走自定义详情,不动其他页面)。
## contract-manage/form 独立表单页也加 archive-page-form__title
- 需求:`/business/contract-manage/form?mode=add&name=新运费合同管理` 也要同样顶部页面标题。
- 改动:`business-crud-page.vue``formPageTitle()` 条件再并入 `isContractConfig``config.permission==='contract_manage'`contract-manage 已用 `contract-form-page` prop 走 `PageAvueForm`)。标题取 `query.name`(「新运费合同管理」)或回退「新增/编辑合同管理」。
- 验证:Vite 2889 编译通过(HTTP 200,无错误),HMR 生效。至此 business-crud-page 系列(shipping-template / transport-plan / waybill-manage / contract-manage)独立表单页均已带统一标题。
- 渲染走 `detailSections` 分支(`section-card` + `el-descriptions(:column=2)`),值经 `formatDetailValue` 转字典 label。
- 验证:`temporary-credit-limit.js``node --check` 语法通过;business-crud-page.vue 仅 template 表达式改动。待用户在 2889 刷新核对两列效果。
## 合同管理详情弹窗 label 对齐修复
- 需求:`/business/contract-manage` 详情弹窗里两列 `el-descriptions` 的 label(尤其右边列 label)对齐不一致,长 label(如"是否需要加盖法人章")换行后行高/垂直位置错乱。
- 根因:`business-crud-page.vue``&__detail-content .section-card` 中对 `.el-descriptions__label` 只写了 `width: 140px``text-align: right`,缺 `min/max-width``vertical-align``line-height``box-sizing`;对 `.el-descriptions__content` 也缺垂直对齐,导致跨列/跨行 label 视觉上不齐。另外 `contract-manage.vue` 底部的 `<style>`(未加 `scoped`)把 `.el-dialog .el-form-item__label``.el-col` 全局覆盖,会污染全站弹窗。
- 改动:
1. `src/views/business/components/business-crud-page.vue`15440 行附近):
- label 与 content 统一加 `box-sizing:border-box``vertical-align:middle``line-height:1.4`、一致 paddinglabel 加 `min-width:140px; max-width:140px` 防止被内容撑开或压缩,content 保持 `min-height:42px`
- **新增 `:deep(.el-descriptions__table) { width:100%; table-layout:fixed }`**:强制表格按固定列宽渲染,label 占 140px 后,左右两个 content 列均分剩余宽度,解决用户截图里"合同期限"卡片左右内容区宽度不一致的问题。
2. `src/views/business/contract-manage.vue`:把底部无 scoped 的全局样式改为 `<style scoped>`,并把选择器限定到 `.contract-manage-dialog`;删除污染性更强的 `.el-col { margin-bottom:0 !important }` 全局覆盖。
- 验证:Vite 2889 编译通过(HTTP 200),HMR 生效。待用户在 2889 刷新核对详情弹窗两列 label 是否左右/上下对齐、左右内容区宽度是否均等。
## 应付明细调整弹窗:控件撑满单元格 + 表头居中
- 需求:`/settlement/payable-detail`(即 `receivable-payable-detail.vue`)"调整费用"弹窗中,里程 / 杂项 / 装卸费等列的 `el-input-number` 默认宽度 180px,超出窄列后被截断(见截图);同时要求所有表头居中。
- 改动(单文件 `src/views/settlement/receivable-payable-detail.vue`):
1. 给调整弹窗 `<el-table>``class="settlement-detail-page__adjust-table"`
2. 页面级表格样式 `.settlement-detail-page__table, .settlement-detail-page :deep(.el-table)``th.el-table__cell` 增加 `text-align: center !important;`,统一所有表头居中(覆盖金额列 `align: right` 导致的表头右偏)。
3. 新增 `.settlement-detail-page__adjust-table` 样式块:`.el-input-number``.el-input``.el-select` 全部 `width: 100% !important;`,使控件撑满单元格不被截断;`el-input-number` 内部输入保持右对齐。
- 验证:Vite 2889 编译通过(HTTP 200),HMR 生效。待用户在 2889 刷新核对调整弹窗输入框是否完整显示、表头是否全部居中。
## formal-settlement:把表格放进 tabs
- 需求:`/settlement/formal-settlement` 原本 `el-tabs`(应付/应收) 是空的 type 切换器,表格+工具栏在 tabs 外面;要求把表格放进 tabs 内。
- 改动:
1. 新建 `src/views/settlement/components/formal-settlement-table-panel.vue`:把工具栏(新增/付款申请/同步金蝶/打印结算单/导出 + 刷新) + `el-table` + 分页整体抽成子组件;props 收 `rows/columns/loading/page/hasPermission`emits `create/payment/sync/export/refresh/page-change/update:selection/action`;行操作(查看/编辑/删除/提交/通过/驳回/作废)经 `rowActions(row)` 计算可见项后 emit `action {type,row}`;工具栏"打印"经 `emitSingle('print')` 取选中单行后 emit action(与父 `selectedOne` 行为一致)。
2. `src/views/settlement/formal-settlement.vue`:删除原 toolbar+table+pagination 块与对应 scoped 样式(`__toolbar/__toolbar-right/__links/__pagination`),在 `el-tabs` 两个 `el-tab-pane` 内各放一个 `<formal-settlement-table-panel>`DRY,共用同一组件);新增 `handlePageChange({current,size})``handleAction({type,row})` 分发行操作;`handlePrint(row)` 改为接收行参数(不再用 selectedOne);删除无用 `handleSizeChange`
- 验证:Vite build 通过(EXIT=0),无编译错误。待用户在 2889 刷新核对:应付/应收两个 tab 各自带完整表格与工具栏、切换 tab 触发 reload。
## 预结算单结算汇率输入框居左
- 需求:`/settlement/pre-settlement` 弹窗中「结算汇率」字段(el-input-number)默认居中,要求改为居左。
- 改动:
1. `src/option/settlement/preSettlementForm.js``exchangeRate` 字段新增 `class: 'exchange-rate-input'`
2. `src/views/settlement/components/pre-settlement-editor.vue`
- `el-input-number` 上绑定 `:class="field.class"`
- scoped style 的 `&__form` 内增加 `:deep(.exchange-rate-input .el-input__inner) { text-align: left; }`
- 影响:仅针对「结算汇率」字段,不改动其他数字输入框对齐方式。
## transport-reconciliation:同样把表格放进 tabs
- 需求:`/settlement/transport-reconciliation` 与 formal-settlement 同样问题——`el-tabs`(应付/应收) 是空 type 切换器、工具栏+表格在 tabs 外;要求把表格放进 tabs 内。
- 改动:
1. 新建 `src/views/settlement/components/transport-reconciliation-table-panel.vue`:工具栏(新增/导出+刷新)+`el-table`+分页抽成子组件;props 同 formal 面板,emits `create/export/refresh/page-change/update:selection/action`;列渲染差异:`.link` 列点击 emit view、`reconciliationStatusName``row.reconciliationStatus==='completed'?'success':'warning'` 的 el-tag;行操作 `rowActions(row)` 为 查看(常显)+编辑/删除/确认(均 `hasPermission && isEditable(row)`isEditable=`reconciliationStatus==='unfinished'`),确认非 danger。
2. `src/views/settlement/transport-reconciliation.vue`:删除原 toolbar+table+pagination 块与对应 scoped 样式(`__toolbar/__toolbar-right/__links/__pagination`),在两个 `el-tab-pane` 内各放一个面板;保留 `.reconciliation-page__settlement-tabs`(含 `:deep(.el-tabs__header){margin:0}`);新增 `handlePageChange``handleAction({type,row})`(view→openView/edit→openEdit/delete→handleDelete/complete→handleComplete);删除 `handleSizeChange`。导出按钮无权限守卫、仍 emit `export` 由父 `handleExport`(用 `this.settlementType`)处理。
- 验证:Vite build 通过(EXIT=0)。待用户在 2889 刷新核对应付/应收 tab 各自带完整表格与工具栏、切换触发 reload。
## process-config 弹窗重写为手写 el-dialog(对齐 credit-score-quantification 风格)
- 原因:用户参考 `/vehicle/credit-score-quantification` 的弹窗(手写 `el-dialog` + 多个 `<section-card>` 堆叠、卡间 12px、底部 #footer 按钮),要求 process-config 弹窗同样"白底卡片包住蓝标内容块、卡间距 12px、风格统一"。原实现用的是 Avue 内置 dialog + formslot,与参考页机制不一致(即使视觉接近)。
- 改动(3 文件):
1. `src/views/business/process-config.vue`:删除 Avue 内置 dialog 相关(`before-open``#basicInfo-form`/`#nodeConfigJson-form`/`#menu-form-before` 插槽、`rowSave/rowUpdate/submitRow/stopSubmitLoading/handleProcessConfigSave`、数据项 `submitStatus`);新增手写 `<el-dialog class="process-config-dialog" append-to-body width="96%" top="4vh" :close-on-click-modal="false">`,内部 `<div class="process-config-form">` 堆三张 `<section-card>`:基本信息 / 现行过程节点(timeline)/ 节点设置(node 表);`#footer` 放 取消 / 保存草稿(plain) / 保存并启用(primary)`submitting` 控制 loading。新增 `openConfig(row, readonly)``submitConfig(mode)``handleDialogClosed`,保留全部节点配置逻辑(NODE_TEMPLATES/cloneNode/parseNodeConfig/syncNodeFields/timelineDescriptions/validateRow/checkEnabledProjectBeforeSubmit/confirmDuplicateVoucher 等)。菜单 查看→`openConfig(row,true)`、编辑→`openConfig(row)`、新建→`openConfig()`
2. `src/option/business/process-config.js`option 删除仅作表单容器的 `basicInfo`/`nodeConfigJson` 两列与 `saveBtn/updateBtn/dialogTop/dialogWidth/dialogCustomClass` 等 dialog 配置。
3. `src/styles/element-ui.scss`:删除原先 `.process-config-dialog .avue-form / .avue-form__group / .avue-dialog__footer` 三处透明化/归零规则(已无用),改为 `.process-config-dialog .el-dialog__body { max-height: calc(100vh - 130px) }`
- 关键点:弹窗内容走 `append-to-body` 会被 teleport 出 `.process-config-page`,故所有弹窗内样式(timeline/node-table/require-*/finish-days)改用顶层 scoped 类 `.process-config-form__*`(不再嵌套在 `.process-config-page` 下),否则 scoped 选择器因缺祖先类而失效。12px 卡距由全局 `.section-card{margin-bottom:12px}` + 全局灰底 `.el-dialog__body{background:#f2f2f2;padding:12px}` 保证,与参考页一致。
- 验证:Vite build 通过(EXIT=0),无编译错误;2889 HMR 生效。待用户刷新核对弹窗卡片风格。
## 多页面「基本信息」类分区统一改为 4 列(span 8→6)
- 用户逐一要求把各结算/合同页的「基本信息」分区从 3 列改为 4 列。列数由 el-col `:span` 控制(24/8=3 → 24/6=4)。
- 手写 el-col 循环页面(直接改 span):
- `src/views/settlement/components/formal-settlement-editor.vue``:span="8"``:span="6"`(结算基本信息)。
- `src/views/settlement/components/transport-reconciliation-editor.vue``:span="8"``:span="6"`(导入外部账单核对)。
- `src/views/settlement/components/settlement-adjustment-editor.vue``:span="8"``:span="6"`(基本信息)。
- `src/views/settlement/components/pre-settlement-editor.vue`:动态 `:span="field.span || 8"``|| 6`(仅备注显式 span:24 仍整行)。
- `src/views/business/contract-manage-change.vue``基本信息``:span="8"``:span="6"`(变更类型/备注 `:span="24"` 保持整行)。
- Avue 分组表单页面(合同新增/编辑 form 页):非手写循环,`option.span=8` 控制默认列数。
- 改动 `src/views/business/components/business-crud-page.vue``buildFormGroupOption`:当 `this.isContractConfig` 时,对首组(基本信息,=groups[0] 顶层 column)每个非分区标题、非 `span===24` 的列设 `col.span = 6`;备注(span:24)与分区标题列保持原样。其余分组仍 3 列。
- 验证:2889 HMR 生效。注意合同 form 页走 Avue group,不能用简单 span 替换,须改 buildFormGroupOption。
## 续:更多页面「基本信息」类分区改 4 列(span 8→6)
- `src/option/business/temporary-credit-limit.js`Avue 配置页,扁平 column 列表,8 个表单字段 `span:8``span:6`(备注/附件 span:24 保持整行)。注意 createCrudOption 不设默认 span,可见字段都有显式 span。
- `src/views/transportCapacity/ship.vue`:「基础船舶信息」10 个 `:span="8"``:span="6"`;该页 12 空格缩进=基础船舶信息区、14 空格缩进=船舶证书信息区,**勿用全局 replace_all**12 空格串是 14 空格的子串会误伤证书区),必须按行号区间 sed(146,253)或带 label 上下文逐个替换。
- `src/views/business/project-apply.vue``.project-apply-form__grid` `repeat(3,...)``repeat(4,...)`label 规则(作用域 `__grid` 内)`width:100px !important; min-width:0 !important; white-space:normal`——6 汉字宽超出换行,`!important` 压过 label-width="auto" 的行内 width 与全局 min-width:160px。
## ship 页 label 规则:最多 6 汉字宽、超出换行
- `src/views/transportCapacity/ship.vue`el-form `label-width`(原 HEAD 为 auto,工作区历史为 150)→ `100`6 汉字≈84px+冒号≈98px100px 恰容 6 字);`.ship-form` 内新增 `:deep(.el-form-item__label) { white-space: normal; line-height: 20px }` 允许换行。适用整个 ship 表单(含证书区)。
- 注意:改该文件前先确认 git 暂存区状态,`git checkout -- <file>` 恢复的是 index 版本;本文件 index=HEAD=auto,工作区曾为 150(历史未提交改动),已被本次需求覆盖。
## master-order-dispatch 起-终路由条左侧节点被裁切修复
- 问题:`/business/master-order?mode=dispatch` 顶部 overview 的路由节点条(起/经/终),最左侧"起(梧州段)"被裁掉半字。根因:`.route-overview``justify-content: flex-end` 右对齐 + `overflow-x:auto`,节点总宽(4×130+3×56=688px)超出右侧栏可用宽度时,溢出从左侧跑出可视区,且节点 `flex:0 0 130px` 写死、长名字会撑爆。
- 改动(`src/views/business/components/master-order-dispatch.vue` scoped 样式,3 处):
1. `.route-overview``justify-content:flex-end``flex-start`padding 改为 `8px 24px 0`(左右留白,滚动时两端节点完整可见)。
2. `.route-overview__node``flex:0 0 130px``flex:0 0 auto; min-width:130px; max-width:200px; padding:0 4px`strong/small 加 `max-width:100%; overflow:hidden; text-overflow:ellipsis`(长名字省略号不撑爆)。
3. `.route-overview__line``flex:1 0 56px; min-width:56px``flex:0 1 56px; min-width:24px`(line 可收缩,滚动兜底更平滑)。
- 说明:1200px media query 里原有 `justify-content:flex-start` 现与默认一致,冗余但无害,保留未删。仅样式改动,无 JS 逻辑变化。
## master-order-dispatch 路由节点地址/联系人加 tooltip(hover 提示完整内容)
- 需求:起-终节点条地址过长截断("九头山货..."等)时,鼠标移上去能看到完整内容。
- 改动(`src/views/business/components/master-order-dispatch.vue` 模板,21-45 行):起/经、终节点各自的 strong(名称)+ 两个 small(地址、联系人)共 6 个文本元素分别用 `<el-tooltip placement="top" :show-after="120">` 包裹,`:content` 与原文本一致;保持原有 `text-overflow:ellipsis` 截断视觉不变。
- `:show-after="120"` 让鼠标停留 120ms 才弹,避免快速划过连续触发多个 tooltip;`placement="top"` 配合节点垂直居中位置弹出,体验稳定。
- 风险:仅模板增加 6 个 el-tooltip 实例,无 JS/style 改动;与之前段头 `el-tooltip`segment-checkbox-label)用法一致。
## shipping-template(发货模板)表单页:模板信息+基本信息 4 列 + label 统一宽度
- 需求:`/business/shipping-template/form` 的「模板信息」「基本信息」两组改 4 列;左侧 label 宽度一致右对齐;全站统一(6 汉字宽、超出换行)。
- 改动 3 处:
1. `business-crud-page.vue` `buildFormGroupOption` 新增 `isShippingTemplatePage` 分支:对 groups[0](模板信息)、groups[1](基本信息)的非标题、非 span24 列设 `span:6`(该页分组:templateInfoTitle/basicInfoTitle/shippingInfoTitle/goodsInfoTitle/attachmentTitle 均已在 sectionTitleProps,发货信息及之后组保持原样)。
2. `src/option/business/shipping-template.js`option 顶层加 `labelWidth: 100`(覆盖 createCrudOption 的 'auto',全表单 label 一致 100px;右对齐已由 labelPosition:'right' 保证)。
3. `src/styles/element-ui.scss` `.shipping-template-dialog .el-form-item__label { white-space: normal; line-height: 20px }`(长 label 换行,独立表单页容器也是 div.shipping-template-dialogclass 命中)。
- 模式沉淀:contractisContractConfig 首组)/ shipping-template(前两组)都走 buildFormGroupOption 统一处理;普通 el-col 循环页直接改 spanCSS grid 页改 repeat 数 + label 规则。4 列=span 66 汉字=100px。
## transport-plan / waybill-manage 表单页:整表单 4 列 + label 统一(全站统一)
- `business-crud-page.vue` `buildFormGroupOption` 重构:抽取 `setFourColumns(group)` helper(非标题、非 span24 列设 span:6);contract→首组、shipping-template→前两组、transport_plan/waybill_manage→**所有分组**统一 4 列(这两页很多字段无显式 span 会落到 Avue 默认 12=2 列,是之前不 4 列的原因)。
- `waybill-manage.js` option 加 `labelWidth:100`transport-plan.js 上轮已加);element-ui.scss 追加 `.waybill-manage-dialog .el-form-item__label` 换行规则(transport-plan-dialog 上轮已加)。
- 经验:Avue 表单「没 4 列」先查是否有字段漏设 span(默认 12);统一 4 列优先走 buildFormGroupOption 的页面级分支,避免逐个字段改。
## ⚠️ option.labelWidth 必须放在 createCrudOption 展开之后
- 第一次给 transport-plan.js、waybill-manage.js 加 `labelWidth: 100` 时写在 `...createCrudOption(...)` **之前**,被 createCrudOption 自带 `labelWidth: 'auto'` 覆盖,label 仍是 auto(按内容参差,看着像"150 不统一")。shipping-template 当时写在 createCrudOption 之后才生效。
- 教训:业务 option 文件顶层 `export const option = { labelWidth: 100, ...createCrudOption([...]) }` 写法**无效**,必须 `export const option = { ...createCrudOption([...]), labelWidth: 100, dialogWidth, ... }` 后写覆盖前写。
## payable-detail 主表格单据号列固定左侧
- 需求:`/settlement/payable-detail` 主表格除序号外,「单据号」列也固定左侧(横向滚动时始终可见)。
- 改动(2 处):
1. `src/option/settlement/receivable-payable-detail.js``documentNo` 列加 `fixed: 'left'`
2. `src/views/settlement/receivable-payable-detail.vue`:主表格 v-for 动态列加 `:fixed="column.fixed"`(模板需显式透传才生效)。
- 注意:payable-detail 与 receivable-detail 共用同一组件,应收明细页同步生效(两侧一致)。左侧固定区 = 选择框52 + 序号64 + 单据号180。
## project-apply 列表列全部居中
- 需求:`/business/project-apply` avue-crud 列表所有列居中看效果。
- 改动(单文件 `src/views/business/project-apply.vue` `buildTableOption()`):column map 展开时加 `align:'center'` + `headerAlign:'center'`。avue 中 align 只管单元格内容,表头需 headerAlign 单独设。
- 弹窗内子表格(客户/承运商/材料/变更记录/选人)本就 align="center",无需动。Vite 2889 已 HMR。
## project-apply 审批状态列改纯文字
- 需求:审批状态列从 el-tag 改为纯文字。
- 改动(单文件 `src/views/business/project-apply.vue`):`#approvalStatus` 插槽去掉 `<el-tag>` 包裹,直接输出 `displayStatus(row,'approvalStatus')`;删除已无引用的 `statusTagType()` 方法。Vite 2889 编译 200。
## 全站表格列居中(含操作列)
- 需求:project-apply 居中效果被确认后,要求操作列按钮也居中,并应用到全站表格。
- 改动(`src/styles/element-ui.scss`2 处全局规则):
1. `.avue-crud .avue-crud__menu.avue-crud__menu``justify-content: start``center``text-align: left``center`(Avue 操作列按钮居中,含下拉更多按钮)。
2. 表头背景规则后新增全站规则:`.el-table th.el-table__cell .cell, .el-table td.el-table__cell .cell { text-align: center }`(所有 el-table 表头+单元格内容居中,命中固定列;.cell 直接设置覆盖 el-table-column align 的继承值)。
- project-apply.vue 页面级 `align:'center'` 保留(双保险,无害);手写表格操作列 `.cell:has(> .el-link)` 本就 `justify-content:center`
- 注意:全站居中会覆盖个别页面金额列 `align: right` 等显式右对齐,后续若某页要右对齐需页面级更高特异性覆盖。
## ⚠️ Vite optimizeDeps 缓存坑(patch node_modules 后必须重启 dev server
- 修改 `node_modules/@smallwei/avue/lib/...` 这种深度依赖后,HMR **不会重新打包**:Vite 首次启动时已把整个 Avue ES module 预打包到 `node_modules/.vite/deps/@smallwei_avue.js`,缓存的 chunk 持续被 dev server 返回,导致代码修改"看起来没生效"。
- 解法(任选其一):
1. 重启 dev serverVite 会重新 optimizeDeps)。
2. 手动删 `node_modules/.vite/deps/@smallwei_avue.js` + `_metadata.json`,再让 server 收到请求时触发重新打包(最干净,连同 `_metadata.json` 一起删,否则 fileHash 缓存会让它误以为已最新)。
- 经验:以后再 patch 任何被 Vite optimizeDeps 预打包的依赖(如 element-plus / avue / wangeditor 等),都要重启 server 才能生效。
## 全站弹窗/表单 label 去掉冒号(pnpm patch 修复 Avue 默认值)
- 需求:全站所有 el-form / avue-form 的字段 label(车站名称:、所属省份: 等)后冒号要去掉。
- 关键事实(重要,避免重复踩坑):
- Element Plus 的 label-suffix 不是用 `::after` 渲染,而是把后缀直接拼接到 label 文本节点:
```js
// node_modules/element-plus/es/components/form/src/form-item.vue...mjs:115
const currentLabel = computed(() => `${props.label || ""}${formContext?.labelSuffix || ""}`);
```
- EP el-form 的 labelSuffix prop 默认 `''`(空),不产生冒号;冒号来自 Avue。
- Avue 默认 `labelSuffix: ":"`,逻辑 `this.tableOption.labelSuffix || ":"`(空串 falsy 回落 `":"`),透传给 el-form 的 `label-suffix` prop。
- **踩坑**Avue package.json 有 `"module": "es/index.mjs"` 和 `"main": "lib/index.js"`。Vite 用 ES 模块版(`es/`),不是 CommonJS 版(`lib/`)。**pnpm patch 必须同时覆盖 `es/` 和 `lib/` 两个文件**,否则 Vite 加载的 `es/` 版本未被 patch,冒号照旧!
- 另一个坑:Vite 把 Avue 预打包到 `node_modules/.vite/deps/@smallwei_avue.js`。patch 后必须删整个 `.vite` 目录 + 重启 dev server,否则预打包缓存仍是旧代码。
- 实施(pnpm patch + 手动补 es/ 版本):
1. `pnpm patch @smallwei/avue` → 临时目录,编辑 `lib/.../index2.js`。
2. `pnpm patch-commit <临时目录>` → 生成 `patches/@smallwei__avue.patch`(仅含 lib/ 改动)。
3. **手动更新 patch 文件**:追加 `es/packages/element-plus/form/index2.mjs` 的 diff(同样 `|| ":"` → `|| ""`)。
4. **手动编辑 node_modules** 中 `es/.../index2.mjs`pnpm patch 只生成了 lib/ 的改动,es/ 版本需手动改)。
5. 删 `node_modules/.vite` 整个目录 → 重启 `pnpm dev` → Vite 重新预打包(含 es/ patch)→ 浏览器强刷。
- patch 文件:`patches/@smallwei__avue.patch`(含 es/ + lib/ 两处 diff)。
- pnpm 配置:`pnpm-workspace.yaml` 内 `patchedDependencies: { '@smallwei/avue': patches/@smallwei__avue.patch }`。
- ⚠️ 提交时务必带上:`patches/`、`pnpm-workspace.yaml`、`pnpm-lock.yaml`,缺一不可。
- ⚠️ `pnpm install` 会从 patch 文件重新应用补丁——如果 patch 文件没有 es/ 的 diff,es/ 版本会被还原为原始代码(冒号回来)。已手动补上 es/ diff 到 patch 文件。