smart-bid-web/src/views/enterpriseLibrary/personnel/components/PersonnelForm.vue

487 lines
16 KiB
Vue

<!-- 新增人员表单 -->
<template>
<div class="app-container" :class="{ 'no-pointer-events': showUploadAnimation || showSaveAnimation }">
<!-- 全局上传动画 -->
<GlobalUploadAnimation v-if="showUploadAnimation" :text="animationText" subtext="正在处理文件,请稍候..." />
<!-- 保存动画 -->
<GlobalUploadAnimation v-if="showSaveAnimation" text="数据上传中" subtext="请稍后,正在保存数据..." />
<div class="content-header">
<el-button class="reset-btn" @click="handleClose">返回</el-button>
<el-button class="search-btn" :loading="isSaving" @click="handleSave">
保存
</el-button>
</div>
<div class="content-body">
<el-row :gutter="24" class="content-row">
<!-- 基本信息 -->
<el-col :span="6" class="pane-left">
<BasicInfoPersonnel ref="basicInfoPersonnel" @handlePersonnelPosition="handlePersonnelPosition"
:detailData="detailData" />
</el-col>
<!-- 资质信息 -->
<el-col :span="6" class="pane-center" v-show="!isProjectChiefEngineer">
<QualificationInfoPersonnel ref="qualificationInfoPersonnel" :personnelPosition="personnelPosition"
:detailData="detailData" />
</el-col>
<!-- 其他信息 -->
<el-col :span="6" class="pane-right">
<OtherInfoPersonnel ref="otherInfoPersonnel" :detailData="detailData"
:personnelPosition="personnelPosition" />
</el-col>
</el-row>
</div>
</div>
</template>
<script>
import { encryptWithSM4, decryptWithSM4 } from '@/utils/sm'
import BasicInfoPersonnel from './child/BasicInfo.vue'
import QualificationInfoPersonnel from './child/QualificationInfo.vue'
import OtherInfoPersonnel from './child/OtherInfo.vue'
import GlobalUploadAnimation from '@/views/common/GlobalUploadAnimation.vue'
import { addDataAPI, editDataAPI, getDetailDataAPI } from '@/api/enterpriseLibrary/personnel/personnel'
// 常量定义
const PERSONNEL_POSITION_DEFAULT = {
label: '项目经理',
value: 'project_manager',
qualification: '建造师证书、安全考核B证'
}
const CERTIFICATE_TYPES = {
CONSTRUCTOR: 'constructor_certificate',
TITLE: 'professional_title_certificate'
}
// 空对象常量,避免重复创建
const EMPTY_OBJECT = Object.freeze({})
export default {
name: 'PersonnelForm',
components: {
BasicInfoPersonnel,
QualificationInfoPersonnel,
OtherInfoPersonnel,
GlobalUploadAnimation
},
dicts: ['personnel_position'],
data() {
return {
enterpriseId: decryptWithSM4(this.$route.query.enterpriseId),
personnelId: decryptWithSM4(this.$route.query.personnelId),
type: decryptWithSM4(this.$route.query.type),
personnelPosition: { ...PERSONNEL_POSITION_DEFAULT },
showUploadAnimation: false,
showSaveAnimation: false,
uploadQueue: 0,
animationText: '识别中',
isSaving: false,
detailData: {}
}
},
computed: {
isProjectChiefEngineer() {
return this.personnelPosition.value === 'project_chief_engineer'
},
isEditMode() {
return this.type === 'edit'
},
// 计算属性安全获取ref
qualificationInfoRef() {
return this.$refs.qualificationInfoPersonnel
}
},
watch: {
'dict.type.personnel_position': {
handler(newVal) {
if (newVal && newVal.length > 0) {
this.getDetail();
}
},
}
},
created() {
},
mounted() {
this.$bus.$on('startUpload', this.handleStartUpload)
this.$bus.$on('endUpload', this.handleEndUpload)
},
beforeDestroy() {
this.cleanup()
},
methods: {
// 清理资源
cleanup() {
this.$bus.$off('startUpload', this.handleStartUpload)
this.$bus.$off('endUpload', this.handleEndUpload)
this.resetAnimationState()
},
// 重置动画状态
resetAnimationState() {
this.showUploadAnimation = false
this.showSaveAnimation = false
this.uploadQueue = 0
this.animationText = '识别中'
this.isSaving = false
},
// 返回
handleClose() {
const obj = {
path: "/personnel/index",
query: {
enterpriseId: encryptWithSM4(this.enterpriseId || '0'),
}
}
this.$tab.closeOpenPage(obj)
},
// 获取详情
async getDetail() {
if (!this.isEditMode) return
try {
const res = await getDetailDataAPI({
personnelId: this.personnelId,
enterpriseId: this.enterpriseId
})
console.log('res', res);
this.detailData = res.data
this.getPersonnelPosition();
} catch (error) {
console.error('获取详情失败:', error)
// this.$message.error('获取详情失败')
}
},
// 获取人员职位-回显
getPersonnelPosition() {
const obj = this.dict.type.personnel_position.find(item => item.value === this.detailData.enterprisePersonnel.personnelPosition)
this.personnelPosition = {
label: obj.label,
value: obj.value,
qualification: obj.raw.remark
}
},
// 人员职位
handlePersonnelPosition(data) {
this.personnelPosition = data
},
// 获取资源文件
getResourceFilePo(fileList) {
if (!fileList || !Array.isArray(fileList)) return null
const resourceFiles = fileList
.map(file => file?.response?.fileRes ? { ...file.response.fileRes } : null)
.filter(Boolean)
return resourceFiles.length > 0 ? resourceFiles[0] : null
},
// 获取建造师证书数据
getBuilderCertificateData(raw) {
if (!raw) return null
return {
personnelCertificateId: this.isEditMode && this.qualificationInfoRef ?
this.qualificationInfoRef.personnelCertificateId?.[0] : null,
professionalType: raw.professionalType,
certificateCode: raw.certificateCode,
certificateLevel: raw.certificateLevel,
certificateValidityPeriod: this.formatDateRange(raw.certificateValidityPeriod),
useValidityPeriod: this.formatDateRange(raw.useValidityPeriod),
certificateType: CERTIFICATE_TYPES.CONSTRUCTOR
}
},
// 获取证书类型
getCertificateType(fileList) {
if (!fileList || !Array.isArray(fileList) || fileList.length === 0) return ''
const firstFile = fileList[0]
console.log(firstFile);
return firstFile?.response?.fileRes?.businessType || firstFile?.businessType || ''
},
// 获取 B证、C证、其他证书数据
getOtherCertificateData(raw) {
if (!raw) return null
return {
personnelCertificateId: this.isEditMode && this.qualificationInfoRef ?
this.qualificationInfoRef.personnelCertificateId?.[1] : null,
certificateCode: raw.certificateCode2,
certificateValidityPeriod: this.formatDateRange(raw.certificateValidityPeriod2),
registerProfessional: raw.registerProfessional,
certificateType: this.getCertificateType(raw.fileList2)
}
},
// 获取职称证书数据
getTitleData(raw) {
if (!raw) return null
return {
personnelCertificateId: this.isEditMode ? this.$refs.otherInfoPersonnel.personnelCertificateId?.[0] : null,
titleName: raw.titleName,
professionalName: raw.professionalName,
certificateCode: raw.certificateCode,
certificateType: CERTIFICATE_TYPES.TITLE
}
},
// 格式化日期范围
formatDateRange(dateArray) {
return dateArray && Array.isArray(dateArray) && dateArray.length === 2 ? dateArray.join(' - ') : ''
},
// 安全获取数组
safeGetArray(value) {
return Array.isArray(value) ? value : []
},
// 处理证书文件数据
processCertificateFiles(qualificationData = EMPTY_OBJECT, otherData = EMPTY_OBJECT) {
const personnelCertificateFiles = []
// 建造师证书 - 安全访问qualificationData属性
const qualificationFileList = this.safeGetArray(qualificationData.fileList)
if (qualificationFileList.length > 0) {
const builderCert = {
personnelCertificate: this.getBuilderCertificateData(qualificationData),
resourceFilePo: this.getResourceFilePo(qualificationFileList),
}
// 确保数据有效才添加
if (builderCert.personnelCertificate) {
personnelCertificateFiles.push(builderCert)
}
}
// B证、C证、其他证书 - 安全访问qualificationData属性
const qualificationFileList2 = this.safeGetArray(qualificationData.fileList2)
if (qualificationFileList2.length > 0) {
const otherCert = {
personnelCertificate: this.getOtherCertificateData(qualificationData),
resourceFilePo: this.getResourceFilePo(qualificationFileList2),
}
if (otherCert.personnelCertificate) {
personnelCertificateFiles.push(otherCert)
}
}
// 职称证书 - 安全访问otherData属性
const otherFileList = this.safeGetArray(otherData.fileList)
if (otherFileList.length > 0) {
const titleCert = {
personnelCertificate: this.getTitleData(otherData),
resourceFilePo: this.getResourceFilePo(otherFileList),
}
if (titleCert.personnelCertificate) {
personnelCertificateFiles.push(titleCert)
}
}
return personnelCertificateFiles
},
// 组装表单数据
assembleFormData(basicInfoData, qualificationData = EMPTY_OBJECT, otherData = EMPTY_OBJECT) {
console.log(qualificationData);
console.log(otherData);
// 安全合并所有文件列表
const allFiles = [
...this.safeGetArray(basicInfoData.fileList),
...this.safeGetArray(basicInfoData.fileList2),
...this.safeGetArray(basicInfoData.fileList3),
...this.safeGetArray(basicInfoData.fileList4),
].map(file => JSON.parse(JSON.stringify(file)))
const formData = {
...basicInfoData,
files: allFiles.map(file =>
file?.response?.fileRes ? { ...file.response.fileRes } : null
).filter(Boolean),
delFiles: this.safeGetArray(basicInfoData.delFileList),
delCertificateFiles: [
...this.safeGetArray(qualificationData.delFileList),
...this.safeGetArray(otherData.delFileList)
],
enterpriseId: this.enterpriseId,
personnelCertificateFiles: this.processCertificateFiles(qualificationData, otherData),
personnelIntroduction: otherData.personnelIntroduction || ''
}
// 删除临时属性
const tempProps = ['fileList', 'fileList2', 'fileList3', 'fileList4', 'delFileList', 'allFiles']
tempProps.forEach(prop => delete formData[prop])
return formData
},
// 保存
async handleSave() {
if (this.isSaving) return
this.isSaving = true
this.showSaveAnimation = true
try {
// 并行校验所有表单,安全处理可能不存在的组件
const [basicInfoData, qualificationData, otherData] = await Promise.all([
this.$refs.basicInfoPersonnel.validate(),
this.isProjectChiefEngineer ? Promise.resolve(EMPTY_OBJECT) : this.qualificationInfoRef?.validate?.() || Promise.resolve(EMPTY_OBJECT),
this.$refs.otherInfoPersonnel.validate()
])
// 组装完整数据
const formData = this.assembleFormData(basicInfoData, qualificationData, otherData)
console.log('所有表单校验通过,完整数据:', formData)
// 保存请求
const res = await this.savePersonnel(formData)
if (res.code === 200) {
this.$message.success('保存成功')
this.handleClose()
}
} catch (error) {
if(error instanceof Error && error.message.includes('未填写完整')){
this.$message.error(error.message)
}
} finally {
this.isSaving = false
this.showSaveAnimation = false
}
},
// 保存接口
async savePersonnel(formData) {
const apiParams = this.isEditMode
? { ...formData, personnelId: this.personnelId }
: formData
const apiMethod = this.isEditMode ? editDataAPI : addDataAPI
return await apiMethod(apiParams)
},
// 开始上传
handleStartUpload(text) {
this.animationText = text
this.uploadQueue++
this.showUploadAnimation = true
},
// 结束上传
handleEndUpload() {
if (this.uploadQueue > 0) {
this.uploadQueue--
}
if (this.uploadQueue === 0) {
this.showUploadAnimation = false
}
}
}
}
</script>
<style scoped lang="scss">
.app-container {
padding: 24px;
//background: linear-gradient(180deg, #F1F6FF 20%, #E5EFFF 100%);
min-height: 100vh;
overflow-y: auto;
position: relative;
&.no-pointer-events {
pointer-events: none;
* {
pointer-events: none;
}
}
}
.content-body {
margin-top: 20px;
}
.content-row {
margin: 0;
display: flex;
flex-wrap: nowrap;
gap: 16px;
}
.pane-left,
.pane-center,
.pane-right {
background: #fff;
border-radius: 16px;
min-height: 600px;
box-shadow: 0px 4px 20px 0px rgba(31, 35, 55, 0.1);
padding: 0;
margin-bottom: 20px;
flex: 1;
min-width: 0;
}
.content-header {
display: flex;
justify-content: flex-end;
align-items: center;
margin-bottom: 20px;
gap: 12px;
}
.search-btn {
width: 98px;
height: 36px;
background: #1F72EA;
box-shadow: 0px 4px 8px 0px rgba(51, 135, 255, 0.5);
border-radius: 4px;
border: none;
color: #fff;
font-size: 14px;
transition: all 0.3s ease;
&:hover {
background: #4A8BFF;
box-shadow: 0px 6px 12px 0px rgba(51, 135, 255, 0.6);
}
&.is-loading {
opacity: 0.7;
pointer-events: none;
}
}
.reset-btn {
width: 98px;
height: 36px;
background: #FFFFFF;
box-shadow: 0px 4px 8px 0px rgba(76, 76, 76, 0.2);
border-radius: 4px;
border: none;
color: #666;
font-size: 14px;
transition: all 0.3s ease;
&:hover {
background: #f5f5f5;
color: #409EFF;
box-shadow: 0px 6px 12px 0px rgba(76, 76, 76, 0.3);
}
}
</style>