From 0dc723247c2b9851fd0b9ff0138d63f90a2265e3 Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期二, 10 二月 2026 09:45:49 +0800
Subject: [PATCH] 人员薪资台账接口调联

---
 src/views/personnelManagement/monthlyStatistics/components/formDia.vue |  318 ++++++++++++++++++++++++++
 src/api/personnelManagement/monthlyStatistics.js                       |   65 +++++
 src/views/personnelManagement/monthlyStatistics/index.vue              |  303 +++++++++++++++++++++++++
 3 files changed, 686 insertions(+), 0 deletions(-)

diff --git a/src/api/personnelManagement/monthlyStatistics.js b/src/api/personnelManagement/monthlyStatistics.js
new file mode 100644
index 0000000..a070d0f
--- /dev/null
+++ b/src/api/personnelManagement/monthlyStatistics.js
@@ -0,0 +1,65 @@
+import request from "@/utils/request";
+
+// 浜哄憳钖祫鍙拌处鍒楄〃
+export function monthlyStatisticsListPage(query) {
+  return request({
+    url: "/compensationPerformance/listPage",
+    method: "get",
+    params: query,
+  });
+}
+
+// 浜哄憳钖祫鍙拌处璇︽儏
+export function monthlyStatisticsGet(id) {
+  return request({
+    url: "/monthlyStatistics/get",
+    method: "get",
+    params: { id },
+  });
+}
+
+// 鏂板浜哄憳钖祫鍙拌处
+export function monthlyStatisticsAdd(data) {
+  return request({
+    url: "/compensationPerformance/add",
+    method: "post",
+    data,
+  });
+}
+
+// 缂栬緫浜哄憳钖祫鍙拌处
+export function monthlyStatisticsUpdate(data) {
+  return request({
+    url: "/compensationPerformance/update",
+    method: "post",
+    data,
+  });
+}
+
+// 鍒犻櫎浜哄憳钖祫鍙拌处
+export function monthlyStatisticsDelete(ids) {
+  return request({
+    url: "/compensationPerformance/delete",
+    method: "delete",
+    data: ids,
+  });
+}
+
+// 瀵煎嚭浜哄憳钖祫鍙拌处
+export function monthlyStatisticsExport(query) {
+  return request({
+    url: "/compensationPerformance/export",
+    method: "get",
+    params: query,
+    responseType: "blob",
+  });
+}
+
+// 浜哄憳鍒楄〃
+export function staffOnJobList(query) {
+  return request({
+    url: "/staff/staffOnJob/list",
+    method: "get",
+    params: query,
+  });
+}
diff --git a/src/views/personnelManagement/monthlyStatistics/components/formDia.vue b/src/views/personnelManagement/monthlyStatistics/components/formDia.vue
new file mode 100644
index 0000000..c67fdd6
--- /dev/null
+++ b/src/views/personnelManagement/monthlyStatistics/components/formDia.vue
@@ -0,0 +1,318 @@
+<template>
+  <el-dialog v-model="dialogVisible"
+             :title="title"
+             width="700px"
+             :close-on-click-modal="false">
+    <el-form ref="formRef"
+             :model="form"
+             :rules="rules"
+             label-width="140px"
+             label-position="top">
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="缁熻鏈堜唤"
+                        prop="payDate">
+            <el-date-picker v-model="form.payDate"
+                            type="month"
+                            value-format="YYYY-MM"
+                            format="YYYY-MM"
+                            placeholder="璇烽�夋嫨鏈堜唤"
+                            style="width: 100%"
+                            :disabled="operationType === 'view'" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="濮撳悕"
+                        prop="staffId">
+            <el-select v-model="form.staffId"
+                       placeholder="璇烽�夋嫨鍛樺伐"
+                       style="width: 100%"
+                       :disabled="operationType === 'view'">
+              <el-option v-for="item in userList"
+                         :key="item.id"
+                         :label="item.staffName"
+                         :value="item.id" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="鍩烘湰宸ヨ祫"
+                        prop="basicSalary">
+            <el-input v-model="form.basicSalary"
+                      type="number"
+                      placeholder="璇疯緭鍏ュ熀鏈伐璧�"
+                      :disabled="operationType === 'view'" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="璁′欢宸ヨ祫"
+                        prop="pieceworkSalary">
+            <el-input v-model="form.pieceworkSalary"
+                      type="number"
+                      placeholder="璇疯緭鍏ヨ浠跺伐璧�"
+                      :disabled="operationType === 'view'" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="璁℃椂宸ヨ祫"
+                        prop="hourlySalary">
+            <el-input v-model="form.hourlySalary"
+                      type="number"
+                      placeholder="璇疯緭鍏ヨ鏃跺伐璧�"
+                      :disabled="operationType === 'view'" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="鍏朵粬鏀跺叆"
+                        prop="otherIncome">
+            <el-input v-model="form.otherIncome"
+                      type="number"
+                      placeholder="璇疯緭鍏ュ叾浠栨敹鍏�"
+                      :disabled="operationType === 'view'" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="绀句繚涓汉"
+                        prop="socialSecurityIndividuals">
+            <el-input v-model="form.socialSecurityIndividuals"
+                      type="number"
+                      placeholder="璇疯緭鍏ョぞ淇濅釜浜�"
+                      :disabled="operationType === 'view'" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="鍏Н閲戜釜浜�"
+                        prop="providentFundIndividuals">
+            <el-input v-model="form.providentFundIndividuals"
+                      type="number"
+                      placeholder="璇疯緭鍏ュ叕绉噾涓汉"
+                      :disabled="operationType === 'view'" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="涓汉鎵�寰楃◣"
+                        prop="personalIncomeTax">
+            <el-input v-model="form.personalIncomeTax"
+                      type="number"
+                      placeholder="璇疯緭鍏ヤ釜浜烘墍寰楃◣"
+                      :disabled="operationType === 'view'" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="鍏朵粬鎵f"
+                        prop="otherDeductions">
+            <el-input v-model="form.otherDeductions"
+                      type="number"
+                      placeholder="璇疯緭鍏ュ叾浠栨墸娆�"
+                      :disabled="operationType === 'view'" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row :gutter="20">
+        <el-col :span="24">
+          <el-form-item label="澶囨敞"
+                        prop="remark">
+            <el-input v-model="form.remark"
+                      type="textarea"
+                      placeholder="璇疯緭鍏ュ娉�"
+                      :rows="3"
+                      :disabled="operationType === 'view'" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="dialogVisible = false">鍙栨秷</el-button>
+        <el-button type="primary"
+                   @click="submitForm"
+                   v-if="operationType !== 'view'">
+          纭畾
+        </el-button>
+      </span>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+  import { ref, reactive, computed, onMounted } from "vue";
+  import { ElMessage } from "element-plus";
+  import {
+    monthlyStatisticsAdd,
+    monthlyStatisticsUpdate,
+    staffOnJobList,
+  } from "@/api/personnelManagement/monthlyStatistics.js";
+
+  const props = defineProps({
+    modelValue: {
+      type: Boolean,
+      default: false,
+    },
+    operationType: {
+      type: String,
+      default: "add",
+    },
+    row: {
+      type: Object,
+      default: () => ({}),
+    },
+  });
+
+  const emit = defineEmits(["update:modelValue", "close"]);
+
+  const dialogVisible = computed({
+    get: () => props.modelValue,
+    set: val => emit("update:modelValue", val),
+  });
+
+  const title = computed(() => {
+    if (props.operationType === "add") return "鏂板钖祫鍙拌处";
+    if (props.operationType === "edit") return "缂栬緫钖祫鍙拌处";
+    return "鏌ョ湅钖祫鍙拌处";
+  });
+
+  const formRef = ref();
+  const form = reactive({
+    id: "",
+    payDate: "",
+    staffId: "",
+    basicSalary: 0,
+    pieceworkSalary: 0,
+    hourlySalary: 0,
+    otherIncome: 0,
+    socialSecurityIndividuals: 0,
+    providentFundIndividuals: 0,
+    personalIncomeTax: 0,
+    otherDeductions: 0,
+    payableWages: 0,
+    deductibleWages: 0,
+    actualWages: 0,
+    remark: "",
+  });
+
+  const rules = {
+    payDate: [{ required: true, message: "璇烽�夋嫨缁熻鏈堜唤", trigger: "change" }],
+    staffId: [{ required: true, message: "璇烽�夋嫨鍛樺伐", trigger: "change" }],
+    basicSalary: [{ required: true, message: "璇疯緭鍏ュ熀鏈伐璧�", trigger: "blur" }],
+  };
+
+  const userList = ref([]);
+
+  const loadUserList = () => {
+    // userListNoPage().then(res => {
+    //   userList.value = res.data || [];
+    // });
+    staffOnJobList().then(res => {
+      userList.value = res.data || [];
+    });
+  };
+
+  const openDialog = (type, row) => {
+    // 閲嶇疆琛ㄥ崟
+    Object.assign(form, {
+      id: "",
+      payDate: "",
+      staffId: "",
+      basicSalary: 0,
+      pieceworkSalary: 0,
+      hourlySalary: 0,
+      otherIncome: 0,
+      socialSecurityIndividuals: 0,
+      providentFundIndividuals: 0,
+      personalIncomeTax: 0,
+      otherDeductions: 0,
+      payableWages: 0,
+      deductibleWages: 0,
+      actualWages: 0,
+      remark: "",
+    });
+
+    if (type === "add") {
+      dialogVisible.value = true;
+    } else if (type === "edit" || type === "view") {
+      if (row && row.id) {
+        Object.assign(form, row);
+        dialogVisible.value = true;
+      }
+    }
+  };
+
+  const submitForm = () => {
+    formRef.value.validate(valid => {
+      if (valid) {
+        form.basicSalary = Number(form.basicSalary);
+        form.pieceworkSalary = Number(form.pieceworkSalary);
+        form.hourlySalary = Number(form.hourlySalary);
+        form.otherIncome = Number(form.otherIncome);
+        form.socialSecurityIndividuals = Number(form.socialSecurityIndividuals);
+        form.providentFundIndividuals = Number(form.providentFundIndividuals);
+        form.personalIncomeTax = Number(form.personalIncomeTax);
+        form.otherDeductions = Number(form.otherDeductions);
+
+        // 璁$畻搴斿彂宸ヨ祫銆佸簲鎵e伐璧勫拰瀹炲彂宸ヨ祫
+        const payableWages =
+          form.basicSalary +
+          form.pieceworkSalary +
+          form.hourlySalary +
+          form.otherIncome;
+        const deductibleWages =
+          form.socialSecurityIndividuals +
+          form.providentFundIndividuals +
+          form.personalIncomeTax +
+          form.otherDeductions;
+        const actualWages = payableWages - deductibleWages;
+
+        const submitData = {
+          ...form,
+          payableWages,
+          deductibleWages,
+          actualWages,
+        };
+
+        if (props.operationType === "add") {
+          monthlyStatisticsAdd(submitData).then(res => {
+            if (res.code === 200) {
+              ElMessage.success("鏂板鎴愬姛");
+              dialogVisible.value = false;
+              emit("close");
+            } else {
+              ElMessage.error(res.msg || "鏂板澶辫触");
+            }
+          });
+        } else if (props.operationType === "edit") {
+          monthlyStatisticsUpdate(submitData).then(res => {
+            if (res.code === 200) {
+              ElMessage.success("鏇存柊鎴愬姛");
+              dialogVisible.value = false;
+              emit("close");
+            } else {
+              ElMessage.error(res.msg || "鏇存柊澶辫触");
+            }
+          });
+        }
+      }
+    });
+  };
+
+  onMounted(() => {
+    loadUserList();
+  });
+
+  defineExpose({
+    openDialog,
+  });
+</script>
+
+<style scoped>
+  .dialog-footer {
+    text-align: right;
+  }
+</style>
\ No newline at end of file
diff --git a/src/views/personnelManagement/monthlyStatistics/index.vue b/src/views/personnelManagement/monthlyStatistics/index.vue
new file mode 100644
index 0000000..741a4b7
--- /dev/null
+++ b/src/views/personnelManagement/monthlyStatistics/index.vue
@@ -0,0 +1,303 @@
+<template>
+  <div class="app-container">
+    <div class="search_form">
+      <div>
+        <span class="search_title">濮撳悕锛�</span>
+        <el-input v-model="searchForm.staffName"
+                  style="width: 240px"
+                  placeholder="璇疯緭鍏ュ鍚嶆悳绱�"
+                  @change="handleQuery"
+                  clearable
+                  :prefix-icon="Search" />
+        <span class="search_title ml10">鏈堜唤锛�</span>
+        <el-date-picker v-model="searchForm.payDateStr"
+                        type="month"
+                        @change="handleQuery"
+                        value-format="YYYY-MM"
+                        format="YYYY-MM"
+                        placeholder="璇烽�夋嫨鏈堜唤"
+                        style="width: 240px"
+                        clearable />
+        <el-button type="primary"
+                   @click="handleQuery"
+                   style="margin-left: 10px">
+          鎼滅储
+        </el-button>
+      </div>
+      <div>
+        <el-button @click="handleExport"
+                   style="margin-right: 10px">瀵煎嚭</el-button>
+        <el-button type="primary"
+                   @click="openForm('add')">鏂板鍙拌处</el-button>
+        <el-button type="danger"
+                   plain
+                   @click="handleDelete">鍒犻櫎</el-button>
+      </div>
+    </div>
+    <div class="table_list">
+      <PIMTable rowKey="id"
+                :column="tableColumn"
+                :tableData="tableData"
+                :page="page"
+                :isSelection="true"
+                @selection-change="handleSelectionChange"
+                :tableLoading="tableLoading"
+                @pagination="pagination"
+                :total="page.total"></PIMTable>
+    </div>
+    <form-dia v-model="dialogVisible"
+              :operation-type="operationType"
+              :row="currentRow"
+              ref="formDia"
+              @close="handleQuery"></form-dia>
+  </div>
+</template>
+
+<script setup>
+  import { Search } from "@element-plus/icons-vue";
+  import {
+    onMounted,
+    ref,
+    reactive,
+    toRefs,
+    getCurrentInstance,
+    nextTick,
+  } from "vue";
+  import { ElMessageBox } from "element-plus";
+  import dayjs from "dayjs";
+  import FormDia from "./components/formDia.vue";
+  import {
+    monthlyStatisticsListPage,
+    monthlyStatisticsDelete,
+  } from "@/api/personnelManagement/monthlyStatistics.js";
+
+  const data = reactive({
+    searchForm: {
+      staffName: "",
+      payDateStr: "",
+    },
+  });
+
+  const { searchForm } = toRefs(data);
+
+  const tableColumn = ref([
+    {
+      label: "鍛樺伐濮撳悕",
+      prop: "staffName",
+    },
+    {
+      label: "閮ㄩ棬",
+      prop: "deptName",
+      width: 140,
+    },
+    {
+      label: "鏈堜唤",
+      prop: "payDate",
+    },
+    {
+      label: "鍩烘湰宸ヨ祫",
+      prop: "basicSalary",
+    },
+    {
+      label: "璁′欢宸ヨ祫",
+      prop: "pieceworkSalary",
+    },
+    {
+      label: "璁℃椂宸ヨ祫",
+      prop: "hourlySalary",
+    },
+    {
+      label: "鍏朵粬鏀跺叆",
+      prop: "otherIncome",
+    },
+    {
+      label: "绀句繚涓汉",
+      prop: "socialSecurityIndividuals",
+    },
+    {
+      label: "鍏Н閲戜釜浜�",
+      prop: "providentFundIndividuals",
+      width: 140,
+    },
+    {
+      label: "宸ヨ祫涓◣",
+      prop: "personalIncomeTax",
+    },
+    {
+      label: "鍏朵粬鏀嚭",
+      prop: "otherDeductions",
+    },
+    {
+      label: "搴斿彂宸ヨ祫",
+      prop: "payableWages",
+    },
+    {
+      label: "搴旀墸宸ヨ祫",
+      prop: "deductibleWages",
+    },
+    {
+      label: "瀹炲彂宸ヨ祫",
+      prop: "actualWages",
+    },
+    {
+      label: "澶囨敞",
+      prop: "remark",
+      width: 150,
+    },
+    {
+      dataType: "action",
+      label: "鎿嶄綔",
+      align: "center",
+      fixed: "right",
+      width: 220,
+      operation: [
+        {
+          name: "缂栬緫",
+          type: "text",
+          clickFun: row => {
+            openForm("edit", row);
+          },
+        },
+        // {
+        //   name: "鏌ョ湅",
+        //   type: "text",
+        //   clickFun: row => {
+        //     openForm("view", row);
+        //   },
+        // },
+      ],
+    },
+  ]);
+
+  const tableData = ref([]);
+  const selectedRows = ref([]);
+  const tableLoading = ref(false);
+  const page = reactive({
+    current: 1,
+    size: 100,
+    total: 0,
+  });
+
+  const formDia = ref();
+  const dialogVisible = ref(false);
+  const operationType = ref("add");
+  const currentRow = ref({});
+  const { proxy } = getCurrentInstance();
+
+  // 鏌ヨ鍒楄〃
+  /** 鎼滅储鎸夐挳鎿嶄綔 */
+  const handleQuery = () => {
+    page.current = 1;
+    getList();
+  };
+
+  const pagination = obj => {
+    page.current = obj.page;
+    page.size = obj.limit;
+    getList();
+  };
+
+  const getList = () => {
+    tableLoading.value = true;
+    monthlyStatisticsListPage({ ...page, ...searchForm.value })
+      .then(res => {
+        tableLoading.value = false;
+        tableData.value = res.data.records;
+        page.total = res.data.total;
+      })
+      .catch(err => {
+        tableLoading.value = false;
+      });
+  };
+
+  // 琛ㄦ牸閫夋嫨鏁版嵁
+  const handleSelectionChange = selection => {
+    selectedRows.value = selection;
+  };
+
+  // 鎵撳紑寮规
+  const openForm = (type, row) => {
+    operationType.value = type;
+    currentRow.value = row || {};
+    dialogVisible.value = true;
+    nextTick(() => {
+      formDia.value?.openDialog(type, row);
+    });
+  };
+
+  // 鍒犻櫎
+  const handleDelete = () => {
+    let ids = [];
+    if (selectedRows.value.length > 0) {
+      ids = selectedRows.value.map(item => item.id);
+    } else {
+      proxy.$modal.msgWarning("璇烽�夋嫨鏁版嵁");
+      return;
+    }
+    ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚垹闄わ紝鏄惁纭鍒犻櫎锛�", "鍒犻櫎", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        monthlyStatisticsDelete(ids).then(res => {
+          proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+          getList();
+        });
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
+      });
+  };
+
+  // 瀵煎嚭
+  const handleExport = () => {
+    ElMessageBox.confirm("鏄惁纭瀵煎嚭浜哄憳钖祫鍙拌处锛�", "瀵煎嚭", {
+      confirmButtonText: "纭",
+      cancelButtonText: "鍙栨秷",
+      type: "warning",
+    })
+      .then(() => {
+        proxy.download(
+          "/compensationPerformance/export",
+          { ...searchForm.value, ...page },
+          "浜哄憳钖祫鍙拌处.xlsx"
+        );
+      })
+      .catch(() => {
+        proxy.$modal.msg("宸插彇娑�");
+      });
+  };
+
+  onMounted(() => {
+    getList();
+  });
+</script>
+
+<style scoped>
+  .search_form {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 20px;
+    flex-wrap: wrap;
+    gap: 10px;
+  }
+
+  .search_title {
+    font-weight: 500;
+    margin-right: 5px;
+  }
+
+  .ml10 {
+    margin-left: 10px;
+  }
+
+  .table_list {
+    margin-top: 20px;
+  }
+
+  .dialog-footer {
+    text-align: right;
+  }
+</style>
\ No newline at end of file

--
Gitblit v1.9.3