From a4d0446d7c1c1e56641fd4e887ad4d0ecd0534ca Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期四, 05 三月 2026 17:43:55 +0800
Subject: [PATCH] 排班管理页面完成70%

---
 src/views/personnelManagement/socialSecuritySet/components/formDia.vue |  369 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 369 insertions(+), 0 deletions(-)

diff --git a/src/views/personnelManagement/socialSecuritySet/components/formDia.vue b/src/views/personnelManagement/socialSecuritySet/components/formDia.vue
new file mode 100644
index 0000000..3feab2d
--- /dev/null
+++ b/src/views/personnelManagement/socialSecuritySet/components/formDia.vue
@@ -0,0 +1,369 @@
+<template>
+  <div>
+    <FormDialog
+      v-model="dialogFormVisible"
+      :operation-type="operationType"
+      :title="dialogTitle"
+      width="80%"
+      @close="closeDia"
+      @confirm="submitForm"
+      @cancel="closeDia"
+    >
+      <el-form ref="formRef" :model="form" :rules="rules" label-position="top">
+        <el-row :gutter="24">
+          <!-- 宸︿晶锛氶�傜敤浜哄憳 -->
+          <el-col :span="8">
+            <el-form-item label="閫傜敤浜哄憳锛�" prop="deptIds">
+              <div class="dept-checkbox-wrap">
+                <el-checkbox-group v-model="form.deptIds">
+                  <div
+                    v-for="dept in deptList"
+                    :key="dept.deptId"
+                    class="dept-checkbox-item"
+                  >
+                    <el-checkbox :value="dept.deptId">
+                      {{ dept.deptName }}
+                      <span v-if="dept.personCount != null" class="dept-count"
+                        >{{ dept.personCount }}浜�</span
+                      >
+                    </el-checkbox>
+                  </div>
+                </el-checkbox-group>
+              </div>
+            </el-form-item>
+          </el-col>
+          <!-- 鍙充晶锛氬熀纭�淇℃伅 + 淇濋櫓绫诲瀷 -->
+          <el-col :span="16">
+            <!-- 鍩虹淇℃伅 -->
+            <el-card class="form-card" shadow="never">
+              <template #header>
+                <span class="card-title"><span class="card-title-line">|</span> 鍩虹淇℃伅</span>
+                <el-icon class="card-collapse"><ArrowUp /></el-icon>
+              </template>
+              <el-form-item label="鏂规鏍囬锛�" prop="title">
+                <el-input v-model="form.title" placeholder="璇疯緭鍏�" clearable />
+              </el-form-item>
+              <el-form-item label="澶囨敞锛�" prop="remark">
+                <el-input
+                  v-model="form.remark"
+                  type="textarea"
+                  :rows="2"
+                  placeholder="璇疯緭鍏�"
+                  clearable
+                />
+              </el-form-item>
+            </el-card>
+
+            <!-- 淇濋櫓绫诲瀷 -->
+            <el-card class="form-card" shadow="never">
+              <template #header>
+                <span class="card-title"><span class="card-title-line">|</span> 淇濋櫓绫诲瀷</span>
+                <el-button type="primary" size="small" @click="addInsuranceBenefit">
+                  娣诲姞淇濋櫓绂忓埄
+                </el-button>
+              </template>
+              <el-row :gutter="16">
+                <el-col
+                  v-for="(item, index) in form.insuranceBenefits"
+                  :key="item._key"
+                  :span="12"
+                >
+                  <div class="insurance-benefit-card">
+                    <div class="insurance-benefit-title">
+                      淇濋櫓绂忓埄{{ index + 1 }}
+                      <el-button
+                        v-if="form.insuranceBenefits.length > 1"
+                        type="danger"
+                        link
+                        size="small"
+                        class="card-delete-btn"
+                        @click="removeInsuranceBenefit(index)"
+                      >
+                        鍒犻櫎
+                      </el-button>
+                    </div>
+                    <el-form-item
+                      :prop="'insuranceBenefits.' + index + '.insuranceType'"
+                      label="淇濋櫓绫诲瀷锛�"
+                      label-width="100px"
+                    >
+                      <el-select
+                        v-model="item.insuranceType"
+                        placeholder="璇烽�夋嫨"
+                        clearable
+                        style="width: 100%"
+                      >
+                        <el-option
+                          v-for="opt in insuranceTypeOptions"
+                          :key="opt.value"
+                          :label="opt.label"
+                          :value="opt.value"
+                        />
+                      </el-select>
+                    </el-form-item>
+                    <el-form-item label="缂磋垂鍩烘暟锛�" label-width="100px">
+                      <div class="checkbox-group-inline">
+                        <el-checkbox v-model="item.baseOnSalary">鏍规嵁鍩烘湰宸ヨ祫缂寸撼</el-checkbox>
+                        <el-checkbox v-model="item.useBasicSalary">璋冪敤鍩烘湰宸ヨ祫</el-checkbox>
+                      </div>
+                    </el-form-item>
+                    <el-form-item label="涓汉缂磋垂姣斾緥锛�" label-width="100px">
+                      <div class="personal-ratio-wrap">
+                        <el-input
+                          v-model="item.personalRatio"
+                          placeholder="璇疯緭鍏�"
+                          clearable
+                          style="width: 100px"
+                          type="number"
+                        />
+                        <span class="ratio-unit">(%)</span>
+                        <span class="ratio-plus">+</span>
+                        <el-input
+                          v-model="item.personalFixed"
+                          placeholder="璇疯緭鍏�"
+                          clearable
+                          style="width: 100px"
+                          type="number"
+                        />
+                      </div>
+                    </el-form-item>
+                  </div>
+                </el-col>
+              </el-row>
+            </el-card>
+          </el-col>
+        </el-row>
+      </el-form>
+    </FormDialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, toRefs, getCurrentInstance, nextTick } from "vue";
+import FormDialog from "@/components/Dialog/FormDialog.vue";
+import { ArrowUp } from "@element-plus/icons-vue";
+import { listDept } from "@/api/system/dept.js";
+import {
+  socialSecurityInfo,
+  socialSecurityAdd,
+  socialSecurityUpdate,
+} from "@/api/personnelManagement/socialSecuritySet.js";
+
+const emit = defineEmits(["close"]);
+const { proxy } = getCurrentInstance();
+
+const dialogFormVisible = ref(false);
+const operationType = ref("add");
+const formRef = ref(null);
+const deptList = ref([]);
+
+const dialogTitle = () =>
+  operationType.value === "add" ? "鏂板鏂规" : "缂栬緫鏂规";
+
+// 淇濋櫓绫诲瀷閫夐」锛堝彲鎸夊瓧鍏告浛鎹級
+const insuranceTypeOptions = [
+  { label: "鍏昏�佷繚闄�", value: "pension" },
+  { label: "鍖荤枟淇濋櫓", value: "medical" },
+  { label: "澶变笟淇濋櫓", value: "unemployment" },
+  { label: "宸ヤ激淇濋櫓", value: "work_injury" },
+  { label: "鐢熻偛淇濋櫓", value: "maternity" },
+];
+
+const defaultBenefit = () => ({
+  _key: Math.random().toString(36).slice(2),
+  insuranceType: "",
+  baseOnSalary: false,
+  useBasicSalary: false,
+  personalRatio: "",
+  personalFixed: "",
+});
+
+const data = reactive({
+  form: {
+    id: undefined,
+    title: "",
+    remark: "",
+    deptIds: [],
+    insuranceBenefits: [defaultBenefit()],
+  },
+  rules: {
+    title: [{ required: true, message: "璇疯緭鍏ユ柟妗堟爣棰�", trigger: "blur" }],
+    deptIds: [
+      {
+        required: true,
+        type: "array",
+        min: 1,
+        message: "璇疯嚦灏戦�夋嫨涓�涓�傜敤閮ㄩ棬",
+        trigger: "change",
+      },
+    ],
+  },
+});
+const { form, rules } = toRefs(data);
+
+function flattenDept(tree, list = []) {
+  if (!tree || !tree.length) return list;
+  tree.forEach((node) => {
+    list.push({
+      deptId: node.deptId,
+      deptName: node.deptName,
+      personCount: node.personCount ?? null,
+    });
+    if (node.children && node.children.length) {
+      flattenDept(node.children, list);
+    }
+  });
+  return list;
+}
+
+const loadDeptList = () => {
+  listDept().then((res) => {
+    const tree = res.data ?? [];
+    deptList.value = flattenDept(tree);
+  });
+};
+
+const addInsuranceBenefit = () => {
+  form.value.insuranceBenefits.push(defaultBenefit());
+};
+
+const removeInsuranceBenefit = (index) => {
+  form.value.insuranceBenefits.splice(index, 1);
+};
+
+const resetForm = () => {
+  form.value = {
+    id: undefined,
+    title: "",
+    remark: "",
+    deptIds: [],
+    insuranceBenefits: [defaultBenefit()],
+  };
+};
+
+const openDialog = (type, row) => {
+  operationType.value = type;
+  dialogFormVisible.value = true;
+  loadDeptList();
+  resetForm();
+  if (type === "edit" && row?.id) {
+    socialSecurityInfo(row.id).then((res) => {
+      const d = res.data || {};
+      form.value.id = d.id;
+      form.value.title = d.title;
+      form.value.remark = d.remark ?? "";
+      form.value.deptIds = d.deptIds ?? [];
+      form.value.insuranceBenefits =
+        (d.insuranceBenefits && d.insuranceBenefits.length)
+          ? d.insuranceBenefits.map((b) => ({
+              ...b,
+              _key: b._key || Math.random().toString(36).slice(2),
+            }))
+          : [defaultBenefit()];
+    });
+  }
+};
+
+const submitForm = () => {
+  formRef.value?.validate((valid) => {
+    if (!valid) return;
+    const submitData = {
+      ...form.value,
+      insuranceBenefits: form.value.insuranceBenefits.map(
+        ({ _key, ...rest }) => rest
+      ),
+    };
+    if (operationType.value === "add") {
+      socialSecurityAdd(submitData).then(() => {
+        proxy.$modal.msgSuccess("鏂板鎴愬姛");
+        closeDia();
+      });
+    } else {
+      socialSecurityUpdate(submitData).then(() => {
+        proxy.$modal.msgSuccess("淇敼鎴愬姛");
+        closeDia();
+      });
+    }
+  });
+};
+
+const closeDia = () => {
+  proxy.resetForm?.("formRef");
+  dialogFormVisible.value = false;
+  emit("close");
+};
+
+defineExpose({ openDialog });
+</script>
+
+<style scoped>
+.card-title-line {
+  color: #f56c6c;
+  margin-right: 4px;
+}
+.form-card {
+  margin-bottom: 16px;
+}
+.form-card :deep(.el-card__header) {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 12px 16px;
+}
+.card-title {
+  font-weight: 500;
+}
+.card-collapse {
+  color: #999;
+  cursor: pointer;
+}
+.dept-checkbox-wrap {
+  max-height: 320px;
+  overflow-y: auto;
+  padding: 8px 0;
+  border: 1px solid var(--el-border-color);
+  border-radius: 4px;
+  background: #fff;
+}
+.dept-checkbox-item {
+  padding: 6px 12px;
+}
+.dept-count {
+  color: #909399;
+  font-size: 12px;
+  margin-left: 4px;
+}
+.insurance-benefit-card {
+  border: 1px solid var(--el-border-color-lighter);
+  border-radius: 4px;
+  padding: 12px 16px;
+  margin-bottom: 12px;
+  background: #fafafa;
+}
+.insurance-benefit-title {
+  font-size: 14px;
+  margin-bottom: 12px;
+  font-weight: 500;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+.card-delete-btn {
+  margin-left: auto;
+}
+.checkbox-group-inline {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 16px;
+}
+.personal-ratio-wrap {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+.ratio-unit,
+.ratio-plus {
+  color: #606266;
+  font-size: 14px;
+}
+</style>

--
Gitblit v1.9.3