日期: 2026-07-12
状态: 已实现(2026-07-12)
前置:docs/superpowers/specs/2026-06-19-pl-vehicle-personnel-cert-design.md(动态多证、首期不配置必检)
界面模式: P01 标准列表 + 弹窗表单(/#/pl/vehicle、/#/pl/self-vehicle、/#/pl/personnel)
| 环节 | 行为 |
|---|---|
| 证书录入 | 用户点击「添加证书」→ 手选/手输证书名称 → 填写证号、有效期 → 上传附件 |
| 前端校验 | 仅有证书行时「证书名称不能为空」;不校验必备证种齐全 |
| 必备常数 | pl-vehicle-cert.ts / pl-personnel-cert.ts 已定义必备名称,但 validateVehicleRequiredCerts 已 @deprecated |
| 缺失提示 | 查看态标题可显示「(证书缺失)」;保存不阻断 |
| 人员类型 | pl_personnel 无人员类型字段;司机/押运员角色仅在车辆关联子表 pl_vehicle_personnel.role |
| 承运商车辆 | 「全部车辆信息」含自有 + 承运商;必备缺失检查仅对 vehicleType=self |
vehicleType=self 的车辆强制主车/挂车必备;承运商等非山峡车辆不强制。2026-06-19 明确「首期不配置必检规则、安排运输不硬性拦截证书」。本次变更:
hazard_cert_ready 的既有逻辑(危化品计划级证书另案)。pl_cert 动态多证表结构,不新增证书实例表。| # | 决策 | 说明 |
|---|---|---|
| D1 | 必备槽位预置 + 锁定 | 必备行 certName 只读、不可删除;额外行可自由增删 |
| D2 | 必备字段:证号 + 有效期 + 附件 | 提醒天数维持默认 30,不作为必备条件 |
| D3 | 仅山峡自有车强制车辆证 | vehicleType=self 时校验主车 3 证、有挂车号时挂车 4 证;carrier 不校验 |
| D4 | 人员类型三选一 | driver 驾驶员 / escort 押运员 / both 驾驶员兼押运员;人员主数据必填 |
| D5 | 人员必备按类型 | 2026-07-13-pl-personnel-cert-split-design.md 替代:驾/押从业证拆分,both 须 4 证 |
| D6 | 从业资格证别名 | 押运员证 可匹配 押运员从业资格证 |
| D7 | 保存一律阻断 | 历史数据缺必备证时,任何字段保存均须先补齐 |
| D8 | 前后端双校验 | 前端 UX + 后端 API 400,防绕过 |
| D9 | 关联人员角色兼容 | 车辆子表 role 保留;保存山峡自有车时校验人员类型与 role 可匹配(见 §三) |
| D10 | 挂车动态 | 无 trailerNo 不要求挂车槽位;填写挂车号后立即要求 4 项必备 |
vehicleType=self)| 部位 | 条件 | 必备证书(各需证号+有效期+附件) |
|---|---|---|
| 主车 | 始终 | 行驶证、营运证、保险单 |
| 挂车 | trailerNo 非空 |
行驶证、营运证、保险单、罐检报告 |
常数来源:PL_SELF_VEHICLE_HEAD_REQUIRED_CERT_NAMES、PL_SELF_VEHICLE_TRAILER_REQUIRED_CERT_NAMES(web-admin/src/utils/pl-vehicle-cert.ts)。
vehicleType=carrier)pl_personnel.personnel_type)| personnel_type | 展示文案 | 必备证书 |
|---|---|---|
driver |
驾驶员 | 身份证、驾驶证、驾驶员从业资格证 |
escort |
押运员 | 身份证、押运员从业资格证 |
both |
驾驶员兼押运员 | 身份证、驾驶证、驾驶员从业资格证、押运员从业资格证 |
常数来源:PL_PERSONNEL_DRIVER_REQUIRED_CERT_NAMES、PL_PERSONNEL_ESCORT_REQUIRED_CERT_NAMES。
必备槽位 同时满足:
certName 匹配目标名称(从业资格证含别名规则)
certNo 非空 trim
expireDate 非空
attachmentUrls 非空 trim(至少一个 URL)
pl_personnel 新增字段| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
personnel_type |
varchar(20) | ✅(新数据;迁移见 §2.3) | driver / escort / both |
DDL 示例:
ALTER TABLE `pl_personnel`
ADD COLUMN `personnel_type` varchar(20) NOT NULL DEFAULT 'both'
COMMENT '人员类型 driver-驾驶员 escort-押运员 both-驾驶员兼押运员'
AFTER `id_card_no`;
同步:docs/sql/pl_tables.sql、docs/sql/shanxia2.0.sql;增量脚本 backend/scripts/migration_20260712_pl_personnel_type.sql。
| dict_type | 值 |
|---|---|
pl_personnel_type |
driver-驾驶员, escort-押运员, both-驾驶员兼押运员 |
列表 Tag、表单下拉可与字典对齐;必备规则仍硬编码于前后端 util,不依赖运行时字典配置(YAGNI)。
按优先级推断 personnel_type:
pl_vehicle_personnel 中该人仅有 role=driver → driverrole=escort → escortbothboth(必备要求最宽,避免误标 escort 导致缺驾驶证仍可通过)迁移后运营需对人员主数据做一次补录/核对(D7 保存阻断会驱动补齐)。
pl_cert 无结构变更。必备/额外均存同一表;必备行通过 cert_name +(车辆)vehicle_part 识别。
可选:必备行写入时设 order_num 靠前,额外行在后。
/transport/vehicle)if vehicle_type == 'self':
校验主车必备 3 证齐全(证号+有效期+附件)
if trailer_no 非空:
校验挂车必备 4 证齐全
校验关联人员(见 3.2)
else:
跳过车辆必备校验(仅校验 certName 非空等基础项,若有证书行)
「山峡自有车辆信息」页固定 vehicleType=self,与「全部车辆信息」中自有行逻辑一致。
保存时除人员主数据证书外,额外校验:
| 检查项 | 规则 |
|---|---|
| 类型与 role 匹配 | escort 类型不可关联为 driver;driver 类型不可关联为 escort;both 均可 |
| 角色必备证书 | 关联行的 role=driver 时,该人员证书须满足 driver 必备;role=escort 时满足 escort 必备 |
说明:人员主数据按 personnel_type 校验一次;若 both 且仅关联为 escort,主数据仍须驾驶证(因类型为 both)。若未来需放宽,可另开变更。
/transport/personnel)personnel_type 必填。remark)均执行完整必备校验(D7)。trailerNo 从空变为非空:前端立即展示挂车 4 个必备空槽;保存时校验。vehicle_part=trailer 证书行保存策略——保留数据但不阻断(挂车号清空后不再校验挂车必备;证书行仍随 certList 提交,后端按现有 upsert 逻辑处理)。方案:扩展或替换 PlCertFormList → PlCertSlotFormList(推荐新组件,原组件供承运商等非强制场景复用)。
| 模式 | props | 行为 |
|---|---|---|
required |
requiredCertNames, vehiclePart?, strictFields=true |
预置必备行、锁定名称、校验三字段 |
free |
无 required | 等同现有 PlCertFormList |
PlVehicleCertPartTabs:
vehicleType=self → 各 part 传入对应 requiredCertNamesvehicleType=carrier → 子组件 mode=free| 页面 | 变更 |
|---|---|
pl/self-vehicle/index.vue |
证书区 required;保存前 validateSelfVehicleCerts |
pl/vehicle/index.vue |
依 formData.vehicleType 切换 required/free;查看态缺失 suffix 逻辑保持 |
pl/personnel/index.vue |
表单加「人员类型」;证书区按类型 required;列表加类型列 Tag |
人员类型* [ el-select ]
- 驾驶员 (driver)
- 押运员 (escort)
- 驾驶员兼押运员 (both)
both 或空(须必选,无默认值更安全 → 无默认,必选)。┌─ 人员证书 ──────────────────────────────────────┐
│ ▎必备证书 │
│ 身份证 证号* 有效期* 附件* 🔒 │
│ 驾驶证 ... │
│ ▎其他证书 │
│ [+ 添加其他证书] │
└─────────────────────────────────────────────────┘
必备区与额外区可用分组标题或表格分段区分;必备行删除按钮隐藏。
ElMessage.warning + 可选 ElMessageBox.alert 列出缺失项。主车「行驶证」缺少:证号、附件。scroll-to-error 滚动至证书区首个缺失槽。pl_cert_required_service.py(或并入 pl_cert_service.py)def get_required_vehicle_cert_names(vehicle_type, trailer_no, part) -> list[str]
def get_required_personnel_cert_names(personnel_type) -> list[str]
def validate_required_certs(cert_rows, required_names, *, part_label=None) -> None # raises HTTPException 400
def validate_personnel_type_role_compat(personnel_type, role) -> None
| 路由 | 校验时机 |
|---|---|
POST/PUT .../vehicle |
vehicle_type=self 时校验 cert_list + 关联人员 |
POST/PUT .../personnel |
始终校验 personnel_type + cert_list |
错误响应示例:
{
"detail": "必备证书未齐全:主车「保险单」缺少有效期、附件;人员「张三」类型为驾驶员,缺少驾驶证"
}
PersonnelBase / PersonnelCreate / PersonnelUpdate / PersonnelRead 增加 personnel_type(alias personnelType)。VehicleCreate/Update 无需新字段;沿用 vehicle_type。建议在 openspec/changes/transport-plan-management/ 或新 change 中增补 Scenario:
交叉核对:openspec/changes/zkxw-engineering-standards/specs/ui-conventions/spec.md(Tag 展示人员类型、按钮图标等)。
python scripts/check_field_consistency.py 通过(personnel_type 字段)。pl_cert 数据不丢失。| 风险 | 缓解 |
|---|---|
| 历史大量缺证导致无法保存 | D7 为业务明确要求;上线前导出缺证清单,运营批量补录 |
| 人员类型迁移不准 | 迁移规则 §2.3 + 列表展示类型供人工修正 |
| 组件复杂度上升 | 新组件 PlCertSlotFormList,承运商继续用旧组件 |
| 车辆页切换类型 self↔carrier | 切换为 carrier 时隐藏必备槽位且不校验;切回 self 时重新注入必备槽并校验 |
| both 类型关联 escort 仍要驾驶证 | 设计已说明;若业务异议可后续改为「按 role 动态必备」 |
| 模块 | 预估 |
|---|---|
| DB + 迁移 + Schema | 0.5 天 |
| 后端必备校验服务 + API | 0.5 天 |
PlCertSlotFormList + 车辆 tabs 接入 |
1 天 |
| 人员页类型字段 + 证书接入 | 0.5 天 |
| 三页联调 + 历史场景自测 | 0.5~1 天 |
| 合计 | 约 3~3.5 人天 |
personnel_type 列 + 迁移推断脚本_row_to_dict / 校验服务 / vehicle & personnel APIvalidateRequiredCertSlots、buildRequiredCertRowsPlCertSlotFormListself-vehicle、vehicle、personnelcheck_field_consistency.py需求与 pl-vehicle-cert.ts / pl-personnel-cert.ts 完全一致,无需调整必备名称列表,仅需 启用 校验并改造 UI 为固定槽位。