256 lines
7.5 KiB
TypeScript
256 lines
7.5 KiB
TypeScript
import type { CertRow } from '@/api/pl'
|
||
import { matchRequiredCertName } from '@/utils/pl-personnel-cert'
|
||
import {
|
||
PL_CARRIER_VEHICLE_HEAD_REQUIRED_CERT_NAMES,
|
||
PL_CARRIER_VEHICLE_TRAILER_REQUIRED_CERT_NAMES,
|
||
PL_SELF_VEHICLE_HEAD_REQUIRED_CERT_NAMES,
|
||
PL_SELF_VEHICLE_TRAILER_REQUIRED_CERT_NAMES,
|
||
PL_VEHICLE_CERT_PART_HEAD,
|
||
PL_VEHICLE_CERT_PART_TRAILER,
|
||
type PlVehicleCertCheckType,
|
||
getVehicleHeadRequiredCertNames,
|
||
getVehicleTrailerRequiredCertNames,
|
||
normalizeVehicleCertPart
|
||
} from '@/utils/pl-vehicle-cert'
|
||
|
||
export type CertRowWithMeta = CertRow & { _key?: string; _required?: boolean }
|
||
|
||
export function isRequiredCertSlotComplete(
|
||
cert: CertRow | undefined,
|
||
requiredName: string
|
||
): boolean {
|
||
if (!cert) return false
|
||
if (!matchRequiredCertName(cert.certName, requiredName)) return false
|
||
if (!cert.expireDate) return false
|
||
// 证书附件非必填
|
||
return true
|
||
}
|
||
|
||
export function getMissingRequiredCertFieldLabels(
|
||
cert: CertRow | undefined,
|
||
requiredName: string
|
||
): string[] {
|
||
if (!cert || !matchRequiredCertName(cert.certName, requiredName)) {
|
||
return ['有效期']
|
||
}
|
||
const missing: string[] = []
|
||
if (!cert.expireDate) missing.push('有效期')
|
||
// 证书附件非必填
|
||
return missing
|
||
}
|
||
|
||
export function collectRequiredCertSlotErrors(
|
||
certList: CertRow[],
|
||
requiredNames: readonly string[],
|
||
partLabel = ''
|
||
): string[] {
|
||
const errors: string[] = []
|
||
for (const requiredName of requiredNames) {
|
||
const matched = certList.find(c => matchRequiredCertName(c.certName, requiredName))
|
||
const missing = getMissingRequiredCertFieldLabels(matched, requiredName)
|
||
if (missing.length) {
|
||
const prefix = partLabel ? `${partLabel}「${requiredName}」` : `「${requiredName}」`
|
||
errors.push(`${prefix}缺少:${missing.join('、')}`)
|
||
}
|
||
}
|
||
return errors
|
||
}
|
||
|
||
export function collectSelfVehicleRequiredCertErrors(
|
||
certList: CertRow[],
|
||
trailerNo?: string | null
|
||
): string[] {
|
||
return collectVehicleRequiredCertErrors(certList, trailerNo, 'self')
|
||
}
|
||
|
||
export function collectCarrierVehicleRequiredCertErrors(
|
||
certList: CertRow[],
|
||
trailerNo?: string | null
|
||
): string[] {
|
||
return collectVehicleRequiredCertErrors(certList, trailerNo, 'carrier')
|
||
}
|
||
|
||
export function collectVehicleRequiredCertErrors(
|
||
certList: CertRow[],
|
||
trailerNo: string | null | undefined,
|
||
vehicleType: PlVehicleCertCheckType
|
||
): string[] {
|
||
const headCerts = certList.filter(
|
||
c => normalizeVehicleCertPart(c.vehiclePart) === PL_VEHICLE_CERT_PART_HEAD
|
||
)
|
||
const trailerCerts = certList.filter(
|
||
c => normalizeVehicleCertPart(c.vehiclePart) === PL_VEHICLE_CERT_PART_TRAILER
|
||
)
|
||
const errors = [
|
||
...collectRequiredCertSlotErrors(
|
||
headCerts,
|
||
getVehicleHeadRequiredCertNames(vehicleType),
|
||
'主车'
|
||
),
|
||
...(trailerNo?.trim()
|
||
? collectRequiredCertSlotErrors(
|
||
trailerCerts,
|
||
getVehicleTrailerRequiredCertNames(vehicleType),
|
||
'挂车'
|
||
)
|
||
: [])
|
||
]
|
||
return errors
|
||
}
|
||
|
||
function createEmptyCertRow(
|
||
certName: string,
|
||
vehiclePart?: string,
|
||
required = false
|
||
): CertRowWithMeta {
|
||
return {
|
||
_key: `cert-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
||
vehiclePart,
|
||
certName,
|
||
certNo: '',
|
||
expireDate: '',
|
||
remindDays: 30,
|
||
attachmentUrls: '',
|
||
_required: required
|
||
}
|
||
}
|
||
|
||
function findCertIndexForRequired(
|
||
certs: CertRowWithMeta[],
|
||
requiredName: string,
|
||
vehiclePart?: string
|
||
): number {
|
||
return certs.findIndex(cert => {
|
||
if (vehiclePart && normalizeVehicleCertPart(cert.vehiclePart) !== vehiclePart) {
|
||
return false
|
||
}
|
||
return matchRequiredCertName(cert.certName, requiredName)
|
||
})
|
||
}
|
||
|
||
export function mergeCertListWithRequiredSlots(
|
||
existing: CertRowWithMeta[],
|
||
requiredNames: readonly string[],
|
||
options?: { vehiclePart?: string; stripManagedNames?: readonly string[] }
|
||
): CertRowWithMeta[] {
|
||
const vehiclePart = options?.vehiclePart
|
||
const stripManagedNames = options?.stripManagedNames
|
||
const normalizedPart = vehiclePart ? normalizeVehicleCertPart(vehiclePart) : null
|
||
|
||
let source = existing
|
||
if (stripManagedNames?.length) {
|
||
source = existing.filter(cert => {
|
||
if (normalizedPart && normalizeVehicleCertPart(cert.vehiclePart) !== normalizedPart) {
|
||
return true
|
||
}
|
||
if (requiredNames.some(name => matchRequiredCertName(cert.certName, name))) {
|
||
return true
|
||
}
|
||
const isManaged = stripManagedNames.some(name => matchRequiredCertName(cert.certName, name))
|
||
return !isManaged
|
||
})
|
||
}
|
||
|
||
const next = source.map(cert => ({ ...cert }))
|
||
|
||
const extras = next.filter(cert => {
|
||
if (normalizedPart && normalizeVehicleCertPart(cert.vehiclePart) !== normalizedPart) {
|
||
return false
|
||
}
|
||
return !requiredNames.some(name => matchRequiredCertName(cert.certName, name))
|
||
})
|
||
|
||
const requiredRows: CertRowWithMeta[] = requiredNames.map(requiredName => {
|
||
const idx = findCertIndexForRequired(next, requiredName, normalizedPart || undefined)
|
||
if (idx >= 0) {
|
||
return {
|
||
...next[idx],
|
||
certName: requiredName,
|
||
vehiclePart: normalizedPart || next[idx].vehiclePart || PL_VEHICLE_CERT_PART_HEAD,
|
||
_required: true
|
||
}
|
||
}
|
||
return createEmptyCertRow(
|
||
requiredName,
|
||
normalizedPart || PL_VEHICLE_CERT_PART_HEAD,
|
||
true
|
||
)
|
||
})
|
||
|
||
const otherPartRows = normalizedPart
|
||
? next.filter(cert => normalizeVehicleCertPart(cert.vehiclePart) !== normalizedPart)
|
||
: []
|
||
|
||
return [...otherPartRows, ...requiredRows, ...extras.map(c => ({ ...c, _required: false }))]
|
||
}
|
||
|
||
function ensureVehicleCertSlotsByNames(
|
||
certList: CertRowWithMeta[],
|
||
trailerNo: string | null | undefined,
|
||
headRequired: readonly string[],
|
||
trailerRequired: readonly string[]
|
||
): CertRowWithMeta[] {
|
||
let merged = mergeCertListWithRequiredSlots(certList, headRequired, {
|
||
vehiclePart: PL_VEHICLE_CERT_PART_HEAD
|
||
})
|
||
if (trailerNo?.trim()) {
|
||
merged = mergeCertListWithRequiredSlots(merged, trailerRequired, {
|
||
vehiclePart: PL_VEHICLE_CERT_PART_TRAILER
|
||
})
|
||
} else {
|
||
merged = stripTrailerRequiredSlots(merged, trailerRequired)
|
||
}
|
||
return merged
|
||
}
|
||
|
||
export function ensureSelfVehicleCertSlots(
|
||
certList: CertRowWithMeta[],
|
||
trailerNo?: string | null
|
||
): CertRowWithMeta[] {
|
||
return ensureVehicleCertSlotsByNames(
|
||
certList,
|
||
trailerNo,
|
||
PL_SELF_VEHICLE_HEAD_REQUIRED_CERT_NAMES,
|
||
PL_SELF_VEHICLE_TRAILER_REQUIRED_CERT_NAMES
|
||
)
|
||
}
|
||
|
||
export function ensureCarrierVehicleCertSlots(
|
||
certList: CertRowWithMeta[],
|
||
trailerNo?: string | null
|
||
): CertRowWithMeta[] {
|
||
return ensureVehicleCertSlotsByNames(
|
||
certList,
|
||
trailerNo,
|
||
PL_CARRIER_VEHICLE_HEAD_REQUIRED_CERT_NAMES,
|
||
PL_CARRIER_VEHICLE_TRAILER_REQUIRED_CERT_NAMES
|
||
)
|
||
}
|
||
|
||
export function ensureVehicleCertSlots(
|
||
certList: CertRowWithMeta[],
|
||
trailerNo: string | null | undefined,
|
||
vehicleType: PlVehicleCertCheckType
|
||
): CertRowWithMeta[] {
|
||
return vehicleType === 'carrier'
|
||
? ensureCarrierVehicleCertSlots(certList, trailerNo)
|
||
: ensureSelfVehicleCertSlots(certList, trailerNo)
|
||
}
|
||
|
||
export function stripTrailerRequiredSlots(
|
||
certList: CertRowWithMeta[],
|
||
trailerRequiredNames: readonly string[] = PL_SELF_VEHICLE_TRAILER_REQUIRED_CERT_NAMES
|
||
): CertRowWithMeta[] {
|
||
return certList.filter(cert => {
|
||
if (normalizeVehicleCertPart(cert.vehiclePart) !== PL_VEHICLE_CERT_PART_TRAILER) {
|
||
return true
|
||
}
|
||
return !trailerRequiredNames.some(name => matchRequiredCertName(cert.certName, name))
|
||
})
|
||
}
|
||
|
||
export function formatRequiredCertErrors(errors: string[]): string {
|
||
if (!errors.length) return ''
|
||
return `必备证书未齐全:${errors.join(';')}`
|
||
}
|