|
|
@@ -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>
|