diff --git a/web-admin/src/views/pl/personnel/index.vue b/web-admin/src/views/pl/personnel/index.vue
index 419af4af4..e49ae3682 100644
--- a/web-admin/src/views/pl/personnel/index.vue
+++ b/web-admin/src/views/pl/personnel/index.vue
@@ -105,6 +105,23 @@
header-align="center"
:index="(index) => (pagination.page - 1) * pagination.pageSize + index + 1"
/>
+
+
+
+ 未上传
+
+
+
+
+
+
+
+
+
+
@@ -341,6 +378,16 @@
删除
+
+
+ —
+
@@ -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([])
@@ -429,6 +481,7 @@ const formRef = ref()
const isEdit = ref(false)
const certList = ref([])
const certFormListRef = ref()
+const photoUploadRef = ref>()
const viewVisible = ref(false)
const viewLoading = ref(false)
const viewDetail = ref(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(() => {
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;
+}