yubo
2026-04-10 ad77e25e868008e33a701084fd0e8c00a06cc6a0
src/views/user/inemployees.vue
@@ -267,7 +267,6 @@
            show-overflow-tooltip
            prop="allDeptName"
            label="部门(护卫点)"
            width="300"
            sortable="custom"
            :sort-orders="['ascending', 'descending']"
          />
@@ -353,11 +352,11 @@
              v-if="empBaseInfoImageUrl"
              :src="empBaseInfoImageUrl"
              class="avatar"
              @click="openCamera"
              @click="openUploadChoice"
            >
            <div v-else class="avatar-uploader-placeholder" @click="openCamera">
            <div v-else class="avatar-uploader-placeholder" @click="openUploadChoice">
              <i class="el-icon-plus avatar-uploader-icon" />
              <div class="upload-tip">点击拍照上传</div>
              <div class="upload-tip">点击上传照片</div>
            </div>
          </div>
        </el-aside>
@@ -365,7 +364,7 @@
          <el-form
            ref="empBaseInfoForm"
            :model="empBaseInfoForm"
            :rules="rules"
            :rules="isAdd ? addRules : rules"
            label-position="right"
            label-width="120px"
          >
@@ -1003,6 +1002,25 @@
      </div>
    </el-dialog>
    <!-- 上传方式选择弹窗 -->
    <el-dialog
      title="选择上传方式"
      :visible.sync="uploadChoiceDialogVisible"
      width="400px"
      :close-on-click-modal="false"
    >
      <div class="upload-choice-container">
        <div class="upload-choice-item" @click="choiceCamera">
          <i class="el-icon-camera" />
          <span>拍照上传</span>
        </div>
        <div class="upload-choice-item" @click="choiceFile">
          <i class="el-icon-folder-opened" />
          <span>文件上传</span>
        </div>
      </div>
    </el-dialog>
    <!-- 摄像头拍照弹窗 -->
    <el-dialog
      title="拍照上传"
@@ -1066,12 +1084,14 @@
import { calculateSeniority, toCardGetUserInfo, dateToString } from '@/utils/myUtil'
import ArchivesEdit from './archivesEdit'
import { pages } from '@/settings'
import dictMixin from '../../utils/dictMixin'
export default {
  components: {
    ArchivesEdit,
    Pagination, Treeselect
  },
  mixins: [dictMixin],
  data() {
    return {
      dialog: {
@@ -1104,6 +1124,8 @@
      },
      baseicInformationForm: {},
      empBaseInfoImageUrl: '',
      // 上传方式选择弹窗
      uploadChoiceDialogVisible: false,
      // 摄像头相关
      cameraDialogVisible: false,
      capturedImage: '',
@@ -1185,11 +1207,7 @@
      depts: [],
      fileList: [],
      rules: {
        archivesNumb: [{ required: true, message: '请输入档案号', trigger: 'blur' }, {
          max: 20,
          message: this.$t('rules.noMoreThan20'),
          trigger: 'blur'
        }],
        archivesNumb: [{ max: 20, message: this.$t('rules.noMoreThan20'), trigger: 'blur' }],
        empName: [{ required: true, message: '请输入姓名', trigger: 'blur' },
          { min: 2, max: 50, message: this.$t('rules.noMoreThan50'), trigger: 'blur' }],
        empNumb: [{ required: true, message: '请输入员工编号', trigger: 'blur' },
@@ -1225,6 +1243,14 @@
        family: [{ max: 128, message: '长度不超过128个字符', trigger: 'blur' }],
        certificateValidity: [{ required: true, message: '请选择身份证有效期', trigger: 'change' }],
        urgencyPhone: [{ max: 30, message: '长度不超过30个字符', trigger: 'blur' }]
      },
      // 新增模式的简化验证规则(仅验证核心字段)
      addRules: {
        empName: [{ required: true, message: '请输入姓名', trigger: 'blur' },
          { min: 2, max: 50, message: this.$t('rules.noMoreThan50'), trigger: 'blur' }],
        empNumb: [{ required: true, message: '请输入员工编号', trigger: 'blur' },
          { min: 2, max: 20, message: this.$t('rules.noMoreThan20'), trigger: 'blur' }],
        certificateNumb: [{ required: true, message: '请输入身份证号', trigger: 'blur' }]
      },
      gbdaRules: {
        dimissionType: [{ required: true, message: '请选择离职类型', trigger: 'change' }],
@@ -1317,113 +1343,93 @@
      checkedCities: [],
      tableData: [],
      selectDimissionType: 1,
      statusOptions: [],
      empTypeOptions: [],
      inTypeOptions: [],
      nationOptions: [],
      marriageOptions: [],
      educationOptions: [],
      nativePlaceOptions: [],
      archivesStatusOptions: [],
      insuranceTypeOptions: [],
      empCardStatusOptions: [],
      handbookStatusOptions: [],
      ecgOptions: [],
      certificateListOptions: [],
      physicalExamTypeOptions: [],
      contractStatusOptions: [],
      leaveTypeOptions: [],
      insuranceGaersOptions: [],
      applayStatusOptions: [],
      reportStatusOptions: [],
      hospitalizatioFlagOptions: [],
      settleStatusOptions: [],
      arbitrationTypeOptions: [],
      changeTypeOptions: [],
      dimissionTypeOptions: [],
      ageStrOptions: [],
      sexOptions: [],
      openArchivesForm: {}
    }
  },
  computed: {
    // 字典选项计算属性
    statusOptions() {
      return this.getDictOptions('PLITICAL')
    },
    empTypeOptions() {
      return this.getDictOptions('EMPTYPE')
    },
    nationOptions() {
      return this.getDictOptions('NATION')
    },
    marriageOptions() {
      return this.getDictOptions('MARRIAGE')
    },
    educationOptions() {
      return this.getDictOptions('EDUCATION')
    },
    nativePlaceOptions() {
      return this.getDictOptions('NATIVEPLACE')
    },
    archivesStatusOptions() {
      return this.getDictOptions('archivesStatus')
    },
    insuranceTypeOptions() {
      return this.getDictOptions('INSURANCETYPE')
    },
    empCardStatusOptions() {
      return this.getDictOptions('empCardStatus')
    },
    handbookStatusOptions() {
      return this.getDictOptions('handbookStatus')
    },
    certificateListOptions() {
      return this.getDictOptions('certificateList')
    },
    physicalExamTypeOptions() {
      return this.getDictOptions('PHYSICALEXAMTYPE')
    },
    ecgOptions() {
      return this.getDictOptions('ECG')
    },
    contractStatusOptions() {
      return this.getDictOptions('CONTRACTSTATUS')
    },
    leaveTypeOptions() {
      return this.getDictOptions('LEAVETYPE')
    },
    insuranceGaersOptions() {
      return this.getDictOptions('INSURANCETYPE')
    },
    applayStatusOptions() {
      return this.getDictOptions('applayStatus')
    },
    reportStatusOptions() {
      return this.getDictOptions('reportStatus')
    },
    hospitalizatioFlagOptions() {
      return this.getDictOptions('hospitalizatioFlag')
    },
    settleStatusOptions() {
      return this.getDictOptions('settleStatus')
    },
    arbitrationTypeOptions() {
      return this.getDictOptions('ZCTYPE')
    },
    changeTypeOptions() {
      return this.getDictOptions('changeType')
    },
    dimissionTypeOptions() {
      return this.getDictOptions('LZTYPE')
    },
    ageStrOptions() {
      return this.getDictOptions('ageStr')
    },
    sexOptions() {
      return this.getDictOptions('sex')
    }
  },
  mounted() {
    this.fetch()
    this.initDept()
    this.getDicts('ageStr').then(response => {
      this.ageStrOptions = response.data
    })
    this.getDicts('PLITICAL').then(response => {
      this.statusOptions = response.data
    })
    this.getDicts('sex').then(response => {
      this.sexOptions = response.data
    })
    this.getDicts('empType').then(response => {
      this.empTypeOptions = response.data
    })
    this.getDicts('NATION').then(response => {
      this.nationOptions = response.data
    })
    this.getDicts('MARRIAGE').then(response => {
      this.marriageOptions = response.data
    })
    this.getDicts('EDUCATION').then(response => {
      this.educationOptions = response.data
    })
    this.getDicts('NATIVEPLACE').then(response => {
      this.nativePlaceOptions = response.data
    })
    this.getDicts('archivesStatus').then(response => {
      this.archivesStatusOptions = response.data
    })
    this.getDicts('INSURANCETYPE').then(response => {
      this.insuranceTypeOptions = response.data
    })
    this.getDicts('empCardStatus').then(response => {
      this.empCardStatusOptions = response.data
    })
    this.getDicts('handbookStatus').then(response => {
      this.handbookStatusOptions = response.data
    })
    this.getDicts('certificateList').then(response => {
      this.certificateListOptions = response.data
    })
    this.getDicts('PHYSICALEXAMTYPE').then(response => {
      this.physicalExamTypeOptions = response.data
    })
    this.getDicts('ECG').then(response => {
      this.ecgOptions = response.data
    })
    this.getDicts('CONTRACTSTATUS').then(response => {
      this.contractStatusOptions = response.data
    })
    this.getDicts('LEAVETYPE').then(response => {
      this.leaveTypeOptions = response.data
    })
    this.getDicts('INSURANCETYPE').then(response => {
      this.insuranceGaersOptions = response.data
    })
    this.getDicts('applayStatus').then(response => {
      this.applayStatusOptions = response.data
    })
    this.getDicts('reportStatus').then(response => {
      this.reportStatusOptions = response.data
    })
    this.getDicts('hospitalizatioFlag').then(response => {
      this.hospitalizatioFlagOptions = response.data
    })
    this.getDicts('settleStatus').then(response => {
      this.settleStatusOptions = response.data
    })
    this.getDicts('ZCTYPE').then(response => {
      this.arbitrationTypeOptions = response.data
    })
    this.getDicts('changeType').then(response => {
      this.changeTypeOptions = response.data
    })
    this.getDicts('LZTYPE').then(response => {
      this.dimissionTypeOptions = response.data
    })
    // 字典数据已在登录时预加载,直接从 Vuex 获取
    this.initDictTypes(['ageStr', 'PLITICAL', 'sex', 'EMPTYPE', 'NATION', 'MARRIAGE', 'EDUCATION', 'NATIVEPLACE', 'archivesStatus', 'INSURANCETYPE', 'empCardStatus', 'handbookStatus', 'certificateList', 'PHYSICALEXAMTYPE', 'ECG', 'CONTRACTSTATUS', 'LEAVETYPE', 'applayStatus', 'reportStatus', 'hospitalizatioFlag', 'settleStatus', 'ZCTYPE', 'changeType', 'LZTYPE'])
    this.initJob()
  },
  methods: {
@@ -1647,6 +1653,58 @@
        this.$message.error('图片大小超过4M,请重新上传')
      }
    },
    // 打开上传方式选择弹窗
    openUploadChoice() {
      this.uploadChoiceDialogVisible = true
    },
    // 选择拍照上传
    choiceCamera() {
      this.uploadChoiceDialogVisible = false
      this.cameraDialogVisible = true
      this.$nextTick(() => {
        this.initCamera()
      })
    },
    // 选择文件上传
    choiceFile() {
      this.uploadChoiceDialogVisible = false
      // 创建隐藏的文件输入框
      const input = document.createElement('input')
      input.type = 'file'
      input.accept = 'image/*'
      input.onchange = (e) => {
        const file = e.target.files[0]
        if (file) {
          this.handleFileUpload(file)
        }
      }
      input.click()
    },
    // 处理文件上传
    handleFileUpload(file) {
      // 验证文件类型
      const isImage = file.type.startsWith('image/')
      if (!isImage) {
        this.$message.error('请上传图片文件')
        return
      }
      // 验证文件大小(限制10MB)
      const isLt10M = file.size / 1024 / 1024 < 10
      if (!isLt10M) {
        this.$message.error('图片大小不能超过10MB')
        return
      }
      // 生成预览URL
      const imageUrl = URL.createObjectURL(file)
      this.empBaseInfoImageUrl = imageUrl
      // 读取文件为Base64
      const reader = new FileReader()
      reader.onload = (e) => {
        this.empBaseInfoForm.imagePath = e.target.result
        this.$message.success('照片上传成功')
      }
      reader.readAsDataURL(file)
    },
    // 打开摄像头
    openCamera() {
      this.cameraDialogVisible = true
@@ -1860,29 +1918,24 @@
      this.showXzyg(1)
    },
    putEmpBase(formName) {
      // 根据新增/编辑模式使用不同的验证规则
      const validateRules = this.isAdd ? this.addRules : this.rules
      this.$refs[formName].validate((valid) => {
        if (valid) {
          this.submitEmpInfo()
          this.fetch({
            ...this.queryParams,
            ...this.sort
          })
          this.cleanEmpBase()
          this.showXzyg()
          // 刷新、清空、关闭操作已移至 submitEmpInfo 的成功回调中
        }
      })
      }, validateRules)
    },
    putEmpBaseContinue(formName) {
      // 根据新增/编辑模式使用不同的验证规则
      const validateRules = this.isAdd ? this.addRules : this.rules
      this.$refs[formName].validate((valid) => {
        if (valid) {
          this.submitEmpInfo()
          this.cleanEmpBase()
          this.fetch({
            ...this.queryParams,
            ...this.sort
          })
          // 传入 true 表示保存并继续新增,不关闭对话框
          this.submitEmpInfo(true)
        }
      })
      }, validateRules)
    },
    delEmp() {
      var selection = this.$refs.multipleTable.store.states.selection
@@ -2293,9 +2346,25 @@
    getDateString() {
      return dateToString(new Date())
    },
    submitEmpInfo() {
    submitEmpInfo(continueAdd = false) {
      if (this.isAdd) {
        this.$post('hr/empBaseInfo/addInEmp', { ...this.empBaseInfoForm }).then((r) => {
          this.$message({
            message: this.$t('tips.addSuccess'),
            type: 'success'
          })
          // 刷新列表
          this.fetch({
            ...this.queryParams,
            ...this.sort
          })
          // 清空表单
          this.cleanEmpBase()
          // 如果不是继续新增,则关闭对话框
          if (!continueAdd) {
            this.showXzyg()
          }
          // 处理员工已存在的情况
          if (r.data != null) {
            if (r.data.data.empStatus === '0') {
              this.$confirm('该员工已存在,是否修改?', '提示', {
@@ -2344,12 +2413,6 @@
                  remark: ''
                }
                this.dialogShowDkda = true
                // this.$post('hr/empOpenArchives', { ...this.openArchivesForm }).then(() => {
                //   this.$message({
                //     message: this.$t('员工档案打开成功'),
                //     type: 'success'
                //   })
                // })
              })
            }
          }
@@ -2362,6 +2425,11 @@
          })
          this.$emit('success')
          this.cleanEmpBase()
          this.fetch({
            ...this.queryParams,
            ...this.sort
          })
          this.showXzyg()
        })
      }
    },
@@ -2646,6 +2714,42 @@
  }
}
// 上传方式选择容器
.upload-choice-container {
  display: flex;
  justify-content: space-around;
  padding: 20px 0;
  .upload-choice-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: 120px;
    height: 120px;
    border: 2px dashed #d9d9d9;
    border-radius: 8px;
    cursor: pointer;
    transition: all 0.3s;
    &:hover {
      border-color: #409eff;
      background-color: #f5f7fa;
    }
    i {
      font-size: 40px;
      color: #409eff;
      margin-bottom: 10px;
    }
    span {
      font-size: 14px;
      color: #606266;
    }
  }
}
// 摄像头容器
.camera-container {
  text-align: center;