Files
shanxia2.0/web-admin/src/utils/pl-cert-slots.ts
T
2026-08-02 17:18:33 +08:00

256 lines
7.5 KiB
TypeScript
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.
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(';')}`
}