Browse Source

修改日期

seyason 4 years ago
parent
commit
c99ac1606d
38 changed files with 922 additions and 134 deletions
  1. 7 0
      fhKeeper/formulahousekeeper/management-platform/pom.xml
  2. 2 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/aop/SensitiveWordConfig.java
  3. 14 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/config/Config.java
  4. 22 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/config/SchedulerConfig.java
  5. 21 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/AlertTimeController.java
  6. 2 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java
  7. 4 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/TaskController.java
  8. 73 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/WXController.java
  9. 41 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/AlertTime.java
  10. 7 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/TimeType.java
  11. 7 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/User.java
  12. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/AlertTimeMapper.java
  13. 6 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ReportMapper.java
  14. 3 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/UserMapper.java
  15. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/AlertTimeService.java
  16. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ReportService.java
  17. 2 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/UserService.java
  18. 20 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/AlertTimeServiceImpl.java
  19. 21 16
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  20. 159 7
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/UserServiceImpl.java
  21. 78 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/TimingTask.java
  22. 5 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/application.yml
  23. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/AlertTimeMapper.xml
  24. 7 4
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ReportMapper.xml
  25. 2 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/TimeTypeMapper.xml
  26. 12 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/UserMapper.xml
  27. 1 1
      fhKeeper/formulahousekeeper/timesheet/build/webpack.base.conf.js
  28. 10 10
      fhKeeper/formulahousekeeper/timesheet/config/index.js
  29. 4 4
      fhKeeper/formulahousekeeper/timesheet/src/main.js
  30. 1 5
      fhKeeper/formulahousekeeper/timesheet/src/views/expense/expense.vue
  31. 6 3
      fhKeeper/formulahousekeeper/timesheet/src/views/project/projectInside.vue
  32. 24 1
      fhKeeper/formulahousekeeper/timesheet/src/views/settings/timetype.vue
  33. 121 44
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue
  34. 2 2
      fhKeeper/formulahousekeeper/timesheet_h5/src/main.js
  35. 113 23
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/index.vue
  36. 51 0
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/index/index.vue
  37. 24 1
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/my/children/center.vue
  38. 1 0
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/view/index.vue

+ 7 - 0
fhKeeper/formulahousekeeper/management-platform/pom.xml

@@ -130,6 +130,13 @@
             <scope>system</scope>
             <systemPath>${basedir}/opencv/opencv-420.jar</systemPath>
         </dependency>
+
+        <!--微信模版消息推送三方sdk-->
+        <dependency>
+            <groupId>com.github.binarywang</groupId>
+            <artifactId>weixin-java-mp</artifactId>
+            <version>3.3.0</version>
+        </dependency>
     </dependencies>
 
     <build>

+ 2 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/aop/SensitiveWordConfig.java

@@ -20,8 +20,8 @@ import java.time.LocalDate;
 /**
  * 敏感词过滤
  */
-@Aspect
-@Component
+//@Aspect
+//@Component
 public class SensitiveWordConfig {
     @Autowired
     RedisUtil redisUtils;

+ 14 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/config/Config.java

@@ -0,0 +1,14 @@
+package com.management.platform.config;
+
+import org.springframework.boot.web.client.RestTemplateBuilder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+@Configuration
+public class Config {
+  @Bean
+  public RestTemplate restTemplate(RestTemplateBuilder builder){
+    return builder.build();
+  }
+}

+ 22 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/config/SchedulerConfig.java

@@ -0,0 +1,22 @@
+package com.management.platform.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.TaskScheduler;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
+
+@Configuration
+@EnableScheduling
+public class SchedulerConfig {
+    @Bean
+    public TaskScheduler taskScheduler() {
+        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
+        //线程池大小
+        scheduler.setPoolSize(10);
+        //线程名字前缀
+        scheduler.setThreadNamePrefix("spring-task-thread");
+        return scheduler;
+    }
+}
+

+ 21 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/AlertTimeController.java

@@ -0,0 +1,21 @@
+package com.management.platform.controller;
+
+
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * <p>
+ *  前端控制器
+ * </p>
+ *
+ * @author Seyason
+ * @since 2021-05-31
+ */
+@RestController
+@RequestMapping("/alert-time")
+public class AlertTimeController {
+
+}
+

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

@@ -53,8 +53,8 @@ public class ReportController {
      * date 日期 格式yyyy-mm-dd
      */
     @RequestMapping("/exportReport")
-    public HttpRespMsg exportReport(@RequestParam String date) {
-        return reportService.exportReport(date, request);
+    public HttpRespMsg exportReport(String startDate, String endDate, Integer projectId) {
+        return reportService.exportReport(startDate, endDate, projectId, request);
     }
 
 

+ 4 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/TaskController.java

@@ -480,6 +480,8 @@ public class TaskController {
         taskLogService.remove(new QueryWrapper<TaskLog>().eq("task_id",  id));
         //删除留言
         taskCommentService.remove(new QueryWrapper<TaskComment>().eq("task_id", id));
+        //删除近期任务
+        userRecentTaskService.remove(new QueryWrapper<UserRecentTask>().eq("task_id", id));
         deleteSubTask(task);
 
         //删除根任务,需要重新计算项目进度
@@ -498,6 +500,8 @@ public class TaskController {
             taskLogService.remove(new QueryWrapper<TaskLog>().in("task_id",  collect));
             //删除留言
             taskCommentService.remove(new QueryWrapper<TaskComment>().in("task_id", collect));
+            //删除近期任务
+            userRecentTaskService.remove(new QueryWrapper<UserRecentTask>().in("task_id", collect));
             subTasks.forEach(s->{
                 deleteSubTask(s);
             });

+ 73 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/WXController.java

@@ -0,0 +1,73 @@
+package com.management.platform.controller;
+
+import com.management.platform.service.UserService;
+import com.management.platform.util.HttpRespMsg;
+import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
+import me.chanjar.weixin.mp.bean.template.WxMpTemplateData;
+import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+@RestController
+@RequestMapping("/wechat")
+public class WXController {
+
+    @Value("${wx.template_report_fill}")
+    public String TEMPLATE_REPORT_FILL;
+    @Value("${wx.app_id}")
+    public String appId;
+    @Value("${wx.app_secret}")
+    public String appSecret;
+
+    @Resource
+    public UserService userService;
+
+
+    @GetMapping("/bindWeiXin")
+    public HttpRespMsg bindWeiXin(String code, String userId) {
+        return userService.bindWeiXin(code, userId);
+    }
+    /*
+     * 微信测试账号推送
+     * */
+    @GetMapping("/push")
+    public void push(String toOpenId) {
+        if (toOpenId == null) {
+            toOpenId = "o1L3L5lOrOl3_UEJjONaoT2Rne1I";
+        }
+        //1,配置
+        WxMpInMemoryConfigStorage wxStorage = new WxMpInMemoryConfigStorage();
+        wxStorage.setAppId(appId);
+        wxStorage.setSecret(appSecret);
+        WxMpService wxMpService = new WxMpServiceImpl();
+        wxMpService.setWxMpConfigStorage(wxStorage);
+
+        //2,推送消息
+        WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
+                .toUser(toOpenId)//要推送的用户openid
+                .templateId(TEMPLATE_REPORT_FILL)//模版id
+                .url("http://mob.ttkuaiban.com/")//点击模版消息要访问的网址
+                .build();
+        //3,如果是正式版发送模版消息,这里需要配置你的信息
+                templateMessage.addData(new WxMpTemplateData("first", "您今天的工时填报还未完成", "#FF00FF"));
+                templateMessage.addData(new WxMpTemplateData("keyword1", "屈跃庭", "#000000"));
+                templateMessage.addData(new WxMpTemplateData("keyword2", "市场部", "#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();
+        }
+
+    }
+
+
+}

+ 41 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/AlertTime.java

@@ -0,0 +1,41 @@
+package com.management.platform.entity;
+
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableField;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ * <p>
+ * 
+ * </p>
+ *
+ * @author Seyason
+ * @since 2021-05-31
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class AlertTime extends Model<AlertTime> {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId("company_id")
+    private Integer companyId;
+
+    /**
+     * 提醒时间; 例如18:00
+     */
+    @TableField("alert_time")
+    private String alertTime;
+
+
+    @Override
+    protected Serializable pkVal() {
+        return this.companyId;
+    }
+
+}

+ 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-03-29
+ * @since 2021-05-31
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -72,6 +72,12 @@ public class TimeType extends Model<TimeType> {
     @TableField("pay_overtime")
     private Boolean payOvertime;
 
+    /**
+     * 提醒时间
+     */
+    @TableField("alert_time")
+    private String alertTime;
+
 
     @Override
     protected Serializable pkVal() {

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

@@ -16,7 +16,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2021-05-19
+ * @since 2021-05-30
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -121,6 +121,12 @@ public class User extends Model<User> {
     @TableField("is_active")
     private Integer isActive;
 
+    /**
+     * 微信openid
+     */
+    @TableField("wx_openid")
+    private String wxOpenid;
+
     @TableField(exist = false)
     private String departmentName;
 

+ 16 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/AlertTimeMapper.java

@@ -0,0 +1,16 @@
+package com.management.platform.mapper;
+
+import com.management.platform.entity.AlertTime;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ * <p>
+ *  Mapper 接口
+ * </p>
+ *
+ * @author Seyason
+ * @since 2021-05-31
+ */
+public interface AlertTimeMapper extends BaseMapper<AlertTime> {
+
+}

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

@@ -18,7 +18,12 @@ import java.util.Map;
  * @since 2019-12-31
  */
 public interface ReportMapper extends BaseMapper<Report> {
-    List<Map<String, Object>> getAllReportByDate(@Param("date") String date, @Param("companyId") Integer companyId, @Param("userId") String userId);
+    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<Map<String, Object>> getReportByDate(@Param("date") String date, @Param("id") String id);

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

@@ -30,4 +30,7 @@ public interface UserMapper extends BaseMapper<User> {
 
     Integer countUserByDepartmentList(@Param("companyId") Integer companyId,
                                       @Param("departmentIds") List departmentIds);
+
+    List<Map<String, Object>> getPushUserList(@Param("companyId") Integer companyId);
+
 }

+ 16 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/AlertTimeService.java

@@ -0,0 +1,16 @@
+package com.management.platform.service;
+
+import com.management.platform.entity.AlertTime;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ * <p>
+ *  服务类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2021-05-31
+ */
+public interface AlertTimeService extends IService<AlertTime> {
+
+}

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

@@ -19,7 +19,7 @@ import java.util.List;
 public interface ReportService extends IService<Report> {
     HttpRespMsg getReportList(String date, Integer deptId, String userId, HttpServletRequest request);
 
-    HttpRespMsg exportReport(String date, HttpServletRequest request);
+    HttpRespMsg exportReport(@RequestParam String startDate, @RequestParam String endDate, Integer projectId, HttpServletRequest request);
 
     HttpRespMsg getReport(String date, HttpServletRequest request);
 

+ 2 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/UserService.java

@@ -43,4 +43,6 @@ public interface UserService extends IService<User> {
     HttpRespMsg getUserSalaryList(String id);
 
     HttpRespMsg setActive(String id, int isActive);
+
+    HttpRespMsg bindWeiXin(String code, String userId);
 }

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

@@ -0,0 +1,20 @@
+package com.management.platform.service.impl;
+
+import com.management.platform.entity.AlertTime;
+import com.management.platform.mapper.AlertTimeMapper;
+import com.management.platform.service.AlertTimeService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ *  服务实现类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2021-05-31
+ */
+@Service
+public class AlertTimeServiceImpl extends ServiceImpl<AlertTimeMapper, AlertTime> implements AlertTimeService {
+
+}

+ 21 - 16
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java

@@ -13,6 +13,7 @@ import org.apache.poi.hssf.usermodel.*;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.RequestParam;
 
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
@@ -673,7 +674,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
 
     //导出报告
     @Override
-    public HttpRespMsg exportReport(String date, HttpServletRequest request) {
+    public HttpRespMsg exportReport(@RequestParam String startDate, @RequestParam String endDate, Integer projectId, HttpServletRequest request) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         try {
             String userId = request.getHeader("Token");
@@ -681,16 +682,16 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
 
             //准备导出
             HSSFWorkbook workbook = new HSSFWorkbook();
-            HSSFSheet sheet = workbook.createSheet(date + "日报");
+            HSSFSheet sheet = workbook.createSheet("工作日报");
             //创建表头
             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, 10 * 256);
-            sheet.setColumnWidth(4, 50 * 256);
-            sheet.setColumnWidth(5, 18 * 256);
+            sheet.setColumnWidth(3, 15 * 256);
+            sheet.setColumnWidth(4, 15 * 256);
+            sheet.setColumnWidth(5, 100 * 256);
             //设置为居中加粗
             HSSFCellStyle headStyle = workbook.createCellStyle();
             HSSFFont font = workbook.createFont();
@@ -707,14 +708,16 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             headCell = headRow.createCell(2);
             headCell.setCellValue("项目名称");
             headCell.setCellStyle(headStyle);
+
             headCell = headRow.createCell(3);
-            headCell.setCellValue("工作时间");
+            headCell.setCellValue("工作日期");
             headCell.setCellStyle(headStyle);
+
             headCell = headRow.createCell(4);
-            headCell.setCellValue("工作内容");
+            headCell.setCellValue("工作时长(小时)");
             headCell.setCellStyle(headStyle);
             headCell = headRow.createCell(5);
-            headCell.setCellValue("提交时间");
+            headCell.setCellValue("工作内容");
             headCell.setCellStyle(headStyle);
             //设置日期格式
             HSSFCellStyle style = workbook.createCellStyle();
@@ -724,10 +727,10 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             List<Map<String, Object>> allReportByDate = null;
             if (user.getRole() == 0) {
                 //普通员工只能看自己的
-                allReportByDate = reportMapper.getAllReportByDate(date, null, user.getId());
+                allReportByDate = reportMapper.getAllReportByDate(startDate, null, user.getId(), endDate, projectId);
             } else {
                 //管理员看公司所有人的
-                allReportByDate = reportMapper.getAllReportByDate(date, user.getCompanyId(), null);
+                allReportByDate = reportMapper.getAllReportByDate(startDate, user.getCompanyId(), null, endDate, projectId);
             }
 
             for (Map<String, Object> map : allReportByDate) {
@@ -735,16 +738,18 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 row.createCell(0).setCellValue(rowNum);
                 row.createCell(1).setCellValue((String) map.get("name"));
                 row.createCell(2).setCellValue((String) map.get("project"));
-                row.createCell(3).setCellValue(map.get("duration").toString());
-                row.createCell(4).setCellValue((String) map.get("content"));
-                HSSFCell cell = row.createCell(5);
-                cell.setCellValue(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
-                        .format((Timestamp) map.get("time")));
+                HSSFCell cell = row.createCell(3);
+                cell.setCellValue(new SimpleDateFormat("yyyy-MM-dd")
+                        .format((java.sql.Date) map.get("createDate")));
                 cell.setCellStyle(style);
+
+                row.createCell(4).setCellValue(map.get("duration").toString());
+                row.createCell(5).setCellValue((String) map.get("content"));
+
                 rowNum++;
             }
             //生成Excel文件
-            String fileUrlSuffix = date + "日报" + System.currentTimeMillis() + ".xls";
+            String fileUrlSuffix = (startDate==null?"":(startDate+"至"+endDate))+"工作日报" + System.currentTimeMillis() + ".xls";
             FileOutputStream fos = new FileOutputStream(path + fileUrlSuffix);
             workbook.write(fos);
             fos.flush();

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

@@ -1,8 +1,10 @@
 package com.management.platform.service.impl;
 
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.management.platform.constant.Constant;
 import com.management.platform.entity.*;
 import com.management.platform.entity.vo.UserVO;
 import com.management.platform.mapper.*;
@@ -14,8 +16,14 @@ import org.apache.poi.xssf.usermodel.XSSFRow;
 import org.apache.poi.xssf.usermodel.XSSFSheet;
 import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Service;
 import org.springframework.util.NumberUtils;
+import org.springframework.web.client.RestTemplate;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
@@ -40,6 +48,21 @@ import java.util.Map;
  */
 @Service
 public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
+
+    @Value("${wx.template_report_fill}")
+    public String TEMPLATE_REPORT_FILL;
+    @Value("${wx.app_id}")
+    public String appId;
+    @Value("${wx.app_secret}")
+    public String appSecret;
+    public static final String GET_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code";
+    public static final String GET_USERINFO_URL = "https://api.weixin.qq.com/sns/userinfo?access_token=accessToken&openid=openId&lang=zh_CN";
+    @Resource
+    private RedisUtil redisUtil;
+
+    @Autowired
+    private RestTemplate restTemplate;
+
     @Resource
     private HttpServletRequest request;
     @Resource
@@ -421,6 +444,15 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
         //首先先搞到公司id
         Integer companyId = userMapper.selectById(request.getHeader("Token")).getCompanyId();
 
+        //检查人数是否已达上限
+        Company company = companyMapper.selectById(companyId);
+        int maxCnt = company.getStaffCountMax();
+        long cnt = userMapper.selectCount(new QueryWrapper<User>().eq("company_id", companyId).eq("is_active", 1));
+        if (cnt >= maxCnt) {
+            httpRespMsg.setError("人数已达上限,无法导入.请联系客服提高人数上限。");
+            return httpRespMsg;
+        }
+        int canImportNum = (int)(maxCnt - cnt);
         //查询工作时长设置
         TimeType time = timeTypeMapper.selectById(companyId);
         BigDecimal monthHours = time.getMonthDays().multiply(new BigDecimal(time.getAllday()));
@@ -510,14 +542,19 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
 //            if (!file.delete()) {
 //                System.out.println("临时文件" + file.getName() + "删除失败");
 //            }
-            //校验是否有重复账号
-            if (userMapper.selectCount(new QueryWrapper<User>().in("phone", phoneList)) == 0) {
-                for (User user : userList) {
-                    userMapper.insert(user);
-                }
+            //检查本次导入人数是否超过上限
+            if (userList.size() > canImportNum) {
+                httpRespMsg.setError("仅剩余"+canImportNum+"人可添加,请减少本次导入的人员数量或者联系客服提高人数上限。");
             } else {
-                httpRespMsg.setError("手机号有重复 批量新建账号失败");
-                /*这里以后可能需要返回重复的手机号的具体信息*/
+                //校验是否有重复账号
+                if (userMapper.selectCount(new QueryWrapper<User>().in("phone", phoneList)) == 0) {
+                    for (User user : userList) {
+                        userMapper.insert(user);
+                    }
+                } else {
+                    httpRespMsg.setError("手机号有重复 批量新建账号失败");
+                    /*这里以后可能需要返回重复的手机号的具体信息*/
+                }
             }
         } catch (IOException e) {
             e.printStackTrace();
@@ -609,4 +646,119 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
 
         return msg;
     }
+//
+//    private String getAccessToken(String code) {
+//        String accessToken = "";
+//
+//        if (redisUtil.existsKey("wxAccessToken")) {
+//            accessToken = redisUtil.getKey("wxAccessToken");
+//        } else {
+//            // 拼接用户授权接口信息
+//            String requestUrl = GET_TOKEN_URL.replace("APPID", appId).replace("SECRET", appSecret)
+//                    .replace("CODE", code);
+//            // 存储获取到的授权字段信息
+//            JSONObject result = new JSONObject();
+//            Map<String, String> dataMap = new HashMap<>();
+//            ResponseEntity<String> responseEntity = this.restTemplate.exchange(requestUrl,
+//                    HttpMethod.GET, null, String.class);
+//            if (responseEntity.getStatusCode() == HttpStatus.OK) {
+//                String resp = responseEntity.getBody();
+//                log.debug("返回信息==" + resp);
+//                System.out.println("返回信息==" + resp);
+//                dataMap.put("resp", resp);
+//                JSONObject OpenidJSONO = JSONObject.parseObject(resp);
+//                result = OpenidJSONO;
+//                if (OpenidJSONO.containsKey("access_token")) {
+//                    accessToken = OpenidJSONO.getString("access_token");
+//                    redisUtil.setKeyWithExpireTime("wxAccessToken", accessToken, 7200);
+//                    return  accessToken;
+//                }
+//            }
+//
+//        }
+//        return null;
+//    }
+
+    @Override
+    public HttpRespMsg bindWeiXin(String code, String userId) {
+        HttpRespMsg respMsg = new HttpRespMsg();
+        log.debug("code==" + code);
+        System.out.println("code==" + code);
+        // 拼接用户授权接口信息
+        String requestUrl = GET_TOKEN_URL.replace("APPID", appId).replace("SECRET", appSecret)
+                .replace("CODE", code);
+        // 存储获取到的授权字段信息
+        JSONObject result = new JSONObject();
+        Map<String, String> dataMap = new HashMap<>();
+        ResponseEntity<String> responseEntity = this.restTemplate.exchange(requestUrl,
+                HttpMethod.GET, null, String.class);
+        if (responseEntity.getStatusCode() == HttpStatus.OK) {
+            String resp = responseEntity.getBody();
+            log.debug("返回信息==" + resp);
+            System.out.println("返回信息==" + resp);
+            dataMap.put("resp", resp);
+            JSONObject OpenidJSONO = JSONObject.parseObject(resp);
+            result = OpenidJSONO;
+            //{"access_token":"32_sheMcGJRDYXVaBoc06o8iT9CyxquudqHl90qGKHg_MGxFhpFA5S8WKUL_mCnfY7O1gcJpS_gBFa4w5Vqb8pCHA","expires_in":7200,"refresh_token":"32_c4ocyhmbbbKyEmG4pS-ywgbV7FkK3A29F_GdZdHKrcvidy0amQeGmhBAo1WBcEWn0T7kSxjbp0BI4lYYtY4wAw","openid":"o1L3L5lOrOl3_UEJjONaoT2Rne1I","scope":"snsapi_userinfo"}
+            if (OpenidJSONO.containsKey("access_token")) {
+                // OpenidJSONO可以得到的内容:access_token expires_in refresh_token openid scope
+                String openid = String.valueOf(OpenidJSONO.get("openid"));
+                String accessToken = String.valueOf(OpenidJSONO.get("access_token"));
+                // 用户保存的作用域
+                String scope = String.valueOf(OpenidJSONO.get("scope"));
+                String refresh_token = String.valueOf(OpenidJSONO.get("refresh_token"));
+
+                // 第四步:拉取用户信息(需scope为 snsapi_userinfo)
+//                String url = GET_USERINFO_URL.replaceAll("accessToken", accessToken).replaceAll("openId", openid);
+//                responseEntity = this.restTemplate.exchange(url,
+//                        HttpMethod.GET, null, String.class);
+//                resp = responseEntity.getBody();
+//                log.debug("获取微信个人信息返回==" + resp);
+//                System.out.println("获取微信个人信息返回==" + resp);
+//                JSONObject json = JSONObject.parseObject(resp);
+                User user = new User();
+                user.setId(userId);
+                user.setWxOpenid(openid);
+                userMapper.updateById(user);
+//                if (!json.containsKey("errcode")) {
+//                    user.setWxOpenid(openid);
+//                }
+                //创建用户
+//                int cnt = wechatUserMapper.selectCount(new QueryWrapper<WechatUser>().eq("open_id", openid));
+//                if (cnt == 0) {
+//                    //检查昵称是否存在
+//                    WechatUser one = wechatUserMapper.selectOne(new QueryWrapper<WechatUser>().eq("nickname", user.getNickname()));
+//                    if (one != null) {
+//                        //按昵称更新
+//                        one.setAvatar(user.getAvatar());
+//                        one.setOpenId(user.getOpenId());
+//                        wechatUserMapper.updateById(one);
+//                    } else {
+//                        wechatUserMapper.insert(user);
+//                    }
+//                }
+                //检测密码正确时
+                User u = userMapper.selectById(userId);
+
+                Company company = companyMapper.selectById(u.getCompanyId());
+                UserVO userVO = new UserVO().setCompanyName(company.getCompanyName());
+                userVO.setCompany(company);
+                BeanUtils.copyProperties(u, userVO);
+                //还要多返回一个公司名字
+                userVO.setPassword("");
+                LocalDateTime remainingTime = company.getExpirationDate() == null ? LocalDateTime.now() : company.getExpirationDate();
+                userVO.setRemainingTime(remainingTime.toInstant(ZoneOffset.of("+8")).toEpochMilli() -
+                        LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli());
+                //检测是否是项目经理,项目经理有审核功能权限
+                int cnt = projectMapper.selectCount(new QueryWrapper<Project>().eq("incharger_id", userVO.getId()));
+                if (cnt > 0) {
+                    userVO.setLeader(true);
+                }
+                respMsg.data = userVO;
+            } else {
+                respMsg.setError(OpenidJSONO.getString("errcode") + ":" + OpenidJSONO.getString("errmsg"));
+            }
+        }
+        return respMsg;
+    }
 }

+ 78 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/TimingTask.java

@@ -1,17 +1,27 @@
 package com.management.platform.task;
 
+import java.time.LocalDate;
+import java.time.ZoneOffset;
 import java.time.format.DateTimeFormatter;
 
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.management.platform.constant.Constant;
-import com.management.platform.entity.Screenshot;
-import com.management.platform.mapper.ScreenshotMapper;
+import com.management.platform.entity.*;
+import com.management.platform.mapper.*;
 import com.management.platform.service.ScreenshotService;
 import com.management.platform.util.AuthService;
 import com.management.platform.util.CheckPicUtil;
 import com.management.platform.util.RedisUtil;
+import me.chanjar.weixin.mp.api.WxMpInMemoryConfigStorage;
+import me.chanjar.weixin.mp.api.WxMpService;
+import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl;
+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.http.*;
 import org.springframework.scheduling.annotation.EnableScheduling;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
@@ -33,11 +43,26 @@ import java.util.Set;
 @EnableScheduling
 @Component
 public class TimingTask {
+
+    @Value("${wx.template_report_fill}")
+    public String TEMPLATE_REPORT_FILL;
+    @Value("${wx.app_id}")
+    public String appId;
+    @Value("${wx.app_secret}")
+    public String appSecret;
     @Autowired
     private RedisUtil redisUtil;
 
     @Resource
     private ScreenshotMapper screenshotMapper;
+    @Resource
+    private TimeTypeMapper timeTypeMapper;
+    @Resource
+    private UserMapper userMapper;
+    @Resource
+    private DepartmentMapper departmentMapper;
+    @Resource
+    private ReportMapper reportMapper;
 
     @Value(value = "${upload.path}")
     private String path;
@@ -73,4 +98,55 @@ public class TimingTask {
             }
         }
     }
+
+
+
+    //每分钟校验是否有需要提醒的填报
+    @Scheduled(fixedRate = 60 * 1000)
+    private void process() {
+        LocalDateTime now = LocalDateTime.now();
+        DateTimeFormatter dt = DateTimeFormatter.ofPattern("HH:mm");
+        String str = dt.format(now);
+        List<TimeType> typeList = timeTypeMapper.selectList(new QueryWrapper<TimeType>().isNotNull("alert_time")
+                .ge("alert_time", str));
+
+        typeList.forEach(t->{
+            if (str.equals(t.getAlertTime())) {
+                //发送推送提醒
+                List<Map<String, Object>> userList = userMapper.getPushUserList(t.getCompanyId());
+                userList.forEach(u->{
+                    push((u));
+                });
+            }
+        });
+    }
+
+    public void push(Map<String, Object> user) {
+        //1,配置
+        WxMpInMemoryConfigStorage wxStorage = new WxMpInMemoryConfigStorage();
+        wxStorage.setAppId(appId);
+        wxStorage.setSecret(appSecret);
+        WxMpService wxMpService = new WxMpServiceImpl();
+        wxMpService.setWxMpConfigStorage(wxStorage);
+
+        //2,推送消息
+        WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
+                .toUser((String)user.get("wxOpenid"))//要推送的用户openid
+                .templateId(TEMPLATE_REPORT_FILL)//模版id
+                .url("http://mobworktime.ttkuaiban.com/#/edit")//点击模版消息要访问的网址
+                .build();
+        //3,如果是正式版发送模版消息,这里需要配置你的信息
+        templateMessage.addData(new WxMpTemplateData("first", "您今天的工时填报还未完成", "#FF00FF"));
+        templateMessage.addData(new WxMpTemplateData("keyword1", (String)user.get("name"), "#000000"));
+        templateMessage.addData(new WxMpTemplateData("keyword2", (String)user.get("departmentName"), "#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();
+        }
+
+    }
 }

+ 5 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/resources/application.yml

@@ -86,7 +86,11 @@ picrecongnize:
   develop: C:/picrecongnize/develop/
   im: C:/picrecongnize/im/
   design: C:/picrecongnize/design/
-
+# 智能工时管家公众号参数
+wx:
+  template_report_fill: lhwkaW9BKwCvMtCuoAxLw4lZoGgMaucL0Ap0Vz-5KOY
+  app_id: wx749c84daac654e1e
+  app_secret: aacbd046ec1c790836f4f684c96fe585
 ##actuator健康检查配置
 management:
   security:

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

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.management.platform.mapper.AlertTimeMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.AlertTime">
+        <id column="company_id" property="companyId" />
+        <result column="alert_time" property="alertTime" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        company_id, alert_time
+    </sql>
+
+</mapper>

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

@@ -29,7 +29,7 @@
     </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,
+        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
         FROM report AS a
@@ -38,8 +38,11 @@
         left join sub_project as d on d.id = a.sub_project_id
         left join task on task.id = a.task_id
         WHERE a.state = 1
-        <if test="date != null and date != ''">
-            AND a.create_date = #{date}
+        <if test="startDate != null and startDate != ''">
+            AND a.create_date between #{startDate} and #{endDate}
+        </if>
+        <if test="projectId != null">
+            AND a.project_id = #{projectId}
         </if>
         <if test="companyId != null">
             AND c.company_id = #{companyId}
@@ -47,7 +50,7 @@
         <if test="userId != null">
             AND a.creator_id = #{userId}
         </if>
-        ORDER BY a.creator_id ASC
+        ORDER BY a.id desc
     </select>
 
     <!--根据员工id,日期获取当天全部报告信息-->

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

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

+ 12 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/UserMapper.xml

@@ -20,11 +20,12 @@
         <result column="manage_dept_id" property="manageDeptId" />
         <result column="color" property="color" />
         <result column="is_active" property="isActive" />
+        <result column="wx_openid" property="wxOpenid" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id, name, phone, password, portrait_url, create_time, role, company_id, department_id, department_cascade, cost, month_cost, salary_type, manage_dept_id, color, is_active
+        id, name, phone, password, portrait_url, create_time, role, company_id, department_id, department_cascade, cost, month_cost, salary_type, manage_dept_id, color, is_active, wx_openid
     </sql>
     <!--单独分页获取人员-->
     <select id="getUserByDepartment" resultType="java.util.Map">
@@ -71,4 +72,14 @@
             #{departmentId}
         </foreach>
     </select>
+
+
+    <select id="getPushUserList" resultType="java.util.Map">
+        SELECT a.id, a.wx_openid as wxOpenid, a.name, a.phone, b.department_name AS departmentName
+        FROM user AS a LEFT JOIN  department b ON a.department_id = b.department_id
+        WHERE a.company_id = #{companyId}
+        AND a.wx_openid IS NOT NULL
+        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>
 </mapper>

+ 1 - 1
fhKeeper/formulahousekeeper/timesheet/build/webpack.base.conf.js

@@ -17,7 +17,7 @@ module.exports = {
         'vue': 'Vue',
         'vue-router': 'VueRouter',
         'vuex': 'Vuex',
-        'element-ui': 'ElementUI',
+        // 'element-ui': 'ElementUI',
         'echarts': 'echarts',
     },
     output: {

+ 10 - 10
fhKeeper/formulahousekeeper/timesheet/config/index.js

@@ -3,17 +3,17 @@ var path = require('path')
  //var ip = '127.0.0.1'
 
  
-var ip = '47.100.37.243'
+// var ip = '47.100.37.243'
 
-// var os = require('os'), ip = '', ifaces = os.networkInterfaces() // 获取本机ip
-// for (var i in ifaces) {
-//     for (var j in ifaces[i]) {
-//         var val = ifaces[i][j]
-//         if (val.family === 'IPv4' && val.address !== '127.0.0.1') {
-//             ip = val.address
-//         }
-//     }
-// }
+var os = require('os'), ip = '', ifaces = os.networkInterfaces() // 获取本机ip
+for (var i in ifaces) {
+    for (var j in ifaces[i]) {
+        var val = ifaces[i][j]
+        if (val.family === 'IPv4' && val.address !== '127.0.0.1') {
+            ip = val.address
+        }
+    }
+}
 
 module.exports = {
   build: {

+ 4 - 4
fhKeeper/formulahousekeeper/timesheet/src/main.js

@@ -1,10 +1,10 @@
 // import Vue from 'vue'
 // import VueRouter from 'vue-router'
 // Vue.use(VueRouter)
-// import ElementUI from 'element-ui'
-//全局修改默认配置,点击空白处不能关闭弹窗
-// Dialog.props.closeOnClickModal.default = false
-// Vue.use(ElementUI)
+import ElementUI from 'element-ui'
+// 全局修改默认配置,点击空白处不能关闭弹窗
+ElementUI.Dialog.props.closeOnClickModal.default = false
+Vue.use(ElementUI)
 // import Vuex from 'vuex'
 // Vue.use(Vuex)
 // import echarts from 'echarts'

+ 1 - 5
fhKeeper/formulahousekeeper/timesheet/src/views/expense/expense.vue

@@ -455,11 +455,7 @@ export default {
     delec(ids) {
       // console.log('删除操作', ids);
       this.ParticularsList.invoiceList.splice(ids, 1)
-      this.$message({
-        message: '删除成功',
-        type: "success"
-      });
-      // 发起删除请求
+      
     },
      adds() {
       // happenDate

+ 6 - 3
fhKeeper/formulahousekeeper/timesheet/src/views/project/projectInside.vue

@@ -228,7 +228,8 @@
         </el-tabs>
         
         <!--新增任务界面-->
-        <el-dialog class="jm" :title="title" v-if="addFormVisible" :visible.sync="addFormVisible" :close-on-click-modal="false" customClass="customWidth" width="800px">
+        <el-dialog :class="addForm.id==null?'':'jm'" :title="title" v-if="addFormVisible" :visible.sync="addFormVisible" 
+        :close-on-click-modal="false" customClass="customWidth" width="800px">
         <!-- <div style="width: 200%;height:80%;position: absolute;right:-100%;top:0;background:#000;opacity: 0;" @click="sss"></div> -->
 
             <el-form ref="form1" :model="addForm" :rules="taskRules" label-width="100px">
@@ -451,7 +452,7 @@
                 <el-page-header  @back="backToParentTask" title="返回父任务" :content="addForm.parentTname"></el-page-header>
             </div>
             <!-- 评论 -->
-            <div class="remark">
+            <div class="remark" v-show="addForm.id != null">
                 <span class="zh">
                     <span  v-for="(pl, i) in critic" :key="i">
                         <div class="player" v-if="pl">
@@ -1331,7 +1332,8 @@
             },  
             // 获取评论列表
             gain (task) {
-                this.taskId = task.id
+                this.commentList = [];
+                this.taskId = task.id;
                 this.http.post('/task-comment/getList', {taskId: task.id},
                 res => {
                     if (res.code == "ok") {
@@ -1417,6 +1419,7 @@
                 this.addForm = {projectId: stage.projectId, groupId: stage.groupId, stagesId: stage.id, taskLevel:0, planHours: 8, taskType: 0};
                 this.addLoading = false;
                 this.title="创建任务";
+                this.commentList = [];
             },
             renameStage(item) {
                 this.stageForm = JSON.parse(JSON.stringify(item));

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

@@ -143,7 +143,30 @@
         </div>
         </el-col>
         </el-row>
-
+        <!--设置时长样式内容-->
+        <p style="margin-left:10px;color:#666;">填报提醒时间设置</p>
+        <el-row :gutter="20" style="padding-top:10px;width:100%;margin:0 auto;padding-left:10px;padding-right:10px;">
+        <el-col :span="24" >
+            <div class="panel" style="height:60px;">
+                <el-form :inline="true" :model="timeType"  style="margin-top:10px;">
+                 
+                <el-form-item label="未填报提醒时间 " prop="alertTime">
+                    <el-time-picker 
+                            v-model="timeType.alertTime"
+                            placeholder="提醒时间"
+                            style="width:120px;"
+                            format="HH:mm"
+                            value-format="HH:mm"
+                            :picker-options="{
+                            start: '08:00',
+                            end: '23:30'
+                            }">
+                        </el-time-picker>
+                </el-form-item>
+                </el-form>
+            </div>
+        </el-col>
+        </el-row>
         <div style="width:80px;margin:0 auto;padding:20px;">
             <el-button  type="primary" @click="submitInsert" :loading="addLoading">保存</el-button>
         </div>

+ 121 - 44
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

@@ -15,7 +15,7 @@
                 <div slot="header" class="clearfix">
                     <el-date-picker size="small" v-model="date" :editable="false" format="yyyy-MM" value-format="yyyy-MM" @change="changeMonthOut" :clearable="false" type="month" placeholder="选择月份"></el-date-picker>
                     <span >
-                    <span>日期:</span>
+                    <span style="color:#999;">日期:</span>
                     <span v-for="(item,index) in allDate" :id="'day'+index" :class="index==choseDay?'chooseDate date_item':'date_item'" 
                     @click="choseDate(index, item)" :key="index">{{item}}</span>
                     </span>
@@ -60,19 +60,19 @@
                 
                 <div :style="'height:'+tableHeight+'px;width:0.5px;background:#eee;margin-right:10px;margin-left:10px;'" ></div>
                 <div class="allDaily" style="float:left;flex-grow:1">
-                    <!--系统管理员 -->
+                    <!--系统管理员和部门负责人 -->
                     <div class="report_title" v-if="user.role > 0 || user.manageDeptId > 0">
                         <span>工作日报 | {{depData.label}}</span> - 已填写<span style="margin-left:5px;margin-right:5px;color:green;">{{reportList.length}}</span>人,
                     未填写<span style="margin-left:5px;margin-right:5px;color:red;">{{(depData == null?data[0].membCount:(depData.isUser == 1?1:depData.membCount))-reportList.length}}</span>人
                         <span style="float:right;">
                             <el-link type="primary" style="margin-right:10px;" :underline="false" @click="fillInReport(-1)">填写日报</el-link>
-                            <el-link type="primary" style="margin-right:10px;" :underline="false" @click="exportReport">导出日报</el-link>
+                            <el-link type="primary" style="margin-right:10px;" :underline="false" @click="showExportDialog">导出日报</el-link>
                             <el-link type="primary" style="margin-right:10px;" :underline="false" @click="batchApprove">批量审核</el-link>
                         </span>
                     </div>
                     <!--普通员工,含项目经理 -->
-                    <div class="report_title" v-if="user.role==0"><span>日报列表</span>
-                    <span style="float:right;" v-if="user.role==0">
+                    <div class="report_title" v-if="user.role==0 && user.manageDeptId == 0"><span>日报列表</span>
+                    <span style="float:right;" v-if="user.role==0 && user.manageDeptId == 0">
                             <el-link type="primary" style="margin-right:10px;" :underline="false" @click="fillInReport(-1)">填写日报</el-link>
                             <el-link type="primary" v-if="user.leader" style="margin-right:10px;" :underline="false" @click="batchApprove">批量审核</el-link>
                     </span>
@@ -133,13 +133,16 @@
         </div>
 
         <!-- 填写日报的dialog -->
-        <el-dialog title="填写日报" :visible.sync="dialogVisible" width="60%">
+        <el-dialog title="填写日报" :visible.sync="dialogVisible" width="60%" :close-on-click-modal="false">
             <el-form ref="workForm" :model="workForm" :rules="workRules" label-width="100px">
                 <el-form-item label="工作日期" prop="createDate">
                     <el-date-picker v-model="workForm.createDate" :editable="false" format="yyyy-MM-dd" value-format="yyyy-MM-dd" 
                     @change="changeMonth()" :clearable="false" type="date" placeholder="选择工作日期" :disabled="isDisable"></el-date-picker>
-
-                    <span v-if="reportTimeType.type == 3" style="margin-left:30px;" >总时长: {{reportTimeType.allday.toFixed(1)}}小时</span>
+                    <span v-if="reportTimeType.type == 3" style="margin-left:30px;">总时长:</span>
+                    <el-input-number :disabled="!canEdit" v-if="reportTimeType.type == 3" style="margin-left:10px;" @change="changeAllTime"
+                        v-model="reportTimeType.allday" :precision="1" :step="0.5" :max="12" :min="0.5"></el-input-number>
+                    <span v-if="reportTimeType.type == 3">小时</span>
+                    <!-- <span v-if="reportTimeType.type == 3" style="margin-left:30px;" >总时长: {{reportTimeType.allday.toFixed(1)}}小时</span> -->
                 </el-form-item>
                 
                 <div v-for="(domain, index) in workForm.domains" :key="domain.id">
@@ -193,7 +196,7 @@
                             }">
                         </el-time-picker>
                         </span>
-                        <div class="overtime"><el-checkbox v-model="domain.isOvertime">加班</el-checkbox></div>
+                        <div class="overtime"><el-checkbox :disabled="!canEdit" v-model="domain.isOvertime">加班</el-checkbox></div>
                     </el-form-item>
                     
                     <el-form-item label="投入项目" :prop="'domains.' + index + '.projectId'"
@@ -215,11 +218,11 @@
 
                     <el-form-item v-if="reportTimeType.type == 3" label="用时占比" :prop="'domains.' + index + '.'+timeFields[reportTimeType.type]"
                         :rules="{ required: true, message: '请选择工作时长', trigger: 'blur' }">
-                        <div style="width:250px;">
-                            <el-col span="14"><el-slider v-model="domain.progress" :min="10" :show-tooltip="false" :step="10" style="width:136px;" @change="domain.workingTime = (reportTimeType.allday*domain.progress/100).toFixed(1)"></el-slider></el-col>
+                        <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>
                         </div>
-                        <div class="overtime"><el-checkbox v-model="domain.isOvertime">加班</el-checkbox></div>
+                        <div class="overtime"><el-checkbox :disabled="!canEdit" v-model="domain.isOvertime">加班</el-checkbox></div>
                     </el-form-item>
 
                     <!--项目管理专业版模式下,项目下的近期执行的任务 -->
@@ -233,9 +236,9 @@
                         <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>
-                    <el-divider v-if="workForm.domains.length>1"></el-divider>
+                    <el-divider v-if="workForm.domains.length>1" style="margin-bottom:10px;"></el-divider>
                 </div>
-                <el-link v-if="showAddMore" type="primary" :underline="false" @click="addDomain" style="margin-left:40px">添加更多</el-link>
+                <el-link v-if="showAddMore"  :disabled="!canEdit" type="primary" :underline="false" @click="addDomain" style="margin-left:40px">添加更多</el-link>
             </el-form>
             <span slot="footer" class="dialog-footer">
                 <el-button @click="deleteReport" v-if="workForm.domains[0].id != null">删除</el-button>
@@ -246,7 +249,7 @@
         </el-dialog>
 
         <!-- 批量日报审核 -->
-        <el-dialog title="批量日报审核" :visible.sync="approveDialogVisible" width="500px">
+        <el-dialog title="批量日报审核" :visible.sync="approveDialogVisible" width="500px" >
             <el-checkbox v-model="isAllSelect" label="全选" style="margin-left:24px;" @change="selectAll" v-if="reportNames.length > 0"></el-checkbox>
             <el-tree ref="approveTree" node-key="id" :data="reportNames" show-checkbox="true" @check-change="handleCheckChange" >
             </el-tree>
@@ -255,7 +258,31 @@
                 <el-button type="primary" @click="submitBatchApprove" :disabled="batchShowData.length == 0">审核通过</el-button>
             </span>
         </el-dialog>
-
+        <!--导出报表条件选择 -->
+        <el-dialog title="日报导出" v-if="exportDialog" :visible.sync="exportDialog" customClass="customWidth" width="500px">
+            <el-form ref="form3" :model="exportParam" >
+                <el-form-item prop="projectId" label="选择项目">
+                    <el-select v-model="exportParam.projectId" placeholder="全部项目"  clearable style="width:350px;">
+                        <el-option v-for="item in projectList"  :key="item.id" :label="item.projectName" :value="item.id"></el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item prop="projectId" label="日期范围">
+                    <el-date-picker
+                        v-model="exportParam.dateRange" :editable="false" 
+                        format="yyyy-MM-dd" value-format="yyyy-MM-dd" 
+                        :clearable="true" 
+                        range-separator="至"
+                        type="daterange" 
+                        start-placeholder="开始日期"
+                        end-placeholder="结束日期"
+                    ></el-date-picker>
+                </el-form-item>
+                
+            </el-form>
+            <div slot="footer" class="dialog-footer">
+                <el-button type="primary" @click="exportReport" style="width:100%;" >导出</el-button>
+            </div>
+        </el-dialog>
     </section>
 </template>
 
@@ -265,6 +292,8 @@
     export default {
         data() {
             return {
+                exportParam:{projectId: null, dateRange:[]},
+                exportDialog:false,
                 timeFields:['timeType', 'workingTime', 'startTime', 'progress'],
                 subProjectList:[],
                 canEdit: true,
@@ -341,6 +370,15 @@
             };
         },
         methods: {
+            showExportDialog() {
+                this.exportDialog = true;
+            },
+            changeAllTime() {
+                //总时长发生改变,自动按比例计算
+                this.workForm.domains.forEach(d=>{
+                    d.workingTime = (d.progress*this.reportTimeType.allday/100).toFixed(1);
+                });
+            },
             // 是否加班的单机事件
             check() {
                 this.selected = !this.selected
@@ -744,6 +782,7 @@
             // 改变月份
             changeMonthOut() {
                 console.log(sessionStorage.msg)
+
                 this.getAllDate();
                 this.getReportList();
                 this.getDepartment();
@@ -763,6 +802,7 @@
             getAllDate() {
                 var dayArry = [];
                 var day = this.getCountDays();
+                let curMonthDay = null;
                 for (var k = 1; k <= day; k++) {
                     var str = new Date(this.date.replace(/-/g, "/")).getMonth() + 1 + "月" + k + "日";
                     if ( new Date(this.date.replace(/-/g, "/")).getFullYear() == new Date(new Date()).getFullYear() &&
@@ -770,20 +810,36 @@
                         if(sessionStorage.msg) {
                             if(parseInt(sessionStorage.msg.split("-")[2]) == k) {
                                 this.choseDay = k - 1;
+                                curMonthDay = str;
                             }
                         } else {
                             if (new Date().getDate() == k) {
                                 this.choseDay = k - 1;
                             }   
                         }
+                         
                     } else {
                         this.choseDay = 0;
                     }
                     dayArry.push(str);
                 }
                 this.allDate = dayArry;
-                if (sessionStorage.msg) {
+                //不能超过最大日期
+                if (this.choseDay > day-1) {
+                    this.choseDay = day-1;
+                }
+                //从消息点击跳转过来的,直接加载指定日期
+                if (sessionStorage.from == 1 && sessionStorage.msg) {
+                    console.log('sssss');
                     this.curDate = sessionStorage.msg;
+                    sessionStorage.from = 0;
+                } else {
+                    if (curMonthDay != null) {
+                        this.curDate = curMonthDay;
+                    } else {
+                        var d = new Date(this.date.replace(/-/g, "/"))
+                        this.curDate = (d.getMonth()+1)+'月'+d.getDate()+'日';  
+                    }
                 }
             },
 
@@ -832,35 +888,34 @@
 
             //导出日报
             exportReport() {
-                if (this.reportList.length > 0) {
-                    this.listLoading = true;
-                    //首先处理日期
-                    let day = this.choseDay > 9 ? "-" + (this.choseDay + 1) : "-0" + (this.choseDay + 1);
-                    this.http.post( this.port.report.export, { date: this.date + day },
-                    res => {
-                        this.listLoading = false;
-                        if (res.code == "ok") {
-                            location.href = res.data;
-                        } else {
-                            this.$message({
-                                message: res.msg,
-                                type: "error"
-                            });
-                        }
-                    },
-                    error => {
-                        this.listLoading = false;
+                this.listLoading = true;
+                var param = {};
+                if (this.exportParam.dateRange != null) {
+                    param = {startDate:this.exportParam.dateRange[0], endDate: this.exportParam.dateRange[1]};
+                }
+                if (this.exportParam.projectId != null) {
+                    param.projectId = this.exportParam.projectId;
+                }
+                this.http.post( this.port.report.export, param,
+                res => {
+                    this.listLoading = false;
+                    if (res.code == "ok") {
+                        location.href = res.data;
+                        this.exportDialog = false;
+                    } else {
                         this.$message({
-                            message: error,
+                            message: res.msg,
                             type: "error"
                         });
-                    });
-                } else {
+                    }
+                },
+                error => {
+                    this.listLoading = false;
                     this.$message({
-                        message: "当天没有报告 无法导出",
-                        type: "info"
+                        message: error,
+                        type: "error"
                     });
-                }
+                });
             },
 
             //获取项目列表
@@ -936,7 +991,7 @@
                                     projectId: "",
                                     workingTime: this.reportTimeType.type==3?(10*this.reportTimeType.allday/100).toFixed(1):"",
                                     content: "",
-                                    progress:10,
+                                    progress:100,
                                     state: 2,
                                     timeType:0,
                                 }],
@@ -973,11 +1028,23 @@
 
             // 添加模块
             addDomain() {
+                var leftProgress = 10;
+                if (this.reportTimeType.type == 3) {
+                    //计算已经待分配工时比例
+                    let array = this.workForm.domains;
+                    let totalProgress = 0;
+                    for (var i=0;i<array.length; i++) {
+                        totalProgress += array[i].progress;
+                    }
+                    if (totalProgress < 100) {
+                        leftProgress = 100 - totalProgress;
+                    }
+                }
                 this.workForm.domains.push({
                         projectId: "",
-                        workingTime: this.reportTimeType.type == 3?(10*this.reportTimeType.allday/100).toFixed(1):"",
+                        workingTime: this.reportTimeType.type == 3?(leftProgress*this.reportTimeType.allday/100).toFixed(1):"",
                         content: "",
-                        progress:10,
+                        progress:leftProgress,
                         state:2,//2-表示待提交
                 });
                 
@@ -1066,6 +1133,12 @@
                                         type: "error"
                                     });
                                 return;
+                            } else if (total < 100) {
+                                this.$message({
+                                        message: "工时尚未完全分配,无法提交",
+                                        type: "error"
+                                    });
+                                return;
                             }
                         }
                         
@@ -1279,6 +1352,10 @@
 
         },
         mounted() {
+            var now = new Date();
+            var t = util.formatDate.format(now, 'yyyy-MM-dd');
+            var startStr = util.formatDate.format(new Date(), 'yyyy-MM') + "-01";
+            this.exportParam.dateRange = [startStr,t];
             this.getAllDate();
             this.getReportList();
             this.getProjectList();

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

@@ -13,12 +13,12 @@ import "@/components/Vant";
 
 import { Form , Toast , Grid, GridItem , DatetimePicker ,
 Picker , Dialog , NumberKeyboard , Sticky , Skeleton ,
-Panel , Divider , List , pullRefresh , SwipeCell, Checkbox, Search, Slider } from 'vant';
+Panel , Divider , List , pullRefresh , SwipeCell, Checkbox, Search, Slider,Stepper,Tag } from 'vant';
 
 Vue.use(Form).use(Toast).use(Grid).use(GridItem).use(DatetimePicker)
 .use(Picker).use(Dialog).use(NumberKeyboard).use(Sticky).use(Skeleton)
 .use(Panel).use(Divider).use(List).use(pullRefresh).use(SwipeCell)
-.use(Checkbox).use(Search).use(Slider);
+.use(Checkbox).use(Search).use(Slider).use(Stepper).use(Tag);
 
 // rem
 import "amfe-flexible";

+ 113 - 23
fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/index.vue

@@ -8,19 +8,31 @@
             <van-popup v-model="showPicker" position="bottom">
                 <van-datetime-picker v-model="currentDate" type="date" :min-date="minDate" :max-date="maxDate" @confirm="changeTime" @cancel="showPicker = false"/>
             </van-popup>
-            <van-cell title="总时长" v-if="reportTimeType.type == 3" :value="reportTimeType.allday.toFixed(1)+'小时'" ></van-cell>
+            <van-cell title="总时长(h)" v-if="reportTimeType.type == 3">
+                <template>
+                    <van-stepper v-model="reportTimeType.allday" @change="changeAllTime" min="0.5" max="12" step="0.5" :decimal-length="1" />
+                </template>
+            </van-cell>
+            
             <!-- <van-cell title="待分配时长" :value="report.time + 'h'" size="large"></van-cell> -->
 
             <div class="form_domains" v-for="(item,index) in form.domains" :key="item.id">
-                <van-button v-if="index == 0" :disabled="!canEdit" @click="addNewPro" class="form_addNew" icon="plus" type="info" size="mini">新增项目</van-button>
+                <van-tag v-if="index == 0 && canEdit" 
+                :disabled="!canEdit" @click="addNewPro" 
+                class="form_addNew" icon="plus" type="primary" >+ 新增项目</van-tag>
                 <van-icon v-if="index>0&&canEdit" class="form_del" name="delete" @click="delPro(index)" />
 
                 <van-cell-group :title="'项目' + (index+1)">
-                    <van-field readonly clickable name="projectId" :value="item.projectName" label="投入项目" placeholder="请选择投入项目" @click="clickPicker(index)"
+                    <van-field  readonly  name="projectId" :value="item.projectName" label="投入项目" placeholder="请选择投入项目" @click="clickPicker(index)"
                     :rules="[{ required: true, message: '请选择投入项目' }]"/>
                     <van-popup v-model="showPickerProject" position="bottom">
                         <van-picker show-toolbar :columns="project" value-key="projectName" @confirm="choseProject" @cancel="showPickerProject = false" />
                     </van-popup>
+                    <van-field  readonly name="taskId" :value="item.taskName" label="关联任务" placeholder="请选择关联任务" @click="clickPickerTask(index)"
+                    />
+                    <van-popup v-model="showPickerTask" position="bottom">
+                        <van-picker show-toolbar :columns="item.taskList" value-key="taskName" @confirm="choseTask" @cancel="showPickerTask = false" />
+                    </van-popup>
                     <!-- <van-field readonly clickable class="form_input" :value="item.workingTime" name="workingTime" label="工作时长" placeholder="请输入工作时长(单位:小时)"
                     :rules="[{ required: true, message: '请输入工作时长(单位:小时)' }]" @touchstart.native.stop="showNumberKey = true"/>
                     <van-number-keyboard v-model="item.workingTime" :show="showNumberKey" close-button-text="完成" extra-key="." :maxlength="4" @blur="showNumberKey = false" /> -->
@@ -69,7 +81,7 @@
                         <template >
                             <div>
                         <span>用时占比</span>
-                        <van-slider :disabled="!canEdit" :min="10" :step="10" style="width:120px;display:inline-block;margin-left:50px;" v-model="item.progress" @change="item.workingTime = (reportTimeType.allday*item.progress/100).toFixed(1)" >
+                        <van-slider :disabled="!canEdit" :min="10" :step="10" style="width:120px;display:inline-block;margin-left:50px;" v-model="item.progress" :value="100" @change="item.workingTime = (reportTimeType.allday*item.progress/100).toFixed(1)" >
                         <template #button>
                             <div class="custom-button">{{ item.progress }}%</div>
                         </template>
@@ -87,12 +99,13 @@
                         <van-radio name="1">加班</van-radio>
                     </van-radio-group> -->
                     <div class="overtime">
-                        <van-checkbox v-model="item.isOvertime">加班</van-checkbox>
+                        <van-checkbox :disabled="!canEdit" v-model="item.isOvertime">加班</van-checkbox>
                     </div>
                 </van-cell-group>
             </div>
             <div class="form_btn" style="margin: 16px;">
                 <van-button v-if="canEdit" round block type="info" native-type="submit"> 提交 </van-button>
+                <p v-if="canEdit&&form.domains.length>0 && form.domains[0].id != null" round block type="default" style="padding-top:30px;font-size:15px;color:#999;margin:0 auto;text-align:center;" @click="deleteReport"> 删除 </p>
             </div>
         </van-form>
         <div style="padding:15px;">
@@ -107,6 +120,8 @@
     export default {
         data() {
             return {
+
+                showPickerTask:false,
                 canCancel:false,
                 canEdit:false,
                 showEndTime: false,
@@ -120,7 +135,7 @@
                 reportTimeType:{},
                 user: JSON.parse(localStorage.userInfo),
                 minDate: new Date(2010, 0, 1),
-                maxDate: new Date(new Date().getFullYear(),new Date().getMonth(),new Date().getDate()),
+                maxDate: new Date(new Date().getFullYear(),new Date().getMonth(),new Date().getDate()+7),
                 currentDate: new Date(),
                 showPickerTime: false,
                 showPicker: false,
@@ -157,6 +172,35 @@
         },
 
         methods: {
+            //删除日报
+            deleteReport() {
+                this.$dialog.confirm({
+                    title: '删除日报',
+                    message: '确定要删除当天日报吗?'
+                }).then(() => {
+                    const toast = this.$toast.loading({
+                        forbidClick: true,
+                        duration: 0
+                    });
+                    this.$axios.post("/report/delete", {userId: this.user.id, date:this.form.createDate})
+                    .then(res => {
+                        if(res.code == "ok") {
+                            toast.clear();
+                            this.$toast.success('删除成功');
+                            window.location.reload();
+                        } else {
+                            toast.clear();
+                            this.$toast.fail('删除失败');
+                        }
+                    }).catch(err=> {toast.clear();});
+                }).catch(() => {});
+            },
+            changeAllTime() {
+                //总时长发生改变,自动按比例计算
+                this.form.domains.forEach(d=>{
+                    d.workingTime = (d.progress*this.reportTimeType.allday/100).toFixed(1);
+                });
+            },
             cancel() {
                 const toast = this.$toast.loading({
                     forbidClick: true,
@@ -284,6 +328,11 @@
                     if(res.code == "ok") {
                         toast.clear();
                         this.project = res.data;
+                        
+                        if (this.project.length > 0) {
+                            
+                            this.getTaskList(this.project[0].id);
+                        }
                     } else {
                         toast.clear();
                         this.$toast.fail('获取失败');
@@ -321,8 +370,7 @@
                             let array = [];
                             for(var i in list) {
                                 var projectName = "";
-                                var flg = null
-                                list[i].isOvertime == 1 ? flg = true : flg = false
+                                var flg = (list[i].isOvertime == 1);
                                 for(var j in this.project) {
                                     if(this.project[j].id == list[i].projectId) {
                                         projectName = this.project[j].projectName;
@@ -357,12 +405,12 @@
                                 id: null,
                                 projectId: "",
                                 projectName: "",
-                                workingTime: this.reportTimeType.type==3?(this.reportTimeType.allday*10/100).toFixed(1):"",
+                                workingTime: t.type==3?(t.allday).toFixed(1):"",
                                 content: "",
                                 state: 2,
-                                progress:10,
+                                progress:100,
+                                isOvertime:false,
                             }]
-                            
                             this.canEdit = true;
                         }
                     } else {
@@ -382,26 +430,70 @@
 
             // 选择项目
             clickPicker(i) {
+                if (!this.canEdit) return;
                 this.clickIndex = i;
                 this.showPickerProject = true;
             },
+            //选择任务
+            clickPickerTask(i) {
+                if (!this.canEdit) return;
+                this.clickIndex = i;
+                this.showPickerTask = true;
+            },
+            choseTask(value, index) {
+                this.form.domains[this.clickIndex].taskId = value.taskId;
+                this.form.domains[this.clickIndex].taskName = value.taskName;
+                this.showPickerTask = false;
+                
+            },
 
             choseProject(value, index) {
                 this.form.domains[this.clickIndex].projectId = value.id;
                 this.form.domains[this.clickIndex].projectName = value.projectName;
                 this.showPickerProject = false;
+                this.getTaskList(value.id);
+            },
+
+            getTaskList(projectId) {
+                console.log('==============='+projectId);
+                //如果是专业版,需要列出任务列表
+                if (this.user.company.packageProject == 1) {
+                    this.$axios.post("/task/getRecentTask", {projectId: projectId})
+                        .then(res => {
+                            if(res.code == "ok") {
+                                this.form.domains[this.clickIndex].taskList = res.data;
+                                this.$forceUpdate();
+                            } else {
+                                this.$toast.fail('获取失败');
+                            }
+                        }).catch(err=> {toast.clear();});
+                }
             },
 
             // 添加项目 
             addNewPro() {
+                var leftProgress = 10;
+                if (this.reportTimeType.type == 3) {
+                    //计算已经待分配工时比例
+                    let array = this.form.domains;
+                    let totalProgress = 0;
+                    for (var i=0;i<array.length; i++) {
+                        totalProgress += array[i].progress;
+                    }
+                    if (totalProgress < 100) {
+                        leftProgress = 100 - totalProgress;
+                    }
+                }
+
                 this.form.domains.push({
                     id: null,
                     projectId: "",
                     projectName: "",
-                    workingTime: this.reportTimeType.type==3?(10*this.reportTimeType.allday/100).toFixed(1):"",
-                    progress:10,
+                    workingTime: this.reportTimeType.type==3?(leftProgress*this.reportTimeType.allday/100).toFixed(1):"",
+                    progress:leftProgress,
                     content: "",
                     state: 2,
+                    isOvertime:false,
                 })
             },
 
@@ -460,6 +552,9 @@
                     if (total > 100) {
                         this.$toast.fail("用时比例之和不能超过100%");
                         return;
+                    } else if (total < 100) {
+                        this.$toast.fail("工时尚未完全分配,无法提交");
+                        return;
                     }
                 }
                 
@@ -495,18 +590,13 @@
                     } else {
                         formData.append("content", this.form.domains[i].content);
                     }
-                    formData.append("createDate", this.form.createDate);
-
-                    if(this.form.domains[i].isOvertime == undefined ) {
-                        this.form.domains[i].isOvertime = '0'
-                        formData.append("isOvertime", this.form.domains[i].isOvertime);
-                    } else if (this.form.domains[i].isOvertime == false){
-                        this.form.domains[i].isOvertime = '0'
-                        formData.append("isOvertime", this.form.domains[i].isOvertime);
+                    if (this.form.domains[i].taskId == null) {
+                        formData.append("taskId", 0);
                     } else {
-                        this.form.domains[i].isOvertime = '1'
-                        formData.append("isOvertime", this.form.domains[i].isOvertime);
+                        formData.append("taskId", this.form.domains[i].taskId);
                     }
+                    formData.append("createDate", this.form.createDate);
+                    formData.append("isOvertime", this.form.domains[i].isOvertime?1:0);
                 }
                 this.$axios.post("/report/editReport", formData)
                 .then(res => {

+ 51 - 0
fhKeeper/formulahousekeeper/timesheet_h5/src/views/index/index.vue

@@ -89,6 +89,57 @@
             }
 
             this.getMessage();
+
+            let href = window.location.href;
+            if (this.user.wxOpenid == null || this.user.wxOpenid == undefined || this.user.wxOpenid == 'undefined') {
+                // localStorage.openId = 'o1L3L5lOrOl3_UEJjONaoT2Rne1I';
+                //会自动跳转到首页
+                // let href = 'http://hq.tangusoft.com/?code=011Ptjgc2rx1eI09Irgc2Rvsgc2PtjgF&state=1#/index';
+                
+                if (href.includes("com/?code")) {  //url包括 com/?code 证明为从微信跳转回来的
+                    var url = href; //vue自动在末尾加了 #/ 符号,截取去掉
+                    var jingPosit = url.indexOf("com/") + 4; //获取域名结束的位置
+
+                    // var urlLeft = url.substring(0, jingPosit);//url左侧部分
+                    var urlRight = url.substring(jingPosit, url.length); //url右侧部分
+                    console.log('urlRight=' + urlRight);
+                    // window.location = urlLeft + "#/home" + urlRight;//拼接跳转
+                    //获取code
+                    var code = urlRight.substring('?code='.length,urlRight.indexOf('#/index'));
+                    if (code.indexOf('&state=1') > 0) {
+                        code = code.substring(0, code.indexOf('&state=1'));
+                    }
+
+                    //调用后台接口,注册用户
+                    console.log('获取到code=='+code);
+
+                    this.$axios.get("/wechat/bindWeiXin", {params:{code:code, userId: this.user.id}})
+                        .then(res => {
+                            console.log(res);
+                            if (res == null) {
+                                this.$toast.fail('绑定失败');
+                            } else if(res.errcode != null) {
+                                //报错了
+                                console.log(res.errmsg);
+                            } else {
+                                //获取openId
+                                if (res.data != null && res.data.wxOpenid != undefined) {
+                                    localStorage.userInfo = JSON.stringify(res.data);
+                                    console.log('绑定成功');
+                                    this.user = res.data;
+                                    window.location.href = '/#/my/center';
+                                    // if (sessionStorage.prePage != null) {
+                                    //     window.location.href = '/#'+sessionStorage.prePage;
+                                    // }
+                                }
+                            }
+                        }).catch(err=> {
+                            alert('err=' + err);
+                        });
+                }
+            } else {
+                
+            }
         },
         components: {
             Footer

+ 24 - 1
fhKeeper/formulahousekeeper/timesheet_h5/src/views/my/children/center.vue

@@ -16,6 +16,12 @@
                 <van-cell title="公司" :title-style="'flex: 0.5;'" :value="userInfo.companyName"></van-cell>
                 <van-cell title="修改密码" isLink to="/my/set"></van-cell>
             </div>
+            <van-cell title="绑定微信" @click="bindWeiXin" style="margin-top:10px;" :title-style="'flex: 2.5;'" label="绑定微信后可接收工时填报提醒">
+                <template>
+                    <span v-if="userInfo.wxOpenid == null" style="color:#ff0000;">未绑定</span>
+                    <span v-if="userInfo.wxOpenid != null" style="color:#7CCD7C;">已绑定</span>
+                </template>
+            </van-cell>
             <van-button class="logout" @click="logout" block round type="danger" >退出登录</van-button>
         </main>
 
@@ -46,11 +52,28 @@
                 this.$store.commit("updateLogin", false);
                 localStorage.removeItem("userInfo");
                 this.$router.push("/login");
-            }
+            },
+            bindWeiXin(){
+                if (this.userInfo.wxOpenid != null) {
+                    return;
+                }
+                var url = "http://mobworktime.ttkuaiban.com/";//授权回调页面
+                var appId = "wx749c84daac654e1e";//工时管家公众号
+                var weixinUrl="https://open.weixin.qq.com/connect/oauth2/authorize?appid="+appId+"&redirect_uri="+encodeURI(url)+"&response_type=code&scope=snsapi_base&state=1#wechat_redirect";
+                console.log(weixinUrl);
+                window.location.href = weixinUrl;
+            },
         },
         create() {
+            
         },
         mounted() {
+            // if (localStorage.openId == null || localStorage.openId == undefined || localStorage.openId == 'undefined') {
+            //     localStorage.openId = 'oBCQbt2yf7d-OxFuAF4tTJYbiI1I';//测试账号
+            //     // localStorage.openId = 'osp3lt91BuJ_JkwoqawsCI5b8IZM';//测试账号
+            // }
+            
+            
         }
     };
 </script>

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

@@ -22,6 +22,7 @@
                     </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" 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