From 7b936dc5e04df96d319e6895fe883cad8ff57dd4 Mon Sep 17 00:00:00 2001
From: zouyu <2723363702@qq.com>
Date: 星期二, 21 四月 2026 15:56:52 +0800
Subject: [PATCH] 奖惩记录功能调整并迁移到绩效模块

---
 src/views/CNAS/personnel/personnelInfo/index.vue                   |    6 
 src/views/performance/rewardAndPunishment/index.vue                |  549 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/views/system/user/index.vue                                    |   21 +
 src/views/performance/attendance/index.vue                         |    4 
 src/assets/icons/svg/jcjilu.svg                                    |    1 
 src/views/performance/attendance/components/staffClockInRecord.vue |    2 
 6 files changed, 575 insertions(+), 8 deletions(-)

diff --git a/src/assets/icons/svg/jcjilu.svg b/src/assets/icons/svg/jcjilu.svg
new file mode 100644
index 0000000..e13dda3
--- /dev/null
+++ b/src/assets/icons/svg/jcjilu.svg
@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1776757351956" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1675" xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16"><path d="M574.4 396.7c-1.8-9.8-4.3-22.1-4.3-22.1h76.2l130.2-249c2.8-6.3 2-13.6-2.1-19-4-5.9-10.7-9.3-17.8-9.2h-481c-6.7 0.3-12.8 3.7-16.6 9.2-4.4 5.3-5.7 12.5-3.4 19l127.7 248.1h75.8s-2.5 12.9-4.6 22.7c-130.3 25.6-224.7 139-226.3 271.8 3.5 155.6 132.1 279.1 287.7 276.4 155.6 2.7 284.2-120.8 287.7-276.4-1.6-133.7-97.7-247.4-229.2-271.5z m-58.4 499c-128.5 2.3-234.5-99.9-237-228.4 2.5-128.4 108.6-230.6 237.1-228.2 128.4-2.4 234.5 99.7 237.1 228.2C750.6 795.8 644.5 898 516 895.7z m0 0" fill="#ffffff" p-id="1676"></path><path d="M330.3 668.2c1.8-100.1 84.1-180.1 184.2-179 48.4-0.9 95.1 17.5 130 51 34.8 33.6 54.9 79.6 55.8 128-2.7 100.4-85.3 180.2-185.8 179.3-99.9 0.1-181.6-79.5-184.2-179.3z m0 0" fill="#ffffff" p-id="1677"></path></svg>
\ No newline at end of file
diff --git a/src/views/CNAS/personnel/personnelInfo/index.vue b/src/views/CNAS/personnel/personnelInfo/index.vue
index b75b5a1..c5a4ac2 100644
--- a/src/views/CNAS/personnel/personnelInfo/index.vue
+++ b/src/views/CNAS/personnel/personnelInfo/index.vue
@@ -66,10 +66,10 @@
                                 :departId="departId"
                                 :isDepartment="isDepartment"></job-responsibilities>
         </el-tab-pane>
-        <el-tab-pane label="濂栨儵璁板綍" name="濂栨儵璁板綍">
+        <!-- <el-tab-pane label="濂栨儵璁板綍" name="濂栨儵璁板綍">
           <rewardPunishmentRecord v-if="activeName === '濂栨儵璁板綍'"
-                                  :departId="departId" :isDepartment="isDepartment"></rewardPunishmentRecord>
-        </el-tab-pane>
+                                  :departId="departId" :isDepartment="isDepartment"></rewardPunishmentRecord> -->
+        <!-- </el-tab-pane> -->
         <el-tab-pane label="鍩硅璁板綍" name="鍩硅璁板綍">
           <training-record v-if="activeName === '鍩硅璁板綍'" ref="trainingRecord"
                            :departId="departId"
diff --git a/src/views/performance/attendance/components/staffClockInRecord.vue b/src/views/performance/attendance/components/staffClockInRecord.vue
index 54a5ded..5873c9a 100644
--- a/src/views/performance/attendance/components/staffClockInRecord.vue
+++ b/src/views/performance/attendance/components/staffClockInRecord.vue
@@ -333,7 +333,7 @@
     getDataSourceTypeTag(type) {
       const tagMap = {
         0: 'success',
-        1: '',
+        1: 'info',
       };
       return tagMap[type] || '';
     },
diff --git a/src/views/performance/attendance/index.vue b/src/views/performance/attendance/index.vue
index c8784c4..c5865eb 100644
--- a/src/views/performance/attendance/index.vue
+++ b/src/views/performance/attendance/index.vue
@@ -255,7 +255,7 @@
 
 <script>
 import StaffClockInRecord from "./components/staffClockInRecord.vue";
-import {selectAllUser} from '@/api/system/user'
+import {selectUserListByPerformance} from '@/api/system/user'
 import {
   pageAttendanceRecord,
   checkDutyDate,
@@ -622,7 +622,7 @@
     },
     //鏌ヨ鐢ㄦ埛鍒楄〃
     getUserList(){
-      selectAllUser().then(res=>{
+      selectUserListByPerformance().then(res=>{
         this.userList = res.data
       }).catch(error=>{
         console.error(error)
diff --git a/src/views/performance/rewardAndPunishment/index.vue b/src/views/performance/rewardAndPunishment/index.vue
new file mode 100644
index 0000000..ace57bc
--- /dev/null
+++ b/src/views/performance/rewardAndPunishment/index.vue
@@ -0,0 +1,549 @@
+<!-- 濂栨儵璁板綍 -->
+<template>
+  <div class="app-container">
+    <div style="display: flex; justify-content: space-between">
+      <div style="display: flex">
+        <div
+          style="
+            margin-bottom: 18px;
+            margin-right: 10px;
+            display: flex;
+            align-items: center;
+            line-height: 32px;
+          "
+        >
+          <span
+            style="
+              width: 48px;
+              font-size: 14px;
+              font-weight: 700;
+              color: #606266;
+            "
+            >濮撳悕</span
+          >
+          <el-input
+            size="small"
+            placeholder="璇疯緭鍏�"
+            clearable
+            v-model="search.userName"
+            @keyup.enter.native="getPersonnelTraining()"
+          ></el-input>
+        </div>
+        <div
+          style="
+            margin-bottom: 18px;
+            margin-right: 10px;
+            display: flex;
+            align-items: center;
+            line-height: 32px;
+          "
+        >
+          <span
+            style="
+              width: 88px;
+              font-size: 14px;
+              font-weight: 700;
+              color: #606266;
+            "
+            >濂栨儵鏃ユ湡</span
+          >
+          <el-date-picker
+            v-model="search.searchTimeList"
+            :picker-options="pickerOptions"
+            align="right"
+            clearable
+            @change="getPersonnelTraining()"
+            end-placeholder="缁撴潫鏃ユ湡"
+            format="yyyy-MM-dd HH:mm:ss"
+            range-separator="鑷�"
+            size="small"
+            start-placeholder="寮�濮嬫棩鏈�"
+            style="width: 100%"
+            type="daterange"
+            unlink-panels
+            :default-time="['00:00:00', '23:59:59']"
+            value-format="yyyy-MM-dd HH:mm:ss"
+          >
+          </el-date-picker>
+        </div>
+        <div style="line-height: 30px">
+          <el-button size="mini" type="primary" @click="getPersonnelTraining()"
+            >鏌ヨ</el-button
+          >
+          <el-button size="mini" @click="refreshTable()">閲嶇疆</el-button>
+          <el-button size="small" type="primary" @click="addRow"
+            >鏂板</el-button
+          >
+          <el-button
+            :loading="outLoading"
+            size="small"
+            type="success"
+            @click="handleDown"
+            >瀵煎嚭</el-button
+          >
+        </div>
+      </div>
+    </div>
+    <div class="table">
+      <el-table
+        :data="tableData"
+        v-loading="tableLoading"
+        :header-cell-style="{
+          background: '#f8f8f9',
+          color: '#515a6e',
+          textAlign: 'center',
+        }"
+        :cell-style="{ textAlign: 'center' }"
+        border
+        height="66.5vh"
+        style="width: 100%"
+      >
+        <el-table-column label="搴忓彿" type="index" width="80"></el-table-column>
+        <el-table-column
+          label="鍛樺伐缂栧彿"
+          min-width="120"
+          width="120"
+          prop="account"
+        ></el-table-column>
+        <el-table-column
+          label="鍛樺伐濮撳悕"
+          min-width="180"
+          width="120"
+          prop="userName"
+        ></el-table-column>
+        <el-table-column
+          label="濂栨儵鍚嶇О"
+          min-width="140"
+          width="140"
+          prop="rewardPunishName"
+        ></el-table-column>
+        <el-table-column
+          label="濂栨儵鏃堕棿"
+          min-width="160"
+          width="160"
+          prop="rewardPunishTime"
+        ></el-table-column>
+        <el-table-column
+          label="濂栨儵璁″垎"
+          min-width="120"
+          width="120"
+          prop="rewardPunishScore"
+        ></el-table-column>
+        <el-table-column
+          label="濂栨儵閲戦"
+          min-width="120"
+          width="120"
+          prop="rewardPunishSum"
+        ></el-table-column>
+        <el-table-column
+          label="濂栨儵鍐呭"
+          min-width="160"
+          prop="rewardPunishContent"
+          show-overflow-tooltip
+        ></el-table-column>
+        <el-table-column
+          label="鍒涘缓浜�"
+          min-width="120"
+          width="120"
+          prop="createUserName"
+        ></el-table-column>
+        <el-table-column
+          label="鍒涘缓鏃堕棿"
+          min-width="160"
+          width="160"
+          prop="createTime"
+        ></el-table-column>
+        <el-table-column
+          label="鏇存柊浜�"
+          min-width="120"
+          width="120"
+          prop="updateUserName"
+        ></el-table-column>
+        <el-table-column
+          label="鏇存柊鏃堕棿"
+          min-width="160"
+          width="160"
+          prop="updateTime"
+        ></el-table-column>
+        <el-table-column fixed="right" label="鎿嶄綔" width="100" align="center">
+          <template v-slot="scope">
+            <el-button size="small" type="text" @click="editForm(scope.row)"
+              >缂栬緫</el-button
+            >
+            <el-button
+              size="small"
+              type="text"
+              style="color: #f56c6c"
+              @click="deleteRow(scope.row)"
+              >鍒犻櫎</el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+      <el-pagination
+        :current-page="1"
+        :page-size="search.size"
+        :page-sizes="[10, 20, 30, 50, 100]"
+        :total="search.total"
+        layout="->,total, sizes, prev, pager, next, jumper"
+        background
+        style="margin-top: 10px"
+        @size-change="handleSizeChange"
+        @current-change="handleCurrentChange"
+      >
+      </el-pagination>
+    </div>
+    <el-dialog
+      :visible.sync="dialogVisible"
+      title="濂栨儵璁板綍"
+      width="50%"
+      @open="getUserList"
+    >
+      <div style="height: 40vh">
+        <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+          <el-row>
+            <el-col :span="12">
+              <el-form-item label="鍛樺伐缂栧彿">
+                <el-input
+                  v-model="form.account"
+                  disabled
+                  size="small"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="鍛樺伐濮撳悕" prop="userId">
+                <el-select
+                  :disabled="form.id"
+                  v-model="form.userId"
+                  placeholder="璇烽�夋嫨鍛樺伐濮撳悕"
+                  size="small"
+                  style="width: 100%"
+                  @change="selectUserChange"
+                >
+                  <el-option
+                    v-for="item in responsibleOptions"
+                    :key="item.id"
+                    :label="item.name"
+                    :value="item.id"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row>
+            <el-col :span="12">
+              <el-form-item label="濂栨儵鍚嶇О" prop="rewardPunishName">
+                <el-input
+                  placeholder="璇疯緭鍏ュ鎯╁悕绉�"
+                  type="textarea"
+                  :rows="1"
+                  v-model="form.rewardPunishName"
+                  size="small"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="濂栨儵鏃堕棿" prop="rewardPunishTime">
+                <el-date-picker
+                  v-model="form.rewardPunishTime"
+                  format="yyyy-MM-dd"
+                  placeholder="閫夋嫨鏃ユ湡"
+                  size="small"
+                  style="width: 100%"
+                  type="date"
+                  value-format="yyyy-MM-dd"
+                >
+                </el-date-picker>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row>
+            <el-col :span="12">
+              <el-form-item label="濂栨儵璁″垎" prop="rewardPunishScore">
+                <el-input-number
+                  controls-position="right"
+                  :precision="1"
+                  style="width: 100%"
+                  v-model="form.rewardPunishScore"
+                  size="small"
+                  placeholder="璇疯緭鍏ュ鎯╄鍒�"
+                ></el-input-number>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="濂栨儵閲戦" prop="rewardPunishSum">
+                <el-input-number
+                  controls-position="right"
+                  :precision="2"
+                  style="width: 100%"
+                  :min="0"
+                  v-model="form.rewardPunishSum"
+                  size="small"
+                  placeholder="璇疯緭鍏ュ鎯╅噾棰�"
+                ></el-input-number>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row>
+            <el-col :span="24">
+              <el-form-item label="濂栨儵鍐呭">
+                <el-input
+                  v-model="form.rewardPunishContent"
+                  :rows="2"
+                  size="small"
+                  type="textarea"
+                  placeholder="璇疯緭鍏ュ鎯╁唴瀹�"
+                ></el-input>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </el-form>
+      </div>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="dialogVisible = false">鍙� 娑�</el-button>
+        <el-button type="primary" @click="saveOrUpdate">纭� 瀹�</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {
+  addOrUpdateRewardPunishment,
+  deleteRewardPunishment,
+  rewardPunishmentExport,
+  rewardPunishmentPage,
+} from "@/api/cnas/personal/personRewardPunishmentRecord";
+import { selectUserListByPerformance } from "@/api/system/user";
+import { transformExcel } from "@/utils/file";
+
+export default {
+  data() {
+    return {
+      tableData: [],
+      tableLoading: false,
+      search: {
+        size: 20,
+        current: 1,
+        total: 0,
+        userName: "",
+        searchTimeList: [],
+      },
+      form: {
+        account: "",
+        userId: null,
+        rewardPunishScore: null,
+        rewardPunishName: "",
+        rewardPunishTime: null,
+        rewardPunishSum: null,
+        rewardPunishContent: "",
+      },
+      dialogVisible: false,
+      outLoading: false,
+      pickerOptions: {
+        shortcuts: [
+          {
+            text: "鏈�杩戜竴鍛�",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
+              picker.$emit("pick", [start, end]);
+            },
+          },
+          {
+            text: "鏈�杩戜竴涓湀",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
+              picker.$emit("pick", [start, end]);
+            },
+          },
+          {
+            text: "鏈�杩戜笁涓湀",
+            onClick(picker) {
+              const end = new Date();
+              const start = new Date();
+              start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
+              picker.$emit("pick", [start, end]);
+            },
+          },
+        ],
+      },
+      rules: {
+        userId: [
+          {
+            required: true,
+            message: "璇烽�夋嫨鍛樺伐",
+            trigger: "change",
+          },
+        ],
+        rewardPunishScore: [
+          {
+            required: true,
+            message: "璇疯緭鍏ュ鎯╄鍒�",
+            trigger: "blur",
+          },
+        ],
+        rewardPunishName: [
+          {
+            required: true,
+            message: "璇疯緭鍏ュ鎯╁悕绉�",
+            trigger: "blur",
+          },
+        ],
+        rewardPunishTime: [
+          {
+            required: true,
+            message: "璇疯緭鍏ュ鎯╂椂闂�",
+            trigger: "blur",
+          },
+        ],
+        rewardPunishSum: [
+          {
+            required: true,
+            message: "璇疯緭鍏ュ鎯╅噾棰�",
+            trigger: "blur",
+          },
+        ],
+      },
+      responsibleOptions: [],
+    };
+  },
+  mounted() {
+    this.getPersonnelTraining();
+    this.getUserList();
+  },
+  methods: {
+    refreshTable() {
+      this.search.userName = "";
+      this.search.searchTimeList = [];
+      this.$nextTick(() => {
+        this.getPersonnelTraining();
+      });
+    },
+    handleSizeChange(val) {
+      this.search.size = val;
+      this.getPersonnelTraining();
+    },
+    handleCurrentChange(val) {
+      this.search.current = val;
+      this.getPersonnelTraining();
+    },
+    async getPersonnelTraining() {
+      const params = {
+        userId: "",
+        departmentId: "",
+        current: this.search.curent,
+        size: this.search.size,
+        userName: this.search.userName,
+        startTime: this.search.searchTimeList && this.search.searchTimeList[0],
+        endTime: this.search.searchTimeList && this.search.searchTimeList[1],
+      };
+      this.tableLoading = true;
+      rewardPunishmentPage(params)
+        .then((res) => {
+          this.tableLoading = false;
+          this.tableData = res.data.records;
+          this.search.total = res.data.total;
+        })
+        .catch((err) => {
+          this.tableLoading = false;
+        });
+    },
+    addRow() {
+      this.dialogVisible = true;
+    },
+    handleDown() {
+      this.outLoading = true;
+      let params = {
+        userName: this.search.userName,
+      };
+      if (this.search.searchTimeList && this.search.searchTimeList.length > 0) {
+        params.startTime = this.search.searchTimeList[0];
+        params.endTime = this.search.searchTimeList[1];
+      }
+      rewardPunishmentExport(params).then((res) => {
+        transformExcel(res, "涓ぉ鑰愪笣璐ㄩ噺閮ㄥ鎯╄褰�.xlsx");
+        this.$message.success("瀵煎嚭鎴愬姛");
+        this.$nextTick(() => {
+          this.outLoading = false;
+        });
+      });
+    },
+    // 鑾峰彇璐熻矗浜轰俊鎭帴鍙�
+    getUserList() {
+      this.responsibleOptions = [];
+      selectUserListByPerformance({ isTestUser: true }).then((res) => {
+        if (res.code == 200) {
+          this.responsibleOptions = res.data;
+        }
+      });
+    },
+    selectUserChange(val) {
+      const index = this.responsibleOptions.findIndex(
+        (item) => item.id === val
+      );
+      console.log(val, index);
+      if (index > -1) {
+        this.form.userName = this.responsibleOptions[index].name;
+        this.form.account = this.responsibleOptions[index].account;
+      }
+    },
+    // 鎵撳紑琛ㄥ崟寮规
+    editForm(row) {
+      this.dialogVisible = true;
+      this.form = { ...row };
+    },
+    // 鎻愪氦琛ㄥ崟鏁版嵁
+    saveOrUpdate() {
+      this.$refs.form.validate(async (valid) => {
+        if (valid) {
+          addOrUpdateRewardPunishment(this.form).then((res) => {
+            this.dialogVisible = false;
+            this.$message.success("鎿嶄綔鎴愬姛");
+            this.getPersonnelTraining();
+          });
+        }
+      });
+    },
+    deleteRow(row) {
+      this.$confirm("鏄惁鍒犻櫎褰撳墠鏁版嵁?", "璀﹀憡", {
+        confirmButtonText: "纭畾",
+        cancelButtonText: "鍙栨秷",
+        type: "warning",
+      })
+        .then(() => {
+          deleteRewardPunishment({ id: row.id })
+            .then((res) => {
+              if (res.code === 500) {
+                return;
+              }
+              this.$message.success("鍒犻櫎鎴愬姛");
+              this.getPersonnelTraining();
+            })
+            .catch((e) => {
+              this.$message.error("鍒犻櫎澶辫触");
+            });
+        })
+        .catch(() => {});
+    },
+  },
+  watch: {
+    dialogVisible(newVal) {
+      if (newVal === false) {
+        this.form = {};
+        this.$refs.form.resetFields();
+      }
+    },
+  },
+};
+</script>
+<style scoped>
+.dateTime >>> .el-form-item__content {
+  width: 260px;
+}
+</style>
diff --git a/src/views/system/user/index.vue b/src/views/system/user/index.vue
index 5fcb8ba..7ff8d4e 100644
--- a/src/views/system/user/index.vue
+++ b/src/views/system/user/index.vue
@@ -48,6 +48,11 @@
           </div>
           <el-col>
             <el-table ref="dragTable" v-loading="loading" row-key="userId" :data="userList" :header-cell-style="{ background: '#f8f8f9', color: '#515a6e' }" border>
+              <el-table-column label="鎷栨嫿" align="center" width="60">
+                <template slot-scope="scope">
+                  <i class="el-icon-rank drag-handle" :data-user-id="scope.row.userId"></i>
+                </template>
+              </el-table-column>
               <el-table-column label="搴忓彿" align="center" type="index" />
               <el-table-column label="濮撳悕" align="center" key="nickName" prop="nickName" :show-overflow-tooltip="true" />
               <el-table-column label="璐﹀彿" align="center" key="userName" prop="userName" :show-overflow-tooltip="true" />
@@ -523,6 +528,7 @@
         ghostClass: 'sortable-ghost', // 鎷栨嫿鍗犱綅绗︽牱寮�
         chosenClass: 'sortable-chosen', // 閫変腑琛屾牱寮�
         dragClass: 'sortable-drag', // 鎷栨嫿鍏冪礌鏍峰紡
+        handle: '.drag-handle',
         disabled: this.loading || this.sortSaving,
         // 鎷栨嫿缁撴潫瑙﹀彂锛堟牳蹇冮�昏緫锛�
         onEnd: async({ oldIndex, newIndex }) => {
@@ -1000,8 +1006,19 @@
 </script>
 
 <style scoped lang="scss">
-:deep(.el-table__body-wrapper tbody tr) {
-  cursor: move;
+:deep(.drag-handle) {
+  cursor: grab;
+  color: #909399;
+  font-size: 16px;
+  display: inline-block;
+  user-select: none;
+}
+:deep(.drag-handle:hover) {
+  color: #409EFF;
+  cursor: grab;
+}
+:deep(.drag-handle:active) {
+  cursor: grabbing;
 }
 :deep(.sortable-ghost) {
   opacity: 0.8;

--
Gitblit v1.9.3