Browse Source

日报驳回加原因等功能开发

seyason 3 năm trước cách đây
mục cha
commit
55165345bd
27 tập tin đã thay đổi với 963 bổ sung248 xóa
  1. 2 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/config/RefererInterceptor.java
  2. 90 95
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java
  3. 30 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/Report.java
  4. 7 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/TimeType.java
  5. 11 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/vo/WorktimeItem.java
  6. 6 5
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ReportMapper.java
  7. 2 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/UserMapper.java
  8. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ReportService.java
  9. 4 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/DingDingServiceImpl.java
  10. 215 55
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  11. 66 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/TimingTask.java
  12. 2 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/application.yml
  13. 17 7
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ReportMapper.xml
  14. 2 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/TimeTypeMapper.xml
  15. 8 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/UserMapper.xml
  16. 190 18
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue
  17. 40 9
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/list.vue
  18. 2 2
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/list_department.vue
  19. 2 2
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/list_profession.vue
  20. 148 20
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/index.vue
  21. 2 2
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/index/index.vue
  22. 7 1
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/msg/index.vue
  23. 4 2
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/project/index.vue
  24. 19 2
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/review/department_list.vue
  25. 45 12
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/review/index.vue
  26. 19 2
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/review/profession_list.vue
  27. 22 7
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/view/index.vue

+ 2 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/config/RefererInterceptor.java

@@ -30,12 +30,12 @@ public class RefererInterceptor extends HandlerInterceptorAdapter {
         String host = req.getServerName();
         //对外开放的回调url排除在外
         String reqUrl = req.getRequestURI();
-        System.out.println(reqUrl);
+//        System.out.println(reqUrl);
         if (!StringUtils.isEmpty(excludeUrls)) {
             String[] split = excludeUrls.split(",");
             boolean isPass = false;
             for (String pattern: split) {
-                System.out.println(""+pattern+", "+reqUrl);
+//                System.out.println(""+pattern+", "+reqUrl);
                 if (matcher.match(pattern, reqUrl)) {
                     isPass = true;
                     break;

+ 90 - 95
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java

@@ -9,6 +9,7 @@ import com.management.platform.entity.Report;
 import com.management.platform.entity.ReportProfessionProgress;
 import com.management.platform.entity.User;
 import com.management.platform.entity.UserSalary;
+import com.management.platform.entity.vo.WorktimeItem;
 import com.management.platform.service.ReportService;
 import com.management.platform.service.UserSalaryService;
 import com.management.platform.service.UserService;
@@ -129,12 +130,20 @@ public class ReportController {
                                   String[] targetUids,
                                   String[] professionProgress,
                                   String[] stage,
-                                  String[] pics
+                                  String[] pics,
+                                  Integer[] multiWorktime
                                     ) {
         List<Report> reportList = new ArrayList<>();
         String token = request.getHeader("Token");
         List<String> targetUidList = null;
         List<User> targetUserList = null;
+        //初始化,防止老版本无此字段奔溃报错
+        if (multiWorktime == null) {
+            multiWorktime = new Integer[projectId.length];
+            for(int i=0;i<multiWorktime.length; i++) {
+                multiWorktime[i] = 0;
+            }
+        }
         //代填
         if (targetUids != null && targetUids.length > 0) {
             String val = targetUids[0];
@@ -243,6 +252,7 @@ public class ReportController {
                                         .setProjectId(projectId[i])
                                         .setSubProjectId(subProjectId[i] == 0?null:subProjectId[i])
                                         .setReportTimeType(reportTimeType[i])
+                                        .setMultiWorktime(multiWorktime[i])
                                         .setContent(content[i])
                                         .setState(0)
                                         .setPicAdd(pics!=null?pics[i]:null)
@@ -258,30 +268,14 @@ public class ReportController {
                                 if (progress != null && progress[i] != null) {
                                     report.setProgress(progress[i]);
                                 }
-                                if (report.getReportTimeType() == 0) {
-                                    report.setWorkingTime(workingTime[i])
-                                            .setCost(hourCost.multiply(new BigDecimal(workingTime[i])))
-                                            .setTimeType(timeType[i]);
-                                } else if (report.getReportTimeType() == 1) {
-                                    report.setWorkingTime(workingTime[i])
-                                            .setCost(hourCost.multiply(new BigDecimal(workingTime[i])));
-                                } else if (report.getReportTimeType() == 2) {
-                                    //时间范围填报, 计算一下时长
-                                    try {
-                                        report.setStartTime(startTime[i]).setEndTime(endTime[i]);
-                                        long time = sdf.parse(report.getEndTime()).getTime() - sdf.parse(report.getStartTime()).getTime();
-                                        int minutes = (int)time/1000/60;
-                                        double hours = minutes*1.0f/60;
-                                        report.setWorkingTime(hours);
-                                        report.setCost(hourCost.multiply(new BigDecimal(hours)));
-                                    } catch (ParseException e) {
-                                        e.printStackTrace();
-                                    }
-                                } else if (report.getReportTimeType() == 3) {
-                                    //计算时长
-                                    report.setWorkingTime(workingTime[i])
-                                            .setCost(hourCost.multiply(new BigDecimal(workingTime[i])));
+                                //计算工时和成本
+                                if (report.getMultiWorktime() == 0) {
+                                    fillReportHours(report, hourCost, workingTime==null?null:workingTime[i], timeType==null?null:timeType[i], startTime==null?null:startTime[i], endTime==null?null:endTime[i],  sdf);
+                                } else {
+                                    fillReportHours(report, hourCost, workingTime==null?null:workingTime[i], timeType==null?null:timeType[i], null, null, sdf);
                                 }
+
+
                                 //项目专业的进展
                                 fillReportProgress(report, professionProgress[i]);
                                 reportList.add(report);
@@ -293,6 +287,7 @@ public class ReportController {
                                             .setProjectId(projectId[i])
                                             .setSubProjectId(subProjectId[i] == 0?null:subProjectId[i])
                                             .setReportTimeType(reportTimeType[i])
+                                            .setMultiWorktime(multiWorktime[i])
                                             .setContent(content[i])
                                             .setStage(stage!=null && stage.length > 0  && !StringUtil.isEmpty(stage[i])?stage[i]:null)
                                             .setState(1)//代填,直接是审核通过状态
@@ -308,30 +303,13 @@ public class ReportController {
                                     if (progress != null && progress[i] != null) {
                                         report.setProgress(progress[i]);
                                     }
-                                    if (report.getReportTimeType() == 0) {
-                                        report.setWorkingTime(workingTime[i])
-                                                .setCost(subsUser.getCost().multiply(new BigDecimal(workingTime[i])))
-                                                .setTimeType(timeType[i]);
-                                    } else if (report.getReportTimeType() == 1) {
-                                        report.setWorkingTime(workingTime[i])
-                                                .setCost(subsUser.getCost().multiply(new BigDecimal(workingTime[i])));
-                                    } else if (report.getReportTimeType() == 2) {
-                                        //时间范围填报, 计算一下时长
-                                        try {
-                                            report.setStartTime(startTime[i]).setEndTime(endTime[i]);
-                                            long time = sdf.parse(report.getEndTime()).getTime() - sdf.parse(report.getStartTime()).getTime();
-                                            int minutes = (int)time/1000/60;
-                                            double hours = minutes*1.0f/60;
-                                            report.setWorkingTime(hours);
-                                            report.setCost(subsUser.getCost().multiply(new BigDecimal(hours)));
-                                        } catch (ParseException e) {
-                                            e.printStackTrace();
-                                        }
-                                    } else if (report.getReportTimeType() == 3) {
-                                        //计算时长
-                                        report.setWorkingTime(workingTime[i])
-                                                .setCost(subsUser.getCost().multiply(new BigDecimal(workingTime[i])));
+                                    //计算工时和成本
+                                    if (report.getMultiWorktime() == 0) {
+                                        fillReportHours(report, hourCost, workingTime==null?null:workingTime[i], timeType==null?null:timeType[i], startTime==null?null:startTime[i], endTime==null?null:endTime[i],  sdf);
+                                    } else {
+                                        fillReportHours(report, hourCost, workingTime==null?null:workingTime[i], timeType==null?null:timeType[i], null, null, sdf);
                                     }
+
                                     fillReportProgress(report, professionProgress[i]);
                                     reportList.add(report);
                                 }
@@ -353,6 +331,7 @@ public class ReportController {
                                 .setProjectId(projectId[i])
                                 .setSubProjectId(subProjectId[i] == 0?null:subProjectId[i])
                                 .setReportTimeType(reportTimeType[i])
+                                .setMultiWorktime(multiWorktime[i])
                                 .setContent(content[i])
                                 .setStage(stage!=null && stage.length > 0  && !StringUtil.isEmpty(stage[i])?stage[i]:null)
                                 .setState(0)
@@ -368,30 +347,13 @@ public class ReportController {
                         if (progress != null && progress[i] != null) {
                             report.setProgress(progress[i]);
                         }
-                        if (report.getReportTimeType() == 0) {
-                            report.setWorkingTime(workingTime[i])
-                                    .setCost(hourCost.multiply(new BigDecimal(workingTime[i])))
-                                    .setTimeType(timeType[i]);
-                        } else if (report.getReportTimeType() == 1) {
-                            report.setWorkingTime(workingTime[i])
-                                    .setCost(hourCost.multiply(new BigDecimal(workingTime[i])));
-                        } else if (report.getReportTimeType() == 2) {
-                            //时间范围填报, 计算一下时长
-                            try {
-                                report.setStartTime(startTime[i]).setEndTime(endTime[i]);
-                                long time = sdf.parse(report.getEndTime()).getTime() - sdf.parse(report.getStartTime()).getTime();
-                                int minutes = (int)time/1000/60;
-                                double hours = minutes*1.0f/60;
-                                report.setWorkingTime(hours);
-                                report.setCost(hourCost.multiply(new BigDecimal(hours)));
-                            } catch (ParseException e) {
-                                e.printStackTrace();
-                            }
-                        } else if (report.getReportTimeType() == 3) {
-                            //计算时长
-                            report.setWorkingTime(workingTime[i])
-                                    .setCost(hourCost.multiply(new BigDecimal(workingTime[i])));
+                        //计算工时和成本
+                        if (report.getMultiWorktime() == 0) {
+                            fillReportHours(report, hourCost, workingTime==null?null:workingTime[i], timeType==null?null:timeType[i], startTime==null?null:startTime[i], endTime==null?null:endTime[i], sdf);
+                        } else {
+                            fillReportHours(report, hourCost, workingTime==null?null:workingTime[i], timeType==null?null:timeType[i], null, null, sdf);
                         }
+
                         fillReportProgress(report, professionProgress[i]);
                         reportList.add(report);
                         /*后续需要加入状态*/
@@ -409,6 +371,7 @@ public class ReportController {
                                     .setProjectId(projectId[i])
                                     .setSubProjectId(subProjectId[i] == 0?null:subProjectId[i])
                                     .setReportTimeType(reportTimeType[i])
+                                    .setMultiWorktime(multiWorktime[i])
                                     .setContent(content[i])
                                     .setStage(stage!=null && stage.length > 0  && !StringUtil.isEmpty(stage[i])?stage[i]:null)
                                     .setState(1)//代填的就直接审核通过了
@@ -424,30 +387,14 @@ public class ReportController {
                             if (progress != null && progress[i] != null) {
                                 report.setProgress(progress[i]);
                             }
-                            if (report.getReportTimeType() == 0) {
-                                report.setWorkingTime(workingTime[i])
-                                        .setCost(subsUser.getCost().multiply(new BigDecimal(workingTime[i])))
-                                        .setTimeType(timeType[i]);
-                            } else if (report.getReportTimeType() == 1) {
-                                report.setWorkingTime(workingTime[i])
-                                        .setCost(subsUser.getCost().multiply(new BigDecimal(workingTime[i])));
-                            } else if (report.getReportTimeType() == 2) {
-                                //时间范围填报, 计算一下时长
-                                try {
-                                    report.setStartTime(startTime[i]).setEndTime(endTime[i]);
-                                    long time = sdf.parse(report.getEndTime()).getTime() - sdf.parse(report.getStartTime()).getTime();
-                                    int minutes = (int)time/1000/60;
-                                    double hours = minutes*1.0f/60;
-                                    report.setWorkingTime(hours);
-                                    report.setCost(subsUser.getCost().multiply(new BigDecimal(hours)));
-                                } catch (ParseException e) {
-                                    e.printStackTrace();
-                                }
-                            } else if (report.getReportTimeType() == 3) {
-                                //计算时长
-                                report.setWorkingTime(workingTime[i])
-                                        .setCost(subsUser.getCost().multiply(new BigDecimal(workingTime[i])));
+                            //计算工时和成本
+                            if (report.getMultiWorktime() == 0) {
+                                fillReportHours(report, hourCost, workingTime==null?null:workingTime[i], timeType==null?null:timeType[i], startTime==null?null:startTime[i], endTime==null?null:endTime[i],  sdf);
+                            } else {
+                                fillReportHours(report, hourCost, workingTime==null?null:workingTime[i], timeType==null?null:timeType[i], null, null, sdf);
                             }
+
+
                             fillReportProgress(report, professionProgress[i]);
                             reportList.add(report);
                             /*后续需要加入状态*/
@@ -471,6 +418,54 @@ public class ReportController {
         return reportService.editReport(reportList, createDate.length > 0 ? createDate[0] : null, targetUserList, hourCost, user.getCompanyId());
     }
 
+    private void fillReportHours(Report report, BigDecimal hourCost, Double workingTime, Integer timeType, String startTime, String endTime, SimpleDateFormat sdf) {
+        if (report.getMultiWorktime() == 0) {
+            //普通工时成本计算
+            if (report.getReportTimeType() == 0) {
+                report.setWorkingTime(workingTime)
+                        .setCost(hourCost.multiply(new BigDecimal(workingTime)))
+                        .setTimeType(timeType);
+            } else if (report.getReportTimeType() == 1|| report.getReportTimeType() == 3) {
+                report.setWorkingTime(workingTime)
+                        .setCost(hourCost.multiply(new BigDecimal(workingTime)));
+            } else if (report.getReportTimeType() == 2) {
+                //时间范围填报, 计算一下时长
+                try {
+                    report.setStartTime(startTime).setEndTime(endTime);
+                    long time = sdf.parse(report.getEndTime()).getTime() - sdf.parse(report.getStartTime()).getTime();
+                    int minutes = (int)time/1000/60;
+                    double hours = minutes*1.0f/60;
+                    report.setWorkingTime(hours);
+                    report.setCost(hourCost.multiply(new BigDecimal(hours)));
+                } catch (ParseException e) {
+                    e.printStackTrace();
+                }
+            }
+        } else {
+            //getMultiWorktime==1, content中含有多个工时
+            String str = report.getContent().replaceAll("@",",");
+            JSONArray array = JSONArray.parseArray(str);
+            try {
+                double totalHours = 0;
+                for (int t=0;t<array.size(); t++) {
+                    JSONObject jsonObject = array.getJSONObject(t);
+                    WorktimeItem item = JSONObject.toJavaObject(jsonObject, WorktimeItem.class);
+                    long time = sdf.parse(item.getEndTime()).getTime() - sdf.parse(item.getStartTime()).getTime();
+                    int minutes = (int)time/1000/60;
+                    double hours = minutes*1.0f/60;
+                    item.setTime(hours);
+                    jsonObject.put("time", hours);
+                    totalHours += hours;
+                }
+                report.setWorkingTime(totalHours);
+                report.setCost(hourCost.multiply(new BigDecimal(totalHours)));
+                report.setContent(array.toJSONString());
+            } catch (ParseException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
     /**
      * 删除报告
      * id 要删除的报告的id
@@ -542,8 +537,8 @@ public class ReportController {
      * date 日期 格式yyyy-mm-dd
      */
     @RequestMapping("/deny")
-    public HttpRespMsg denyReport(@RequestParam String id, @RequestParam String date, @RequestParam String reportIds, HttpServletRequest request) {
-        return reportService.denyReport(id, date,reportIds, request);
+    public HttpRespMsg denyReport(@RequestParam String id, @RequestParam String date, @RequestParam String reportIds, String reason, HttpServletRequest request) {
+        return reportService.denyReport(id, date,reportIds, reason, request);
     }
 
     /**

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

@@ -10,6 +10,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import java.io.Serializable;
 import java.util.List;
 
+import com.management.platform.entity.vo.WorktimeItem;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
@@ -20,7 +21,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2021-10-28
+ * @since 2021-11-09
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -169,6 +170,34 @@ public class Report extends Model<Report> {
      */
     @TableField(exist = false)
     private List<String> pics;
+    /**
+     * 是否是多个时间工作事项
+     */
+    @TableField("multi_worktime")
+    private Integer multiWorktime;
+
+
+    @TableField(exist = false)
+    private List<WorktimeItem> worktimeList;
+    /**
+     * 驳回原因
+     */
+    @TableField("reject_reason")
+    private String rejectReason;
+
+    /**
+     * 驳回人姓名
+     */
+    @TableField("reject_username")
+    private String rejectUsername;
+
+    /**
+     * 驳回人id
+     */
+    @TableField("reject_userid")
+    private String rejectUserid;
+
+
     @Override
     protected Serializable pkVal() {
         return this.id;

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

@@ -15,7 +15,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2021-09-08
+ * @since 2021-11-06
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -78,6 +78,12 @@ public class TimeType extends Model<TimeType> {
     @TableField("alert_time")
     private String alertTime;
 
+    /**
+     * 支持同个项目多个时间事项录入
+     */
+    @TableField("multi_worktime")
+    private Integer multiWorktime;
+
 
     @Override
     protected Serializable pkVal() {

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

@@ -0,0 +1,11 @@
+package com.management.platform.entity.vo;
+
+import lombok.Data;
+
+@Data
+public class WorktimeItem {
+    String startTime;
+    String endTime;
+    String content;
+    double time;
+}

+ 6 - 5
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ReportMapper.java

@@ -6,6 +6,7 @@ import org.apache.ibatis.annotations.Param;
 
 import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -18,11 +19,11 @@ import java.util.Map;
  * @since 2019-12-31
  */
 public interface ReportMapper extends BaseMapper<Report> {
-    List<Map<String, Object>> getAllReportByDate(@Param("startDate") String startDate,
-                                                 @Param("companyId") Integer companyId,
-                                                 @Param("userId") String userId,
-                                                 @Param("endDate") String endDate,
-                                                 @Param("projectId") Integer projectId
+    List<HashMap<String, Object>> getAllReportByDate(@Param("startDate") String startDate,
+                                                     @Param("companyId") Integer companyId,
+                                                     @Param("userId") String userId,
+                                                     @Param("endDate") String endDate,
+                                                     @Param("projectId") Integer projectId
                                                  );
 
     //按当前人员获取本人报告

+ 2 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/UserMapper.java

@@ -33,4 +33,6 @@ public interface UserMapper extends BaseMapper<User> {
 
     List<Map<String, Object>> getPushUserList(@Param("companyId") Integer companyId);
 
+    List<Map<String, Object>> getProjectPushUserList(@Param("projectId") Integer projectId);
+
 }

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

@@ -35,7 +35,7 @@ public interface ReportService extends IService<Report> {
 
     HttpRespMsg approveReport(String id, String reportIds, HttpServletRequest request);
 
-    HttpRespMsg denyReport(String id, String date, String reportIds,HttpServletRequest request);
+    HttpRespMsg denyReport(String id, String date, String reportIds, String reason, HttpServletRequest request);
 
     HttpRespMsg singleApproveReport(Integer id, HttpServletRequest request);
 

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

@@ -233,6 +233,10 @@ public class DingDingServiceImpl implements DingDingService {
                 JSONObject result = json.getJSONObject("result");
                 String dduid = result.getString("userid");
                 List<User> userList = userMapper.selectList(new QueryWrapper<User>().eq("dingding_userid", dduid).orderByDesc("create_time"));
+                if (userList.size() == 0) {
+                    httpRespMsg.setError("您尚无权使用系统,请联系管理员。");
+                    return httpRespMsg;
+                }
                 User user = userList.get(0);
                 if (user.getIsActive() == 0) {
                     httpRespMsg.setError("该账户已停用,无法登陆。请联系管理员");

+ 215 - 55
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java

@@ -8,6 +8,7 @@ import com.management.platform.controller.WeiXinCorpController;
 import com.management.platform.entity.*;
 import com.management.platform.entity.vo.DepartmentVO;
 import com.management.platform.entity.vo.UserMonthWork;
+import com.management.platform.entity.vo.WorktimeItem;
 import com.management.platform.mapper.*;
 import com.management.platform.service.DepartmentService;
 import com.management.platform.service.ReportProfessionProgressService;
@@ -62,6 +63,9 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
 
     @Value("${wx.template_report_pass}")
     public String TEMPLATE_REPORT_PASS;
+    @Value("${wx.template_report_reject}")
+    public String TEMPLATE_REPORT_REJECT;
+
     @Value("${wx.app_id}")
     public String appId;
     @Value("${wx.app_secret}")
@@ -334,6 +338,11 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                         }
                         report.put("pics", picList);
                     }
+                    if (((Integer)report.get("multiWorktime")) == 1) {
+                        //设置多个工时情况下的报告列表
+                        String timeStr = (String)report.get("content");
+                        report.put("worktimeList", JSONArray.parse(timeStr));
+                    }
                 }
             }
             httpRespMsg.data = nameList;
@@ -400,6 +409,17 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                     }
                     r.setPics(list);
                 }
+                if (r.getMultiWorktime() == 1) {
+                    //设置多个工时情况下的报告列表
+                    String timeStr = r.getContent();
+                    JSONArray parse = JSONArray.parseArray(timeStr);
+                    List<WorktimeItem> list = new ArrayList<>();
+                    for (int i=0;i<parse.size(); i++) {
+                        JSONObject obj = parse.getJSONObject(i);
+                        list.add(JSONObject.toJavaObject(obj, WorktimeItem.class));
+                    }
+                    r.setWorktimeList(list);
+                }
             });
             resultMap.put("report", reports);
             //顺便再获取一下可分配时间
@@ -682,7 +702,13 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                         }
                         report.put("pics", picList);
                     }
+                    if (((Integer)report.get("multiWorktime")) == 1) {
+                        //设置多个工时情况下的报告列表
+                        String timeStr = (String)report.get("content");
+                        report.put("worktimeList", JSONArray.parse(timeStr));
+                    }
                 }
+
             }
             httpRespMsg.data = nameList;
         } catch (NullPointerException e) {
@@ -809,14 +835,46 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                         if (wxCorpInfo != null && user.getCorpwxUserid() != null) {
                             wxCorpInfoService.sendWXCorpMsg(wxCorpInfo, user.getCorpwxUserid(), msg);
                         } else if (user.getWxOpenid() != null){
-                            push(p.getProjectName(), user);
+                            pushPass(p.getProjectName(), user);
                         }
                     });
         });
     }
 
+    public boolean pushReject(String str, User user, String rejectUsername, String reason) {
+        //1,配置
+        WxMpInMemoryConfigStorage wxStorage = new WxMpInMemoryConfigStorage();
+        wxStorage.setAppId(appId);
+        wxStorage.setSecret(appSecret);
+        WxMpService wxMpService = new WxMpServiceImpl();
+        wxMpService.setWxMpConfigStorage(wxStorage);
+
+        //2,推送消息
+        WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
+                .toUser(user.getWxOpenid())//要推送的用户openid
+                .templateId(TEMPLATE_REPORT_REJECT)//模版id
+                .url("http://mobworktime.ttkuaiban.com/")//点击模版消息要访问的网址
+                .build();
+        //3,如果是正式版发送模版消息,这里需要配置你的信息
+        templateMessage.addData(new WxMpTemplateData("first", "你好,您的日报被驳回", "#FF00FF"));
+        templateMessage.addData(new WxMpTemplateData("keyword1", str, "#000000"));
+        templateMessage.addData(new WxMpTemplateData("keyword2", rejectUsername, "#000000"));
+        templateMessage.addData(new WxMpTemplateData("keyword3", reason, "#000000"));
+        templateMessage.addData(new WxMpTemplateData("keyword4", DateTimeFormatter.ofPattern("yyyy-MM-dd").format(LocalDateTime.now()), "#000000"));
+        templateMessage.addData(new WxMpTemplateData("remark", "请修改后重新提交", "#000000"));
+        //                templateMessage.addData(new WxMpTemplateData(name2, value2, color2));
+        try {
+            wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
+        } catch (Exception e) {
+            System.out.println("推送失败:" + e.getMessage());
+            e.printStackTrace();
+            return false;
+        }
+        return true;
+    }
+
 
-    public boolean push(String projectName, User user) {
+    public boolean pushPass(String projectName, User user) {
         //1,配置
         WxMpInMemoryConfigStorage wxStorage = new WxMpInMemoryConfigStorage();
         wxStorage.setAppId(appId);
@@ -849,13 +907,15 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
 
     //审核未通过 以及 撤销审核某天某人
     @Override
-    public HttpRespMsg denyReport(String id, String date, String reportIds, HttpServletRequest request) {
+    public HttpRespMsg denyReport(String id, String date, String reportIds, String reason, HttpServletRequest request) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         try {
             User user = userMapper.selectById(request.getHeader("Token"));
             Company company = companyMapper.selectById(user.getCompanyId());
             final List<Long> ids = ListUtil.convertIdsArrayToList(reportIds);
-
+            if (reason == null) {
+                reason = "-";
+            }
             if (company.getPackageEngineering() == 1) {
                 //检查是否有专业进度待审核
                 List<ReportProfessionProgress> list = reportProfessionProgressService.list(new QueryWrapper<ReportProfessionProgress>().eq("report_id", ids.get(0)).eq("audit_state", 0));
@@ -886,9 +946,26 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 }
             }
             //直接进行项目经理审核驳回
-            reportMapper.update(new Report().setState(2),
+            reportMapper.update(new Report().setState(2)
+                            .setRejectReason(reason).setRejectUserid(user.getId()).setRejectUsername(user.getName()),
                     new QueryWrapper<Report>().in("id", ListUtil.convertIdsArrayToList(reportIds)));
-            informationMapper.insert(new Information().setType(0).setContent(date).setUserId(id));
+            List<Report> rList = reportMapper.selectList(new QueryWrapper<Report>().in("id", ListUtil.convertIdsArrayToList(reportIds)));
+            List<Integer> collect = rList.stream().map(Report::getProjectId).collect(Collectors.toList());
+            List<Project> projectList = projectMapper.selectList(new QueryWrapper<Project>().in("id", collect));
+            String pNames = projectList.stream().map(Project::getProjectName).collect(Collectors.joining(", ", "[", "]"));
+            String str = "您"+date+"填写的日报中"+pNames+"项目被领导驳回。原因:" + reason;
+            informationMapper.insert(new Information().setType(0).setContent(date).setUserId(id).setMsg(str));
+
+            //发送企业微信通知消息
+            User reporter = userMapper.selectById(id);
+            String corpwxUserid = reporter.getCorpwxUserid();
+            if (corpwxUserid != null) {
+                WxCorpInfo info = wxCorpInfoMapper.selectOne(new QueryWrapper<WxCorpInfo>().eq("company_id", company.getId()));
+                wxCorpInfoService.sendWXCorpMsg(info, corpwxUserid, str);
+            } else if (reporter.getWxOpenid() != null){
+                //发送个人微信通知
+                pushReject(str, reporter, user.getName(), reason);
+            }
 
         } catch (NullPointerException e) {
             httpRespMsg.setError("验证失败");
@@ -1156,7 +1233,6 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             nameList.forEach(n->{
                 n.put("dateStr",sdf.format((java.sql.Date)n.get("date")));
             });
-            System.out.println("日报人员列表: "+nameList.size());
             List<User> userList = userMapper.selectList(new QueryWrapper<User>().eq("company_id", companyId));
             List<Profession> professions = professionMapper.selectList(new QueryWrapper<Profession>().eq("company_id", curUser.getCompanyId()));
             for (int index=0;index<nameList.size(); index++) {
@@ -1165,9 +1241,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 List<Map<String, Object>> list2 = null;
                 //获取相关项目的报告
                 List<Map<String, Object>> inchargeReportList= reportMapper.getProfessionInchargeReportByDate(createDate.toString(), leaderId, state);
-                System.out.println("人员日报列表:"+inchargeReportList.size());
                 list2 = inchargeReportList.stream().filter(i->i.get("creatorId").equals(map2.get("id"))).collect(Collectors.toList());
-                System.out.println("人员日报列表, 过滤后:"+list2.size());
                 //按项目过滤
                 if (projectId != null) {
                     list2 = list2.stream().filter(report->(((Integer)report.get("projectId")).equals(projectId))).collect(Collectors.toList());
@@ -1217,6 +1291,11 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                         }
                         report.put("pics", picList);
                     }
+                    if (((Integer)report.get("multiWorktime")) == 1) {
+                        //设置多个工时情况下的报告列表
+                        String timeStr = (String)report.get("content");
+                        report.put("worktimeList", JSONArray.parse(timeStr));
+                    }
                 }
             }
             httpRespMsg.data = nameList;
@@ -1242,7 +1321,6 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             //按日期过滤
             if (!StringUtils.isEmpty(date)) {
                 nameList = nameList.stream().filter(map->{
-                    System.out.println("已有数据日期=="+sdf.format((java.sql.Date)map.get("date"))+", 参数date="+date);
                     return (sdf.format((java.sql.Date)map.get("date"))).equals(date);
                 }).collect(Collectors.toList());
             }
@@ -1306,6 +1384,11 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                         }
                         report.put("pics", picList);
                     }
+                    if (((Integer)report.get("multiWorktime")) == 1) {
+                        //设置多个工时情况下的报告列表
+                        String timeStr = (String)report.get("content");
+                        report.put("worktimeList", JSONArray.parse(timeStr));
+                    }
                 }
             }
             httpRespMsg.data = nameList;
@@ -1471,22 +1554,57 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         try {
             String userId = request.getHeader("Token");
             User user = userMapper.selectById(userId);
+            //检查模式,是否是一个项目多个工作事项的情况
+            TimeType timeType = timeTypeMapper.selectOne(new QueryWrapper<TimeType>().eq("company_id", user.getCompanyId()));
+            Company company = companyMapper.selectById(user.getCompanyId());
 
             //准备导出
             HSSFWorkbook workbook = new HSSFWorkbook();
             HSSFSheet sheet = workbook.createSheet("工作日报");
+            List<String> titles = new ArrayList<String>();
+            titles.addAll(Arrays.asList(new String[]{
+                    "序号","上传者","项目名称","子项目名称"
+            }));
+            //项目管理专业版以上,包括任务
+            if (company.getPackageProject() == 1) {
+                titles.add("关联任务");
+            }
+            titles.add("工作日期");
+            titles.add("工作时长(小时)");
+            if (timeType.getMultiWorktime() == 1) {
+                titles.add("工时时间");
+            }
+            if (timeType.getMultiWorktime() == 0) {
+                titles.add("是否加班");
+            }
+            titles.add("工作事项");
+
             //创建表头
             HSSFRow headRow = sheet.createRow(0);
             //设置列宽 setColumnWidth的第二个参数要乘以256 这个参数的单位是1/256个字符宽度
-            sheet.setColumnWidth(0, 5 * 256);
-            sheet.setColumnWidth(1, 10 * 256);
-            sheet.setColumnWidth(2, 20 * 256);
-            sheet.setColumnWidth(3, 20 * 256);
-            sheet.setColumnWidth(4, 60 * 256);
-
-            sheet.setColumnWidth(5, 15 * 256);
-            sheet.setColumnWidth(6, 15 * 256);
-            sheet.setColumnWidth(7, 100 * 256);
+            for (int i=0;i<titles.size(); i++) {
+                int width = 10;
+                if (i == 0) {
+                    width = 10;
+                } else if (i == 4 && company.getPackageProject() == 1){
+                    width = 60;
+                } else if (i == titles.size() -1){
+                    //最后一个是工作事项
+                    width = 100;
+                } else {
+                    width = 20;
+                }
+                sheet.setColumnWidth(i, width * 256);
+            }
+//
+//            sheet.setColumnWidth(1, 10 * 256);
+//            sheet.setColumnWidth(2, 20 * 256);
+//            sheet.setColumnWidth(3, 20 * 256);
+//            sheet.setColumnWidth(4, 60 * 256);
+//
+//            sheet.setColumnWidth(5, 15 * 256);
+//            sheet.setColumnWidth(6, 15 * 256);
+//            sheet.setColumnWidth(7, 100 * 256);
             //设置为居中加粗
             HSSFCellStyle headStyle = workbook.createCellStyle();
             HSSFFont font = workbook.createFont();
@@ -1494,39 +1612,39 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             headStyle.setFont(font);
             //表头
             HSSFCell headCell;
-            headCell = headRow.createCell(0);
-            headCell.setCellValue("序号");
-            headCell.setCellStyle(headStyle);
-            headCell = headRow.createCell(1);
-            headCell.setCellValue("上传者");
-            headCell.setCellStyle(headStyle);
-            headCell = headRow.createCell(2);
-            headCell.setCellValue("项目名称");
-            headCell.setCellStyle(headStyle);
-            headCell = headRow.createCell(3);
-            headCell.setCellValue("子项目名称");
-            headCell.setCellStyle(headStyle);
-            headCell = headRow.createCell(4);
-            headCell.setCellValue("关联任务");
-            headCell.setCellStyle(headStyle);
-            headCell = headRow.createCell(5);
-            headCell.setCellValue("工作日期");
-            headCell.setCellStyle(headStyle);
-            headCell = headRow.createCell(6);
-            headCell.setCellValue("工作时长(小时)");
-            headCell.setCellStyle(headStyle);
-            headCell = headRow.createCell(7);
-            headCell.setCellValue("工作内容");
-            headCell.setCellStyle(headStyle);
-            headCell = headRow.createCell(8);
-            headCell.setCellValue("加班");
-            headCell.setCellStyle(headStyle);
+
+            for (int i=0;i<titles.size(); i++) {
+                headCell = headRow.createCell(i);
+                headCell.setCellValue(titles.get(i));
+                headCell.setCellStyle(headStyle);
+            }
+
+//            headCell = headRow.createCell(1);
+//            headCell.setCellValue("上传者");
+//            headCell.setCellStyle(headStyle);
+//            headCell = headRow.createCell(2);
+//            headCell.setCellValue("项目名称");
+//            headCell.setCellStyle(headStyle);
+//            headCell = headRow.createCell(3);
+//            headCell.setCellValue("子项目名称");
+//            headCell.setCellStyle(headStyle);
+//            headCell = headRow.createCell(4);
+//            headCell.setCellValue("关联任务");
+//            headCell.setCellStyle(headStyle);
+//            headCell = headRow.createCell(5);
+//            headCell.setCellValue("工作日期");
+//            headCell.setCellStyle(headStyle);
+//            headCell = headRow.createCell(6);
+//            headCell.setCellValue("工作时长(小时)");
+//            headCell.setCellStyle(headStyle);
+//
+
             //设置日期格式
             HSSFCellStyle style = workbook.createCellStyle();
             style.setDataFormat(HSSFDataFormat.getBuiltinFormat("yy/mm/dd hh:mm"));
             //新增数据行 并且装填数据
             int rowNum = 1;
-            List<Map<String, Object>> allReportByDate = null;
+            List<HashMap<String, Object>> allReportByDate = null;
             if (user.getRole() == 0) {
                 //普通员工只能看自己的
                 allReportByDate = reportMapper.getAllReportByDate(startDate, null, user.getId(), endDate, projectId);
@@ -1534,6 +1652,31 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 //管理员看公司所有人的
                 allReportByDate = reportMapper.getAllReportByDate(startDate, user.getCompanyId(), null, endDate, projectId);
             }
+            if (timeType.getMultiWorktime() == 1) {
+                java.text.DecimalFormat df = new java.text.DecimalFormat("#0.00");
+                //重新处理一下数据,把工作时间和工作事项移出来
+                List<HashMap<String, Object>> dealDataList = new ArrayList<HashMap<String, Object>>();
+                for (HashMap<String, Object> map : allReportByDate) {
+                    if ((Integer)map.get("multiWorktime") == 1) {
+                        String data = (String)map.get("content");
+                        JSONArray array = JSONArray.parseArray(data);
+                        for (int i=0;i<array.size(); i++) {
+                            JSONObject obj = array.getJSONObject(i);
+                            WorktimeItem worktimeItem = JSONObject.toJavaObject(obj, WorktimeItem.class);
+                            HashMap<String, Object> newMap = new HashMap<>();
+                            newMap.putAll(map);
+                            newMap.put("startTime", worktimeItem.getStartTime());
+                            newMap.put("endTime", worktimeItem.getEndTime());
+                            newMap.put("content", worktimeItem.getContent());
+                            newMap.put("duration", df.format(worktimeItem.getTime()));
+                            dealDataList.add(newMap);
+                        }
+                    } else {
+                        dealDataList.add(map);
+                    }
+                }
+                allReportByDate = dealDataList;
+            }
 
             for (Map<String, Object> map : allReportByDate) {
                 HSSFRow row = sheet.createRow(rowNum);
@@ -1541,17 +1684,33 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 row.createCell(1).setCellValue((String) map.get("name"));
                 row.createCell(2).setCellValue((String) map.get("project"));
                 row.createCell(3).setCellValue((String) map.get("subProjectName"));
-                row.createCell(4).setCellValue((String) map.get("taskName"));
-
-                HSSFCell cell = row.createCell(5);
+                int index = 4;
+                if (company.getPackageProject() == 1) {
+                    row.createCell(4).setCellValue((String) map.get("taskName"));
+                    index++;
+                }
+                HSSFCell cell = row.createCell(index);
                 cell.setCellValue(new SimpleDateFormat("yyyy-MM-dd")
                         .format((java.sql.Date) map.get("createDate")));
                 cell.setCellStyle(style);
-                row.createCell(6).setCellValue(map.get("duration").toString());
-                row.createCell(7).setCellValue((String) map.get("content"));
-                int isOverTime = (Integer) map.get("isOvertime");
-                row.createCell(8).setCellValue(isOverTime==1?"加班":"-");
-
+                index++;
+                row.createCell(index).setCellValue(map.get("duration").toString());
+                index++;
+                if (timeType.getMultiWorktime() == 1) {
+                    if ((Integer)map.get("multiWorktime") == 1) {
+                        row.createCell(index).setCellValue(map.get("startTime").toString()+"-"+map.get("endTime").toString());
+                    } else {
+                        row.createCell(index).setCellValue("");
+                    }
+                    index++;
+                }
+                if (timeType.getMultiWorktime() == 0) {
+                    int isOverTime = (Integer) map.get("isOvertime");
+                    row.createCell(index).setCellValue(isOverTime==1?"加班":"-");
+                    index++;
+                }
+                row.createCell(index).setCellValue((String) map.get("content"));
+                index++;
                 rowNum++;
             }
             //生成Excel文件
@@ -1564,6 +1723,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             httpRespMsg.data = "/upload/" + fileUrlSuffix;
         } catch (NullPointerException e) {
             httpRespMsg.setError("验证失败或缺少数据");
+            e.printStackTrace();
             return httpRespMsg;
         } catch (IOException e) {
             httpRespMsg.setError("文件生成错误");

+ 66 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/TimingTask.java

@@ -1,5 +1,7 @@
 package com.management.platform.task;
 
+import java.time.LocalDate;
+import java.time.Period;
 import java.time.format.DateTimeFormatter;
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -17,6 +19,7 @@ import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
 import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.format.annotation.DateTimeFormat;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
@@ -37,6 +40,8 @@ public class TimingTask {
 
     @Value("${wx.template_report_fill}")
     public String TEMPLATE_REPORT_FILL;
+    @Value("${wx.template_project_deadline}")
+    public String TEMPLATE_PROJECT_DEADLINE;
     @Value("${wx.app_id}")
     public String appId;
     @Value("${wx.app_secret}")
@@ -60,13 +65,46 @@ public class TimingTask {
     private WxCorpInfoMapper wxCorpInfoMapper;
     @Value(value = "${upload.path}")
     private String path;
+    @Resource
+    private ProjectMapper projectMapper;
+
+    //检查项目到期,距离到期时间3天内的,每天提醒
+    @Scheduled(cron = "0 0 10 ? * *")
+    private void projectDeadlineAlert() {
+        LocalDate start = LocalDate.now();
+        start = start.plusDays(1);
+        LocalDate end = LocalDate.now();
+        end = end.plusDays(3);
+        //获取未来3天内超期的项目
+        List<Project> list = projectMapper.selectList(new QueryWrapper<Project>().eq("status", 1).between("plan_end_date", start, end));
+        System.out.println("即将超期项目=="+list.size());
+
+        for (Project p : list) {
+
+            Period period = Period.between(start, p.getPlanEndDate());
+            int days = period.getDays();
+            List<Map<String, Object>> userList = userMapper.getProjectPushUserList(p.getId());
+            userList.forEach(u->{
+                if (u.get("corpwxUserid") != null) {
+                    //TODO: 推送到企业微信
+//                    String corpUid = (String) u.get("corpwxUserid");
+//                    wxCorpInfoService.sendWXCorpMsg(cpList.get(0), corpUid, "请及时填写今日的工作报告哦");
+                } else {
+                    pushProjectNotify((String)u.get("wxOpenid"), p.getProjectName(), days, p.getPlanEndDate().format(DateTimeFormatter.ofPattern("")));
+                }
+            });
 
+        }
+    }
 
     //每分钟校验是否有需要提醒的填报
     @Scheduled(fixedRate = 60 * 1000)
     private void process() {
         LocalDateTime now = LocalDateTime.now();
         int day = now.getDayOfWeek().getValue();
+
+
+
         //周末休息
         if (day == 7||day==6) {
             return;
@@ -95,9 +133,36 @@ public class TimingTask {
             }
         });
     }
+    //推送项目即将逾期通知
+    public void pushProjectNotify(String wxOpenid, String projectName, int days, String endDate) {
+        //1,配置
+        WxMpInMemoryConfigStorage wxStorage = new WxMpInMemoryConfigStorage();
+        wxStorage.setAppId(appId);
+        wxStorage.setSecret(appSecret);
+        WxMpService wxMpService = new WxMpServiceImpl();
+        wxMpService.setWxMpConfigStorage(wxStorage);
 
+        //2,推送消息
+        WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
+                .toUser(wxOpenid)//要推送的用户openid
+                .templateId(TEMPLATE_PROJECT_DEADLINE)//模版id
+                .build();
+        //3,如果是正式版发送模版消息,这里需要配置你的信息
+        templateMessage.addData(new WxMpTemplateData("first", "项目还有"+days+"天到期", "#FF00FF"));
+        templateMessage.addData(new WxMpTemplateData("keyword1", projectName, "#000000"));
+        templateMessage.addData(new WxMpTemplateData("keyword2", endDate, "#000000"));
+        templateMessage.addData(new WxMpTemplateData("remark", "如有您参与的任务,请按时完成,谢谢", "#000000"));
+        //                templateMessage.addData(new WxMpTemplateData(name2, value2, color2));
+        try {
+            System.out.println("======推送消息===");
+            wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
+        } catch (Exception e) {
+            System.out.println("推送失败:" + e.getMessage());
+            e.printStackTrace();
+        }
+    }
 
-
+    //推送日报未填消息
     public void push(Map<String, Object> user) {
         //1,配置
         WxMpInMemoryConfigStorage wxStorage = new WxMpInMemoryConfigStorage();

+ 2 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/application.yml

@@ -92,6 +92,8 @@ wx:
   app_id: wx749c84daac654e1e
   app_secret: aacbd046ec1c790836f4f684c96fe585
   template_report_pass: dbMuR2v7lxXLwRaorIWQ4T6ilvn0vzqmDDkD_3ZsaXc
+  template_project_deadline: kY2Qzec64uOANNXA0yn-PV09ZnNjCeGXwWjTaVmQiLU
+  template_report_reject: TICiRkvCpF4NCbkPOjefXTpz7jXgpt0SZGkNjCMIt3M
 ##actuator健康检查配置
 management:
   security:

+ 17 - 7
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ReportMapper.xml

@@ -24,18 +24,24 @@
         <result column="department_audit_state" property="departmentAuditState" />
         <result column="stage" property="stage" />
         <result column="pic_str" property="picStr" />
+        <result column="multi_worktime" property="multiWorktime" />
+        <result column="reject_reason" property="rejectReason" />
+        <result column="reject_username" property="rejectUsername" />
+        <result column="reject_userid" property="rejectUserid" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id, creator_id, project_id, create_date, working_time, content, state, create_time, time_type, cost, start_time, end_time, report_time_type, sub_project_id, task_id, is_overtime, progress, department_audit_state, stage, pic_str
+        id, creator_id, project_id, create_date, working_time, content, state, create_time, time_type, cost, start_time, end_time, report_time_type, sub_project_id, task_id, is_overtime, progress, department_audit_state, stage, pic_str, multi_worktime, reject_reason, reject_username, reject_userid
     </sql>
+
     <!--根据日期获取全部报告信息-->
     <select id="getAllReportByDate" resultType="java.util.Map">
         SELECT c.name, b.project_name AS project, 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.report_time_type as reportTimeType, a.start_time as startTime,
         a.end_time as endTime, d.name as subProjectName,a.task_id as taskId, task.name as taskName, a.is_overtime as isOvertime,a.progress as progress,
-        a.department_audit_state as departmentAuditState, a.pic_str as picStr
+        a.department_audit_state as departmentAuditState, a.pic_str as picStr, multi_worktime as multiWorktime
+        , reject_reason as rejectReason, reject_username as rejectUsername, reject_userid as rejectUserid
         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
@@ -54,7 +60,7 @@
         <if test="userId != null">
             AND a.creator_id = #{userId}
         </if>
-        ORDER BY a.id desc
+        ORDER BY a.creator_id, a.create_date desc
     </select>
 
     <!--根据员工id,日期获取当天全部报告信息-->
@@ -62,7 +68,8 @@
         SELECT a.id, a.project_id as projectId,b.project_name AS project, a.working_time AS time, a.content, a.state, a.time_type as timeType, a.cost, a.report_time_type as reportTimeType, a.start_time as startTime,
         a.end_time as endTime, b.incharger_id as inchargerId,
         a.creator_id as creatorId, d.name as subProjectName,a.task_id as taskId, task.name as taskName, a.is_overtime as isOvertime,a.progress as progress,
-        a.department_audit_state as departmentAuditState, a.stage, a.pic_str as picStr
+        a.department_audit_state as departmentAuditState, a.stage, a.pic_str as picStr, multi_worktime as multiWorktime
+        , reject_reason as rejectReason, reject_username as rejectUsername, reject_userid as rejectUserid
         FROM report AS a
         JOIN project AS b ON a.project_id=b.id
         left join sub_project as d on d.id = a.sub_project_id
@@ -81,7 +88,8 @@
         a.cost, a.report_time_type as reportTimeType, a.start_time as startTime,
         a.end_time as endTime, b.incharger_id as inchargerId,
         a.creator_id as creatorId, d.name as subProjectName,a.task_id as taskId, task.name as taskName, a.is_overtime as isOvertime,a.progress as progress,
-        a.department_audit_state as departmentAuditState, a.stage, a.pic_str as picStr
+        a.department_audit_state as departmentAuditState, a.stage, a.pic_str as picStr, multi_worktime as multiWorktime
+        , reject_reason as rejectReason, reject_username as rejectUsername, reject_userid as rejectUserid
         FROM report AS a
         JOIN project AS b ON a.project_id=b.id
         left join sub_project as d on d.id = a.sub_project_id
@@ -103,7 +111,8 @@
         a.cost, a.report_time_type as reportTimeType, a.start_time as startTime,
         a.end_time as endTime, b.incharger_id as inchargerId,
         a.creator_id as creatorId, d.name as subProjectName,a.task_id as taskId, task.name as taskName,
-        a.is_overtime as isOvertime,a.progress as progress, a.stage, a.pic_str as picStr
+        a.is_overtime as isOvertime,a.progress as progress, a.stage, a.pic_str as picStr, multi_worktime as multiWorktime
+        , reject_reason as rejectReason, reject_username as rejectUsername, reject_userid as rejectUserid
         FROM report AS a
         JOIN project AS b ON a.project_id=b.id
         left join sub_project as d on d.id = a.sub_project_id
@@ -125,7 +134,8 @@
         a.end_time as endTime, d.name as subProjectName,a.task_id as taskId, task.name as taskName,
         b.incharger_id as inchargerId,
         a.is_overtime as isOvertime,a.progress as progress,
-        a.department_audit_state as departmentAuditState, a.stage, a.pic_str as picStr
+        a.department_audit_state as departmentAuditState, a.stage, a.pic_str as picStr, multi_worktime as multiWorktime
+        , reject_reason as rejectReason, reject_username as rejectUsername, reject_userid as rejectUserid
         FROM report AS a
         JOIN project AS b ON a.project_id=b.id
         left join sub_project as d on d.id = a.sub_project_id

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

@@ -13,11 +13,12 @@
         <result column="type" property="type" />
         <result column="pay_overtime" property="payOvertime" />
         <result column="alert_time" property="alertTime" />
+        <result column="multi_worktime" property="multiWorktime" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        company_id, allday, am, pm, month_days, hour_cost_input_type, type, pay_overtime, alert_time
+        company_id, allday, am, pm, month_days, hour_cost_input_type, type, pay_overtime, alert_time, multi_worktime
     </sql>
 
 </mapper>

+ 8 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/UserMapper.xml

@@ -89,4 +89,12 @@
         AND a.is_active = 1
         AND NOT EXISTS(SELECT 1 FROM report WHERE report.`creator_id` = a.id AND report.`create_date` = DATE_FORMAT(NOW(), '%Y-%m-%d'))
     </select>
+    <!--获取项目的参与人的推送id -->
+    <select id="getProjectPushUserList" resultType="java.util.Map">
+        SELECT a.id, a.wx_openid as wxOpenid, a.corpwx_userid as corpwxUserid
+        FROM user AS a
+        WHERE  a.wx_openid IS NOT NULL
+        AND a.is_active = 1
+        AND a.id in (select user_id from participation where project_id = #{projectId})
+    </select>
 </mapper>

+ 190 - 18
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

@@ -102,10 +102,10 @@
                             </span>
                             <div class="checkbtn">
                                 <el-button v-if="(user.role == 1 || user.role == 2 || user.id == item1.data[0].inchargerId) && item1.state == 0" type="primary" :loading="logining" size="small" @click="approve(item1.id, item1)">通过</el-button>
-                                <el-button v-if="(user.role == 1 || user.role == 2 || user.id == item1.data[0].inchargerId) && item1.state == 0" type="danger" :loading="logining" size="small" @click="deny(item1.id,0, item1)">驳回</el-button>
+                                <el-button v-if="(user.role == 1 || user.role == 2 || user.id == item1.data[0].inchargerId) && item1.state == 0" type="danger" :loading="logining" size="small" @click="showDenyDialog(item1.id,0, item1)">驳回</el-button>
                                 <!--自己可以撤回待审核状态的报告 -->
                                 <el-button v-if="(user.id == item1.id) && item1.state == 0" type="normal" :loading="logining" size="small" @click="cancel(item1)">撤回</el-button>
-                                <el-button v-if="(user.role == 1 || user.role == 2 || user.id == item1.data[0].inchargerId) && item1.state == 1" type="normal" :loading="logining" size="small" @click="deny(item1.id,1, item1)">撤销</el-button>
+                                <el-button v-if="(user.role == 1 || user.role == 2 || user.id == item1.data[0].inchargerId) && item1.state == 1" type="normal" :loading="logining" size="small" @click="showDenyDialog(item1.id,1, item1)">撤销</el-button>
                                 <el-button v-if="item1.state >= 2 && user.id == item1.id" type="primary" size="small" @click="isSubstitude=false; fillInReport(index1,0)">编辑日报</el-button>
                             </div>
                             <div class="one_daily_body">
@@ -117,7 +117,7 @@
                                             <span v-if="user.company.packageEngineering == 0">
                                             <span style="margin-left:15px;color:#DAA520;" v-if="item2.state == 0">[ 待审核 ]</span>
                                             <span style="margin-left:15px;color:#32CD32;" v-else-if="item2.state == 1">[ 已通过 ]</span>
-                                            <span style="margin-left:15px;color:#FF0000;" v-else-if="item2.state == 2">[ 已驳回 ]</span>
+                                            <span style="margin-left:15px;color:#FF0000;" v-else-if="item2.state == 2">[ 已驳回 ] 原因:{{item2.rejectReason}}</span>
                                             <span style="margin-left:15px;color:#FF0000;" v-else-if="item2.state == 3">[ 已撤回 ]</span>
                                             </span>
                                             <span v-if="user.company.packageEngineering == 1">
@@ -125,7 +125,7 @@
                                                 <span style="margin-left:15px;color:#DAA520;" v-if="item2.state == 0 && item2.departmentAuditState == 0">[ 待部门审核 ]</span>
                                                 <span style="margin-left:15px;color:#DAA520;" v-if="item2.state == 0 && item2.departmentAuditState == 1">[ 待项目经理审核 ]</span>
                                                 <span style="margin-left:15px;color:#32CD32;" v-else-if="item2.state == 1">[ 已通过 ]</span>
-                                                <span style="margin-left:15px;color:#FF0000;" v-else-if="item2.state == 2">[ 已驳回 ]</span>
+                                                <span style="margin-left:15px;color:#FF0000;" v-else-if="item2.state == 2">[ 已驳回 ] 原因:{{item2.rejectReason}}</span>
                                                 <span style="margin-left:15px;color:#FF0000;" v-else-if="item2.state == 3">[ 已撤回 ]</span>
                                             </span>
                                             </p>
@@ -145,6 +145,7 @@
                                             </p>
                                             <p v-if="item2.taskId != null">任务:{{item2.taskName}}
                                             </p>
+                                            <div v-if="item2.multiWorktime==0">
                                             <p style="display: inline-block;">时长:
                                                 <span v-if="item2.reportTimeType == 0" style="margin-right:10px;">{{typeList[item2.timeType]}}</span>
                                                 <span v-if="item2.reportTimeType == 2" style="margin-right:10px;">{{item2.startTime+'-'+item2.endTime}}</span>
@@ -153,9 +154,19 @@
                                             <!-- 阶段 -->
                                             <span v-if="item2.stage != null" style="margin-left:10px;"> 投入阶段:{{item2.stage}}</span>
                                             </p>
-                                            
                                             <p>事项:<span v-html="item2.content"></span></p>
-                                            
+                                            </div>
+                                            <div v-if="item2.multiWorktime==1" >
+                                                <div v-for="(timeItem, tIndex) in item2.worktimeList" :key="tIndex"
+                                                    style="border: 0.5px #ddd solid;margin-bottom:5px;padding:5px;">
+                                                    <p style="display: inline-block;">时长:
+                                                        <span v-if="item2.reportTimeType == 2" style="margin-right:10px;">{{timeItem.startTime+'-'+timeItem.endTime}}</span>
+                                                    {{timeItem.time.toFixed(2)}}h  
+                                                    <el-tag type="danger" size="mini" style="margin-left: 65px" v-if="item2.isOvertime === 1">加班</el-tag>
+                                                    </p>
+                                                    <p>事项:<span v-html="timeItem.content"></span></p>
+                                                </div>
+                                            </div>
                                             <!--照片的显示 -->
                                             <p v-if="item2.pics != null && item2.pics.length > 0"> 
                                                 <el-image v-for="(pic, index) in item2.pics" :key="index"
@@ -197,7 +208,8 @@
                 </el-form-item>
                 
                 <div v-for="(domain, index) in workForm.domains" :key="domain.id">
-                    <el-form-item v-if="reportTimeType.type != 3" label="工作时长" :prop="'domains.' + index + '.'+timeFields[reportTimeType.type]"
+                    <div v-if="reportTimeType.multiWorktime==0">
+                    <el-form-item v-if="reportTimeType.type != 3" :label="reportTimeType.type ==2?'工作时间':'工作时长'" :prop="'domains.' + index + '.'+timeFields[reportTimeType.type]"
                         :rules="{ required: true, message: '请选择工作时长', trigger: 'blur' }">
                         <el-select v-model="domain.timeType" style="width:200px;"
                             v-if="reportTimeType.type == 0"
@@ -279,7 +291,7 @@
                     </el-form-item>
 
                     <el-form-item v-if="reportTimeType.type == 3" label="用时占比" :prop="'domains.' + index + '.'+timeFields[reportTimeType.type]"
-                        :rules="{ required: true, message: '请选择工作时长', trigger: 'blur' }">
+                        :rules="{ required: true, message: '请设置用时占比', trigger: 'blur' }">
                         <div style="width:300px;">
                             <el-col span="14"><el-slider :disabled="!canEdit" v-model="domain.progress" :min="10" :show-tooltip="false" :step="10" style="width:180px;" @change="domain.workingTime = (reportTimeType.allday*domain.progress/100).toFixed(1)"></el-slider></el-col>
                             <el-col span="10"><span style="margin-left:10px;float:right;"><span style="margin-right:10px;">{{domain.progress}}%</span>{{domain.workingTime}}小时</span></el-col>
@@ -306,6 +318,96 @@
                         <el-input v-model="domain.content" type="textarea" :rows="4" placeholder="请填写工作事项" clearable
                          :disabled="workForm.domains.length==0?true:(workForm.domains[index].state>=2?false:true)"></el-input>
                     </el-form-item>
+                    </div>
+                    <!--多个工作事项填报的情况 -->
+                    <div v-if="reportTimeType.multiWorktime==1">
+                        <el-form-item label="投入项目" :prop="'domains.' + index + '.projectId'"
+                            :rules="{ required: true, message: '请选择投入项目', trigger: ['change','blur'] }">
+                            <el-select v-model="domain.projectId" placeholder="请选择" style="width:200px;" clearable="true"  filterable="true"
+                            @change="selectProject(domain, index)"
+                            :disabled="workForm.domains.length==0?true:(workForm.domains[index].state>=2?false:true)">
+                                <el-option v-for="item in projectList" :key="item.id" :label="item.projectName" :value="item.id"></el-option>
+                            </el-select>
+                            <!--子项目 -->
+                            <el-select v-model="domain.subProjectId" placeholder="请选择" style="width:200px;" clearable="true" v-if="domain.subProjectList != null && domain.subProjectList.length> 0"
+                            :disabled="workForm.domains.length==0?true:(workForm.domains[index].state>=2?false:true)">
+                                <el-option v-for="item in domain.subProjectList" :key="item.id" :label="item.name" :value="item.id"></el-option>
+                            </el-select>
+                            <!-- 项目的阶段 -->
+                            <span v-if="user.company.packageProject == 1 && domain.stages != null && domain.stages.length> 0" 
+                                style="margin-left:30px;">投入阶段</span>
+                            <el-select v-model="domain.stage" placeholder="请选择" style="width:200px;margin-left:10px;" clearable="true" v-if="user.company.packageProject == 1 && domain.stages != null && domain.stages.length> 0"
+                            :disabled="workForm.domains.length==0?true:(workForm.domains[index].state>=2?false:true)">
+                                <el-option v-for="item in domain.stages" :key="item" :label="item" :value="item"></el-option>
+                            </el-select>
+
+                            <el-link v-if="index >= 1" type="primary" :underline="false" @click="delDomain(index)" style="float:right;margin-right:10px;"
+                                :disabled="workForm.domains.length==0?true:(workForm.domains[index].state>=2?false:true)">
+                                <i class="fa fa-trash" style="color: red;;font-size:18px;"></i>
+                            </el-link>
+                            <el-link type="primary" v-if="workForm.domains[index].state == 0 || workForm.domains[index].state == 2"
+                                :underline="false" style="margin-left:5px;" @click="copyProject(index)">复制</el-link>
+                        </el-form-item>
+                        <!--工程专业版本模式下, 各个专业的进度填报 -->
+                        <el-form-item label="专业进度" :prop="'domains.' + index + '.professionProgress'" v-if="user.company.packageEngineering==1">
+                            <span v-for="item in domain.professionProgress" :key="item.professionId" style="margin-right:10px;">
+                                <span>{{item.professionName}}</span> / 进度:
+                                <el-input size="mini" style="width:60px;" v-model="item.progress"
+                                @keyup.native="onProgressChange" 
+                                :disabled="workForm.domains.length==0?true:(workForm.domains[index].state>=2?false:true)"></el-input>%
+                            </span>
+                        </el-form-item>
+                        <!--项目管理专业版模式下,项目下的近期执行的任务 -->
+                        <el-form-item label="相关任务" :prop="'domains.' + index + '.taskId'" v-if="user.company.packageProject==1" >
+                            <el-select v-model="domain.taskId" placeholder="请选择" style="width:100%;" filterable="true" 
+                            :disabled="workForm.domains.length==0?true:(workForm.domains[index].state>=2?false:true)">
+                                <el-option v-for="item in domain.taskList" :key="item.taskId" :label="item.taskName" :value="item.taskId"></el-option>
+                            </el-select>
+                        </el-form-item>
+                        <div v-for="(timeItem,tIndex) in domain.worktimeList" :key="tIndex" 
+                            style="border: 0.5px #ddd solid;padding:5px;margin-bottom:15px;position:relative;">
+                        <i v-if="tIndex>0&&workForm.domains[index].state>=2" @click="removeTimeItem(domain,tIndex)"  class="el-icon-delete" style="position:absolute;right:-7px;top:-7px;font-size:16px;"></i>
+                        <el-form-item label="工作时间" :prop="'domains.' + index + '.worktimeList.'+tIndex+'.'+timeFields[reportTimeType.type]"
+                            :rules="{ required: true, message: '请选择工作时间', trigger: 'blur' }">
+                            
+                            <span v-if="reportTimeType.type == 2">
+                            <!--时间范围选择 -->
+                            <el-time-picker 
+                            :disabled="workForm.domains.length==0?true:(workForm.domains[index].state>=2?false:true)"
+                                v-model="timeItem.startTime"
+                                placeholder="起始时间"
+                                style="width:120px;"
+                                format="HH:mm"
+                                value-format="HH:mm"
+                                :picker-options="{
+                                start: '00:00',
+                                end: '23:59'
+                                }">
+                            </el-time-picker> - <el-time-picker 
+                            :disabled="workForm.domains.length==0?true:(workForm.domains[index].state>=2?false:true)"
+                                v-model="timeItem.endTime"
+                                placeholder="结束时间"
+                                style="width:120px;"
+                                format="HH:mm"
+                                value-format="HH:mm"
+                                :picker-options="{
+                                start: '00:00',
+                                end: '23:59',
+                                minTime: timeItem.startTime
+                                }">
+                            </el-time-picker>
+                            </span>
+                        </el-form-item>
+                        <el-form-item label="工作事项" :prop="'domains.' + index + '.worktimeList.'+tIndex+'.content'" 
+                        :rules="{ required: true, message: '填写工作事项', trigger: 'blur' }">
+                            <el-input v-model="timeItem.content" type="textarea" :rows="2" placeholder="请填写工作事项" clearable
+                            :disabled="workForm.domains.length==0?true:(workForm.domains[index].state>=2?false:true)"
+                            ></el-input>
+                        </el-form-item>
+                        </div>
+                        <el-link size="small" @click="addNewWorktime(index, domain)" style="margin-left:15px;margin-top:5px;margin-bottom:5px;">添加工时</el-link>
+                    </div>
+
                     <!--照片的显示 -->
                     <p v-if="domain.pics != null && domain.pics.length > 0" style="text-align:center;"> 
                         <el-image v-for="(pic, index) in domain.pics" :key="index"
@@ -445,7 +547,16 @@
                 <!-- <el-button >导出</el-button> -->
             </div>
         </el-dialog>
-        
+        <!--驳回弹出框 -->
+        <el-dialog title="请输入原因"  v-if="denyReasonDialog" :visible.sync="denyReasonDialog" :close-on-click-modal="false" customClass="customWidth" width="500px">
+            <div>
+                <el-input type="textarea" v-model="denyForm.reason" rows="2" :placeholder="'请输入您决定'+(denyForm.i==0?'驳回':'撤销')+'的原因'" />
+            </div>
+            <div slot="footer" class="dialog-footer">
+                <el-button  @click="denyReasonDialog = false" >取消</el-button>
+                <el-button type="primary" @click="deny()" >确定</el-button>
+            </div>
+        </el-dialog>
     </section>
 </template>
 
@@ -455,6 +566,8 @@
     export default {
         data() {
             return {
+                denyForm:null,
+                denyReasonDialog:false,
                 monthWorkTimeDialog: false,
                 isSubstitude:false,
                 isFill:false,
@@ -506,6 +619,8 @@
                         timeType:0,
                         content: "",
                         state: 2,
+                        multiWorktime:0,
+                        worktimeList:[],
                     }],
                 },
                 workRules: {
@@ -551,6 +666,14 @@
             };
         },
         methods: {
+            removeTimeItem(item, index) {
+                item.worktimeList.splice(index, 1);
+            },
+            //添加工时
+            addNewWorktime(index, item) {
+                item.worktimeList.push({});
+            },
+
             //复制项目
             copyProject(index) {
                 var leftProgress = 10;
@@ -1568,6 +1691,8 @@
                                     stages:list.report[i].stages,
                                     stage:list.report[i].stage,
                                     pics: list.report[i].pics,
+                                    multiWorktime: list.report[i].multiWorktime,
+                                    worktimeList: list.report[i].worktimeList,
                                 })
                                 if (list.report[i].state >= 2) {
                                     this.canEdit = true;
@@ -1590,6 +1715,8 @@
                                     progress:100,
                                     state: 2,
                                     timeType:0,
+                                    multiWorktime: this.reportTimeType.multiWorktime,
+                                    worktimeList:[{}],
                                 }],
                                 userId:null,
                                 userNames:null,
@@ -1637,6 +1764,8 @@
                                     progress:100,
                                     state: 2,
                                     timeType:0,
+                                    multiWorktime: this.reportTimeType.multiWorktime,
+                                    worktimeList:[{}],
                                 }],
                             }
                     this.canEdit = true;
@@ -1666,6 +1795,8 @@
                         content: "",
                         progress:leftProgress,
                         state:2,//2-表示待提交
+                        multiWorktime: this.reportTimeType.multiWorktime,
+                        worktimeList:[{}],
                 });
                 
                 if (this.reportTimeType.type == 0) {
@@ -1813,11 +1944,47 @@
                             }
                             
                             
-                            if (this.workForm.domains[i].content == null || this.workForm.domains[i].content == '') {
-                                formData.append("content", '-');
+                            //处理多个时间事项
+                            formData.append("multiWorktime", this.workForm.domains[i].multiWorktime);
+                            if (this.reportTimeType.multiWorktime == 1) {
+                                //检查时间是否有重叠
+                                var workList = this.workForm.domains[i].worktimeList;
+                                for (var j=0;j<workList.length; j++) {
+                                    var curItem = workList[j];
+                                    //检查开始时间是否大于结束时间
+                                    if (curItem.startTime >= curItem.endTime) {
+                                        this.$message({
+                                            message: "时间段"+curItem.startTime+'-'+curItem.endTime+"有误:"+ 
+                                                "结束时间必须大于开始时间",
+                                            type: "error"
+                                        });
+                                        return;
+                                    }
+                                    for (var p = j+1;p<workList.length; p++) {
+                                        var jItem = workList[p];
+                                        if ((jItem.startTime>=curItem.startTime&&jItem.startTime < curItem.endTime)
+                                                || (jItem.endTime>curItem.startTime&&jItem.endTime <= curItem.endTime)) {
+                                            this.$message({
+                                                message: "时间段"+curItem.startTime+'-'+curItem.endTime+"与"+ 
+                                                    jItem.startTime+'-'+jItem.endTime+ "存在重叠,请修改。",
+                                                type: "error"
+                                            });
+                                            return;
+                                        }
+                                    }
+                                }
+                                let m = JSON.stringify(this.workForm.domains[i].worktimeList);
+                                m = m.replace(/,/g,"@");//replaceAll(',','@');企业微信不兼容replaceAll
+                                console.log(m);
+                                formData.append("content", m);
                             } else {
-                                formData.append("content", this.workForm.domains[i].content);
+                                if (this.workForm.domains[i].content == null || this.workForm.domains[i].content == '') {
+                                    formData.append("content", '-');
+                                } else {
+                                    formData.append("content", this.workForm.domains[i].content);
+                                }
                             }
+                            
                             if (this.isBatch == 0) {
                                 formData.append("createDate", this.workForm.createDate);
                             } else {
@@ -1969,26 +2136,31 @@
                     });
                 });
             },
-
-            // 未通过日报
-            deny(id,i, item) {
-                this.logining = true;
+            showDenyDialog(id,i, item) {
+                this.denyReasonDialog = true;
                 let day = (this.choseDay+1) > 9 ? "-" + (this.choseDay + 1) : "-0" + (this.choseDay + 1);
                 var ids = '';
                 var data = item.data;
                 data.forEach(element => {
                     ids +=(element.id+',');
                 });
-                this.http.post( this.port.report.deny, {id: id , date: this.date +day, reportIds: ids},
+                this.denyForm = {id: id ,i:i, date: this.date +day, reportIds: ids, reason:null};
+            },
+            // 未通过日报
+            deny() {
+                this.logining = true;
+                
+                this.http.post( this.port.report.deny, this.denyForm,
                 res => {
                     this.logining = false;
                     if (res.code == "ok") {
                         this.$message({
-                            message: i==0?"驳回成功":"撤销成功",
+                            message: this.denyForm.i==0?"驳回成功":"撤销成功",
                             type: "success"
                         });
                         this.getReportList();
                         this.getDepartment();
+                        this.denyReasonDialog = false;
                     } else {
                         this.$message({
                             message: res.msg,

+ 40 - 9
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/list.vue

@@ -51,6 +51,7 @@
                                         </span>
                                 </p>
                                 <p v-if="item.taskId != null">任务:{{item.taskName}}</p>
+                                <div v-if="item.multiWorktime==0">
                                 <p>时长:{{item.time}}h <span class="propsbtn" v-if="item.isOvertime === 1">
                                     <el-tag type="danger" size="mini" style="margin-left: 65px">加班</el-tag></span>
                                     <!-- 阶段 -->
@@ -58,6 +59,18 @@
                                 </p>
                                 <p v-if="user.role>=1&&user.role<=3">成本:{{item.cost}}元</p>
                                 <p>事项:<span v-html="item.content"></span></p>
+                                </div>
+                                <div v-if="item.multiWorktime==1" >
+                                    <div v-for="(timeItem, tIndex) in item.worktimeList" :key="tIndex"
+                                        style="border: 0.5px #ddd solid;margin:5px 0px;padding:5px; ">
+                                        <p style="line-height:20px;margin:5px 0px;">时长:
+                                            <span v-if="item.reportTimeType == 2" style="margin-right:10px;">{{timeItem.startTime+'-'+timeItem.endTime}}</span>
+                                        {{timeItem.time.toFixed(1)}}h  
+                                        </p>
+                                        <p style="line-height:20px;margin:5px 0px;">事项:<span v-html="timeItem.content"></span></p>
+                                    </div>
+                                </div>
+
                                 <!--照片的显示 -->
                                 <p v-if="item.pics != null && item.pics.length > 0"> 
                                     <el-image v-for="(pic, index) in item.pics" :key="index"
@@ -87,12 +100,21 @@
             <el-table-column label="操作" width="220">
                 <template slot-scope="scope">
                     <el-button v-if="scope.row.state == 0" type="primary" :loading="logining" size="small" @click="approve(scope.row.id,scope.row.date, scope.row)">通过</el-button>
-                    <el-button v-if="scope.row.state == 0" type="danger" :loading="logining" size="small" @click="deny(scope.row.id,0,scope.row.date, scope.row)">驳回</el-button>
-                    <el-button v-if="scope.row.state == 1" type="danger" :loading="logining" size="small" @click="deny(scope.row.id,1,scope.row.date, scope.row)">撤销</el-button>
+                    <el-button v-if="scope.row.state == 0" type="danger" :loading="logining" size="small" @click="showDenyDialog(scope.row.id,0,scope.row.dateStr, scope.row)">驳回</el-button>
+                    <el-button v-if="scope.row.state == 1" type="danger" :loading="logining" size="small" @click="showDenyDialog(scope.row.id,1,scope.row.dateStr, scope.row)">撤销</el-button>
                 </template>
             </el-table-column>
         </el-table>
-        
+        <!--驳回弹出框 -->
+        <el-dialog title="请输入原因"  v-if="denyReasonDialog" :visible.sync="denyReasonDialog" :close-on-click-modal="false" customClass="customWidth" width="500px">
+            <div>
+                <el-input type="textarea" v-model="denyForm.reason" rows="2" :placeholder="'请输入您决定'+(denyForm.i==0?'驳回':'撤销')+'的原因'" />
+            </div>
+            <div slot="footer" class="dialog-footer">
+                <el-button  @click="denyReasonDialog = false" >取消</el-button>
+                <el-button type="primary" @click="deny()" >确定</el-button>
+            </div>
+        </el-dialog>
         <!--工具条-->
         <!-- <el-col v-if="search.value != -1" :span="24" class="toolbar">
             <el-pagination
@@ -114,6 +136,8 @@
     export default {
         data() {
             return {
+                denyForm:null,
+                denyReasonDialog:false,
                 isAllSelect:false,
                 user: JSON.parse(sessionStorage.getItem("user")),
 
@@ -138,6 +162,7 @@
             };
         },
         methods: {
+            
             // 获取部门列表
             getDepartment() {
                 this.http.post( this.port.manage.depList, {},
@@ -321,24 +346,30 @@
                     });
                 });
             },
-            // 未通过日报
-            deny(id,i,date, item) {
-                this.logining = true;
-                var time = date;
+            showDenyDialog(id,i, date, item) {
+                this.denyReasonDialog = true;
                 var ids = '';
                 var data = item.data;
                 data.forEach(element => {
                     ids +=(element.id+',');
                 });
-                this.http.post( this.port.report.deny, {id: id , date: time, reportIds: ids},
+                this.denyForm = {id: id ,i:i, date: date, reportIds: ids, reason:null};
+            },
+
+            // 未通过日报
+            deny() {
+                this.logining = true;
+                
+                this.http.post( this.port.report.deny, this.denyForm,
                 res => {
                     this.logining = false;
                     if (res.code == "ok") {
                         this.$message({
-                            message: i==0?"驳回成功":"撤销成功",
+                            message: this.denyForm.i==0?"驳回成功":"撤销成功",
                             type: "success"
                         });
                         this.getList();
+                        this.denyReasonDialog = false;
                     } else {
                         this.$message({
                             message: res.msg,

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

@@ -79,8 +79,8 @@
             </el-table-column>
             <el-table-column label="操作" width="220">
                 <template slot-scope="scope">
-                    <el-button type="primary" :loading="logining" size="small" @click="approve(scope.row.id,scope.row.date, scope.row)">通过</el-button>
-                    <el-button type="danger" :loading="logining" size="small" @click="deny(scope.row.id,0,scope.row.date, scope.row)">驳回</el-button>
+                    <el-button type="primary" :loading="logining" size="small" @click="approve(scope.row.id,scope.row.dateStr, scope.row)">通过</el-button>
+                    <el-button type="danger" :loading="logining" size="small" @click="deny(scope.row.id,0,scope.row.dateStr, scope.row)">驳回</el-button>
                 </template>
             </el-table-column>
         </el-table>

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

@@ -85,8 +85,8 @@
             </el-table-column>
             <el-table-column label="操作" width="220">
                 <template slot-scope="scope">
-                    <el-button type="primary" :loading="logining" size="small" @click="approve(scope.row.id,scope.row.date, scope.row)">通过</el-button>
-                    <el-button type="danger" :loading="logining" size="small" @click="deny(scope.row.id,0,scope.row.date, scope.row)">驳回</el-button>
+                    <el-button type="primary" :loading="logining" size="small" @click="approve(scope.row.id,scope.row.dateStr, scope.row)">通过</el-button>
+                    <el-button type="danger" :loading="logining" size="small" @click="deny(scope.row.id,0,scope.row.dateStr, scope.row)">驳回</el-button>
                 </template>
             </el-table-column>
         </el-table>

+ 148 - 20
fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/index.vue

@@ -18,9 +18,9 @@
 
             <div class="form_domains" v-for="(item,index) in form.domains" :key="item.id">
                 <div style="float:right;margin-top:10px;margin-right:10px;">
-                <van-tag v-if="canEdit&&item.projectName.length>0" color="#fff"
+                <!-- <van-tag v-if="canEdit&&item.projectName.length>0" color="#fff"
                 @click="copyProject(index)" style="border: 1px solid #20a0ff;padding:5px;"
-                 icon="plus" type="default" ><span style="color:#666;padding: 0 5px;">复制项目</span></van-tag>
+                 icon="plus" type="default" ><span style="color:#666;padding: 0 5px;">复制项目</span></van-tag> -->
                 
                 <van-tag v-if="index>0&&canEdit" color="#fff" 
                 @click="delPro(index)" style="border: 1px solid #ff0000;padding:5px;margin-left:10px;"
@@ -53,8 +53,11 @@
                     <!-- <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" /> -->
+                    
+                    <!-- 常规选择时间的方式 -->
                     <!-- 全天上下午模式 -->
-                    <van-field v-if="reportTimeType.type < 2" readonly clickable  :value="item.label" label="工作时长" placeholder="请选择工作时长(小时)" @click="clickTimePicker(index)"
+                    <div v-if="reportTimeType.multiWorktime==0">
+                    <van-field v-if="reportTimeType.type < 2" readonly clickable  :value="reportTimeType.type==0?item.label:(parseFloat(item.workingTime).toFixed(1)+'h')" label="工作时长" placeholder="请选择工作时长(小时)" @click="clickTimePicker(index)"
                     :rules="[{ required: true, message: '请选择工作时长' }]"/>
                     <van-popup v-model="showPickerTime" position="bottom">
                         <van-picker show-toolbar :columns="timeType"  value-key="label" @confirm="choseTimePick" @cancel="showPickerTime = false" />
@@ -76,7 +79,7 @@
                         type="time"
                         @confirm="confirmTime(item,0);"
                         @cancel="showStartTime = false"
-                        :min-hour="8"
+                        :min-hour="0"
                         :max-hour="23"
                         />
                         <!-- :filter="filter" 原本这个属性在里面 -->
@@ -87,7 +90,7 @@
                         <van-datetime-picker
                         v-model="endTime"
                         type="time"
-                        :min-hour="8"
+                        :min-hour="0"
                         :max-hour="23"
                         @confirm="confirmTime(item,1)"
                         @cancel="showEndTime = false" 
@@ -110,14 +113,64 @@
                     <van-field class="form_input" :disabled = "!canEdit"
                     v-model="item.content" name="content" type="textarea" label="工作事项" placeholder="请输入工作事项" 
                     rows="3" autosize  />
+                    </div>
                     
-                    <!-- 单选按钮 -->
-                    <!-- <van-radio-group v-model="isOvertime" direction="horizontal" class="overtime">
-                        <van-radio name="0">不加班</van-radio>
-                        <van-radio name="1">加班</van-radio>
-                    </van-radio-group> -->
-                    <div class="overtime">
-                        <van-checkbox :disabled="!canEdit" v-model="item.isOvertime">加班</van-checkbox>
+                    <!-- 多个时间和工作事项的选择方式 -->
+                    <div v-if="reportTimeType.multiWorktime==1">
+                        <div v-for="(timeItem, tindex) in item.worktimeList" :key="tindex" style="position:relative;border:#ccc 0.5px solid;margin:7px;">
+                            <van-tag v-if="tindex>0 && canEdit" style="position:absolute; right:-7px;top:-7px;z-index:10;" 
+                                @click="removeTimeItem(item,tindex)">X</van-tag>
+                            
+                            <!-- 时间段选择模式 -->
+                            <van-field readonly v-if="reportTimeType.type == 2" :clickable="canEdit" name="datetimePicker" 
+                            :value="timeItem.startTime" label="开始时间" placeholder="点击选择时间" 
+                            :rules="[{ required: true, message: '必填项' }]"
+                                @click="canEdit?showStartDialog(timeItem):''"  />
+                            
+                            <van-field v-if="reportTimeType.type == 2" readonly :clickable="canEdit" name="datetimePicker" 
+                            :value="timeItem.endTime" label="结束时间" placeholder="点击选择时间" 
+                            :rules="[{ required: true, message: '必填项' }]"
+                                @click="canEdit?showEndDialog(timeItem):''"  />
+                            
+                            <van-field class="form_input" :disabled="!canEdit" style="color:#333;-webkit-text-fill-color:#646566;"
+                            
+                                v-model="timeItem.content" name="content" type="textarea" label="工作事项" placeholder="请输入工作事项" 
+                                :rules="[{ required: true, message: '必填项' }]"
+                                rows="1" autosize  />
+                        </div>
+                        <!--时间选择器 , 做统一处理,不能放到循环里,不然会有多个公用showStartTime,最后一个会现在最上层UI,导致BUG -->
+                        <van-popup v-model="showWorkStartTime" position="bottom">
+                                <van-datetime-picker
+                                v-model="startTime"
+                                type="time"
+                                @confirm="confirmWorkTime(0);"
+                                @cancel="showWorkStartTime = false"
+                                :min-hour="0"
+                                :max-hour="23"
+                                />
+                                <!-- :filter="filter" 原本这个属性在里面 -->
+                            </van-popup>
+                        <van-popup v-model="showWorkEndTime" position="bottom" >
+                                <van-datetime-picker
+                                v-model="endTime"
+                                type="time"
+                                :min-hour="0"
+                                :max-hour="23"
+                                @confirm="confirmWorkTime(1)"
+                                @cancel="showWorkEndTime = false" 
+                                />
+                                <!-- :filter="filter" 原本这个属性在里面 -->
+                            </van-popup>
+                    </div>
+                    
+                    <div style="width:100%;" v-if="canEdit&&reportTimeType.multiWorktime==1">
+                    <van-tag  style="text-align:center;padding:5px;margin-left:15px;border: 1px solid #20a0ff;"
+                        :disabled="!canEdit" @click="addNewWorktime(index, item)" 
+                        icon="plus" color="#ffffff" ><span style="color:#999;text-align:center;padding: 0 5px;"> 添加工时  </span></van-tag>
+                    </div>
+                    
+                    <div class="overtime" >
+                        <van-checkbox :disabled="!canEdit" v-model="item.isOvertime" v-if="reportTimeType.multiWorktime !=1">加班</van-checkbox>
                         <van-tag style="position:absolute;right:10px;" v-if="isCorpWX&&canEdit" type="primary" size="large" @click="takePhoto(index)">拍照上传</van-tag>
                     </div>
                     <div style="padding:5px;text-align:center;" v-if="!isIOSystem">
@@ -167,6 +220,9 @@
     export default {
         data() {
             return {
+                showWorkStartTime:false,
+                showWorkEndTime:false,
+                curWorktime:null,
                 isIOSystem:false,
                 imgShow: false,
                 isCorpWX:false,
@@ -204,6 +260,8 @@
                         workingTime: "",
                         content: "",
                         state: 2,
+                        multiWorktime:0,
+                        worktimeList:{}
                         // pics:["https://worktime.ttkuaiban.com/upload/bc4df504fa724e6cab69872e2c1cfb35.png",
                         // "https://worktime.ttkuaiban.com/upload/bc4df504fa724e6cab69872e2c1cfb35.png",
                         // "https://worktime.ttkuaiban.com/upload/bc4df504fa724e6cab69872e2c1cfb35.png",]
@@ -227,6 +285,17 @@
         },
 
         methods: {
+            showEndDialog(timeItem) {
+                this.curWorktime = timeItem;
+                this.showWorkEndTime = true;
+            },
+            showStartDialog(timeItem) {
+                this.curWorktime = timeItem;
+                this.showWorkStartTime = true;
+            },
+            removeTimeItem(item, index) {
+                item.worktimeList.splice(index, 1);
+            },
             isIOS(){
                 var u = navigator.userAgent;
                 var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
@@ -368,6 +437,16 @@
                 }).catch(err=> {toast.clear();});
             },
 
+            confirmWorkTime(field) {
+                if (field == 0) {
+                    this.curWorktime.startTime = this.startTime;
+                    this.showWorkStartTime = false;
+                } else {
+                    this.curWorktime.endTime = this.endTime;
+                    this.showWorkEndTime = false;
+                }
+            },
+
             confirmTime(item, field) {
                 if (field == 0) {
                     item.startTime = this.startTime;
@@ -528,6 +607,7 @@
                                         tname = filterList[0].taskName;
                                     }
                                 } 
+                                
                                 array.push({
                                     id: list[i].id,
                                     projectId: list[i].projectId,
@@ -547,6 +627,8 @@
                                     professionProgress:list[i].professionProgressList,
                                     pics: list[i].pics,
                                     iospics:list[i].pics,
+                                    multiWorktime:t.multiWorktime,
+                                    worktimeList:list[i].worktimeList
                                 })
                                 if (list[i].state >= 2) {
                                     this.canEdit = true;
@@ -563,12 +645,14 @@
                                 id: null,
                                 projectId: "",
                                 projectName: "",
-                                workingTime: t.type==3?(t.allday).toFixed(1):"",
+                                workingTime: t.type==3?(t.allday).toFixed(1):"8.0",
                                 content: "",
                                 state: 2,
                                 progress:100,
                                 isOvertime:false,
                                 professionProgress:[],
+                                multiWorktime:t.multiWorktime,
+                                worktimeList:[{}]
                             }]
                             this.canEdit = true;
                         }
@@ -646,6 +730,10 @@
                         }).catch(err=> {toast.clear();});
                 }
             },
+            //添加工时
+            addNewWorktime(index, item) {
+                item.worktimeList.push({});
+            },
 
             // 添加项目 
             addNewPro() {
@@ -666,11 +754,13 @@
                     id: null,
                     projectId: "",
                     projectName: "",
-                    workingTime: this.reportTimeType.type==3?(leftProgress*this.reportTimeType.allday/100).toFixed(1):"",
+                    workingTime: this.reportTimeType.type==3?(leftProgress*this.reportTimeType.allday/100).toFixed(1):"4.0",
                     progress:leftProgress,
                     content: "",
                     state: 2,
                     isOvertime:false,
+                    multiWorktime: this.reportTimeType.multiWorktime,
+                    worktimeList:[{}]
                 })
             },
 
@@ -749,6 +839,7 @@
                         formData.append("subProjectId", 0);
                     }
                     formData.append("reportTimeType", this.reportTimeType.type);
+                    formData.append("multiWorktime", this.reportTimeType.multiWorktime);
                     if (this.reportTimeType.type == 0) {
                         formData.append("timeType", this.form.domains[i].timeType);
                         formData.append("workingTime", parseFloat(this.form.domains[i].workingTime));
@@ -762,11 +853,7 @@
                         formData.append("workingTime",this.form.domains[i].workingTime);
                     }
                     
-                    if (this.form.domains[i].content == null || this.form.domains[i].content == '') {
-                        formData.append("content", '-');
-                    } else {
-                        formData.append("content", this.form.domains[i].content);
-                    }
+                    
                     if (this.form.domains[i].taskId == null) {
                         formData.append("taskId", 0);
                     } else {
@@ -791,6 +878,44 @@
                     } else {
                         formData.append("pics", "@");
                     }
+                    //处理多个时间事项
+                    if (this.reportTimeType.multiWorktime == 1) {
+                        //检查时间是否有重叠
+                        var workList = this.form.domains[i].worktimeList;
+                        for (var j=0;j<workList.length; j++) {
+                            var curItem = workList[j];
+                            if (curItem.startTime == null || curItem.endTime == null) {
+                                this.$toast.fail("请设置工作时间");
+                                return;
+                            }
+                            //检查开始时间是否大于结束时间
+                            if (curItem.startTime >= curItem.endTime) {
+                                this.$toast.fail("时间段"+curItem.startTime+'-'+curItem.endTime+"有误:"+ 
+                                        "结束时间必须大于开始时间");
+                                return;
+                            }
+                            for (var p = j+1;p<workList.length; p++) {
+                                var jItem = workList[p];
+                                if ((jItem.startTime>=curItem.startTime&&jItem.startTime < curItem.endTime)
+                                        || (jItem.endTime>curItem.startTime&&jItem.endTime <= curItem.endTime)) {
+                                    
+                                    this.$toast.fail("时间段"+curItem.startTime+'-'+curItem.endTime+"与"+ 
+                                            jItem.startTime+'-'+jItem.endTime+ "存在重叠,请修改。");
+                                    return;
+                                }
+                            }
+                        }
+
+                        let m = JSON.stringify(this.form.domains[i].worktimeList);
+                        m = m.replace(/,/g,"@");//replaceAll(',','@');企业微信不兼容replaceAll
+                        formData.append("content", m);
+                    } else {
+                        if (this.form.domains[i].content == null || this.form.domains[i].content == '') {
+                            formData.append("content", '-');
+                        } else {
+                            formData.append("content", this.form.domains[i].content);
+                        }
+                    }
                 }
                 this.$axios.post("/report/editReport", formData)
                 .then(res => {
@@ -872,8 +997,11 @@
   line-height: 150px;
   text-align: center;
   background-color: #39a9ed;
-}
 
+}
+    .van-field__control:disabled {
+        -webkit-text-fill-color:#646566;
+    }
     .login_form {
         margin-top: 46px;
     }

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

@@ -39,7 +39,7 @@
                 ],
                 routers: [
                     {
-                        name: '我的日报',
+                        name: '查看日报',
                         url: '/calendar',
                         icon: 'description'
                     },
@@ -121,7 +121,7 @@
                         icon: 'todo-list-o'
                     });
                 }
-                if (this.user.role == 5) {//项目管理员
+                if (this.user.role == 3||this.user.role == 5||this.user.role == 6) {//项目管理员,公司领导,公司高层
                     this.routers.push({
                         name: '项目管理',
                         url: '/project',

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

@@ -4,9 +4,12 @@
         
         <div class="login_form">
             <van-list v-model="loading" :finished="finished" finished-text="没有更多了" :error.sync="error" error-text="请求失败,点击重新加载" @load="getMessage">
-                <van-cell @click="readMsg(index)" v-for="(item,index) in list" :key="index" :title="msgType[item.type]" :label="item.time.replace('T', ' ')" >
+                <van-cell @click="readMsg(index)" v-for="(item,index) in list" :key="index" 
+                :title="item.msg==null?msgType[item.type]:item.msg" :label="item.time.replace('T', ' ')" >
+                <div >
                     <span v-if="item.checked == 1" style="color:green">已读</span>
                     <span v-if="item.checked == 0" style="color:red">未读</span>
+                </div>
                 </van-cell>
             </van-list>
         </div>
@@ -100,6 +103,9 @@
 </script>
 
 <style lang="less" scoped>
+    .van-cell__title {
+      min-width: 90%;
+    }
     .login_form {
         margin-top: 46px;
     }

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

@@ -1,13 +1,13 @@
 <template>
     <div>
-        <van-nav-bar title="项目管理" left-text="返回" @click-left="back" right-text="新增项目" @click-right="openDialog(-1)" fixed left-arrow/>
+        <van-nav-bar title="项目管理" left-text="返回" @click-left="back" :right-text="canEdit?新增项目:''" @click-right="openDialog(-1)" fixed left-arrow/>
         
         <div class="login_form">
             <van-pull-refresh v-model="refreshing" @refresh="onRefresh">
                 <van-list v-model="loading" :finished="finished" finished-text="没有更多了" :error.sync="error" error-text="请求失败,点击重新加载" @load="getProject">
                     <van-swipe-cell v-for="(item,index) in list" :key="index">
                         <van-cell :border="false" :title="item.projectName" :value="item.projectCode"/>
-                        <template slot="right">
+                        <template slot="right" v-if="canEdit">
                             <van-button square type="info" text="编辑" @click="openDialog(index)"/>
                             <van-button square type="danger" text="删除" @click="delPro(index)"/>
                         </template>
@@ -43,6 +43,7 @@
     export default {
         data() {
             return {
+                canEdit:false,
                 user: JSON.parse(localStorage.userInfo),
                 showPickerUser: false,
                 showPickerIncharger: false,
@@ -283,6 +284,7 @@
 
         mounted() {
             this.getUsers();
+            this.canEdit = this.user.role==1||this.user.role==2||this.user.role==5
         }
     };
 </script>

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

@@ -22,8 +22,25 @@
                         </div>
                         
                         <div class="project_title" v-if="item1.taskId != null" >任务:{{item1.taskName}}</div>
-                        <div class="project_time">时长:{{item1.time}}h <span class="one_span" v-if="item1.isOvertime === 1">加班</span></div>
-                        <div class="project_content">事项:<span v-html="item1.content"></span></div>
+                        <!--根据类型选择使用的模板 -->
+                        <div v-if="item1.multiWorktime == 0">
+                            <div class="project_time">时长:
+                                <span v-if="item1.reportTimeType == 0" style="margin-right:10px;">{{fullDayTxt[item1.timeType]}}</span>
+                                <span v-if="item1.reportTimeType == 2" style="margin-right:10px;">{{item1.startTime+'-'+item1.endTime}}</span>{{item1.time}}h
+                                <div class="button" v-if="item1.isOvertime == 1">加班</div>
+                            </div>
+                            <div class="project_content">事项:<span v-html="item1.content"></span></div>
+                            
+                        </div>
+                        <div v-if="item1.multiWorktime == 1">
+                            <div style="position:relative;border:#ccc 0.5px solid;padding:3px;margin:5px 0px;" v-for="(timeItem, index) in item1.worktimeList" :key="index" >
+                                <div class="project_time">时长:
+                                    <!-- <span v-if="timeItem.reportTimeType == 0" style="margin-right:10px;">{{fullDayTxt[item1.timeType]}}</span> -->
+                                    <span style="margin-right:10px;">{{timeItem.startTime+'-'+timeItem.endTime}}</span>{{timeItem.time}}h
+                                </div>
+                                <div 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">
                             <span v-for="(p, index) in item1.pics"  :key="p" style="margin-right:10px;">
                             <img  :src="p" style="width:80px; height:80px;" @click="showLargeImg(item1.pics, index)"/>

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

@@ -28,8 +28,25 @@
                         </div>
                         
                         <div class="project_title" v-if="item1.taskId != null" >任务:{{item1.taskName}}</div>
-                        <div class="project_time">时长:{{item1.time}}h <span class="one_span" v-if="item1.isOvertime === 1">加班</span></div>
-                        <div class="project_content">事项:<span v-html="item1.content"></span></div>
+                        <!--根据类型选择使用的模板 -->
+                        <div v-if="item1.multiWorktime == 0">
+                            <div class="project_time">时长:
+                                <span v-if="item1.reportTimeType == 0" style="margin-right:10px;">{{fullDayTxt[item1.timeType]}}</span>
+                                <span v-if="item1.reportTimeType == 2" style="margin-right:10px;">{{item1.startTime+'-'+item1.endTime}}</span>{{item1.time}}h
+                                <div class="button" v-if="item1.isOvertime == 1">加班</div>
+                            </div>
+                            <div class="project_content">事项:<span v-html="item1.content"></span></div>
+                            
+                        </div>
+                        <div v-if="item1.multiWorktime == 1">
+                            <div style="position:relative;border:#ccc 0.5px solid;padding:3px;margin:5px 0px;" v-for="(timeItem, index) in item1.worktimeList" :key="index" >
+                                <div class="project_time">时长:
+                                    <!-- <span v-if="timeItem.reportTimeType == 0" style="margin-right:10px;">{{fullDayTxt[item1.timeType]}}</span> -->
+                                    <span style="margin-right:10px;">{{timeItem.startTime+'-'+timeItem.endTime}}</span>{{timeItem.time}}h
+                                </div>
+                                <div 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">
                             <span v-for="(p, index) in item1.pics"  :key="p" style="margin-right:10px;">
                             <img  :src="p" style="width:80px; height:80px;" @click="showLargeImg(item1.pics, index)"/>
@@ -38,8 +55,8 @@
                     </div>
                     <div class="form_btn" slot="footer">
                         <van-button v-if="(user.role != 0 || user.id == item.data[0].inchargerId) && item.state == 0" size="small" type="info" @click="approve(item.id, item)">通过</van-button>
-                        <van-button v-if="(user.role != 0 || user.id == item.data[0].inchargerId) && item.state == 0" size="small" type="danger" @click="deny(item.id,1, item)">驳回</van-button>
-                        <van-button v-if="(user.role != 0 || user.id == item.data[0].inchargerId) && item.state == 1" size="small" type="danger" @click="deny(item.id,2, item)">撤销</van-button>
+                        <van-button v-if="(user.role != 0 || user.id == item.data[0].inchargerId) && item.state == 0" size="small" type="danger" @click="showDenyDialog(item.id,0,item.dateStr, item)">驳回</van-button>
+                        <van-button v-if="(user.role != 0 || user.id == item.data[0].inchargerId) && item.state == 1" size="small" type="danger" @click="showDenyDialog(item.id,1,item.dateStr, item)">撤销</van-button>
                     </div>
                     <van-popup v-model="imgShow" position="bottom" closeable >
                         <van-swipe class="my-swipe"  indicator-color="white">
@@ -50,6 +67,13 @@
                     </van-popup>
                 </van-panel>
             </van-skeleton>
+            <van-popup v-model="denyReasonDialog" position="bottom" closeable >
+                <van-cell>请输入原因</van-cell>
+                <van-field class="form_input"
+                    v-model="denyForm.reason" name="reason" type="textarea" :placeholder="'请输入您决定'+(denyForm.i==0?'驳回':'撤销')+'的原因'"
+                    rows="3" autosize  />
+                <van-button style="width:100%;" type="info" @click="deny()">提交</van-button>
+            </van-popup>
         </div>
     </div>
 </template>
@@ -58,6 +82,8 @@
     export default {
         data() {
             return {
+                denyForm:{reason:null},
+                denyReasonDialog:false,
                 tmpPics:[],
                 imgShow: false,
                 user: JSON.parse(localStorage.userInfo),
@@ -155,11 +181,8 @@
                 }).catch(err=> {toast.clear();});
             },
 
-            deny(id,i, item) {
-                const toast = this.$toast.loading({
-                    forbidClick: true,
-                    duration: 0
-                });
+            showDenyDialog(id,i,date, item) {
+                this.denyReasonDialog = true;
                 var ids = '';
                 var data = item.data;
                 data.forEach(element => {
@@ -167,15 +190,25 @@
                         ids +=(element.id+',');
                     }
                 });
-                this.$axios.post("/report/deny", {id: id , date: this.nowTime, reportIds: ids})
+                this.denyForm = {id: id ,i:i, date: date, reportIds: ids, reason:null};
+            },
+
+            deny() {
+                const toast = this.$toast.loading({
+                    forbidClick: true,
+                    duration: 0
+                });
+                
+                this.$axios.post("/report/deny", this.denyForm)
                 .then(res => {
                     if(res.code == "ok") {
                         toast.clear();
-                        this.$toast.success(i==0?'驳回成功':'撤销成功');
+                        this.$toast.success(this.denyForm.i==0?'驳回成功':'撤销成功');
                         this.getReport();
+                        this.denyReasonDialog = false;
                     } else {
                         toast.clear();
-                        this.$toast.fail(i==0?'驳回失败':'撤销失败');
+                        this.$toast.fail(this.denyForm.i==0?'驳回失败':'撤销失败');
                     }
                 }).catch(err=> {toast.clear();});
             }

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

@@ -23,8 +23,25 @@
                         </div>
                         
                         <div class="project_title" v-if="item1.taskId != null" >任务:{{item1.taskName}}</div>
-                        <div class="project_time">时长:{{item1.time}}h <span class="one_span" v-if="item1.isOvertime === 1">加班</span></div>
-                        <div class="project_content">事项:<span v-html="item1.content"></span></div>
+                        <!--根据类型选择使用的模板 -->
+                        <div v-if="item1.multiWorktime == 0">
+                            <div class="project_time">时长:
+                                <span v-if="item1.reportTimeType == 0" style="margin-right:10px;">{{fullDayTxt[item1.timeType]}}</span>
+                                <span v-if="item1.reportTimeType == 2" style="margin-right:10px;">{{item1.startTime+'-'+item1.endTime}}</span>{{item1.time}}h
+                                <div class="button" v-if="item1.isOvertime == 1">加班</div>
+                            </div>
+                            <div class="project_content">事项:<span v-html="item1.content"></span></div>
+                            
+                        </div>
+                        <div v-if="item1.multiWorktime == 1">
+                            <div style="position:relative;border:#ccc 0.5px solid;padding:3px;margin:5px 0px;" v-for="(timeItem, index) in item1.worktimeList" :key="index" >
+                                <div class="project_time">时长:
+                                    <!-- <span v-if="timeItem.reportTimeType == 0" style="margin-right:10px;">{{fullDayTxt[item1.timeType]}}</span> -->
+                                    <span style="margin-right:10px;">{{timeItem.startTime+'-'+timeItem.endTime}}</span>{{timeItem.time}}h
+                                </div>
+                                <div 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">
                             <span v-for="(p, index) in item1.pics"  :key="p" style="margin-right:10px;">
                             <img  :src="p" style="width:80px; height:80px;" @click="showLargeImg(item1.pics, index)"/>

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

@@ -16,12 +16,13 @@
                             <!-- <i v-if="parseFloat(item.reportTime)>parseFloat(item.calculateTime)+0.5" 
                                 style="color:red;margin-right:8px;" class="fa fa-exclamation-triangle"></i> -->
                             总填报:
-                            <span :style="parseFloat(item.reportTime)>parseFloat(item.calculateTime)+0.5?'color:red':''">{{item.reportTime}}h</span>
+                            <span>{{parseFloat(item.reportTime).toFixed(1)}}h</span>
                         </span>
                         <!-- <span>系统智能统计:{{item.calculateTime}}h</span> -->
                     </div>
                     <div v-for="(item1,index1) in item.data" class="one_report_data" :key="index1">
-                        <div class="project_title">项目:{{item1.project}}</div>
+                        <div class="project_title">项目:{{item1.project}} <span :style="'color:'+statusColor[item1.state]">[{{statusTxt[item1.state]}}] </span></div>
+                        <div style="color:red;" v-if="item1.state ==2&&item1.rejectReason!=null">原因:{{item1.rejectReason}}</div>
                         <div class="project_title" v-if="user.company.packageEngineering == 1">
                             专业进度:
                             <span style="margin-right:10px;" v-for="progressItem in item1.professionProgress" :key="progressItem.id">{{progressItem.professionName}}({{progressItem.progress}}%) 
@@ -29,12 +30,25 @@
                         </div>
                         
                         <div class="project_title" v-if="item1.taskId != null" >任务:{{item1.taskName}}</div>
-                        <div class="project_time">时长:
-                            <span v-if="item1.reportTimeType == 0" style="margin-right:10px;">{{fullDayTxt[item1.timeType]}}</span>
-                            <span v-if="item1.reportTimeType == 2" style="margin-right:10px;">{{item1.startTime+'-'+item1.endTime}}</span>{{item1.time}}h
-                            <div class="button" v-if="item1.isOvertime == 1">加班</div>
+                        <!--根据类型选择使用的模板 -->
+                        <div v-if="item1.multiWorktime == 0">
+                            <div class="project_time">时长:
+                                <span v-if="item1.reportTimeType == 0" style="margin-right:10px;">{{fullDayTxt[item1.timeType]}}</span>
+                                <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">加班</div>
+                            </div>
+                            <div class="project_content">事项:<span v-html="item1.content"></span></div>
+                            
+                        </div>
+                        <div v-if="item1.multiWorktime == 1">
+                            <div style="position:relative;border:#ccc 0.5px solid;padding:3px;margin:5px 0px;" v-for="(timeItem, index) in item1.worktimeList" :key="index" >
+                                <div class="project_time">时长:
+                                    <!-- <span v-if="timeItem.reportTimeType == 0" style="margin-right:10px;">{{fullDayTxt[item1.timeType]}}</span> -->
+                                    <span style="margin-right:10px;">{{timeItem.startTime+'-'+timeItem.endTime}}</span>{{timeItem.time.toFixed(1)}}h
+                                </div>
+                                <div class="project_content">事项:<span v-html="timeItem.content"></span></div>
+                            </div>
                         </div>
-                        <div class="project_content">事项:<span v-html="item1.content"></span></div>
                         <div style="padding:5px;text-align:center;" v-if="item1.pics != null && item1.pics.length > 0">
                             <span v-for="(p, index) in item1.pics"  :key="p" style="margin-right:10px;">
                             <img  :src="p" style="width:80px; height:80px;" @click="showLargeImg(item1.pics, index)"/>
@@ -63,6 +77,7 @@
                 imgShow: false,
                 hasWaiting: false,
                 state: 0,
+                statusColor:['orange','green','red'],
                 user: JSON.parse(localStorage.userInfo),
                 minDate: new Date(2010, 0, 1),
                 maxDate: new Date(new Date().getFullYear(),new Date().getMonth(),new Date().getDate()),