Merge remote-tracking branch 'websoft/master'

This commit is contained in:
2026-09-10 02:01:42 +08:00
9 changed files with 686 additions and 369 deletions
+158
View File
@@ -55,3 +55,161 @@
- api 新增 `getFundRiskStats` → GET /blade-transport/project-apply/fund-risk-stats。
- 后端契约(未改,待确认数据来源):ProjectApplyVO 补齐 `fundUseRisk`(high/medium/none)、`fundUseRiskName``fundUseRate``usedFundLimit`/`remainingFundLimit`;列表 selectProjectApplyPage 填充每行风险 + buildQuery 支持 `fundUseRisk` 过滤;新增 `/fund-risk-stats` 返回 {high,medium,none}。
- ⚠️ 关键阻塞:系统里项目与任何费用表(运输计划/运单/账单/客商)均无关联字段,`usedFundLimit`(已使用资金) 无真实取数来源;临时额度模块的 project-apply/list 返回里 usedFundLimit 实际为空。需业务方确认「已使用资金」口径后才能算风险等级,否则列表风险列全空、统计为 0。
## 合同文件「批量下载」按钮与标题同排(contract-manage.vue 内联 AttachmentSection
- 需求:编辑页「合同文件」分组里「批量下载」按钮目前独立一行、靠右偏上,高度与标题区错位;要放到「合同文件」标题右侧同排、垂直居中。
- 修复:`const AttachmentSection = defineComponent(...)` 内联组件 render() —— 原结构是 `div.dialog-section-title`(标题) 与 `div.contract-manage-form__attachment-head`(批量下载按钮) 两个并列 div;改为把标题与按钮放进同一个 `contract-manage-form__attachment-head` flex 容器(标题左、按钮右)。
- 样式:`:deep(.contract-manage-form__attachment-head)``justify-content:flex-end` 改为 `space-between` + `align-items:center`,并 `.dialog-section-title { margin-bottom:0 }` 抵消标题自身 20px 下边距(2674 行 `:deep(.dialog-section-title)` 会命中子组件渲染的标题)。
- 注意:AttachmentSection 用 render 函数产出 DOM,不带当前组件 scoped data 属性,因此样式必须走 `:deep()`scoped 的 `&__billing-head, &__attachment-head`(2723) 只作用于当前组件模板内(计费信息),对子组件 DOM 无效。
- 影响:4 处使用(编辑/详情 × 合同文件/其它附件)布局统一变为「标题 + 批量下载」同排。
- 主人二次反馈「下面的其它附件也一样」——确认 L463(编辑/其它附件 description) / L765(详情/其它附件 description+readonly) 与 L313(编辑/合同文件) / L671(详情/合同文件) 都走同一份 render+样式,无需新代码;指挥浏览器 Cmd+Shift+R 硬刷即可看到效果(render 函数组件的 :deep() 样式改 HMR 经常不传播)。
## 合同格式下拉改读业务字典 contractFormatcontract-manage.vue
- 需求:新增/编辑合同管理页「合同格式」下拉由硬编码改为读取业务字典 `contractFormat`
- 原实现:`option/business/contract-manage.js``contractFormat``dicData` 硬编码 `[{label:'电子合同',value:'电子合同'},{label:'纸质合同',value:'纸质合同'}]`view 里 `contractFormatOptions` computed 直接 `columnOptions('contractFormat')` 读 column.dicData。
- 修复(4 处):
1. data 加 `contractFormatDictOptions: []`(与 settlementModeDictOptions 并列)。
2. computed `contractFormatOptions` 改为优先 `contractFormatDictOptions`,为空回退 `columnOptions('contractFormat')`(保留静态兜底)。
3. `loadSettlementDictionaries``Promise.all` 追加 `getBizDictionary({ code: 'contractFormat' })`,用 `records()` 映射 `{label:dictValue,value:dictKey}` 填充 `contractFormatDictOptions`;再**条件覆盖** column.dicData(仅当字典非空时),避免字典未配时清掉静态兜底。
4. `detailValue` 的字典映射分支加入 `prop === 'contractFormat'`,详情页「合同格式」也走 `displayStatus` 显示 label。
- 后端契约:`getBizDictionary``GET /blade-system/dict-biz/dictionary?code=contractFormat`,返回 `List<DictBiz>{code,dictKey,dictValue,remark}`DictBizController#dictionary 按 code 精确匹配)。字典 code 按用户口述用驼峰 `contractFormat`(现有 settle_method 为下划线,若后台实际是 `contract_format` 需调整 code)。
- 参考模式:与 `settlementMode`settle_method)完全一致。
## 合同类别 contractCategory 也读业务字典(contract-manage.vue,续上)
- 需求:模板里 label 实为「合同类型」(prop `contractCategory`),下拉由硬编码 `common.js#contractCategoryOptions`(客户合同/承运商合同/补充协议,value 中文)改为读业务字典 `contractCategory`
- 修复(3 处,与 contractFormat 同模式):
1. data 加 `contractCategoryDictOptions: []`
2. computed `contractCategoryOptions` 改为优先 `contractCategoryDictOptions`,空回退 `columnOptions('contractCategory')`(静态兜底)。
3. `loadSettlementDictionaries``Promise.all` 追加 `getBizDictionary({ code: 'contractCategory' })`,映射填充后**条件覆盖** `contractCategory` 列 dicData(仅非空时)。
- 列表页列显示/搜索下拉、详情页(detailValue 已有 `prop==='contractCategory'` 分支走 displayStatus)均因 column.dicData 覆盖而自动跟随业务字典。
- 注意:`config.defaultForm.contractCategory: '客户合同'`option/contract-manage.js)为硬编码中文默认值,若业务字典 dictKey 非中文需同步调整。
## 车辆新增弹窗:所有组织移位 + 使用部门 + 长期有效清空(vehicle.vue + 后端)
- 需求(transportCapacity/vehicle):①所有组织移到「基础车辆信息」区末尾;②新增「使用部门」字段(后端 transport_vehicle 无该字段,需加);③车牌号三段(省/号码/颜色)包成整体输入框+减宽;④勾选「长期有效」清空对应日期且不可编辑。
- 用户确认:所有组织+使用部门放「基础车辆信息」区末尾;使用部门用部门下拉(复用 getDeptTree→organizationOptions,存部门名,仅部门名非级联);车牌号包成整体输入框+减宽;长期有效 4 组全部统一清空(强制报废/行驶证/道路运输证/年审)。
- 前端(vehicle.vue):
1. emptyForm 加 `useDepartment: ''`
2. 删「基础车辆信息」首行的「所有组织」(原 span24),在区末新增一行:`使用部门`(span8, el-select+organizationOptions, 存 rawLabel) + `所有组织`(span8, el-cascader)。organizationOptions 由 initDeptTree 填充。
3. `.plate-group` 由 grid(112px 1fr 112px) 改为 flex 整体输入框:外层 border+radius+focus-within 变化;内部 el-select/el-input 去 box-shadow、无边框,第一/最后 select flex 0 0 30%、中间 input flex 1,子项间用 `::before` 竖线分隔。
4. 4 组 date-picker 加 `:disabled="xxxLongTerm === 1"`(行驶证/道路运输证原缺,补上),checkbox 加 `@change="handleLongTermChange(日期prop, longterm prop)"`;新增方法 handleLongTermChange(勾选时清空日期并 validateField)。
5. formRulesdrivingLicenseEndDate / roadTransportCertEndDate 由 required 改为 validator(新增 validateDrivingLicenseEndDate / validateRoadTransportCertEndDatelongTerm===1 时跳过必填),annualReview 原 validator 已支持。
- 后端(tms-erp-api-ws,新增 use_department 列):
- `TransportVehicle.java``private String useDepartment`
- `blade_transport_vehicle.sql` 建表加 `use_department varchar(50)`
- `TransportVehicleExcel.java``使用部门` 列。
- `TransportVehicleMapper.xml` resultMap + BaseColumn + QueryCondition(useDepartment LIKE) 各加。
- `TransportVehicleServiceImpl.prepare()` trim useDepartmentvalidate() 加长度≤50 校验。
- 新增增量迁移 `doc/sql/transport/blade_transport_vehicle_use_department_patch_20260909.sql`ALTER ADD use_department AFTER organization_name)。
- 注意:VO 继承 entity 无需改;Wrapper BeanUtil 同名透传。已上线库须执行 patch SQL(建表脚本是 DROP+CREATE,勿直接跑)。
- 后续补充(vehicle.vue「车辆资质图片」区输入框对齐):第一行原 span `6/8/10` 不等宽(行驶证档案编号 6 / 行驶证有效期 8 / 道路运输证号 10)导致输入框宽度不齐,统一改为 `8/8/8`,与下方各行 span=8 一致。仅改 2 处 span,无逻辑改动。
## 车辆车牌号:改为单一 input 输入框(vehicle.vue3 段 → 1 段)
- 需求:基础车辆信息区「车牌号」原本是「省简称 select + 号码 input + 车牌颜色 select」三段拼装(plateProvince/plateNoBody/plateColor),改为**单个 el-input** 直接输入完整车牌号(如 京A12345)。
- 改动(vehicle.vue):
1. 模板:`plate-group` 三段 → `<el-input v-model="vehicleForm.plateNo" maxlength="8" show-word-limit placeholder="如:京A12345" @input="handlePlateNoInput" @blur="validatePlateFormat">`。删除 `.plate-group` 样式(含 focus-within、竖线分隔、child flex 占比等整段)。
2. emptyForm:删 `plateProvince``plateNoBody`,保留 `plateNo``plateColor`(后端字段继续存历史车牌颜色)。
3. data:删 `plateProvinceOptions``plateColorOptions` 暴露;常量 `plateProvinceOptions`31 省简称数组)整段删除;`plateColorOptions` 常量删除。
4. validatePlateNo 重写:直接校验 `value`(完整车牌号),正则 `/^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼港澳]{1}[A-Z][A-Z0-9挂学警港澳]{5,6}$/`,错误提示改为「请输入标准车牌号(如:京A12345)」。
5. syncPlateNo / splitPlateNo 重写:splitPlateNo 改为 noop(保留以兼容 OCR 后回调链路);syncPlateNo 改为 trim+upperCase 单一字段。
6. handlePlateBodyInput 改名为 handlePlateNoInput,新增 validatePlateFormat 失焦校验。
7. 车辆认证审核弹窗只读详情同步删「车牌颜色」展示(与录入表单一致)。
- 后端不动:`plate_no` / `plate_color` 字段保留(数据库兼容历史车牌颜色),Service 无强制校验。
- 受影响:OCR 自动识别(行驶证/机动车销售统一发票)后续调用链路无需改,因 syncPlateNo 仍对 plateNo 做归一化。
## 车辆新增/编辑弹窗:输入框宽度统一(vehicle.vue,前后两区)
- 需求(transportCapacity/vehicle):新增/编辑弹窗「基础车辆信息」+「车辆资质图片」两个 section 各输入框宽度要一致;截图红框重点为车牌号、外廓尺寸、所有组织/使用部门。
- 根因:基础区原本列宽混乱(车牌号 span10 / 车辆类型 span6 / 外廓尺寸 span6,其余 span6),行内 3 列与 2 列混排;图片区文本字段全 span6,与基础区不一致。
- 改动(vehicle.vue 仅模板 el-col span):
1. 「基础车辆信息」区所有字段统一为 **span=83 列布局)**:车牌号、车辆类型、外廓尺寸、核定载质量、准牵引总质量、业务关系、能源类型、强制报废日期、海关备案号、所有组织、使用部门。
2. 「车辆资质图片」区文本/日期字段(行驶证档案编号、行驶证有效期、道路运输证号、道路运输证有效期、道路运输年审有效期、机动车登记编号、机动车登记日期)统一为 **span=8**
- 不改:只读「车辆认证审核」弹窗(L140 起的 span6 只读详情 + large 图片上传)与新增弹窗内「证件图片」上传控件(固定尺寸)保持 span6。外廓尺寸为复合控件(长/宽/高三 input),口径同 span8。
- ⚠️ 注意:操作中曾误删核定载质量 el-col 标签(453-460 行开始时少 el-col、尾部多一个 `</el-col>`),已修复为完整 `<el-col :span="8">...`
- 后续纠偏(2026-09-09 傍晚):主人反馈「为什么不是按4列显示」——上一步把"基础车辆信息/车辆资质图片"两区误改成 3 列(span8)。同项目惯例 driver.vue/customer-archive.vue 全为 **4 列(span6,仅复合长字段 span12**,故统一重排为 4 列:
- 基础车辆信息:`车牌号(6)+车辆类型(6)+外廓尺寸(12)``核定载质量/准牵引总质量/业务关系/能源类型(4×6)``强制报废日期/海关备案号/所有组织/使用部门(4×6)`
- 车辆资质图片:`行驶证有效期/道路运输证号/道路运输证有效期/道路运输年审有效期(4×6)``机动车登记编号/机动车登记日期/行驶证档案编号(3×6)`,图片上传控件仍 span6
- 该区 span8 已全部移除,编辑表单区仅剩外廓尺寸 span12(复合控件长/宽/高)+ 图片上传 span6。
- 宽度微调(2026-09-09 傍晚2):主人圈出「所有组织」字段宽度与同行不一致。根因:`.vehicle-form` 里的 `:deep(...){width:100%}` 选择器只列了 `el-select / el-input / el-input-number / el-date-editor`**漏了 `el-cascader`**(所有组织用的就是级联选择器,默认不撑满整列)。已补 `:deep(.el-cascader)` 进该规则,全表单仅此一处用到 el-cascader,故所有组织与同行等宽。
## 司机弹窗:补 placeholder + 每行统一 3 列(transportCapacity/driver.vue
- 需求:新增/编辑司机弹窗 ①输入框无提示文案;②基础身份信息 3 列,其它 4 列 / 6 列行不统一。
- 改动(仅 1819 行 driver.vue 的 126-540 行 `<driver-dialog>` 模板,script/style 不动):
1. **基础身份信息**:行结构不动,7 个 el-form-item 控件补 placeholder(司机姓名/身份证号/出生年月/性别/民族/学历/详细地址)。住址已有「请选择省市区」、驾驶车辆已有「请输入或选择车辆」,不动。
2. **驾驶证信息**:第一行 4 列(:span=6)拆为两行 — 行 1(准驾车型 / 档案编号 / 有效期 date-range:span=8+ 行 2(长期有效,:span=8)。起/止 placeholder 保留简短「起」「止」,符合主人 1.B 偏好(保留原 date-range 二联结构,不改 daterange)。
3. **从业资格证信息**:第一行 4 列拆为两行 — 行 1(从业资格认证类型 / 资格证号 / 有效期,:span=8+ 行 2(长期有效,:span=8)。资格证号 placeholder 保留「默认身份证号」不动。
4. **司机联系信息**:一行 6 列拆为两行 — 行 1(司机类型 / 手机号 / 紧急联系人姓名,:span=8)+ 行 2(紧急联系人手机号 / 与联系人关系 / 所属组织,:span=8)。所属组织已有 placeholder,不动。
- 决策点(主人确认):1.B(保留原 date-range 二联 + 第三列留空后拆行的方案);2 保留;3.A(驾驶证主页/副页、封面页/内容页两组上传图仍 :span=12,与占整行宽的原貌一致)。
- 校验:node 脚本统计 4 个 section-card / 13 个 el-row / 31 个 el-col / 24 个 el-form-item,全平衡。改动只在 driver.vue 一文件,~25 处 Edit 调用。
## 新增/修改司机弹窗改为 4 列布局(driver.vue16:xx
- 主人要求 /transportCapacity/driver 新增司机弹窗改 4 列显示。原文件各分组已是 span=6(4 列栅格)但每行只放 3 个字段,第 4 列空置。
- 重排结果(仅删行断 + 移动字段顺序,字段/校验/逻辑零改动):
1. 基础身份信息:行1 司机姓名/身份证号/出生年月/性别;行2 民族/学历/住址/详细地址;行3 驾驶车辆/岗位;行4 三张证件照(不变)。
2. 驾驶证信息:行1 准驾车型/档案编号/有效期/长期有效;上传主页/副页 span 12→6。
3. 从业资格证信息:行1 类型/资格证号/有效期/长期有效;上传封面页/内容页 span 12→6。
4. 司机联系信息:行1 司机类型/手机号/紧急联系人姓名/紧急联系人手机号;行2 与联系人关系/所属组织。
- 注意:此前日志记录的「3 列 span=8」方案已被后续改动覆盖为 span=6,本次以当前文件实际状态为准。
## 客商档案:客商类型→客商分类 + 新增客商类型(customer-archive.vue
- ⚠️ 补救(17:34):data 里的 `customerKindOptions`/`customerKindMap` 定义一度丢失(模板 v-for 空 → 下拉显示「无数据」,主人截图反馈),已补回 datacustomerTypeOptions 之后)。其余 8 处改动经 grep 确认完好。
- 需求:原「客商类型」(prop `customerType`,字典 customer_type:客户/承运商,多选)改名「客商分类」;另新增「客商类型」字段,取值为 外部客商 / 内部组织。
- 约定:prop `customerType` 不动(其它页面 loading-manage / dept / business-crud-page 都以 `customerType:'承运商'` 作为后端查询参数),只改中文标签。
- 新增字段 prop = **`customerKind`**(前端自定义命名,后端尚无对应字段,需后端在客商表加列并透传),值 `external`(外部客商) / `internal`(内部组织),单选下拉,默认 `external`
- 改动点(8 处,全在 `src/views/vehicle/customer-archive.vue`):
1. 表单:227 行 label 改「客商分类」;其后新增一个 `el-col :span="6"` 表单项「客商类型」prop `customerKind`el-select + `customerKindOptions`)。
2. data 新增 `customerKindOptions` / `customerKindMap`external/internal ↔ 中文)。
3. formRules`customerType` 提示文案改「请选择客商分类」。
4. 列表列:「客户类型」label 改「客商分类」;新增「客商类型」列(prop customerKindtype selectdicData 内外部,minWidth 120**未加 search**)。
5. `emptyArchive()``customerKind: 'external'`
6. `normalizeDetail()``customerKind: detail.customerKind || 'external'`
7. 变更记录 `getChangeFieldLabel``customerType:'客商分类'`,新增 `customerKind:'客商类型'`
8. `formatChangeFieldValue`:别名映射加 `客商分类/客户类型 → customerType``客商类型 → customerKind`valueMap 加 `customerKind:{external:'外部客商',internal:'内部组织'}`
- 校验:用 @vue/compiler-sfc 编译模板,0 错误。
## 上传证件项 label 贴顶修复(全局 archive-form 垂直居中提升)
- 症状:driver.vue 司机弹窗「驾驶证主页/副页」「封面页/内容页」等证件上传项(el-form-item 内容高 151pxlabel 文字贴顶,普通 32px 控件行看不出来。
- 根因:`archive-form` class 被 driver / vehicle / ship / credit-score-quantification / loading-manage / waybill-import-dialog 等 8+ 页面复用,但「label 与控件垂直居中」规则 `.archive-form :deep(.el-form-item){align-items:center}` 锁在 customer-archive.vue 的 scoped 样式里,其它页面拿不到;flex 容器默认 stretchlabel(32px) 在高控件行内贴顶。
- 修复:规则提升到 `src/styles/element-ui.scss` 全局(紧跟 is-required 星号配套规则之后):`.archive-form .el-form-item { align-items:center }`。全站 archive-form 表单一并生效,label 与控件垂直居中。SCSS 编译通过。
## 司机弹窗「驾驶证信息」:有效期必填、长期有效不必填(transportCapacity/driver.vue
- 需求:驾驶证信息区「有效期」(起/止,prop `drivingLicenseEndDate`)要必填(显示红*),「长期有效」复选框(prop `drivingLicenseLongTerm`)不必填(去掉红*)。
- 改动(formRules 两处):
1. `drivingLicenseEndDate` 规则加 `required: true``{ required: true, validator: this.validateDrivingLicenseEndDate, trigger: 'change' }`)——仅用于渲染红*,实际校验仍走自定义 validator。
2. 删除 `drivingLicenseLongTerm``{ required: true, message: '请选择驾驶证是否长期有效' }` 规则块。
- 关键机制(Element Plus 2.14 + async-validator 4.2.5 已核实):
- 红* 由 `form-item``isRequired = normalizedRules.some(rule => rule.required)` 决定,只要规则对象带 `required` 就显示。
- async-validator 的 `rule.validate` 中,若存在 `rule.validator`,直接走自定义 validator 并 return**不会**再执行 required 空值检查 → 勾「长期有效」时止日被清空/disabled 也不会误报必填。
- 校验逻辑保持原样:起始日期恒必填;`longTerm !== 1` 时止日必填。
- 未同步:「从业资格证信息」区仍是「有效期无红* + 长期有效有红*」的旧形态(待主人确认是否一并统一)。
## 司机弹窗 placeholder 统一为「请输入/请选择」(transportCapacity/driver.vue
- 需求:新增/修改司机弹窗输入框 placeholder 去掉后面的字段名称,只保留「请输入」「请选择」。
- 改动:18 处 placeholder 简化(7 处 → `请输入`10 处 → `请选择`1 处动态 cascader `driverForm.addressRegion || '请选择省市区'``|| '请选择'`)。
- **保留 4 处**(主人明确确认):
1. 驾驶车辆 `请输入或选择车辆` —— 复合操作(既可输入又可点按钮选车),去掉会丢语义。
2. 日期范围 `起` / `止` —— 起止标记,非字段提示。
3. 资格证号 `默认身份证号` —— 默认值说明,非「请输入」格式。
- 未动:表单**校验错误提示 message 仍带字段名**(如「请选择准驾车型」),因报错需指明具体字段,与 placeholder 定位不同;主人未要求,暂保持。
- 做法:Python 脚本按完整 placeholder 字符串精确替换(18 规则全命中,未匹配即 exit 1 中断),比逐个 Edit 高效且不会误伤。
- 校验:`@vue/compiler-sfc` parse + compileTemplate + compileScript 三项全通过。注意脚本必须放在**项目根目录**下运行(放 /tmp 解析不到 node_modules 里的 @vue/compiler-sfc)。
- ⚠️ git diff 显示的 `drivingLicenseLongTerm` 校验规则变动是**本次之前工作区已有的未提交改动**(见上一条日志),非本次引入,勿混淆。
## 「自动生成结算单」开关加 ? 悬浮提示(3 处)
- 需求:开关「开启」后面加问号图标,hover 提示「开启时,系统根据配置规则归集运单,定时生成结算单」。
- 改动文件(同一文案,为保持全站一致):
1. `src/views/business/components/business-crud-page.vue`1884 行附近,`.business-crud-page__settlement-switch`
2. `src/views/business/contract-manage.vue`357 行附近,`.contract-manage-form__settlement-switch`
3. `src/views/business/contract-manage-change.vue`55 行,`.settlement-switch`,原为单行长标签已拆行)
- 写法:`<el-radio :label="1">开启</el-radio>` 后插 `<el-tooltip content="..." placement="top"><el-icon class="settlement-switch-tip"><QuestionFilled /></el-icon></el-tooltip>`,再接「关闭」radio。
- 样式:`.settlement-switch-tip { margin:0 4px; color:#a8abb2; cursor:help; font-size:14px }`,各文件 scoped 样式内一份(contract-manage 写在 `&__settlement-switch` 内)。
- 注意:图标走 main.js 全局注册(@element-plus/icons-vue 全量注册),无需 import;写法与 `project-apply.vue``__label-tip` 一致。
- 这三个大文件本身已存在 prettier 格式问题(改动前 git HEAD 版本同样 fail),非本次引入。
## 车辆认证审核弹窗排版对齐新增弹窗(vehicle.vue
- 诊断 `/project-apply/fund-risk-stats` No endpoint:前端已定义 getFundRiskStatsapi/business/project-apply.js:13),后端未实现;方案已给(后端补 R<{high,medium}> / 前端 catch 降级),待主人选。
- vehicle.vue 认证审核弹窗重排对齐新增弹窗:取消独立「尺寸重量」卡片,外廓尺寸改长/宽/高组合(span12dimension-group);基础信息 3 行×4 列(车牌号/车辆类型/外廓尺寸;载质量/牵引质量/业务关系/能源类型;强制报废日期/海关备案号/所有组织/使用部门);证件信息按新增「车辆资质图片」行分布(止+运输证号+止+年审 / 登记编号+登记日期+档案编号+行驶证起 / 运输证起 / 备注整行);label「所属组织」改「所有组织」对齐新增;dimension-group 样式复制进 .certification-audit 作用域。@vue/compiler-sfc 编译校验通过。
- vehicle.vue 行驶证有效期必填星号:原规则是纯 validatorElement Plus 不渲染星号),改为 data 内 baseRules + computed formRulesdrivingLicenseEndDate 动态返回 required 规则(长期有效=1 时返回 [],星号与校验同步消失);删除 validateDrivingLicenseEndDate。经验:Element Plus 星号只在规则含 required:true 时渲染,且 el-form-item 的 :required 属性会被注入为 {required} 规则并产生英文默认提示,不宜用它控制星号。
- 备注:车辆保存的「车牌颜色不能为空」「行驶证有效期:必填」均为后端校验(前端无对应规则),需后端改。
- driver.vue 从业资格证有效期对齐驾驶证:单日期改 date-range 起/止(列宽 6+6+6+6 → 6+6+8+4),新增 qualificationStartDate 字段(emptyForm + handleSubmit normalize);qualificationEndDate 规则加 required:true(渲染星号,validator 豁免长期);删除 qualificationLongTerm 的 required 规则(长期有效不必填);勾长期后起止均 disabled(比驾驶证更严,驾驶证起仍可编辑)。⚠️ 后端需新增 qualification_start_date 字段否则起日期无法存取。
- driver.vue 补充:驾驶证「有效期起」加 :disabled(长期=1 时禁用);驾驶证与从业资格证两个 validator 统一在「长期有效=1」时直接 callback() 放行(起止都不校验、不提示)。禁用不清空旧值,提交时保留原日期(后端以 longTerm 为准)。
- contract-manage-change.vue「合同文件」批量下载按钮上移:标题与按钮合并进 .section-headflex space-between 同排),与「其它附件/计费信息」分区一致;删除无引用的 .attachment-head 样式。
- contract-manage-change.vue「自动生成结算单」问号提示图标位置:原 icon 夹在开启/关闭 radio 之间,因 .el-radio 默认 margin-right:32px 导致离开启远、贴关闭;修复 = 开启 radio margin-right 清零 + icon margin 改 0 32px 0 8px。
+11
View File
@@ -2,6 +2,17 @@
## 设计约定(已确认)
### 表单 placeholder 统一为「请输入 / 请选择」(不带字段名)
- **规范**:输入框 placeholder 只写动作词 `请输入` / `请选择`,**后面不跟字段名称**(label 已说明是什么字段,重复无意义)。
- 已生效页面:waybill-manage、transport-plan、shipping-template、temporary-credit-limit(搜索栏 + 弹窗表单)、**driver.vue**2026-09-09 补齐 18 处)。
- **3 类例外必须保留,不要误简化**:
1. 复合操作:如 `请输入或选择车辆`(既可键盘输入又可点按钮弹窗选车),丢掉会损失语义。
2. 日期区间的 `起` / `止`:是起止标记,不是字段提示。
3. 默认值说明:如资格证号 `默认身份证号`、车牌号 `如:京A12345`,属于示例/默认值提示,非「请输入」格式。
- **校验错误提示 message 保持带字段名**(如「请选择准驾车型」「请输入档案编号」),与 placeholder 定位不同——报错必须指明具体哪个字段,不能只说「请选择」。
- Avue 配置化表单:在 option 各列加 `placeholder`;搜索栏可用通用工具 `withSearchPlaceholders([...])`(见 temporary-credit-limit.js)。
- 批量改法:Python 脚本按**完整 placeholder 字符串**做精确替换,每规则校验 `count > 0` 否则中断,避免误伤;改完用 `@vue/compiler-sfc` 的 parse + compileTemplate + compileScript 验证(脚本须放项目根目录下跑,放 /tmp 解析不到 node_modules)。
### 弹窗分组:统一用全局 `<section-card>`
- 弹窗 body 背景由 `src/styles/element-ui.scss` 全局 `.el-dialog__body { background:#f5f6fa; padding:16px }` 统一灰底。
- 每组 = 白底卡片:4px 主色竖条 + 8px 间距 + 圆角 6px + 淡阴影。组件全局注册 `<section-card title="...">`,支持 `#title` / `#extra` slot。
+8
View File
@@ -1085,6 +1085,14 @@ td.el-table-fixed-column--right {
line-height: inherit;
margin-right: 4px;
}
// ============ archive-formlabel 与控件垂直居中(全站复用) ============
// archive-form class 被 driver / vehicle / ship / credit-score-quantification / loading-manage 等
// 多个页面复用,但「垂直居中」规则原本锁在 customer-archive.vue 的 scoped 样式里,
// 其它页面拿不到 → 高控件行(证件上传 151px)label 贴顶。提升为全局统一生效。
.archive-form .el-form-item {
align-items: center;
}
//.el-form-item {
// margin-bottom: 15px !important;
//}
@@ -1883,6 +1883,12 @@
<span>自动生成结算单</span>
<el-radio-group v-model="settlementRuleForm.autoGenerate" :disabled="dialogReadonly">
<el-radio :label="1">开启</el-radio>
<el-tooltip
content="开启时,系统根据配置规则归集运单,定时生成结算单"
placement="top"
>
<el-icon class="settlement-switch-tip"><QuestionFilled /></el-icon>
</el-tooltip>
<el-radio :label="0">关闭</el-radio>
</el-radio-group>
</div>
@@ -14453,6 +14459,13 @@ export default {
color: #303133;
}
.settlement-switch-tip {
margin: 0 4px;
color: #a8abb2;
cursor: help;
font-size: 14px;
}
&__module-form {
width: 100%;
+14 -3
View File
@@ -30,8 +30,8 @@
</section>
<section class="change-section">
<div class="section-head">
<div class="dialog-section-title">合同文件</div>
<div class="attachment-head">
<el-button type="primary" :disabled="!contractFileRows.length" @click="handleContractFileBatchDownload">批量下载</el-button>
</div>
<el-table :data="contractFileRows" border class="change-table" @selection-change="selectedContractFiles = $event"><el-table-column type="selection" width="55" /><el-table-column type="index" label="序号" width="70" /><el-table-column label="文件名" min-width="240"><template #default="{ row }"><el-link type="primary" @click="previewAttachment(row, contractFileRows)">{{ row.originalName || row.name }}</el-link></template></el-table-column><el-table-column label="文件大小" width="120"><template #default="{ row }">{{ formatFileSize(row.size) }}</template></el-table-column><el-table-column prop="uploadUserName" label="上传人" width="140" /><el-table-column prop="uploadTime" label="上传时间" width="170" /><el-table-column label="操作" width="100"><template #default="{ $index }"><el-link type="danger" @click="removeContractFile($index)">删除</el-link></template></el-table-column></el-table>
@@ -52,7 +52,16 @@
<el-tab-pane label="预结算配置" name="pre" />
<el-tab-pane label="正式结算配置" name="formal" />
</el-tabs>
<div class="settlement-switch"><span>自动生成结算单</span><el-radio-group v-model="settlementRule.autoGenerate"><el-radio :label="1">开启</el-radio><el-radio :label="0">关闭</el-radio></el-radio-group></div>
<div class="settlement-switch">
<span>自动生成结算单</span>
<el-radio-group v-model="settlementRule.autoGenerate">
<el-radio :label="1">开启</el-radio>
<el-tooltip content="开启时,系统根据配置规则归集运单,定时生成结算单" placement="top">
<el-icon class="settlement-switch-tip"><QuestionFilled /></el-icon>
</el-tooltip>
<el-radio :label="0">关闭</el-radio>
</el-radio-group>
</div>
<el-form v-if="settlementRule.autoGenerate === 1" :model="settlementRule" label-position="right" label-width="auto" class="settlement-form">
<el-form-item label="账单起始日期" required><el-date-picker v-model="settlementRule.billStartDate" type="date" placeholder="请选择账单起始日期" format="YYYY-MM-DD" value-format="YYYY-MM-DD" /></el-form-item>
<el-form-item label="结算类型" required><el-select v-model="settlementRule.settlementType" placeholder="请选择结算类型" @change="handleSettlementTypeChange"><el-option v-for="item in settlementTypeOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item>
@@ -206,11 +215,13 @@ export default {
.change-table { margin: 16px 0; }
.ratio-tip { margin-bottom: 8px; color: #f56c6c; }
.unit { margin-left: 8px; }
.attachment-head { display: flex; align-items: center; justify-content: flex-end; margin-bottom: 12px; }
.attachment-upload { display: flex; justify-content: flex-start; margin-top: 12px; }
.attachment-upload .vehicle-attachment-upload { width: auto; }
.attachment-upload .el-upload { display: inline-flex; }
.settlement-switch { display: flex; align-items: center; gap: 12px; margin-bottom: 16px; }
// 问号提示图标紧靠「开启」一侧:清掉开启 radio 自带的右间距,图标与「关闭」之间留大间距
.settlement-switch :deep(.el-radio:first-child) { margin-right: 0; }
.settlement-switch-tip { margin: 0 32px 0 8px; color: #a8abb2; cursor: help; font-size: 14px; }
.settlement-form { display: grid; grid-template-columns: repeat(3, minmax(240px, 1fr)); gap: 8px 28px; }
.change-reason-section { margin: 20px 0; }
.page-footer {
+96 -53
View File
@@ -163,6 +163,17 @@
/>
</el-select>
</el-form-item>
<el-form-item label="所属组织" prop="organizationName">
<el-cascader
v-model="organizationCascaderValue"
:options="organizationOptions"
:props="organizationCascaderProps"
placeholder="请选择"
clearable
filterable
@visible-change="visible => visible && loadOrganizationOptions()"
/>
</el-form-item>
<el-form-item label="甲方" prop="partyA">
<el-select
v-model="form.partyA"
@@ -197,6 +208,15 @@
/>
</el-select>
</el-form-item>
<el-form-item label="签订日期" prop="signDate">
<el-date-picker
v-model="form.signDate"
type="date"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="请选择签订日期"
/>
</el-form-item>
<el-form-item label="合同期限" prop="startDate">
<div class="contract-manage-form__date-range">
<el-date-picker
@@ -216,28 +236,29 @@
/>
</div>
</el-form-item>
<el-form-item label="所属组织" prop="organizationName">
<el-cascader
v-model="organizationCascaderValue"
:options="organizationOptions"
:props="organizationCascaderProps"
placeholder="请选择"
clearable
filterable
@visible-change="visible => visible && loadOrganizationOptions()"
/>
<el-form-item label="回款账期">
<el-input
v-model="form.paymentDays"
placeholder="请输入"
@input="value => integerInput('paymentDays', value)"
><template #suffix>天</template></el-input>
</el-form-item>
<el-form-item label="经办人">
<el-input v-model="form.handlerUserName" disabled />
<el-form-item label="开票周期">
<el-input
v-model="form.invoiceCycle"
placeholder="请输入"
@input="value => positiveIntegerInput('invoiceCycle', value)"
><template #suffix>天</template></el-input>
</el-form-item>
<el-form-item label="签订日期" prop="signDate">
<el-date-picker
v-model="form.signDate"
type="date"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="请选择签订日期"
<el-form-item label="结算币种" prop="settlementCurrency">
<el-select v-model="form.settlementCurrency" placeholder="请选择结算币种" clearable>
<el-option
v-for="item in settlementCurrencyOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="合同格式" prop="contractFormat">
<el-select v-model="form.contractFormat" placeholder="请选择合同格式">
@@ -249,12 +270,6 @@
/>
</el-select>
</el-form-item>
<el-form-item label="是否需要加盖法人章" prop="legalSealFlag">
<el-select v-model="form.legalSealFlag" placeholder="请选择">
<el-option label="" :value="0" />
<el-option label="" :value="1" />
</el-select>
</el-form-item>
<el-form-item label="一式">
<el-input
v-model="form.copyCount"
@@ -263,14 +278,10 @@
><template #suffix>份</template></el-input
>
</el-form-item>
<el-form-item label="结算币种" prop="settlementCurrency">
<el-select v-model="form.settlementCurrency" placeholder="请选择结算币种" clearable>
<el-option
v-for="item in settlementCurrencyOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
<el-form-item label="是否需要加盖法人章" prop="legalSealFlag">
<el-select v-model="form.legalSealFlag" placeholder="请选择">
<el-option label="" :value="0" />
<el-option label="" :value="1" />
</el-select>
</el-form-item>
<el-form-item label="结算方式" prop="settlementMode">
@@ -283,19 +294,8 @@
/>
</el-select>
</el-form-item>
<el-form-item label="开票周期">
<el-input
v-model="form.invoiceCycle"
placeholder="请输入"
@input="value => positiveIntegerInput('invoiceCycle', value)"
><template #suffix>天</template></el-input>
</el-form-item>
<el-form-item label="回款账期">
<el-input
v-model="form.paymentDays"
placeholder="请输入"
@input="value => integerInput('paymentDays', value)"
><template #suffix>天</template></el-input>
<el-form-item label="经办人">
<el-input v-model="form.handlerUserName" disabled />
</el-form-item>
</div>
<el-form-item label="备注" prop="remark" class="contract-manage-form__remark">
@@ -356,6 +356,12 @@
<span>自动生成结算单</span>
<el-radio-group v-model="settlementRuleForm.autoGenerate">
<el-radio :label="1">开启</el-radio>
<el-tooltip
content="开启时系统根据配置规则归集运单定时生成结算单"
placement="top"
>
<el-icon class="settlement-switch-tip"><QuestionFilled /></el-icon>
</el-tooltip>
<el-radio :label="0">关闭</el-radio>
</el-radio-group>
</div>
@@ -431,7 +437,7 @@
<el-icon style="margin-left: 4px; color: #909399; cursor: pointer"><el-icon-question-filled /></el-icon>
</el-tooltip>
</div>
<el-button type="primary" plain @click="addPaymentRatio">添加</el-button>
<el-button type="primary" @click="addPaymentRatio">添加</el-button>
</div>
<el-table :data="paymentRatioRows" border>
<el-table-column type="index" label="序号" width="80" align="center" />
@@ -1170,8 +1176,8 @@ const AttachmentSection = defineComponent({
class: 'contract-manage-form__section contract-manage-form__section--panel',
},
[
h('div', { class: 'dialog-section-title' }, this.title),
h('div', { class: 'contract-manage-form__attachment-head' }, [
h('div', { class: 'dialog-section-title' }, this.title),
h(
ElButton,
{ type: 'primary', disabled: !this.rows.length, onClick: this.batchDownload },
@@ -1270,6 +1276,8 @@ export default {
selectedProjectId: '',
settlementCurrencyOptions: [],
settlementModeDictOptions: [],
contractFormatDictOptions: [],
contractCategoryDictOptions: [],
contractPartyAOptions: [],
contractPartyBOptions: [],
contractPartyLoading: false,
@@ -1358,7 +1366,9 @@ export default {
return this.selectionList.map(item => item.id).join(',');
},
contractCategoryOptions() {
return this.columnOptions('contractCategory');
return this.contractCategoryDictOptions.length
? this.contractCategoryDictOptions
: this.columnOptions('contractCategory');
},
signTypeOptions() {
return this.columnOptions('signType');
@@ -1372,7 +1382,9 @@ export default {
: this.columnOptions('settlementMode');
},
contractFormatOptions() {
return this.columnOptions('contractFormat');
return this.contractFormatDictOptions.length
? this.contractFormatDictOptions
: this.columnOptions('contractFormat');
},
organizationCascaderProps() {
return {
@@ -1822,7 +1834,9 @@ export default {
Promise.all([
getSystemDictionary({ code: 'currency_type' }),
getBizDictionary({ code: 'settle_method' }),
]).then(([currencyRes, methodRes]) => {
getBizDictionary({ code: 'contractFormat' }),
getBizDictionary({ code: 'contractCategory' }),
]).then(([currencyRes, methodRes, formatRes, categoryRes]) => {
const records = response => {
const data = response?.data?.data || response?.data || [];
return Array.isArray(data) ? data : data.records || [];
@@ -1835,10 +1849,26 @@ export default {
label: item.dictValue,
value: item.dictKey,
}));
this.contractFormatDictOptions = records(formatRes).map(item => ({
label: item.dictValue,
value: item.dictKey,
}));
this.contractCategoryDictOptions = records(categoryRes).map(item => ({
label: item.dictValue,
value: item.dictKey,
}));
['settlementMode'].forEach(prop => {
const column = this.findColumn(this.tableOption.column, prop);
if (column) column.dicData = this.settlementModeDictOptions;
});
const formatColumn = this.findColumn(this.tableOption.column, 'contractFormat');
if (formatColumn && this.contractFormatDictOptions.length) {
formatColumn.dicData = this.contractFormatDictOptions;
}
const categoryColumn = this.findColumn(this.tableOption.column, 'contractCategory');
if (categoryColumn && this.contractCategoryDictOptions.length) {
categoryColumn.dicData = this.contractCategoryDictOptions;
}
});
},
syncCurrentProjectOption() {
@@ -2428,7 +2458,8 @@ export default {
prop === 'approvalStatus' ||
prop === 'contractCategory' ||
prop === 'signType' ||
prop === 'effectiveType'
prop === 'effectiveType' ||
prop === 'contractFormat'
) {
return this.displayStatus(this.detailRow, prop);
}
@@ -2733,6 +2764,13 @@ export default {
align-items: center;
gap: 20px;
margin-bottom: 16px;
.settlement-switch-tip {
margin: 0 4px;
color: #a8abb2;
cursor: help;
font-size: 14px;
}
}
&__settlement-grid {
@@ -2819,8 +2857,13 @@ export default {
:deep(.contract-manage-form__attachment-head) {
display: flex;
align-items: center;
justify-content: flex-end;
justify-content: space-between;
margin-bottom: 16px;
// 标题与「批量下载」按钮同排、垂直居中,消除标题自身下边距
.dialog-section-title {
margin-bottom: 0;
}
}
:deep(.contract-manage-form__attachment-upload) {
+98 -45
View File
@@ -125,43 +125,48 @@
>
<section-card title="基础身份信息">
<el-row :gutter="18">
<el-col :span="8">
<el-col :span="6">
<el-form-item label="司机姓名" prop="driverName">
<el-input v-model="driverForm.driverName" maxlength="10" show-word-limit />
<el-input
v-model="driverForm.driverName"
maxlength="10"
show-word-limit
placeholder="请输入"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="身份证号" prop="idCardNo">
<el-input
v-model="driverForm.idCardNo"
maxlength="18"
show-word-limit
placeholder="请输入"
@input="driverForm.idCardNo = String(driverForm.idCardNo || '').toUpperCase()"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="出生年月" prop="birthday">
<el-date-picker
v-model="driverForm.birthday"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-col :span="6">
<el-form-item label="性别" prop="gender">
<el-select v-model="driverForm.gender" clearable>
<el-select v-model="driverForm.gender" clearable placeholder="请选择">
<el-option label="男" value="男" />
<el-option label="女" value="女" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="民族" prop="nation">
<el-select v-model="driverForm.nation" clearable filterable>
<el-select v-model="driverForm.nation" clearable filterable placeholder="请选择">
<el-option
v-for="item in nationOptions"
:key="item"
@@ -171,9 +176,14 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="学历" prop="education">
<el-select v-model="driverForm.education" clearable filterable>
<el-select
v-model="driverForm.education"
clearable
filterable
placeholder="请选择"
>
<el-option
v-for="item in educationOptions"
:key="item"
@@ -183,30 +193,33 @@
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-col :span="6">
<el-form-item label="住址" prop="addressRegion">
<el-cascader
ref="addressRegionCascader"
v-model="driverForm.addressRegionPath"
:options="regionOptions"
:props="regionCascaderProps"
:placeholder="driverForm.addressRegion || '请选择省市区'"
:placeholder="driverForm.addressRegion || '请选择'"
clearable
filterable
@change="handleAddressRegionChange"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="详细地址" prop="address">
<el-input v-model="driverForm.address" maxlength="200" show-word-limit />
<el-input
v-model="driverForm.address"
maxlength="200"
show-word-limit
placeholder="请输入"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-col :span="6">
<el-form-item label="驾驶车辆" prop="drivingVehicle">
<el-input
v-model="driverForm.drivingVehicle"
@@ -238,7 +251,7 @@
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-col :span="6">
<image-upload-field
label="身份证正面照"
prop="idCardFront"
@@ -249,7 +262,7 @@
@success="url => handleIdCardUploadSuccess('idCardFront', url)"
/>
</el-col>
<el-col :span="8">
<el-col :span="6">
<image-upload-field
label="身份证反面照"
prop="idCardBack"
@@ -260,7 +273,7 @@
@success="url => handleIdCardUploadSuccess('idCardBack', url)"
/>
</el-col>
<el-col :span="8">
<el-col :span="6">
<image-upload-field
label="大头照"
prop="headPhoto"
@@ -284,6 +297,7 @@
filterable
allow-create
default-first-option
placeholder="请选择"
>
<el-option
v-for="item in drivingTypeOptions"
@@ -296,10 +310,14 @@
</el-col>
<el-col :span="6">
<el-form-item label="档案编号" prop="drivingLicenseNo">
<el-input v-model="driverForm.drivingLicenseNo" maxlength="50" />
<el-input
v-model="driverForm.drivingLicenseNo"
maxlength="50"
placeholder="请输入"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-col :span="8">
<el-form-item label="有效期" prop="drivingLicenseEndDate">
<div class="date-range">
<el-date-picker
@@ -307,6 +325,7 @@
type="date"
value-format="YYYY-MM-DD"
placeholder="起"
:disabled="driverForm.drivingLicenseLongTerm === 1"
@change="validateFormField('drivingLicenseEndDate')"
/>
<el-date-picker
@@ -320,7 +339,7 @@
</div>
</el-form-item>
</el-col>
<el-col :span="6">
<el-col :span="4">
<el-form-item label="长期有效" prop="drivingLicenseLongTerm">
<el-checkbox
v-model="driverForm.drivingLicenseLongTerm"
@@ -334,7 +353,7 @@
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="12">
<el-col :span="6">
<image-upload-field
label="驾驶证主页"
prop="drivingLicenseFront"
@@ -347,7 +366,7 @@
"
/>
</el-col>
<el-col :span="12">
<el-col :span="6">
<image-upload-field
label="驾驶证副页"
prop="drivingLicenseBack"
@@ -371,6 +390,7 @@
v-model="driverForm.qualificationType"
clearable
filterable
placeholder="请选择"
>
<el-option
v-for="item in qualificationTypeOptions"
@@ -390,18 +410,29 @@
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-col :span="8">
<el-form-item label="有效期" prop="qualificationEndDate">
<div class="date-range">
<el-date-picker
v-model="driverForm.qualificationStartDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="起"
:disabled="driverForm.qualificationLongTerm === 1"
@change="validateFormField('qualificationEndDate')"
/>
<el-date-picker
v-model="driverForm.qualificationEndDate"
type="date"
value-format="YYYY-MM-DD"
placeholder="止"
:disabled="driverForm.qualificationLongTerm === 1"
@change="validateFormField('qualificationEndDate')"
/>
</div>
</el-form-item>
</el-col>
<el-col :span="6">
<el-col :span="4">
<el-form-item label="长期有效" prop="qualificationLongTerm">
<el-checkbox
v-model="driverForm.qualificationLongTerm"
@@ -415,7 +446,7 @@
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="12">
<el-col :span="6">
<image-upload-field
label="封面页"
prop="qualificationFront"
@@ -426,7 +457,7 @@
@success="url => handleQualificationUploadSuccess('qualificationFront', url)"
/>
</el-col>
<el-col :span="12">
<el-col :span="6">
<image-upload-field
label="内容页"
prop="qualificationBack"
@@ -444,7 +475,7 @@
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="司机类型" prop="driverType">
<el-select v-model="driverForm.driverType" filterable>
<el-select v-model="driverForm.driverType" filterable placeholder="请选择">
<el-option label="自有" value="自有" />
<el-option label="外协" value="外协" />
<el-option label="承运管理员" value="承运管理员" />
@@ -453,7 +484,11 @@
</el-col>
<el-col :span="6">
<el-form-item label="手机号" prop="mobile">
<el-input v-model="driverForm.mobile" maxlength="20" />
<el-input
v-model="driverForm.mobile"
maxlength="20"
placeholder="请输入"
/>
</el-form-item>
</el-col>
<el-col :span="6">
@@ -461,13 +496,18 @@
<el-input
v-model="driverForm.emergencyContactName"
maxlength="20"
placeholder="请输入"
@input="driverForm.emergencyContactName = removeDigits($event)"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="紧急联系人手机号" prop="emergencyContactMobile">
<el-input v-model="driverForm.emergencyContactMobile" maxlength="20" />
<el-input
v-model="driverForm.emergencyContactMobile"
maxlength="20"
placeholder="请输入"
/>
</el-form-item>
</el-col>
<el-col :span="6">
@@ -478,6 +518,7 @@
filterable
allow-create
default-first-option
placeholder="请选择"
>
<el-option
v-for="item in relationOptions"
@@ -496,7 +537,7 @@
:props="organizationCascaderProps"
filterable
clearable
placeholder="请选择 所属组织"
placeholder="请选择"
/>
</el-form-item>
</el-col>
@@ -645,6 +686,7 @@ const emptyForm = () => ({
drivingLicenseBack: '',
qualificationType: '',
qualificationNo: '',
qualificationStartDate: '',
qualificationEndDate: '',
qualificationLongTerm: 0,
qualificationFront: '',
@@ -783,18 +825,14 @@ export default {
drivingType: [{ required: true, message: '请选择准驾车型', trigger: 'change' }],
drivingLicenseNo: [{ required: true, message: '请输入档案编号', trigger: 'blur' }],
drivingLicenseEndDate: [
{ validator: this.validateDrivingLicenseEndDate, trigger: 'change' },
],
drivingLicenseLongTerm: [
{ required: true, message: '请选择驾驶证是否长期有效', trigger: 'change' },
{ required: true, validator: this.validateDrivingLicenseEndDate, trigger: 'change' },
],
qualificationType: [
{ required: true, message: '请选择从业资格认证类型', trigger: 'change' },
],
qualificationNo: [{ required: true, message: '请输入资格证号', trigger: 'blur' }],
qualificationEndDate: [{ validator: this.validateQualificationEndDate, trigger: 'change' }],
qualificationLongTerm: [
{ required: true, message: '请选择从业资格证是否长期有效', trigger: 'change' },
qualificationEndDate: [
{ required: true, validator: this.validateQualificationEndDate, trigger: 'change' },
],
driverType: [{ required: true, message: '请选择司机类型', trigger: 'change' }],
mobile: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
@@ -1029,19 +1067,33 @@ export default {
}
},
validateDrivingLicenseEndDate(rule, value, callback) {
// 勾选长期有效:起止均已禁用,不再校验
if (this.driverForm.drivingLicenseLongTerm === 1) {
callback();
return;
}
if (!this.driverForm.drivingLicenseStartDate) {
callback(new Error('请选择驾驶证有效期起'));
return;
}
if (this.driverForm.drivingLicenseLongTerm !== 1 && !value) {
if (!value) {
callback(new Error('请选择驾驶证有效期止'));
return;
}
callback();
},
validateQualificationEndDate(rule, value, callback) {
if (this.driverForm.qualificationLongTerm !== 1 && !value) {
callback(new Error('请选择从业资格证有效期'));
// 勾选长期有效:起止均已禁用,不再校验
if (this.driverForm.qualificationLongTerm === 1) {
callback();
return;
}
if (!this.driverForm.qualificationStartDate) {
callback(new Error('请选择从业资格证有效期起'));
return;
}
if (!value) {
callback(new Error('请选择从业资格证有效期止'));
return;
}
callback();
@@ -1553,6 +1605,7 @@ export default {
birthday: this.normalizeBirthday(this.driverForm.birthday),
drivingLicenseStartDate: this.normalizeBirthday(this.driverForm.drivingLicenseStartDate),
drivingLicenseEndDate: this.normalizeBirthday(this.driverForm.drivingLicenseEndDate),
qualificationStartDate: this.normalizeBirthday(this.driverForm.qualificationStartDate),
qualificationEndDate: this.normalizeBirthday(this.driverForm.qualificationEndDate),
qualificationNo: this.driverForm.qualificationNo || this.driverForm.idCardNo,
posts: this.driverForm.postList.join(','),
+233 -251
View File
@@ -130,77 +130,80 @@
<el-form label-position="right" label-width="auto" class="certification-audit archive-form">
<section-card title="基础信息">
<el-row :gutter="18">
<el-col :span="24">
<el-form-item label="所属组织">
<span class="certification-audit__value">{{ auditValue('organizationName') }}</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-col :span="6">
<el-form-item label="车牌号">
<span class="certification-audit__value">{{ auditValue('plateNo') }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="车牌颜色">
<span class="certification-audit__value">{{ auditValue('plateColor') }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="车辆类型">
<span class="certification-audit__value">{{ auditValue('vehicleType') }}</span>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="外廓尺寸(毫米)">
<div class="dimension-group">
<div class="dimension-group__item">
<span class="dimension-group__label"></span>
<span class="certification-audit__value">{{ auditValue('outerLength') }}</span>
</div>
<div class="dimension-group__item">
<span class="dimension-group__label"></span>
<span class="certification-audit__value">{{ auditValue('outerWidth') }}</span>
</div>
<div class="dimension-group__item">
<span class="dimension-group__label"></span>
<span class="certification-audit__value">{{ auditValue('outerHeight') }}</span>
</div>
</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-col :span="6">
<el-form-item label="核定载质量(KG">
<span class="certification-audit__value">{{ auditValue('approvedLoadKg') }}</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="准牵引总质量(KG">
<span class="certification-audit__value">{{ auditValue('tractionMassKg') }}</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="业务关系">
<span class="certification-audit__value">{{ auditValue('businessRelation') }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="能源类型">
<span class="certification-audit__value">{{ auditValue('energyType') }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
</el-row>
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="强制报废日期">
<span class="certification-audit__value">
{{ auditValue('compulsoryScrapDate') }}
</span>
</el-form-item>
</el-col>
</el-row>
</section-card>
<section-card title="尺寸重量">
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="外廓长度(毫米)">
<span class="certification-audit__value">{{ auditValue('outerLength') }}</span>
<el-col :span="6">
<el-form-item label="海关备案号">
<span class="certification-audit__value">{{ auditValue('customsRecordNo') }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="外廓宽度(毫米)">
<span class="certification-audit__value">{{ auditValue('outerWidth') }}</span>
<el-col :span="6">
<el-form-item label="所有组织">
<span class="certification-audit__value">
{{ auditValue('organizationName') }}
</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="外廓高度(毫米)">
<span class="certification-audit__value">{{ auditValue('outerHeight') }}</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="核定载质量(KG">
<span class="certification-audit__value">{{ auditValue('approvedLoadKg') }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="准牵引总质量(KG">
<span class="certification-audit__value">{{ auditValue('tractionMassKg') }}</span>
<el-col :span="6">
<el-form-item label="使用部门">
<span class="certification-audit__value">{{ auditValue('useDepartment') }}</span>
</el-form-item>
</el-col>
</el-row>
@@ -208,17 +211,52 @@
<section-card title="证件信息">
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="海关备案号">
<span class="certification-audit__value">{{ auditValue('customsRecordNo') }}</span>
<el-col :span="6">
<el-form-item label="行驶证有效期止">
<span class="certification-audit__value">
{{ auditValue('drivingLicenseEndDate') }}
</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="道路运输证号">
<span class="certification-audit__value">
{{ auditValue('roadTransportCertNo') }}
</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="道路运输证有效期止">
<span class="certification-audit__value">
{{ auditValue('roadTransportCertEndDate') }}
</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="道路运输年审有效期">
<span class="certification-audit__value">
{{ auditValue('annualReviewEndDate') }}
</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="机动车登记编号">
<span class="certification-audit__value">{{ auditValue('registrationNo') }}</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="机动车登记日期">
<span class="certification-audit__value">{{ auditValue('registrationDate') }}</span>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="行驶证档案编号">
<span class="certification-audit__value">{{ auditValue('drivingLicenseNo') }}</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="行驶证有效期起">
<span class="certification-audit__value">
{{ auditValue('drivingLicenseStartDate') }}
@@ -227,21 +265,7 @@
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="行驶证有效期止">
<span class="certification-audit__value">
{{ auditValue('drivingLicenseEndDate') }}
</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="道路运输证号">
<span class="certification-audit__value">
{{ auditValue('roadTransportCertNo') }}
</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="道路运输证有效期起">
<span class="certification-audit__value">
{{ auditValue('roadTransportCertStartDate') }}
@@ -249,34 +273,6 @@
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="道路运输证有效期止">
<span class="certification-audit__value">
{{ auditValue('roadTransportCertEndDate') }}
</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="道路运输年审有效期">
<span class="certification-audit__value">
{{ auditValue('annualReviewEndDate') }}
</span>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="机动车登记编号">
<span class="certification-audit__value">{{ auditValue('registrationNo') }}</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-form-item label="机动车登记日期">
<span class="certification-audit__value">{{ auditValue('registrationDate') }}</span>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="24">
<el-form-item label="备注">
@@ -288,7 +284,7 @@
<section-card title="证件图片">
<el-row :gutter="18">
<el-col :span="8">
<el-col :span="6">
<image-upload-field
label="行驶证主页正面"
prop="drivingLicenseImage"
@@ -298,7 +294,7 @@
class-prefix="vehicle"
/>
</el-col>
<el-col :span="8">
<el-col :span="6">
<image-upload-field
label="行驶证主页反面"
prop="drivingLicenseMainBack"
@@ -308,7 +304,7 @@
class-prefix="vehicle"
/>
</el-col>
<el-col :span="8">
<el-col :span="6">
<image-upload-field
label="行驶证副页正面"
prop="drivingLicenseViceFront"
@@ -320,7 +316,7 @@
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-col :span="6">
<image-upload-field
label="行驶证副页反面"
prop="drivingLicenseViceBack"
@@ -330,7 +326,7 @@
class-prefix="vehicle"
/>
</el-col>
<el-col :span="8">
<el-col :span="6">
<image-upload-field
label="道路运输证图片"
prop="roadTransportCertImage"
@@ -340,7 +336,7 @@
class-prefix="vehicle"
/>
</el-col>
<el-col :span="8">
<el-col :span="6">
<image-upload-field
label="机动车登记本"
prop="registrationImage"
@@ -394,55 +390,16 @@
>
<section-card title="基础车辆信息">
<el-row :gutter="18">
<el-col :span="24">
<el-form-item label="所有组织" prop="organizationName">
<el-cascader
v-model="organizationCascaderValue"
:options="organizationTreeOptions"
:props="organizationCascaderProps"
filterable
clearable
placeholder="请选择 所属组织"
/>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="10">
<el-col :span="6">
<el-form-item label="车牌号" prop="plateNo">
<div class="plate-group">
<el-select
v-model="vehicleForm.plateProvince"
placeholder="请选择"
filterable
@change="syncPlateNo(false)"
>
<el-option
v-for="item in plateProvinceOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-input
v-model="vehicleForm.plateNoBody"
maxlength="7"
v-model="vehicleForm.plateNo"
maxlength="8"
show-word-limit
@input="handlePlateBodyInput"
placeholder="如:京A12345"
@input="handlePlateNoInput"
@blur="validatePlateFormat"
/>
<el-select
v-model="vehicleForm.plateColor"
placeholder="请选择"
@change="syncPlateNo(false)"
>
<el-option
v-for="item in plateColorOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</div>
</el-form-item>
</el-col>
<el-col :span="6">
@@ -462,7 +419,7 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="12">
<el-form-item label="外廓尺寸(毫米)">
<div class="dimension-group">
<div class="dimension-group__item">
@@ -494,25 +451,27 @@
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-col :span="6">
<el-form-item label="核定载质量(KG" prop="approvedLoadKg">
<el-input
v-model="vehicleForm.approvedLoadKg"
inputmode="numeric"
placeholder="请输入"
@input="vehicleForm.approvedLoadKg = digitsOnly($event)"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="准牵引总质量(KG" prop="tractionMassKg">
<el-input
v-model="vehicleForm.tractionMassKg"
inputmode="numeric"
placeholder="请输入"
@input="vehicleForm.tractionMassKg = digitsOnly($event)"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="业务关系" prop="businessRelation">
<el-select v-model="vehicleForm.businessRelation">
<el-option
@@ -524,9 +483,7 @@
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-col :span="6">
<el-form-item label="能源类型" prop="energyType">
<el-select
v-model="vehicleForm.energyType"
@@ -543,7 +500,9 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
</el-row>
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="强制报废日期" prop="compulsoryScrapDate">
<div class="date-with-flag">
<el-date-picker
@@ -556,71 +515,98 @@
v-model="vehicleForm.compulsoryScrapLongTerm"
:true-label="1"
:false-label="0"
@change="handleLongTermChange('compulsoryScrapDate', 'compulsoryScrapLongTerm')"
>
长期有效
</el-checkbox>
</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="海关备案号" prop="customsRecordNo">
<el-input v-model="vehicleForm.customsRecordNo" maxlength="50" />
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="所有组织" prop="organizationName">
<el-cascader
v-model="organizationCascaderValue"
:options="organizationTreeOptions"
:props="organizationCascaderProps"
filterable
clearable
placeholder="请选择"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="使用部门" prop="useDepartment">
<el-select
v-model="vehicleForm.useDepartment"
filterable
clearable
placeholder="请选择"
>
<el-option
v-for="item in organizationOptions"
:key="item.id"
:label="item.rawLabel"
:value="item.rawLabel"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
</section-card>
<section-card title="车辆资质图片">
<el-row :gutter="18">
<el-col :span="6">
<el-form-item label="行驶证档案编号" prop="drivingLicenseNo">
<el-input v-model="vehicleForm.drivingLicenseNo" maxlength="50" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="行驶证有效期" prop="drivingLicenseEndDate">
<div class="date-with-flag">
<el-date-picker
v-model="vehicleForm.drivingLicenseEndDate"
type="date"
value-format="YYYY-MM-DD"
:disabled="vehicleForm.drivingLicenseLongTerm === 1"
/>
<el-checkbox
v-model="vehicleForm.drivingLicenseLongTerm"
:true-label="1"
:false-label="0"
@change="handleLongTermChange('drivingLicenseEndDate', 'drivingLicenseLongTerm')"
>
长期有效
</el-checkbox>
</div>
</el-form-item>
</el-col>
<el-col :span="10">
<el-col :span="6">
<el-form-item label="道路运输证号" prop="roadTransportCertNo">
<el-input v-model="vehicleForm.roadTransportCertNo" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-col :span="6">
<el-form-item label="道路运输证有效期" prop="roadTransportCertEndDate">
<div class="date-with-flag">
<el-date-picker
v-model="vehicleForm.roadTransportCertEndDate"
type="date"
value-format="YYYY-MM-DD"
:disabled="vehicleForm.roadTransportCertLongTerm === 1"
/>
<el-checkbox
v-model="vehicleForm.roadTransportCertLongTerm"
:true-label="1"
:false-label="0"
@change="handleLongTermChange('roadTransportCertEndDate', 'roadTransportCertLongTerm')"
>
长期有效
</el-checkbox>
</div>
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="道路运输年审有效期" prop="annualReviewEndDate">
<div class="date-with-flag">
<el-date-picker
@@ -633,6 +619,7 @@
v-model="vehicleForm.annualReviewLongTerm"
:true-label="1"
:false-label="0"
@change="handleLongTermChange('annualReviewEndDate', 'annualReviewLongTerm')"
>
长期有效
</el-checkbox>
@@ -641,12 +628,12 @@
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-col :span="6">
<el-form-item label="机动车登记编号" prop="registrationNo">
<el-input v-model="vehicleForm.registrationNo" maxlength="50" />
</el-form-item>
</el-col>
<el-col :span="8">
<el-col :span="6">
<el-form-item label="机动车登记日期" prop="registrationDate">
<el-date-picker
v-model="vehicleForm.registrationDate"
@@ -655,9 +642,14 @@
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="行驶证档案编号" prop="drivingLicenseNo">
<el-input v-model="vehicleForm.drivingLicenseNo" maxlength="50" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-col :span="6">
<image-upload-field
label="行驶证主页正面"
prop="drivingLicenseImage"
@@ -672,7 +664,7 @@
"
/>
</el-col>
<el-col :span="8">
<el-col :span="6">
<image-upload-field
label="行驶证主页反面"
prop="drivingLicenseMainBack"
@@ -687,7 +679,7 @@
"
/>
</el-col>
<el-col :span="8">
<el-col :span="6">
<image-upload-field
label="行驶证副页正面"
prop="drivingLicenseViceFront"
@@ -704,7 +696,7 @@
</el-col>
</el-row>
<el-row :gutter="18">
<el-col :span="8">
<el-col :span="6">
<image-upload-field
label="行驶证副页反面"
prop="drivingLicenseViceBack"
@@ -719,7 +711,7 @@
"
/>
</el-col>
<el-col :span="8">
<el-col :span="6">
<image-upload-field
label="道路运输证图片"
prop="roadTransportCertImage"
@@ -731,7 +723,7 @@
@success="url => handleVehicleCertificateUploadSuccess('roadTransportCertImage', url)"
/>
</el-col>
<el-col :span="8">
<el-col :span="6">
<image-upload-field
label="机动车登记本"
prop="registrationImage"
@@ -787,8 +779,7 @@ import VehicleLedger from './components/vehicle-ledger.vue';
const emptyForm = () => ({
id: '',
organizationName: '',
plateProvince: '',
plateNoBody: '',
useDepartment: '',
plateNo: '',
plateColor: '',
vehicleType: '',
@@ -823,44 +814,6 @@ const emptyForm = () => ({
status: 1,
});
const plateProvinceOptions = [
'京',
'津',
'沪',
'渝',
'冀',
'豫',
'云',
'辽',
'黑',
'湘',
'皖',
'鲁',
'新',
'苏',
'浙',
'赣',
'鄂',
'桂',
'甘',
'晋',
'蒙',
'陕',
'吉',
'闽',
'贵',
'粤',
'青',
'藏',
'川',
'宁',
'琼',
'港',
'澳',
].map(item => ({ label: item, value: item }));
const plateColorOptions = ['黄牌', '蓝牌', '绿牌'].map(item => ({ label: item, value: item }));
export default {
components: {
ImageUploadField,
@@ -904,8 +857,6 @@ export default {
vehicleCertificateUploads: {},
uploadHeaders: getUploadHeaders(),
organizationOptions: [],
plateProvinceOptions,
plateColorOptions,
vehicleTypeOptions: [
{ label: '高栏车', value: '高栏' },
{ label: '低栏车', value: '低栏' },
@@ -916,7 +867,7 @@ export default {
],
businessRelationOptions: ['自有', '管理挂靠', '业务合作'],
energyTypeOptions: ['柴油', '汽油', '新能源', '天然气'],
formRules: {
baseRules: {
organizationName: [{ required: true, message: '请选择所属组织', trigger: 'change' }],
plateNo: [
{ required: true, message: '请输入车牌号', trigger: 'change' },
@@ -927,12 +878,9 @@ export default {
businessRelation: [{ required: true, message: '请选择业务关系', trigger: 'change' }],
energyType: [{ required: true, message: '请选择能源类型', trigger: 'change' }],
drivingLicenseNo: [{ required: true, message: '请输入行驶证档案编号', trigger: 'blur' }],
drivingLicenseEndDate: [
{ required: true, message: '请选择行驶证有效期', trigger: 'change' },
],
roadTransportCertNo: [{ required: true, message: '请输入道路运输证号', trigger: 'blur' }],
roadTransportCertEndDate: [
{ required: true, message: '请选择道路运输证有效期', trigger: 'change' },
{ validator: this.validateRoadTransportCertEndDate, trigger: 'change' },
],
annualReviewEndDate: [{ validator: this.validateAnnualReviewEndDate, trigger: 'change' }],
registrationNo: [{ required: true, message: '请输入机动车登记编号', trigger: 'blur' }],
@@ -941,6 +889,16 @@ export default {
},
computed: {
...mapGetters(['permission', 'userInfo']),
// 行驶证有效期:用 required 规则渲染必填星号;勾选「长期有效」后免填,星号同步消失
formRules() {
return {
...this.baseRules,
drivingLicenseEndDate:
this.vehicleForm.drivingLicenseLongTerm === 1
? []
: [{ required: true, message: '请选择行驶证有效期', trigger: 'change' }],
};
},
// 所属组织级联:表单存组织名称,级联控件用 id 路径,两者在此双向转换
organizationCascaderValue: {
get() {
@@ -1021,19 +979,20 @@ export default {
return String(value).replace(/\D/g, '');
},
validatePlateNo(rule, value, callback) {
const plateProvince = String(this.vehicleForm.plateProvince || '').trim();
const plateNoBody = String(this.vehicleForm.plateNoBody || '')
const plateNo = String(value || '')
.trim()
.toUpperCase();
const plateColor = String(this.vehicleForm.plateColor || '').trim();
if (!plateProvince || !plateNoBody || !plateColor) {
callback(new Error('请完整填写车牌号'));
if (!plateNo) {
callback(new Error('请输入车牌号'));
return;
}
if (!/^[A-Z][A-Z0-9挂学警港澳]{5,6}$/.test(plateNoBody)) {
callback(new Error('请输入标准车牌号'));
if (!/^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼港澳]{1}[A-Z][A-Z0-9挂学警港澳]{5,6}$/.test(plateNo)) {
callback(new Error('请输入标准车牌号(如:京A12345'));
return;
}
if (this.vehicleForm.plateNo !== plateNo) {
this.vehicleForm.plateNo = plateNo;
}
callback();
},
validateAnnualReviewEndDate(rule, value, callback) {
@@ -1043,6 +1002,24 @@ export default {
}
callback();
},
// 长期有效勾选:勾选时清空对应日期(不可编辑由 date-picker :disabled 控制),取消勾选后可重新填报
handleLongTermChange(dateProp, longTermProp) {
if (this.vehicleForm[longTermProp] === 1) {
this.vehicleForm[dateProp] = '';
}
this.$refs.vehicleForm?.validateField(dateProp);
},
validateRoadTransportCertEndDate(rule, value, callback) {
if (this.vehicleForm.roadTransportCertLongTerm === 1 && !value) {
callback();
return;
}
if (!value) {
callback(new Error('请选择道路运输证有效期'));
return;
}
callback();
},
openVehicle(row, readonly = false) {
this.readonly = readonly;
if (!row?.id) {
@@ -1415,33 +1392,25 @@ export default {
);
return option ? option.value : vehicleType;
},
handlePlateBodyInput(value) {
this.vehicleForm.plateNoBody = String(value || '').toUpperCase();
this.syncPlateNo();
handlePlateNoInput(value) {
this.vehicleForm.plateNo = String(value || '')
.replace(/\s/g, '')
.toUpperCase();
},
validatePlateFormat() {
this.$refs.vehicleForm?.validateField('plateNo');
},
syncPlateNo(validate = true) {
const plateProvince = String(this.vehicleForm.plateProvince || '').trim();
const plateNoBody = String(this.vehicleForm.plateNoBody || '')
.trim()
const plateNo = String(this.vehicleForm.plateNo || '')
.replace(/\s/g, '')
.toUpperCase();
const plateColor = String(this.vehicleForm.plateColor || '').trim();
this.vehicleForm.plateNoBody = plateNoBody;
this.vehicleForm.plateNo =
plateProvince && plateNoBody ? `${plateProvince}${plateNoBody}` : '';
this.vehicleForm.plateColor = plateColor;
this.vehicleForm.plateNo = plateNo;
if (validate) {
this.$refs.vehicleForm?.validateField('plateNo');
}
},
splitPlateNo() {
const plateNo = String(this.vehicleForm.plateNo || '').trim();
if (!plateNo) {
this.vehicleForm.plateProvince = '';
this.vehicleForm.plateNoBody = '';
return;
}
this.vehicleForm.plateProvince = plateNo.slice(0, 1);
this.vehicleForm.plateNoBody = plateNo.slice(1).toUpperCase();
// 车牌号已统一为单一 input,无需拆分
},
handleSubmit() {
this.syncPlateNo();
@@ -1666,6 +1635,26 @@ export default {
word-break: break-all;
}
// 外廓尺寸长/宽/高组合,排版复用编辑弹窗的 dimension-group
.dimension-group {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
align-items: center;
}
.dimension-group__item {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
}
.dimension-group__label {
flex-shrink: 0;
color: #303133;
}
&__reason {
margin-top: 12px;
}
@@ -1676,17 +1665,11 @@ export default {
:deep(.el-date-editor.el-input__wrapper),
:deep(.el-select),
:deep(.el-input),
:deep(.el-input-number) {
:deep(.el-input-number),
:deep(.el-cascader) {
width: 100%;
}
.plate-group {
display: grid;
grid-template-columns: 112px minmax(0, 1fr) 112px;
gap: 8px;
align-items: center;
}
.dimension-group {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
@@ -1704,7 +1687,6 @@ export default {
.dimension-group__label {
flex-shrink: 0;
color: #303133;
font-weight: 500;
}
.date-with-flag {
+42 -4
View File
@@ -224,7 +224,7 @@
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="客商类" prop="customerType">
<el-form-item label="客商类" prop="customerType">
<el-select
v-model="archiveForm.customerType"
multiple
@@ -241,6 +241,23 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="客商类型" prop="customerKind">
<el-select
v-model="archiveForm.customerKind"
placeholder="请选择"
filterable
clearable
>
<el-option
v-for="item in customerKindOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="企业法人" prop="legalPerson">
<el-input v-model="archiveForm.legalPerson" maxlength="10" show-word-limit />
@@ -1628,6 +1645,14 @@ export default {
regionOptions: [],
customerNatureOptions: [],
customerTypeOptions: [],
customerKindOptions: [
{ label: '外部客商', value: 'external' },
{ label: '内部组织', value: 'internal' },
],
customerKindMap: {
external: '外部客商',
internal: '内部组织',
},
businessScopeOptions: [],
qualificationTypeOptions: [],
creditLevelOptions: ['A', 'B', 'C', 'D', 'E', 'F'].map(item => ({
@@ -1660,7 +1685,7 @@ export default {
trigger: 'blur',
},
],
customerType: [{ required: true, message: '请选择客商类', trigger: 'change' }],
customerType: [{ required: true, message: '请选择客商类', trigger: 'change' }],
legalPerson: [{ required: true, message: '请输入企业法人', trigger: 'blur' }],
contactPhone: [
{ required: true, message: '请输入联系电话', trigger: 'blur' },
@@ -1767,7 +1792,7 @@ export default {
overHidden: true,
},
{
label: '客户类型',
label: '客商分类',
prop: 'customerType',
search: true,
searchType: 'select',
@@ -1777,6 +1802,16 @@ export default {
minWidth: 140,
dicData: [{ label: '全部', value: '' }],
},
{
label: '客商类型',
prop: 'customerKind',
type: 'select',
minWidth: 120,
dicData: [
{ label: '外部客商', value: 'external' },
{ label: '内部组织', value: 'internal' },
],
},
{
label: '客户性质',
prop: 'customerNature',
@@ -2188,7 +2223,8 @@ export default {
customerCode: '客商编号',
shortName: '客商简称',
fullName: '客商名称',
customerType: '客户类型',
customerType: '客商分类',
customerKind: '客商类型',
customerNature: '客户性质',
unifiedCreditCode: '统一信用代码',
deptName: '所属组织',
@@ -2376,6 +2412,7 @@ export default {
rejected: '审核不通过',
},
status: { 1: '启用', 2: '停用', 0: '停用' },
customerKind: { external: '外部客商', internal: '内部组织' },
};
if (Array.isArray(value)) {
return value.map(item => this.formatChangeFieldValue(normalizedField, item)).join('、');
@@ -3661,6 +3698,7 @@ export default {
...detail,
deptId: this.normalizeDeptIds(detail),
customerType: this.splitValue(detail.customerType),
customerKind: detail.customerKind || 'external',
invoiceTaxRate: this.normalizeOptionalAmount(detail.invoiceTaxRate),
registeredCapital: this.normalizeOptionalAmount(detail.registeredCapital),
maxCreditLimit: this.normalizeOptionalAmount(detail.maxCreditLimit),