提交
This commit is contained in:
1 parent
0e60b88cba
commit
87f7f6bf38
34 files changed
+18326
-6165
No files matched your search
@@ -419,6 +419,7 @@ export interface PlanQuery {
|
||||
transportStatus?: string
|
||||
archiveStatus?: string
|
||||
customerName?: string
|
||||
productName?: string
|
||||
planDateFrom?: string
|
||||
planDateTo?: string
|
||||
page?: number
|
||||
|
||||
@@ -205,6 +205,8 @@ const props = withDefaults(
|
||||
enforceSelfVehicleRequired?: boolean
|
||||
/** 承运商车辆:按主车/挂车注入必备槽位(行驶证、营运证) */
|
||||
enforceCarrierVehicleRequired?: boolean
|
||||
/** 人员:切换类型时隐藏非当前类型的必备槽(如 driver 不展示押运员从业资格证) */
|
||||
stripManagedCertNames?: readonly string[]
|
||||
}>(),
|
||||
{
|
||||
title: '证书',
|
||||
@@ -216,7 +218,8 @@ const props = withDefaults(
|
||||
requiredCertNames: () => [],
|
||||
requiredVehiclePart: PL_VEHICLE_CERT_PART_HEAD,
|
||||
enforceSelfVehicleRequired: false,
|
||||
enforceCarrierVehicleRequired: false
|
||||
enforceCarrierVehicleRequired: false,
|
||||
stripManagedCertNames: () => []
|
||||
}
|
||||
)
|
||||
|
||||
@@ -294,7 +297,8 @@ function syncRequiredSlots() {
|
||||
}
|
||||
if (props.requiredCertNames?.length) {
|
||||
certList.value = mergeCertListWithRequiredSlots(certList.value, props.requiredCertNames, {
|
||||
vehiclePart: props.requiredVehiclePart
|
||||
vehiclePart: props.requiredVehiclePart,
|
||||
stripManagedNames: props.stripManagedCertNames
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -319,6 +323,7 @@ watch(
|
||||
props.enforceSelfVehicleRequired,
|
||||
props.enforceCarrierVehicleRequired,
|
||||
props.requiredCertNames,
|
||||
props.stripManagedCertNames,
|
||||
props.trailerNo
|
||||
] as const,
|
||||
() => {
|
||||
|
||||
@@ -21,7 +21,6 @@ export function isRequiredCertSlotComplete(
|
||||
): boolean {
|
||||
if (!cert) return false
|
||||
if (!matchRequiredCertName(cert.certName, requiredName)) return false
|
||||
if (!cert.certNo?.trim()) return false
|
||||
if (!cert.expireDate) return false
|
||||
if (!cert.attachmentUrls?.trim()) return false
|
||||
return true
|
||||
@@ -32,10 +31,9 @@ export function getMissingRequiredCertFieldLabels(
|
||||
requiredName: string
|
||||
): string[] {
|
||||
if (!cert || !matchRequiredCertName(cert.certName, requiredName)) {
|
||||
return ['证号', '有效期', '附件']
|
||||
return ['有效期', '附件']
|
||||
}
|
||||
const missing: string[] = []
|
||||
if (!cert.certNo?.trim()) missing.push('证号')
|
||||
if (!cert.expireDate) missing.push('有效期')
|
||||
if (!cert.attachmentUrls?.trim()) missing.push('附件')
|
||||
return missing
|
||||
@@ -133,12 +131,28 @@ function findCertIndexForRequired(
|
||||
export function mergeCertListWithRequiredSlots(
|
||||
existing: CertRowWithMeta[],
|
||||
requiredNames: readonly string[],
|
||||
options?: { vehiclePart?: string }
|
||||
options?: { vehiclePart?: string; stripManagedNames?: readonly string[] }
|
||||
): CertRowWithMeta[] {
|
||||
const vehiclePart = options?.vehiclePart
|
||||
const next = existing.map(cert => ({ ...cert }))
|
||||
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
|
||||
|
||||
@@ -5,10 +5,20 @@ 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_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'
|
||||
@@ -69,7 +79,10 @@ export function getRequiredPersonnelCertNamesByType(
|
||||
if (personnelType === 'escort') {
|
||||
return PL_PERSONNEL_ESCORT_REQUIRED_CERT_NAMES
|
||||
}
|
||||
return PL_PERSONNEL_DRIVER_REQUIRED_CERT_NAMES
|
||||
if (personnelType === 'driver') {
|
||||
return PL_PERSONNEL_DRIVER_REQUIRED_CERT_NAMES
|
||||
}
|
||||
return PL_PERSONNEL_BOTH_REQUIRED_CERT_NAMES
|
||||
}
|
||||
|
||||
/** 车辆关联人员:从人员主数据类型映射为关联角色(司机/押运员) */
|
||||
@@ -105,7 +118,6 @@ export function isPersonnelCertStrictComplete(
|
||||
): boolean {
|
||||
if (!cert?.certName?.trim()) return false
|
||||
if (!matchRequiredCertName(cert.certName, requiredName)) return false
|
||||
if (!cert.certNo?.trim()) return false
|
||||
if (!cert.expireDate) return false
|
||||
if (!cert.attachmentUrls?.trim()) return false
|
||||
return true
|
||||
@@ -117,12 +129,8 @@ export function matchRequiredCertName(
|
||||
): boolean {
|
||||
const name = actualName?.trim() || ''
|
||||
if (!name) return false
|
||||
if (requiredName === '从业资格证') {
|
||||
return (
|
||||
name === '从业资格证' ||
|
||||
name === '危化品从业资格证' ||
|
||||
name.includes('从业资格证')
|
||||
)
|
||||
if (requiredName === '押运员从业资格证') {
|
||||
return name === '押运员从业资格证' || name === '押运员证'
|
||||
}
|
||||
return name === requiredName
|
||||
}
|
||||
@@ -130,7 +138,6 @@ export function matchRequiredCertName(
|
||||
export function isPersonnelCertUploaded(cert?: Pick<CertRow, 'certName' | 'certNo' | 'expireDate' | 'attachmentUrls'>) {
|
||||
return Boolean(
|
||||
cert?.certName?.trim() &&
|
||||
cert?.certNo?.trim() &&
|
||||
cert?.expireDate &&
|
||||
cert?.attachmentUrls?.trim()
|
||||
)
|
||||
@@ -190,7 +197,8 @@ export function getPersonnelCertMissingSuffix(
|
||||
export const PL_PERSONNEL_CERT_SHORT_LABELS: Record<string, string> = {
|
||||
身份证: '身',
|
||||
驾驶证: '驾',
|
||||
从业资格证: '业'
|
||||
驾驶员从业资格证: '业',
|
||||
押运员从业资格证: '押'
|
||||
}
|
||||
|
||||
export type PersonnelCertBadgeStatus = 'missing' | 'valid' | 'expiring' | 'expired'
|
||||
@@ -207,10 +215,9 @@ function getPersonnelCertMissingFieldLabels(
|
||||
requiredName: string
|
||||
): string[] {
|
||||
if (!cert || !matchRequiredCertName(cert.certName, requiredName)) {
|
||||
return ['证号', '有效期', '附件']
|
||||
return ['有效期', '附件']
|
||||
}
|
||||
const missing: string[] = []
|
||||
if (!cert.certNo?.trim()) missing.push('证号')
|
||||
if (!cert.expireDate) missing.push('有效期')
|
||||
if (!cert.attachmentUrls?.trim()) missing.push('附件')
|
||||
return missing
|
||||
|
||||
@@ -79,7 +79,6 @@ export function mergeVehicleCertsByPart<T extends CertRow>(
|
||||
export function isVehicleCertUploaded(cert?: Pick<CertRow, 'certName' | 'certNo' | 'expireDate' | 'attachmentUrls'>) {
|
||||
return Boolean(
|
||||
cert?.certName?.trim() &&
|
||||
cert?.certNo?.trim() &&
|
||||
cert?.expireDate &&
|
||||
cert?.attachmentUrls?.trim()
|
||||
)
|
||||
@@ -264,10 +263,9 @@ function getVehicleCertMissingFieldLabels(
|
||||
requiredName: string
|
||||
): string[] {
|
||||
if (!cert || !matchVehicleRequiredCertName(cert.certName, requiredName)) {
|
||||
return ['证号', '有效期', '附件']
|
||||
return ['有效期', '附件']
|
||||
}
|
||||
const missing: string[] = []
|
||||
if (!cert.certNo?.trim()) missing.push('证号')
|
||||
if (!cert.expireDate) missing.push('有效期')
|
||||
if (!cert.attachmentUrls?.trim()) missing.push('附件')
|
||||
return missing
|
||||
|
||||
@@ -311,6 +311,7 @@
|
||||
upload-folder="pl/personnel-cert"
|
||||
:cert-name-options="certNameOptions"
|
||||
:required-cert-names="requiredPersonnelCertNames"
|
||||
:strip-managed-cert-names="PL_PERSONNEL_BOTH_REQUIRED_CERT_NAMES"
|
||||
layout="table"
|
||||
/>
|
||||
</el-form>
|
||||
@@ -388,6 +389,7 @@ import {
|
||||
getPersonnelAffiliationLabel,
|
||||
getPersonnelAffiliationTagType,
|
||||
PL_PERSONNEL_AFFILIATION_OPTIONS,
|
||||
PL_PERSONNEL_BOTH_REQUIRED_CERT_NAMES,
|
||||
PL_PERSONNEL_TYPE_OPTIONS
|
||||
} from '@/utils/pl-personnel-cert'
|
||||
|
||||
@@ -431,8 +433,8 @@ const viewDetail = ref<PersonnelDetail | null>(null)
|
||||
const certNameOptions = [
|
||||
'身份证',
|
||||
'驾驶证',
|
||||
'从业资格证',
|
||||
'危化品从业资格证',
|
||||
'驾驶员从业资格证',
|
||||
'押运员从业资格证',
|
||||
'押运员证',
|
||||
'健康证'
|
||||
]
|
||||
@@ -462,9 +464,20 @@ const formData = reactive({
|
||||
})
|
||||
|
||||
const requiredPersonnelCertNames = computed(() =>
|
||||
getRequiredPersonnelCertNamesByType(formData.personnelType)
|
||||
formData.personnelType
|
||||
? getRequiredPersonnelCertNamesByType(formData.personnelType)
|
||||
: []
|
||||
)
|
||||
|
||||
function mergePersonnelCertSlots(list: CertListItem[]) {
|
||||
if (!formData.personnelType) return list
|
||||
return mergeCertListWithRequiredSlots(
|
||||
list,
|
||||
getRequiredPersonnelCertNamesByType(formData.personnelType),
|
||||
{ stripManagedNames: PL_PERSONNEL_BOTH_REQUIRED_CERT_NAMES }
|
||||
)
|
||||
}
|
||||
|
||||
const formRules = {
|
||||
personnelName: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
||||
personnelAffiliation: [{ required: true, message: '请选择人员归属', trigger: 'change' }],
|
||||
@@ -575,10 +588,7 @@ watch(
|
||||
() => formData.personnelType,
|
||||
() => {
|
||||
if (!formData.personnelType) return
|
||||
certList.value = mergeCertListWithRequiredSlots(
|
||||
certList.value,
|
||||
getRequiredPersonnelCertNamesByType(formData.personnelType)
|
||||
)
|
||||
certList.value = mergePersonnelCertSlots(certList.value)
|
||||
certFormListRef.value?.refreshRequiredSlots?.()
|
||||
}
|
||||
)
|
||||
@@ -715,7 +725,7 @@ async function handleEdit(row: Personnel) {
|
||||
status: detail.status,
|
||||
remark: detail.remark || ''
|
||||
})
|
||||
certList.value = mergeCertListWithRequiredSlots(
|
||||
certList.value = mergePersonnelCertSlots(
|
||||
(detail.certList || []).map(c => ({
|
||||
_key: c.id || c.certId || `cert-${Math.random().toString(36).slice(2, 10)}`,
|
||||
certId: c.id || c.certId,
|
||||
@@ -724,8 +734,7 @@ async function handleEdit(row: Personnel) {
|
||||
expireDate: c.expireDate,
|
||||
remindDays: c.remindDays ?? 30,
|
||||
attachmentUrls: c.attachmentUrls || ''
|
||||
})),
|
||||
getRequiredPersonnelCertNamesByType(detail.personnelType || 'both')
|
||||
}))
|
||||
)
|
||||
certFormListRef.value?.refreshRequiredSlots?.()
|
||||
} catch {
|
||||
|
||||
@@ -19,6 +19,14 @@
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="产品名称">
|
||||
<el-input
|
||||
v-model="searchForm.productName"
|
||||
placeholder="搜索产品名称"
|
||||
clearable
|
||||
@keyup.enter="handleSearch"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="跟进状态">
|
||||
<el-select v-model="searchForm.followStatus" placeholder="全部" clearable>
|
||||
<el-option label="草稿" value="draft" />
|
||||
@@ -398,8 +406,9 @@ const activePlanTypeTab = ref<PlPlanTypeTab>('all')
|
||||
const searchForm = reactive({
|
||||
planNo: '',
|
||||
planType: '',
|
||||
productName: '',
|
||||
followStatus: '',
|
||||
transportStatus: '',
|
||||
transportStatus: 'arranged',
|
||||
customerName: '',
|
||||
archiveStatus: ''
|
||||
})
|
||||
@@ -451,6 +460,7 @@ async function fetchData() {
|
||||
followStatus: searchForm.followStatus || undefined,
|
||||
transportStatus: searchForm.transportStatus || undefined,
|
||||
customerName: keyword ? undefined : searchForm.customerName || undefined,
|
||||
productName: keyword ? undefined : searchForm.productName || undefined,
|
||||
archiveStatus: searchForm.archiveStatus || undefined,
|
||||
planDateFrom: dateRange.value?.[0] || undefined,
|
||||
planDateTo: dateRange.value?.[1] || undefined,
|
||||
@@ -480,8 +490,9 @@ function handleReset() {
|
||||
activePlanTypeTab.value = 'all'
|
||||
searchForm.planNo = ''
|
||||
searchForm.planType = ''
|
||||
searchForm.productName = ''
|
||||
searchForm.followStatus = ''
|
||||
searchForm.transportStatus = ''
|
||||
searchForm.transportStatus = 'arranged'
|
||||
searchForm.customerName = ''
|
||||
searchForm.archiveStatus = ''
|
||||
quickSearch.value = ''
|
||||
@@ -581,6 +592,7 @@ useDbInstantSearch(
|
||||
() => ({
|
||||
planNo: searchForm.planNo,
|
||||
planType: searchForm.planType,
|
||||
productName: searchForm.productName,
|
||||
followStatus: searchForm.followStatus,
|
||||
transportStatus: searchForm.transportStatus,
|
||||
customerName: searchForm.customerName,
|
||||
|
||||
Reference in new issue
Block a user