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

291 lines
9.4 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'
export const PL_PERSONNEL_CERT_MISSING_SUFFIX = '(证书缺失)'
export const PL_PERSONNEL_DRIVER_REQUIRED_CERT_NAMES = [
'身份证',
'驾驶证',
'驾驶员从业资格证'
] as const
export const PL_PERSONNEL_ESCORT_REQUIRED_CERT_NAMES = [
'身份证',
'押运员从业资格证'
] as const
export const PL_PERSONNEL_BOTH_REQUIRED_CERT_NAMES = [
'身份证',
'驾驶证',
'驾驶员从业资格证',
'押运员从业资格证'
] as const
export type PersonnelCertRole = 'driver' | 'escort'
export type PersonnelType = 'driver' | 'escort' | 'both'
export const PL_PERSONNEL_TYPE_OPTIONS = [
{ value: 'driver', label: '驾驶员' },
{ value: 'escort', label: '押运员' },
{ value: 'both', label: '驾驶员兼押运员' }
] as const
export type PersonnelAffiliation = 'self' | 'carrier'
export const PL_PERSONNEL_AFFILIATION_OPTIONS = [
{ value: 'self', label: '山峡' },
{ value: 'carrier', label: '非山峡' }
] as const
export function getPersonnelAffiliationLabel(affiliation?: string | null): string {
const found = PL_PERSONNEL_AFFILIATION_OPTIONS.find(item => item.value === affiliation)
return found?.label || affiliation || '—'
}
export function getPersonnelAffiliationTagType(
affiliation?: string | null
): 'success' | 'warning' | 'info' {
if (affiliation === 'self') return 'success'
if (affiliation === 'carrier') return 'warning'
return 'info'
}
export function validatePersonnelAffiliationVehicleCompat(
personnelAffiliation: string | undefined,
vehicleType: string | undefined,
onError: (message: string) => void
): boolean {
if (!personnelAffiliation || !vehicleType) return true
if (personnelAffiliation === vehicleType) return true
const vehicleLabel = vehicleType === 'self' ? '山峡' : '非山峡'
onError(`${vehicleLabel}车辆仅可关联${vehicleLabel}人员`)
return false
}
export function getPersonnelTypeLabel(type?: string | null): string {
const found = PL_PERSONNEL_TYPE_OPTIONS.find(item => item.value === type)
return found?.label || type || '—'
}
export function getPersonnelTypeTagType(type?: string | null): 'success' | 'warning' | 'primary' | 'info' {
if (type === 'driver') return 'primary'
if (type === 'escort') return 'warning'
if (type === 'both') return 'success'
return 'info'
}
export function getRequiredPersonnelCertNamesByType(
personnelType?: string | null
): readonly string[] {
if (personnelType === 'escort') {
return PL_PERSONNEL_ESCORT_REQUIRED_CERT_NAMES
}
if (personnelType === 'driver') {
return PL_PERSONNEL_DRIVER_REQUIRED_CERT_NAMES
}
return PL_PERSONNEL_BOTH_REQUIRED_CERT_NAMES
}
/** 车辆关联人员:从人员主数据类型映射为关联角色(司机/押运员) */
export function mapPersonnelTypeToVehicleRole(
personnelType?: string | null
): 'driver' | 'escort' | undefined {
if (personnelType === 'driver') return 'driver'
if (personnelType === 'escort') return 'escort'
if (personnelType === 'both') return 'driver'
return undefined
}
export function validatePersonnelTypeRoleCompat(
personnelType: string | undefined,
role: string | undefined,
onError: (message: string) => void
): boolean {
if (!personnelType || !role) return true
if (personnelType === 'driver' && role === 'escort') {
onError('驾驶员类型人员不可关联为押运员')
return false
}
if (personnelType === 'escort' && role === 'driver') {
onError('押运员类型人员不可关联为驾驶员')
return false
}
return true
}
export function isPersonnelCertStrictComplete(
cert: Pick<CertRow, 'certName' | 'certNo' | 'expireDate' | 'attachmentUrls'> | undefined,
requiredName: string
): boolean {
if (!cert?.certName?.trim()) return false
if (!matchRequiredCertName(cert.certName, requiredName)) return false
if (!cert.expireDate) return false
// 证书附件非必填
return true
}
export function matchRequiredCertName(
actualName: string | undefined,
requiredName: string
): boolean {
const name = actualName?.trim() || ''
if (!name) return false
if (requiredName === '押运员从业资格证') {
return name === '押运员从业资格证' || name === '押运员证'
}
return name === requiredName
}
export function isPersonnelCertUploaded(cert?: Pick<CertRow, 'certName' | 'certNo' | 'expireDate' | 'attachmentUrls'>) {
return Boolean(cert?.certName?.trim() && cert?.expireDate)
}
export function getRequiredPersonnelCertNames(role: PersonnelCertRole): readonly string[] {
return role === 'driver'
? PL_PERSONNEL_DRIVER_REQUIRED_CERT_NAMES
: PL_PERSONNEL_ESCORT_REQUIRED_CERT_NAMES
}
export function getMissingPersonnelCertNames(
role: PersonnelCertRole,
certList: CertRow[] = []
): string[] {
return getRequiredPersonnelCertNames(role).filter(requiredName => {
const matched = certList.find(cert => matchRequiredCertName(cert.certName, requiredName))
return !isPersonnelCertUploaded(matched)
})
}
export function hasPersonnelCertMissing(
role: PersonnelCertRole,
certList: CertRow[] = []
): boolean {
return getMissingPersonnelCertNames(role, certList).length > 0
}
/** 人员主数据页:按人员类型检查必备证书 */
export function hasPersonnelMasterCertMissing(
certList: CertRow[] = [],
personnelType?: string | null
): boolean {
const required = getRequiredPersonnelCertNamesByType(personnelType || 'both')
return required.some(requiredName => {
const matched = certList.find(cert => matchRequiredCertName(cert.certName, requiredName))
return !isPersonnelCertStrictComplete(matched, requiredName)
})
}
export function getPersonnelCertMissingSuffix(
certList: CertRow[] = [],
role?: PersonnelCertRole,
personnelType?: string | null
): string {
if (personnelType) {
return hasPersonnelMasterCertMissing(certList, personnelType)
? PL_PERSONNEL_CERT_MISSING_SUFFIX
: ''
}
if (role) {
return hasPersonnelCertMissing(role, certList) ? PL_PERSONNEL_CERT_MISSING_SUFFIX : ''
}
return hasPersonnelMasterCertMissing(certList) ? PL_PERSONNEL_CERT_MISSING_SUFFIX : ''
}
export const PL_PERSONNEL_CERT_SHORT_LABELS: Record<string, string> = {
身份证: '身',
驾驶证: '驾',
驾驶员从业资格证: '业',
押运员从业资格证: '押'
}
export type PersonnelCertBadgeStatus = 'missing' | 'valid' | 'expiring' | 'expired'
export interface PersonnelCertBadgeItem {
requiredName: string
shortLabel: string
status: PersonnelCertBadgeStatus
title: string
}
function getPersonnelCertMissingFieldLabels(
cert: Pick<CertRow, 'certName' | 'certNo' | 'expireDate' | 'attachmentUrls'> | undefined,
requiredName: string
): string[] {
if (!cert || !matchRequiredCertName(cert.certName, requiredName)) {
return ['有效期']
}
const missing: string[] = []
if (!cert.expireDate) missing.push('有效期')
// 证书附件非必填
return missing
}
function formatPersonnelCertBadgeTooltip(
requiredName: string,
status: PersonnelCertBadgeStatus,
cert?: Pick<CertRow, 'certName' | 'certNo' | 'expireDate' | 'attachmentUrls' | 'remindDays'>
): string {
if (status === 'missing') {
if (!cert || !matchRequiredCertName(cert.certName, requiredName)) {
return `${requiredName}:缺失(未登记该证书)`
}
const missing = getPersonnelCertMissingFieldLabels(cert, requiredName)
return `${requiredName}:缺失(缺少${missing.join('、')})`
}
const expireDate = cert?.expireDate || '—'
const expDate = new Date(expireDate)
const today = new Date()
today.setHours(0, 0, 0, 0)
expDate.setHours(0, 0, 0, 0)
const diffDays = Math.ceil((expDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24))
const certNo = cert?.certNo?.trim()
const certNoText = certNo ? `,证号 ${certNo}` : ''
if (status === 'expired') {
return `${requiredName}:已过期,有效期 ${expireDate},已过期 ${Math.abs(diffDays)} 天${certNoText}`
}
if (status === 'expiring') {
return `${requiredName}:临期,有效期 ${expireDate},剩余 ${diffDays} 天${certNoText}`
}
return `${requiredName}:正常,有效期 ${expireDate}${certNoText}`
}
export function getPersonnelCertBadgeStatus(
cert: Pick<CertRow, 'certName' | 'certNo' | 'expireDate' | 'attachmentUrls' | 'remindDays'> | undefined,
requiredName: string
): PersonnelCertBadgeStatus {
if (!isPersonnelCertStrictComplete(cert, requiredName)) {
return 'missing'
}
const expireDate = cert?.expireDate
if (!expireDate) return 'missing'
const remindDays = cert?.remindDays ?? 30
const expDate = new Date(expireDate)
const today = new Date()
today.setHours(0, 0, 0, 0)
expDate.setHours(0, 0, 0, 0)
const diffDays = Math.ceil((expDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24))
if (diffDays <= 0) return 'expired'
if (diffDays <= remindDays) return 'expiring'
return 'valid'
}
export function getPersonnelRequiredCertBadges(
personnelType: string | null | undefined,
certList: CertRow[] = []
): PersonnelCertBadgeItem[] {
return getRequiredPersonnelCertNamesByType(personnelType).map(requiredName => {
const matched = certList.find(cert => matchRequiredCertName(cert.certName, requiredName))
const status = getPersonnelCertBadgeStatus(matched, requiredName)
const shortLabel = PL_PERSONNEL_CERT_SHORT_LABELS[requiredName] || requiredName.slice(0, 1)
return {
requiredName,
shortLabel,
status,
title: formatPersonnelCertBadgeTooltip(requiredName, status, matched)
}
})
}