From c7043ff58f55df9c75ae161b3c134039dc895616 Mon Sep 17 00:00:00 2001
From: yubo <autumnal_wind@yeah.net>
Date: 星期六, 11 四月 2026 19:52:20 +0800
Subject: [PATCH] fix(user): 在职员工界面优化

---
 src/views/user/components/EmpBaseEdit.vue | 1115 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1,115 insertions(+), 0 deletions(-)

diff --git a/src/views/user/components/EmpBaseEdit.vue b/src/views/user/components/EmpBaseEdit.vue
new file mode 100644
index 0000000..bb1359c
--- /dev/null
+++ b/src/views/user/components/EmpBaseEdit.vue
@@ -0,0 +1,1115 @@
+<template>
+  <el-dialog title="基本信息" :visible.sync="visible" width="50%" class="baseinfo" @close="handleClose">
+    <el-container>
+      <el-aside width="200px">
+        <!-- 头像显示区域 -->
+        <div class="avatar-wrapper">
+          <img
+            v-if="empBaseInfoImageUrl"
+            :src="empBaseInfoImageUrl"
+            class="avatar"
+            @click="openUploadChoice"
+          >
+          <div v-else class="avatar-uploader-placeholder" @click="openUploadChoice">
+            <i class="el-icon-plus avatar-uploader-icon" />
+            <div class="upload-tip">点击上传照片</div>
+          </div>
+        </div>
+      </el-aside>
+      <el-main>
+        <el-form
+          ref="empBaseInfoForm"
+          :model="empBaseInfoForm"
+          :rules="isAdd ? addRules : rules"
+          label-position="right"
+          label-width="120px"
+        >
+          <el-row>
+            <el-col :span="12">
+              <el-form-item label="档案号" prop="archivesNumb">
+                <el-input v-model="empBaseInfoForm.archivesNumb" />
+              </el-form-item>
+              <el-form-item label="员工编号" prop="empNumb">
+                <el-input v-model="empBaseInfoForm.empNumb" />
+              </el-form-item>
+              <el-form-item label="身份证号码" prop="certificateNumb">
+                <el-input
+                  v-model="empBaseInfoForm.certificateNumb"
+                  @input="generateUserInfo(0,empBaseInfoForm.certificateNumb)"
+                />
+              </el-form-item>
+              <el-form-item label="员工类别" prop="empType">
+                <el-select v-model="empBaseInfoForm.empType" placeholder="请选择员工类型">
+                  <el-option
+                    v-for="dict in empTypeOptions"
+                    :key="dict.dicItemCode"
+                    :label="dict.dicItemName"
+                    :value="dict.dicItemCode"
+                  />
+                </el-select>
+              </el-form-item>
+              <el-form-item label="民族" prop="nation">
+                <el-select v-model="empBaseInfoForm.nation" filterable placeholder="请选择民族">
+                  <el-option
+                    v-for="dict in nationOptions"
+                    :key="dict.dicItemCode"
+                    :label="dict.dicItemName"
+                    :value="dict.dicItemCode"
+                  />
+                </el-select>
+              </el-form-item>
+              <el-form-item label="婚姻状态" prop="marriage">
+                <el-select v-model="empBaseInfoForm.marriage" placeholder="请选择婚姻状态">
+                  <el-option
+                    v-for="dict in marriageOptions"
+                    :key="dict.dicItemCode"
+                    :label="dict.dicItemName"
+                    :value="dict.dicItemCode"
+                  />
+                </el-select>
+              </el-form-item>
+              <el-form-item label="身高(cm)" prop="stature">
+                <el-input v-model="empBaseInfoForm.stature" />
+              </el-form-item>
+              <el-form-item label="政治面貌" prop="politics">
+                <el-select v-model="empBaseInfoForm.politics" placeholder="请选择政治面貌">
+                  <el-option
+                    v-for="dict in statusOptions"
+                    :key="dict.dicItemCode"
+                    :label="dict.dicItemName"
+                    :value="dict.dicItemCode"
+                  />
+                </el-select>
+              </el-form-item>
+              <el-form-item label="学历" prop="education">
+                <el-select v-model="empBaseInfoForm.education" placeholder="请选择学历">
+                  <el-option
+                    v-for="dict in educationOptions"
+                    :key="dict.dicItemCode"
+                    :label="dict.dicItemName"
+                    :value="dict.dicItemCode"
+                  />
+                </el-select>
+              </el-form-item>
+              <el-form-item label="籍贯" prop="nativePlaceName">
+                <el-autocomplete
+                  v-model="empBaseInfoForm.nativePlaceName"
+                  class="inline-input"
+                  :fetch-suggestions="querySearch"
+                  placeholder="请输入籍贯"
+                  @select="placeNameSelect"
+                />
+              </el-form-item>
+              <el-form-item label="现住址" prop="currentAddress">
+                <el-input v-model="empBaseInfoForm.currentAddress" />
+              </el-form-item>
+              <el-form-item label="电话号码" prop="telePhone">
+                <el-input v-model="empBaseInfoForm.telePhone" />
+              </el-form-item>
+              <el-form-item label="招聘介绍人" prop="introducer">
+                <el-input v-model="empBaseInfoForm.introducer" />
+              </el-form-item>
+              <el-form-item label="银行名称">
+                <el-input v-model="empBaseInfoForm.bankName" />
+              </el-form-item>
+              <el-form-item label="社保档位" prop="insuranceType">
+                <el-select v-model="empBaseInfoForm.insuranceType" placeholder="请选择社保档位">
+                  <el-option
+                    v-for="dict in insuranceTypeOptions"
+                    :key="dict.dicItemCode"
+                    :label="dict.dicItemName"
+                    :value="dict.dicItemCode"
+                  />
+                </el-select>
+              </el-form-item>
+              <el-form-item label="家庭成员及关系1" prop="family">
+                <el-input v-model="empBaseInfoForm.family" />
+              </el-form-item>
+              <el-form-item label="家庭成员及关系2" prop="urgencyPhone">
+                <el-input v-model="empBaseInfoForm.urgencyPhone" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="姓名" prop="empName">
+                <el-input v-model="empBaseInfoForm.empName" />
+              </el-form-item>
+              <el-form-item label="部门(护卫点)" required message="请选择护卫点" prop="deptId">
+                <treeselect
+                  v-model="empBaseInfoForm.deptId"
+                  :multiple="false"
+                  :options="depts"
+                  :clear-value-text="$t('common.clear')"
+                  placeholder="请选择部门(护卫点)"
+                  style="width:100%"
+                  @select="empDeptNameSelect"
+                />
+              </el-form-item>
+              <el-form-item label="岗位" prop="jobName">
+                <el-autocomplete
+                  v-model="empBaseInfoForm.jobName"
+                  class="inline-input"
+                  :fetch-suggestions="querySearchJob"
+                  placeholder="请输入岗位"
+                  @select="jobNameSelect"
+                />
+              </el-form-item>
+              <el-form-item label="性别" prop="sex">
+                <el-select v-model="empBaseInfoForm.sex" placeholder="请选择性别">
+                  <el-option
+                    v-for="dict in sexOptions"
+                    :key="dict.dicItemCode"
+                    :label="dict.dicItemName"
+                    :value="dict.dicItemCode"
+                  />
+                </el-select>
+              </el-form-item>
+              <el-form-item label="身份证有效期" prop="certificateValidity">
+                <el-date-picker
+                  v-model="empBaseInfoForm.certificateValidity"
+                  type="date"
+                  value-format="yyyy-MM-dd"
+                  placeholder="选择日期"
+                />
+              </el-form-item>
+              <el-form-item label="年龄" prop="age">
+                <el-input v-model="empBaseInfoForm.age" />
+              </el-form-item>
+              <el-form-item label="出生日期" prop="birthdate">
+                <el-date-picker
+                  v-model="empBaseInfoForm.birthdate"
+                  type="date"
+                  value-format="yyyy-MM-dd"
+                  placeholder="选择日期"
+                />
+              </el-form-item>
+              <el-form-item label="入职日期" prop="entryDate">
+                <el-date-picker
+                  v-model="empBaseInfoForm.entryDate"
+                  type="date"
+                  value-format="yyyy-MM-dd"
+                  placeholder="选择日期"
+                  @input="calculateSeniority"
+                />
+              </el-form-item>
+              <el-form-item label="入司工龄" prop="seniority">
+                <el-input v-model="empBaseInfoForm.seniority" />
+              </el-form-item>
+              <el-form-item label="户籍地址" prop="censusAddress">
+                <el-input v-model="empBaseInfoForm.censusAddress" />
+              </el-form-item>
+              <el-form-item label="保安员证号" prop="guardNumb">
+                <el-input v-model="empBaseInfoForm.guardNumb" />
+              </el-form-item>
+              <el-form-item label="保安员回执" prop="returnReceipt">
+                <el-input v-model="empBaseInfoForm.returnReceipt" />
+              </el-form-item>
+              <el-form-item label="档案情况" prop="archivesStatus">
+                <el-select v-model="empBaseInfoForm.archivesStatus" placeholder="请选择档案情况">
+                  <el-option
+                    v-for="dict in archivesStatusOptions"
+                    :key="dict.dicItemCode"
+                    :label="dict.dicItemName"
+                    :value="dict.dicItemCode"
+                  />
+                </el-select>
+              </el-form-item>
+              <el-form-item label="银行账号">
+                <el-input v-model="empBaseInfoForm.bankNumb" />
+              </el-form-item>
+              <el-form-item label="社保电脑号" prop="socialNumb">
+                <el-input v-model="empBaseInfoForm.socialNumb" />
+              </el-form-item>
+              <el-form-item label="员工手册" prop="handbookStatus">
+                <el-select v-model="empBaseInfoForm.handbookStatus" placeholder="请选择员工手册">
+                  <el-option
+                    v-for="dict in handbookStatusOptions"
+                    :key="dict.dicItemCode"
+                    :label="dict.dicItemName"
+                    :value="dict.dicItemCode"
+                  />
+                </el-select>
+              </el-form-item>
+              <el-form-item label="工作证" prop="empCardStatus">
+                <el-select v-model="empBaseInfoForm.empCardStatus" placeholder="请选择工作证">
+                  <el-option
+                    v-for="dict in empCardStatusOptions"
+                    :key="dict.dicItemCode"
+                    :label="dict.dicItemName"
+                    :value="dict.dicItemCode"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="24">
+              <el-form-item label="相关证件" prop="certificateList">
+                <el-select v-model="empBaseInfoForm.certificateList" multiple placeholder="请选择相关证件">
+                  <el-option
+                    v-for="dict in certificateListOptions"
+                    :key="dict.dicItemCode"
+                    :label="dict.dicItemName"
+                    :value="dict.dicItemCode"
+                  />
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </el-form>
+      </el-main>
+    </el-container>
+    <div slot="footer" class="dialog-footer">
+      <el-button type="primary" @click="putEmpBase('empBaseInfoForm')">保 存</el-button>
+      <el-button type="primary" @click="putEmpBaseContinue('empBaseInfoForm')">保存并继续新增</el-button>
+      <el-button @click="handleClose">取 消</el-button>
+    </div>
+
+    <!-- 上传方式选择弹窗 -->
+    <el-dialog
+      title="选择上传方式"
+      :visible.sync="uploadChoiceDialogVisible"
+      width="400px"
+      :close-on-click-modal="false"
+      append-to-body
+    >
+      <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="拍照上传"
+      :visible.sync="cameraDialogVisible"
+      width="640px"
+      :close-on-click-modal="false"
+      append-to-body
+      @close="closeCamera"
+    >
+      <div class="camera-container">
+        <!-- 视频预览 -->
+        <video
+          v-show="!capturedImage"
+          ref="video"
+          class="camera-video"
+          autoplay
+          playsinline
+        />
+        <!-- 画布(用于拍照) -->
+        <canvas ref="canvas" style="display: none;" />
+
+        <!-- 拍照结果预览 -->
+        <img
+          v-if="capturedImage"
+          :src="capturedImage"
+          class="captured-image"
+        >
+      </div>
+
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="closeCamera">取消</el-button>
+        <el-button
+          v-if="!capturedImage"
+          type="primary"
+          @click="takePhoto"
+        >
+          拍照
+        </el-button>
+        <el-button
+          v-if="capturedImage"
+          @click="retakePhoto"
+        >
+          重拍
+        </el-button>
+        <el-button
+          v-if="capturedImage"
+          type="primary"
+          @click="confirmPhoto"
+        >
+          确认使用
+        </el-button>
+      </div>
+    </el-dialog>
+  </el-dialog>
+</template>
+
+<script>
+import Treeselect from '@riophae/vue-treeselect'
+import '@riophae/vue-treeselect/dist/vue-treeselect.css'
+import { calculateSeniority, toCardGetUserInfo } from '@/utils/myUtil'
+import { pages } from '@/settings'
+import dictMixin from '@/utils/dictMixin'
+
+export default {
+  name: 'EmpBaseEdit',
+  components: { Treeselect },
+  mixins: [dictMixin],
+  props: {
+    dialogVisible: {
+      type: Boolean,
+      default: false
+    },
+    isAdd: {
+      type: Boolean,
+      default: false
+    },
+    employeeData: {
+      type: Object,
+      default: () => ({})
+    },
+    depts: {
+      type: Array,
+      default: () => []
+    }
+  },
+  data() {
+    return {
+      empBaseInfoForm: {
+        imagePath: '',
+        empId: '',
+        customerId: '',
+        archivesNumb: '',
+        deptId: null,
+        deptName: '',
+        empNumb: '',
+        allDeptName: '',
+        empName: '',
+        sex: '1',
+        JobId: '',
+        jobName: '',
+        empType: '01',
+        certificateType: '',
+        certificateNumb: '',
+        nation: '01',
+        certificateValidity: '',
+        marriage: '1',
+        age: '',
+        stature: '',
+        birthdate: '',
+        politics: '13',
+        entryDate: '',
+        education: '17',
+        seniority: '',
+        nativePlace: '',
+        nativePlaceName: '',
+        censusAddress: '',
+        currentAddress: '',
+        guardNumb: '',
+        telePhone: '',
+        returnReceipt: '',
+        introducer: '',
+        archivesStatus: '0',
+        bankName: '',
+        bankNumb: '',
+        insuranceType: '',
+        socialNumb: '',
+        family: '',
+        handbookStatus: '0',
+        urgencyPhone: '',
+        empCardStatus: '0',
+        certificateList: '',
+        createTime: '',
+        creator: '',
+        modifyTime: '',
+        modifier: '',
+        delFlag: '0',
+        empStatus: 0,
+        version: '',
+        entryType: '20'
+      },
+      empBaseInfoImageUrl: '',
+      uploadChoiceDialogVisible: false,
+      cameraDialogVisible: false,
+      capturedImage: '',
+      stream: null,
+      restaurants: [{ value: '北京市', code: '110000' },
+        { value: '天津市', code: '120000' },
+        { value: '河北省', code: '130000' },
+        { value: '山西省', code: '140000' },
+        { value: '内蒙古自治区', code: '150000' },
+        { value: '辽宁省', code: '210000' },
+        { value: '吉林省', code: '220000' },
+        { value: '黑龙江省', code: '230000' },
+        { value: '上海市', code: '310000' },
+        { value: '江苏省', code: '320000' },
+        { value: '浙江省', code: '330000' },
+        { value: '安徽省', code: '340000' },
+        { value: '福建省', code: '350000' },
+        { value: '江西省', code: '360000' },
+        { value: '山东省', code: '370000' },
+        { value: '河南省', code: '410000' },
+        { value: '湖北省', code: '420000' },
+        { value: '湖南省', code: '430000' },
+        { value: '广东省', code: '440000' },
+        { value: '广西壮族自治区', code: '450000' },
+        { value: '海南省', code: '460000' },
+        { value: '重庆市', code: '500000' },
+        { value: '四川省', code: '510000' },
+        { value: '贵州省', code: '520000' },
+        { value: '云南省', code: '530000' },
+        { value: '西藏自治区', code: '540000' },
+        { value: '陕西省', code: '610000' },
+        { value: '甘肃省', code: '620000' },
+        { value: '青海省', code: '630000' },
+        { value: '宁夏回族自治区', code: '640000' },
+        { value: '新疆维吾尔自治区', code: '650000' },
+        { value: '台湾省', code: '710000' },
+        { value: '香港特别行政区', code: '810000' },
+        { value: '澳门特别行政区', code: '820000' }],
+      restaurJob: [{ value: '总经理', code: '2942725270000031' },
+        { value: '总秘', code: '2942725270000032' },
+        { value: '总助', code: '2942725270000033' },
+        { value: '经理', code: '2942725270000022' },
+        { value: '副经理', code: '2942725270000015' },
+        { value: '助理', code: '2942725270000030' },
+        { value: '项目经理', code: '2942725270000027' },
+        { value: '大队长', code: '2942725270000006' },
+        { value: '大队长兼内勤', code: '2942725270000007' },
+        { value: '中队长', code: '2942725270000029' },
+        { value: '队长', code: '2942725270000011' },
+        { value: '分队长', code: '2942725270000013' },
+        { value: '副队长', code: '2942725270000014' },
+        { value: '班长', code: '2942725270000002' },
+        { value: '保安员', code: '2942725270000003' },
+        { value: '内勤', code: '2942725270000024' },
+        { value: '保洁', code: '2942725270000004' },
+        { value: '电工', code: '2942725270000010' },
+        { value: '绿化工', code: '2942725270000023' },
+        { value: '出纳', code: '2942725270000005' },
+        { value: '人事专员', code: '2942725270000025' },
+        { value: '司机', code: '2942725270000026' },
+        { value: '购买保险', code: '2942725270000016' },
+        { value: '广州燃气中队长', code: '2942725270000018' },
+        { value: '管理员', code: '2942725270000017' },
+        { value: '监控员', code: '2942725270000021' },
+        { value: '员工', code: '2942725270000028' }],
+      rules: {
+        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' },
+          { min: 2, max: 20, message: this.$t('rules.noMoreThan20'), trigger: 'blur' }],
+        deptName: [
+          { required: true, message: '请选择护卫点', trigger: 'input' }
+        ],
+        certificateNumb: [{ required: true, message: '请输入身份证号', trigger: 'blur' }],
+        jobName: [{ required: true, message: '请选择岗位', trigger: 'change' }],
+        nativePlaceName: [{ required: true, message: '请选择籍贯', trigger: 'change' }],
+        bankName: [{ required: true, message: '请输入银行名称', trigger: 'blur' }, {
+          max: 36,
+          message: '长度不超过36个字符',
+          trigger: 'blur'
+        }],
+        insuranceType: [{ required: true, message: '请选择社保档位', trigger: 'change' }],
+        entryDate: [{ required: true, message: '请选择入职日期', trigger: 'change' }],
+        seniority: [{ required: true, message: '请输入入司工龄', trigger: 'blur' }],
+        archivesStatus: [{ required: true, message: '请选择档案情况', trigger: 'change' }],
+        bankNumb: [{ required: true, message: '请输入银行账号', trigger: 'blur' }, {
+          max: 32,
+          message: '长度不超过32个字符',
+          trigger: 'blur'
+        }],
+        empType: [{ required: true, message: '请选择员工类型', trigger: 'change' }],
+        censusAddress: [{ max: 128, message: '长度不超过128个字符', trigger: 'blur' }],
+        currentAddress: [{ max: 128, message: '长度不超过128个字符', trigger: 'blur' }],
+        guardNumb: [{ max: 40, message: '长度不超过40个字符', trigger: 'blur' }],
+        telePhone: [{ max: 30, message: '长度不超过30个字符', trigger: 'blur' }],
+        returnReceipt: [{ max: 40, message: '长度不超过40个字符', trigger: 'blur' }],
+        introducer: [{ max: 32, message: '长度不超过32个字符', trigger: 'blur' }],
+        socialNumb: [{ max: 40, message: '长度不超过40个字符', trigger: 'blur' }],
+        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' }]
+      }
+    }
+  },
+  computed: {
+    visible: {
+      get() {
+        return this.dialogVisible
+      },
+      set(val) {
+        this.$emit('update:dialogVisible', val)
+      }
+    },
+    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')
+    },
+    sexOptions() {
+      return this.getDictOptions('sex')
+    }
+  },
+  watch: {
+    employeeData: {
+      handler(newVal) {
+        if (Object.keys(newVal).length > 0) {
+          this.loadEmployeeData(newVal)
+        } else {
+          this.cleanEmpBase()
+        }
+      },
+      immediate: true,
+      deep: true
+    }
+  },
+  mounted() {
+    this.initDictTypes(['PLITICAL', 'EMPTYPE', 'NATION', 'MARRIAGE', 'EDUCATION', 'NATIVEPLACE', 'archivesStatus', 'INSURANCETYPE', 'empCardStatus', 'handbookStatus', 'certificateList', 'sex'])
+    this.initJob()
+  },
+  methods: {
+    initJob() {
+      this.$get('system/position/dicJob').then((r) => {
+        this.restaurJob = r.data.data
+      })
+    },
+    loadEmployeeData(row) {
+      let certificateList = []
+      if (row.certificateList && typeof row.certificateList === 'string') {
+        certificateList = row.certificateList.split(',')
+        row.certificateList = certificateList
+      }
+
+      this.empBaseInfoForm.empId = row.empId
+      this.empBaseInfoForm.customerId = row.customerId
+      this.empBaseInfoForm.archivesNumb = row.archivesNumb
+      this.empBaseInfoForm.deptId = row.deptId
+      this.empBaseInfoForm.deptName = row.deptName
+      this.empBaseInfoForm.empNumb = row.empNumb
+      this.empBaseInfoForm.empName = row.empName
+      this.empBaseInfoForm.JobId = row.JobId
+      this.empBaseInfoForm.jobName = row.jobName
+      this.empBaseInfoForm.empType = row.empType
+      this.empBaseInfoForm.certificateType = row.certificateType
+      this.empBaseInfoForm.nation = row.nation
+      this.empBaseInfoForm.certificateValidity = row.certificateValidity
+      this.empBaseInfoForm.marriage = row.marriage
+      this.empBaseInfoForm.certificateNumb = row.certificateNumb
+      if (!row.birthdate) {
+        var userinfo = toCardGetUserInfo(this.empBaseInfoForm.certificateNumb)
+        if (userinfo !== null) {
+          this.empBaseInfoForm.age = userinfo.age
+          this.empBaseInfoForm.birthdate = userinfo.birth
+          this.empBaseInfoForm.sex = userinfo.sex
+        }
+      } else {
+        this.empBaseInfoForm.age = row.age
+        this.empBaseInfoForm.birthdate = row.birthdate
+        this.empBaseInfoForm.sex = row.sex
+      }
+      this.empBaseInfoForm.stature = row.stature
+      this.empBaseInfoForm.politics = row.politics
+      this.empBaseInfoForm.entryDate = row.entryDate
+      this.empBaseInfoForm.entryType = row.entryType
+      this.empBaseInfoForm.education = row.education
+      this.empBaseInfoForm.seniority = row.seniority
+      this.empBaseInfoForm.nativePlace = row.nativePlace
+      this.empBaseInfoForm.nativePlaceName = row.nativePlaceName
+      this.empBaseInfoForm.censusAddress = row.censusAddress
+      this.empBaseInfoForm.currentAddress = row.currentAddress
+      this.empBaseInfoForm.guardNumb = row.guardNumb
+      this.empBaseInfoForm.telePhone = row.telePhone
+      this.empBaseInfoForm.returnReceipt = row.returnReceipt
+      this.empBaseInfoForm.introducer = row.introducer
+      this.empBaseInfoForm.archivesStatus = row.archivesStatus
+      this.empBaseInfoForm.bankName = row.bankName
+      this.empBaseInfoForm.bankNumb = row.bankNumb
+      this.empBaseInfoForm.insuranceType = row.insuranceType
+      this.empBaseInfoForm.insuranceTypeName = row.insuranceTypeName
+      this.empBaseInfoForm.socialNumb = row.socialNumb
+      this.empBaseInfoForm.family = row.family
+      this.empBaseInfoForm.handbookStatus = row.handbookStatus
+      this.empBaseInfoForm.urgencyPhone = row.urgencyPhone
+      this.empBaseInfoForm.empCardStatus = row.empCardStatus
+      this.empBaseInfoForm.certificateList = row.certificateList
+      this.empBaseInfoForm.createTime = row.createTime
+      this.empBaseInfoForm.creator = row.creator
+      this.empBaseInfoForm.modifyTime = row.modifyTime
+      this.empBaseInfoForm.modifier = row.modifier
+      this.empBaseInfoForm.delFlag = row.delFlag
+      this.empBaseInfoForm.version = row.version
+      this.empBaseInfoImageUrl = pages.getEmpBaseInfoImage + row.empId + '?t=' + parseInt(100 * Math.random())
+    },
+    cleanEmpBase() {
+      this.empBaseInfoForm.empId = ''
+      this.empBaseInfoForm.customerId = ''
+      this.empBaseInfoForm.archivesNumb = ''
+      this.empBaseInfoForm.deptId = ''
+      this.empBaseInfoForm.deptName = ''
+      this.empBaseInfoForm.empNumb = ''
+      this.empBaseInfoForm.empName = ''
+      this.empBaseInfoForm.sex = ''
+      this.empBaseInfoForm.JobId = ''
+      this.empBaseInfoForm.jobName = ''
+      this.empBaseInfoForm.empType = ''
+      this.empBaseInfoForm.certificateType = ''
+      this.empBaseInfoForm.certificateNumb = ''
+      this.empBaseInfoForm.nation = ''
+      this.empBaseInfoForm.certificateValidity = ''
+      this.empBaseInfoForm.marriage = ''
+      this.empBaseInfoForm.age = ''
+      this.empBaseInfoForm.stature = ''
+      this.empBaseInfoForm.birthdate = ''
+      this.empBaseInfoForm.politics = ''
+      this.empBaseInfoForm.entryDate = ''
+      this.empBaseInfoForm.education = ''
+      this.empBaseInfoForm.seniority = ''
+      this.empBaseInfoForm.nativePlace = ''
+      this.empBaseInfoForm.nativePlaceName = ''
+      this.empBaseInfoForm.censusAddress = ''
+      this.empBaseInfoForm.currentAddress = ''
+      this.empBaseInfoForm.guardNumb = ''
+      this.empBaseInfoForm.telePhone = ''
+      this.empBaseInfoForm.returnReceipt = ''
+      this.empBaseInfoForm.introducer = ''
+      this.empBaseInfoForm.archivesStatus = ''
+      this.empBaseInfoForm.bankName = ''
+      this.empBaseInfoForm.bankNumb = ''
+      this.empBaseInfoForm.insuranceType = ''
+      this.empBaseInfoForm.socialNumb = ''
+      this.empBaseInfoForm.family = ''
+      this.empBaseInfoForm.handbookStatus = ''
+      this.empBaseInfoForm.urgencyPhone = ''
+      this.empBaseInfoForm.empCardStatus = ''
+      this.empBaseInfoForm.certificateList = ''
+      this.empBaseInfoForm.createTime = ''
+      this.empBaseInfoForm.creator = ''
+      this.empBaseInfoForm.modifyTime = ''
+      this.empBaseInfoForm.modifier = ''
+      this.empBaseInfoForm.delFlag = ''
+      this.empBaseInfoForm.version = ''
+      this.empBaseInfoForm.imagePath = ''
+      this.empBaseInfoImageUrl = ''
+    },
+    putEmpBase(formName) {
+      const validateRules = this.isAdd ? this.addRules : this.rules
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          this.submitEmpInfo()
+        }
+      }, validateRules)
+    },
+    putEmpBaseContinue(formName) {
+      const validateRules = this.isAdd ? this.addRules : this.rules
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          this.submitEmpInfo(true)
+        }
+      }, validateRules)
+    },
+    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.$emit('success')
+
+          this.cleanEmpBase()
+
+          if (!continueAdd) {
+            this.handleClose()
+          }
+
+          if (r.data != null) {
+            if (r.data.data.empStatus === '0') {
+              this.$emit('employee-exists', r.data.data)
+            } else {
+              this.$emit('need-open-archives', {
+                empId: r.data.data.empId,
+                empName: r.data.data.empName,
+                certificateNumb: r.data.data.certificateNumb,
+                dimissionType: r.data.data.dimissionType,
+                remark: r.data.data.remark
+              })
+            }
+          }
+        })
+      } else {
+        this.$put('hr/empBaseInfo', { ...this.empBaseInfoForm }).then(() => {
+          this.$message({
+            message: this.$t('tips.updateSuccess'),
+            type: 'success'
+          })
+          this.$emit('success')
+          this.cleanEmpBase()
+          this.handleClose()
+        })
+      }
+    },
+    handleClose() {
+      this.cleanEmpBase()
+      this.$emit('close')
+    },
+    generateUserInfo(index, val) {
+      var userinfo = toCardGetUserInfo(val)
+      if (userinfo === null) {
+        return
+      }
+      this.empBaseInfoForm.age = userinfo.age
+      this.empBaseInfoForm.birthdate = userinfo.birth
+      this.empBaseInfoForm.sex = userinfo.sex
+    },
+    calculateSeniority(val) {
+      this.empBaseInfoForm.seniority = calculateSeniority(val)
+    },
+    querySearch(queryString, cb) {
+      var restaurants = this.restaurants
+      var results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants
+      cb(results)
+    },
+    createFilter(queryString) {
+      return (restaurant) => {
+        return (restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0)
+      }
+    },
+    placeNameSelect(item) {
+      this.empBaseInfoForm.nativePlaceName = item.value
+      this.empBaseInfoForm.nativePlace = item.code
+    },
+    querySearchJob(queryString, cb) {
+      const restaurants = this.restaurJob
+      const results = queryString ? restaurants.filter(this.createFilter(queryString)) : restaurants
+      cb(results)
+    },
+    jobNameSelect(item) {
+      this.empBaseInfoForm.jobName = item.value
+      this.empBaseInfoForm.JobId = item.code
+    },
+    empDeptNameSelect(val) {
+      this.empBaseInfoForm.deptId = val.id
+      this.empBaseInfoForm.deptName = val.label
+      this.empBaseInfoForm.allDeptName = val.allDeptName
+    },
+    getImageBlob(url, cb) {
+      var xhr = new XMLHttpRequest()
+      xhr.open('get', url, true)
+      xhr.responseType = 'blob'
+      xhr.onload = function() {
+        if (this.status === 200) {
+          if (cb) cb(this.response)
+        }
+      }
+      xhr.send()
+    },
+    preView(url) {
+      var this_ = this
+      const reader = new FileReader()
+      this.getImageBlob(url, function(blob) {
+        reader.readAsDataURL(blob)
+      })
+      reader.onload = function(e) {
+        var img = document.createElement('img')
+        img.src = e.target.result
+        this_.empBaseInfoForm.imagePath = e.target.result
+      }
+    },
+    handlePictureCardPreview(file) {
+      this.empBaseInfoImageUrl = URL.createObjectURL(file.raw)
+      if (file.size < 4400000) {
+        var fileName = file.name
+        var suffix = fileName.substring(fileName.lastIndexOf('.') + 1).toUpperCase()
+        if (suffix === 'JPG' || suffix === 'PNG') {
+          this.preView(this.empBaseInfoImageUrl)
+        } else {
+          this.$message.error('只能上传jpg/png文件,且不超过4MB,请重新上传!')
+        }
+      } else {
+        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
+      }
+      const isLt10M = file.size / 1024 / 1024 < 10
+      if (!isLt10M) {
+        this.$message.error('图片大小不能超过10MB')
+        return
+      }
+      const imageUrl = URL.createObjectURL(file)
+      this.empBaseInfoImageUrl = imageUrl
+      const reader = new FileReader()
+      reader.onload = (e) => {
+        this.empBaseInfoForm.imagePath = e.target.result
+        this.$message.success('照片上传成功')
+      }
+      reader.readAsDataURL(file)
+    },
+    openCamera() {
+      this.cameraDialogVisible = true
+      this.$nextTick(() => {
+        this.initCamera()
+      })
+    },
+    async initCamera() {
+      try {
+        this.stream = await navigator.mediaDevices.getUserMedia({
+          video: {
+            width: { ideal: 640 },
+            height: { ideal: 480 },
+            facingMode: 'user'
+          },
+          audio: false
+        })
+
+        const video = this.$refs.video
+        if (video) {
+          video.srcObject = this.stream
+        }
+      } catch (error) {
+        this.$message.error('无法访问摄像头,请检查摄像头权限设置')
+        console.error('摄像头初始化失败:', error)
+      }
+    },
+    takePhoto() {
+      const video = this.$refs.video
+      const canvas = this.$refs.canvas
+
+      if (!video || !canvas) return
+
+      canvas.width = video.videoWidth || 640
+      canvas.height = video.videoHeight || 480
+
+      const ctx = canvas.getContext('2d')
+      ctx.drawImage(video, 0, 0, canvas.width, canvas.height)
+
+      this.capturedImage = canvas.toDataURL('image/jpeg', 0.9)
+
+      this.stopCamera()
+    },
+    retakePhoto() {
+      this.capturedImage = ''
+      this.initCamera()
+    },
+    confirmPhoto() {
+      if (this.capturedImage) {
+        this.empBaseInfoImageUrl = this.capturedImage
+        this.empBaseInfoForm.imagePath = this.capturedImage
+        this.closeCamera()
+        this.$message.success('照片已保存')
+      }
+    },
+    closeCamera() {
+      this.stopCamera()
+      this.cameraDialogVisible = false
+      this.capturedImage = ''
+    },
+    stopCamera() {
+      if (this.stream) {
+        this.stream.getTracks().forEach(track => {
+          track.stop()
+        })
+        this.stream = null
+      }
+
+      const video = this.$refs.video
+      if (video) {
+        video.srcObject = null
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.baseinfo .el-container {
+  .el-aside {
+    background-color: #fff;
+  }
+
+  .el-main {
+    background-color: #fff;
+  }
+}
+
+.el-autocomplete {
+  width: 100%;
+}
+
+.el-select {
+  width: 100%;
+}
+
+.el-aside {
+  padding: 20px;
+  background: #f3f5f8;
+  height: 600px;
+
+  .el-tree {
+    height: 100%;
+  }
+}
+
+.avatar-wrapper {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  margin-bottom: 10px;
+
+  .avatar {
+    width: 150px;
+    height: 150px;
+    border-radius: 4px;
+    cursor: pointer;
+    object-fit: cover;
+    border: 1px dashed #d9d9d9;
+
+    &:hover {
+      border-color: #409eff;
+    }
+  }
+
+  .avatar-uploader-placeholder {
+    width: 150px;
+    height: 150px;
+    border: 1px dashed #d9d9d9;
+    border-radius: 4px;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    cursor: pointer;
+    background-color: #fafafa;
+
+    &:hover {
+      border-color: #409eff;
+      background-color: #f0f9ff;
+    }
+
+    .avatar-uploader-icon {
+      font-size: 28px;
+      color: #8c939d;
+      line-height: 1;
+      margin-bottom: 8px;
+    }
+
+    .upload-tip {
+      font-size: 12px;
+      color: #8c939d;
+    }
+  }
+}
+
+.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;
+
+  .camera-video {
+    width: 100%;
+    max-width: 600px;
+    height: auto;
+    border-radius: 4px;
+    background: #000;
+  }
+
+  .captured-image {
+    width: 100%;
+    max-width: 600px;
+    height: auto;
+    border-radius: 4px;
+  }
+}
+</style>

--
Gitblit v1.8.0