<!-- 计量人员新增 -->
<script lang="ts" setup name="addNotice">
import type { FormInstance, FormRules, UploadProps, UploadUserFile } from 'element-plus'
import { ElMessage, ElMessageBox, ElTable } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'
import lodash from 'lodash'
import type { technologyJobType } from '../person-interface'
import userListDialog from './userListDialog.vue'
import { getUserList } from '@/api/system/user'
import { getDeptTreeList } from '@/api/system/dept'
import { UploadFile } from '@/api/measure/file'
import { toTreeList } from '@/utils/structure'
import type { TreeStructure, userType } from '@/views/system/user/user-interface'
import { getDictByCode } from '@/api/system/dict'
import { getStaffAdd, getStaffDetail, getStaffupdate } from '@/api/measure/person'
import { getPhotoUrl } from '@/api/system/tool'
// import showPhoto from '@/views/system/tool/showPhoto.vue'
const $route = useRoute()
const $router = useRouter()
const ruleFormRef = ref<FormInstance>() // from组件
const ruleForm: { [key: string]: string | any } = ref({
account: '',
name: '', // 姓名
minioFileName: '', // 照片
idCard: '', // 省份证号
birthday: '', // 出生年月
technologyJob: '', // 技术职务
administrationJob: '', // 行政职务
staffNo: '', // 人员编号
sex: '', // 性别
education: '', // 文化程度
deptId: '', // 工作部门 科室
major: '', // 计量专业
workDate: '', // 从事日期
mainExaminer: '', // 是否主考人
specialOperator: '', // 是否特操人
technologyExam: '', // 技术考核
remark: '', // 备注
certificateList: [], // 证书列表
}) // 表单
const multipleTableRef = ref<InstanceType<typeof ElTable>>()
const title = ref('')
const staffId = ref('')
// 身份证号码验证规则
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)) {
callback()
}
else {
callback(new Error('验证失败'))
}
}
const rules = ref({
name: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],
minioFileName: [{ required: true, message: '头像不能为空', trigger: 'blur' }],
idCard: [{ required: true, message: '身份证号码不符合规范', trigger: 'blur', validator: validateIDcard }],
sex: [{ required: true, message: '性别不能为空', trigger: 'blur' }],
mainExaminer: [{ required: true, message: '请先选择', trigger: 'blur' }],
specialOperator: [{ required: true, message: '请先选择', trigger: 'blur' }],
technologyExam: [{ required: true, message: '请先选择', trigger: 'blur' }],
deptId: [{ required: true, message: '请先选择', trigger: 'blur' }],
major: [{ required: true, message: '请先输入', trigger: 'blur' }],
}) // 表单验证规则
// 根据身份证得到生日
const getBirthday = () => {
ruleFormRef.value?.validateField('idCard').then((res) => {
// 获取生日
const IdCard = ruleForm.value.idCard
const birthday = `${IdCard.substring(6, 10)}-${IdCard.substring(10, 12)}`
ruleForm.value.birthday = birthday
// 获取性别 第17位 奇数为男 偶数为女
// ruleForm.value.sex
if (Number(IdCard.substring(16, 17)) % 2) {
// 男
ruleForm.value.sex = '1'
}
else {
// 女
ruleForm.value.sex = '2'
}
})
}
const dialogVisible = ref<boolean>(false) // 弹窗显示
const userList = ref<userType[]>([]) // 可使用人列表
const getuser = () => {
getUserList({ offset: 1, limit: 99999 }).then((res) => {
userList.value = res.data.rows
})
} // 获取创建人列表
const selectWhether = ref([
{ name: '是', value: '0' },
{ name: '否', value: '1' },
])
const deptProps = reactive({
parent: 'pid', value: 'id', label: 'name', children: 'children',
})
const PubList = ref<TreeStructure[]>([])
const PubListTree = ref([])
// 获取组织列表
const getPubList = () => {
getDeptTreeList().then((res) => {
// 转成树结构
PubList.value = res.data
PubListTree.value = toTreeList(res.data, '0', true)
})
}
const sexList = ref<{ id: string; value: string; name: string }[]>()
// 获取性别
const getSexList = () => {
getDictByCode('sex').then((response) => {
sexList.value = response.data
})
}
const educationList = ref<technologyJobType[]>([])
// 获取文化程度列表
const geteducationList = () => {
getDictByCode('education').then((response) => {
educationList.value = response.data
})
}
// 重置表单内容
const resetFormData = () => {
ruleForm.value = {
name: '', // 姓名
minioFileName: '', // 照片
idCard: '', // 省份证号
birthday: '', // 出生年月
technologyJob: '', // 技术职务
administrationJob: '', // 行政职务
staffNo: '', // 人员编号
sex: '', // 性别
education: '', // 文化程度
deptId: '', // 工作部门 科室
major: '', // 计量专业
workDate: '', // 从事日期
mainExaminer: '', // 是否主考人
specialOperator: '', // 是否特操人
technologyExam: '', // 技术考核
remark: '', // 备注
certificateList: [], // 证书列表
}
}
const technologyJobList = ref<technologyJobType[]>([])
// 获取技术职务列表
const getTechnologyJobList = () => {
getDictByCode('technologyJob').then((response) => {
technologyJobList.value = response.data
})
}
const administrationJobList = ref<technologyJobType[]>([])
// 获取行政职务列表
const getAdministrationJob = () => {
getDictByCode('administrationJob').then((response) => {
administrationJobList.value = response.data
})
}
const userListRef = ref()
// 选择人员
const selectPerson = () => {
userListRef.value.initDialog()
}
// 保存选择人员信息
const confirmPerson = (row: userType) => {
ruleForm.value.name = row.name
ruleForm.value.account = row.account
ruleFormRef.value?.clearValidate('name')
}
// 上传请求
const uploadQuarterlyEvaluateFile: any = (file: any) => {
const fd = new FormData()
fd.append('multipartFile', file.file)
UploadFile(fd).then((res) => {
if (res.code === 200) {
ruleFormRef.value?.clearValidate('minioFileName')
ElMessage.success('上传成功')
ruleForm.value.minioFileName = res.data[0]
}
})
}
// 上传之前的验证
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
}
interface certificateType {
certificateNo: string // 证书号
certificateName: string // 证书名称
certificateCompany: string // 发证单位
certificateDate: string // 发证日期
validDate: string// 有效日期
editable?: boolean // 编辑模式
checked?: number
staffId?: any
}
// 证书对象
const addObj: { [key: string]: object } = {
证书基本信息: {
certificateNo: '', // 证书号
certificateName: '', // 证书名称
certificateCompany: '', // 发证单位
certificateDate: '', // 发证日期
validDate: '', // 有效日期
editable: true, // 编辑模式
checked: -1,
},
计量工作培训记录: {
a1: '',
a2: '',
a3: '',
a4: '',
a5: '',
a6: '',
editable: true, // 编辑模式
checked: -1,
},
业务记录: {
b1: '',
b2: '',
b3: '',
b4: '',
b5: '',
b6: '',
b7: '',
editable: true, // 编辑模式
checked: -1,
},
}
const changeList = ref([
{
name: '证书基本信息',
data: [] as any[],
deepData: [] as any[],
column: [
{
text: '发证单位',
value: 'certificateCompany',
id: 'zs01',
required: true,
isDate: false, // 是否日期
},
{
text: '证书号',
value: 'certificateNo',
id: 'zs02',
required: true,
isDate: false, // 是否日期
},
{
text: '证书名称',
value: 'certificateName',
id: 'zs03',
required: true,
isDate: false, // 是否日期
},
{
text: '发证日期',
value: 'certificateDate',
id: 'zs04',
required: true,
isDate: true, // 是否日期
},
{
text: '有效日期',
value: 'validDate',
id: 'zs05',
required: true,
isDate: true, // 是否日期
},
],
},
{
name: '计量工作培训记录',
data: [] as any[],
deepData: [] as any[],
column: [
{
text: '培训时间',
value: 'a1',
id: 'zs01',
required: true,
isDate: true, // 是否日期
},
{
text: '培训班名称',
value: 'a2',
id: 'zs02',
required: true,
isDate: false, // 是否日期
},
{
text: '类别',
value: 'a3',
id: 'zs03',
required: true,
isSelect: true, // 是否下拉框
},
{
text: '计划编号',
value: 'a4',
id: 'zs04',
required: true,
isDate: false, // 是否日期
},
{
text: '培训对象',
value: 'a5',
id: 'zs05',
required: true,
isDate: false, // 是否日期
},
{
text: '培训结果',
value: 'a6',
id: 'zs05',
required: true,
isDate: false, // 是否日期
},
],
},
{
name: '业务记录',
data: [] as any[],
deepData: [] as any[],
column: [
{
text: '样品编号',
value: 'b1',
id: 'zs01',
required: true,
isDate: false, // 是否日期
},
{
text: '样品名称',
value: 'b2',
id: 'zs02',
required: true,
isDate: false, // 是否日期
},
{
text: '委托书编号',
value: 'b3',
id: 'zs03',
required: true,
isDate: false, // 是否日期
},
{
text: '合同编号',
value: 'b4',
id: 'zs04',
required: true,
isDate: false, // 是否日期
},
{
text: '检定时间',
value: 'b5',
id: 'zs05',
required: true,
isDate: true, // 是否日期
},
{
text: '检定结果',
value: 'b6',
id: 'zs05',
required: true,
isDate: false, // 是否日期
},
{
text: '业务额',
value: 'b7',
id: 'zs05',
required: true,
isDate: false, // 是否日期
},
],
},
])
const current = ref('证书基本信息')
const currentShow = computed(() => {
return changeList.value.filter(item => item.name === current.value)[0]
})
// 检查证书列表
function checkPersonList() {
for (let index = 0; index < currentShow.value.data.length; index++) {
const item = currentShow.value.data[index]
for (const prop of currentShow.value.column) {
// 检查必填
if (prop.required && !item[prop.value]) {
ElMessage.warning(`请先完善第${index + 1}行中${prop.text}`)
return false
}
}
}
return true
}
// 将证书列表置为不可编辑状态
function setAllRowReadable() {
for (const item of currentShow.value.data) {
item.editable = false
}
}
// 双击行显示输入框
const dblclickRow = (row: certificateType) => {
setAllRowReadable()
row.editable = true
}
const SelectionList = ref()
// 表格选中
const handleSelectionChange = (e: certificateType[]) => {
SelectionList.value = e
}
// 添加行
const addRow = () => {
// certificateRef.value.initDialog({ title: '添加' })
if (checkPersonList()) {
setAllRowReadable()
currentShow.value.data.push({
...addObj[current.value],
staffId: staffId.value ? staffId.value : undefined,
})
}
}
// 删除行
const removeRow = () => {
currentShow.value.data = currentShow.value.data.filter((item) => {
return !SelectionList.value.includes(item)
})
}
// 处理编辑记录的函数
const handleRecord = (initData: any[], finalData: any[]) => {
const certificateList = [] as any[]
// 新增的
finalData.forEach((item) => {
if (!item.id) {
certificateList.push(item)
}
})
// 删除的
const arr = finalData.filter(item => item.id).map(data => (data.id))
const statrArr = initData.map(data => (data.id))
const deleteArr = statrArr.filter((item) => {
return !arr.includes(item)
})
deleteArr.forEach((string) => {
certificateList.push({
id: string,
isDel: 1,
})
})
// 修改的
const editArr = statrArr.filter((item) => {
return arr.includes(item)
})
const editArrObject = finalData.filter((item) => {
return editArr.includes(item.id)
})
editArrObject.forEach((item) => {
certificateList.push({
...item,
createTime: null,
updateTime: null,
})
})
console.log(certificateList, '最终需要提交的数组')
return certificateList
}
// 提交
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) { return }
await formEl.validate((valid, fields) => {
if (valid) {
ElMessageBox.confirm(
'确认提交吗?',
'提示',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
},
).then(() => {
changeList.value[0].data = changeList.value[0].data.filter(item => item.certificateName !== '')
if (changeList.value[0].data.length) {
for (const key in ruleForm.value) {
if (ruleForm.value[key] === '') {
ruleForm.value[key] = null
}
}
(title.value === '新建'
? getStaffAdd({ ...ruleForm.value, certificateList: changeList.value[0].data, birthday: `${ruleForm.value.idCard.substring(6, 10)}-${ruleForm.value.idCard.substring(10, 12)}-${ruleForm.value.idCard.substring(12, 14)}` })
: getStaffupdate({ ...ruleForm.value, certificateList: handleRecord(changeList.value[0].deepData, changeList.value[0].data), birthday: `${ruleForm.value.idCard.substring(6, 10)}-${ruleForm.value.idCard.substring(10, 12)}-${ruleForm.value.idCard.substring(12, 14)}` })).then((res) => {
if (res.code == 200) {
formEl?.resetFields()
ElMessage.success('提交成功')
resetFormData()
$router.go(-1)
}
})
}
else {
ElMessage.warning('请先填写证书信息')
}
})
}
})
}
// 取消
const resetForm = (formEl: FormInstance | undefined) => {
formEl?.resetFields()
changeList.value[0].data = []
$router.go(-1)
resetFormData()
}
// 弹窗初始化
const initDialog = (row: any) => {
changeList.value[0].data = []
dialogVisible.value = true
title.value = row.title
resetFormData()
getuser()
getPubList()
getSexList()
geteducationList()
getTechnologyJobList()
getAdministrationJob()
if (row.title === '新建') {
// ruleForm.value.deptId = ''
}
else {
getStaffDetail({ ...row }).then((res) => {
if (res.code === 200) {
ruleForm.value = res.data
changeList.value[0].data = res.data.certificateList
changeList.value[0].deepData = lodash.cloneDeep(res.data.certificateList)
staffId.value = changeList.value[0].deepData[0]?.staffId
}
})
}
}
defineExpose({ initDialog })
// 图片预览地址
const photoUrl = ref('')
watch(() => ruleForm.value.minioFileName, (newVal) => {
getPhotoUrl(newVal).then((res) => {
if (res.code === 200) {
photoUrl.value = res.data
}
})
})
onMounted(() => {
initDialog($route.query)
})
</script>
<template>
<app-container style="overflow: hidden;">
<detail-page :title="`计量人员-${title}`">
<template #btns>
<el-button v-if="title !== '详情'" type="primary" @click="submitForm(ruleFormRef)">
完成
</el-button>
<el-button type="info" @click="resetForm(ruleFormRef)">
关闭
</el-button>
</template>
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-position="right" label-width="110px" :class="[title === '详情' ? 'isDetail' : '']" border stripe>
<el-row :gutter="24">
<el-col :span="6">
<el-form-item prop="minioFileName">
<el-upload
class="avatar-uploader"
:show-file-list="false"
:http-request="uploadQuarterlyEvaluateFile"
:before-upload="beforeAvatarUpload"
:disabled="title === '详情'"
accept="image/png, image/jpeg,image/jpg"
>
<!-- <show-photo v-if="ruleForm.minioFileName" :minio-file-name="ruleForm.minioFileName" width="178px" height="178px" /> -->
<img v-if="ruleForm.minioFileName" :src="photoUrl" class="avatar">
<el-icon v-else class="avatar-uploader-icon">
<plus />
</el-icon>
</el-upload>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="姓名" prop="name">
<el-input v-model.trim="ruleForm.name" placeholder="名称" :disabled="title === '详情'" @focus="selectPerson">
<template #append>
<el-button size="small" :disabled="title === '详情'" @click="selectPerson">
选择
</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item label="出生年月" prop="birthday" class="marg-item">
<el-date-picker
v-model="ruleForm.birthday" type="date" format="YYYY-MM" value-format="YYYY-MM"
placeholder="出生年月" :disabled="title === '详情'"
style="width: 100%;"
/>
</el-form-item>
<el-form-item label="工作部门" prop="deptId" class="marg-item">
<!-- <el-select v-if="title === '详情'" v-model="ruleForm.deptId" disabled style="width: 100%;">
<el-option v-for="item in PubList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
<com-tree-select
v-else
ref="selectTreeRef" v-model="ruleForm.deptId" :options="PubListTree"
placeholder="选择组织机构" :tree-props="deptProps" style="width: 100%;"
/> -->
<dept-select v-model="ruleForm.deptId" :data="PubListTree" :placeholder="title === '详情' ? ' ' : '选择组织机构'" :disabled="title === '详情'" style="width: 100%;" />
</el-form-item>
<el-form-item label="计量专业" prop="major" class="marg-item">
<el-input v-model.trim="ruleForm.major" placeholder="计量专业" :disabled="title === '详情'" />
</el-form-item>
<el-form-item label="是否特操人" prop="specialOperator" class="marg-item">
<el-select v-model="ruleForm.specialOperator" placeholder="是否特操人" :disabled="title === '详情'" style="width: 100%;">
<el-option v-for="item in selectWhether" :key="item.value" :label="item.name" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="人员编号" prop="staffNo">
<el-input v-model.trim="ruleForm.staffNo" placeholder="系统自动生成" disabled />
</el-form-item>
<el-form-item label="性别" prop="sex" class="marg-item">
<el-select v-model="ruleForm.sex" placeholder="请选择性别" :disabled="title === '详情'" style="width: 100%;">
<el-option v-for="item in sexList" :key="item.id" :label="item.name" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="技术职务" prop="technologyJob" class="marg-item">
<el-select v-model="ruleForm.technologyJob" placeholder="技术职务" :disabled="title === '详情'" style="width: 100%;">
<el-option v-for="item in technologyJobList" :key="item.id" :label="item.name" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="从事日期" prop="workDate" class="marg-item">
<el-date-picker
v-model="ruleForm.workDate" type="date" format="YYYY-MM-DD" value-format="YYYY-MM-DD HH:mm:ss" :disabled="title === '详情'"
placeholder="请选择从事日期" style="width: 100%;"
/>
</el-form-item>
<el-form-item label="是否技术考核" prop="technologyExam" class="marg-item" style="width: 100%;">
<el-select v-model="ruleForm.technologyExam" placeholder="技术考核" :disabled="title === '详情'" style="width: 100%;">
<el-option v-for="item in selectWhether" :key="item.value" :label="item.name" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="身份证号" prop="idCard">
<el-input ref="cardRef" v-model.trim="ruleForm.idCard" placeholder="身份证号" :disabled="title === '详情'" @blur="getBirthday" />
</el-form-item>
<el-form-item label="文化程度" prop="education" class="marg-item">
<el-select v-model="ruleForm.education" placeholder="文化程度" :disabled="title === '详情'" style="width: 100%;">
<el-option v-for="item in educationList" :key="item.id" :label="item.name" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="行政职务" prop="administrationJob" class="marg-item">
<el-select v-model="ruleForm.administrationJob" placeholder="行政职务" :disabled="title === '详情'" style="width: 100%;">
<el-option v-for="item in administrationJobList" :key="item.id" :label="item.name" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="是否主考人" prop="mainExaminer" class="marg-item">
<el-select v-model="ruleForm.mainExaminer" placeholder="是否主考人" :disabled="title === '详情'" style="width: 100%;">
<el-option v-for="item in selectWhether" :key="item.value" :label="item.name" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="备注" prop="remark" class="marg-item">
<el-input v-model.trim="ruleForm.remark" placeholder="请输入备注" :disabled="title === '详情'" />
</el-form-item>
</el-col>
</el-row>
</el-form>
</detail-page>
<detail-block-switch :title="current">
<template #menu>
<el-radio-group v-model="current">
<el-radio-button v-for="item in changeList" :key="item.name" :label="item.name">
{{ item.name }}
</el-radio-button>
</el-radio-group>
</template>
<template #btns>
<el-button v-if="title !== '详情'" type="info" @click="removeRow">
删除行
</el-button>
<el-button v-if="title !== '详情'" type="primary" @click="addRow">
添加行
</el-button>
<!-- <el-button v-if="title !== '详情'" type="primary" @click="editRow">
编辑行
</el-button> -->
</template>
<el-table ref="multipleTableRef" :data="currentShow.data" style="width: 100%;" border @selection-change="handleSelectionChange" @row-dblclick="dblclickRow">
<el-table-column type="selection" width="38" />
<el-table-column align="center" label="序号" width="80" type="index" />
<el-table-column v-for="item in currentShow.column" :key="item.id" :prop="item.value" :label="item.text" align="center">
<template #header>
<span v-show="item.required" style="color: red;">*</span><span>{{ item.text }}</span>
</template>
<template #default="scope">
<span v-if="!scope.row.editable">{{ scope.row[item.value] }}</span>
<el-input v-if="scope.row.editable && !item.isDate" v-model="scope.row[item.value]" :autofocus="true" :placeholder="`${item.text}`" class="input" />
<el-date-picker
v-if="scope.row.editable && item.isDate"
v-model="scope.row[item.value]" type="date" format="YYYY-MM-DD" value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="`${item.text}`"
/>
</template>
</el-table-column>
</el-table>
<!-- 选择用户列表组件 -->
<user-list-dialog ref="userListRef" @add="confirmPerson" />
</detail-block-switch>
</app-container>
</template>
<style lang="scss" scoped>
:deep(.el-radio__label) {
display: none;
}
:deep(.el-icon.avatar-uploader-icon) {
font-size: 28px;
color: #8c939d;
width: 190px;
height: 250px;
text-align: center;
}
:deep(.avatar-uploader .avatar) {
width: 190px;
height: 250px;
display: block;
}
.isDetail {
::v-deep {
.el-form-item.is-required:not(.is-no-asterisk) .el-form-item__label-wrap > .el-form-item__label::before,
.el-form-item.is-required:not(.is-no-asterisk) > .el-form-item__label::before {
display: none;
}
}
}
</style>
<style scoped>
.avatar-uploader .avatar {
width: 190px;
height: 250px;
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: 190px;
height: 250px;
text-align: center;
}
</style>