Просмотр исходного кода

提交异常工时,日常工时上限校验逻辑修改(排除加班工时)

QuYueTing 3 дней назад
Родитель
Сommit
4c77ee186f
34 измененных файлов с 1119 добавлено и 368 удалено
  1. 48 22
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java
  2. 27 49
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserCorpwxTimeController.java
  3. 23 10
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserDingdingTimeController.java
  4. 12 3
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/WeiXinCorpController.java
  5. 8 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/EstimateTimeSetting.java
  6. 3 3
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/SysFunction.java
  7. 11 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/TimeType.java
  8. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/UserDingdingTime.java
  9. 12 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/vo/UserWorkDayVO.java
  10. 4 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ReportMapper.java
  11. 4 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/UserDingdingTimeMapper.java
  12. 4 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ReportService.java
  13. 3 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/UserDingdingTimeService.java
  14. 8 8
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/DingDingServiceImpl.java
  15. 2 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/PermissionServiceImpl.java
  16. 27 86
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectApprovalServiceImpl.java
  17. 266 86
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  18. 2 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/SysRoleServiceImpl.java
  19. 223 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/UserDingdingTimeServiceImpl.java
  20. 5 4
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/UserServiceImpl.java
  21. 42 4
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/util/ExcelUtil.java
  22. 2 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/EstimateTimeSettingMapper.xml
  23. 91 8
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ReportMapper.xml
  24. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/UserDingdingTimeMapper.xml
  25. 2 1
      fhKeeper/formulahousekeeper/timesheet/src/permissions.js
  26. 123 8
      fhKeeper/formulahousekeeper/timesheet/src/views/corpreport/list.vue
  27. 4 1
      fhKeeper/formulahousekeeper/timesheet/src/views/settings/timetype.vue
  28. 63 25
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue
  29. 2 2
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/dailyReportReview.vue
  30. 2 2
      fhKeeper/formulahousekeeper/timesheet_h5/src/main.js
  31. 70 26
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/index.vue
  32. 6 6
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/weekEdit.vue
  33. 2 2
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/review/index.vue
  34. 1 1
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/view/index.vue

+ 48 - 22
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java

@@ -393,7 +393,8 @@ public class ReportController {
                                   Integer[] reportTargetDeptId,
                                   String weeklyAttachment,
                                   String[] extraField4,
-                                  String[] extraField5
+                                  String[] extraField5,
+                                  Boolean abnormalTime
                                     ) {
         List<Report> reportList = new ArrayList<>();
         String token = request.getHeader("Token");
@@ -536,9 +537,14 @@ public class ReportController {
         if (extraField1 == null) {
             extraField1 = new Integer[projectId.length];
             for(int i=0;i<extraField1.length; i++) {
-                extraField1[i] = null;
+                if (abnormalTime != null && abnormalTime) {
+                    extraField1[i] = 1;//标记为异常工时
+                } else {
+                    extraField1[i] = null;
+                }
             }
         }
+
         if (extraField2 == null) {
             extraField2 = new Integer[projectId.length];
             for(int i=0;i<extraField2.length; i++) {
@@ -1478,14 +1484,23 @@ public class ReportController {
                             }
                             BigDecimal hasReport = new BigDecimal(sum).add(new BigDecimal(todayProjectTime));
                             BigDecimal multiply = new BigDecimal(first.get().getManDay()).multiply(new BigDecimal(comTimeType.getAllday()));
+
                             if(hasReport.doubleValue()>multiply.doubleValue()){
-                                sendReportTimeWarningMsg(company, first.get(), null, null);
-                                if (estimateTimeSetting.getProjectFronzeOnLack() == 1) {
-                                    httpRespMsg.setError("超过当前项目["+first.get().getProjectName()+"]预算工时,无法继续提交工时");
-                                    return httpRespMsg;
+                                if (estimateTimeSetting.getProjectExceedPercent() > 0) {
+                                    //检查是否超过预设的提醒百分比;设置的数值是剩余的百分比
+                                    if (hasReport.subtract(multiply).divide(multiply, 2, RoundingMode.HALF_UP).multiply(new BigDecimal(100)).compareTo(new BigDecimal(estimateTimeSetting.getProjectExceedPercent()))>0) {
+                                        httpRespMsg.setError("不得超过当前项目["+first.get().getProjectName()+"]预估工时的"+estimateTimeSetting.getProjectExceedPercent()+"%,无法继续提交工时");
+                                        return httpRespMsg;
+                                    }
                                 } else {
-                                    //仅提醒
-                                    warningLackProjects += first.get().getProjectName() + ",";
+                                    sendReportTimeWarningMsg(company, first.get(), null, null);
+                                    if (estimateTimeSetting.getProjectFronzeOnLack() == 1) {
+                                        httpRespMsg.setError("超过当前项目["+first.get().getProjectName()+"]预估工时,无法继续提交工时");
+                                        return httpRespMsg;
+                                    } else {
+                                        //仅提醒
+                                        warningLackProjects += first.get().getProjectName() + ",";
+                                    }
                                 }
                             } else {
                                 //检查是否超过预设的提醒百分比;设置的数值是剩余的百分比
@@ -1643,17 +1658,12 @@ public class ReportController {
                     return httpRespMsg;
                 }
             } else {
-                //校验下限
+                //校验下限
                 if (dailyWorktime < comTimeType.getMinReportTime()) {
                     HttpRespMsg httpRespMsg = new HttpRespMsg();
                     //httpRespMsg.setError("每日工作时长不得超过"+comTimeType.getAllday()+"小时");
                     httpRespMsg.setError("每日工作时长不得少于" + comTimeType.getMinReportTime() + "小时");
                     return httpRespMsg;
-                } else if (company.getId() != Constant.ZHE_ZHONG_COMPANY_ID && dailyWorktime > comTimeType.getMaxReportTime()) {//陕西柘中不做上限校验
-                    HttpRespMsg httpRespMsg = new HttpRespMsg();
-                    //httpRespMsg.setError("每日工作时长不得超过"+comTimeType.getAllday()+"小时");
-                    httpRespMsg.setError("每日工作时长不得超过" + comTimeType.getMaxReportTime() + "小时");
-                    return httpRespMsg;
                 }
             }
         }
@@ -1673,25 +1683,36 @@ public class ReportController {
                 }
                 //查找同一个人同一天的日报合计工作时长
                 double dailyWorktime = reportList.stream().filter(r->r.getCreatorId().equals(creatorId) && r.getCreateDate().isEqual(cDate)).mapToDouble(Report::getWorkingTime).sum();
+                double overtime = reportList.stream().filter(r->r.getCreatorId().equals(creatorId) && r.getCreateDate().isEqual(cDate) && r.getOvertimeHours() != null).mapToDouble(Report::getOvertimeHours).sum();
+                dailyWorktime = dailyWorktime - overtime;//去掉加班的时长,得到正常工作时间的内的时间。
                 //数据库中已经填报过的工时
                 if (dailyWorktime > comTimeType.getMaxReportTime()) {
                     HttpRespMsg httpRespMsg = new HttpRespMsg();
-                    //httpRespMsg.setError("每日工作时长不得超过"+comTimeType.getMaxReportTime()+"小时");
-                    httpRespMsg.setError(MessageUtils.message("profession.MaxReportTimeError",comTimeType.getMaxReportTime()));
+                    if (overtime > 0) {
+                        httpRespMsg.setError("每日非加班工作时长不得超过"+comTimeType.getMaxReportTime()+"小时");
+                    } else {
+                        httpRespMsg.setError("每日工作时长不得超过"+comTimeType.getMaxReportTime()+"小时");
+                    }
                     return httpRespMsg;
                 } else {
                     //查找数据库中可能已有老的记录
-                    List<Report> oldList = reportMapper.selectList(new QueryWrapper<Report>().select("id, state, working_time").eq("create_date", cDate).eq("creator_id", creatorId));
+                    List<Report> oldList = reportMapper.selectList(new QueryWrapper<Report>().select("id, state, working_time, overtime_hours").eq("create_date", cDate).eq("creator_id", creatorId));
                     //老的记录需要合并计算不在本次reportList中,并且状态是0(待审核)或者1(已通过)的
                     //按周填报都可能会有已存在日报的情况,前端没有传过来
                     double existingWorktime = oldList.stream().filter(old->!reportList.stream().anyMatch(newItem->old.getId().equals(newItem.getId()))
                             && (old.getState() == 0 || old.getState() == 1)).mapToDouble(Report::getWorkingTime).sum();
                     if (existingWorktime > 0) {
                         dailyWorktime += existingWorktime;
-                        if (dailyWorktime > comTimeType.getMaxReportTime()) {
+                        double existingOvertime = oldList.stream().filter(old->!reportList.stream().anyMatch(newItem->old.getId().equals(newItem.getId()))
+                                && (old.getState() == 0 || old.getState() == 1)).mapToDouble(Report::getOvertimeHours).sum();
+                        double nonOverWorkingTime = dailyWorktime - existingOvertime;
+                        if (nonOverWorkingTime > comTimeType.getMaxReportTime()) {
                             HttpRespMsg httpRespMsg = new HttpRespMsg();
-                            //httpRespMsg.setError("每日工作时长不得超过"+comTimeType.getMaxReportTime()+"小时");
-                            httpRespMsg.setError(MessageUtils.message("profession.MaxReportTimeError",comTimeType.getMaxReportTime()));
+                            if ((overtime + existingOvertime) > 0) {
+                                httpRespMsg.setError("每日非加班工作时长不得超过"+comTimeType.getMaxReportTime()+"小时");
+                            } else {
+                                httpRespMsg.setError("每日工作时长不得超过"+comTimeType.getMaxReportTime()+"小时");
+                            }
                             return httpRespMsg;
                         }
                     }
@@ -1704,9 +1725,9 @@ public class ReportController {
                         List<UserDingdingTime> userDingdingTimes = userDingdingTimeMapper.selectList(new QueryWrapper<UserDingdingTime>().eq("user_id", creatorId).eq("work_date", cDate));
                         if (userDingdingTimes.size() > 0) {
                             UserDingdingTime userDingdingTime = userDingdingTimes.get(0);
-                            if (Math.abs(userDingdingTime.getWorkHours() - dailyWorktime) > 0.01) {
+                            if (Math.abs(userDingdingTime.getWorkHours() - (dailyWorktime + overtime)) > 0.01) {
                                 HttpRespMsg httpRespMsg = new HttpRespMsg();
-                                httpRespMsg.setError("每日填报工作时长("+dailyWorktime+"h)必须与考勤时长("+userDingdingTime.getWorkHours()+"h)一致");
+                                httpRespMsg.setError("每日填报工作时长("+dailyWorktime + overtime+"h)必须与考勤时长("+userDingdingTime.getWorkHours()+"h)一致");
                                 return httpRespMsg;
                             }
                         }
@@ -2908,6 +2929,11 @@ public class ReportController {
         return reportService.getReportListByToken(json);
     }
 
+    @RequestMapping("/getAbnormalReportList")
+    public HttpRespMsg getAbnormalReportList(String startDate, String endDate, String userId, Integer pageIndex, Integer pageSize) {
+        return reportService.getAbnormalReportList(startDate, endDate, userId, pageIndex, pageSize);
+    }
+
     @GetMapping("/fixIssue")
     public HttpRespMsg fixIssue(Integer companyId, String startDate, String endDate){
         return reportService.fixIssue(companyId, startDate, endDate);

+ 27 - 49
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserCorpwxTimeController.java

@@ -41,6 +41,8 @@ import java.time.format.TextStyle;
 import java.util.*;
 import java.util.stream.Collectors;
 
+import static com.management.platform.util.ExcelUtil.getCellDateValue;
+
 /**
  * <p>
  *  前端控制器
@@ -58,6 +60,8 @@ public class UserCorpwxTimeController {
     @Resource
     private ExcelParserService excelParserService;
 
+    @Resource
+    private UserDingdingTimeService userDingdingTimeService;
     @Resource
     UserCorpwxTimeMapper userCorpwxTimeMapper;
     @Resource
@@ -131,7 +135,7 @@ public class UserCorpwxTimeController {
         User user = userMapper.selectById(token);
         List<Map> list = new ArrayList<Map>();
 //        int onlyWorkDays = 0;//只要工作日
-        List<SysRichFunction> functionList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "全部企微考勤");
+        List<SysRichFunction> functionList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "查看全部考勤");
         if (functionList.size() > 0) {
             //查看全部人员的
             list = userCorpwxTimeMapper.getUserDataList(user.getCompanyId(), startDate, endDate, null, null);
@@ -150,18 +154,6 @@ public class UserCorpwxTimeController {
             }
         }
 
-        //工作日处理,排除常规周末和法定节假日
-//        if (onlyWorkDays == 1) {
-//            DateTimeFormatter dtf =  DateTimeFormatter.ofPattern("yyyy/MM/dd");
-//            list = list.stream().filter(time->{
-//                String date = (String)time.get("createDate");
-//                if (WorkDayCalculateUtils.isWorkDay(LocalDate.parse(date, dtf))) {
-//                    return true;
-//                } else {
-//                    return false;
-//                }
-//            }).collect(Collectors.toList());
-//        }
         DateTimeFormatter standFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
         HashMap item = new HashMap();
         item.put("list", list);
@@ -379,9 +371,9 @@ public class UserCorpwxTimeController {
         Integer manageDeptId = user.getManageDeptId();
         HttpRespMsg ret = null;
         if (onlySelfData) {
-            ret = getMyData(startDate, endDate);
+            ret = dingding != null ? userDingdingTimeService.getMyData(startDate, endDate) : getMyData(startDate, endDate);
         } else {
-            ret = getMyDeptMembsData(startDate, endDate);
+            ret = dingding != null ? userDingdingTimeService.getMyDeptMembsData(startDate, endDate) : getMyDeptMembsData(startDate, endDate);
         }
 
         HashMap map = (HashMap)ret.data;
@@ -425,14 +417,13 @@ public class UserCorpwxTimeController {
                 continue;
             }
             Double workHours = (Double) dataItem.get("workHours");
-            System.out.println((String)dataItem.get("username") + " workHours:" + workHours);
             if (workHours == null  || workHours.doubleValue() <= 0.0) {
                 continue;
             }
             String createDate = (String)dataItem.get("createDate");
             dataList.add(createDate);
             dataList.add((String)dataItem.get("weekDayTxt"));
-            if(wxCorpInfo.getSaasSyncContact()==1){
+            if(wxCorpInfo != null && wxCorpInfo.getSaasSyncContact()==1){
                 dataList.add("$userName="+(String)dataItem.get("corpwxUserid")+"$");
             }else if(dingding!=null&&dingding.getContactNeedTranslate()==1){
                 dataList.add("$userName="+(String)dataItem.get("username")+"$");
@@ -474,7 +465,7 @@ public class UserCorpwxTimeController {
         Integer manageDeptId = user.getManageDeptId();
 
         List<Map> list;
-        List<SysRichFunction> functionList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "全部企微考勤");
+        List<SysRichFunction> functionList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "查看全部考勤");
         //只取当月中工作日的数据
         DateTimeFormatter dtf1 = DateTimeFormatter.ofPattern("yyyy-MM-dd");
         DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyyMMdd");
@@ -954,14 +945,14 @@ public class UserCorpwxTimeController {
      * @return 处理结果
      */
     @RequestMapping("/importAndCompareWorktime")
-    public HttpRespMsg importAndCompareWorktime(MultipartFile multipartFile, HttpServletRequest request) {
+    public HttpRespMsg importAndCompareWorktime(String yearMonth, MultipartFile multipartFile, HttpServletRequest request) {
         HttpRespMsg msg = new HttpRespMsg();
         // 固定处理公司ID为7544的数据
         Integer companyId = 7544;
-        //清空2025年全年的日报
+        //清空yearMonth指定年月的工时
         //查询日志
-        reportMapper.delete(new QueryWrapper<Report>().eq("company_id", companyId).between("create_date", "2025-01-01", "2025-12-31"));
-        reportLogMapper.delete(new QueryWrapper<ReportLog>().eq("company_id", companyId).between("create_date", "2025-01-01", "2025-12-31"));
+        reportMapper.delete(new QueryWrapper<Report>().eq("company_id", companyId).between("create_date", yearMonth + "-01", yearMonth + "-31"));
+        reportLogMapper.delete(new QueryWrapper<ReportLog>().eq("company_id", companyId).between("create_date", yearMonth + "-01", yearMonth + "-31"));
         String fileName = multipartFile.getOriginalFilename();
         File file = new File(fileName == null ? "file" : fileName);
         InputStream inputStream = null;
@@ -1362,24 +1353,7 @@ public class UserCorpwxTimeController {
         System.out.println(notFoundUserNames);
     }
     
-    /**
-     * 从Sheet名称中提取年月信息
-     */
-    private String extractYearMonth(String sheetName) {
-        // 处理类似 "1月", "2月" 等格式,默认为2025年
-        if (sheetName.matches("\\d+月")) {
-            int month = Integer.parseInt(sheetName.replace("月", ""));
-            return String.format("2025%02d", month);
-        }
-        
-        // 处理其他可能的格式
-        if (sheetName.matches("\\d{4}-\\d{1,2}")) {
-            String[] parts = sheetName.split("-");
-            return String.format("%s%02d", parts[0], Integer.parseInt(parts[1]));
-        }
-        
-        return null;
-    }
+
     
     /**
      * 根据姓名或工号查找用户
@@ -2181,21 +2155,25 @@ public class UserCorpwxTimeController {
         Row headerRow = staffSheet.getRow(0);
         List<String> monthColumns = new ArrayList<>();
         LocalDate now = LocalDate.now();
-        //设置日期为2025-01-01,以便计算月份
-        LocalDate startMonth = now.withDayOfMonth(1).withMonth(1).withYear(startYear);
-
+        int startYearColIndex = -1;
         if (headerRow != null) {
             for (int colIndex = 11; colIndex < 11 + 24; colIndex++) { // 第12列开始(索引11)
                 Cell cell = headerRow.getCell(colIndex);
                 if (cell != null) {
-                    String monthStr = startMonth.plusMonths(colIndex - 11).format(DateTimeFormatter.ofPattern("yyyy-MM"));
-                    monthColumns.add(monthStr);
+                    String dateValue = getCellDateValue(cell);
+                    String headMonth = dateValue.substring(0,"2026-01".length());
+                    if (headMonth.startsWith(startYear.toString())) {
+                        monthColumns.add(headMonth);
+                        if (startYearColIndex == -1) {
+                            startYearColIndex = colIndex;
+                        }
+                    }
                 }
             }
         }
         
         System.out.println("找到月份列: " + monthColumns);
-        
+        if (monthColumns.size() == 0) return result;
         // 收集所有员工姓名用于批量查询
         List<String> userNameList = new ArrayList<>();
         for (int rowIndex = 1; rowIndex <= staffSheet.getLastRowNum(); rowIndex++) {
@@ -2329,13 +2307,13 @@ public class UserCorpwxTimeController {
                 
                 // 解析工时月份(从第12列开始,索引11)
                 List<String> availableMonths = new ArrayList<>();
-                for (int colIndex = 0; colIndex < monthColumns.size(); colIndex++) {
-                    Cell monthCell = row.getCell(11 + colIndex);
+                for (int colIndex = startYearColIndex; colIndex < startYearColIndex + monthColumns.size(); colIndex++) {
+                    Cell monthCell = row.getCell(colIndex);
                     if (monthCell != null) {
                         String cellValue = getCellStringValue(monthCell);
                         // 检查是否打对钩(√ 或其他标记)
                         if ("√".equals(cellValue) || "✓".equals(cellValue) || "1".equals(cellValue) || "是".equals(cellValue)) {
-                            availableMonths.add(monthColumns.get(colIndex));
+                            availableMonths.add(monthColumns.get(colIndex - startYearColIndex));
                         }
                     }
                 }

+ 23 - 10
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserDingdingTimeController.java

@@ -3,14 +3,10 @@ package com.management.platform.controller;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.management.platform.entity.CompanyDingding;
-import com.management.platform.entity.LeaveSheet;
-import com.management.platform.entity.User;
-import com.management.platform.entity.UserDingdingTime;
-import com.management.platform.mapper.CompanyDingdingMapper;
-import com.management.platform.mapper.LeaveSheetMapper;
-import com.management.platform.mapper.UserDingdingTimeMapper;
-import com.management.platform.mapper.UserMapper;
+import com.management.platform.entity.*;
+import com.management.platform.mapper.*;
+import com.management.platform.service.ReportService;
+import com.management.platform.service.UserDingdingTimeService;
 import com.management.platform.util.HttpRespMsg;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -19,7 +15,14 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -39,8 +42,8 @@ public class UserDingdingTimeController {
     private UserMapper userMapper;
     @Resource
     private CompanyDingdingMapper companyDingdingMapper;
-    @Autowired
-    private LeaveSheetMapper leaveSheetMapper;
+    @Resource
+    private UserDingdingTimeService userDingdingTimeService;
 
     @RequestMapping("/getUserCardTime")
     public HttpRespMsg getUserCardTime(String userId, String date) {
@@ -95,5 +98,15 @@ public class UserDingdingTimeController {
         msg.data = userDingdingTimeMapper.selectOne(queryWrapper);
         return msg;
     }
+
+    @RequestMapping("/getMyData")
+    public HttpRespMsg getMyData(String startDate, String endDate) {
+        return userDingdingTimeService.getMyData(startDate, endDate);
+    }
+
+    @RequestMapping("/getMyDeptMembsData")
+    public HttpRespMsg getMyDeptMembsData(String startDate, String endDate) {
+        return userDingdingTimeService.getMyDeptMembsData(startDate, endDate);
+    }
 }
 

+ 12 - 3
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/WeiXinCorpController.java

@@ -4098,8 +4098,11 @@ public class WeiXinCorpController {
         HttpRespMsg msg=new HttpRespMsg();
         JSONArray jsonArrayFilter = new JSONArray();
         JSONObject filter1 = new JSONObject();
-        filter1.put("key","record_type");
-        filter1.put("value",5);
+//        filter1.put("key","record_type");
+//        filter1.put("value",5);
+//        jsonArrayFilter.add(filter1);
+        filter1.put("key","template_id");
+        filter1.put("value","C4ehNNZ2vtdaWtU3o1xfXybvDENKM6kUoFbucvMua");
         jsonArrayFilter.add(filter1);
         JSONObject filter2 = new JSONObject();
         filter2.put("key","sp_status");
@@ -4108,7 +4111,13 @@ public class WeiXinCorpController {
         JSONArray approvalInfo = wxCorpInfoService.getApprovalInfo(companyId, startDate, endDate, "", jsonArrayFilter);
         //TODO: 获取审批单详情
 
-        msg.setData(approvalInfo.toArray());
+        JSONArray approvalDetailList = new JSONArray();
+        for (int i=0;i<approvalInfo.size(); i++) {
+            String spNo = approvalInfo.getString(i);
+            JSONObject approvalDetail = wxCorpInfoService.getApprovalDetail(7, spNo);
+            approvalDetailList.add(approvalDetail);
+        }
+        msg.setData(approvalDetailList.toArray());
         return msg;
     }
 

+ 8 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/EstimateTimeSetting.java

@@ -14,7 +14,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2023-11-19
+ * @since 2026-02-25
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -30,7 +30,7 @@ public class EstimateTimeSetting extends Model<EstimateTimeSetting> {
     private Integer companyId;
 
     /**
-     * 项目人天填写要求:0-非必填,1-工时系统必填,同步的非必填,2-全部必填
+     * 项目人天填写要求:0-非必填,1-工时系统必填,同步的非必填,2-工时系统和外部同步的都必填
      */
     @TableField("project_man_day_fill_mode")
     private Integer projectManDayFillMode;
@@ -59,6 +59,12 @@ public class EstimateTimeSetting extends Model<EstimateTimeSetting> {
     @TableField("group_fronze_on_lack")
     private Integer groupFronzeOnLack;
 
+    /**
+     * 超过项目预估工时的比例设置,禁止填报
+     */
+    @TableField("project_exceed_percent")
+    private Integer projectExceedPercent;
+
 
     @Override
     protected Serializable pkVal() {

+ 3 - 3
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/SysFunction.java

@@ -119,10 +119,10 @@ public class SysFunction extends Model<SysFunction> {
     private Integer packageFinance;
 
     /**
-     * 是否属于同步企业微信
+     * 是否属于考勤打卡
      */
-    @TableField("sync_corpwx_time")
-    private Integer syncCorpwxTime;
+    @TableField("sync_card_time")
+    private Integer syncCardTime;
 
     /**
      * 财务上传是否需要审核

+ 11 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/TimeType.java

@@ -686,6 +686,17 @@ public class TimeType extends Model<TimeType> {
     @TableField("not_check_cardtime")
     private Boolean notCheckCardtime;
 
+    /**
+     * 工时填报隐藏分组
+     */
+    @TableField("hide_group")
+    private Boolean hideGroup;
+
+    /**
+     * 工时填报隐藏工作内容
+     */
+    @TableField("hide_content")
+    private Boolean hideContent;
 
     @TableField(exist = false)
     private List<User> userList;

+ 1 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/UserDingdingTime.java

@@ -64,7 +64,7 @@ public class UserDingdingTime extends Model<UserDingdingTime> {
     private String weekDayTxt;
 
     @TableField("work_hours")
-    private Float workHours;
+    private Double workHours;
 
     @TableField("ask_leave_time")
     private Float askLeaveTime;

+ 12 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/vo/UserWorkDayVO.java

@@ -0,0 +1,12 @@
+package com.management.platform.entity.vo;
+
+import lombok.Data;
+
+import java.time.LocalDate;
+
+@Data
+public class UserWorkDayVO {
+    private String userId;
+    private String userName;
+    private String workDate;
+}

+ 4 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ReportMapper.java

@@ -280,4 +280,8 @@ public interface ReportMapper extends BaseMapper<Report> {
                                                                            @Param("branchDepartment")List<Integer> branchDepartment,@Param("field")String field,@Param("fieldValue")String fieldValue);
 
     List<Map<String, Object>> getJingYuErrorData(List<String> userIds, String startDate, String endDate);
+
+    Integer getAbnormalDataCount(Integer companyId, String startDate, String endDate, String userId);
+
+    List<Map<String, Object>> getAbnormalData(Integer companyId, String startDate, String endDate, String userId, Integer startPage, Integer pageSize);
 }

+ 4 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/UserDingdingTimeMapper.java

@@ -3,6 +3,9 @@ package com.management.platform.mapper;
 import com.management.platform.entity.UserDingdingTime;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 
+import java.util.List;
+import java.util.Map;
+
 /**
  * <p>
  *  Mapper 接口
@@ -12,5 +15,6 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
  * @since 2022-04-25
  */
 public interface UserDingdingTimeMapper extends BaseMapper<UserDingdingTime> {
+    public List<Map> getUserDataList(Integer companyId, String startDate, String endDate, Integer deptId, String userId);
 
 }

+ 4 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ReportService.java

@@ -32,6 +32,8 @@ public interface ReportService extends IService<Report> {
 
     HttpRespMsg getCardTime(String date, HttpServletRequest request);
 
+    HttpRespMsg getMissingCardTimeUserList(String startDate, String endDate, HttpServletRequest request);
+
     HttpRespMsg editReport(List<Report> reportList, String date, List<User> userList, BigDecimal hourCost, Integer companyId, String summary, String weeklyAttachment);
 
     HttpRespMsg deleteReport(String userId, String date);
@@ -202,4 +204,6 @@ public interface ReportService extends IService<Report> {
     List<String> getUserMissingReportDates(String userId, String startDate, String endDate);
 
     HttpRespMsg updateNoReportUserList(HttpServletRequest request, String startTime, String endTime,String createDate,Double leaveDuration, String corpwxUserId);
+
+    HttpRespMsg getAbnormalReportList(String startDate, String endDate, String userId, Integer pageIndex, Integer pageSize);
 }

+ 3 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/UserDingdingTimeService.java

@@ -2,6 +2,7 @@ package com.management.platform.service;
 
 import com.management.platform.entity.UserDingdingTime;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.management.platform.util.HttpRespMsg;
 
 /**
  * <p>
@@ -12,5 +13,6 @@ import com.baomidou.mybatisplus.extension.service.IService;
  * @since 2022-04-25
  */
 public interface UserDingdingTimeService extends IService<UserDingdingTime> {
-
+    public HttpRespMsg getMyData(String startDate, String endDate);
+    public HttpRespMsg getMyDeptMembsData(String startDate, String endDate);
 }

+ 8 - 8
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/DingDingServiceImpl.java

@@ -763,7 +763,7 @@ public class DingDingServiceImpl implements DingDingService {
             double hours = seconds/3600.0;
             UserDingdingTime upItem = new UserDingdingTime();
             upItem.setId(time.getId());
-            upItem.setWorkHours((float)hours);
+            upItem.setWorkHours((double)hours);
             upData.add(upItem);
             System.out.println("修改工时为:"+hours);
             count++;
@@ -2033,7 +2033,7 @@ public class DingDingServiceImpl implements DingDingService {
                         }
                         //计算上下班间隔时长
                         long deltaTimeSec = Duration.between(onDutyEarleast, offDutyLatest).getSeconds();
-                        float hours = deltaTimeSec*1.0f/3600;
+                        double hours = deltaTimeSec*1.0f/3600;
                         cardTime.setWorkHours(hours);
                         if (showLog) System.out.println(user.getName()+", "+cardTime.getWorkDate().toString()+" 上下班打卡时间:"+onDutyEarleast+" - "+offDutyLatest+", 时长:"+hours);
                         //对比,看看之前是否已经存了
@@ -2397,10 +2397,10 @@ public class DingDingServiceImpl implements DingDingService {
                                         }
                                     }
                                 }
-                                timeItem.setWorkHours((float)workHours);
+                                timeItem.setWorkHours((double)workHours);
                             } else {
                                 long seconds = (offDutyLatest.getUserCheckTime() - onDutyEarleast.getUserCheckTime())/1000;
-                                float hours = seconds*1.0f/3600;
+                                double hours = seconds*1.0f/3600;
                                 timeItem.setWorkHours(hours);
                             }
                             //对比,看看之前是否已经存了
@@ -2941,12 +2941,12 @@ public class DingDingServiceImpl implements DingDingService {
                         //第一天
                         long seconds = end.toEpochSecond(ZoneOffset.ofHours(8)) - start.toEpochSecond(ZoneOffset.ofHours(8));
                         double workHours = DateTimeUtil.getHoursFromDouble(DateTimeUtil.getHoursFromSeconds((int) seconds));
-                        userDingdingTime.setWorkHours((float)workHours);
+                        userDingdingTime.setWorkHours((double)workHours);
                     } else if (endDateStr.equals(date)) {
                         //最后一天
                         long seconds = end.toEpochSecond(ZoneOffset.ofHours(8)) - start.toEpochSecond(ZoneOffset.ofHours(8));
                         double workHours = DateTimeUtil.getHoursFromDouble(DateTimeUtil.getHoursFromSeconds((int) seconds));
-                        userDingdingTime.setWorkHours((float)workHours);
+                        userDingdingTime.setWorkHours((double)workHours);
                     } else {
                         //中间天,就是全天
                         LocalDateTime normalStart = LocalDateTime.parse(realPlanStartTime);
@@ -2956,13 +2956,13 @@ public class DingDingServiceImpl implements DingDingService {
                         //计算realPlanEndTime-realPlanStartTime
                         long seconds = normalEnd.toEpochSecond(ZoneOffset.ofHours(8)) - normalStart.toEpochSecond(ZoneOffset.ofHours(8));
                         double workHours = DateTimeUtil.getHoursFromDouble(DateTimeUtil.getHoursFromSeconds((int) seconds));
-                        userDingdingTime.setWorkHours((float)(workHours));
+                        userDingdingTime.setWorkHours((double)(workHours));
                     }
                 } else {
                     //就是当天,计算时长
                     long seconds = end.toEpochSecond(ZoneOffset.ofHours(8)) - start.toEpochSecond(ZoneOffset.ofHours(8));
                     double workHours = DateTimeUtil.getHoursFromDouble(DateTimeUtil.getHoursFromSeconds((int) seconds));
-                    userDingdingTime.setWorkHours((float)workHours);
+                    userDingdingTime.setWorkHours((double)workHours);
                 }
                 if (userDingdingTime.getWorkHours() > 8.0) {
                     //全天的情况,要减去中间的休息时间

+ 2 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/PermissionServiceImpl.java

@@ -265,8 +265,8 @@ public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permiss
 //            if (company.getPackageProvider() == 1) {
 //                wrapper.or().eq("package_provider", 1);
 //            }
-            if (timeType.getSyncCorpwxTime() == 1) {
-                wrapper.or().eq("sync_corpwx_time", 1);
+            if (timeType.getSyncCorpwxTime() == 1 || timeType.getSyncDingding() == 1) {
+                wrapper.or().eq("sync_card_time", 1);
             }
             //开通财务审核功能的
             if (timeType.getFinanceAudit() == 1) {

+ 27 - 86
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectApprovalServiceImpl.java

@@ -20,6 +20,7 @@ import org.apache.poi.xssf.usermodel.XSSFRow;
 import org.apache.poi.xssf.usermodel.XSSFSheet;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
@@ -105,7 +106,8 @@ public class ProjectApprovalServiceImpl extends ServiceImpl<ProjectApprovalMappe
     private CompanyDingdingMapper companyDingdingMapper;
     @Value(value = "${upload.path}")
     private String path;
-
+    @Autowired
+    private ProjectLevelMapper projectLevelMapper;
 
 
     @Override
@@ -198,8 +200,10 @@ public class ProjectApprovalServiceImpl extends ServiceImpl<ProjectApprovalMappe
     public HttpRespMsg importData(MultipartFile multipartFile) {
         HttpRespMsg msg=new HttpRespMsg();
         User user = userMapper.selectById(request.getHeader("token"));
-        String[] arr=new String[]{"正常","紧急","重要","重要且紧急","低风险","中风险","高风险"};
+//        String[] arr=new String[]{"正常","紧急","重要","重要且紧急","低风险","中风险","高风险"};
         WxCorpInfo wxCorpInfo = wxCorpInfoService.getOne(new LambdaQueryWrapper<WxCorpInfo>().eq(WxCorpInfo::getCompanyId, user.getCompanyId()));
+        Integer companyId = wxCorpInfo.getCompanyId();
+        List<ProjectLevel> projectLevels = projectLevelMapper.selectList(new LambdaQueryWrapper<ProjectLevel>().eq(ProjectLevel::getCompanyId, companyId));
         CompanyDingding dingding = companyDingdingMapper.selectOne(new LambdaQueryWrapper<CompanyDingding>().eq(CompanyDingding::getCompanyId, user.getCompanyId()));
         TimeType timeType = timeTypeMapper.selectById(user.getCompanyId());
         String fileName = multipartFile.getOriginalFilename();
@@ -227,14 +231,6 @@ public class ProjectApprovalServiceImpl extends ServiceImpl<ProjectApprovalMappe
             Company company = companyMapper.selectById(user.getCompanyId());
             //由于第一行需要指明列对应的标题
             int rowNum = sheet.getLastRowNum();
-            HashMap<String, Integer> projectLevelMap = new HashMap<>();
-            projectLevelMap.put(MessageUtils.message("excel.normal"), 1);
-            projectLevelMap.put(MessageUtils.message("excel.urgent"), 2);
-            projectLevelMap.put(MessageUtils.message("excel.important"), 3);
-            projectLevelMap.put(MessageUtils.message("excel.impAndUrg"), 4);
-            projectLevelMap.put(MessageUtils.message("excel.lowRisk"), 5);
-            projectLevelMap.put(MessageUtils.message("excel.MedRisk"), 6);
-            projectLevelMap.put(MessageUtils.message("excel.highRisk"), 7);
             List<String> existCodeList = new ArrayList<>();
             List<String> userNameList=new ArrayList<>();
             for (int rowIndex = 0; rowIndex <= rowNum; rowIndex++) {
@@ -451,7 +447,7 @@ public class ProjectApprovalServiceImpl extends ServiceImpl<ProjectApprovalMappe
                 if (levelCell != null) {
                     String levelStr = levelCell.getStringCellValue();
                     if (!StringUtils.isEmpty(levelStr)) {
-                        project.setLevel(projectLevelMap.get(levelStr));
+                        projectLevels.stream().filter(pl -> pl.getProjectLevelName().equals(levelStr)).findFirst().ifPresent(pl -> project.setLevel(pl.getId()));
                     }
                 }
                 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
@@ -486,84 +482,29 @@ public class ProjectApprovalServiceImpl extends ServiceImpl<ProjectApprovalMappe
                     projectApprovalLogService.save(projectApprovalLog);
                 }
                 if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1){
-                    //更新审核人 发送到企业微信完成审核动作
-                    String detail = wxCorpInfoService.getTemplateDetail(user.getCompanyId());
-                    if(detail!=null&&!StringUtils.isEmpty(detail)){
-                        DateTimeFormatter df=DateTimeFormatter.ofPattern("yyyy-MM-dd");
-                        JSONObject resultOb = JSONObject.parseObject(detail);
-                        JSONObject template_content = resultOb.getJSONObject("template_content");
-                        JSONArray controls = template_content.getJSONArray("controls");
-                        Map<String,Object> templateMap=new HashMap<>();
-                        WxCorpTemplate template = wxCorpTemplateService.getById(user.getCompanyId());
-                        JSONObject requestData=new JSONObject();
-                        requestData.put("template_id",template.getTemplateId());
-                        requestData.put("creator_userid",user.getCorpwxUserid());
-                        //审核模式  0-通过接口指定审批人、抄送人(此时approver、notifyer等参数可用);1-使用此模板在管理后台设置的审批流程(需要保证审批流程中没有“申请人自选”节点),支持条件审批
-                        requestData.put("use_template_approver",1);
-                        JSONObject apply_data=new JSONObject();
-                        JSONArray contents=new JSONArray();
-                        for (int j = 0; j < controls.size(); j++) {
-                            JSONObject item = controls.getJSONObject(j);
-                            JSONObject property = item.getJSONObject("property");
-                            //控件id指定到指定控件
-                            String templateId = property.getString("id");
-                            //控件名称 这里为了统一工时管家格式 限制企业需要用到我们提供的名称(项目编号 项目名称 项目分类 项目类型 项目经理 级别 合同金额 计划开始日期 计划结束日期)
-                            JSONArray titleArray = property.getJSONArray("title");
-                            //控件类型 在这里统一用Text  Text-文本;Textarea-多行文本;Number-数字;Money-金额;Date-日期/日期+时间;Selector-单选/多选;Contact-成员/部门;Tips-说明文字;File-附件;Table-明细;Attendance-假勤控件;Vacation-请假控件;Location-位置;RelatedApproval-关联审批单;Formula-公式;DateRange-时长;BankAccount-收款账户
-                            String control = property.getString("control");
-                            //是否必填
-                            String require = property.getString("require");
-                            //是否打印
-                            String un_print = property.getString("un_print");
-                            JSONObject content=new JSONObject();
-                            content.put("control",control);
-                            content.put("id",templateId);
-                            JSONObject value=new JSONObject();
-                            JSONObject titleOb = titleArray.getJSONObject(0);
-                            switch (titleOb.getString("text")){
-                                case "项目编号":
-                                    value.put("text",project.getProjectCode()==null?"":project.getProjectCode());
-                                    break;
-                                case "项目名称":
-                                    value.put("text",project.getProjectName());
-                                    break;
-                                case "项目分类":
-                                    value.put("text",project.getCategoryName()==null?"":project.getCategoryName());
-                                    break;
-                                case "项目类型":
-                                    value.put("text",project.getIsPublic()==0?"正式项目":"非项目");
-                                    break;
-                                case "项目经理":
-                                    value.put("text",project.getInchargerName()==null?"":project.getInchargerName());
-                                    break;
-                                case "级别":
-                                    value.put("text",arr[project.getLevel()-1]);
-                                    break;
-                                case "合同金额":
-                                    value.put("text",project.getContractAmount()==null?String.valueOf(0):String.valueOf(project.getContractAmount()));
-                                    break;
-                                case "计划开始日期":
-                                    value.put("text",project.getPlanStartDate()==null?"":df.format(project.getPlanStartDate()));
-                                    break;
-                                case "计划结束日期":
-                                    value.put("text",project.getPlanEndDate()==null?"":df.format(project.getPlanEndDate()));
-                                    break;
+                    //发送待审核消息
+                    if (project.getProjectApprovalCheckList() != null && project.getProjectApprovalCheckList().size() > 0) {
+                        //取第一个审核人,发送消息通知
+                        String checkerId = project.getProjectApprovalCheckList().get(0).getUserId();
+                        User checker = userMapper.selectById(checkerId);
+                        if (checker != null) {
+                            StringBuilder sb=new StringBuilder();
+                            sb.append("立项审批,项目["+project.getProjectName()+"]待您审核");
+                            String corpwxUserid = checker.getCorpwxUserid();
+                            if (corpwxUserid != null) {
+                                wxCorpInfoService.sendWXCorpMsg(wxCorpInfo, corpwxUserid,sb.toString(), "projectApproval", WxCorpInfoServiceImpl.TEXT_CARD_MSG_PROJECT_CREATE);
                             }
-                            content.put("value",value);
-                            contents.add(content);
+                            //系统消息
+                            Information information=new Information();
+                            information.setType(12);
+                            information.setUserId(checkerId);
+                            information.setTime(LocalDateTime.now());
+                            information.setContent("projectApproval");
+                            information.setMsg("立项审批,项目["+project.getProjectName()+"]待您审核");
+                            informationMapper.insert(information);
                         }
-                        apply_data.put("contents",contents);
-                        requestData.put("apply_data",apply_data);
-                        requestData.put("approver",new JSONArray());
-                        requestData.put("summary_list",new JSONArray());
-                        System.out.println("===============>发送到企业微信的数据"+requestData);
-                        //发送到企业微信
-                        String sp_no = wxCorpInfoService.applyEvent(request, requestData);
-                        project.setWxCorpSpNo(sp_no);
-                        projectApprovalService.updateById(project);
-                    }else {
-                        throw  new Exception("企业暂未同步服务商审批模板,推送企业微信审批失败,请联系服务商!");
                     }
+
                 }
                 importCount++;
                 //导入项目参与人,遵守只增不减的原则, 避免误删

+ 266 - 86
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java

@@ -14,6 +14,7 @@ import com.management.platform.service.*;
 import com.management.platform.util.*;
 import com.management.platform.webservice.po.*;
 import lombok.SneakyThrows;
+import lombok.Synchronized;
 import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
 import me.chanjar.weixin.mp.api.WxMpService;
 import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
@@ -340,14 +341,19 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                     //再根据人分别获取当天的报告
                     List<Map<String, Object>> rList = new ArrayList<Map<String, Object>>();
                     BigDecimal total = new BigDecimal(0);
+                    boolean abnormalTime = false;
                     for (Map<String, Object> report : reportList) {
                         if (((String)report.get("creatorId")).equals((String)memb.get("id"))) {
                             rList.add(report);
                             total = total.add((BigDecimal) report.get("cost"));
+                            if(report.get("extraField1") != null && (int)report.get("extraField1") == 1) {
+                                abnormalTime = true;
+                            }
                         }
                     }
                     memb.put("data", rList);
                     memb.put("cost", total);
+                    memb.put("abnormalTime", abnormalTime);
                     double reportTime = 0;
                     if (rList.size() > 0) {
                         int state = 1;
@@ -1236,6 +1242,90 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         return msg;
     }
 
+    /**
+     * 获取工作日考勤缺失的用户列表
+     * @param startDate
+     * @param endDate
+     * @param request
+     * @return
+     */
+    @Override
+    public HttpRespMsg getMissingCardTimeUserList(String startDate, String endDate, HttpServletRequest request) {
+        String token = request.getHeader("TOKEN");
+        User user = userMapper.selectById(token);
+        int companyId = user.getCompanyId();
+        TimeType timeType = timeTypeMapper.selectById(companyId);
+        HttpRespMsg msg = new HttpRespMsg();
+        LocalDate start = LocalDate.parse(startDate);
+        LocalDate end = LocalDate.parse(endDate);
+        //间隔不能超过31天
+        if (start.until(end, ChronoUnit.DAYS) > 31) {
+            msg.setError("日期间隔不能超过31天");
+            return msg;
+        }
+        List<User> userList = userMapper.selectList(new QueryWrapper<User>().eq("company_id", companyId)
+                .and(wrapper->wrapper.eq("is_active", 1)
+                        .or(w2->w2.eq("is_active", 0).ge("inactive_date", startDate))));
+        List<UserWorkDayVO> retDataList = new ArrayList<>();
+        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        if (timeType.getShowDdCardtime() == 1) {
+            List<UserDingdingTime> dingdingTimeList = userDingdingTimeMapper.selectList(new QueryWrapper<UserDingdingTime>().eq("company_id", companyId)
+                    .between("work_date", startDate, endDate));
+            while (!start.isAfter(end)) {
+                if (WorkDayCalculateUtils.isWorkDay(start)) {
+                    String startStr = df.format(start);
+                    for (User curUser : userList) {
+                        boolean find = dingdingTimeList.stream().anyMatch(time->time.getUserId().equals(curUser.getId()) && df.format(time.getWorkDate()).equals(startStr));
+                        if (!find) {
+                            UserWorkDayVO userCorpwxTime = new UserWorkDayVO();
+                            userCorpwxTime.setUserId(curUser.getId());
+                            userCorpwxTime.setUserName(curUser.getName());
+                            userCorpwxTime.setWorkDate(startStr);
+                            retDataList.add(userCorpwxTime);
+                        }
+                    }
+                }
+                start = start.plusDays(1);
+            }
+        }else if(timeType.getSyncFanwei()==1){
+            List<UserFvTime> userFvTimeList = userFvTimeMapper.selectList(new QueryWrapper<UserFvTime>().eq("company_id", companyId)
+                    .between("work_date", startDate, endDate));
+            while (start.isBefore(end)) {
+                String startStr = df.format(start);
+                for (User curUser : userList) {
+                    boolean find = userFvTimeList.stream().anyMatch(time->time.getUserId().equals(curUser.getId()) && df.format(time.getWorkDate()).equals(startStr));
+                    if (!find) {
+                        UserWorkDayVO userCorpwxTime = new UserWorkDayVO();
+                        userCorpwxTime.setUserId(curUser.getId());
+                        userCorpwxTime.setUserName(curUser.getName());
+                        userCorpwxTime.setWorkDate(startStr);
+                        retDataList.add(userCorpwxTime);
+                    }
+                }
+                start = start.plusDays(1);
+            }
+        }else if (timeType.getSyncCorpwxTime() == 1) {
+            List<UserCorpwxTime> corpwxTimeList = userCorpwxTimeMapper.selectList(new QueryWrapper<UserCorpwxTime>().eq("company_id", companyId)
+                    .between("create_date", startDate, endDate));
+            while (start.isBefore(end)) {
+                String startStr = df.format(start);
+                for (User curUser : userList) {
+                    boolean find = corpwxTimeList.stream().anyMatch(time -> time.getCorpwxUserid().equals(curUser.getCorpwxUserid()) && df.format(time.getCreateDate()).equals(startStr));
+                    if (!find) {
+                        UserWorkDayVO userCorpwxTime = new UserWorkDayVO();
+                        userCorpwxTime.setUserId(curUser.getId());
+                        userCorpwxTime.setUserName(curUser.getName());
+                        userCorpwxTime.setWorkDate(startStr);
+                        retDataList.add(userCorpwxTime);
+                    }
+                }
+                start = start.plusDays(1);
+            }
+        }
+        msg.setData(retDataList);
+        return msg;
+    }
+
     //新增或编辑报告
     @Override
     @Transactional(rollbackFor = Exception.class)
@@ -1834,6 +1924,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 String name = (String)a.get("name");
                 String uid = (String)a.get("userId");
                 String corpwxUserid = (String)a.get("corpwxUserid");
+                boolean abnormalTime = a.get("extraField1") != null && (int)a.get("extraField1") == 1;
 
                 if (lastName == null || !(lastName.get("name").equals(name) && lastName.get("dateStr").equals(createDate))) {
                     lastName = new HashMap<String, Object>();
@@ -1841,6 +1932,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                     lastName.put("name", name);
                     lastName.put("userId", uid);
                     lastName.put("corpwxUserid", corpwxUserid);
+                    lastName.put("abnormalTime", abnormalTime);
                     nameList.add(lastName);
                     userDailyReportList = new ArrayList<>();
                     lastName.put("data", userDailyReportList);
@@ -2188,6 +2280,8 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 String uid = (String)a.get("userId");
                 String corpwxUserid = (String)a.get("corpwxUserid");
                 String fillUserName = (String)a.get("fillUserName");
+                boolean abnormalTime = a.get("extraField1") != null && (int)a.get("extraField1") == 1;
+
                 if (lastName == null || !(lastName.get("name").equals(name) && lastName.get("dateStr").equals(createDate))) {
                     lastName = new HashMap<String, Object>();
                     lastName.put("dateStr", createDate);
@@ -2195,6 +2289,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                     lastName.put("userId", uid);
                     lastName.put("corpwxUserid", corpwxUserid);
                     lastName.put("fillUserName", fillUserName);
+                    lastName.put("abnormalTime", abnormalTime);
                     nameList.add(lastName);
                     userDailyReportList = new ArrayList<>();
                     lastName.put("data", userDailyReportList);
@@ -2369,6 +2464,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                         String multiDegrId = (String)report.get("multiDegrId");
                         if (multiDegrId != null) {
                             multiDegrId = multiDegrId.replace("@", ",");
+                            System.out.println("multiDegrId=="+multiDegrId+", date="+map.get("dateStr")+", user="+map.get("userId")+", "+map.get("name"));
                             JSONArray array = JSONArray.parseArray(multiDegrId);
                             String s = "";
                             for (int i=0;i<array.size(); i++) {
@@ -4986,6 +5082,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                     }
                 }
             }
+
         }
         msg.data =reportFillStatus;
         return msg;
@@ -5278,10 +5375,16 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 needRangeUserList.add(u);
             }
         }
-        System.out.println("needRangeUserList:"+needRangeUserList.size());
         List<UserMonthWork> userMonthWorks = new ArrayList<UserMonthWork>();
+        TimeType timeType = timeTypeMapper.selectById(user.getCompanyId());
         //获取所有同步的企业微信数据
-        List<UserCorpwxTime> userCorpwxTimeList = userCorpwxTimeMapper.selectList(new QueryWrapper<UserCorpwxTime>().between("create_date", LocalDate.parse(startDate, df), LocalDate.parse(endDate, df)).eq("company_id", user.getCompanyId()));
+        List<UserCorpwxTime> userCorpwxTimeList = new ArrayList<>();
+        List<UserDingdingTime> userDingdingTimeList = new ArrayList<>();
+        if (timeType.getSyncCorpwxTime() == 1) {
+            userCorpwxTimeList = userCorpwxTimeMapper.selectList(new QueryWrapper<UserCorpwxTime>().between("create_date", LocalDate.parse(startDate, df), LocalDate.parse(endDate, df)).eq("company_id", user.getCompanyId()));
+        } else if (timeType.getSyncDingding() == 1) {
+            userDingdingTimeList = userDingdingTimeMapper.selectList(new QueryWrapper<UserDingdingTime>().between("work_date", LocalDate.parse(startDate, df), LocalDate.parse(endDate, df)).eq("company_id", user.getCompanyId()));
+        }
         String lastUserId = null;
         UserMonthWork lastUserData = null;
         for (Map<String, Object> data : list) {
@@ -5337,7 +5440,6 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
         DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
         //对于全部的用户已填日报,对比请假的数据,进行填充
-        TimeType timeType = timeTypeMapper.selectById(companyId);
         //获取请假数据(本系统内的、钉钉和泛微同步的)
         List<LeaveSheet> leaveSheets = null;
         if (timeType.getSyncDingding() == 1||timeType.getSyncFanwei()==1 || company.getPackageOa() == 1) {
@@ -5353,35 +5455,69 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         for (UserMonthWork userMonthWork : userMonthWorks) {
             List<Map<String, Object>> worktimeList = userMonthWork.worktimeList;
             //获取到该员工请假的数据
-            List<UserCorpwxTime> curUserCorpTime = userCorpwxTimeList.stream().filter(s->s.getCorpwxUserid() != null && s.getCorpwxUserid().equals(userMonthWork.corpwxUserId)).collect(Collectors.toList());
-            curUserCorpTime.forEach(corpTime->{
-                LocalDate wDate = corpTime.getCreateDate();
-                String curWDateStr = df.format(wDate);
-                double askLeaveTime = corpTime.getAskLeaveTime();
-                Optional<Map<String, Object>> find = worktimeList.stream().filter(w->((String)w.get("createDate")).equals(curWDateStr)).findFirst();
-                if (find.isPresent()) {
-                    //打卡的有记录,如果有请假,加上请假
-                    if (askLeaveTime > 0) {
-                        String newStr = (double)find.get().get("workingTime")+"("+MessageUtils.message("leave.leave")+(corpTime.getAskLeaveTime())+"h)";
-                        find.get().put("workingTime", newStr);
+            if (timeType.getSyncCorpwxTime() == 1) {
+                List<UserCorpwxTime> curUserCorpTime = userCorpwxTimeList.stream().filter(s->s.getCorpwxUserid() != null && s.getCorpwxUserid().equals(userMonthWork.corpwxUserId)).collect(Collectors.toList());
+                curUserCorpTime.forEach(corpTime->{
+                    LocalDate wDate = corpTime.getCreateDate();
+                    String curWDateStr = df.format(wDate);
+                    double askLeaveTime = corpTime.getAskLeaveTime();
+                    Optional<Map<String, Object>> find = worktimeList.stream().filter(w->((String)w.get("createDate")).equals(curWDateStr)).findFirst();
+                    if (find.isPresent()) {
+                        //打卡的有记录,如果有请假,加上请假
+                        if (askLeaveTime > 0) {
+                            String newStr = (double)find.get().get("workingTime")+"("+MessageUtils.message("leave.leave")+(corpTime.getAskLeaveTime())+"h)";
+                            find.get().put("workingTime", newStr);
+                        }
+                    } else {
+                        //没有对应的打卡记录,直接加上当天请假
+                        if (askLeaveTime > 0) {
+                            Map<String, Object> leaveMap = new HashMap<>();
+                            leaveMap.put("createDate", curWDateStr);
+                            leaveMap.put("workingTime", MessageUtils.message("leave.leaveOfDay")+corpTime.getAskLeaveTime()+"h");
+                            worktimeList.add(leaveMap);
+                        } else {
+                            Map<String, Object> leaveMap = new HashMap<>();
+                            leaveMap.put("createDate", curWDateStr);
+                            leaveMap.put("workingTime", "漏填");
+                            leaveMap.put("missReport", 1);
+                            worktimeList.add(leaveMap);
+                        }
+
                     }
-                } else {
-                    //没有对应的打卡记录,直接加上当天请假
-                    if (askLeaveTime > 0) {
-                        Map<String, Object> leaveMap = new HashMap<>();
-                        leaveMap.put("createDate", curWDateStr);
-                        leaveMap.put("workingTime", MessageUtils.message("leave.leaveOfDay")+corpTime.getAskLeaveTime()+"h");
-                        worktimeList.add(leaveMap);
+                });
+            }
+            if (timeType.getSyncDingding() == 1) {
+                List<UserDingdingTime> curUserTime = userDingdingTimeList.stream().filter(s->s.getUserId() != null && s.getUserId().equals(userMonthWork.userId)).collect(Collectors.toList());
+                curUserTime.forEach(corpTime->{
+                    LocalDate wDate = corpTime.getWorkDate();
+                    String curWDateStr = df.format(wDate);
+                    double askLeaveTime = corpTime.getAskLeaveTime();
+                    Optional<Map<String, Object>> find = worktimeList.stream().filter(w->((String)w.get("createDate")).equals(curWDateStr)).findFirst();
+                    if (find.isPresent()) {
+                        //打卡的有记录,如果有请假,加上请假
+                        if (askLeaveTime > 0) {
+                            String newStr = (double)find.get().get("workingTime")+"("+MessageUtils.message("leave.leave")+(corpTime.getAskLeaveTime())+"h)";
+                            find.get().put("workingTime", newStr);
+                        }
                     } else {
-                        Map<String, Object> leaveMap = new HashMap<>();
-                        leaveMap.put("createDate", curWDateStr);
-                        leaveMap.put("workingTime", "漏填");
-                        leaveMap.put("missReport", 1);
-                        worktimeList.add(leaveMap);
+                        //没有对应的打卡记录,直接加上当天请假
+                        if (askLeaveTime > 0) {
+                            Map<String, Object> leaveMap = new HashMap<>();
+                            leaveMap.put("createDate", curWDateStr);
+                            leaveMap.put("workingTime", MessageUtils.message("leave.leaveOfDay")+corpTime.getAskLeaveTime()+"h");
+                            worktimeList.add(leaveMap);
+                        } else {
+                            Map<String, Object> leaveMap = new HashMap<>();
+                            leaveMap.put("createDate", curWDateStr);
+                            leaveMap.put("workingTime", "漏填");
+                            leaveMap.put("missReport", 1);
+                            worktimeList.add(leaveMap);
+                        }
+
                     }
+                });
+            }
 
-                }
-            });
 
             //钉钉请假的数据
             if ((timeType.getSyncDingding() == 1 || timeType.getSyncFanwei()==1 || company.getPackageOa() == 1)&& leaveSheets != null && leaveSheets.size() > 0) {
@@ -6615,7 +6751,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 //titles.add("工作事项");
                 titles.add(MessageUtils.message("excel.workItems"));
             }
-            if(exportType==0 && (stateKey==1 || stateKey==0 || stateKey==2)){
+            if(exportType==0 && (stateKey==1 || stateKey==0 || stateKey==2 || stateKey == 3)){
                 //titles.add("审核状态");
                 titles.add(MessageUtils.message("excel.auditStatus"));
             }
@@ -7148,15 +7284,19 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                     }
 
                 }
-                if(exportType==0 && (stateKey==1 || stateKey ==0 || stateKey==2)){
+                if(exportType==0 && (stateKey==1 || stateKey ==0 || stateKey==2 || stateKey == 3)){
                     Integer state = (Integer) map.get("state");
                     switch (state){
                         //case 0:row.createCell(index).setCellValue("待审核");
+                        case -1:item.add(MessageUtils.message("stages.reviewed"));//导入待审核
+                            break;
                         case 0:item.add(MessageUtils.message("stages.reviewed"));
                             break;
                         //case 1:row.createCell(index).setCellValue("已通过");
                         case 1:item.add(MessageUtils.message("stages.passed"));
                             break;
+                        case 3:item.add("待提交");
+                            break;
                         case 99:
                             item.add("未提交");
                             break;
@@ -7211,8 +7351,10 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 if (exportType==0) {
                     Integer packageEngineering = company.getPackageEngineering();
                     if (packageEngineering.equals(0)) {
-                        if (String.valueOf(map.get("state")).equals("1")) {
+                        if (String.valueOf(map.get("state")).equals("3")) {
                             item.add("——");
+                        } else if (String.valueOf(map.get("state")).equals("1")) {
+                            item.add("已通过");
                         } else if (String.valueOf(map.get("state")).equals("-1")) {
                             item.add("导入待审核");
                         } else {
@@ -12244,7 +12386,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         List<Department> departmentList = departmentMapper.selectList(new QueryWrapper<Department>().eq("company_id", companyId));
         //维修部、三大中心、装配
         List<Integer> produceDeptIdList = departmentList.stream().filter(dept->dept.getDepartmentName().equals("维修部") || dept.getDepartmentName().equals("北京中心")
-                || dept.getDepartmentName().equals("上海中心") || dept.getDepartmentName().equals("武汉中心") || dept.getDepartmentName().equals("设备一部-装配")).map(Department::getDepartmentId).collect(Collectors.toList());
+                || dept.getDepartmentName().equals("上海中心") || dept.getDepartmentName().equals("武汉中心") || dept.getDepartmentName().equals("生产部")).map(Department::getDepartmentId).collect(Collectors.toList());
         Integer rdDeptId = departmentList.stream().filter(dept->dept.getDepartmentName().equals("研发中心")).findFirst().get().getDepartmentId();
         Integer cuServiceDeptId = departmentList.stream().filter(dept->dept.getDepartmentName().equals("售后部")).findFirst().get().getDepartmentId();
         List<User> userList = userMapper.selectList(new QueryWrapper<User>().eq("company_id", companyId));
@@ -12717,68 +12859,76 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                     double time = 0;
                     if (!StringUtils.isEmpty(stringCellValue)) {
                         time = Double.parseDouble(stringCellValue);
-                        if (time >= 0) {
-                            Report report = new Report();
-                            report.setCompanyId(companyId);
-                            report.setCreatorId(reportCreator.getId());
-                            report.setDeptId(reportCreator.getDepartmentId());
-                            report.setProjectId(project.getId());
-                            report.setSubProjectId(subProjectId);
-                            report.setReportTimeType(1);
-                            report.setWorkingTime(time);
-                            report.setMultiWorktime(timeType.getMultiWorktime());
-                            report.setMultiDegrId(contentIds);
-                            report.setContent(workContent);
-                            if (timeType.getImportReportAuditNormal() == 0) {
-                                //老的导入日报审核模式
-                                if (timeType.getNeedDeptAudit() == 0) {
-                                    report.setState(1);//导入的直接算审核通过
-                                } else {
-                                    report.setState(-1);//待部门上级审核员审核
-                                    report.setDepartmentAuditState(1);//部门已审核,到上层领导审核
-                                }
+                    }
+                    if (time == 0) {
+                        //从考勤中获取
+                        UserDingdingTime dingdingTime = userDingdingTimeMapper.selectOne(new QueryWrapper<UserDingdingTime>()
+                                .eq("user_id", reportCreator.getId()).eq("work_date", LocalDate.parse(reportDate, dtf)).last("limit 1"));
+                        if (dingdingTime != null) {
+                            time = dingdingTime.getWorkHours();
+                        }
+                    }
+                    if (time >= 0) {
+                        Report report = new Report();
+                        report.setCompanyId(companyId);
+                        report.setCreatorId(reportCreator.getId());
+                        report.setDeptId(reportCreator.getDepartmentId());
+                        report.setProjectId(project.getId());
+                        report.setSubProjectId(subProjectId);
+                        report.setReportTimeType(1);
+                        report.setWorkingTime(time);
+                        report.setMultiWorktime(timeType.getMultiWorktime());
+                        report.setMultiDegrId(contentIds);
+                        report.setContent(workContent);
+                        if (timeType.getImportReportAuditNormal() == 0) {
+                            //老的导入日报审核模式
+                            if (timeType.getNeedDeptAudit() == 0) {
+                                report.setState(1);//导入的直接算审核通过
                             } else {
-                                report.setState(isPass?1:0);//正北,前端勾选了审核通过,直接设置为通过
-                                //目前仅支持项目审核人审核的模式
-                                if (timeType.getReportAuditType() == 0) {
-                                    int mode = 0;
-                                    if (company.getNonProjectSimple() == 1) {
-                                        //启用了简易模式
-                                        List<ProjectAuditor> pAuditorList = new ArrayList<>();
-                                        if (project.getIsPublic() == 1) {
-                                            //非项目,该员工的部门主要负责人审核
-                                            mode = 1;
-                                            //优先取员工的上级领导
-                                            String superiorId = user.getSuperiorId();
-                                            if (superiorId == null) {
-                                                Integer departmentId = user.getDepartmentId();
-                                                Department department = departmentMapper.selectById(departmentId);
-                                                if (department != null) {
-                                                    superiorId = department.getManagerId();
-                                                }
+                                report.setState(-1);//待部门上级审核员审核
+                                report.setDepartmentAuditState(1);//部门已审核,到上层领导审核
+                            }
+                        } else {
+                            report.setState(isPass?1:0);//正北,前端勾选了审核通过,直接设置为通过
+                            //目前仅支持项目审核人审核的模式
+                            if (timeType.getReportAuditType() == 0) {
+                                int mode = 0;
+                                if (company.getNonProjectSimple() == 1) {
+                                    //启用了简易模式
+                                    List<ProjectAuditor> pAuditorList = new ArrayList<>();
+                                    if (project.getIsPublic() == 1) {
+                                        //非项目,该员工的部门主要负责人审核
+                                        mode = 1;
+                                        //优先取员工的上级领导
+                                        String superiorId = user.getSuperiorId();
+                                        if (superiorId == null) {
+                                            Integer departmentId = user.getDepartmentId();
+                                            Department department = departmentMapper.selectById(departmentId);
+                                            if (department != null) {
+                                                superiorId = department.getManagerId();
                                             }
-                                            report.setProjectAuditorId(superiorId);
                                         }
+                                        report.setProjectAuditorId(superiorId);
                                     }
-                                    if (mode == 0) {
-                                        //没有设置部门的审核人,按项目日报审核人设置
-                                        List<ProjectAuditor> projectAuditors = projectAuditorMapper.selectList(new QueryWrapper<ProjectAuditor>().eq("project_id", project.getId()));
-                                        if (projectAuditors.size() > 0) {
-                                            report.setProjectAuditorId(projectAuditors.get(0).getAuditorId());
-                                        }
+                                }
+                                if (mode == 0) {
+                                    //没有设置部门的审核人,按项目日报审核人设置
+                                    List<ProjectAuditor> projectAuditors = projectAuditorMapper.selectList(new QueryWrapper<ProjectAuditor>().eq("project_id", project.getId()));
+                                    if (projectAuditors.size() > 0) {
+                                        report.setProjectAuditorId(projectAuditors.get(0).getAuditorId());
                                     }
-                                    report.setIsDeptAudit(0);
-                                    report.setIsFinalAudit(1);
                                 }
+                                report.setIsDeptAudit(0);
+                                report.setIsFinalAudit(1);
                             }
-
-                            report.setCreateDate(LocalDate.parse(reportDate, dtf));
-                            report.setCost(reportCreator.getCost()==null?new BigDecimal(0) : reportCreator.getCost().multiply(new BigDecimal(time)));
-                            reportList.add(report);
-                        } else if (time < 0) {
-                            msg.setError(MessageUtils.message("report.negativeError",username));
-                            return msg;
                         }
+
+                        report.setCreateDate(LocalDate.parse(reportDate, dtf));
+                        report.setCost(reportCreator.getCost()==null?new BigDecimal(0) : reportCreator.getCost().multiply(new BigDecimal(time)));
+                        reportList.add(report);
+                    } else if (time < 0) {
+                        msg.setError(MessageUtils.message("report.negativeError",username));
+                        return msg;
                     }
                 }
             }
@@ -13434,6 +13584,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
 
     @Override
     @Transactional(rollbackFor = Exception.class)
+    @Synchronized
     public HttpRespMsg updateNoReportUserList(HttpServletRequest request, String startTime, String endTime,String createDate,Double leaveDuration, String corpwxUserId) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         try{
@@ -13484,6 +13635,35 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         return httpRespMsg;
     }
     }
+
+    @Override
+    public HttpRespMsg getAbnormalReportList(String startDate, String endDate, String userId, Integer pageIndex, Integer pageSize) {
+        HttpRespMsg msg = new HttpRespMsg();
+        Integer companyId = userMapper.selectById(request.getHeader("token")).getCompanyId();
+        Integer start=null;
+        Integer size=null;
+        if(pageIndex!=null&&pageSize!=null){
+            size=pageSize;
+            start=(pageIndex-1)*size;
+        }
+        List<Map<String,Object>> resultList = reportMapper.getAbnormalData(companyId,startDate,endDate,userId,start,size);
+        QueryWrapper<Report> queryWrapper = new QueryWrapper<>();
+        queryWrapper.eq("company_id",companyId);
+        queryWrapper.between("create_date",startDate,endDate);
+        queryWrapper.eq("extra_field1", 1);//异常工时的标记
+        if(!StringUtils.isEmpty(userId)){
+            queryWrapper.eq("creator_id",userId);
+        }
+        Integer count = reportMapper.selectCount(queryWrapper);
+        Map<String,Object> map=new HashMap<>();
+        map.put("result",resultList);
+        map.put("total",count);
+        map.put("pageIndex",pageIndex);
+        map.put("pageSize",pageSize);
+        msg.setData(map);
+        return msg;
+    }
+
     public String getWeek(DayOfWeek dayOfWeek){
         //获取中文形式的星期几
         String dayOfWeekChinese = "";

+ 2 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/SysRoleServiceImpl.java

@@ -161,8 +161,8 @@ public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> impl
             functionQueryWrapper.or().eq("package_finance", 1);
         }
         //企业微信考勤查看全部的权限
-        if (timeType.getSyncCorpwxTime() == 1) {
-            functionQueryWrapper.or().eq("sync_corpwx_time", 1);
+        if (timeType.getSyncCorpwxTime() == 1 || timeType.getSyncDingding() == 1) {
+            functionQueryWrapper.or().eq("sync_card_time", 1);
         }
         //开通财务审核功能的
         if (timeType.getFinanceAudit() == 1) {

+ 223 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/UserDingdingTimeServiceImpl.java

@@ -1,10 +1,26 @@
 package com.management.platform.service.impl;
 
-import com.management.platform.entity.UserDingdingTime;
-import com.management.platform.mapper.UserDingdingTimeMapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.management.platform.entity.*;
+import com.management.platform.entity.vo.SysRichFunction;
+import com.management.platform.mapper.*;
+import com.management.platform.service.ReportService;
 import com.management.platform.service.UserDingdingTimeService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.management.platform.util.HttpRespMsg;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * <p>
@@ -16,5 +32,210 @@ import org.springframework.stereotype.Service;
  */
 @Service
 public class UserDingdingTimeServiceImpl extends ServiceImpl<UserDingdingTimeMapper, UserDingdingTime> implements UserDingdingTimeService {
+    @Resource
+    private UserDingdingTimeMapper userDingdingTimeMapper;
+    @Resource
+    private DepartmentMapper departmentMapper;
+    @Resource
+    private SysFunctionMapper sysFunctionMapper;
+    @Resource
+    private UserMapper userMapper;
+    @Resource
+    private CompanyDingdingMapper companyDingdingMapper;
+    @Autowired
+    private LeaveSheetMapper leaveSheetMapper;
+    @Autowired
+    private HttpServletRequest request;
+    @Resource
+    private ReportService reportService;
+    @Resource
+    private ParticipationMapper participationMapper;
+    @Resource
+    private ProjectMapper projectMapper;
+    @Override
+    public HttpRespMsg getMyData(String startDate, String endDate) {
+        String token = request.getHeader("TOKEN");
+        User user = userMapper.selectById(token);
+        List<Map> list = new ArrayList<Map>();
+        String curUserId = user.getCompanyId() == 469?user.getId():(user.getRoleName().contains("管理员")?null:user.getId());
+        list = userDingdingTimeMapper.getUserDataList(user.getCompanyId(), startDate, endDate, null, curUserId);
+        //工作日处理,排除常规周末和法定节假日
+        DateTimeFormatter standFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        HashMap item = new HashMap();
+        item.put("list", list);
+        if (list.size() > 0) {
+            List<String> userIdList = new ArrayList<>();
+            for (int i=0;i<list.size(); i++) {
+                Map map = list.get(i);
+                String userId = (String)map.get("userId");
+                userIdList.add(userId);
+            }
+            //员工参与的项目
+            List<Participation> participationList = participationMapper.selectList(new QueryWrapper<Participation>().in("user_id", userIdList));
+            List<String> names = new ArrayList<>();
+            LocalDate localStart = LocalDate.parse(startDate, standFormatter);
+            LocalDate localEnd = LocalDate.parse(endDate, standFormatter);
+            if (participationList.size() > 0) {
+                List<Integer> collect = participationList.stream().map(Participation::getProjectId).collect(Collectors.toList());
+                List<Project> projectList = projectMapper.selectList(new QueryWrapper<Project>().in("id", collect).eq("is_public", 0).eq("company_id", user.getCompanyId()).orderByAsc("id"));
+
+                names = projectList.stream().filter(p->{
+                    if (p.getPlanStartDate() != null) {
+                        if (p.getPlanStartDate().isAfter(localEnd)) {
+                            return false;
+                        }
+                    }
+                    if (p.getPlanEndDate() != null) {
+                        if (p.getPlanEndDate().isBefore(localStart)) {
+                            return false;
+                        }
+                    }
+                    return true;
+                }).map(Project::getProjectName).collect(Collectors.toList());
+            }
+            //添加公共项目
+            List<Project> publicProjects = projectMapper.selectList(new QueryWrapper<Project>().eq("company_id", user.getCompanyId()).eq("is_public", 1));
+            if (publicProjects.size() > 0) {
+                List<String> collect = publicProjects.stream().filter(p->{
+                    if (p.getPlanStartDate() != null) {
+                        if (p.getPlanStartDate().isAfter(localEnd)) {
+                            return false;
+                        }
+                    }
+                    if (p.getPlanEndDate() != null) {
+                        if (p.getPlanEndDate().isBefore(localStart)) {
+                            return false;
+                        }
+                    }
+                    return true;
+                }).map(Project::getProjectName).collect(Collectors.toList());
+                names.addAll(collect);
+            }
+            item.put("projects", names);
+        } else {
+            item.put("projects", new ArrayList<String>());
+        }
+
+        //获取该时间段已经审核通过的报告
+        List<Report> reportList = reportService.list(new QueryWrapper<Report>()
+                .eq("company_id", user.getCompanyId()).between("create_date", startDate, endDate).eq("state", 1));
+
+        DateTimeFormatter splashDtf = DateTimeFormatter.ofPattern("yyyy/MM/dd");
+        //数据填充
+        for (Map dataItem : list) {
+            List<String> dataList = new ArrayList<>();
+            String userId = (String)dataItem.get("userId");
+            String createDate = (String)dataItem.get("createDate");
+            //检查该人员当天是否已经有审核通过的
+            boolean hasPassed = reportList.stream().anyMatch(r->r.getCreatorId().equals(userId) && splashDtf.format(r.getCreateDate()).equals(createDate));
+            dataItem.put("hasPassed", hasPassed);
+        }
+
+        //返回数据
+        HttpRespMsg msg = new HttpRespMsg();
+        msg.data = item;
+        return msg;
+    }
+
+
+    public HttpRespMsg getMyDeptMembsData(String startDate, String endDate) {
+        String token = request.getHeader("TOKEN");
+        User user = userMapper.selectById(token);
+        List<Map> list = new ArrayList<Map>();
+//        int onlyWorkDays = 0;//只要工作日
+        List<SysRichFunction> functionList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), "查看全部考勤");
+        if (functionList.size() > 0) {
+            //查看全部人员的
+            list = userDingdingTimeMapper.getUserDataList(user.getCompanyId(), startDate, endDate, null, null);
+        } else {
+            Integer manageDeptId = user.getManageDeptId();
+            if (manageDeptId != null && manageDeptId != 0) {
+                //一个人可能担任多个部门负责人
+                List<Department> departments = departmentMapper.selectList(new QueryWrapper<Department>().eq("manager_id", user.getId()));
+                if (departments.size() == 1) {
+                    list = userDingdingTimeMapper.getUserDataList(user.getCompanyId(), startDate, endDate, manageDeptId, null);
+                } else {
+                    for (Department d:departments) {
+                        list.addAll(userDingdingTimeMapper.getUserDataList(user.getCompanyId(), startDate, endDate, d.getDepartmentId(), null));
+                    }
+                }
+            }
+        }
+
+        DateTimeFormatter standFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        HashMap item = new HashMap();
+        item.put("list", list);
+        if (list.size() > 0) {
+            List<String> userIdList = new ArrayList<>();
+            for (int i=0;i<list.size(); i++) {
+                Map map = list.get(i);
+                String userId = (String)map.get("userId");
+                userIdList.add(userId);
+            }
+            //员工参与的项目
+            List<Participation> participationList = participationMapper.selectList(new QueryWrapper<Participation>().in("user_id", userIdList));
+            List<String> names = new ArrayList<>();
+            LocalDate localStart = LocalDate.parse(startDate, standFormatter);
+            LocalDate localEnd = LocalDate.parse(endDate, standFormatter);
+            if (participationList.size() > 0) {
+                List<Integer> collect = participationList.stream().map(Participation::getProjectId).collect(Collectors.toList());
+                List<Project> projectList = projectMapper.selectList(new QueryWrapper<Project>().in("id", collect).eq("is_public", 0).eq("company_id", user.getCompanyId()).orderByAsc("id"));
+
+                names = projectList.stream().filter(p->{
+                    if (p.getPlanStartDate() != null) {
+                        if (p.getPlanStartDate().isAfter(localEnd)) {
+                            return false;
+                        }
+                    }
+                    if (p.getPlanEndDate() != null) {
+                        if (p.getPlanEndDate().isBefore(localStart)) {
+                            return false;
+                        }
+                    }
+                    return true;
+                }).map(Project::getProjectName).collect(Collectors.toList());
+            }
+            //添加公共项目
+            List<Project> publicProjects = projectMapper.selectList(new QueryWrapper<Project>().eq("company_id", user.getCompanyId()).eq("is_public", 1));
+            if (publicProjects.size() > 0) {
+                List<String> collect = publicProjects.stream().filter(p->{
+                    if (p.getPlanStartDate() != null) {
+                        if (p.getPlanStartDate().isAfter(localEnd)) {
+                            return false;
+                        }
+                    }
+                    if (p.getPlanEndDate() != null) {
+                        if (p.getPlanEndDate().isBefore(localStart)) {
+                            return false;
+                        }
+                    }
+                    return true;
+                }).map(Project::getProjectName).collect(Collectors.toList());
+                names.addAll(collect);
+            }
+            item.put("projects", names);
+        } else {
+            item.put("projects", new ArrayList<String>());
+        }
+
+        //获取该时间段已经审核通过的报告
+        List<Report> reportList = reportService.list(new QueryWrapper<Report>()
+                .eq("company_id", user.getCompanyId()).between("create_date", startDate, endDate).eq("state", 1));
+
+        DateTimeFormatter splashDtf = DateTimeFormatter.ofPattern("yyyy/MM/dd");
+        //数据填充
+        for (Map dataItem : list) {
+            List<String> dataList = new ArrayList<>();
+            String userId = (String)dataItem.get("userId");
+            String createDate = (String)dataItem.get("createDate");
+            //检查该人员当天是否已经有审核通过的
+            boolean hasPassed = reportList.stream().anyMatch(r->r.getCreatorId().equals(userId) && splashDtf.format(r.getCreateDate()).equals(createDate));
+            dataItem.put("hasPassed", hasPassed);
+        }
 
+        //返回数据
+        HttpRespMsg msg = new HttpRespMsg();
+        msg.data = item;
+        return msg;
+    }
 }

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

@@ -722,8 +722,8 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
 //            if (company.getPackageProvider() == 1) {
 //                wrapper.or().eq("package_provider", 1);
 //            }
-                if (timeType.getSyncCorpwxTime() == 1) {
-                    wrapper.or().eq("sync_corpwx_time", 1);
+                if (timeType.getSyncCorpwxTime() == 1 || timeType.getSyncDingding() == 1) {
+                    wrapper.or().eq("sync_card_time", 1);
                 }
                 //开通财务审核功能的
                 if (timeType.getFinanceAudit() == 1) {
@@ -2840,10 +2840,11 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
     public HttpRespMsg getUserListByRole(HttpServletRequest request,String tableName) {
         HttpRespMsg httpRespMsg=new HttpRespMsg();
         User user = userMapper.selectById(request.getHeader("token"));
+        System.out.println("tableName==="+tableName);
         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 allName = sysFunctionList.stream().filter(sl -> sl.getName().contains("全公司")||sl.getName().contains("全部")||sl.getName().contains("工时成本预警表")||sl.getName().contains("日报待审核")||sl.getName().contains("客户项目利润表")).findFirst().get().getName();
-        String deptName = sysFunctionList.stream().filter(sl -> sl.getName().contains("负责部门")||sl.getName().contains("负责")||sl.getName().contains("工时成本预警表")||sl.getName().contains("日报待审核")||sl.getName().contains("客户项目利润表")).findFirst().get().getName();
+        String allName = sysFunctionList.stream().filter(sl -> sl.getName().contains("全公司")||sl.getName().contains("全部")||sl.getName().contains("工时成本预警表")||sl.getName().contains("日报待审核")||sl.getName().contains("客户项目利润表") || sl.getName().equals("异常工时表")).findFirst().get().getName();
+        String deptName = sysFunctionList.stream().filter(sl -> sl.getName().contains("负责部门")||sl.getName().contains("负责")||sl.getName().contains("工时成本预警表")||sl.getName().contains("日报待审核")||sl.getName().contains("客户项目利润表") || sl.getName().equals("异常工时表")).findFirst().get().getName();
         List<SysRichFunction> functionAllList = sysFunctionMapper.getRoleFunctions(user.getRoleId(),allName);
         List<SysRichFunction> functionDeptList = sysFunctionMapper.getRoleFunctions(user.getRoleId(), deptName);
         List<Department> departmentList = departmentMapper.selectList(new QueryWrapper<Department>().eq("manager_id", user.getId()));

+ 42 - 4
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/util/ExcelUtil.java

@@ -17,16 +17,54 @@ import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletResponse;
 import java.io.File;
 import java.io.FileOutputStream;
+import java.text.SimpleDateFormat;
 import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 import static org.apache.poi.ss.usermodel.TableStyleType.headerRow;
 
 @Component
 public class ExcelUtil {
+
+    // 通用方法:解析单元格中的日期值(兼容不同单元格类型)
+    public static String getCellDateValue(Cell cell) {
+        // 1. 先判断单元格类型
+        switch (cell.getCellTypeEnum()) {
+            case NUMERIC:
+                // 关键:判断是否为日期格式(Excel日期本质是数字)
+                if (DateUtil.isCellDateFormatted(cell)) {
+                    System.out.println("是日期类型");
+                    // 获取Date对象
+                    Date date = cell.getDateCellValue();
+                    // 格式化输出(自定义格式,比如yyyy/MM/dd)
+                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+                    return sdf.format(date);
+                } else {
+                    double numericValue = cell.getNumericCellValue();
+                    // 关键:POI的DateUtil直接转Java Date(自动处理1900/1904基准)
+                    Date date = DateUtil.getJavaDate(numericValue);
+                    // 格式化输出(按需调整格式,比如yyyy-MM-dd)
+                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+                    return sdf.format(date);
+                }
+            case STRING:
+                // 单元格是文本类型(比如手动输入的"2025/12/1")
+                String text = cell.getStringCellValue().trim();
+                // 可选:校验文本是否为日期格式,再转换
+                try {
+                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+                    Date date = sdf.parse(text);
+                    return sdf.format(date); // 统一格式输出
+                } catch (Exception e) {
+                    return text; // 不是日期则返回原文本
+                }
+            case BLANK:
+                return "";
+            default:
+                return "不支持的单元格类型:" + cell.getCellType();
+        }
+    }
+
     /**
      * 简单Excel导出
      * @param title     标题

+ 2 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/EstimateTimeSettingMapper.xml

@@ -10,11 +10,12 @@
         <result column="project_fronze_on_lack" property="projectFronzeOnLack" />
         <result column="group_warning_percent" property="groupWarningPercent" />
         <result column="group_fronze_on_lack" property="groupFronzeOnLack" />
+        <result column="project_exceed_percent" property="projectExceedPercent" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        company_id, project_man_day_fill_mode, project_warning_percent, project_fronze_on_lack, group_warning_percent, group_fronze_on_lack
+        company_id, project_man_day_fill_mode, project_warning_percent, project_fronze_on_lack, group_warning_percent, group_fronze_on_lack, project_exceed_percent
     </sql>
 
 </mapper>

+ 91 - 8
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ReportMapper.xml

@@ -94,11 +94,17 @@
     </if>
         <if test="stateKey!=null">
             <if test="stateKey==1">
-                (a.state = 1 or a.state = 0 or a.state = -1)
+                (a.state = 1 or a.state = 0 or a.state = -1 or a.state = 3)
             </if>
             <if test="stateKey==2">
                 (a.state=0 or a.state = -1)
             </if>
+            <if test="stateKey==3">
+                a.state=3
+            </if>
+            <if test="stateKey==4">
+                (a.state = 1 or a.state = 0 or a.state = -1)
+            </if>
         </if>)
         <if test="startDate != null and startDate != ''">
             AND a.create_date between #{startDate} and #{endDate}
@@ -154,11 +160,17 @@
     </if>
         <if test="stateKey!=null">
             <if test="stateKey==1">
-                (a.state = 1 or a.state = 0 or a.state = -1)
+                (a.state = 1 or a.state = 0 or a.state = -1 or a.state = 3)
             </if>
             <if test="stateKey==2">
                 (a.state=0 or a.state = -1)
             </if>
+            <if test="stateKey==3">
+                a.state=3
+            </if>
+            <if test="stateKey==4">
+                (a.state = 1 or a.state = 0 or a.state = -1)
+            </if>
         </if>)
         <if test="startDate != null and startDate != ''">
             AND a.create_date between #{startDate} and #{endDate}
@@ -212,11 +224,17 @@
     </if>
         <if test="stateKey!=null">
             <if test="stateKey==1">
-                (a.state = 1 or a.state = 0 or a.state = -1)
+                (a.state = 1 or a.state = 0 or a.state = -1 or a.state = 3)
             </if>
             <if test="stateKey==2">
                 (a.state=0 or a.state = -1)
             </if>
+            <if test="stateKey==3">
+                a.state=3
+            </if>
+            <if test="stateKey==4">
+                (a.state = 1 or a.state = 0 or a.state = -1)
+            </if>
         </if>)
         <if test="startDate != null and startDate != ''">
             AND a.create_date between #{startDate} and #{endDate}
@@ -273,11 +291,17 @@
     </if>
         <if test="stateKey!=null">
             <if test="stateKey==1">
-                (a.state = 1 or a.state = 0 or a.state = -1)
+                (a.state = 1 or a.state = 0 or a.state = -1 or a.state = 3)
             </if>
             <if test="stateKey==2">
                 (a.state=0 or a.state = -1)
             </if>
+            <if test="stateKey==3">
+                a.state=3
+            </if>
+            <if test="stateKey==4">
+                (a.state = 1 or a.state = 0 or a.state = -1)
+            </if>
         </if>)
         <if test="startDate != null and startDate != ''">
             AND a.create_date between #{startDate} and #{endDate}
@@ -334,11 +358,17 @@
     </if>
         <if test="stateKey!=null">
             <if test="stateKey==1">
-                (a.state = 1 or a.state = 0 or a.state = -1)
+                (a.state = 1 or a.state = 0 or a.state = -1 or a.state = 3)
             </if>
             <if test="stateKey==2">
                 (a.state=0 or a.state = -1)
             </if>
+            <if test="stateKey==3">
+                a.state=3
+            </if>
+            <if test="stateKey==4">
+                (a.state = 1 or a.state = 0 or a.state = -1)
+            </if>
         </if>)
         <if test="startDate != null and startDate != ''">
             AND a.create_date between #{startDate} and #{endDate}
@@ -417,11 +447,17 @@
     </if>
         <if test="stateKey!=null">
             <if test="stateKey==1">
-                (a.state = 1 or a.state=0 or a.state = -1)
+                (a.state = 1 or a.state = 0 or a.state = -1 or a.state = 3)
             </if>
             <if test="stateKey==2">
                 (a.state=0 or a.state = -1)
             </if>
+            <if test="stateKey==3">
+                a.state=3
+            </if>
+            <if test="stateKey==4">
+                (a.state = 1 or a.state = 0 or a.state = -1)
+            </if>
         </if>)
         <if test="startDate != null and startDate != ''">
             AND a.create_date between #{startDate} and #{endDate}
@@ -489,11 +525,17 @@
     </if>
         <if test="stateKey!=null">
             <if test="stateKey==1">
-                (a.state = 1 or a.state=0 or a.state = -1)
+                (a.state = 1 or a.state = 0 or a.state = -1 or a.state = 3)
             </if>
             <if test="stateKey==2">
                 (a.state=0 or a.state = -1)
             </if>
+            <if test="stateKey==3">
+                a.state=3
+            </if>
+            <if test="stateKey==4">
+                (a.state = 1 or a.state = 0 or a.state = -1)
+            </if>
         </if>)
         <if test="startDate != null and startDate != ''">
             AND a.create_date between #{startDate} and #{endDate}
@@ -554,11 +596,17 @@
         </if>
         <if test="stateKey!=null">
             <if test="stateKey==1">
-                (a.state = 1 or a.state=0 or a.state = -1)
+                (a.state = 1 or a.state = 0 or a.state = -1 or a.state = 3)
             </if>
             <if test="stateKey==2">
                 (a.state=0 or a.state = -1)
             </if>
+            <if test="stateKey==3">
+                a.state=3
+            </if>
+            <if test="stateKey==4">
+                (a.state = 1 or a.state = 0 or a.state = -1)
+            </if>
         </if>
         )
         <if test="startDate != null and startDate != ''">
@@ -1748,4 +1796,39 @@
         </if>
         GROUP BY report.creator_id, report.create_date HAVING SUM(working_time) > 8.0;
     </select>
+
+    <select id="getAbnormalData" resultType="java.util.Map">
+        SELECT a.id, a.creator_id as creatorId, c.name,c.job_number as jobNumber,c.corpwx_userid as corpwxUserId,c.corpwx_deptid as corpwxDeptId,
+        b.project_name AS project,b.project_code as projectCode, a.working_time AS duration, a.content, a.create_time  AS time,a.create_date as createDate,
+        a.state, a.time_type as timeType, a.cost, a.start_time as startTime,u.job_number as jobNumber,
+        a.end_time  as endTime, a.is_overtime as isOvertime,a.progress as progress,
+        a.department_audit_state as departmentAuditState,  a.pic_str as picStr, multi_worktime as multiWorktime,a.is_dept_audit as isDeptAudit,
+        a.group_audit_state as groupAuditState,a.project_audit_state as projectAuditState,a.audit_dept_managerid as deptAuditorName,
+        degree_id as degree_id,report_extra_degree.name as degreeName,a.group_id as groupId,a.custom_data as customData
+        ,u.name as projectAuditorName,u.corpwx_userid as projectAuditorCorpwxUserId, a.project_auditor_id as projectAuditorId, department.department_name as departmentName,department.department_id as departmentId, a.overtime_hours as overtimeHours, a.custom_text as customText,a.project_audit_time  as projectAuditTime
+        FROM report AS a
+        JOIN project AS b ON a.project_id=b.id
+        LEFT JOIN user AS c ON a.creator_id=c.id
+        left join sub_project as d on d.id = a.sub_project_id
+        left join report_extra_degree on report_extra_degree.id = a.degree_id
+        left join user u on u.id = a.project_auditor_id
+        left join department on department.department_id = a.dept_id
+        WHERE a.extra_field1 = 1
+        <if test="startDate != null and startDate != ''">
+            AND a.create_date &gt;= #{startDate}
+        </if>
+        <if test="endDate != null and endDate != ''">
+            AND a.create_date &lt;= #{endDate}
+        </if>
+        <if test="companyId != null">
+            AND c.company_id = #{companyId}
+        </if>
+        <if test="userId != null">
+            AND a.creator_id = #{userId}
+        </if>
+        ORDER BY a.creator_id, a.create_date desc
+        <if test="startPage != null and pageSize != null">
+            LIMIT #{startPage}, #{pageSize}
+        </if>
+    </select>
 </mapper>

+ 16 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/UserDingdingTimeMapper.xml

@@ -25,4 +25,20 @@
         id, work_date, dingding_userid, user_id, dingding_corpid, company_id, start_time, end_time, week_day, week_day_txt, work_hours, is_offi_business,modified_by_admin, ask_leave_time
     </sql>
 
+    <select id="getUserDataList" resultType="java.util.HashMap" >
+        SELECT user.id as userId, department.department_name as departmentName, DATE_FORMAT(a.work_date, '%Y/%m/%d') as createDate,a.start_time as startTime, a.end_time as endTime, a.work_hours as workHours, user.name as username,user.dingding_userid as dingdingUserid,a.week_day as weekDay,
+        week_day_txt as weekDayTxt,work_hours as cardTime, 0 as outdoorTime, ask_leave_time as askLeaveTime FROM user_dingding_time a
+        LEFT JOIN user ON user.`dingding_userid` = a.`dingding_userid` AND a.`company_id` = user.`company_id`
+        left join department on department.department_id = user.department_id
+        WHERE a.work_date BETWEEN #{startDate} AND #{endDate}
+        AND a.company_id = #{companyId}
+        and (a.`work_date` &gt;= user.`induction_date` OR user.`induction_date` IS NULL)
+        <if test="deptId != null">
+            AND user.`department_id` = #{deptId}
+        </if>
+        <if test="userId != null">
+            AND user.`id` = #{userId}
+        </if>
+        ORDER BY a.work_date ASC
+    </select>
 </mapper>

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

@@ -120,7 +120,7 @@ const StringUtil = {
         reportRartTimelyTaskHours: false, // 部分项目任务工时填报及时表 //
         reportOnProductionAndManufacturingCosts: false, // 生产制造成本表 //
         reportTheNumberOfWorkingHoursStatistics: false, // 工作包令号工时统计表 //
-
+        reportAbnormal: false, //异常工时表
         // 请假模块
         leaveFil : false, // 请假填报 // 
         leaveAudit : false, // 请假审核 //
@@ -318,6 +318,7 @@ const StringUtil = {
         arr[i] == '生产制造成本表' ? obj.reportOnProductionAndManufacturingCosts = true : ''
         arr[i] == '印花税管理' ? obj.contractStampDuty = true : ''
         arr[i] == '工作包令号工时统计表' ? obj.reportTheNumberOfWorkingHoursStatistics = true : ''
+        arr[i] == '异常工时表' ? obj.reportAbnormal = true : ''
     }
     return obj
   }

+ 123 - 8
fhKeeper/formulahousekeeper/timesheet/src/views/corpreport/list.vue

@@ -55,6 +55,7 @@
                   <el-menu-item index="1-33" v-if="permissions.reportAllTimelyTaskHours || permissions.reportRartTimelyTaskHours" @click="ssl(32)"><p>任务工时填报及时表</p></el-menu-item>
                   <el-menu-item index="1-34" v-if="permissions.reportOnProductionAndManufacturingCosts" @click="ssl(33)"><p>生产制造成本报表</p></el-menu-item>
                   <el-menu-item index="1-35" v-if="permissions.reportTheNumberOfWorkingHoursStatistics" @click="ssl(34)"><p>工作包令号工时统计表</p></el-menu-item>
+                  <el-menu-item index="1-36" v-if="permissions.reportAbnormal" @click="ssl(35)"><p>异常工时表</p></el-menu-item>
                 </el-submenu>
               </el-menu>
           </el-col>
@@ -1799,6 +1800,53 @@
             </el-table>
           </template>
 
+          <!-- 异常工时报表 -->
+          <template v-if="ins == 35">
+            <el-table key="35" border :data="abnormalWorkHoursList" highlight-current-row v-loading="abnormalWorkHoursLoading" :height="+tableHeight" style="width: 100%;" :max-height="+tableHeight + 50">
+              <el-table-column prop="name" align="center" label="员工" width="120">
+                <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.name}}
+                  </span>
+                </template>
+              </el-table-column>
+              <el-table-column prop="departmentName" align="center" label="所属部门" width="150">
+                <template slot-scope="scope">
+                  <span v-if="user.userNameNeedTranslate == '1'">
+                    <TranslationOpenDataText type='departmentName' :openid='scope.row.corpwxDeptId'></TranslationOpenDataText>
+                  </span>
+                  <span v-if="user.userNameNeedTranslate != '1'">
+                    {{scope.row.departmentName}}
+                  </span>
+                </template>
+              </el-table-column>
+              <el-table-column prop="createDate" align="center" label="工作日期" width="120"></el-table-column>
+              <el-table-column prop="project" align="center" label="投入项目" min-width="200"></el-table-column>
+              <el-table-column prop="duration" align="center" label="工作时长" width="100">
+                <template slot-scope="scope">
+                  {{scope.row.duration ? scope.row.duration.toFixed(1) + 'h' : '-'}}
+                </template>
+              </el-table-column>
+              <el-table-column prop="content" align="center" label="工作内容" min-width="200"></el-table-column>
+              <el-table-column prop="picStr" align="center" label="附件照片" width="100">
+                <template slot-scope="scope">
+                  <el-link v-if="scope.row.picStr" type="primary" :underline="false" @click="viewAttachments(scope.row.picStr)">查看</el-link>
+                  <span v-else>-</span>
+                </template>
+              </el-table-column>
+              <el-table-column prop="state" align="center" label="审批状态" width="100">
+                <template slot-scope="scope">
+                  <span :style="{color: scope.row.state == 1 ? 'green' : scope.row.state == 2 ? 'red' : ''}">
+                    {{scope.row.state == 0 ? '待审批' : scope.row.state == 1 ? '已通过' : scope.row.state == 2 ? '已驳回' : scope.row.state == 3 ? '待提交' : '-'}}
+                  </span>
+                </template>
+              </el-table-column>
+            </el-table>
+          </template>
+
         <!--工具条-->
         <el-col :span="24" class="toolbar" v-if="ins != 6 && ins != 20 && ins != 21 && tabPosition==0 && tabsType == 'all' && ins != 31 && ins != 32 && ins != 33">
           <el-pagination
@@ -1812,7 +1860,7 @@
                 :total="groupTotal"
                 style="float:left;"
             ></el-pagination>
-            <el-pagination
+            <el-pagination v-else
                 @size-change="handleSizeChange"
                 @current-change="handleCurrentChange"
                 :current-page="page"
@@ -1823,7 +1871,6 @@
                 style="float:right;"
             ></el-pagination>
         </el-col>
-
         </div>
     </div>
 
@@ -2200,6 +2247,18 @@
             <el-button @click="unallocatedPublicWorkingHoursVisable = false">关闭</el-button>
           </span>
         </el-dialog>
+
+        <!-- 查看附件照片 -->
+        <el-dialog title="附件照片" :visible.sync="attachmentDialogVisible" width="745px" :before-close="handleClose">
+          <div>
+            <viewer :images="attachmentImages">
+              <img v-for="src in attachmentImages" :src="src.url" :key="src.name" style="width: 100px; height: 100px; margin: 5px; cursor: pointer;">
+            </viewer>
+          </div>
+          <span slot="footer" class="dialog-footer">
+            <el-button @click="attachmentDialogVisible = false">关闭</el-button>
+          </span>
+        </el-dialog>
   </section>
 </template>
 
@@ -2229,12 +2288,12 @@ export default {
       selectYmonth:this.dayjs(new Date()).format('YYYY-MM'),
       themeColor: getThemeColor(),
       screeningCondition: { // 筛选条件的判断
-        project: [4, 8, 9, 10, 11, 14, 15, 17, 19, 20, 21, 22, 28, 30, 31,34], // 项目筛选条件 (不等于)
+        project: [4, 8, 9, 10, 11, 14, 15, 17, 19, 20, 21, 22, 28, 30, 31,34,35], // 项目筛选条件 (不等于)
         months: [14, 15], // 月份筛选条件 (等于)
         monthRange: [19, 30], // 月份区间筛选条件 (等于)
-        staff: [6, 8, 9, 19, 11, 14, 18, 23, 25, 26,28, 30, 32], // 人员筛选条件 (等于)
+        staff: [6, 8, 9, 19, 11, 14, 18, 23, 25, 26,28, 30, 32, 35], // 人员筛选条件 (等于)
         departments: [14, 15, 23,21,26,28,19, 30], // 部门筛选条件 (等于)
-        timePeriod: [5, 6, 8, 9, 10, 11, 12, 16, 17, 18, 20, 21, 22, 24, 25, 26,28,34], // 时间段筛选条件 (等于)
+        timePeriod: [5, 6, 8, 9, 10, 11, 12, 16, 17, 18, 20, 21, 22, 24, 25, 26,28,34,35], // 时间段筛选条件 (等于)
       },
       efficentList:[],
       groupNames: [],
@@ -2293,14 +2352,14 @@ export default {
       this.$t('statisticsofovertimework'),this.$t('timecostearlywarningtable'),this.$t('personneltimeallocationtable'),
       this.$t('statisticsofstafffillingintimerate'),this.$t('dailyreporttobereviewedstatistics'),this.$t('statisticsofpersonnelhours'),this.$t('taskgrouptimesheet'),this.$t('projectcostbaselinetable'),
       this.$t('ren-yuan-yue-du-gong-shi-biao'), this.$t('bumenchanyuqingkuang'), this.$t('ge-fen-zu-yu-jie-duan-gong-shi-biao'), this.$t('ziXiangMuGongShiChengBenBiao'), this.$t('renWuZhongQiBiao'), this.$t('fteBaoBiao'), this.$t('youXiaoGongShiShuaiBiao'), this.$t('xiangMuFenLeiGongShiZhanBiBiao'), this.$t('fenLeiGongShiMingXiBiao'),
-      this.$t('yuanGongXiangMuJinDuBiao'), this.$t('fenZuHaoYongJinDuBiao'), this.$t('xiangMuHaoYongJinDuBiao'), this.$t('yuanGongRenWuJinDuBiao'), this.$t('xiangMuYuGuGongShiBiao'),this.$t('yuanGongRenWuWanChengQingKuangBiao'), this.$t('taskPlanCostReport'), 'FTE计划报表', '月度财务工时表', '任务工时填报及时表', '生产制造成本报表', '工作包令号工时统计表'],
+      this.$t('yuanGongXiangMuJinDuBiao'), this.$t('fenZuHaoYongJinDuBiao'), this.$t('xiangMuHaoYongJinDuBiao'), this.$t('yuanGongRenWuJinDuBiao'), this.$t('xiangMuYuGuGongShiBiao'),this.$t('yuanGongRenWuWanChengQingKuangBiao'), this.$t('taskPlanCostReport'), 'FTE计划报表', '月度财务工时表', '任务工时填报及时表', '生产制造成本报表', '工作包令号工时统计表', '异常工时表'],
 
       shuzArr: [this.$t('projectreport'),this.$t('projectTaskReport'),this.$t('projectcoststatement'),
       this.$t('projectbalancesheet'),this.$t('customerprojectincomestatement'),this.$t('projectphasetimesheet'),
       this.$t('statisticsofovertimework'),this.$t('timecostearlywarningtable'),this.$t('personneltimeallocationtable'),
       this.$t('employeereporttimelinessrate'),this.$t('dailyreporttobereviewedstatistics'),this.$t('statisticsofpersonnelhours'),this.$t('taskgrouptimesheet'),this.$t('projectcostbaselinetable'),
       this.$t('ren-yuan-yue-du-gong-shi-biao'), this.$t('bumenchanyuqingkuang'), this.$t('ge-fen-zu-yu-jie-duan-gong-shi-biao'), this.$t('ziXiangMuGongShiChengBenBiao'), this.$t('renWuZhongQiBiao'), this.$t('fteBaoBiao'),this.$t('youXiaoGongShiShuaiBiao'), this.$t('xiangMuFenLeiGongShiZhanBiBiao'), this.$t('fenLeiGongShiMingXiBiao'),
-      this.$t('yuanGongXiangMuJinDuBiao'), this.$t('fenZuHaoYongJinDuBiao'), this.$t('xiangMuHaoYongJinDuBiao'), this.$t('yuanGongRenWuJinDuBiao'), this.$t('xiangMuYuGuGongShiBiao'),this.$t('yuanGongRenWuWanChengQingKuangBiao'), this.$t('taskPlanCostReport'), 'FTE计划报表', '月度财务工时表', '任务工时填报及时表', '生产制造成本报表', '工作包令号工时统计表'],
+      this.$t('yuanGongXiangMuJinDuBiao'), this.$t('fenZuHaoYongJinDuBiao'), this.$t('xiangMuHaoYongJinDuBiao'), this.$t('yuanGongRenWuJinDuBiao'), this.$t('xiangMuYuGuGongShiBiao'),this.$t('yuanGongRenWuWanChengQingKuangBiao'), this.$t('taskPlanCostReport'), 'FTE计划报表', '月度财务工时表', '任务工时填报及时表', '生产制造成本报表', '工作包令号工时统计表', '异常工时表'],
 
       ins: 10000,
       user: JSON.parse(sessionStorage.user),
@@ -2507,6 +2566,14 @@ export default {
         autoNumStrs: []
       },
       workPackageOrderNumberLoading: false,
+
+      // 异常工时报表
+      abnormalWorkHoursList: [],
+      abnormalWorkHoursLoading: false,
+
+      // 附件照片查看
+      attachmentDialogVisible: false,
+      attachmentImages: [],
     };
   },
   computed: {},
@@ -2750,6 +2817,7 @@ export default {
       if(this.permissions.reportAllTimelyTaskHours || this.permissions.reportRartTimelyTaskHours) {this.ssl(32);this.takCompletedStatus = '1-33';return} else
       if(this.permissions.reportOnProductionAndManufacturingCosts) {this.ssl(33);this.takCompletedStatus = '1-34';return} else
       if(this.permissions.reportTheNumberOfWorkingHoursStatistics) {this.ssl(34);this.takCompletedStatus = '1-35';return} else
+      if(this.permissions.reportAbnormal) {this.ssl(35);this.defaultActive = '1-36';return} else
       {this.allWrong = false}
     },
     rowspan(spanArr,position,spanName,dataItem = [],fields=false){
@@ -2852,7 +2920,6 @@ export default {
     },
 
     getUserList() {
-      console.log(this.shuzArr[this.ins])
       this.http.post('/user/getUserListByRole', {
         tableName: this.shuzArr[this.ins] || this.$t('statisticsofovertimework')
       },
@@ -3203,6 +3270,9 @@ export default {
                 if(this.ins == 34) {
                   this.getWorkPackageOrderNumber()
                 }
+                if(this.ins == 35) {
+                  this.getAbnormalWorkHours()
+                }
             },
       exportExcel() {
         var url = "/project";
@@ -3474,6 +3544,9 @@ export default {
           url = `/project/exportWorkOrderNumStatistics`
           sl.startDate = this.rangeDatas[0]
           sl.endDate = this.rangeDatas[1]
+        } else if(this.ins == 35) {
+          fName = `异常工时表.xlsx`
+          url = `/report/exportAbnormalWorkHours`
         }
         this.exportReportLoading = true
           this.http.post(url, sl,
@@ -4773,6 +4846,9 @@ export default {
       if(this.ins == 34) {
         this.getWorkPackageOrderNumber()
       }
+      else if (this.ins == 35) {
+        this.getAbnormalWorkHours();
+      }
     },
     getTaskPlanAndRealCost() {
       this.listLoading = true;
@@ -5608,6 +5684,45 @@ export default {
       })
     },
 
+    // 获取异常工时报表
+    getAbnormalWorkHours() {
+      this.abnormalWorkHoursLoading = true
+      let parameter = {
+        pageIndex: this.page,
+        pageSize: this.size,
+        startDate: this.rangeDatas[0],
+        endDate: this.rangeDatas[1]
+      }
+      if(this.userId) {
+        parameter.userId = this.userId
+      }
+      this.postData(`/report/getAbnormalReportList`, parameter).then(res => {
+        this.abnormalWorkHoursList = res.data.result || []
+        this.total = res.data.total || 0
+      }).finally(() => {
+        this.abnormalWorkHoursLoading = false
+      })
+    },
+
+    // 查看附件照片
+    viewAttachments(picStr) {
+      if (!picStr) {
+        return
+      }
+      var str = picStr.replace('@',',');
+      var attachments = JSON.parse(str);
+      
+      // 使用viewer控件显示图片
+      this.attachmentImages = attachments.map(item => {
+        return {
+          name: item,
+          url: `/upload/${item}`,
+          title: '附件照片'
+        }
+      })
+      this.attachmentDialogVisible = true
+    },
+
     // 任务工时填报及时表开始合并
     timelyFormForFillingInTaskHoursSpanMethod({ row, column, rowIndex }) {
       const theFirstColumn = {

+ 4 - 1
fhKeeper/formulahousekeeper/timesheet/src/views/settings/timetype.vue

@@ -36,10 +36,13 @@
                             </el-tooltip>
                             </span>
                         </el-form-item>
-                        <el-form-item :label="$t('imitillingtime')">
+                        <el-form-item label="日常工时上限">
                             <el-select :disabled="timeType.lockWorktime" v-model="timeType.maxReportTime" type="number" style="width:120px;">
                                 <el-option v-for="item in maxReportTimeRange" :key="item" :label="item.toFixed(1)" :value="item"></el-option>
                             </el-select><span style="margin-left:5px" class="themeFontColor">{{ $t('time.hour') }}</span>
+                            <el-tooltip effect="dark" content="当日总填报工作时长上限。在可填加班的情况下,该数值是减去加班时间后的时长限制" placement="top-start">
+                                <i class="el-icon-question" style="color:#606266"></i>
+                            </el-tooltip>
                         </el-form-item>
                         <el-form-item :label="$t('minReportTime')">
                             <el-select :disabled="timeType.lockWorktime" v-model="timeType.minReportTime" type="number" style="width:120px;">

+ 63 - 25
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

@@ -319,7 +319,7 @@
                                                         <span style="margin-right:10px;">{{item2.progress}}%</span>
                                                     </p>
                                                     
-                                                    <p>{{$t('other.matters')}}:<span v-html="item2.content"></span></p>
+                                                    <p v-if="!user.timeType.hideContent">工作内容:<span v-html="item2.content"></span></p>
                                                     </div>
                                                     <div v-if="item2.multiWorktime==1" >
                                                         <p>{{$t('other.projectDuration')}}:{{item2.time == '*' ? item2.time : item2.time.toFixed(1)}}h  <el-tag type="danger" size="mini" style="margin-left: 65px" v-if="item2.isOvertime === 1">{{ $t('other.WorkOvertime') }}<span v-if="item2.overtimeHours">{{item2.overtimeHours === '*' ? '*' : item2.overtimeHours.toFixed(1)}}h</span></el-tag></p>
@@ -332,7 +332,7 @@
                                                                 {{timeItem.detail}}
                                                             </span>
                                                             </p>
-                                                            <p>{{$t('other.matters')}}:<span v-html="timeItem.content"></span></p>
+                                                            <p v-if="!user.timeType.hideContent">工作内容:<span v-html="timeItem.content"></span></p>
                                                         </div>
                                                     </div>
                                                     <p v-if="item2.state == 1 && user.timeType.needEvaluate == 1">{{$t('other.evaluation')}}:<span v-html="item2.evaluate"></span></p>
@@ -459,6 +459,13 @@
                                 @click="refreshBeiSengAttendanceTwo(workForm.createDate)"></el-button>
                         
                         <span v-if="user.companyId==5978" style="margin-left:5px;" class="themeFontColor"><i class="el-icon-warning"></i>{{$t('other.kaoqingTimeTip')}}</span>
+
+                        <!--针对羲和超导的异常填报功能-->
+                        <template v-if="(user.companyId <= 10 || user.companyId == 8555) && !workForm.time">
+                            <span style="margin-left:70px;color:#666;">异常填报</span>
+                            <el-switch v-model="workForm.abnormalTime" :disabled="!canEdit"></el-switch>
+                        </template>
+                        
                     </el-form-item>
                     <!-- 000000 -->
                     <div v-for="(domain, index) in workForm.domains" :key="domain.id" :style="index>0?'margin-top:10px;':''">
@@ -598,7 +605,7 @@
                                 <el-option v-for="item in timeBasecostList" :label="item.name" :value="item.id" :key="item.id"></el-option>
                             </el-select>
                         </el-form-item>
-                        <el-form-item v-if="user.company.packageProject == 1&& (user.company.nonProjectSimple==0 || (user.company.nonProjectSimple==1&&domain.isPublic != 1))">
+                        <el-form-item v-if="user.company.packageProject == 1&& (user.company.nonProjectSimple==0 || (user.company.nonProjectSimple==1&&domain.isPublic != 1)) && !user.timeType.hideGroup">
                             <template slot="label">
                                 <span style="color:#FF0000;" v-if="user.timeType.taskGroupRequired == 1 || user.timeType.reportAuditType == 1 || user.timeType.reportAuditType == 2 || user.timeType.reportAuditType == 9">*</span>
                                 <span >{{$t('lable.taskGrouping')}}</span>
@@ -777,14 +784,14 @@
                             </el-select>
                         </el-form-item>
                         <!--工作事项--> <!-- 加力股份不需要工作内容填报 -->
-                        <el-form-item :label="$t('other.workMatters') " :prop="'domains.' + index + '.content'" v-if="user.companyId != 7544"
+                        <el-form-item :label="$t('other.workMatters') " :prop="'domains.' + index + '.content'" v-if="user.companyId != 7544 && !user.timeType.hideContent"
                         :rules="user.timeType.workContentState == 1 ? { required: true, message: $t('other.tianworkMatters'), trigger: 'blur' } : null">
                             <el-input v-model="domain.content" type="textarea" :rows="4" :placeholder="$t('defaultText.pleaseFillOut')" clearable style="width:75%;margin-right:7%"
                             :disabled="workForm.domains.length==0?true:(workForm.domains[index].state>=2?false:true)"></el-input>
                         </el-form-item>
 
                         <!-- 拍照上传 -->
-                        <el-form-item :label="$t('tuPianShangChuan')" v-if="user.timeType.choseFromAlbum == 1" :prop="'domains.' + index + '.imgListUrl'" 
+                        <el-form-item :label="$t('tuPianShangChuan')" v-if="user.timeType.choseFromAlbum == 1 || ((user.companyId <= 10 || user.companyId == 8555) && workForm.abnormalTime)" :prop="'domains.' + index + '.imgListUrl'" 
                         :rules="isCustomization(user, reportPictureRequired) ? { required: true, message: $t('pleaseselectpictures'), trigger: 'blur' } : null">
                             <div class="photos">
                                 <div>
@@ -848,7 +855,7 @@
                                     <el-option v-for="item in timeBasecostList" :label="item.name" :value="item.id" :key="item.id"></el-option>
                                 </el-select>
                             </el-form-item>
-                            <el-form-item v-if="user.company.packageProject == 1">
+                            <el-form-item v-if="user.company.packageProject == 1 && !user.timeType.hideGroup">
                                 <template slot="label">
                                     <span style="color:#FF0000;" v-if="user.timeType.taskGroupRequired == 1">*</span>
                                     <span >{{$t('lable.taskGrouping')}}</span>
@@ -958,7 +965,7 @@
                                     <el-option v-for="item in multiOptionData" :key="item.value" :label="item.value" :value="item.value"></el-option>
                                 </el-select>
                             </el-form-item>
-                            <el-form-item :label="$t('other.workMatters')" :prop="'domains.' + index + '.worktimeList.'+tIndex+'.content'" 
+                            <el-form-item :label="$t('other.workMatters')" :prop="'domains.' + index + '.worktimeList.'+tIndex+'.content'" v-if="!user.timeType.hideContent"
                             :rules="user.timeType.workContentState == 1 ? { required: true, message: $t('other.tianworkMatters'), trigger: 'blur' } : null">
                                 <el-input v-model="timeItem.content" type="textarea" :rows="2" :placeholder="$t('other.tianworkMatters')" clearable
                                 :disabled="workForm.domains.length==0?true:(workForm.domains[index].state>=2?false:true)"
@@ -1182,7 +1189,7 @@
                     </el-select>
                 </div>
                 <!-- 任务分组 -->
-                <div class="zhoFel" v-if="user.company.packageProject == 1">
+                <div class="zhoFel" v-if="user.company.packageProject == 1 && !user.timeType.hideGroup">
                     <p>{{$t('lable.taskGrouping')}}</p>
                     <el-select v-model="zhoBao.groupId" :placeholder="$t('defaultText.pleaseSelectaTaskGroup')" clearable="true" style="width: 355px"
                      @change="getGroupStages(zhoBao, 0), getWeeklyTaskList(zhoBao)">
@@ -1297,8 +1304,8 @@
                         <el-col span="10"><span style="float:right;"><span style="margin-right:10px;">{{zhoBao.progress || 0}}%</span>{{zhoBao.workingTime | amounts}}{{$t('time.hour')}}</span></el-col>
                     </div>
                 </div>
-                <div class="zhoFel" v-if="user.companyId != 7544">
-                    <p>{{$t('other.workMatters')}}</p>
+                <div class="zhoFel" v-if="user.companyId != 7544 && !user.timeType.hideContent">
+                    <p>工作内容</p>
                     <el-input type="textarea" v-model="zhoBao.content" :placeholder="$t('other.tianworkMatters')" style="width: 355px" clearable></el-input>
                 </div>
             </div>
@@ -1333,9 +1340,11 @@
                 </el-form-item>
                 <el-form-item prop="stateKey" :label="$t('other.doesItContain')">
                     <el-select v-model="stateKey" :placeholder="$t('defaultText.pleaseChoose')" filterable style="width:350px;">
-                        <el-option :label="$t('state.alreadyPassed')" value="0"></el-option>
-                        <el-option :label="$t('state.WaitingAudit')" value="2"></el-option>
-                        <el-option :label="$t('state.alreadyPassedAndWaitingAudit')" value="1"></el-option>
+                        <el-option label="已通过" value="0"></el-option>
+                        <el-option label="待审核" value="2"></el-option>
+                        <el-option label="待提交" value="3"></el-option>
+                        <el-option label="已通过和待审核" value="4"></el-option>
+                        <el-option label="已通过、待审核和待提交" value="1"></el-option>
                     </el-select>
                 </el-form-item>
 
@@ -2123,7 +2132,7 @@
         </el-dialog>
 
         <!--基于企业微信考勤数据的工时导入 -->
-        <el-dialog :title="$t('other.Batchimportofworkinghours')" v-if="importWxDialog" :visible.sync="importWxDialog" customClass="customWidth" width="1100px">
+        <el-dialog :title="$t('other.Batchimportofworkinghours')" v-if="importWithCardTimeDialog" :visible.sync="importWithCardTimeDialog" customClass="customWidth" width="1100px">
             <el-steps :active="active" finish-status="success" style="margin-left:50px;">
             <el-step :title="$t('other.selectadaterangetoimporthours')">
             </el-step>
@@ -2204,7 +2213,7 @@
             <el-link v-show="active==1" style="margin-right:300px;margin-top:10px;" 
                 type="primary" @click="downloadCheckInExcel">{{$t('template.DownloadEmployeeHourStatisticsTemplate')}}.xlsx</el-link>
             
-                <el-button @click="refreshWXCardTimeByRange" v-if="active == 1" :loading="refreshingTime">刷新考勤</el-button>
+                <el-button @click="refreshWXCardTimeByRange" v-if="active == 1 && user.corpwxUserId != null" :loading="refreshingTime">刷新考勤</el-button>
             <el-button @click="pre" v-if="active!=0">{{$t('btn.previousstep')}}</el-button>
             
             <el-button @click="next" :disabled="importWxParam.date==null" v-if="active<2">{{$t('btn.nextStep')}}</el-button>
@@ -2550,7 +2559,7 @@
                 importWxParam:{date:null,},
                 active:0,
                 permissions: JSON.parse(sessionStorage.getItem("permissions")),
-                importWxDialog:false,
+                importWithCardTimeDialog:false,
                 importDialog:false,
                 denyForm:null,
                 denyReasonDialog:false,
@@ -3975,7 +3984,7 @@
                 res => {
                     if (res.code == "ok") {
                         this.$message({message:this.$t('message.submittedSuccessfully'), type:'success'});
-                        this.importWxDialog = false;
+                        this.importWithCardTimeDialog = false;
                     } 
                 },
                 error => {
@@ -4014,7 +4023,7 @@
                     return;
                 }
                 this.checkinLoading = true;
-                this.http.post('/user-corpwx-time/getMyData',{ 
+                this.http.post(this.user.dingdingUserid != null?'/user-dingding-time/getMyData':'/user-corpwx-time/getMyData',{ 
                     startDate: this.importWxParam.date[0],
                     endDate: this.importWxParam.date[1],
                 },
@@ -4103,7 +4112,7 @@
                             //换成弹出框,以免有人等了半天回来啥也没看到
                             this.importResultMsg = this.$t('chengGongDaoRu') +res.data+this.$t('other.workHourData')+(res.msg?res.msg:"");
                             this.getReportList();
-                            this.importWxDialog = false;
+                            this.importWithCardTimeDialog = false;
                         } else {
                             this.importResultMsg = this.$t('export.Importfailure')+":"+res.msg;
                         }
@@ -4147,7 +4156,7 @@
                             //换成弹出框,以免有人等了半天回来啥也没看到
                             this.importResultMsg = this.$t('chengGongDaoRu')+res.data+this.$t('other.workHourData')+(res.msg?res.msg:"");;
                             this.getReportList();
-                            this.importWXDialog = false;
+                            this.importWithCardTimeDialog = false;
                         } else {
                             this.importResultMsg = this.$t('export.Importfailure')+":"+res.msg;
                         }
@@ -6796,8 +6805,14 @@
                             this.canEdit = false;
                             let candelete = true
                             const { reportExtraField4Name, reportExtraField5Name } = this.user.timeType
+                            var abnormalTime = false;
                             for(var i in list.report) {
                                 var flg = null
+                                if (this.user.companyId <= 10 || this.user.companyId == 8555) {
+                                    if (list.report[i].extraField1) {
+                                        abnormalTime = true;
+                                    }
+                                }
                                 list.report[i].isOvertime == 1 ? flg = true : flg = false
                                 var targetP = this.fillProjectList.filter(p=>p.id == list.report[i].projectId);
                                 var filteredRespList = null
@@ -6892,7 +6907,8 @@
                                 userNames:null,
                                 userId:null,
                                 time: list.time,
-                                showRefresh: list.showRefresh
+                                showRefresh: list.showRefresh,
+                                abnormalTime: abnormalTime
                             }
                             if(res.data.timeType.type == 3) {
                                 const reportCalculationReportList = res.data.report || []
@@ -7377,7 +7393,7 @@
                 if(this.user.timeType.customTextStatus == 1 && !this.zhoBao.customText){
                     errtips += this.user.timeType.customTextName + '、'
                 }
-                if(this.user.timeType.workContentState == 1 && !this.zhoBao.content){
+                if(!this.user.timeType.hideContent && this.user.timeType.workContentState == 1 && !this.zhoBao.content){
                     errtips +=  this.$t('other.workMatters') +'、'
                 }
                 if(errtips){
@@ -8907,8 +8923,25 @@
                             totalTime += parseFloat(this.workForm.domains[t].workingTime);
                         }
                         //批量填报不校验考勤时长,交给后台校验
+                        
                         if (!this.isBatch) {
-                            if(this.user.timeType.notAllowedNoAttendance == 1 && this.isDraft == 0 && !(this.isSubstitude && this.user.timeType.notCheckCardtime)){
+                            if ((this.user.companyId <= 10 || this.user.companyId == 8555) && this.workForm.abnormalTime) {
+                                //异常上报情况,需要上传图片
+                                var findImg = false;
+                                for (var t=0;t<this.workForm.domains.length; t++) {
+                                    if(this.workForm.domains[t].imgList != null && this.workForm.domains[t].imgList.length > 0) {
+                                        findImg = true;
+                                        break;
+                                    }
+                                }
+                                if (!findImg) {
+                                    this.$message({
+                                        message: '异常上报需上传凭证截图',
+                                        type: 'error'
+                                    })
+                                    return
+                                }
+                            } else if(this.user.timeType.notAllowedNoAttendance == 1 && this.isDraft == 0 && !(this.isSubstitude && this.user.timeType.notCheckCardtime)){
                                 if (this.workForm.time){
                                     if (this.workForm.time.workHours == 0){
                                         this.$message({
@@ -9057,7 +9090,6 @@
                             } 
                             
                             if(this.user.timeType.choseFromAlbum == 1 ) {
-                                console.log(this.workForm.domains[i].imgList)
                                 if(this.workForm.domains[i].imgList) {
                                     let imgListFor = this.workForm.domains[i].imgList
                                     for(var b in imgListFor) {
@@ -9251,6 +9283,12 @@
                                     return
                                 }
                             }
+                            //羲合超导的异常工时上报
+                            if (this.user.companyId <= 10 || this.user.companyId == 8555) {
+                                if (this.workForm.abnormalTime) {
+                                    formData.append('abnormalTime', this.workForm.abnormalTime);
+                                }
+                            }
                         }
 
                         this.submitingReport = true;
@@ -9300,7 +9338,7 @@
                 if(this.user.timeType.syncCorpwxTime == 0) {
                     this.importDialog = true
                 } else {
-                    this.importWxDialog = true
+                    this.importWithCardTimeDialog = true
                 }
             },
             // 跳转

+ 2 - 2
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/dailyReportReview.vue

@@ -176,7 +176,7 @@
                                   </p>
 
                                   <div v-if="item.multiWorktime==0">
-                                  <p>{{ $t('other.matters') }}:<span v-html="item.content"></span></p>
+                                  <p v-if="!user.timeType.hideContent">{{ $t('other.matters') }}:<span v-html="item.content"></span></p>
                                   </div>
                                   <div v-if="item.multiWorktime==1" >
                                       <div v-for="(timeItem, tIndex) in item.worktimeList" :key="tIndex"
@@ -186,7 +186,7 @@
                                           {{timeItem.time.toFixed(1)}}h  
                                           <span v-if="timeItem.detail" style="margin-left:10px;"> {{timeItem.detail}} </span>
                                           </p>
-                                          <p style="line-height:20px;margin:5px 0px;">{{ $t('other.matters') }}:<span v-html="timeItem.content"></span></p>
+                                          <p style="line-height:20px;margin:5px 0px;" v-if="!user.timeType.hideContent">{{ $t('other.matters') }}:<span v-html="timeItem.content"></span></p>
                                       </div>
                                   </div>
 

+ 2 - 2
fhKeeper/formulahousekeeper/timesheet_h5/src/main.js

@@ -20,14 +20,14 @@ Picker , Dialog , NumberKeyboard , Sticky , Skeleton ,
 Panel , Divider , List , pullRefresh , SwipeCell, Checkbox, 
 Search, Slider,Stepper,Tag, Calendar, Row, Col, RadioGroup, Radio, 
 Loading ,DropdownMenu, DropdownItem, Button, ActionSheet, PullRefresh,Tabbar,
-TabbarItem,Uploader,Collapse, CollapseItem,Empty,CheckboxGroup} from 'vant';
+TabbarItem,Uploader,Collapse, CollapseItem,Empty,CheckboxGroup,Switch} from 'vant';
 
 Vue.use(Form).use(Toast).use(Grid).use(GridItem).use(DatetimePicker)
 .use(Picker).use(Dialog).use(NumberKeyboard).use(Sticky).use(Skeleton)
 .use(Panel).use(Divider).use(List).use(pullRefresh).use(SwipeCell)
 .use(Checkbox).use(Search).use(Slider).use(Stepper).use(Tag).use(Calendar).use(RadioGroup).use(Radio)
 .use(Row).use(Col).use(Loading).use(DropdownMenu).use(DropdownItem).use(Button).use(ActionSheet)
-.use(PullRefresh).use(Tabbar).use(TabbarItem).use(Popover).use(Uploader).use(Collapse).use(CollapseItem).use(Empty).use(CheckboxGroup);
+.use(PullRefresh).use(Tabbar).use(TabbarItem).use(Popover).use(Uploader).use(Collapse).use(CollapseItem).use(Empty).use(CheckboxGroup).use(Switch);
 
 // rem
 import "amfe-flexible";

+ 70 - 26
fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/index.vue

@@ -29,9 +29,9 @@
                     <template #default>
                         <div class="attendanceRecord">
                             <template v-if="user.timeType.syncDingding == 1 || user.timeType.syncCorpwxTime == 1">
-                                <span>打卡:</span>
+                                <span>考勤:</span>
                                 <template v-if="user.companyId != 7536">
-                                    <span v-if="!report.time">暂无考勤记录</span>
+                                    <span v-if="!report.time">暂无</span>
                                     <span v-else>{{ report.time.startTime }}-{{ report.time.endTime }}, 工作{{
                                         report.time.workHours }}h
                                         <span v-if="report.time.askLeaveTime">|&nbsp;请假{{ report.time.askLeaveTime
@@ -41,7 +41,7 @@
                                 </template>
                             </template>
                             <template v-if="user.companyId == 7536">
-                                <span v-if="!report.time">暂无考勤记录</span>
+                                <span v-if="!report.time">暂无</span>
                                 <span v-else>工作{{
                                     report.time.workHours }}h
                                     <span v-if="report.time.askLeaveTime">|&nbsp;请假{{ report.time.askLeaveTime
@@ -64,6 +64,10 @@
                             style="height:0.6rem;padding:0 0.16667rem;" :loading="cardRefLoading" loading-size="0.26667rem"
                             @click.stop.native="cardtimeRefresh(form.createDate, (fillingAgent.id || ''))"
                             v-if="substitute && fillingAgent.name && (user.timeType.syncDingding == 1 || user.timeType.syncCorpwxTime == 1)"></van-button>
+                            <template v-if="(user.companyId <= 10 || user.companyId == 8555) && !report.time">
+                                <span style="margin:0 10px;color:#DAA520;">异常填报</span>
+                                <van-switch v-model="form.abnormalTime" size="20px" :disabled="!canEdit" @change="$forceUpdate()"/>
+                            </template>
                         </template>
                         <template v-if="user.companyId == 7536">
                             <van-button icon="replay" native-type="button" type="default" size="mini"
@@ -114,7 +118,7 @@
                     type="default"><span style="color:#666;padding: 0 5px;">删除</span></van-tag>
                 </div>
                 <!-- <van-icon v-if="index>0&&canEdit" class="form_del" name="delete" @click="delPro(index)" /> -->
-                <van-cell-group :title="(user.companyId == 781 ? '任务' : '项目') + (index + 1)">
+                <van-cell-group :title="'项目' + (index + 1)">
                     <van-field readonly name="userReportDeptName" v-if="user.timeType.userWithMultiDept == 1 && userReportDeptList.length > 0"
                         :value="item.userReportDeptName" :label="'填报部门'" placeholder="请选择部门" :disabled="!item.canEdit" @click="selectDeptPopup(index, item)">
                         <template #input>
@@ -145,7 +149,7 @@
                     </van-popup>
                     <!--任务分组 -->
                     <van-field readonly name="groupId" :disabled="!item.canEdit"
-                        v-if="user.company.packageProject == 1 && item.taskGroups != null && item.taskGroups.length > 0 && (user.company.nonProjectSimple==0 || (user.company.nonProjectSimple==1&& (item.projectId && (projectss.filter(p => p.id == item.projectId)[0] || {}).isPublic!=1)))"
+                        v-if="user.company.packageProject == 1 && !user.timeType.hideGroup && item.taskGroups != null && item.taskGroups.length > 0 && (user.company.nonProjectSimple==0 || (user.company.nonProjectSimple==1&& (item.projectId && (projectss.filter(p => p.id == item.projectId)[0] || {}).isPublic!=1)))"
                         clickable :rules="[{ required: user.companyId == 3092 ? true : false, message: '请选择任务分组' }]"
                         :value="item.groupName" label="任务分组" placeholder="请选择任务分组"
                         @click="clickPickTaskGroup(index, item)" />
@@ -369,10 +373,6 @@
                         <van-picker show-toolbar :columns="item.serviceList" value-key="serviceName" @confirm="choseService"
                             @cancel="item.showPickerService = false; $forceUpdate()" />
                     </van-popup>
-                    <!-- <van-field readonly clickable class="form_input" :value="item.workingTime" name="workingTime" label="工作时长" placeholder="请输入工作时长(单位:小时)"
-                    :rules="[{ required: true, message: '请输入工作时长(单位:小时)' }]" @touchstart.native.stop="showNumberKey = true"/>
-                    <van-number-keyboard v-model="item.workingTime" :show="showNumberKey" close-button-text="完成" extra-key="." :maxlength="4" @blur="showNumberKey = false" /> -->
-                    <!-- 常规选择时间的方式 -->
                     <!-- 全天上下午模式 -->
                     <div v-if="reportTimeType.multiWorktime == 0 && (!hideWorkingHours || (hideWorkingHours && item.canEdit)) ">
                         <van-field v-if="reportTimeType.type < 2" readonly clickable :disabled="!item.canEdit"
@@ -425,8 +425,8 @@
                                 </div>
                             </template>
                         </van-cell>
-                        <van-field class="form_input" :disabled="!item.canEdit" v-model="item.content" name="content" v-if="user.companyId != 7544"
-                            type="textarea" :label="user.companyId == 781 ? '具体内容与结果' : '工作事项'" placeholder="请输入"
+                        <van-field class="form_input" :disabled="!item.canEdit" v-model="item.content" name="content" v-if="!user.timeType.hideContent"
+                            type="textarea" label="工作事项" placeholder="请输入"
                             :rules="user.timeType.workContentState == 1 ? [{ required: true, message: '请填写工作事项' }] : null"
                             rows="3" autosize />
                     </div>
@@ -494,10 +494,16 @@
                                 placeholder="请输入加班时长" style="width: 5.2rem" @input="$forceUpdate()"></van-field>
                             <span :class="item.canEdit ? 'overListTime' : 'overListTime hoveOver'">小时</span>
                         </div>
-                        <van-tag style="position:absolute;right:10px;" v-if="user.companyId != 4281 && isCorpWX && item.canEdit" type="primary"
+                        <!--羲合超导的照片上传-->
+                        <template v-if="(user.companyId <= 10|| user.companyId == 8555)">
+                            <van-tag style="position:absolute;right:10px;" v-if="form.abnormalTime && item.canEdit" type="primary"
+                            size="large" @click="takePhoto(index)">图片上传</van-tag>
+                        </template>
+                        <template v-else>
+                            <van-tag style="position:absolute;right:10px;" v-if="user.companyId != 4281 && isCorpWX && item.canEdit" type="primary"
                             size="large" @click="takePhoto(index)">拍照上传</van-tag>
-                        <!-- <van-tag style="position:absolute;right:10px;" type="primary" size="large" @click="takePhoto(index)">拍照上传</van-tag> -->
-                        <!-- <van-tag style="position:absolute;right:10px;" type="primary" size="large" @click="takePhoto(index)">拍照上传</van-tag> -->
+                        </template>
+                        
                     </div>
                     <div style="padding:5px;text-align:center;" v-if="!isIOSystem">
                         <span v-for="(p, index) in item.pics" :key="p" style="margin-right:15px;">
@@ -533,14 +539,14 @@
                     <van-tag size="large"
                     style="text-align:center;margin:10px;padding:12px;margin-bottom:120px;border: 1px solid #20a0ff;"
                     @click="addNewPro" icon="plus" color="#ffffff">
-                        <span style="color:#999;text-align:center;padding: 0 50px;"> + 新增{{ user.companyId == 781 ? '任务' : '项目' }}</span>
+                        <span style="color:#999;text-align:center;padding: 0 50px;"> + 新增项目</span>
                     </van-tag>
                 </template>
                 <template v-if="reportSettingsRow.type == 3 && canEdit">
                     <van-tag size="large"
                     style="text-align:center;margin:10px;padding:12px;margin-bottom:120px;border: 1px solid #20a0ff;"
                     @click="addNewPro" icon="plus" color="#ffffff">
-                        <span style="color:#999;text-align:center;padding: 0 50px;"> + 新增{{ user.companyId == 781 ? '任务' : '项目' }}</span>
+                        <span style="color:#999;text-align:center;padding: 0 50px;"> + 新增项目</span>
                     </van-tag>
                 </template>
                 
@@ -691,7 +697,7 @@ export default {
             selectTime: null,
             reportTimeType: {},
             user: JSON.parse(localStorage.userInfo),
-            minDate: new Date(2010, 0, 1),
+            minDate: new Date(2020, 0, 1),
             maxDate: new Date(),
             currentDate: new Date(),
             showPickerTime: false,
@@ -718,6 +724,7 @@ export default {
                     degreeId: '',
                     extraField4: '',
                     extraField5: '',
+                    abnormalTime: false
                     // pics:["https://worktime.ttkuaiban.com/upload/bc4df504fa724e6cab69872e2c1cfb35.png",
                     // "https://worktime.ttkuaiban.com/upload/bc4df504fa724e6cab69872e2c1cfb35.png",
                     // "https://worktime.ttkuaiban.com/upload/bc4df504fa724e6cab69872e2c1cfb35.png",]
@@ -1690,7 +1697,19 @@ export default {
                             }
                             arr.push(obj)
                         }
-                        this.vacationTime = arr
+                        this.vacationTime = arr;
+                        //设置maxDate
+                        if (t.fillAhead == 1) {
+                            // 如果fillAhead为1,设置maxDate为今天+30天
+                            let today = new Date();
+                            let maxDate = new Date(today);
+                            maxDate.setDate(today.getDate() + 30);
+                            this.maxDate = maxDate;
+                        } else {
+                            // 否则设置为今天
+                            this.maxDate = new Date();
+                        }
+                        
                     } else {
                         this.$toast.clear();
                         this.$toast.fail(res.msg);
@@ -1887,7 +1906,13 @@ export default {
                             this.canCancel = false;
                             this.canDeleteReport = true
                             let array = [];
+                            var abnormalTime = false;
                             for (var i in list) {
+                                if (this.user.companyId <= 10 || this.user.companyId == 8555) {
+                                    if (list[i].extraField1) {
+                                        abnormalTime = true;
+                                    }
+                                }
                                 var projectName = list[i].projectName;
                                 var flg = (list[i].isOvertime == 1);
                                 for (var j in this.project) {
@@ -1895,7 +1920,6 @@ export default {
                                         projectName = this.project[j].projectName;
                                     }
                                 }
-                                console.log(projectName);
                                 let tname = '';
                                 if (list[i].taskId != null && list[i].taskList.length > 0) {
                                     let filterList = list[i].taskList.filter(t => t.taskId == list[i].taskId);
@@ -1920,7 +1944,6 @@ export default {
                                     wuduName = arr.join(',')
                                 }
                                 let serverPicArray = [];
-                                // console.log('picArray=='+list[i].picStr);
                                 if (list[i].picStr != null && list[i].picStr != '@') {
                                     serverPicArray = JSON.parse(list[i].picStr.replace(/@/g, ","));
                                 }
@@ -2040,6 +2063,7 @@ export default {
                                     }, 0);
                                 }
                             }
+                            this.form.abnormalTime = abnormalTime;
                             this.form.domains = array;
                             this.setTotalReportHours()
                         } else {
@@ -2071,6 +2095,7 @@ export default {
                                 auditorThird: { name: '', id: '' },
                                 ccUserid: { name: '', id: '' }
                             }]
+                            this.$set(this.form,'abnormalTime', false);
                              // businessTrips 有数据的情况下
                              const businessTrips = res.data.businessTrips || []
                             if(businessTrips.length > 0) {
@@ -2822,15 +2847,30 @@ export default {
                 }
             }
             //非代填情况下要校验考勤
-            if (!this.substitute && this.user.timeType.notAllowedNoAttendance == 1 && this.isDraft == 0) {
-                if (this.report.time) {
-                    if (this.report.time.workHours == 0) {
+            if (!this.substitute) {
+                if ((this.user.companyId <= 10 || this.user.companyId == 8555) && this.form.abnormalTime) {
+                    //异常上报情况,需要上传图片
+                    var findImg = false;
+                    for (var t=0;t<this.form.domains.length; t++) {
+                        if(this.form.domains[t].serverPics != null && this.form.domains[t].serverPics.length > 0) {
+                            findImg = true;
+                            break;
+                        }
+                    }
+                    if (!findImg) {
+                        this.$toast.fail('异常上报需上传凭证截图')
+                        return
+                    }
+                } else if (this.user.timeType.notAllowedNoAttendance == 1 && this.isDraft == 0) {
+                    if (this.report.time) {
+                        if (this.report.time.workHours == 0) {
+                            this.$toast.fail('无考勤记录不可填报')
+                            return
+                        }
+                    } else {
                         this.$toast.fail('无考勤记录不可填报')
                         return
                     }
-                } else {
-                    this.$toast.fail('无考勤记录不可填报')
-                    return
                 }
             }
             //针对凡己,不校验考勤时长
@@ -3089,6 +3129,10 @@ export default {
                     formData.append('extraField4', this.form.domains[i].extraField4 || '');
                     formData.append('extraField5', this.form.domains[i].extraField5 || '');
                 }
+                //羲合超导的异常工时上报
+                if (this.user.companyId <= 10 || this.user.companyId == 8555) {
+                    formData.append("abnormalTime", this.form.abnormalTime);
+                }
             }
             if (!this.flgLg) {
                 return

+ 6 - 6
fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/weekEdit.vue

@@ -78,7 +78,7 @@
                                 @cancel="item.showPickerSubProject = false;$forceUpdate();" />
                         </van-popup>
                         <!--任务分组 -->
-                        <van-field  readonly  name="groupId" v-if="user.company.packageProject==1&&item.taskGroups != null && item.taskGroups.length > 0 && (user.company.nonProjectSimple==0 || (user.company.nonProjectSimple==1 && (item.projectId && (projectss.filter(p => p.id == item.projectId)[0] || {}).isPublic!=1)))" clickable 
+                        <van-field  readonly  name="groupId" v-if="user.company.packageProject==1&&!user.timeType.hideGroup&&item.taskGroups != null && item.taskGroups.length > 0 && (user.company.nonProjectSimple==0 || (user.company.nonProjectSimple==1 && (item.projectId && (projectss.filter(p => p.id == item.projectId)[0] || {}).isPublic!=1)))" clickable 
                             :value="item.groupName" :label="user.timeType.enableNewWeeklyfill == 1 ? '分组' : '任务分组'" :placeholder="user.timeType.enableNewWeeklyfill == 1 ? '请选择分组' : '请选择任务分组'" 
                         @click="clickPickTaskGroup(index, item)" :disabled="item.state<=1" />
                         <!-- <van-popup v-model="item.showPickerTaskGroup" position="bottom">
@@ -132,7 +132,7 @@
                         <!-- 审核人 -->
                     <template v-if="user.timeType.reportAuditType != 3">
                         <van-field  readonly  name="projectAuditorId" v-if="item.auditUserList != null && item.auditUserList.length > 0" clickable
-                            :value="item.projectAuditorName" :label="user.companyId==781?'审核人':'项目审核人'" placeholder="请选择审核人"  :disabled="item.state<=1"
+                            :value="item.projectAuditorName" label="项目审核人" placeholder="请选择审核人"  :disabled="item.state<=1"
                         @click="clickPickAuditor(index, item)">
                             <template #input>
                                 <span v-if="user.userNameNeedTranslate == '1'"><TranslationOpenDataText type='userName' :openid='item.projectAuditorName'></TranslationOpenDataText></span>
@@ -225,7 +225,7 @@
 
                         <!-- 任务里程碑 -->
                         <van-field :label="'任务/里程碑'" :value="item.taskName" :disabled="item.state<=1" readonly 
-                            clickable v-if="user.company.packageProject == 1 && !user.timeType.hideStages && (user.company.nonProjectSimple==0 || (item.projectId && (projectss.filter(p => p.id == item.projectId)[0] || {}).isPublic!=1))" 
+                            clickable v-if="user.company.packageProject == 1 && !user.timeType.hideTask && (user.company.nonProjectSimple==0 || (item.projectId && (projectss.filter(p => p.id == item.projectId)[0] || {}).isPublic!=1))" 
                             @click="clickTakKer(index, item)"
                         />
                         <van-popup v-model="item.showTaksDegree" position="bottom" @click-overlay="overlayPopup(index, 'showTaksDegree')">
@@ -317,9 +317,9 @@
                                 </div>
                             </template>
                         </van-cell>
-                        <van-field class="form_input" v-if="user.companyId != 7544"
-                        v-model="item.content" name="content" type="textarea" :label="user.companyId==781?'具体内容与结果': user.companyId == wuqiId ? '日报' : '工作事项'" :placeholder="user.companyId == wuqiId ? '所在项目负责哪一模块(可重复填写),无需涉及技术细节。' : '请输入'" :disabled="item.state<=1"
-                        rows="3" autosize :rules="user.timeType.workContentState == 1 ? [{ required: true, message: user.companyId==781?'具体内容与结果':user.companyId == wuqiId ? '日报' : '工作事项' }] : null" :maxlength="user.companyId == wuqiId ? 30 : 150" />
+                        <van-field class="form_input" v-if="!user.timeType.hideContent"
+                        v-model="item.content" name="content" type="textarea" :label="user.companyId == wuqiId ? '日报' : '工作事项'" :placeholder="user.companyId == wuqiId ? '所在项目负责哪一模块(可重复填写),无需涉及技术细节。' : '请输入'" :disabled="item.state<=1"
+                        rows="3" autosize :rules="user.timeType.workContentState == 1 ? [{ required: true, message: user.companyId == wuqiId ? '日报' : '工作事项' }] : null" :maxlength="user.companyId == wuqiId ? 30 : 150" />
                         </div>
                         
                         <!-- 多个时间和工作事项的选择方式 -->

+ 2 - 2
fhKeeper/formulahousekeeper/timesheet_h5/src/views/review/index.vue

@@ -161,7 +161,7 @@
                                 <span v-if="item1.reportTimeType == 2" style="margin-right:10px;">{{item1.startTime+'-'+item1.endTime}}</span>{{item1.time.toFixed(1)}}h
                                 <div class="button" v-if="item1.isOvertime == 1">加班<span v-if="item1.overtimeHours">{{item1.overtimeHours.toFixed(1)}}h</span></div>
                             </div>
-                            <div class="project_content">事项:<span v-html="item1.content"></span></div>
+                            <div v-if="!user.timeType.hideContent" class="project_content">事项:<span v-html="item1.content"></span></div>
                         </div>
                         <div v-if="item1.multiWorktime == 1">
                             <div>
@@ -174,7 +174,7 @@
                                     <span style="margin-right:10px;">{{timeItem.startTime+'-'+timeItem.endTime}}</span>{{timeItem.time.toFixed(1)}}h
                                     <span style="margin-left:5px;" v-if="timeItem.detail">{{ timeItem.detail }}</span>
                                 </div>
-                                <div class="project_content">事项:<span v-html="timeItem.content"></span></div>
+                                <div v-if="!user.timeType.hideContent" class="project_content">事项:<span v-html="timeItem.content"></span></div>
                             </div>
                         </div>
                         <div style="padding:5px;text-align:center;" v-if="item1.pics != null && item1.pics.length > 0">

+ 1 - 1
fhKeeper/formulahousekeeper/timesheet_h5/src/views/view/index.vue

@@ -163,7 +163,7 @@
                                         {{ item1.progress }}%
                                     </div>
                                     
-                                    <div class="project_content">事项:<span v-html="item1.content"></span></div>
+                                    <div v-if="!user.timeType.hideContent" class="project_content">事项:<span v-html="item1.content"></span></div>
 
                                 </div>
                                 <div v-if="item1.multiWorktime == 1">