|
|
@@ -107,6 +107,23 @@
|
|
|
header-align="center"
|
|
|
:index="(index) => (pagination.page - 1) * pagination.pageSize + index + 1"
|
|
|
/>
|
|
|
+ <el-table-column
|
|
|
+ v-if="isColumnVisible('photoUrls')"
|
|
|
+ label="照片"
|
|
|
+ min-width="80"
|
|
|
+ align="center"
|
|
|
+ header-align="center"
|
|
|
+ >
|
|
|
+ <template #default="{ row }">
|
|
|
+ <img
|
|
|
+ v-if="firstPhotoUrl(row.photoUrls)"
|
|
|
+ class="pl-photo-thumb"
|
|
|
+ :src="resolvePhotoDisplayUrl(firstPhotoUrl(row.photoUrls))"
|
|
|
+ alt="车辆照片"
|
|
|
+ />
|
|
|
+ <span v-else class="pl-photo-thumb pl-photo-thumb--empty">未上传</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
<el-table-column
|
|
|
prop="licensePlate"
|
|
|
label="车牌号"
|
|
|
@@ -127,6 +144,17 @@
|
|
|
</div>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
+ <el-table-column
|
|
|
+ v-if="isColumnVisible('certAttachments')"
|
|
|
+ label="证书附件"
|
|
|
+ min-width="340"
|
|
|
+ align="left"
|
|
|
+ header-align="left"
|
|
|
+ >
|
|
|
+ <template #default="{ row }">
|
|
|
+ <PlCertListAttachmentCell :cert-list="row.certList" show-vehicle-part />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
<el-table-column label="车辆类型" min-width="90" align="center" header-align="center" v-if="isColumnVisible('vehicleType')">
|
|
|
<template #default>
|
|
|
<el-tag type="success" size="small">自有</el-tag>
|
|
|
@@ -249,6 +277,15 @@
|
|
|
maxlength="512"
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
+ <el-form-item label="车辆照片" style="grid-column: 1 / -1">
|
|
|
+ <AttachmentUploadField
|
|
|
+ ref="photoUploadRef"
|
|
|
+ v-model="formData.photoUrls"
|
|
|
+ :limit="1"
|
|
|
+ accept="image/*"
|
|
|
+ upload-folder="pl/vehicle-photo"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
</div>
|
|
|
|
|
|
<div class="sub-table-section">
|
|
|
@@ -364,6 +401,16 @@
|
|
|
<el-icon><Delete /></el-icon>删除
|
|
|
</el-button>
|
|
|
</template>
|
|
|
+ <template #photo>
|
|
|
+ <AttachmentUploadField
|
|
|
+ v-if="viewDetail?.photoUrls"
|
|
|
+ :model-value="viewDetail.photoUrls"
|
|
|
+ disabled
|
|
|
+ :limit="1"
|
|
|
+ accept="image/*"
|
|
|
+ />
|
|
|
+ <span v-else>—</span>
|
|
|
+ </template>
|
|
|
<template #vehicleDetailTabs>
|
|
|
<PlVehicleCertPersonnelTabs
|
|
|
:cert-list="viewDetail?.certList || []"
|
|
|
@@ -383,6 +430,7 @@
|
|
|
<script setup lang="ts">
|
|
|
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, Edit, Plus, RefreshLeft, Search, Van } from '@element-plus/icons-vue'
|
|
|
import {
|
|
|
@@ -399,6 +447,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 type { VehiclePersonnelViewRow } from '@/components/business/pl-vehicle-personnel-view-list/index.vue'
|
|
|
import SystemViewDialog, {
|
|
|
type ViewSection
|
|
|
@@ -414,6 +463,7 @@ import {
|
|
|
getPlVehicleStatusTagType,
|
|
|
normalizePlPersonnelSummaryDisplay
|
|
|
} from '@/utils/pl-display'
|
|
|
+import { firstPhotoUrl, resolvePhotoDisplayUrl } from '@/utils/pl-photo'
|
|
|
import {
|
|
|
collectSelfVehicleRequiredCertErrors,
|
|
|
ensureSelfVehicleCertSlots,
|
|
|
@@ -442,6 +492,8 @@ const vehicleList = ref<Vehicle[]>([])
|
|
|
const carrierList = ref<Carrier[]>([])
|
|
|
|
|
|
const listColumnDefs = [
|
|
|
+ { prop: 'photoUrls', label: '照片' },
|
|
|
+ { prop: 'certAttachments', label: '证书附件' },
|
|
|
{ prop: 'vehicleType', label: '车辆类型' },
|
|
|
{ prop: 'vehicleStatus', label: '车辆状态' },
|
|
|
{ prop: 'loadCapacity', label: '载重(吨)' },
|
|
|
@@ -456,13 +508,14 @@ const {
|
|
|
isIndeterminate,
|
|
|
isColumnVisible,
|
|
|
resetToDefault
|
|
|
-} = usePersistedListColumns('pl-self-vehicle-list-columns', listColumnDefs)
|
|
|
+} = usePersistedListColumns('pl-self-vehicle-list-columns-v3', listColumnDefs)
|
|
|
|
|
|
const personnelOptions = ref<Personnel[]>([])
|
|
|
const selectedRows = ref<Vehicle[]>([])
|
|
|
const dialogVisible = ref(false)
|
|
|
const dialogTitle = ref('')
|
|
|
const formRef = ref()
|
|
|
+const photoUploadRef = ref<InstanceType<typeof AttachmentUploadField>>()
|
|
|
const isEdit = ref(false)
|
|
|
|
|
|
const personnelList = ref<VehiclePersonnelRow[]>([])
|
|
|
@@ -495,7 +548,8 @@ const formData = reactive({
|
|
|
carrierId: '',
|
|
|
carrierName: '',
|
|
|
vehicleStatus: 'available',
|
|
|
- remark: ''
|
|
|
+ remark: '',
|
|
|
+ photoUrls: ''
|
|
|
})
|
|
|
|
|
|
const formRules = {
|
|
|
@@ -563,7 +617,16 @@ const viewSections = computed<ViewSection[]>(() => {
|
|
|
{ label: '备注', value: d.remark || '—', span: 'full' }
|
|
|
],
|
|
|
fieldLayout: 'table',
|
|
|
- tableMode: 'triple',
|
|
|
+ tableMode: 'triple'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '车辆照片',
|
|
|
+ fields: [],
|
|
|
+ slot: 'photo'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '',
|
|
|
+ fields: [],
|
|
|
slot: 'vehicleDetailTabs'
|
|
|
}
|
|
|
]
|
|
|
@@ -833,7 +896,8 @@ function handleAdd() {
|
|
|
carrierId: '',
|
|
|
carrierName: '',
|
|
|
vehicleStatus: 'available',
|
|
|
- remark: ''
|
|
|
+ remark: '',
|
|
|
+ photoUrls: ''
|
|
|
})
|
|
|
resetSubTables()
|
|
|
certList.value = ensureSelfVehicleCertSlots([], formData.trailerNo)
|
|
|
@@ -880,7 +944,8 @@ async function handleEdit(row: Vehicle) {
|
|
|
carrierId: detail.carrierId || '',
|
|
|
carrierName: detail.carrierName || '',
|
|
|
vehicleStatus: detail.vehicleStatus,
|
|
|
- remark: detail.remark
|
|
|
+ remark: detail.remark,
|
|
|
+ photoUrls: detail.photoUrls || ''
|
|
|
})
|
|
|
personnelList.value = resolvePersonnelRows(
|
|
|
(detail.personnelList || []).map(p => ({
|
|
|
@@ -915,6 +980,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)
|
|
|
@@ -934,6 +1003,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(),
|
|
|
@@ -1032,4 +1102,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;
|
|
|
+}
|
|
|
</style>
|