ソースを参照

异常工时表

QuYueTing 2 日 前
コミット
d95301698f

+ 52 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ExceptionInfosController.java

@@ -0,0 +1,52 @@
+package com.management.platform.controller;
+
+
+import com.management.platform.entity.User;
+import com.management.platform.service.ExceptionInfosService;
+import com.management.platform.service.UserService;
+import com.management.platform.util.HttpRespMsg;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * <p>
+ *  前端控制器
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-24
+ */
+@RestController
+@RequestMapping("/exception-infos")
+public class ExceptionInfosController {
+
+    @Autowired
+    private HttpServletRequest request;
+
+    @Resource
+    private ExceptionInfosService exceptionInfosService;
+
+    @Resource
+    private UserService userService;
+
+    @RequestMapping("/getExceptionReport")
+    public HttpRespMsg getExceptionReport(String ymonth, String userId) {
+        String token = request.getHeader("TOKEN");
+        User user = userService.getById(token);
+        return exceptionInfosService.getExceptionReport(user.getCompanyId(), ymonth, userId);
+    }
+
+    @RequestMapping("/exportExceptionReport")
+    public HttpRespMsg exportExceptionReport(String ymonth, String userId) {
+        String token = request.getHeader("TOKEN");
+        User user = userService.getById(token);
+        return exceptionInfosService.exportExceptionReport(user.getCompanyId(), ymonth, userId);
+    }
+
+}
+

+ 102 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ExceptionInfos.java

@@ -0,0 +1,102 @@
+package com.management.platform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import java.time.LocalDate;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableField;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ * <p>
+ * 
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-24
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class ExceptionInfos extends Model<ExceptionInfos> {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    /**
+     * 当日此异常的次数
+     */
+    @TableField("count")
+    private Integer count;
+
+    /**
+     * 校准状态类型:1-迟到;2-早退;3-缺卡;4-旷工;5-地点异常;6-设备异常
+     */
+    @TableField("exception")
+    private Integer exception;
+
+    /**
+     * 时长(秒)
+     */
+    @TableField("duration")
+    private Integer duration;
+
+    /**
+     * 企微用户id
+     */
+    @TableField("corpwx_userid")
+    private String corpwxUserid;
+
+    /**
+     * 日期
+     */
+    @TableField("create_date")
+    private LocalDate createDate;
+
+    /**
+     * 主表id
+     */
+    @TableField("corpwxtime_id")
+    private Integer corpwxtimeId;
+
+    /**
+     * 公司id
+     */
+    @TableField("company_id")
+    private Integer companyId;
+
+    @TableField(exist = false)
+    private String userName;
+
+    @TableField(exist = false)
+    private String departmentName;
+
+    //迟到次数
+    @TableField(exist = false)
+    private Integer lateCount = 0;
+
+    //早退次数
+    @TableField(exist = false)
+    private Integer earlyLeaveCount = 0;
+
+    //缺卡次数
+    @TableField(exist = false)
+    private Integer missPunchCount = 0;
+
+    //旷工次数
+    @TableField(exist = false)
+    private Integer absenteeismCount = 0;
+
+
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

+ 49 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ExceptionInfosSum.java

@@ -0,0 +1,49 @@
+package com.management.platform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.time.LocalDate;
+
+/**
+ * <p>
+ * 
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-24
+ */
+@Data
+public class ExceptionInfosSum {
+    private String corpwxUserid;
+
+    @TableField(exist = false)
+    private String userName;
+
+    @TableField(exist = false)
+    private String departmentName;
+
+    //迟到次数
+    @TableField(exist = false)
+    private Integer lateCount = 0;
+
+    //早退次数
+    @TableField(exist = false)
+    private Integer earlyLeaveCount = 0;
+
+    //缺卡次数
+    @TableField(exist = false)
+    private Integer missPunchCount = 0;
+
+    //旷工次数
+    @TableField(exist = false)
+    private Integer absenteeismCount = 0;
+
+
+}

+ 19 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ExceptionInfosMapper.java

@@ -0,0 +1,19 @@
+package com.management.platform.mapper;
+
+import com.management.platform.entity.ExceptionInfos;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+import java.time.LocalDate;
+import java.util.List;
+
+/**
+ * <p>
+ *  Mapper 接口
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-24
+ */
+public interface ExceptionInfosMapper extends BaseMapper<ExceptionInfos> {
+
+}

+ 20 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ExceptionInfosService.java

@@ -0,0 +1,20 @@
+package com.management.platform.service;
+
+import com.management.platform.entity.ExceptionInfos;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.management.platform.util.HttpRespMsg;
+
+/**
+ * <p>
+ *  服务类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-24
+ */
+public interface ExceptionInfosService extends IService<ExceptionInfos> {
+
+    HttpRespMsg getExceptionReport(Integer companyId, String ymonth, String userId);
+
+    HttpRespMsg exportExceptionReport(Integer companyId, String ymonth, String userId);
+}

+ 145 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ExceptionInfosServiceImpl.java

@@ -0,0 +1,145 @@
+package com.management.platform.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.management.platform.entity.*;
+import com.management.platform.mapper.DepartmentMapper;
+import com.management.platform.mapper.ExceptionInfosMapper;
+import com.management.platform.mapper.UserMapper;
+import com.management.platform.mapper.WxCorpInfoMapper;
+import com.management.platform.service.ExcelExportService;
+import com.management.platform.service.ExceptionInfosService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.management.platform.util.HttpRespMsg;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import javax.annotation.Resource;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * <p>
+ *  服务实现类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2026-05-24
+ */
+@Service
+public class ExceptionInfosServiceImpl extends ServiceImpl<ExceptionInfosMapper, ExceptionInfos> implements ExceptionInfosService {
+
+    @Value(value = "${upload.path}")
+    private String path;
+
+    @Resource
+    private ExceptionInfosMapper exceptionInfosMapper;
+    @Resource
+    private UserMapper userMapper;
+    @Resource
+    private DepartmentMapper departmentMapper;
+    @Resource
+    private ExcelExportService excelExportService;
+    @Resource
+    private WxCorpInfoMapper wxCorpInfoMapper;
+
+    @Override
+    public HttpRespMsg getExceptionReport(Integer companyId, String ymonth, String userId) {
+        HttpRespMsg httpRespMsg = new HttpRespMsg();
+        //获取年月的开始结束日期
+        LocalDate startDate = LocalDate.parse(ymonth+"-01");
+        LocalDate endDate = startDate.plusMonths(1).minusDays(1);
+        List<User> userList = userMapper.selectList(new QueryWrapper<User>().eq("company_id", companyId));
+        List<Department> departmentList = departmentMapper.selectList(new QueryWrapper<Department>().eq("company_id", companyId));
+        QueryWrapper<ExceptionInfos> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("company_id", companyId).between("create_date", startDate, endDate).orderByAsc("corpwx_userid");
+        if (!StringUtils.isEmpty(userId)) {
+            User user = userMapper.selectById(userId);
+            queryWrapper.eq("corpwx_userid", user.getCorpwxUserid());
+        }
+        List<ExceptionInfos> list = exceptionInfosMapper.selectList(queryWrapper);
+        List<ExceptionInfosSum> finalList = new ArrayList<>();
+        String lastCorpwxUserid = null;
+        //按人员进行分组
+        for (int i=0; i<list.size(); i++) {
+            ExceptionInfos exceptionInfos = list.get(i);
+            if (lastCorpwxUserid == null || !lastCorpwxUserid.equals(exceptionInfos.getCorpwxUserid())) {
+                lastCorpwxUserid = exceptionInfos.getCorpwxUserid();
+                ExceptionInfosSum sumItem = new ExceptionInfosSum();
+                sumItem.setCorpwxUserid(exceptionInfos.getCorpwxUserid());
+                addExceptionCount(exceptionInfos, sumItem);
+                finalList.add(sumItem);
+            } else {
+                addExceptionCount(exceptionInfos, finalList.get(finalList.size() - 1));
+            }
+        }
+
+        if (finalList.size() > 0) {
+            for (ExceptionInfosSum exceptionInfos : finalList) {
+                User user = userList.stream().filter(ul -> ul.getCorpwxUserid().equals(exceptionInfos.getCorpwxUserid())).findFirst().orElse(null);
+                if (user != null) {
+                    exceptionInfos.setUserName(user.getName());
+                    if (user.getDepartmentId() != null && user.getDepartmentId() != 0) {
+                        departmentList.stream().filter(dl -> dl.getDepartmentId().equals(user.getDepartmentId())).findFirst().ifPresent(department -> exceptionInfos.setDepartmentName(department.getDepartmentName()));
+                    }
+                }
+            }
+        }
+        httpRespMsg.setData(finalList);
+        return httpRespMsg;
+    }
+
+    private void addExceptionCount(ExceptionInfos currentItem, ExceptionInfosSum finalItem) {
+        int count = currentItem.getCount();
+        if (currentItem.getException() == 1) {//迟到次数
+            finalItem.setLateCount(finalItem.getLateCount() + count);
+        } else if (currentItem.getException() == 2) {//早退次数
+            finalItem.setEarlyLeaveCount(finalItem.getEarlyLeaveCount() + count);
+        } else if (currentItem.getException() == 3) {//缺卡次数
+            finalItem.setMissPunchCount(finalItem.getMissPunchCount() + count);
+        } else if (currentItem.getException() == 4) {//旷工次数
+            finalItem.setAbsenteeismCount(finalItem.getAbsenteeismCount() + count);
+        }
+    }
+
+    @Override
+    public HttpRespMsg exportExceptionReport(Integer companyId, String ymonth, String userId) {
+        // 复用查询逻辑获取数据
+        HttpRespMsg queryResult = getExceptionReport(companyId, ymonth, userId);
+        WxCorpInfo wxCorpInfo = wxCorpInfoMapper.selectOne(new LambdaQueryWrapper<WxCorpInfo>().eq(WxCorpInfo::getCompanyId, companyId));
+        @SuppressWarnings("unchecked")
+        List<ExceptionInfosSum> finalList = (List<ExceptionInfosSum>) queryResult.data;
+
+        // 构建导出数据
+        List<List<String>> exportList = new ArrayList<>();
+        // 表头
+        exportList.add(Arrays.asList("姓名", "部门", "迟到次数", "早退次数", "缺卡次数", "旷工次数", "合计异常次数"));
+        // 数据行
+        for (ExceptionInfosSum item : finalList) {
+            int total = item.getLateCount() + item.getEarlyLeaveCount()
+                    + item.getMissPunchCount() + item.getAbsenteeismCount();
+            exportList.add(Arrays.asList(
+                    item.getUserName() == null ? "" : (wxCorpInfo.getSaasSyncContact() == 1 ? ("$userName=" + item.getUserName() + "$") : item.getUserName()),
+                    item.getDepartmentName() == null ? "" :(wxCorpInfo.getSaasSyncContact() == 1 ? ("$departmentName=" + item.getDepartmentName() + "$") : item.getDepartmentName()),
+                    String.valueOf(item.getLateCount()),
+                    String.valueOf(item.getEarlyLeaveCount()),
+                    String.valueOf(item.getMissPunchCount()),
+                    String.valueOf(item.getAbsenteeismCount()),
+                    String.valueOf(total)
+            ));
+        }
+
+        String fileName = "考勤异常报表_" + ymonth + "_" + System.currentTimeMillis();
+        try {
+            return excelExportService.exportGeneralExcelByTitleAndList(wxCorpInfo, null, fileName, exportList, path);
+        } catch (Exception e) {
+            e.printStackTrace();
+            HttpRespMsg httpRespMsg = new HttpRespMsg();
+            httpRespMsg.setError("导出失败:" + e.getMessage());
+            return httpRespMsg;
+        }
+    }
+}

+ 1 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/UserServiceImpl.java

@@ -2888,7 +2888,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
         }
         ReportForm reportForm = reportFormMapper.selectOne(new QueryWrapper<ReportForm>().eq("report_form_name", tableName));
         List<SysFunction> sysFunctionList = sysFunctionMapper.selectList(new QueryWrapper<SysFunction>().eq("report_form_id", reportForm.getId()));
-        String[] allowTables = new String[]{"日报待审核统计","工时成本预警表","日报待审核","客户项目利润表","异常工时表","餐补表","工时日报表","日报明细表","项目薪资成本表", "部门成本表", "额外工时表"};
+        String[] allowTables = new String[]{"日报待审核统计","工时成本预警表","日报待审核","客户项目利润表","异常工时表","餐补表","工时日报表","日报明细表","项目薪资成本表", "部门成本表", "额外工时表", "考勤异常表"};
         String allName = sysFunctionList.stream().filter(sl -> sl.getName().contains("全公司")||sl.getName().contains("全部")|| Arrays.stream(allowTables).anyMatch(sl.getName()::equals)).findFirst().get().getName();
         String deptName = sysFunctionList.stream().filter(sl -> sl.getName().contains("负责部门")||sl.getName().contains("负责")|| Arrays.stream(allowTables).anyMatch(sl.getName()::equals)).findFirst().get().getName();
         List<SysRichFunction> functionAllList = sysFunctionMapper.getRoleFunctions(user.getRoleId(),allName);

+ 23 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/WxCorpInfoServiceImpl.java

@@ -199,6 +199,8 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
 
     @Resource
     private com.management.platform.service.OvertimeService overtimeService;
+    @Resource
+    private ExceptionInfosMapper exceptionInfosMapper;
 
 
     //获取服务商provider_access_token
@@ -2185,6 +2187,10 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
                             }
                         }
                     }
+                    //保存异常情况记录
+                    if (corpInfo.getCompanyId() == Constant.XI_HE_CHAO_DAO_JIA_XING_COMPANY_ID || corpInfo.getCompanyId() == Constant.XI_HE_CHAO_DAO_COMPANY_ID) {
+                        saveException(ct, jsonObject.getJSONArray("exception_infos"));
+                    }
                 }
             } else {
                 //TODO: 记录同步失败
@@ -2195,6 +2201,23 @@ public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpI
         }
     }
 
+    private void saveException(UserCorpwxTime userCorpwxTime,JSONArray exceptionInfos) {
+        //删除当日的异常
+        exceptionInfosMapper.delete(new QueryWrapper<ExceptionInfos>().eq("corpwxtime_id", userCorpwxTime.getId()));
+        if (exceptionInfos != null && exceptionInfos.size() > 0) {
+            //重新保存
+            for (int i = 0; i < exceptionInfos.size(); i++) {
+                ExceptionInfos exceptionInfos1 = exceptionInfos.getObject(i, ExceptionInfos.class);
+                exceptionInfos1.setCorpwxtimeId(userCorpwxTime.getId());
+                exceptionInfos1.setCreateDate(userCorpwxTime.getCreateDate());
+                exceptionInfos1.setCorpwxUserid(userCorpwxTime.getCorpwxUserid());
+                exceptionInfos1.setCompanyId(userCorpwxTime.getCompanyId());
+                exceptionInfosMapper.insert(exceptionInfos1);
+            }
+        }
+    }
+
+
     private double convertDayTimeToHours(double d) {
         if (d == 12) {
             return 4.0;

+ 21 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ExceptionInfosMapper.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.management.platform.mapper.ExceptionInfosMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.ExceptionInfos">
+        <id column="id" property="id" />
+        <result column="count" property="count" />
+        <result column="exception" property="exception" />
+        <result column="duration" property="duration" />
+        <result column="corpwx_userid" property="corpwxUserid" />
+        <result column="create_date" property="createDate" />
+        <result column="corpwxtime_id" property="corpwxtimeId" />
+        <result column="company_id" property="companyId" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, count, exception, duration, corpwx_userid, create_date, corpwxtime_id, company_id
+    </sql>
+</mapper>

+ 1 - 0
fhKeeper/formulahousekeeper/timesheet/src/permissions.js

@@ -354,6 +354,7 @@ const StringUtil = {
         arr[i] == '全部员工考勤数据' ? obj.allAttendanceRecord = true : ''
         arr[i] == '负责部门员工考勤数据' ? obj.attendanceRecord = true : ''
         arr[i] == '额外工时表' ? obj.extraWorkHours = true : ''
+        arr[i] == '考勤异常表' ? obj.attendanceException = true : ''
         
     }
     return obj

+ 157 - 4
fhKeeper/formulahousekeeper/timesheet/src/views/corpreport/list.vue

@@ -357,6 +357,12 @@
                   @click="ssl(45)"
                   ><p>额外工时表</p></el-menu-item
                 >
+                <el-menu-item
+                  index="1-46"
+                  v-if="permissions.attendanceException"
+                  @click="ssl(46)"
+                  ><p>考勤异常表</p></el-menu-item
+                >
                 <!-- <el-menu-item index="1-45" v-if="permissions.engineeringProgress" @click="ssl(42)"><p>施工进度表</p></el-menu-item> -->
               </el-submenu>
             </el-menu>
@@ -6776,6 +6782,115 @@
               </el-table>
             </template>
 
+            <!-- 考勤异常表 -->
+            <template v-if="ins == 46">
+              <el-table
+                key="46"
+                border
+                :data="exceptionReportList"
+                highlight-current-row
+                v-loading="exceptionReportLoading"
+                :height="+tableHeight"
+                style="width: 100%"
+                :max-height="+tableHeight + 50"
+              >
+                <el-table-column
+                  type="index"
+                  label="序号"
+                  width="60"
+                  align="center"
+                ></el-table-column>
+                <el-table-column
+                  prop="userName"
+                  align="center"
+                  label="姓名"
+                >
+                  <template slot-scope="scope">
+                    <span v-if="user.userNameNeedTranslate == '1'">
+                      <TranslationOpenDataText
+                        type="userName"
+                        :openid="scope.row.corpwxUserid"
+                      ></TranslationOpenDataText>
+                    </span>
+                    <span v-if="user.userNameNeedTranslate != '1'">
+                      {{ scope.row.userName }}
+                    </span>
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  prop="departmentName"
+                  align="center"
+                  label="部门"
+                >
+                  <template slot-scope="scope">
+                    <span v-if="user.userNameNeedTranslate == '1'">
+                      <TranslationOpenDataText
+                        type="departmentName"
+                        :openid="scope.row.departmentName"
+                      ></TranslationOpenDataText>
+                    </span>
+                    <span v-if="user.userNameNeedTranslate != '1'">
+                      {{ scope.row.departmentName }}
+                    </span>
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  prop="lateCount"
+                  align="center"
+                  label="迟到次数"
+                  width="100"
+                >
+                  <template slot-scope="scope">
+                    {{ scope.row.lateCount || 0 }}
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  prop="earlyLeaveCount"
+                  align="center"
+                  label="早退次数"
+                  width="100"
+                >
+                  <template slot-scope="scope">
+                    {{ scope.row.earlyLeaveCount || 0 }}
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  prop="missPunchCount"
+                  align="center"
+                  label="缺卡次数"
+                  width="100"
+                >
+                  <template slot-scope="scope">
+                    {{ scope.row.missPunchCount || 0 }}
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  prop="absenteeismCount"
+                  align="center"
+                  label="旷工次数"
+                  width="100"
+                >
+                  <template slot-scope="scope">
+                    {{ scope.row.absenteeismCount || 0 }}
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  align="center"
+                  label="合计异常次数"
+                  width="120"
+                >
+                  <template slot-scope="scope">
+                    {{
+                      (scope.row.lateCount || 0) +
+                      (scope.row.earlyLeaveCount || 0) +
+                      (scope.row.missPunchCount || 0) +
+                      (scope.row.absenteeismCount || 0)
+                    }}
+                  </template>
+                </el-table-column>
+              </el-table>
+            </template>
+
             <!--工具条-->
             <el-col
               :span="24"
@@ -6795,7 +6910,8 @@
                 ins != 40 &&
                 ins != 41 &&
                 ins != 42 &&
-                ins != 45
+                ins != 45 &&
+                ins != 46
               "
             >
               <el-pagination
@@ -7628,12 +7744,12 @@ export default {
         // 筛选条件的判断
         project: [
           4, 8, 9, 10, 11, 14, 15, 17, 19, 20, 21, 22, 28, 30, 31, 34, 35, 36,
-          37, 38, 40, 41, 42, 43, 44, 45,
+          37, 38, 40, 41, 42, 43, 44, 45, 46,
         ], // 项目筛选条件 (不等于)
-        months: [14, 15, 43, 44], // 月份筛选条件 (等于)
+        months: [14, 15, 43, 44, 46], // 月份筛选条件 (等于)
         monthRange: [19, 30], // 月份区间筛选条件 (等于)
         staff: [
-          6, 8, 9, 19, 11, 14, 18, 23, 25, 26, 28, 30, 32, 35, 36, 37, 38, 39, 45,
+          6, 8, 9, 19, 11, 14, 18, 23, 25, 26, 28, 30, 32, 35, 36, 37, 38, 39, 45, 46,
         ], // 人员筛选条件 (等于)
         departments: [14, 15, 23, 21, 26, 28, 19, 30, 36, 37, 38, 45], // 部门筛选条件 (等于)
         timePeriod: [
@@ -7772,6 +7888,7 @@ export default {
         "员工假勤表",
         "员工考勤表",
         "额外工时表",
+        "考勤异常表",
       ],
 
       shuzArr: [
@@ -7821,6 +7938,7 @@ export default {
         "员工假勤表",
         "员工考勤表",
         "额外工时表",
+        "考勤异常表",
       ],
 
       ins: 10000,
@@ -8097,6 +8215,10 @@ export default {
       // 额外工时表
       extraWorkHoursList: [],
       extraWorkHoursLoading: false,
+
+      // 考勤异常表
+      exceptionReportList: [],
+      exceptionReportLoading: false,
     };
   },
   computed: {},
@@ -9064,6 +9186,9 @@ export default {
       if (this.ins == 45) {
         this.getExtraWorkHours();
       }
+      if (this.ins == 46) {
+        this.getExceptionReport();
+      }
     },
     exportExcel() {
       var url = "/project";
@@ -9498,6 +9623,14 @@ export default {
           sl.departmentId =
             this.departmentIdArray[this.departmentIdArray.length - 1];
         }
+      } else if (this.ins == 46) {
+        fName = `考勤异常表.xlsx`;
+        url = `/exception-infos/exportExceptionReport`;
+        sl.ymonth =
+          this.monthPersonnel || this.dayjs(new Date()).format("YYYY-MM");
+        if (this.userId) {
+          sl.userId = this.userId;
+        }
       }
       this.exportReportLoading = true;
       this.http.post(
@@ -10982,6 +11115,8 @@ export default {
         this.getEmployeeCorpTime();
       } else if (this.ins == 42) {
         this.getConstructionStage();
+      } else if (this.ins == 46) {
+        this.getExceptionReport();
       }
     },
 
@@ -12539,6 +12674,24 @@ export default {
         });
     },
 
+    // 获取考勤异常表
+    getExceptionReport() {
+      this.exceptionReportLoading = true;
+      let parameter = {
+        ymonth: this.monthPersonnel || this.dayjs(new Date()).format("YYYY-MM"),
+      };
+      if (this.userId) {
+        parameter.userId = this.userId;
+      }
+      this.postData(`/exception-infos/getExceptionReport`, parameter)
+        .then((res) => {
+          this.exceptionReportList = Array.isArray(res.data) ? res.data : [];
+        })
+        .finally(() => {
+          this.exceptionReportLoading = false;
+        });
+    },
+
     // 获取施工进度表
     getConstructionStage() {
       this.constructionStageLoading = true;