diff --git a/web-admin/src/components/business/pl-cert-list-attachment-cell/index.vue b/web-admin/src/components/business/pl-cert-list-attachment-cell/index.vue new file mode 100644 index 000000000..41dcf0716 --- /dev/null +++ b/web-admin/src/components/business/pl-cert-list-attachment-cell/index.vue @@ -0,0 +1,183 @@ + + + + + + 未上传 + {{ item.label }} + + + + — + + + + + diff --git a/web-admin/src/views/pl/vehicle/index.vue b/web-admin/src/views/pl/vehicle/index.vue index 2bad72b25..465e57213 100644 --- a/web-admin/src/views/pl/vehicle/index.vue +++ b/web-admin/src/views/pl/vehicle/index.vue @@ -112,6 +112,23 @@ header-align="center" :index="(index) => (pagination.page - 1) * pagination.pageSize + index + 1" /> + + + + 未上传 + + + + + + + @@ -257,6 +285,15 @@ maxlength="512" /> + + + @@ -375,6 +412,16 @@ 删除 + + + — + import ListToolbarEndActions from '@/components/business/list-toolbar-end-actions/index.vue' import ListQuickSearchInput from '@/components/business/list-quick-search-input/index.vue' +import AttachmentUploadField from '@/components/business/attachment-upload-field/index.vue' import { usePersistedListColumns } from '@/composables/usePersistedListColumns' import { Check, Close, Delete, DocumentCopy, Edit, Plus, RefreshLeft, Search, Van } from '@element-plus/icons-vue' import { @@ -417,6 +465,7 @@ import { import PlVehicleCertPartTabs from '@/components/business/pl-vehicle-cert-part-tabs/index.vue' import PlVehicleCertPersonnelTabs from '@/components/business/pl-vehicle-cert-personnel-tabs/index.vue' import PlVehicleCertBadges from '@/components/business/pl-vehicle-cert-badges/index.vue' +import PlCertListAttachmentCell from '@/components/business/pl-cert-list-attachment-cell/index.vue' import PlPasteRecognizeDialog from '@/components/business/pl-paste-recognize-dialog/index.vue' import type { VehiclePersonnelViewRow } from '@/components/business/pl-vehicle-personnel-view-list/index.vue' import SystemViewDialog, { @@ -427,6 +476,7 @@ import { pickBxList, pickBxPageResult } from '@/utils/http/bx' import { DEFAULT_LIST_PAGE_SIZE } from '@/utils/table/tableConfig' import { formatDateTime } from '@/utils/format' import { formatPlVehiclePlateDisplay, normalizePlPersonnelSummaryDisplay } from '@/utils/pl-display' +import { firstPhotoUrl, resolvePhotoDisplayUrl } from '@/utils/pl-photo' import { ensureVehicleCertSlots } from '@/utils/pl-cert-slots' import { PL_VEHICLE_CERT_NAME_OPTIONS, @@ -452,6 +502,8 @@ const vehicleList = ref([]) const carrierList = ref([]) const listColumnDefs = [ + { prop: 'photoUrls', label: '照片' }, + { prop: 'certAttachments', label: '证书附件' }, { prop: 'vehicleType', label: '车辆类型' }, { prop: 'vehicleStatus', label: '车辆状态' }, { prop: 'loadCapacity', label: '载重(吨)' }, @@ -466,7 +518,7 @@ const { isIndeterminate, isColumnVisible, resetToDefault -} = usePersistedListColumns('pl-vehicle-list-columns', listColumnDefs) +} = usePersistedListColumns('pl-vehicle-list-columns-v3', listColumnDefs) const personnelOptions = ref([]) const selectedRows = ref([]) @@ -474,6 +526,7 @@ const dialogVisible = ref(false) const pasteVisible = ref(false) const dialogTitle = ref('') const formRef = ref() +const photoUploadRef = ref>() const isEdit = ref(false) const personnelList = ref([]) @@ -508,7 +561,8 @@ const formData = reactive({ carrierName: '', loadCapacity: null as number | null, vehicleStatus: 'available', - remark: '' + remark: '', + photoUrls: '' }) const formRules = { @@ -586,7 +640,16 @@ const viewSections = computed(() => { }, { label: '备注', value: d.remark || '—', span: true }, { label: '创建时间', value: formatDateTime(d.createTime) } - ], + ] + }, + { + title: '车辆照片', + fields: [], + slot: 'photo' + }, + { + title: '', + fields: [], slot: 'vehicleDetailTabs' } ] @@ -884,7 +947,8 @@ function handleAdd() { carrierName: '', loadCapacity: null, vehicleStatus: 'available', - remark: '' + remark: '', + photoUrls: '' }) resetSubTables() certList.value = ensureVehicleCertSlots([], formData.trailerNo, VEHICLE_TYPE_LOCK) @@ -928,7 +992,8 @@ async function handleEdit(row: Vehicle) { carrierName: detail.carrierName, loadCapacity: detail.loadCapacity ?? null, vehicleStatus: detail.vehicleStatus, - remark: detail.remark + remark: detail.remark, + photoUrls: detail.photoUrls || '' }) personnelList.value = resolvePersonnelRows( (detail.personnelList || []).map(p => ({ @@ -960,6 +1025,10 @@ async function handleSubmit() { ElMessage.warning('证书附件上传中,请稍候') return } + if (photoUploadRef.value?.hasUploading?.()) { + ElMessage.warning('附件上传中,请稍候') + return + } if (formData.carrierId) { const carrier = carrierList.value.find(c => c.id === formData.carrierId) @@ -979,6 +1048,7 @@ async function handleSubmit() { loadCapacity: formData.loadCapacity, vehicleStatus: formData.vehicleStatus, remark: formData.remark, + photoUrls: formData.photoUrls || undefined, personnelList: personnelList.value.map(p => ({ personnelId: p.personnelId, personnelName: p.personnelName.trim(), @@ -1095,4 +1165,24 @@ onMounted(() => { flex-wrap: nowrap; gap: 6px; } + +.pl-photo-thumb { + width: 48px; + height: 48px; + border-radius: 8px; + object-fit: cover; + border: 1px solid var(--el-border-color, #d9e8e2); + display: inline-block; + vertical-align: middle; + background: #f8fafc; +} + +.pl-photo-thumb--empty { + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 11px; + color: #94a3b8; + box-sizing: border-box; +}