feat(web-admin): add personnel photo on driver/escort pages

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-08-02 10:37:19 +08:00
1 parent 9089e455e9
commit 8d9f5777e0
1 file changed
+90 -4
+90 -4
View File
@@ -105,6 +105,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="personnelName"
label="姓名"
@@ -122,6 +139,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" />
</template>
</el-table-column>
<el-table-column
v-if="isColumnVisible('personnelAffiliation')"
label="人员归属"
@@ -289,6 +317,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/personnel-photo"
/>
</el-form-item>
</div>
<!-- 人员证书(列表) -->
@@ -341,6 +378,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 #certs>
<PlCertViewList :cert-list="viewDetail?.certList || []" layout="table" />
</template>
@@ -362,6 +409,8 @@ import PlCertFormList from '@/components/business/pl-cert-form-list/index.vue'
import PlPasteRecognizeDialog from '@/components/business/pl-paste-recognize-dialog/index.vue'
import PlCertViewList from '@/components/business/pl-cert-view-list/index.vue'
import PlPersonnelCertBadges from '@/components/business/pl-personnel-cert-badges/index.vue'
import PlCertListAttachmentCell from '@/components/business/pl-cert-list-attachment-cell/index.vue'
import AttachmentUploadField from '@/components/business/attachment-upload-field/index.vue'
import SystemViewDialog, {
type ViewSection
} from '@/components/business/system-view-dialog/index.vue'
@@ -372,6 +421,7 @@ import { useDbInstantSearch } from '@/composables/useDbInstantSearch'
import { pickBxPageResult } from '@/utils/http/bx'
import { DEFAULT_LIST_PAGE_SIZE } from '@/utils/table/tableConfig'
import { formatDateTime } from '@/utils/format'
import { firstPhotoUrl, resolvePhotoDisplayUrl } from '@/utils/pl-photo'
import { canDeleteByCreator, filterDeletableByCreator, allRowsDeletableByCreator, CREATOR_DELETE_TIP } from '@/utils/creator-delete'
import {
collectRequiredCertSlotErrors,
@@ -401,6 +451,8 @@ const isCarrierPersonnel = computed(() => affiliationMode.value === 'carrier')
const loading = ref(false)
const listColumnDefs = [
{ prop: 'photoUrls', label: '照片' },
{ prop: 'certAttachments', label: '证书附件' },
{ prop: 'personnelAffiliation', label: '人员归属' },
{ prop: 'personnelType', label: '人员类型' },
{ prop: 'phoneNo', label: '手机号' },
@@ -417,7 +469,7 @@ const {
isIndeterminate,
isColumnVisible,
resetToDefault
} = usePersistedListColumns('pl-personnel-list-columns', listColumnDefs)
} = usePersistedListColumns('pl-personnel-list-columns-v3', listColumnDefs)
const submitLoading = ref(false)
const personnelRows = ref<Personnel[]>([])
@@ -429,6 +481,7 @@ const formRef = ref()
const isEdit = ref(false)
const certList = ref<CertListItem[]>([])
const certFormListRef = ref<CertFormListRef>()
const photoUploadRef = ref<InstanceType<typeof AttachmentUploadField>>()
const viewVisible = ref(false)
const viewLoading = ref(false)
const viewDetail = ref<PersonnelDetail | null>(null)
@@ -463,7 +516,8 @@ const formData = reactive({
personnelType: '',
personnelAffiliation: 'self',
status: '1',
remark: ''
remark: '',
photoUrls: ''
})
const requiredPersonnelCertNames = computed(() =>
@@ -559,6 +613,11 @@ const viewSections = computed<ViewSection[]>(() => {
fieldLayout: 'table',
tableMode: 'triple'
},
{
title: '人员照片',
fields: [],
slot: 'photo'
},
{
title: `人员证书${
viewDetail.value && !isCarrierPersonnel.value
@@ -697,7 +756,8 @@ function handleAdd() {
personnelType: '',
personnelAffiliation: affiliationMode.value,
status: '1',
remark: ''
remark: '',
photoUrls: ''
})
resetCertList()
dialogVisible.value = true
@@ -740,7 +800,8 @@ async function handleEdit(row: Personnel) {
personnelType: detail.personnelType || 'both',
personnelAffiliation: affiliationMode.value,
status: detail.status,
remark: detail.remark || ''
remark: detail.remark || '',
photoUrls: detail.photoUrls || ''
})
certList.value = mergePersonnelCertSlots(
@@ -767,6 +828,10 @@ async function handleSubmit() {
ElMessage.warning('证书附件上传中,请稍候')
return
}
if (photoUploadRef.value?.hasUploading?.()) {
ElMessage.warning('附件上传中,请稍候')
return
}
submitLoading.value = true
try {
@@ -779,6 +844,7 @@ async function handleSubmit() {
personnelAffiliation: formData.personnelAffiliation,
status: formData.status,
remark: formData.remark || undefined,
photoUrls: formData.photoUrls || undefined,
certList: certList.value.map(c => ({
certId: c.certId,
certName: c.certName.trim(),
@@ -866,4 +932,24 @@ onMounted(() => {
flex-wrap: wrap;
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>