<!-- 人员登记 基本信息 --> <script name="RegisterBasic" lang="ts" setup> import type { UploadProps } from 'element-plus' import { ElLoading, ElMessage, ElMessageBox, dayjs } from 'element-plus' import type { IStaffBasicInfo } from '../person-regitster' import FilterSysUser from '@/views/resource/common/filterSysUser.vue' import type { IDictType } from '@/commonInterface/resource-interface' import nationList from '@/assets/json/nation-code.json' import FilePreviewDialog from '@/components/filePreview/filePreviewDialog.vue' import ImagePreviewDialog from '@/components/ImagePreview/imagePreviewDialog.vue' import { UploadFile } from '@/api/file' import { getPhotoUrl } from '@/api/system/tool' import { addStaffBasic, getStaffBasic, updateStaffBasic } from '@/api/resource/register' import { getDictByCode } from '@/api/system/dict' const props = defineProps({ operation: { type: String, default: '' }, }) const emit = defineEmits(['afterAddBasic', 'returnToList']) const staffBasicInfo = ref<IStaffBasicInfo>({ id: '', account: '', // 账号 pictureFile: '', // 照片文件 staffNo: '', // 人员编号 staffName: '', // 姓名 idCard: '', // 身份证号 birthday: '', // 生日 nativePlace: '', // 籍贯 education: '', // 学历 educationName: '', educationFile: '', // 教育科研文件 recordDate: '', // 建档日期 officerNo: '', // 军官/文职证号 gender: '', // 性别 genderName: '', graduationDate: '', // 毕业时间 nation: '', // 民族 degree: '', // 学位 degreeName: '', deptId: '', // 工作部门 deptName: '', station: '', // 岗位 major: '', // 从事专业 certificateNumber: '', // 证号 engageDate: '', // 从事专业时间 partyDate: '', // 党团时间 enlistmentDate: '', // 入伍时间 staffType: '', // 人员类别 staffTypeName: '', workingExperience: '', // 工作简历 rankExperience: '', // 军衔/时间 titleExperience: '', // 职称职务/时间 treatmentExperience: '', // 待遇级别/时间 rewardsPunishments: '', // 奖惩情况 remark: '', // 备注 }) const basicFormRef = ref() const refFilterDialog = ref() const refFilePreviewDlg = ref() const refImagePreviewDlg = ref() const educationDict = ref<IDictType[]>([]) const genderDict = ref<IDictType[]>([]) const degreeDict = ref<IDictType[]>([]) const staffTypeDict = ref<IDictType[]>([]) // 身份证号码验证规则 const validateIDcard = (rule: any, value: any, callback: any) => { const rr = /^(^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$)|(^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])((\d{4})|\d{3}[X])$)$/ if (rr.test(value)) { // 校验成功后自动填充出生日期和性别字段 // 获取生日 const idCardStr = staffBasicInfo.value.idCard staffBasicInfo.value.birthday = `${idCardStr.substring(6, 10)}-${idCardStr.substring(10, 12)}-${idCardStr.substring(12, 14)}` // 获取性别 第17位 奇数为男 偶数为女 if (Number(idCardStr.substring(16, 17)) % 2) { const tarList = genderDict.value.filter(item => item.name === '男') if (tarList.length > 0) { staffBasicInfo.value.gender = tarList[0].value staffBasicInfo.value.genderName = tarList[0].name } } else { const tarList = genderDict.value.filter(item => item.name === '女') if (tarList.length > 0) { staffBasicInfo.value.gender = tarList[0].value staffBasicInfo.value.genderName = tarList[0].name } } callback() } else { // 校验失败将出生日期和性别字段清除 staffBasicInfo.value.birthday = '' staffBasicInfo.value.gender = '' callback(new Error('验证失败')) } } const staffBasicRules = ref({ staffName: [{ required: true, message: '姓名不能为空', trigger: ['change', 'blur'] }], gender: [{ required: true, message: '性别不能为空', trigger: ['change', 'blur'] }], staffType: [{ required: true, message: '人员类别不能为空', trigger: ['change', 'blur'] }], deptName: [{ required: true, message: '工作部门不能为空', trigger: ['change', 'blur'] }], birthday: [{ required: true, message: '出生时间不能为空', trigger: ['change', 'blur'] }], nativePlace: [{ required: true, message: '籍贯不能为空', trigger: 'blur' }], nation: [{ required: true, message: '民族不能为空', trigger: ['change', 'blur'] }], graduationDate: [{ required: true, message: '毕业时间不能为空,请选择', trigger: 'blur' }], enlistmentDate: [{ required: true, message: '入伍时间不能为空,请选择', trigger: 'blur' }], partyDate: [{ required: true, message: '党团时间不能为空,请选择', trigger: 'blur' }], education: [{ required: true, message: '学历不能为空,请选择', trigger: ['change', 'blur'] }], degree: [{ required: true, message: '学位不能为空,请选择', trigger: ['change', 'blur'] }], idCard: [{ required: true, message: '身份证号码为空或不符合规范', trigger: 'blur', validator: validateIDcard }], officerNo: [{ required: true, message: '军官/文职证号不能为空', trigger: 'blur' }], recordDate: [{ required: true, message: '建档时间不能为空,请选择', trigger: 'blur' }], station: [{ required: true, message: '岗位不能为空', trigger: 'blur' }], major: [{ required: true, message: '从事专业不能为空', trigger: 'blur' }], engageDate: [{ required: true, message: '从事专业时间不能为空,请选择', trigger: 'blur' }], }) // 表单验证规则 // 逻辑 // 上传请求 const uploadAvatarFile: any = (file: any) => { const fd = new FormData() fd.append('multipartFile', file.file) const loading = ElLoading.service({ lock: true, background: 'rgba(255, 255, 255, 0.8)', }) UploadFile(fd).then((res) => { if (res.code === 200) { ElMessage.success('上传成功') staffBasicInfo.value.pictureFile = res.data[0] loading.close() } }).catch(() => { loading.close() ElMessage.error('上传失败') }) } // 上传之前的验证 const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => { if (rawFile.type !== 'image/jpeg' && rawFile.type !== 'image/png' && rawFile.type !== 'image/jpg') { ElMessage.error('只能上传png/jpeg/jpg格式的图片') return false } else if (rawFile.size / 1024 / 1024 > 2) { ElMessage.error('图片不能大于2MB') return false } return true } // 上传教育档案文件附件请求 const uploadEducationFile: any = (file: any) => { const fd = new FormData() fd.append('multipartFile', file.file) const loading = ElLoading.service({ lock: true, background: 'rgba(255, 255, 255, 0.8)', }) UploadFile(fd).then((res) => { if (res.code === 200) { ElMessage.success('教育档案附件上传成功') staffBasicInfo.value.educationFile = res.data[0] loading.close() } }).catch(() => { loading.close() ElMessage.error('教育档案附件上传失败') }) } // 从用户账号中筛选进行人员登记 const showFilterSysUser = () => { refFilterDialog.value.showOrHideFilterDialog(true) } const recordSelectedHandler = (row: any) => { refFilterDialog.value.showOrHideFilterDialog(false) staffBasicInfo.value.staffName = row.name staffBasicInfo.value.account = row.account staffBasicInfo.value.deptId = row.deptId staffBasicInfo.value.deptName = row.deptName staffBasicInfo.value.major = row.deptName // 默认与工作部门一致,可编辑 staffBasicInfo.value.staffType = row.avatar // 使用avata字段代替人员类型 } const openFilePreviewDialog = (filename: string) => { if (filename.lastIndexOf('.pdf') > 0) { refFilePreviewDlg.value.initDialog(filename) } else { refImagePreviewDlg.value.initDialog(filename) } } const reloadStaffBasic = (staffId: string) => { getStaffBasic({ id: staffId, }).then((res) => { if (res.code === 200) { staffBasicInfo.value = res.data } }) } // 查询人员基本信息 const getStaffBasicById = (staffId: string) => { if (sessionStorage.getItem('staffBasicInfo') === null || sessionStorage.getItem('staffBasicInfo') === undefined) { getStaffBasic({ id: staffId, }).then((res) => { if (res.code === 200) { staffBasicInfo.value = res.data } }) } else { staffBasicInfo.value = JSON.parse(sessionStorage.getItem('staffBasicInfo')!) } } // 新增 const addStaffBase = () => { addStaffBasic(staffBasicInfo.value).then((res) => { if (res.code === 200) { // 提示保存成功 ElMessage.success('保存成功') // 设置返回的人员id emit('afterAddBasic', res.data.id) // 提示是否继续添加其他信息 ElMessageBox.confirm( '是否继续添加其他信息', '提示', { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning', }, ).then(() => { // 确认操作 停留在当前页 继续添加教育等其他信息 }).catch(() => { // 取消操作 返回到列表页 emit('returnToList') }) } else { // 提示失败信息 ElMessage.error(`保存失败:${res.message}`) } }) } // 编辑 const updateStaffBase = () => { updateStaffBasic(staffBasicInfo.value).then((res) => { if (res.code === 200) { // 提示保存成功 ElMessage.success('保存成功') } else { // 提示失败信息 ElMessage.error(`保存失败:${res.message}`) } }) } // 保存基本信息 const saveBasicForm = async () => { if (!basicFormRef) { return } await basicFormRef.value.validate((valid: boolean, fields: any) => { if (valid === true) { ElMessageBox.confirm( '确认保存吗?', '提示', { confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning', }, ).then(() => { if (props.operation === 'create') { addStaffBase() } else if (props.operation === 'update') { updateStaffBase() } }) } }) } const searchDate = reactive({ disableTodayAfter: (date: string) => { const today = dayjs() const target = dayjs(date) return target.diff(today, 'day') >= 0 }, }) const autoSetEngageDate = () => { if (staffBasicInfo.value.recordDate !== '' && staffBasicInfo.value.engageDate === '') { staffBasicInfo.value.engageDate = staffBasicInfo.value.recordDate } } // 查询学历字典值 const getEducationDict = async () => { await getDictByCode('bizEducation').then((res) => { if (res.code === 200) { educationDict.value = res.data sessionStorage.setItem('educationDict', JSON.stringify(educationDict.value)) } }) } // 查询性别字典值 const getGenderDict = async () => { await getDictByCode('sex').then((res) => { if (res.code === 200) { genderDict.value = res.data } }) } // 查询学位字典值 const getDegreeDict = async () => { await getDictByCode('bizDegree').then((res) => { if (res.code === 200) { degreeDict.value = res.data } }) } // 查询人员类别字典值 const getStaffTypeDict = async () => { await getDictByCode('bizStaffType').then((res) => { if (res.code === 200) { staffTypeDict.value = res.data } }) } // 查询训练场地字典值 const getTrainSiteDict = async () => { await getDictByCode('bizTrainSite').then((res) => { if (res.code === 200) { sessionStorage.setItem('bizTrainSite', JSON.stringify(res.data)) } }) } // 查询训练方式字典值 const getTrainWayDict = async () => { await getDictByCode('bizTrainWay').then((res) => { if (res.code === 200) { sessionStorage.setItem('bizTrainWay', JSON.stringify(res.data)) } }) } const getCertTypeDict = async () => { await getDictByCode('bizCertType').then((res) => { if (res.code === 200) { sessionStorage.setItem('bizCertType', JSON.stringify(res.data)) } }) } // 图片预览地址 const photoUrl = ref('') watch(() => staffBasicInfo.value.pictureFile, (newVal) => { if (newVal !== undefined) { getPhotoUrl(newVal).then((res) => { if (res.code === 200) { photoUrl.value = res.data } }) } }) // 教育档案预览地址 const educationFileUrl = ref('') watch(() => staffBasicInfo.value.educationFile, (newVal) => { if (newVal !== undefined) { getPhotoUrl(newVal).then((res) => { if (res.code === 200) { educationFileUrl.value = res.data } }) } }) // 监听性别 显示中文 立即监听 watch(() => staffBasicInfo.value.gender, (newVal) => { getGenderDict() getCertTypeDict() getEducationDict() getTrainSiteDict() getTrainWayDict() getDegreeDict() getStaffTypeDict() }, { immediate: true }) defineExpose({ saveBasicForm, getStaffBasicById, reloadStaffBasic, }) </script> <template> <app-container> <el-form ref="basicFormRef" :model="staffBasicInfo" :rules="staffBasicRules" label-position="right" label-width="120px" border stripe> <!-- 第一行 --> <el-row :gutter="24"> <!-- 第一行 第一列 --> <el-col :span="8"> <el-form-item label="姓名" prop="staffName"> <el-input v-model="staffBasicInfo.staffName" placeholder="请输入姓名,必填项" :disabled="true"> <template #append> <el-button size="small" :disabled="props.operation !== 'create'" @click="showFilterSysUser"> 选择 </el-button> </template> </el-input> <el-input v-model="staffBasicInfo.account" type="hidden" /> </el-form-item> <el-form-item label="性别" prop="gender"> <el-input v-model="staffBasicInfo.gender" type="hidden" /> <el-input v-model="staffBasicInfo.genderName" placeholder="自动从身份证号获取" :disabled="true" /> </el-form-item> <el-form-item label="籍贯" prop="nativePlace"> <el-input v-model="staffBasicInfo.nativePlace" :disabled="props.operation === 'detail'" placeholder="请输入籍贯" /> </el-form-item> <el-form-item label="人员类别" prop="staffType"> <el-select v-model="staffBasicInfo.staffType" placeholder="请选择人员类别" disabled style="width: 100%;"> <el-option v-for="item in staffTypeDict" :key="item.id" :label="item.name" :value="item.value" /> </el-select> </el-form-item> <el-form-item label="入伍时间" prop="enlistmentDate"> <el-date-picker v-model="staffBasicInfo.enlistmentDate" type="month" format="YYYY-MM" value-format="YYYY-MM-DD" :disabled-date="searchDate.disableTodayAfter" placeholder="请选择入伍时间" :disabled="props.operation === 'detail'" style="width: 100%;" /> </el-form-item> <el-form-item label="学历" prop="education"> <el-select v-model="staffBasicInfo.education" placeholder="请选择学历" :disabled="props.operation === 'detail'" style="width: 100%;"> <el-option v-for="item in educationDict" :key="item.id" :label="item.name" :value="item.value" /> </el-select> </el-form-item> <el-form-item label="毕业时间" prop="graduationDate"> <el-date-picker v-model="staffBasicInfo.graduationDate" type="month" format="YYYY-MM" value-format="YYYY-MM-DD" :disabled-date="searchDate.disableTodayAfter" placeholder="请选择毕业时间" :disabled="props.operation === 'detail'" style="width: 100%;" /> </el-form-item> <el-form-item label="工作部门" prop="deptName"> <el-input v-model="staffBasicInfo.deptName" placeholder="自动从人员信息获取" :disabled="true" style="width: 100%;" /> <el-input v-model="staffBasicInfo.deptId" type="hidden" /> </el-form-item> <el-form-item label="从事专业" prop="major"> <el-input v-model="staffBasicInfo.major" :disabled="props.operation === 'detail'" placeholder="请输入从事计量专业" /> </el-form-item> </el-col> <!-- 第一行 第二列 --> <el-col :span="8"> <el-form-item label="身份证号" prop="idCard"> <el-input ref="idCard" v-model="staffBasicInfo.idCard" placeholder="请输入身份证号,必填项" :disabled="props.operation === 'detail'" :clearable="true" /> </el-form-item> <el-form-item label="出生时间" prop="birthday"> <el-input v-model="staffBasicInfo.birthday" placeholder="自动从身份证号获取" :disabled="true" /> </el-form-item> <el-form-item label="民族" prop="nation"> <el-select v-model="staffBasicInfo.nation" placeholder="请选择民族" style="width: 100%;" :disabled="props.operation === 'detail'" :filterable="true" :clearable="true"> <el-option v-for="item in nationList" :key="item.id" :label="item.text" :value="item.text" /> </el-select> </el-form-item> <el-form-item label="军官/文职证号" prop="officerNo"> <el-input v-model="staffBasicInfo.officerNo" placeholder="请输入证件号(军官证/文职证)" :disabled="props.operation === 'detail'" /> </el-form-item> <el-form-item label="党团时间" prop="partyDate"> <el-date-picker v-model="staffBasicInfo.partyDate" type="month" format="YYYY-MM" value-format="YYYY-MM-DD" :disabled-date="searchDate.disableTodayAfter" placeholder="请选择党团时间" :disabled="props.operation === 'detail'" style="width: 100%;" /> </el-form-item> <el-form-item label="学位" prop="degree"> <el-select v-model="staffBasicInfo.degree" placeholder="请选择学位" :disabled="props.operation === 'detail'" style="width: 100%;" :clearable="true"> <el-option v-for="item in degreeDict" :key="item.id" :label="item.name" :value="item.value" /> </el-select> </el-form-item> <el-form-item label="建档时间" prop="recordDate"> <el-date-picker v-model="staffBasicInfo.recordDate" type="date" format="YYYY-MM-DD" value-format="YYYY-MM-DD" :disabled-date="searchDate.disableTodayAfter" placeholder="请选择建档时间" :disabled="props.operation === 'detail'" style="width: 100%;" @change="autoSetEngageDate" /> </el-form-item> <el-form-item label="岗位" prop="station"> <el-input v-model="staffBasicInfo.station" :disabled="props.operation === 'detail'" placeholder="请输入岗位" /> </el-form-item> <el-form-item label="从事专业时间" prop="engageDate"> <el-date-picker v-model="staffBasicInfo.engageDate" type="date" format="YYYY-MM-DD" value-format="YYYY-MM-DD" :disabled-date="searchDate.disableTodayAfter" placeholder="请选择从事专业时间" :disabled="props.operation === 'detail'" style="width: 100%;" /> </el-form-item> </el-col> <!-- 第一行 第三列 --> <el-col :span="8"> <el-form-item> <el-upload class="avatar-uploader" :show-file-list="false" :http-request="uploadAvatarFile" :before-upload="beforeAvatarUpload" accept="image/png, image/jpeg,image/jpg" :disabled="props.operation === 'detail'" > <img v-if="staffBasicInfo.pictureFile" :src="photoUrl" class="avatar"> <el-icon v-else class="avatar-uploader-icon"> <plus /> </el-icon> </el-upload> </el-form-item> </el-col> </el-row> <!-- 第二行 --> <el-row :gutter="24"> <el-col :span="8"> <el-form-item label="军衔/时间"> <el-input v-model="staffBasicInfo.rankExperience" placeholder="请输入军衔及获得时间" type="textarea" :rows="3" :disabled="props.operation === 'detail'" /> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="待遇级别/时间"> <el-input v-model="staffBasicInfo.treatmentExperience" placeholder="请输入待遇级别及获得时间" type="textarea" :rows="3" :disabled="props.operation === 'detail'" /> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="职称职务/时间"> <el-input v-model="staffBasicInfo.titleExperience" placeholder="请输入职称职务及获得时间" type="textarea" :rows="3" :disabled="props.operation === 'detail'" /> </el-form-item> </el-col> </el-row> <!-- 教育档案附件 --> <!-- <el-row :gutter="24"> <el-col :span="20"> <el-form-item label="教育档案附件" prop="file"> <el-input v-model="staffBasicInfo.educationFile" type="hidden" /> <el-link @click="openFilePreviewDialog(staffBasicInfo.educationFile!)"> {{ staffBasicInfo.educationFile }} </el-link> <el-upload :show-file-list="false" :http-request="uploadEducationFile" style="width: 50%; margin-left: 20px;" > <el-button v-if="props.operation !== 'detail'" type="primary"> <template v-if="staffBasicInfo.educationFile !== ''"> 替换 </template> <template v-else> 上传 </template> </el-button> </el-upload> </el-form-item> </el-col> </el-row> --> <!-- 第三行 --> <el-row :gutter="24"> <el-col :span="24"> <el-form-item label="工作简历"> <el-input v-model="staffBasicInfo.workingExperience" placeholder="请输入工作简历" type="textarea" :rows="3" :disabled="props.operation === 'detail'" /> </el-form-item> </el-col> </el-row> <!-- 第四行 --> <el-row :gutter="24"> <el-col :span="24"> <el-form-item label="奖惩情况"> <el-input v-model="staffBasicInfo.rewardsPunishments" placeholder="请输入奖惩情况" type="textarea" :rows="3" :disabled="props.operation === 'detail'" /> </el-form-item> </el-col> </el-row> <!-- 第五行 --> <el-row :gutter="24"> <el-col :span="24"> <el-form-item label="备注"> <el-input v-model="staffBasicInfo.remark" placeholder="请输入备注信息" type="textarea" :rows="1" :disabled="props.operation === 'detail'" /> </el-form-item> </el-col> </el-row> </el-form> <filter-sys-user ref="refFilterDialog" @record-selected="recordSelectedHandler" /> <file-preview-dialog ref="refFilePreviewDlg" /> <image-preview-dialog ref="refImagePreviewDlg" /> </app-container> </template> <style scoped> .avatar-uploader .avatar { width: 300px; height: 400px; display: block; } </style> <style> .avatar-uploader .el-upload { border: 1px dashed var(--el-border-color); border-radius: 6px; cursor: pointer; position: relative; overflow: hidden; transition: var(--el-transition-duration-fast); } .avatar-uploader .el-upload:hover { border-color: var(--el-color-primary); } .el-icon.avatar-uploader-icon { font-size: 28px; color: #8c939d; width: 300px; height: 400px; text-align: center; } </style>