From 2763efef00d553bc65e08bd21384cdd6dd2a5a2f Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期四, 05 三月 2026 11:52:05 +0800
Subject: [PATCH] 进销存升级 1.添加社会保险设置页面,开发与联调 2.丰富新增入职所填字段字段并于用户管理关联 3.修改人员薪资页面样式和逻辑

---
 src/views/personnelManagement/employeeRecord/components/NewOrEditFormDia.vue |  620 ++++++++++++++++++++++++-------------------------------
 1 files changed, 273 insertions(+), 347 deletions(-)

diff --git a/src/views/personnelManagement/employeeRecord/components/NewOrEditFormDia.vue b/src/views/personnelManagement/employeeRecord/components/NewOrEditFormDia.vue
index 1c06a98..e44bdb8 100644
--- a/src/views/personnelManagement/employeeRecord/components/NewOrEditFormDia.vue
+++ b/src/views/personnelManagement/employeeRecord/components/NewOrEditFormDia.vue
@@ -1,373 +1,299 @@
 <template>
-  <div>
-    <el-dialog v-model="dialogFormVisible"
-               :title="operationType === 'add' ? '鏂板鍏ヨ亴' : '缂栬緫浜哄憳'"
-               width="70%"
-               @close="closeDia">
-      <el-form :model="form"
-               label-width="140px"
-               label-position="top"
-               :rules="rules"
-               ref="formRef">
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="鍛樺伐缂栧彿锛�"
-                          prop="staffNo">
-              <el-input v-model="form.staffNo"
-                        placeholder="璇疯緭鍏�"
-                        clearable
-                        :disabled="operationType !== 'add'" />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="濮撳悕锛�"
-                          prop="staffName">
-              <el-input v-model="form.staffName"
-                        placeholder="璇疯緭鍏�"
-                        clearable />
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="鎬у埆锛�"
-                          prop="sex">
-              <el-select v-model="form.sex">
-                <el-option label="鐢�"
-                           value="鐢�" />
-                <el-option label="濂�"
-                           value="濂�" />
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="鎴风睄浣忓潃锛�"
-                          prop="nativePlace">
-              <el-input v-model="form.nativePlace"
-                        placeholder="璇疯緭鍏�"
-                        clearable />
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="宀椾綅锛�"
-                          prop="sysPostId">
-              <el-select v-model="form.sysPostId"
-                         placeholder="璇烽�夋嫨宀椾綅"
-                         clearable>
-                <el-option v-for="item in postOptions"
-                           :key="item.postId"
-                           :label="item.postName"
-                           :value="item.postId"
-                           :disabled="item.status === '1'" />
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="鐜颁綇鍧�锛�"
-                          prop="adress">
-              <el-input v-model="form.adress"
-                        placeholder="璇疯緭鍏�"
-                        clearable />
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="閮ㄩ棬锛�"
-                          prop="sysDeptId">
-              <el-tree-select v-model="form.sysDeptId"
-                              :data="deptOptions"
-                              :props="{ value: 'id', label: 'label', children: 'children' }"
-                              value-key="id"
-                              placeholder="璇烽�夋嫨閮ㄩ棬"
-                              check-strictly />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="骞撮緞锛�"
-                          prop="age">
-              <el-input-number v-model="form.age"
-                               :precision="0"
-                               :step="1"
-                               style="width: 100%" />
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="绗竴瀛﹀巻锛�"
-                          prop="firstStudy">
-              <el-input v-model="form.firstStudy"
-                        placeholder="璇疯緭鍏�"
-                        clearable />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="涓撲笟锛�"
-                          prop="profession">
-              <el-input v-model="form.profession"
-                        placeholder="璇疯緭鍏�"
-                        clearable />
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="鑱旂郴鐢佃瘽锛�"
-                          prop="phone">
-              <el-input v-model="form.phone"
-                        placeholder="璇疯緭鍏�"
-                        clearable />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="绱ф�ヨ仈绯讳汉锛�"
-                          prop="emergencyContact">
-              <el-input v-model="form.emergencyContact"
-                        placeholder="璇疯緭鍏�"
-                        clearable />
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="绱ф�ヨ仈绯讳汉鑱旂郴鐢佃瘽锛�"
-                          prop="emergencyContactPhone">
-              <el-input v-model="form.emergencyContactPhone"
-                        placeholder="璇疯緭鍏�"
-                        clearable />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="鍚堝悓骞撮檺锛�"
-                          prop="contractTerm">
-              <el-input-number v-model="form.contractTerm"
-                               :precision="0"
-                               :step="1"
-                               style="width: 100%"
-                               :disabled="true" />
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="30">
-          <el-col :span="12">
-            <el-form-item label="鍚堝悓寮�濮嬫棩鏈燂細"
-                          prop="contractStartTime">
-              <el-date-picker v-model="form.contractStartTime"
-                              type="date"
-                              placeholder="璇烽�夋嫨鏃ユ湡"
-                              value-format="YYYY-MM-DD"
-                              format="YYYY-MM-DD"
-                              clearable
-                              style="width: 100%"
-                              @change="calculateContractTerm" />
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="鍚堝悓缁撴潫鏃ユ湡锛�"
-                          prop="contractEndTime">
-              <el-date-picker v-model="form.contractEndTime"
-                              type="date"
-                              placeholder="璇烽�夋嫨鏃ユ湡"
-                              value-format="YYYY-MM-DD"
-                              format="YYYY-MM-DD"
-                              clearable
-                              style="width: 100%"
-                              @change="calculateContractTerm" />
-            </el-form-item>
-          </el-col>
-        </el-row>
+  <FormDialog
+    v-model="dialogFormVisible"
+    :operation-type="operationType"
+    :title="dialogTitle"
+    width="90%"
+    @close="closeDia"
+    @confirm="submitForm"
+    @cancel="closeDia"
+  >
+    <div class="form-dia-body">
+      <el-form
+        ref="formRef"
+        :model="form"
+        :rules="rules"
+        label-position="top"
+      >
+        <BasicInfoSection :form="form" :operation-type="operationType" />
+        <JobInfoSection
+          :form="form"
+          :post-options="postOptions"
+          :dept-options="deptOptions"
+        />
+        <EducationWorkSection :form="form" />
+        <EmergencyAndAttachmentSection :form="form" />
       </el-form>
-      <template #footer>
-        <div class="dialog-footer">
-          <el-button type="primary"
-                     @click="submitForm">纭</el-button>
-          <el-button @click="closeDia">鍙栨秷</el-button>
-        </div>
-      </template>
-    </el-dialog>
-  </div>
+    </div>
+  </FormDialog>
 </template>
 
 <script setup>
-  import { ref, onMounted } from "vue";
-  import { findPostOptions } from "@/api/system/post.js";
-  import { listDept } from "@/api/system/dept.js";
-  import {
-    staffOnJobInfo,
-    createStaffOnJob,
-    updateStaffOnJob,
-  } from "@/api/personnelManagement/staffOnJob.js";
-  import { deptTreeSelect } from "@/api/system/user.js";
-  const { proxy } = getCurrentInstance();
-  const emit = defineEmits(["close"]);
+import {
+  ref,
+  reactive,
+  toRefs,
+  onMounted,
+  getCurrentInstance,
+  nextTick,
+} from "vue";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+import { findPostOptions } from "@/api/system/post.js";
+import { deptTreeSelect } from "@/api/system/user.js";
+import {
+  staffOnJobInfo,
+  createStaffOnJob,
+  updateStaffOnJob,
+} from "@/api/personnelManagement/staffOnJob.js";
 
-  const dialogFormVisible = ref(false);
-  const operationType = ref("");
-  const id = ref(0);
-  const data = reactive({
-    form: {
-      staffNo: "",
-      staffName: "",
-      sex: "",
-      nativePlace: "",
-      postJob: "",
-      adress: "",
-      firstStudy: "",
-      profession: "",
-      age: 0,
-      phone: "",
-      emergencyContact: "",
-      emergencyContactPhone: "",
-      contractTerm: 0,
-      contractStartTime: "",
-      contractEndTime: "",
-      sysPostId: undefined,
-      sysDeptId: undefined,
-    },
-    rules: {
-      staffNo: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-      staffName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-      sex: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-      nativePlace: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-      postJob: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-      adress: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-      firstStudy: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-      profession: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-      age: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-      phone: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-      emergencyContact: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-      emergencyContactPhone: [
-        { required: true, message: "璇疯緭鍏�", trigger: "blur" },
-      ],
-      contractTerm: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-      contractStartTime: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-      contractEndTime: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
-      sysDeptId: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
-    },
-    postOptions: [], // 宀椾綅閫夐」
-    deptOptions: [], // 閮ㄩ棬閫夐」
+import BasicInfoSection from "./BasicInfoSection.vue";
+import JobInfoSection from "./JobInfoSection.vue";
+import EducationWorkSection from "./EducationWorkSection.vue";
+import EmergencyAndAttachmentSection from "./EmergencyAndAttachmentSection.vue";
+
+const { proxy } = getCurrentInstance();
+const emit = defineEmits(["close"]);
+
+const dialogFormVisible = ref(false);
+const operationType = ref("add");
+const id = ref(0);
+const formRef = ref(null);
+
+const dialogTitle = () =>
+  operationType.value === "add" ? "鏂板鍏ヨ亴" : "缂栬緫浜哄憳";
+
+const createEmptyEducation = () => ({
+  degree: "",
+  school: "",
+  admissionDate: "",
+  graduationDate: "",
+  major: "",
+  academicDegree: "",
+});
+
+const createEmptyWork = () => ({
+  company: "",
+  department: "",
+  position: "",
+  startDate: "",
+  endDate: "",
+  description: "",
+});
+
+const createEmptyEmergency = () => ({
+  name: "",
+  relation: "",
+  phone: "",
+  address: "",
+});
+
+const createDefaultForm = () => ({
+  id: undefined,
+  // 鍩烘湰淇℃伅
+  staffNo: "",
+  staffName: "",
+  aliasName: "",
+  phone: "",
+  sex: "",
+  birthDate: "",
+  age: undefined,
+  nativePlace: "",
+  nation: "",
+  maritalStatus: "",
+  politicalStatus: "",
+  firstWorkDate: "",
+  workingYears: undefined,
+  idCardNo: "",
+  hukouType: "",
+  email: "",
+  currentAddress: "",
+  // 鍦ㄨ亴淇℃伅
+  jobNo: "",
+  staffType: "",
+  entryDate: "",
+  probationPeriod: undefined,
+  regularDate: "",
+  sysDeptId: undefined,
+  directLeader: "",
+  sysPostId: undefined,
+  jobLevel: "",
+  basicSalary: undefined,
+  // 閾惰鍗′俊鎭�
+  bankName: "",
+  bankCardNo: "",
+  // 鏁欒偛缁忓巻
+  educationList: [createEmptyEducation()],
+  // 宸ヤ綔缁忓巻
+  workExperienceList: [createEmptyWork()],
+  // 绱ф�ヨ仈绯讳汉
+  emergencyContacts: [createEmptyEmergency()],
+  emergencyContact: "",
+  emergencyContactPhone: "",
+  // 鏉愭枡闄勪欢锛堜粎鍓嶇灞曠ず锛�
+  attachments: [],
+});
+
+const state = reactive({
+  form: createDefaultForm(),
+  rules: {
+    staffNo: [{ required: true, message: "璇疯緭鍏ュ憳宸ョ紪鍙�", trigger: "blur" }],
+    staffName: [{ required: true, message: "璇疯緭鍏ュ鍚�", trigger: "blur" }],
+    phone: [{ required: true, message: "璇疯緭鍏ユ墜鏈�", trigger: "blur" }],
+    sex: [{ required: true, message: "璇烽�夋嫨鎬у埆", trigger: "change" }],
+    birthDate: [
+      { required: true, message: "璇烽�夋嫨鍑虹敓鏃ユ湡", trigger: "change" },
+    ],
+    jobNo: [{ required: true, message: "璇疯緭鍏ュ伐鍙�", trigger: "blur" }],
+    staffType: [
+      { required: true, message: "璇烽�夋嫨鍛樺伐绫诲瀷", trigger: "change" },
+    ],
+    entryDate: [
+      { required: true, message: "璇烽�夋嫨鍏ヨ亴鏃ユ湡", trigger: "change" },
+    ],
+    sysDeptId: [
+      { required: true, message: "璇烽�夋嫨閮ㄩ棬", trigger: "change" },
+    ],
+  },
+  postOptions: [],
+  deptOptions: [],
+});
+
+const { form, rules, postOptions, deptOptions } = toRefs(state);
+
+const resetForm = () => {
+  Object.assign(form.value, createDefaultForm());
+  nextTick(() => {
+    formRef.value?.clearValidate();
   });
-  const { form, rules, postOptions, deptOptions } = toRefs(data);
+};
 
-  // 鎵撳紑寮规
-  const openDialog = (type, row) => {
-    operationType.value = type;
-    dialogFormVisible.value = true;
-    if (operationType.value === "edit") {
-      id.value = row.id;
-      staffOnJobInfo(id.value, {}).then(res => {
-        form.value = { ...res.data };
-        if (form.value.sysPostId === 0) {
-          form.value.sysPostId = undefined;
-        }
-        if (form.value.sysDeptId === 0) {
-          form.value.sysDeptId = undefined;
-        }
-        // 缂栬緫鏃朵篃璁$畻涓�娆″悎鍚屽勾闄�
-        calculateContractTerm();
-      });
-    } else {
-      form.value.id = "";
+const fetchPostOptions = () => {
+  findPostOptions().then((res) => {
+    postOptions.value = res.data || [];
+  });
+};
+
+const fetchDeptOptions = () => {
+  deptTreeSelect().then((response) => {
+    deptOptions.value = filterDisabledDept(
+      JSON.parse(JSON.stringify(response.data || []))
+    );
+  });
+};
+
+function filterDisabledDept(deptList) {
+  return deptList.filter((dept) => {
+    if (dept.disabled) {
+      return false;
     }
-  };
-  onMounted(() => {
-    fetchPostOptions();
-    fetchDeptOptions();
+    if (dept.children && dept.children.length) {
+      dept.children = filterDisabledDept(dept.children);
+    }
+    return true;
   });
+}
 
-  const fetchPostOptions = () => {
-    findPostOptions().then(res => {
-      postOptions.value = res.data;
-    });
-  };
+const syncEmergencyToLegacyField = () => {
+  const first = form.value.emergencyContacts?.[0];
+  form.value.emergencyContact = first?.name || "";
+  form.value.emergencyContactPhone = first?.phone || "";
+};
 
-  // 鏌ヨ閮ㄩ棬鍒楄〃
-  const fetchDeptOptions = () => {
-    deptTreeSelect().then(response => {
-      deptOptions.value = filterDisabledDept(
-        JSON.parse(JSON.stringify(response.data))
-      );
-    });
-  };
-
-  /** 杩囨护绂佺敤鐨勯儴闂� */
-  function filterDisabledDept(deptList) {
-    return deptList.filter(dept => {
-      if (dept.disabled) {
-        return false;
+const openDialog = (type, row) => {
+  operationType.value = type;
+  dialogFormVisible.value = true;
+  fetchPostOptions();
+  fetchDeptOptions();
+  resetForm();
+  if (type === "edit" && row?.id) {
+    id.value = row.id;
+    staffOnJobInfo(id.value, {}).then((res) => {
+      const d = res.data || {};
+      Object.assign(form.value, {
+        ...form.value,
+        ...d,
+      });
+      if (!Array.isArray(form.value.educationList) || !form.value.educationList.length) {
+        form.value.educationList = [createEmptyEducation()];
       }
-      if (dept.children && dept.children.length) {
-        dept.children = filterDisabledDept(dept.children);
+      if (
+        !Array.isArray(form.value.workExperienceList) ||
+        !form.value.workExperienceList.length
+      ) {
+        form.value.workExperienceList = [createEmptyWork()];
       }
-      return true;
+      if (
+        !Array.isArray(form.value.emergencyContacts) ||
+        !form.value.emergencyContacts.length
+      ) {
+        form.value.emergencyContacts = [createEmptyEmergency()];
+      }
+      if (form.value.sysPostId === 0) {
+        form.value.sysPostId = undefined;
+      }
+      if (form.value.sysDeptId === 0) {
+        form.value.sysDeptId = undefined;
+      }
     });
   }
+};
 
-  // 鎻愪氦浜у搧琛ㄥ崟
-  const submitForm = () => {
-    if (!form.value.sysPostId) {
-      form.value.sysPostId = undefined;
-    }
-    if (!form.value.sysDeptId) {
-      form.value.sysDeptId = undefined;
-    }
-    proxy.$refs.formRef.validate(valid => {
-      if (valid) {
-        if (operationType.value === "add") {
-          createStaffOnJob(form.value).then(res => {
-            proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
-            closeDia();
-          });
-        } else {
-          updateStaffOnJob(id.value, form.value).then(res => {
-            proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
-            closeDia();
-          });
-        }
-      }
-    });
-  };
-  // 璁$畻鍚堝悓骞撮檺
-  const calculateContractTerm = () => {
-    if (form.value.contractStartTime && form.value.contractEndTime) {
-      const startDate = new Date(form.value.contractStartTime);
-      const endDate = new Date(form.value.contractEndTime);
+onMounted(() => {
+  fetchPostOptions();
+  fetchDeptOptions();
+});
 
-      if (endDate > startDate) {
-        // 璁$畻骞翠唤宸�
-        const yearDiff = endDate.getFullYear() - startDate.getFullYear();
-        const monthDiff = endDate.getMonth() - startDate.getMonth();
-        const dayDiff = endDate.getDate() - startDate.getDate();
-
-        let years = yearDiff;
-
-        // 濡傛灉缁撴潫鏃ユ湡鐨勬湀鏃ュ皬浜庡紑濮嬫棩鏈熺殑鏈堟棩锛屽垯鍑忓幓1骞�
-        if (monthDiff < 0 || (monthDiff === 0 && dayDiff < 0)) {
-          years = yearDiff - 1;
-        }
-
-        form.value.contractTerm = Math.max(0, years);
+const submitForm = () => {
+  if (!form.value.sysPostId) {
+    form.value.sysPostId = undefined;
+  }
+  if (!form.value.sysDeptId) {
+    form.value.sysDeptId = undefined;
+  }
+  syncEmergencyToLegacyField();
+  formRef.value?.validate((valid) => {
+    if (valid) {
+      if (operationType.value === "add") {
+        createStaffOnJob(form.value).then(() => {
+          proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+          closeDia();
+        });
       } else {
-        form.value.contractTerm = 0;
+        updateStaffOnJob(id.value, form.value).then(() => {
+          proxy.$modal.msgSuccess("鎻愪氦鎴愬姛");
+          closeDia();
+        });
       }
-    } else {
-      form.value.contractTerm = 0;
     }
-  };
-
-  // 鍏抽棴寮规
-  const closeDia = () => {
-    proxy.resetForm("formRef");
-    dialogFormVisible.value = false;
-    emit("close");
-  };
-  defineExpose({
-    openDialog,
   });
+};
+
+const closeDia = () => {
+  formRef.value?.resetFields();
+  dialogFormVisible.value = false;
+  emit("close");
+};
+
+defineExpose({
+  openDialog,
+});
 </script>
 
 <style scoped>
+.form-dia-body {
+  padding: 0;
+}
+
+.card-title-line {
+  color: #f56c6c;
+  margin-right: 4px;
+}
+
+.form-card {
+  margin-bottom: 16px;
+}
+
+.dialog-footer {
+  text-align: right;
+}
 </style>
\ No newline at end of file

--
Gitblit v1.9.3