From aae0b49229d8798a2cc31a8449092e2db62e2407 Mon Sep 17 00:00:00 2001
From: zouyu <2723363702@qq.com>
Date: 星期四, 26 三月 2026 11:26:41 +0800
Subject: [PATCH] 绩效管理:人员考勤功能模块

---
 src/views/system/user/index.vue                                      |   55 +
 src/api/system/user.js                                               |    8 
 src/views/performance/attendance/index.vue                           |  644 +++++++++++++++++++++++++++++
 src/api/system/post.js                                               |    8 
 src/views/CNAS/resourceDemand/device/component/acquisitionConfig.vue |    2 
 package.json                                                         |    1 
 src/views/business/rawMaterialInspection/index.vue                   |   29 +
 src/views/performance/class/index.vue                                |   85 +--
 src/views/business/unpass/components/OAProcess.vue                   |    2 
 src/views/performance/attendance/components/staffClockInRecord.vue   |  395 +++++++++++++++++
 src/api/performance/attendance.js                                    |   66 +++
 11 files changed, 1,223 insertions(+), 72 deletions(-)

diff --git a/package.json b/package.json
index 6d2ff7c..3deefe9 100644
--- a/package.json
+++ b/package.json
@@ -44,6 +44,7 @@
     "big.js": "^6.2.2",
     "clipboard": "2.0.8",
     "core-js": "3.37.1",
+    "dayjs": "^1.11.20",
     "dom-to-image": "^2.6.0",
     "echarts": "5.4.0",
     "element-resize-detector": "^1.2.4",
diff --git a/src/api/performance/attendance.js b/src/api/performance/attendance.js
new file mode 100644
index 0000000..653f30b
--- /dev/null
+++ b/src/api/performance/attendance.js
@@ -0,0 +1,66 @@
+// 鐝鐩稿叧鎺ュ彛
+
+import request from "@/utils/request";
+
+// 缁╂晥绠$悊-浜哄憳鑰冨嫟-鏌ヨ浜哄憳鎵撳崱璁板綍
+export function getClockInRecord(query) {
+  return request({
+    url: "/staff/attendance/getClockInRecord",
+    method: "get",
+    params: query,
+  });
+}
+
+// 缁╂晥绠$悊-浜哄憳鑰冨嫟-鍒嗛〉鏌ヨ鑰冨嫟璁板綍
+export function pageAttendanceRecord(query) {
+  return request({
+    url: "/staff/attendance/pageAttendanceRecord",
+    method: "get",
+    params: query,
+  });
+}
+
+// 缁╂晥绠$悊-浜哄憳鑰冨嫟-鏍¢獙閫夋嫨鐨勮�冨嫟鏃堕棿鏄惁瀛樺湪鍚屼竴浜哄憳鐨勮�冨嫟璁板綍
+export function checkDutyDate(query) {
+  return request({
+    url: "/staff/attendance/checkDutyDate",
+    method: "get",
+    params: query,
+  });
+}
+
+// 缁╂晥绠$悊-浜哄憳鑰冨嫟-淇濆瓨鎴栨洿鏂拌�冨嫟璁板綍
+export function saveOrUpdateStaffAttendanceTrackingRecord(data) {
+  return request({
+    url: "/staff/attendance/saveOrUpdateStaffAttendanceTrackingRecord",
+    method: "post",
+    data: data,
+  });
+}
+
+// 缁╂晥绠$悊-浜哄憳鑰冨嫟-鍒犻櫎鑰冨嫟璁板綍
+export function removeStaffAttendanceTrackingRecord(ids) {
+  return request({
+    url: "/staff/attendance/removeStaffAttendanceTrackingRecord",
+    method: "delete",
+    data:ids
+  });
+}
+
+// 缁╂晥绠$悊-浜哄憳鑰冨嫟-鍚屾鑰冨嫟璁板綍
+export function syncAttendanceRecord(query) {
+  return request({
+    url: "/staff/attendance/syncAttendanceRecord",
+    method: "get",
+    params: query
+  });
+}
+
+// 缁╂晥绠$悊-浜哄憳鑰冨嫟-淇敼杩涘嚭璁板綍鐘舵��
+export function changeEnableReport(data) {
+  return request({
+    url: "/staff/attendance/changeEnableReport",
+    method: "post",
+    data: data
+  });
+}
diff --git a/src/api/system/post.js b/src/api/system/post.js
index 1a8e9ca..8c25bc7 100644
--- a/src/api/system/post.js
+++ b/src/api/system/post.js
@@ -9,6 +9,14 @@
   })
 }
 
+// 鑾峰彇宀椾綅閫夋嫨妗嗗垪琛�
+export function optionSelect() {
+  return request({
+    url: '/system/post/optionSelect',
+    method: 'get'
+  })
+}
+
 // 鏌ヨ宀椾綅璇︾粏
 export function getPost(postId) {
   return request({
diff --git a/src/api/system/user.js b/src/api/system/user.js
index ad8d3ce..850ab66 100644
--- a/src/api/system/user.js
+++ b/src/api/system/user.js
@@ -195,3 +195,11 @@
     params: query,
   });
 }
+
+// 鑾峰彇鐢ㄦ埛鍒楄〃
+export function selectAllUser() {
+  return request({
+    url: "/system/newUser/selectAllUser",
+    method: "get",
+  });
+}
diff --git a/src/views/CNAS/resourceDemand/device/component/acquisitionConfig.vue b/src/views/CNAS/resourceDemand/device/component/acquisitionConfig.vue
index 2abdc50..5f0165c 100644
--- a/src/views/CNAS/resourceDemand/device/component/acquisitionConfig.vue
+++ b/src/views/CNAS/resourceDemand/device/component/acquisitionConfig.vue
@@ -274,6 +274,8 @@
         { label: "txt", value: ".txt" },
         { label: "mysql", value: ".mysql" },
         { label: "mqtt", value: ".mqtt" },
+        { label: "sqlserver", value: ".sqlserver" },
+        { label: "serialPort", value: ".serialPort" },
         { label: "png", value: ".png" }
       ],
       spanList: [],
diff --git a/src/views/business/rawMaterialInspection/index.vue b/src/views/business/rawMaterialInspection/index.vue
index 7563e12..dfe0014 100644
--- a/src/views/business/rawMaterialInspection/index.vue
+++ b/src/views/business/rawMaterialInspection/index.vue
@@ -5,8 +5,7 @@
         <el-row>
           <el-form-item label="IFS鍩�" prop="contract">
             <el-select @keyup.enter.native="refreshTable" v-model="componentData.contract" clearable placeholder="璇烽�夋嫨" size="small">
-              <el-option label="ZTNS" value="ZTNS"/>
-              <el-option label="KJNS" value="KJNS"/>
+              <el-option v-for="(item,index) in contractList" :key="index" :label="item.label" :value="item.value"/>
             </el-select>
           </el-form-item>
           <el-form-item label="鎵瑰彿" prop="updateBatchNo">
@@ -119,12 +118,15 @@
       :visible.sync="declareDialogVisible" width="800px" @close="resetFormData">
       <el-form ref="declareObj" :inline="true" :model="declareObj" :rules="declareObjRules" label-width="130px"
         label-position="right">
-        <el-form-item class="declareObj-form-item" label="IFS鍩�:" prop="contract" style="width: calc(50% - 54px)">
-          <el-tag :type="declareObj.contract==='ZTNS'?'':'success'">{{declareObj.contract}}</el-tag>
-        </el-form-item>
         <el-form-item class="declareObj-form-item" label="璁㈠崟鍙�:" prop="orderNo">
           <el-input v-model="declareObj.orderNo" :disabled="declareType !== 'add'" class="addObj-info" clearable
-            placeholder="" size="small"></el-input>
+                    placeholder="" size="small"></el-input>
+        </el-form-item>
+        <el-form-item class="declareObj-form-item" label="IFS鍩�:" prop="contract">
+          <el-select v-model="declareObj.contract" v-if="declareType === 'add'" clearable size="small">
+            <el-option v-for="(item,index) in contractList" :key="index" :label="item.label" :value="item.value"/>
+          </el-select>
+          <el-tag v-else :type="declareObj.contract==='ZTNS'?'':'success'">{{declareObj.contract}}</el-tag>
         </el-form-item>
         <el-form-item class="declareObj-form-item" label="闆朵欢鍙�:" prop="partNo">
           <el-input v-model="declareObj.partNo" :disabled="declareType !== 'add'" class="addObj-info" clearable
@@ -747,6 +749,16 @@
       upLoading: false,
       orderTypeList: [],
       materialPropList: [],
+      contractList:[
+        {
+          label:"ZTNS",
+          value:"ZTNS"
+        },
+        {
+          label:"KJNS",
+          value:"KJNS"
+        },
+      ]
     }
   },
   mounted() {
@@ -1110,7 +1122,9 @@
               id: this.declareObj.id,
               updateBatchNo: this.declareObj.updateBatchNo,
               orderType: this.declareObj.orderType,
-              materialProp: this.declareObj.materialProp
+              materialProp: this.declareObj.materialProp,
+              partNo: this.declareObj.partNo,
+              contract: this.declareObj.contract
             }).then(res => {
               if (res.code === 200) {
                 this.declareDialogVisible = false
@@ -1195,6 +1209,7 @@
         buyUnitMeas: '', // 鍗曚綅
         isExpire: '', // 鍗曚綅
         orderType: null, // 閿�鍞鍗曞垎绫�
+        materialProp: null, // 鐗╂枡灞炴��
       }
     }
   },
diff --git a/src/views/business/unpass/components/OAProcess.vue b/src/views/business/unpass/components/OAProcess.vue
index eba813d..4becedd 100644
--- a/src/views/business/unpass/components/OAProcess.vue
+++ b/src/views/business/unpass/components/OAProcess.vue
@@ -105,7 +105,7 @@
         },
         {
           id: 6,
-          name: "6璐ㄩ噺閮�",
+          name: "6璐ㄩ噺閮ㄥ鐞嗘剰瑙�",
           info: "",
           time: "",
           operator: "",
diff --git a/src/views/performance/attendance/components/staffClockInRecord.vue b/src/views/performance/attendance/components/staffClockInRecord.vue
new file mode 100644
index 0000000..c1860c2
--- /dev/null
+++ b/src/views/performance/attendance/components/staffClockInRecord.vue
@@ -0,0 +1,395 @@
+<script>
+import {getClockInRecord,changeEnableReport} from '@/api/performance/attendance'
+
+export default {
+  name: "staffClockInRecord",
+  props: {
+    queryParams: {
+      type: Object,
+      default: () => ({
+        personCode:null,//浜哄憳缂栧彿
+        swingDate:null,//鍒峰崱鏃堕棿
+        shiftId:null//鐝id
+      }),
+    },
+  },
+  data() {
+    return {
+      loading: false,
+      tableData: [],
+      //杩涘嚭闂ㄧ被鍨嬪垪琛�
+      enterOrExitList:[
+        {
+          label:'杩涢棬',
+          value:1
+        },
+        {
+          label:'鍑洪棬',
+          value:2
+        },
+        {
+          label:'杩�/鍑洪棬',
+          value:3
+        }
+      ],
+      //寮�闂ㄧ被鍨嬪垪琛�
+      openTypeList:[
+        {
+          "value": 42,
+          "label": "鍚堟硶瀵嗙爜寮�闂�"
+        },
+        {
+          "value": 43,
+          "label": "闈炴硶瀵嗙爜寮�闂�"
+        },
+        {
+          "value": 45,
+          "label": "鍚堟硶鎸囩汗寮�闂�"
+        },
+        {
+          "value": 46,
+          "label": "闈炴硶鎸囩汗寮�闂�"
+        },
+        {
+          "value": 48,
+          "label": "杩滅▼寮�闂�"
+        },
+        {
+          "value": 49,
+          "label": "鎸夐挳寮�闂�"
+        },
+        {
+          "value": 50,
+          "label": "閽ュ寵寮�闂�"
+        },
+        {
+          "value": 51,
+          "label": "鍚堟硶鍒峰崱寮�闂�"
+        },
+        {
+          "value": 52,
+          "label": "闈炴硶鍒峰崱寮�闂�"
+        },
+        {
+          "value": 53,
+          "label": "闂ㄧ浜嬩欢"
+        },
+        {
+          "value": 54,
+          "label": "寮傚父寮�闂�"
+        },
+        {
+          "value": 55,
+          "label": "寮傚父鍏抽棬"
+        },
+        {
+          "value": 56,
+          "label": "姝e父鍏抽棬"
+        },
+        {
+          "value": 57,
+          "label": "姝e父寮�闂�"
+        },
+        {
+          "value": 59,
+          "label": "瀵硅璇锋眰浜嬩欢"
+        },
+        {
+          "value": 61,
+          "label": "浜鸿劯鍚堟硶寮�闂�"
+        },
+        {
+          "value": 62,
+          "label": "浜鸿劯闈炴硶寮�闂�"
+        },
+        {
+          "value": 1421,
+          "label": "RFID鏈夋簮鍚堟硶"
+        },
+        {
+          "value": 1422,
+          "label": "RFID鏃犳簮鍚堟硶"
+        },
+        {
+          "value": 1423,
+          "label": "RFID鏈夋簮闈炴硶"
+        },
+        {
+          "value": 1424,
+          "label": "RFID鏃犳簮闈炴硶"
+        },
+        {
+          "value": 1433,
+          "label": "榛戝悕鍗曚簨浠�"
+        },
+        {
+          "value": 1436,
+          "label": "浜鸿瘉鍚堟硶寮�闂�"
+        },
+        {
+          "value": 1437,
+          "label": "浜鸿瘉闈炴硶寮�闂�"
+        },
+        {
+          "value": 1438,
+          "label": "浜鸿瘉鍜岃韩浠借瘉闈炴硶寮�闂�"
+        },
+        {
+          "value": 1439,
+          "label": "浜鸿瘉鍜岃韩浠借瘉鍚堟硶寮�闂�"
+        },
+        {
+          "value": 1448,
+          "label": "RFID鎰熷簲浜嬩欢"
+        },
+        {
+          "value": 1449,
+          "label": "RFID闈炴硶鎰熷簲浜嬩欢"
+        },
+        {
+          "value": 1450,
+          "label": "RFID鎸夐敭浜嬩欢"
+        },
+        {
+          "value": 1451,
+          "label": "RFID闈炴硶鎸夐敭浜嬩欢"
+        },
+        {
+          "value": 1455,
+          "label": "鍏堝埛鍗″悗瀵嗙爜鍚堟硶寮�闂�"
+        },
+        {
+          "value": 1456,
+          "label": "鍏堝埛鍗″悗瀵嗙爜闈炴硶寮�闂�"
+        },
+        {
+          "value": 1461,
+          "label": "鍒峰崱+鎸囩汗缁勫悎鍚堟硶寮�闂�"
+        },
+        {
+          "value": 1462,
+          "label": "鍒峰崱+鎸囩汗缁勫悎闈炴硶寮�闂�"
+        },
+        {
+          "value": 1463,
+          "label": "澶氫汉鍚堟硶寮�闂�"
+        },
+        {
+          "value": 1464,
+          "label": "澶氫汉闈炴硶寮�闂�"
+        },
+        {
+          "value": 1467,
+          "label": "浜哄憳缂栧彿+瀵嗙爜鍚堟硶寮�闂�"
+        },
+        {
+          "value": 1468,
+          "label": "浜哄憳缂栧彿+瀵嗙爜闈炴硶寮�闂�"
+        },
+        {
+          "value": 1469,
+          "label": "浜鸿劯+瀵嗙爜鍚堟硶寮�闂�"
+        },
+        {
+          "value": 1470,
+          "label": "浜鸿劯+瀵嗙爜闈炴硶寮�闂�"
+        },
+        {
+          "value": 1471,
+          "label": "鎸囩汗+瀵嗙爜鍚堟硶寮�闂�"
+        },
+        {
+          "value": 1472,
+          "label": "鎸囩汗+瀵嗙爜闈炴硶寮�闂�"
+        },
+        {
+          "value": 1473,
+          "label": "鎸囩汗+浜鸿劯鍚堟硶寮�闂�"
+        },
+        {
+          "value": 1474,
+          "label": "鎸囩汗+浜鸿劯闈炴硶寮�闂�"
+        },
+        {
+          "value": 1475,
+          "label": "鍒峰崱+浜鸿劯鍚堟硶寮�闂�"
+        },
+        {
+          "value": 1476,
+          "label": "鍒峰崱+浜鸿劯闈炴硶寮�闂�"
+        },
+        {
+          "value": 1487,
+          "label": "鎸囩汗+浜鸿劯+瀵嗙爜鍚堟硶寮�闂�"
+        },
+        {
+          "value": 1488,
+          "label": "鎸囩汗+浜鸿劯+瀵嗙爜闈炴硶寮�闂�"
+        },
+        {
+          "value": 1489,
+          "label": "鍒峰崱+浜鸿劯+瀵嗙爜鍚堟硶寮�闂�"
+        },
+        {
+          "value": 1490,
+          "label": "鍒峰崱+浜鸿劯+瀵嗙爜闈炴硶寮�闂�"
+        },
+        {
+          "value": 1491,
+          "label": "鍒峰崱+鎸囩汗+瀵嗙爜鍚堟硶寮�闂�"
+        },
+        {
+          "value": 1492,
+          "label": "鍒峰崱+鎸囩汗+瀵嗙爜闈炴硶寮�闂�"
+        },
+        {
+          "value": 1493,
+          "label": "鍗�+鎸囩汗+浜鸿劯缁勫悎鍚堟硶寮�闂�"
+        },
+        {
+          "value": 1494,
+          "label": "鍗�+鎸囩汗+浜鸿劯缁勫悎闈炴硶寮�闂�"
+        },
+        {
+          "value": 4603,
+          "label": "鍗�+鎸囩汗+浜鸿劯+瀵嗙爜缁勫悎鍚堟硶寮�闂�"
+        },
+        {
+          "value": 4604,
+          "label": "鍗�+鎸囩汗+浜鸿劯+瀵嗙爜缁勫悎闈炴硶寮�闂�"
+        },
+        {
+          "value": 4626,
+          "label": "浜鸿劯+瀹夊叏甯藉悎娉曞紑闂�"
+        },
+        {
+          "value": 4627,
+          "label": "浜鸿劯+瀹夊叏甯介潪娉曞紑闂�"
+        },
+        {
+          "value": 10001,
+          "label": "鍋ュ悍鐮佸悎娉曞紑闂�"
+        },
+        {
+          "value": 10002,
+          "label": "寮傚父鍋ュ悍鐮佸紑闂�"
+        }
+      ]
+    };
+  },
+  watch: {
+    queryParams: {
+      handler(newVal, oldVal) {
+        this.refreshTable();
+      },
+      deep: true,
+    },
+  },
+  created() {
+    this.refreshTable();
+  },
+  methods: {
+    changeEnableReport(row){
+      let data = {
+        id:row.id,
+        enableReport:row.enableReport
+      }
+      changeEnableReport(data).then(res=>{
+        if(res.code===200){
+          this.$message.success("鎿嶄綔鎴愬姛")
+          this.refreshTable()
+        }
+      })
+    },
+    formatterAttendanceType(value){
+      let label = "";
+      this.openTypeList.forEach(item=>{
+        if(item.value===value){
+          label = item.label
+        }
+      })
+      return label;
+    },
+    formatterEnterOrExitType(value){
+      let label = "";
+      this.enterOrExitList.forEach(item=>{
+        if(item.value===value){
+          label = item.label
+        }
+      })
+      return label;
+    },
+    getDataSourceTypeTag(type) {
+      const tagMap = {
+        0: 'success',
+        1: '',
+      };
+      return tagMap[type] || '';
+    },
+    getDataSourceTypeText(type) {
+      const textMap = {
+        0: 'ICC鍚屾',
+        1: '鎵嬪姩鏂板',
+      };
+      return textMap[type] || '鏈煡';
+    },
+    refreshTable() {
+      this.loading = true;
+      getClockInRecord({...this.queryParams}).then(res => {
+        this.tableData = res.data;
+        this.loading = false;
+      }).catch(() => {
+        this.loading = false;
+      });
+    },
+  },
+};
+</script>
+
+<template>
+  <div class="clock-in-record">
+    <div v-loading="loading">
+      <el-table
+        :data="tableData"
+        border
+        style="width: 100%"
+        height="300"
+        :header-cell-style="{textAlign:'center'}"
+        :cell-style="{textAlign:'center'}"
+      >
+        <el-table-column type="index" label="搴忓彿" width="60"></el-table-column>
+        <el-table-column label="鏄惁绾冲叆鑰冨嫟" prop="enableReport" width="120">
+          <template slot-scope="scope">
+            <el-switch
+              @change="changeEnableReport(scope.row)"
+              v-model="scope.row.enableReport"
+              active-color="#13ce66"
+              inactive-color="#ff4949">
+            </el-switch>
+          </template>
+        </el-table-column>
+        <el-table-column prop="personCode" label="宸ュ彿" min-width="150" width="150"></el-table-column>
+        <el-table-column prop="personName" label="濮撳悕" min-width="150" width="150"></el-table-column>
+        <el-table-column prop="deptName" label="閮ㄩ棬鍚嶇О" min-width="150" width="150"></el-table-column>
+        <el-table-column prop="cardNumber" label="鍗″彿" min-width="150" width="150"></el-table-column>
+        <el-table-column prop="swingTime" label="鍒峰崱鏃堕棿" min-width="180" width="180"></el-table-column>
+        <el-table-column prop="channelName" label="鑰冨嫟鐐归�氶亾鍚嶇О" min-width="180" width="180"></el-table-column>
+        <el-table-column prop="deviceName" label="鑰冨嫟璁惧鍚嶇О" min-width="150" width="150"></el-table-column>
+        <el-table-column prop="deviceCode" label="鑰冨嫟璁惧缂栫爜" min-width="150" width="150"></el-table-column>
+        <el-table-column prop="enterOrExit" label="杩涘嚭闂ㄧ被鍨�" min-width="150" width="150" :formatter="(row)=>formatterEnterOrExitType(row.enterOrExit)"></el-table-column>
+        <el-table-column prop="openType" label="寮�闂ㄧ被鍨�" min-width="150" width="150" :formatter="(row)=>formatterAttendanceType(row.openType)"></el-table-column>
+        <el-table-column prop="isSync" label="鏁版嵁鏉ユ簮" min-width="150" width="150">
+          <template slot-scope="scope">
+            <el-tag :type="getDataSourceTypeTag(scope.row.isSync)">
+              {{ getDataSourceTypeText(scope.row.isSync) }}
+            </el-tag>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="scss">
+
+</style>
diff --git a/src/views/performance/attendance/index.vue b/src/views/performance/attendance/index.vue
new file mode 100644
index 0000000..3726bc0
--- /dev/null
+++ b/src/views/performance/attendance/index.vue
@@ -0,0 +1,644 @@
+<template>
+  <div class="attendance-page">
+    <div class="search">
+      <div class="search_thing">
+        <div class="search_input">
+          <el-date-picker
+            v-model="dateRange"
+            type="datetimerange"
+            range-separator="鑷�"
+            start-placeholder="寮�濮嬫棩鏈�"
+            end-placeholder="缁撴潫鏃ユ湡"
+            :default-time="['00:00:00', '23:59:59']"
+            value-format="yyyy-MM-dd HH:mm:ss"
+            style="width: 340px"
+            size="small"
+            @change="refreshTable"
+          ></el-date-picker>
+        </div>
+      </div>
+      <div class="search_thing">
+        <div class="search_input">
+          <el-input
+            v-model="queryParams.keyword"
+            placeholder="宸ュ彿鎴栧憳宸ュ悕绉�"
+            size="small"
+            style="width: 200px"
+            clearable
+            @keyup.enter.native="refreshTable"
+          ></el-input>
+        </div>
+      </div>
+      <div class="search_thing">
+        <el-button size="mini" type="primary" @click="refreshTable()"
+          >鏌� 璇�</el-button
+        >
+        <el-button size="mini" @click="resetQuery">閲嶇疆</el-button>
+        <el-button size="mini" type="primary" @click="openAddAttendanceDialog()">鎵嬪姩鏂板</el-button>
+        <el-button size="mini" type="success" @click="openSyncAttendanceDialog()">鍚屾鑰冨嫟璁板綍</el-button>
+      </div>
+    </div>
+    <div class="container">
+      <div v-loading="loading">
+        <el-table
+          :data="tableData"
+          border
+          :height="tableHeight"
+          style="width: 100%"
+          :header-cell-style="{textAlign:'center'}"
+          :cell-style="{textAlign:'center'}"
+        >
+          <el-table-column type="index" label="搴忓彿" width="60"></el-table-column>
+          <el-table-column prop="personCode" label="宸ュ彿" min-width="120" width="120"></el-table-column>
+          <el-table-column prop="personName" label="濮撳悕" min-width="120" width="120"></el-table-column>
+          <el-table-column prop="deptName" label="閮ㄩ棬鍚嶇О" min-width="120" width="120"></el-table-column>
+          <el-table-column prop="shiftId" label="鐝绫诲瀷" min-width="120" width="120">
+            <template slot-scope="scope">
+              <el-tag :type="getDictTypeByShift(scope.row.shiftId)">{{ getShiftByDic(scope.row.shiftId) }}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column prop="result" label="鑰冨嫟缁撴灉" min-width="120" width="120">
+            <template slot-scope="scope">
+              <el-tag v-if="getResultTag(scope.row.result)" :type="getResultTag(scope.row.result).type">{{getResultTag(scope.row.result).label}}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column prop="swingDate" label="鑰冨嫟鏃堕棿" min-width="150" width="150"></el-table-column>
+          <el-table-column label="绛惧叆/绛惧嚭鎯呭喌">
+            <el-table-column prop="workDateTime" label="涓婄彮鏃堕棿" min-width="160" width="160">
+              <template slot-scope="scope">
+                <el-tag type="success" v-if="scope.row.workDateTime && scope.row.workClockInState===1">{{ scope.row.workDateTime }}</el-tag>
+                <el-tag type="danger" v-else-if="scope.row.workDateTime && scope.row.workClockInState===0">{{ scope.row.workDateTime }}</el-tag>
+                <span v-else>{{ scope.row.workDateTime }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column prop="offWorkDateTime" label="涓嬬彮鏃堕棿" min-width="160" width="160">
+              <template slot-scope="scope">
+                <el-tag type="success" v-if="scope.row.offWorkDateTime && scope.row.offClockInState===1">{{ scope.row.offWorkDateTime }}</el-tag>
+                <el-tag type="danger" v-else-if="scope.row.offWorkDateTime && scope.row.offClockInState===0">{{ scope.row.offWorkDateTime }}</el-tag>
+                <span v-else>{{ scope.row.offWorkDateTime }}</span>
+              </template>
+            </el-table-column>
+          </el-table-column>
+          <el-table-column label="鑰冨嫟鏃堕暱(h)">
+            <el-table-column prop="plannedWorkHours" label="搴斿嫟鏃堕暱" min-width="80" width="80"></el-table-column>
+            <el-table-column prop="actualWorkHours" label="瀹為檯鏃堕暱" min-width="80" width="80"></el-table-column>
+            <el-table-column prop="absenceWorkHours" label="缂哄嫟鏃堕暱" min-width="80" width="80"></el-table-column>
+          </el-table-column>
+          <el-table-column prop="isSync" label="鏁版嵁鏉ユ簮" min-width="120">
+            <template slot-scope="scope">
+              <el-tag v-if="scope.row.isSync===0" type="success">ICC鍚屾</el-tag>
+              <el-tag v-else-if="scope.row.isSync===1" type="info">鎵嬪姩鏂板</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column prop="createUser" label="鍒涘缓浜�" min-width="150" width="150" :formatter="(row)=>formatterUserName(row.createUser)"></el-table-column>
+          <el-table-column prop="createTime" label="鍒涘缓鏃堕棿" min-width="180" width="180"></el-table-column>
+          <el-table-column prop="updateUser" label="鏇存柊浜�" min-width="150" width="150" :formatter="(row)=>formatterUserName(row.updateUser)"></el-table-column>
+          <el-table-column prop="updateTime" label="鏇存柊鏃堕棿" min-width="180" width="180"></el-table-column>
+          <el-table-column fixed="right" width="180" label="鎿嶄綔">
+            <template slot-scope="scope">
+              <el-button type="text" @click="openClockInDialog(scope.row)">杩涘嚭璁板綍</el-button>
+              <el-button :disabled="scope.row.isSync===0" type="text" @click="openAddAttendanceDialog(scope.row)">缂栬緫</el-button>
+              <el-button :disabled="scope.row.isSync===0" type="text" class="remove-btn" @click="confirmRemoveRecord(scope.row)">鍒犻櫎</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+        <pagination
+          :total="pagination.total"
+          :page.sync="pagination.current"
+          :limit.sync="pagination.size"
+          @pagination="handlePageChange"
+        />
+      </div>
+    </div>
+    <el-dialog
+      title="杩涘嚭璁板綍"
+      :visible.sync="dialogVisible"
+      width="60%">
+      <staff-clock-in-record :key="Math.random()" :query-params="clockInQueryParams" ></staff-clock-in-record>
+    </el-dialog>
+    <el-dialog
+      :title="attendanceForm.id?'缂栬緫鑰冨嫟璁板綍':'鏂板鑰冨嫟璁板綍'"
+      :visible.sync="addAttendanceVisible"
+      @closed="closeAttendanceDialog"
+      width="50%">
+      <el-form ref="attendanceForm" style="margin-top:20px" :model="attendanceForm" :rules="attendanceRule" label-position="right" label-width="100px">
+        <div>
+          <el-divider content-position="left">鍩烘湰淇℃伅</el-divider>
+          <el-row >
+            <el-col :span="12">
+              <el-form-item label="宸ュ彿" prop="personCode">
+                <el-input size="small" v-model="attendanceForm.personCode" disabled></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="濮撳悕" prop="personName">
+                <el-select size="small" style="width:100%" v-model="attendanceForm.personName" placeholder="璇烽�夋嫨鍛樺伐濮撳悕" @change="selectedPersonName" clearable>
+                  <el-option v-for="(item,index) in userList" :key="index" :label="item.name" :value="item.name"></el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row >
+            <el-col :span="12">
+              <el-form-item label="閮ㄩ棬鍚嶇О" prop="deptName">
+                <el-input size="small" v-model="attendanceForm.deptName" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" clearable></el-input>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="鑰冨嫟鏃堕棿" prop="swingDate">
+                <el-date-picker size="small" clearable style="width:100%" type="date" placeholder="璇烽�夋嫨鏃ユ湡" value-format="yyyy-MM-dd" v-model="attendanceForm.swingDate" @change="checkDutyDate"></el-date-picker>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row >
+            <el-col :span="12">
+              <el-form-item label="鐝绫诲瀷" prop="shiftId">
+                <el-select disabled size="small" style="width:100%" v-model="attendanceForm.shiftId" placeholder="璇烽�夋嫨鐝绫诲瀷" clearable>
+                  <el-option v-for="(item,index) in dailyTypeList" :key="index" :label="item.dictLabel" :value="item.dictValue"></el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+<!--              <el-form-item label="鑰冨嫟缁撴灉" prop="result">-->
+<!--                <el-select size="small" style="width:100%" clearable v-model="attendanceForm.result" placeholder="璇烽�夋嫨鑰冨嫟缁撴灉">-->
+<!--                  <el-option v-for="(item,index) in resultList" :key="index" :label="item.label" :value="item.value"/>-->
+<!--                </el-select>-->
+<!--              </el-form-item>-->
+            </el-col>
+          </el-row>
+          <el-divider content-position="left">绛惧叆/绛惧嚭鎯呭喌</el-divider>
+          <el-row>
+            <el-col :span="12">
+              <el-form-item label="涓婄彮鏃堕棿" prop="workTime">
+                <el-time-picker
+                  style="width:100%"
+                  value-format="HH:mm"
+                  v-model="attendanceForm.workDateTime"
+                  format="HH:mm"
+                  placeholder="涓婄彮鏃堕棿">
+                </el-time-picker>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="涓嬬彮鏃堕棿" prop="offWorkTime">
+                <el-time-picker
+                  style="width:100%"
+                  value-format="HH:mm"
+                  format="HH:mm"
+                  v-model="attendanceForm.offWorkDateTime"
+                  placeholder="涓嬬彮鏃堕棿">
+                </el-time-picker>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </div>
+      </el-form>
+      <span slot="footer" class="dialog-footer">
+          <el-button @click="addAttendanceVisible = false">鍙� 娑�</el-button>
+          <el-button type="primary" @click="confirmAddAttendance()">纭� 瀹�</el-button>
+      </span>
+    </el-dialog>
+    <el-dialog
+      title="鍚屾鑰冨嫟璁板綍"
+      :visible.sync="syncAttendanceVisible"
+      @close="()=>{syncDateRange=[]}"
+      width="25%">
+      <el-date-picker
+        style="width:100%"
+        v-model="syncDateRange"
+        type="datetimerange"
+        value-format="yyyy-MM-dd HH:mm:ss"
+        :default-time="['00:00:00', '23:59:59']"
+        range-separator="鑷�"
+        start-placeholder="寮�濮嬫棩鏈�"
+        end-placeholder="缁撴潫鏃ユ湡">
+      </el-date-picker>
+      <span slot="footer" class="dialog-footer">
+    <el-button @click="syncAttendanceVisible = false">鍙� 娑�</el-button>
+    <el-button type="primary" @click="confirmSyncAttendance">纭� 瀹�</el-button>
+  </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import StaffClockInRecord from "./components/staffClockInRecord.vue";
+import {selectAllUser} from '@/api/system/user'
+import {
+  pageAttendanceRecord,
+  checkDutyDate,
+  saveOrUpdateStaffAttendanceTrackingRecord,
+  removeStaffAttendanceTrackingRecord,
+  syncAttendanceRecord
+} from '@/api/performance/attendance'
+import {getDicts} from "@/api/system/dict/data";
+import dayjs from 'dayjs';
+export default {
+  name: "Attendance",
+  components: {
+    StaffClockInRecord
+  },
+  data() {
+    return {
+      syncDateRange:[],
+      syncAttendanceVisible: false,
+      attendanceForm:{
+        workDataId: null,
+        offWorkDataId: null,
+        personCode: null,
+        personName: null,
+        deptName: "璐ㄩ噺閮�",
+        shiftId: null,
+        swingDate: null,
+        workDateTime: null,
+        offWorkDateTime: null,
+        result: null,
+      },
+      attendanceRule:{
+        personName:{required:true,message:'璇烽�夋嫨鍛樺伐濮撳悕',trigger:'change'},
+        deptName:{required:true,message:'璇疯緭鍏ラ儴闂ㄥ悕绉�',trigger:'blur'},
+        dailyName:{required:true,message:'璇疯緭鍏ョ彮娆″悕绉�',trigger:'blur'},
+        shiftId:{required:true,message:'璇烽�夋嫨鐝绫诲瀷',trigger:'change'},
+        swingDate:{required:true,message:'璇烽�夋嫨鑰冨嫟鏃堕棿',trigger:'change'},
+        workDateTime:{required:true,message:'璇烽�夋嫨涓婄彮鏃堕棿',trigger:'change'},
+        offWorkDateTime:{required:true,message:'璇烽�夋嫨涓嬬彮鏃堕棿',trigger:'change'},
+        result:{required:true,message:'璇烽�夋嫨鑰冨嫟缁撴灉',trigger:'change'},
+      },
+      addAttendanceVisible:false,
+      dialogVisible:false,
+      dateRange: [],
+      queryParams: {
+        startDate: "",
+        endDate: "",
+        keyword: "",
+      },
+      userList:[],
+      loading: false,
+      tableData: [],
+      pagination: {
+        current: 1,
+        size: 20,
+        total: 0,
+      },
+      tableHeight:0,
+      resizeHandler: null, // 闃叉姈鍑芥暟寮曠敤
+      clockInQueryParams:{
+        personCode:null,//浜哄憳缂栧彿
+        swingDate:null,//鑰冨嫟鏃堕棿
+        shiftId:null//鐝id
+      },
+      dailyTypeList:[],
+      resultList:[
+        {
+          label:"姝e父",
+          value:1,
+          type:"success"
+        },
+        {
+          label:"寮傚父",
+          value:0,
+          type:"danger"
+        },
+      ],
+    };
+  },
+  created() {
+    this.selectEnumByCategory()
+    this.getUserList()
+    this.getTableHeight();
+    this.dateRange = this.getTimeRange()
+    this.resizeHandler = this.debounce(() => {
+      this.getTableHeight();
+    }, 200);
+    this.refreshTable();
+  },
+  mounted(){
+    this.getTableHeight();
+    window.addEventListener("resize",this.resizeHandler)
+  },
+  beforeDestroy(){
+    window.removeEventListener("resize",this.resizeHandler)
+  },
+  methods: {
+    getShiftByDic(e) {
+      let obj = this.dailyTypeList.find((m) => m.dictValue == e);
+      if (obj) {
+        return obj.dictLabel;
+      }
+      return "鏃�";
+    },
+    getDictTypeByShift(e) {
+      let obj = this.dailyTypeList.find((m) => m.dictValue == e);
+      if (obj) {
+        return obj.listClass;
+      }
+      return "";
+    },
+    selectEnumByCategory() {
+      getDicts("sys_class_type").then((response) => {
+        this.dailyTypeList = response.data;
+      });
+    },
+    /**
+     * 鍒濆鍖栭粯璁ゆ棩鏈熻寖鍥达細杩戜竴涓湀锛堝綋鍓嶆棩鏈� - 30澶� 鑷� 褰撳墠鏃ユ湡锛�
+     */
+    getTimeRange(format = 'YYYY-MM-DD HH:mm:ss') {
+      // 鑾峰彇褰撳墠鏃堕棿
+      const now = dayjs();
+      // 鑾峰彇褰撳墠鏃ユ湡鐨勩�屾棩銆嶏紙1-31锛�
+      const currentDate = now.date();
+
+      let startTime, endTime;
+
+      // 鏍稿績閫昏緫锛氬垽鏂綋鍓嶆棩鏈熸槸鍚﹀ぇ浜�25鍙�
+      if (currentDate > 25) {
+        // 鉁� 鎯呭喌1锛氬綋鍓嶆棩>25 鈫� 褰撴湀26鍙� ~ 娆℃湀25鍙�
+        startTime = now.startOf('month').add(25, 'day'); // 褰撴湀1鍙� +25澶� = 26鍙�
+        endTime = startTime.add(1, 'month').date(25).hour(23)
+          .minute(59)
+          .second(59);     // 娆℃湀25鍙凤紙dayjs鑷姩澶勭悊璺ㄥ勾锛�
+      } else {
+        // 鉁� 鎯呭喌2锛氬綋鍓嶆棩鈮�25 鈫� 涓婃湀26鍙� ~ 褰撴湀25鍙�
+        startTime = now.subtract(1, 'month').startOf('month').add(25, 'day'); // 涓婃湀26鍙�
+        endTime = now.date(25).hour(23)
+          .minute(59)
+          .second(59); // 褰撴湀25鍙�
+      }
+
+      // 杩斿洖鏍煎紡鍖栧悗鐨勬椂闂存暟缁�
+      return [startTime.format(format), endTime.format(format)];
+    },
+    //鍚屾鑰冨嫟璁板綍
+    confirmSyncAttendance(){
+      if(!this.syncDateRange || this.syncDateRange.length<2){
+        this.$message.warning("璇烽�夋嫨鍚屾鏃ユ湡");
+        return
+      }
+      syncAttendanceRecord({
+        startDate:this.syncDateRange[0],
+        endDate:this.syncDateRange[1]
+      }).then(res=>{
+        if(res.code===200){
+          this.$message.success("鍚庡彴鍚屾鑰冨嫟璁板綍涓紝璇峰埛鏂拌〃鏍兼煡鐪�")
+          this.refreshTable()
+        }
+      }).catch(error=>{
+        console.error(error)
+      })
+      this.$nextTick(()=>{
+        this.syncAttendanceVisible = false
+      })
+    },
+    //鎵撳紑鍚屾鑰冨嫟璁板綍寮规
+    openSyncAttendanceDialog(){
+      this.syncAttendanceVisible = true
+    },
+    //鍒犻櫎鑰冨嫟璁板綍
+    confirmRemoveRecord(row){
+      if(!row){
+        this.$message.warning("璇烽�夋嫨涓�鏉¤褰�")
+        return
+      }
+      this.$confirm('鏄惁纭鍒犻櫎璇ユ潯鑰冨嫟璁板綍', '鎻愮ず', {
+        confirmButtonText: '纭畾',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning'
+      }).then(() => {
+        let ids = [row.workDataId,row.offWorkDataId]
+        removeStaffAttendanceTrackingRecord(ids).then(res=>{
+          if(res.code===200){
+            this.$message.success("鍒犻櫎鎴愬姛")
+            this.refreshTable()
+          }
+        }).catch(error=>{
+          console.error(error)
+        })
+      }).catch(() => { });
+    },
+    //鍏抽棴鏂板/缂栬緫鑰冨嫟璁板綍寮规
+    closeAttendanceDialog(){
+      this.attendanceForm.id = null
+      this.$refs.attendanceForm.resetFields()
+    },
+    //閲嶇疆鑰冨嫟璁板綍寮规琛ㄥ崟
+    resetAttendanceForm(){
+      this.attendanceForm = {
+        workDataId: null,
+        offWorkDataId: null,
+        personCode: null,
+        personName: null,
+        deptName: "璐ㄩ噺閮�",
+        shiftId: null,
+        swingDate: null,
+        workDateTime: null,
+        offWorkDateTime: null,
+        result: null,
+      }
+      this.$refs.attendanceForm.resetFields()
+      this.$nextTick(()=>{
+        this.addAttendanceVisible = false
+      })
+    },
+    //纭淇濆瓨鑰冨嫟璁板綍
+    confirmAddAttendance(){
+      this.$refs.attendanceForm.validate(valid=>{
+        if(valid){
+          saveOrUpdateStaffAttendanceTrackingRecord(this.attendanceForm).then(res=>{
+            if(res.code===200){
+              this.$message.success('淇濆瓨鎴愬姛')
+              this.refreshTable()
+              this.resetAttendanceForm()
+            }
+          }).catch(error=>{
+            console.error(error)
+          })
+        }
+      })
+    },
+    //鏍¢獙鎵�閫夎�冨嫟鏃ユ湡鏄惁瀛樺湪璇ヤ汉鍛樼殑鑰冨嫟璁板綍
+    checkDutyDate(val){
+      if(!this.attendanceForm.personCode){
+        this.$message.warning("璇峰厛閫夋嫨浜哄憳鍚嶇О")
+        this.attendanceForm.dutyDate = null
+        return
+      }
+      let params = {
+        personCode:this.attendanceForm.personCode,
+        swingDate: val
+      }
+      checkDutyDate(params).then(res=>{
+        if(res.code===200&&res.data){
+          this.attendanceForm.result = 1//榛樿姝e父
+          this.attendanceForm.shiftId = res.data.shift
+          this.attendanceForm.workDateTime = res.data.startTime
+          this.attendanceForm.offWorkDateTime = res.data.endTime
+        }
+      }).catch(error=>{
+        console.error(error)
+        this.attendanceForm.swingDate = null
+      })
+    },
+    //閫夋嫨浜哄憳鍚嶇О甯﹀嚭宸ュ彿
+    selectedPersonName(val){
+      let account = ""
+      this.userList.forEach(item=>{
+        if(item.name===val){
+          account = item.account
+        }
+      })
+      this.attendanceForm.personCode = account
+    },
+    //鎵撳紑鏂板/缂栬緫鑰冨嫟璁板綍寮规
+    openAddAttendanceDialog(row){
+      if(row){
+        //澶勭悊涓�/涓嬬彮鏃堕棿鏍煎紡
+        let workTime = row.workDateTime&&row.workDateTime.length>8?row.workDateTime.substring(11,16):row.workDateTime
+        let offWorkTime = row.offWorkDateTime&&row.offWorkDateTime.length>8?row.offWorkDateTime.substring(11,16):row.offWorkDateTime
+        row.workDateTime = workTime
+        row.offWorkDateTime = offWorkTime
+        this.attendanceForm = {...row}
+      }
+      this.addAttendanceVisible = true
+    },
+    //鎵撳紑鏌ョ湅鎵撳崱璁板綍寮规
+    openClockInDialog(row){
+      if(row){
+        this.clockInQueryParams.personCode = row.personCode
+        this.clockInQueryParams.shiftId = row.shiftId
+        this.clockInQueryParams.swingDate = row.swingDate
+      }
+      this.$nextTick(()=>{
+        this.dialogVisible = true
+      })
+    },
+    //鏍煎紡鍖栦汉鍛樺悕绉�
+    formatterUserName(value){
+      let userName = ""
+      this.userList.forEach(item=>{
+        if(item.id===value){
+          userName = item.name
+        }
+      })
+      return userName;
+    },
+    //鏍煎紡鍖栬�冨嫟缁撴灉
+    getResultTag(type) {
+      let typeItem = null
+      this.resultList.forEach(item=>{
+        if(item.value===type){
+          typeItem = item
+        }
+      })
+      return typeItem;
+    },
+    //闃叉姈鍑芥暟
+    debounce(fn, delay) {
+      let timer = null;
+      return (...args) => {
+        clearTimeout(timer);
+        timer = setTimeout(() => fn.apply(this, args), delay);
+      };
+    },
+    //鍔ㄦ�佽幏鍙栬〃鏍奸珮搴�
+    getTableHeight(){
+      const innerHeight = window.innerHeight
+      const naviHeight = 96
+      const otherHeight = 128
+      this.tableHeight = innerHeight - naviHeight - otherHeight
+    },
+    //鍒锋柊琛ㄦ牸
+    refreshTable() {
+      this.loading = true;
+        if (this.dateRange && this.dateRange.length === 2) {
+          this.queryParams.startDate = this.dateRange[0];
+          this.queryParams.endDate = this.dateRange[1];
+        } else {
+          this.queryParams.startDate = "";
+          this.queryParams.endDate = "";
+        }
+      pageAttendanceRecord({...this.queryParams, ...this.pagination}).then(res => {
+        this.tableData = res.data.records;
+        this.pagination.total = res.data.total;
+        this.pagination.current = res.data.current;
+        this.loading = false;
+      }).catch(() => {
+        this.loading = false;
+      });
+    },
+    //鍒嗛〉鍒囨崲
+    handlePageChange(page) {
+      this.pagination.current = page.page;
+      this.pagination.size = page.limit;
+      this.refreshTable();
+    },
+    //鏌ヨ鐢ㄦ埛鍒楄〃
+    getUserList(){
+      selectAllUser().then(res=>{
+        this.userList = res.data
+      }).catch(error=>{
+        console.error(error)
+      })
+    },
+    //閲嶇疆鎸夐挳
+    resetQuery() {
+      this.dateRange = this.getTimeRange();
+      this.queryParams = {
+        startDate: "",
+        endDate: "",
+        keyword: "",
+      };
+      this.$nextTick(()=>{
+        this.refreshTable()
+      })
+    },
+  },
+};
+</script>
+
+<style scoped lang="scss">
+.attendance-page {
+  padding: 10px;
+}
+.remove-btn.el-button--text{
+  color:#ff4949
+}
+.search {
+  height: 50px;
+  display: flex;
+  align-items: center;
+  background-color: #fff;
+  margin-left:16px;
+}
+
+.search_thing {
+  display: flex;
+  align-items: center;
+  height: 50px;
+  margin-right: 30px
+}
+
+.search_label {
+  width: 90px;
+  font-size: 14px;
+  text-align: right;
+}
+
+.search_input {
+  display: flex;
+  align-items: center;
+}
+
+.container {
+  background-color: #fff;
+  padding: 0 16px;
+  min-height: calc(100vh - 180px);
+}
+
+::v-deep .el-tabs__header {
+  margin-bottom: 16px;
+}
+
+::v-deep .el-tabs__content {
+  padding: 0;
+}
+</style>
diff --git a/src/views/performance/class/index.vue b/src/views/performance/class/index.vue
index ca34134..f6c52f0 100644
--- a/src/views/performance/class/index.vue
+++ b/src/views/performance/class/index.vue
@@ -265,10 +265,26 @@
           <span style="color: red; margin-right: 4px">*</span>浜哄憳鍚嶇О锛�
         </div>
         <div class="search_input" style="width: calc(100% - 90px)">
-          <el-select v-model="schedulingQuery.userId" placeholder="璇烽�夋嫨" style="width: 100%" multiple clearable
-            collapse-tags>
-            <el-option v-for="item in personList" :key="item.id" :label="item.name" :value="item.id">
-            </el-option>
+          <el-select v-model="schedulingQuery.userId" popper-class="select-with-all" placeholder="璇烽�夋嫨" style="width: 100%" multiple collapse-tags clearable>
+<!--            <el-option v-for="item in personList" :key="item.id" :label="item.name" :value="item.id">-->
+<!--            </el-option>-->
+            <template slot="prefix">
+              <el-button
+                type="text"
+                size="mini"
+                @click="handleSelectAll"
+                style="margin: 4px 0;"
+              >
+                {{ isAllSelected ? '鍙栨秷鍏ㄩ��' : '鍏ㄩ��' }}
+              </el-button>
+              <el-divider style="margin: 5px 0;" />
+            </template>
+            <el-option
+              v-for="item in personList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
           </el-select>
         </div>
       </div>
@@ -378,7 +394,7 @@
       loading: false,
       schedulingQuery: {
         week: "",
-        userId: null,
+        userId: [],
         shift: "",
       },
       list: [],
@@ -399,6 +415,11 @@
       menuY: 0,
       selectedTarget: null,
     };
+  },
+  computed:{
+    isAllSelected() {
+      return this.schedulingQuery.userId.length === this.personList.length && this.personList.length > 0;
+    },
   },
   watch: {
 
@@ -423,6 +444,14 @@
     document.removeEventListener('click', this.handleClickOutside)
   },
   methods: {
+    handleSelectAll() {
+      if (this.isAllSelected) {
+        this.schedulingQuery.userId = [];
+      } else {
+        // 鍙�変腑鍙敤閫夐」鐨剉alue
+        this.schedulingQuery.userId = this.personList.map(item => item.id);
+      }
+    },
     handleContextMenu(target,e) {
       // 闃绘娴忚鍣ㄩ粯璁ゅ彸閿彍鍗�
       e.preventDefault()
@@ -526,40 +555,6 @@
         this.initYear();
       }
     },
-    transFromNumber(num) {
-      let changeNum = [
-        "闆�",
-        "涓�",
-        "浜�",
-        "涓�",
-        "鍥�",
-        "浜�",
-        "鍏�",
-        "涓�",
-        "鍏�",
-        "涔�",
-      ]; //changeNum[0] = "闆�"
-      let unit = ["", "鍗�", "鐧�", "鍗�", "涓�"];
-      num = parseInt(num);
-      let getWan = (temp) => {
-        let strArr = temp.toString().split("").reverse();
-        let newNum = "";
-        for (var i = 0; i < strArr.length; i++) {
-          newNum =
-            (i == 0 && strArr[i] == 0
-              ? ""
-              : i > 0 && strArr[i] == 0 && strArr[i - 1] == 0
-                ? ""
-                : changeNum[strArr[i]] + (strArr[i] == 0 ? unit[0] : unit[i])) +
-            newNum;
-        }
-        return newNum;
-      };
-      let overWan = Math.floor(num / 10000);
-      let noWan = num % 10000;
-      if (noWan.toString().length < 4) noWan = "0" + noWan;
-      return overWan ? getWan(overWan) + "涓�" + getWan(noWan) : getWan(num);
-    },
     init() {
       this.pageLoading = true;
       let year = this.query.year.getFullYear();
@@ -574,16 +569,6 @@
       }).then((res) => {
         this.pageLoading = false;
         this.list = res.data.page
-        // this.list = res.data.page.map((item) => {
-        //   for (let key in item.monthlyAttendance) {
-        //     let type = this.getDayByDic(key);
-        //     if (type != undefined || type != null) {
-        //       item[`day${type}`] = item.monthlyAttendance[key];
-        //     }
-        //   }
-        //   return item;
-        // });
-        console.log(this.list)
         let headerList = res.data.headerList;
         this.weeks = [];
         headerList.forEach((item) => {
@@ -649,7 +634,7 @@
           this.schedulingVisible = false;
           this.schedulingQuery = {
             week: "",
-            userId: null,
+            userId: [],
             shift: "",
           };
           this.refresh();
diff --git a/src/views/system/user/index.vue b/src/views/system/user/index.vue
index 9dd89c3..3306ae7 100644
--- a/src/views/system/user/index.vue
+++ b/src/views/system/user/index.vue
@@ -114,48 +114,61 @@
         </el-row>
         <el-row>
           <el-col :span="12">
+            <el-form-item label="宀椾綅">
+              <el-select style="width:100%" v-model="form.postIds" multiple placeholder="璇烽�夋嫨">
+                <el-option
+                  v-for="item in postOptions"
+                  :key="item.postId"
+                  :label="item.postName"
+                  :value="item.postId"
+                  :disabled="item.status == 1"
+                ></el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
             <el-form-item label="瑙掕壊" prop="roleIds">
-              <el-select v-model="form.roleIds" multiple placeholder="璇烽�夋嫨瑙掕壊" clearable>
+              <el-select style="width:100%" v-model="form.roleIds" multiple placeholder="璇烽�夋嫨瑙掕壊" clearable>
                 <el-option v-for="item in roleOptions" :key="item.roleId" :label="item.roleName" :value="item.roleId"
                   :disabled="item.status == 1"></el-option>
               </el-select>
             </el-form-item>
           </el-col>
+        </el-row>
+        <el-row>
           <el-col :span="12">
             <el-form-item label="瀵嗙爜" prop="password">
               <el-input v-model="form.password" placeholder="璇疯緭鍏ョ敤鎴峰瘑鐮�" type="password" maxlength="20" show-password />
             </el-form-item>
           </el-col>
-        </el-row>
-        <el-row>
           <el-col :span="12">
             <el-form-item label="濮撳悕EN" prop="nameEn">
               <el-input v-model="form.nameEn" placeholder="璇疯緭鍏ュ鍚岴N" maxlength="50" />
             </el-form-item>
           </el-col>
+        </el-row>
+        <el-row>
           <el-col :span="12">
             <el-form-item label="閭" prop="email">
               <el-input v-model="form.email" placeholder="璇疯緭鍏ュ唴瀹�"></el-input>
             </el-form-item>
           </el-col>
-        </el-row>
-        <el-row>
           <el-col :span="12">
             <el-form-item label="鍗曚綅" prop="company">
               <el-select v-model="form.company" placeholder="璇烽�夋嫨鍗曚綅" style="width: 100%" clearable>
-                <el-option v-for="item in postOptions" :key="item.id" :label="item.company"
+                <el-option v-for="item in companyOptions" :key="item.id" :label="item.company"
                   :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="deptId">
               <treeselect v-model="form.deptId" :options="enabledDeptOptions" :show-count="true"
-                placeholder="璇烽�夋嫨褰掑睘閮ㄩ棬" />
+                          placeholder="璇烽�夋嫨褰掑睘閮ㄩ棬" />
             </el-form-item>
           </el-col>
-        </el-row>
-        <el-row>
           <el-col :span="12">
             <el-form-item label="绛惧悕">
               <el-upload class="avatar-uploader" :action="uploadAction" :show-file-list="false"
@@ -166,11 +179,13 @@
               </el-upload>
             </el-form-item>
           </el-col>
+        </el-row>
+        <el-row>
           <el-col :span="12">
             <el-form-item label="涓汉鐓х墖">
               <el-upload class="avatar-uploader" :action="uploadAction" :show-file-list="false"
-                :headers="upload.headers" accept=".png, .jpg, .jpeg, .gif" :on-error="handleUploadError1"
-                :on-success="handleUploadSuccess1" :before-upload="handleBeforeUpload1">
+                         :headers="upload.headers" accept=".png, .jpg, .jpeg, .gif" :on-error="handleUploadError1"
+                         :on-success="handleUploadSuccess1" :before-upload="handleBeforeUpload1">
                 <img v-if="form.pictureUrl" :src="javaApi + '/img/' + form.pictureUrl" class="avatar" alt="">
                 <i v-else class="el-icon-plus avatar-uploader-icon"></i>
               </el-upload>
@@ -278,8 +293,15 @@
   resetUserPwd,
   changeUserStatus,
   deptTreeSelect,
-  selectCompaniesList, selectSimpleList, addPersonUser, uploadFile, selectRoleList, selectCustomEnum, addDepartment
+  selectCompaniesList,
+  selectSimpleList,
+  addPersonUser,
+  uploadFile,
+  selectRoleList,
+  selectCustomEnum,
+  addDepartment
 } from "@/api/system/user";
+import {optionSelect} from '@/api/system/post'
 import { getToken } from "@/utils/auth";
 import Treeselect from "@riophae/vue-treeselect";
 import "@riophae/vue-treeselect/dist/vue-treeselect.css";
@@ -323,6 +345,8 @@
       dateRange: [],
       // 宀椾綅閫夐」
       postOptions: [],
+      //鍗曚綅閫夐」
+      companyOptions:[],
       // 瑙掕壊閫夐」
       roleOptions: [],
       // 琛ㄥ崟鍙傛暟
@@ -703,10 +727,11 @@
       this.reset();
       this.open = true;
       selectCustomEnum().then(res => {
-        this.postOptions = res.data;
+        this.companyOptions = res.data;
       })
       getUser().then(response => {
         this.roleOptions = response.roles;
+        this.postOptions = response.posts
         this.title = "娣诲姞鐢ㄦ埛";
       });
     },
@@ -714,7 +739,7 @@
     handleUpdate(row) {
       this.reset();
       selectCustomEnum().then(res => {
-        this.postOptions = res.data;
+        this.companyOptions = res.data;
       })
       const userId = row.userId || this.ids;
       getUser(userId).then(response => {
@@ -722,6 +747,8 @@
         this.form.password = ''
         this.roleOptions = response.roles;
         this.$set(this.form, "roleIds", response.roleIds);
+        this.postOptions = response.posts
+        this.$set(this.form, "postIds", response.postIds);
         this.open = true;
         this.title = "淇敼鐢ㄦ埛";
       });

--
Gitblit v1.9.3