Преглед на файлове

工程专业相关功能开发

seyason преди 3 години
родител
ревизия
7b3b9e24d3
променени са 91 файла, в които са добавени 6097 реда и са изтрити 479 реда
  1. 108 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/AuthRedirectController.java
  2. 21 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/PpMembsController.java
  3. 115 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProfessionController.java
  4. 5 3
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectController.java
  5. 61 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectNotifyUserController.java
  6. 57 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectProfessionController.java
  7. 74 19
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java
  8. 21 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportProfessionProgressController.java
  9. 5 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserController.java
  10. 226 115
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/WeiXinCorpController.java
  11. 7 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/Company.java
  12. 4 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/EarningSnapshot.java
  13. 66 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/PpMembs.java
  14. 42 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/Profession.java
  15. 7 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/Project.java
  16. 45 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ProjectNotifyUser.java
  17. 74 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ProjectProfession.java
  18. 11 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/Report.java
  19. 57 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ReportProfessionProgress.java
  20. 4 4
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/TimeType.java
  21. 7 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/WxCorpInfo.java
  22. 11 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/vo/UserMonthWork.java
  23. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/PpMembsMapper.java
  24. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ProfessionMapper.java
  25. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ProjectNotifyUserMapper.java
  26. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ProjectProfessionMapper.java
  27. 18 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ReportMapper.java
  28. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ReportProfessionProgressMapper.java
  29. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/PpMembsService.java
  30. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProfessionService.java
  31. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectNotifyUserService.java
  32. 24 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectProfessionService.java
  33. 2 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectService.java
  34. 16 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ReportProfessionProgressService.java
  35. 8 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ReportService.java
  36. 4 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/UserService.java
  37. 1 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/WxCorpInfoService.java
  38. 20 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/PpMembsServiceImpl.java
  39. 20 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProfessionServiceImpl.java
  40. 20 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectNotifyUserServiceImpl.java
  41. 157 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectProfessionServiceImpl.java
  42. 26 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java
  43. 20 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportProfessionProgressServiceImpl.java
  44. 677 24
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  45. 122 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/UserServiceImpl.java
  46. 130 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/WxCorpInfoServiceImpl.java
  47. 17 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/task/TimingTask.java
  48. 1 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/application.yml
  49. 2 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/CompanyMapper.xml
  50. 22 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/PpMembsMapper.xml
  51. 17 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProfessionMapper.xml
  52. 11 10
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectMapper.xml
  53. 18 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectNotifyUserMapper.xml
  54. 21 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectProfessionMapper.xml
  55. 86 7
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ReportMapper.xml
  56. 20 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ReportProfessionProgressMapper.xml
  57. 2 2
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/UserMapper.xml
  58. 2 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/WxCorpInfoMapper.xml
  59. 141 3
      fhKeeper/formulahousekeeper/timesheet/src/assets/myfont/demo_index.html
  60. 27 3
      fhKeeper/formulahousekeeper/timesheet/src/assets/myfont/iconfont.css
  61. 1 1
      fhKeeper/formulahousekeeper/timesheet/src/assets/myfont/iconfont.js
  62. 42 0
      fhKeeper/formulahousekeeper/timesheet/src/assets/myfont/iconfont.json
  63. BIN
      fhKeeper/formulahousekeeper/timesheet/src/assets/myfont/iconfont.ttf
  64. BIN
      fhKeeper/formulahousekeeper/timesheet/src/assets/myfont/iconfont.woff
  65. BIN
      fhKeeper/formulahousekeeper/timesheet/src/assets/myfont/iconfont.woff2
  66. 9 5
      fhKeeper/formulahousekeeper/timesheet/src/main.js
  67. 40 8
      fhKeeper/formulahousekeeper/timesheet/src/routes.js
  68. 14 10
      fhKeeper/formulahousekeeper/timesheet/src/views/Home.vue
  69. 119 15
      fhKeeper/formulahousekeeper/timesheet/src/views/Login.vue
  70. 34 6
      fhKeeper/formulahousekeeper/timesheet/src/views/corpreport/list.vue
  71. 293 0
      fhKeeper/formulahousekeeper/timesheet/src/views/profession/list.vue
  72. 2 2
      fhKeeper/formulahousekeeper/timesheet/src/views/project/cost.vue
  73. 545 37
      fhKeeper/formulahousekeeper/timesheet/src/views/project/info.vue
  74. 472 65
      fhKeeper/formulahousekeeper/timesheet/src/views/project/list.vue
  75. 2 2
      fhKeeper/formulahousekeeper/timesheet/src/views/project/projectInside.vue
  76. 7 7
      fhKeeper/formulahousekeeper/timesheet/src/views/settings/timetype.vue
  77. 171 9
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue
  78. 16 1
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/list.vue
  79. 354 0
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/list_department.vue
  80. 360 0
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/list_profession.vue
  81. 3 2
      fhKeeper/formulahousekeeper/timesheet_h5/src/main.js
  82. 14 0
      fhKeeper/formulahousekeeper/timesheet_h5/src/router/index.js
  83. 42 0
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/index.vue
  84. 116 67
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/index/index.vue
  85. 148 16
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/login/index.vue
  86. 26 13
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/my/children/center.vue
  87. 2 2
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/project/index.vue
  88. 217 0
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/review/department_list.vue
  89. 16 10
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/review/index.vue
  90. 218 0
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/review/profession_list.vue
  91. 6 0
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/view/index.vue

+ 108 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/AuthRedirectController.java

@@ -0,0 +1,108 @@
+package com.management.platform.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.management.platform.entity.Company;
+import com.management.platform.entity.Project;
+import com.management.platform.entity.SysConfig;
+import com.management.platform.entity.User;
+import com.management.platform.entity.vo.UserVO;
+import com.management.platform.mapper.CompanyMapper;
+import com.management.platform.mapper.SysConfigMapper;
+import com.management.platform.mapper.UserMapper;
+import com.management.platform.util.HttpRespMsg;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.*;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.client.RestTemplate;
+import org.springframework.web.servlet.ModelAndView;
+import org.springframework.web.servlet.view.RedirectView;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Controller
+public class AuthRedirectController {
+
+    @Value("${suitId}")
+    private String suitId;
+    @Value("${suitSecret}")
+    private String suitSecret;
+    @Value("${token}")
+    private String token;
+    @Value("${encodingAesKey}")
+    private String encodingAesKey;
+    @Value("${corpId}")
+    private String corpId;
+
+    @Resource
+    SysConfigMapper sysConfigMapper;
+    @Autowired
+    RestTemplate restTemplate;
+    @Resource
+    UserMapper userMapper;
+
+    @RequestMapping("/corpWXAuth")
+    public ModelAndView auth(String code, int state) {
+        Map<String,Object> reqParam = new HashMap<String,Object>(16);
+
+        String url = WeiXinCorpController.GET_CORP_USERINFO_URL.replace("SUITE_ACCESS_TOKEN", getSuiteAccessToken()).replace("CODE", code);
+        String forObject = this.restTemplate.getForObject(url, String.class);
+        JSONObject obj = JSONObject.parseObject(forObject);
+        String wxUserId = obj.getString("UserId");
+        List<User> userList = userMapper.selectList(new QueryWrapper<User>().eq("corpwx_userid", wxUserId));
+        if (userList.size() > 0) {
+            //该用户已存在
+            User curUser = userList.get(0);
+            reqParam.put("userId", curUser.getId());
+        }
+        reqParam.put("hasTriedAutoLogin", 1);
+        String redirecUrl = null;
+        if (state == 0) {
+            redirecUrl = "http://mobworktime.ttkuaiban.com";
+        } else if (state == 1) {
+            redirecUrl = "http://worktime.ttkuaiban.com";
+        }
+        ModelAndView modelAndView = new ModelAndView(
+                new RedirectView(redirecUrl), reqParam);
+
+        return modelAndView;
+    }
+
+    //获取第三方应用临时凭证
+    private String getSuiteAccessToken() {
+        if (WeiXinCorpController.SUITE_ACCESS_TOKEN == null || WeiXinCorpController.suiteTokenExpireTime < System.currentTimeMillis()) {
+            //失效了,需要重新获取
+            HttpHeaders headers = new HttpHeaders();
+            headers.setContentType(MediaType.APPLICATION_JSON);
+            JSONObject reqParam = new JSONObject();
+            reqParam.put("suite_id",  suitId);
+            reqParam.put("suite_secret", suitSecret);
+            SysConfig param = sysConfigMapper.selectOne(new QueryWrapper<SysConfig>().eq("param_key", "wx_suite_ticket"));
+            if (param != null) {
+                reqParam.put("suite_ticket",param.getParamValue());
+            }
+
+            HttpEntity<String> requestEntity = new HttpEntity<String>(reqParam.toJSONString(), headers);
+            ResponseEntity<String> responseEntity = this.restTemplate.exchange(WeiXinCorpController.GET_SUITE_ACCESS_TOKEN_URL,
+                    HttpMethod.POST, requestEntity, String.class);
+            if (responseEntity.getStatusCode() == HttpStatus.OK) {
+                String resp = responseEntity.getBody();
+                JSONObject obj = JSONObject.parseObject(resp);
+                if (obj.getIntValue("errcode") == 0) {
+                    WeiXinCorpController.SUITE_ACCESS_TOKEN = obj.getString("suite_access_token");
+                    WeiXinCorpController.suiteTokenExpireTime = System.currentTimeMillis() + obj.getIntValue("expires_in")*1000;
+                }
+            }
+        }
+        return WeiXinCorpController.SUITE_ACCESS_TOKEN;
+    }
+
+}

+ 21 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/PpMembsController.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-08-28
+ */
+@RestController
+@RequestMapping("/pp-membs")
+public class PpMembsController {
+
+}
+

+ 115 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProfessionController.java

@@ -0,0 +1,115 @@
+package com.management.platform.controller;
+
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.management.platform.entity.*;
+import com.management.platform.mapper.ProfessionMapper;
+import com.management.platform.mapper.ProjectProfessionMapper;
+import com.management.platform.mapper.ReportProfessionProgressMapper;
+import com.management.platform.mapper.UserMapper;
+import com.management.platform.util.HttpRespMsg;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <p>
+ *  前端控制器
+ * </p>
+ *
+ * @author Seyason
+ * @since 2021-08-28
+ */
+@RestController
+@RequestMapping("/profession")
+public class ProfessionController {
+    @Resource
+    HttpServletRequest request;
+    @Resource
+    UserMapper userMapper;
+    @Resource
+    ProfessionMapper professionMapper;
+    @Resource
+    ReportProfessionProgressMapper reportProfessionProgressMapper;
+    @Resource
+    ProjectProfessionMapper projectProfessionMapper;
+
+    @RequestMapping("/addOrMod")
+    public HttpRespMsg addOrMod(Profession info) {
+        HttpRespMsg msg = new HttpRespMsg();
+        String token = request.getHeader("TOKEN");
+        User user = userMapper.selectById(token);
+        if (info.getId() == null) {
+            info.setCompanyId(user.getCompanyId());
+            professionMapper.insert(info);
+        } else {
+            info.setCompanyId(user.getCompanyId());
+            professionMapper.updateById(info);
+            //更新项目专业表中的专业名称
+            ProjectProfession p = new ProjectProfession();
+            p.setProfessionName(info.getName());
+            projectProfessionMapper.update(p, new QueryWrapper<ProjectProfession>().eq("profession_id", info.getId()));
+        }
+        return msg;
+    }
+
+    @RequestMapping("/delete")
+    public HttpRespMsg delete(Integer id) {
+        HttpRespMsg msg = new HttpRespMsg();
+        String token = request.getHeader("TOKEN");
+        User user = userMapper.selectById(token);
+        //检查,该专业是否已经被日报使用
+        Integer cnt = reportProfessionProgressMapper.selectCount(new QueryWrapper<ReportProfessionProgress>().eq("profession_id", id));
+        if (cnt > 0) {
+            msg.setError("该专业已经存在相关日报,无法删除");
+        } else {
+            int r = professionMapper.delete(new QueryWrapper<Profession>().eq("id", id).eq("company_id", user.getCompanyId()));
+            if (r <= 0) {
+                msg.setError("无权删除");
+            }
+        }
+
+        return msg;
+    }
+
+    @RequestMapping("/list")
+    public HttpRespMsg list(@RequestParam Integer pageIndex, @RequestParam Integer pageSize, String keyword) {
+        HttpRespMsg msg = new HttpRespMsg();
+        String token = request.getHeader("TOKEN");
+        User user = userMapper.selectById(token);
+        QueryWrapper<Profession> queryWrapper = new QueryWrapper<Profession>().eq("company_id", user.getCompanyId()).orderByDesc("id");
+        if (!StringUtils.isEmpty(keyword)) {
+            queryWrapper.like("name", keyword);
+        }
+        IPage<Profession> projectIPage = professionMapper.selectPage(new Page<>(pageIndex, pageSize),
+                queryWrapper);
+        List<Profession> list = projectIPage.getRecords();
+        Long total = projectIPage.getTotal();
+        Map<String, Object> map = new HashMap<>();
+        map.put("records", list);
+        map.put("total", total);
+        msg.data = map;
+        return msg;
+    }
+
+    @RequestMapping("/getAll")
+    public HttpRespMsg getAll() {
+        HttpRespMsg msg = new HttpRespMsg();
+        String token = request.getHeader("TOKEN");
+        User user = userMapper.selectById(token);
+        List<Profession> all = professionMapper.selectList(new QueryWrapper<Profession>().eq("company_id", user.getCompanyId()));
+        msg.data = all;
+        return msg;
+    }
+}
+

+ 5 - 3
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectController.java

@@ -59,17 +59,19 @@ public class ProjectController {
      */
     @RequestMapping("/editProject")
     public HttpRespMsg editProject(Integer id, @RequestParam String name, String code, String[] userId, String inchargerId,
+                                   Integer isPublic,
                                    String planStartDate,
                                    String planEndDate,
                                    Integer level,
                                    Double contractAmount,
                                    Double budget,
                                    Integer customerId,
-                                   String projectBaseCostData
+                                   String projectBaseCostData,
+                                   String chosenLeaders
                                    ) {
-        return projectService.editProject(id, name, code, userId, inchargerId, planStartDate, planEndDate, level, contractAmount,
+        return projectService.editProject(id, name, code, userId, inchargerId, isPublic, planStartDate, planEndDate, level, contractAmount,
                 projectBaseCostData,
-                 budget,customerId,request);
+                 budget,customerId,chosenLeaders, request);
     }
 
     @RequestMapping("/adjustBase")

+ 61 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectNotifyUserController.java

@@ -0,0 +1,61 @@
+package com.management.platform.controller;
+
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.management.platform.entity.ProjectNotifyUser;
+import com.management.platform.service.ProjectNotifyUserService;
+import com.management.platform.util.HttpRespMsg;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+
+/**
+ * <p>
+ *  前端控制器
+ * </p>
+ *
+ * @author Seyason
+ * @since 2021-09-01
+ */
+@RestController
+@RequestMapping("/project-notify-user")
+public class ProjectNotifyUserController {
+    @Resource
+    ProjectNotifyUserService projectNotifyUserService;
+
+    @RequestMapping("/add")
+    public HttpRespMsg add(ProjectNotifyUser projectNotifyUser) {
+        HttpRespMsg msg = new HttpRespMsg();
+        int count = projectNotifyUserService.count(new QueryWrapper<ProjectNotifyUser>().eq("project_id", projectNotifyUser.getProjectId()).eq("user_id", projectNotifyUser.getUserId()));
+        if (count > 0) {
+            msg.setError("该用户已存在,无法重复添加");
+        } else {
+            projectNotifyUserService.save(projectNotifyUser);
+        }
+        return msg;
+
+    }
+
+    @RequestMapping("/delete")
+    public HttpRespMsg delete(Integer id) {
+        HttpRespMsg msg = new HttpRespMsg();
+        boolean success = projectNotifyUserService.removeById(id);
+        if (!success) {
+            msg.setError("记录不存在,操作失败");
+        }
+        return msg;
+    }
+
+    @RequestMapping("/get")
+    public HttpRespMsg get(Integer projectId) {
+        HttpRespMsg msg = new HttpRespMsg();
+        msg.data= projectNotifyUserService.list(new QueryWrapper<ProjectNotifyUser>().eq("project_id", projectId));
+        return msg;
+    }
+
+
+
+}
+

+ 57 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectProfessionController.java

@@ -0,0 +1,57 @@
+package com.management.platform.controller;
+
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.management.platform.entity.PpMembs;
+import com.management.platform.entity.ProjectProfession;
+import com.management.platform.mapper.ProjectProfessionMapper;
+import com.management.platform.service.ProjectProfessionService;
+import com.management.platform.util.HttpRespMsg;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <p>
+ *  前端控制器
+ * </p>
+ *
+ * @author Seyason
+ * @since 2021-08-28
+ */
+@RestController
+@RequestMapping("/project-profession")
+public class ProjectProfessionController {
+    @Resource
+    ProjectProfessionService projectProfessionService;
+    @RequestMapping("/modify")
+    public HttpRespMsg modify(Integer projectId, String json) {
+        projectProfessionService.modify(projectId, json);
+        return projectProfessionService.get(projectId);
+    }
+
+    //获取项目相关的工程专业列表
+    @RequestMapping("/get")
+    public HttpRespMsg get(Integer projectId) {
+        return projectProfessionService.get(projectId);
+    }
+
+    //获取项目相关的工程专业列表
+    @RequestMapping("/getMyProfession")
+    public HttpRespMsg getMyProfession(Integer projectId) {
+        return projectProfessionService.getMyProfession(projectId);
+    }
+
+    @RequestMapping("/getProgressData")
+    public HttpRespMsg getProgressData(Integer projectId) {
+        return projectProfessionService.getProgressData(projectId);
+    }
+
+}
+

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

@@ -2,8 +2,11 @@ package com.management.platform.controller;
 
 
 import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.github.pagehelper.util.StringUtil;
 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.service.ReportService;
@@ -83,6 +86,23 @@ public class ReportController {
         return reportService.getReport(date, request);
     }
 
+    private void fillReportProgress(Report report, String professionProgress) {
+        if (!StringUtil.isEmpty(professionProgress)) {
+            professionProgress = professionProgress.replaceAll("@",",");
+            System.out.println("JSON=="+professionProgress);
+            JSONArray array = JSONArray.parseArray(professionProgress);
+            List<ReportProfessionProgress> list = new ArrayList<ReportProfessionProgress>();
+            for(int i=0;i<array.size();i++) {
+                JSONObject jsonObject = array.getJSONObject(i);
+                ReportProfessionProgress item = new ReportProfessionProgress();
+                item.setProfessionId(jsonObject.getInteger("professionId"));
+                item.setProgress(jsonObject.getInteger("progress"));
+                item.setProjectId(report.getProjectId());
+                list.add(item);
+            }
+            report.setProfessionProgressList(list);
+        }
+    }
     /**
      * 新增或编辑报告
      * id 报告id 数组
@@ -104,12 +124,19 @@ public class ReportController {
                                   Integer[] taskId,
                                   Integer[] isOvertime,
                                   Integer[] progress,
-                                  String[] targetUids
+                                  String[] targetUids,
+                                  String[] professionProgress
                                     ) {
         List<Report> reportList = new ArrayList<>();
         String token = request.getHeader("Token");
         List<String> targetUidList = null;
         List<User> targetUserList = null;
+        if (professionProgress != null) {
+            System.out.println("professionProgress.length=="+professionProgress.length);
+            for (int i=0;i<professionProgress.length; i++) {
+                System.out.println(professionProgress[i]);
+            }
+        }
         //代填
         if (targetUids != null && targetUids.length > 0) {
             String val = targetUids[0];
@@ -258,6 +285,8 @@ public class ReportController {
                                     report.setWorkingTime(workingTime[i])
                                             .setCost(hourCost.multiply(new BigDecimal(workingTime[i])));
                                 }
+                                //项目专业的进展
+                                fillReportProgress(report, professionProgress[i]);
                                 reportList.add(report);
                             } else {
                                 //批量代填报的
@@ -304,6 +333,7 @@ public class ReportController {
                                         report.setWorkingTime(workingTime[i])
                                                 .setCost(subsUser.getCost().multiply(new BigDecimal(workingTime[i])));
                                     }
+                                    fillReportProgress(report, professionProgress[i]);
                                     reportList.add(report);
                                 }
                             }
@@ -361,6 +391,7 @@ public class ReportController {
                             report.setWorkingTime(workingTime[i])
                                     .setCost(hourCost.multiply(new BigDecimal(workingTime[i])));
                         }
+                        fillReportProgress(report, professionProgress[i]);
                         reportList.add(report);
                         /*后续需要加入状态*/
                         if (createDate[i] == null || projectId[i] == null) {
@@ -414,6 +445,7 @@ public class ReportController {
                                 report.setWorkingTime(workingTime[i])
                                         .setCost(subsUser.getCost().multiply(new BigDecimal(workingTime[i])));
                             }
+                            fillReportProgress(report, professionProgress[i]);
                             reportList.add(report);
                             /*后续需要加入状态*/
                             if (createDate[i] == null || projectId[i] == null) {
@@ -454,12 +486,41 @@ public class ReportController {
                                       Integer projectId,
                                       String date,
                                       HttpServletRequest request) {
-        System.out.println("departmentId========="+departmentId);
         return reportService.getListByState(state, departmentId,
                 projectId,
                 date,request);
     }
 
+    /**
+     * 获取专业待审核的报告
+     * @param state
+     * @param departmentId
+     * @param projectId
+     * @param date
+     * @param request
+     * @return
+     */
+    @RequestMapping("/listByStateProfession")
+    public HttpRespMsg listByStateProfession(@RequestParam Integer state,
+                                      Integer departmentId,
+                                      Integer projectId,
+                                      String date,
+                                      HttpServletRequest request) {
+        return reportService.listByStateProfession(state, departmentId,
+                projectId,
+                date,request);
+    }
+
+    @RequestMapping("/listByStateDepartment")
+    public HttpRespMsg listByStateDepartment(@RequestParam Integer state,
+                                             Integer projectId,
+                                             String date,
+                                             HttpServletRequest request) {
+        return reportService.listByStateDepartment(state,
+                projectId,
+                date,request);
+    }
+
     /**
      * 按某人某日期审批通过报告
      * id 要通过的报告的用户id
@@ -470,6 +531,7 @@ public class ReportController {
         return reportService.approveReport(id, reportIds, request);
     }
 
+
     /**
      * 按某人某日期审批未通过报告 撤销通过报告
      * id 要未通过的报告的用户id
@@ -491,23 +553,6 @@ public class ReportController {
         return reportService.cancelReport(userId, reportIds, request);
     }
 
-    /**
-     * 单条审批通过报告
-     * id 要通过的报告id
-     */
-    @RequestMapping("/singleApprove")
-    public HttpRespMsg singleApproveReport(@RequestParam Integer id, HttpServletRequest request) {
-        return reportService.singleApproveReport(id, request);
-    }
-
-    /**
-     * 单条审批未通过报告 撤销通过报告
-     * id 要未通过的报告id
-     */
-    @RequestMapping("/singleDeny")
-    public HttpRespMsg singleDenyReport(@RequestParam Integer id, HttpServletRequest request) {
-        return reportService.singleDenyReport(id, request);
-    }
 
     @RequestMapping("/getMembList")
     public HttpRespMsg getMembList(@RequestParam(required=false) String date, Integer manageDeptId, HttpServletRequest request) {
@@ -527,5 +572,15 @@ public class ReportController {
     public HttpRespMsg batchDenyReport(@RequestParam String ids, HttpServletRequest request) {
         return reportService.batchDenyReport(ids, request);
     }
+
+    @RequestMapping("/getUserDailyWorkTime")
+    public HttpRespMsg getUserDailyWorkTime(HttpServletRequest request, String month) {
+        return reportService.getUserDailyWorkTime(request, month);
+    }
+
+    @RequestMapping("/exportUserDailyWorkTime")
+    public HttpRespMsg exportUserDailyWorkTime(HttpServletRequest request, String month) {
+        return reportService.exportUserDailyWorkTime(request, month);
+    }
 }
 

+ 21 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportProfessionProgressController.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-08-29
+ */
+@RestController
+@RequestMapping("/report-profession-progress")
+public class ReportProfessionProgressController {
+
+}
+

+ 5 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserController.java

@@ -40,6 +40,11 @@ public class UserController {
         return userService.loginAdmin(username, password);
     }
 
+    @RequestMapping("/loginByUserId")
+    public HttpRespMsg loginByUserId(@RequestParam String userId) {
+        return userService.loginByUserId(userId);
+    }
+
     /**
      * 登录PC端
      * username 用户名

+ 226 - 115
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/WeiXinCorpController.java

@@ -4,13 +4,16 @@ import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.management.platform.entity.*;
+import com.management.platform.entity.vo.UserVO;
 import com.management.platform.mapper.*;
 import com.management.platform.service.DepartmentService;
+import com.management.platform.service.UserService;
 import com.management.platform.util.*;
 import com.qq.weixin.mp.aes.AesException;
 import com.qq.weixin.mp.aes.WXBizMsgCrypt;
 import lombok.extern.slf4j.Slf4j;
 import org.json.XML;
+import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.http.*;
@@ -20,6 +23,7 @@ import org.springframework.web.client.RestTemplate;
 import javax.annotation.Resource;
 import java.net.URLEncoder;
 import java.time.LocalDateTime;
+import java.time.ZoneOffset;
 import java.util.*;
 
 @RestController
@@ -62,6 +66,8 @@ public class WeiXinCorpController {
     @Resource
     CompanyMapper companyMapper;
     @Resource
+    ProjectMapper projectMapper;
+    @Resource
     ProjectBasecostSettingMapper projectBasecostSettingMapper;
 
     public static String SUITE_ACCESS_TOKEN = null;
@@ -88,6 +94,9 @@ public class WeiXinCorpController {
     DepartmentMapper departmentMapper;
     @Resource
     DepartmentService departmentService;
+    @Resource
+    UserService userService;
+
 
     //"获取企业微信jssdk初始化配置参数"
     @RequestMapping("/getCorpWXConfig")
@@ -276,7 +285,7 @@ public class WeiXinCorpController {
             if (jsonObject.has("AuthCode")) {
                 //企业授权通知
                 String authCode = jsonObject.getString("AuthCode");
-//                handleCorpAuth(authCode);
+                handleCorpAuth(authCode);
             } else if (jsonObject.has("SuiteTicket")) {
                 //ticket推送
                 String ticket = jsonObject.getString("SuiteTicket");
@@ -369,30 +378,25 @@ public class WeiXinCorpController {
                     data.setExpireTime(time);
                     data.setLocation(corpInfo.getString("location"));
                     data.setPermanentCode(permanentCode);
+                    JSONArray jsonArray = obj.getJSONObject("auth_info").getJSONArray("agent");
+                    for (int i=0;i<jsonArray.size(); i++) {
+                        JSONObject jsonObject = jsonArray.getJSONObject(i);
+                        if (jsonObject.getString("name").equals("工时管家")) {
+                            int agentId = jsonObject.getInteger("agentid");
+                            data.setAgentid(agentId);
+                        }
+                    }
+
                     //取到当前授权人,按照授权人与企业进行匹配
                     JSONObject authUserInfo = obj.getJSONObject("auth_user_info");
                     data.setAuthUsername(authUserInfo.getString("name"));
                     String userId = authUserInfo.getString("userid");//授权人的userid
 
-                    //先生成company
-                    //检查是否已经存在
-                    WxCorpInfo oldD = wxCorpInfoMapper.selectById(corpId);
+                    //检查公司名称是否存在
+                    List<Company> cpList = companyMapper.selectList(new QueryWrapper<Company>().eq("company_name", corpName).or().eq("company_name", data.getCorpFullName()));
                     Company company = null;
-                    if (oldD != null) {
-                        company = companyMapper.selectById(oldD.getCompanyId());
-                        if (!company.getExpirationDate().isAfter(LocalDateTime.now())) {
-                            //超期了,不处理
-                            return;
-                        }
-                    }
-                    if (company != null) {
-                        if (!company.getCompanyName().equals(corpName)) {
-                            company.setCompanyName(corpName);
-                            companyMapper.updateById(company);
-                        }
-                        //之前的部门和人员先删除
-                        departmentMapper.delete(new QueryWrapper<Department>().eq("company_id", company.getId()));
-                        userMapper.delete(new QueryWrapper<User>().eq("company_id", company.getId()));
+                    if (cpList.size() > 0) {
+                        company = cpList.get(0);
                     } else {
                         //首先生成一个新公司,增加会员的试用一个月
                         company = new Company().setCompanyName(corpName)
@@ -414,7 +418,14 @@ public class WeiXinCorpController {
                         timeType.setCompanyId(company.getId());
                         timeTypeMapper.insert(timeType);
 
-                        JSONObject userDetail = getUserInfo(curCorpAccessToken, userId);
+                    }
+                    data.setCompanyId(company.getId());
+                    wxCorpInfoMapper.insert(data);
+
+                    JSONObject userDetail = getUserInfo(curCorpAccessToken, userId);
+                    //检查授权人是否存在
+                    int cnt = userMapper.selectCount(new QueryWrapper<User>().eq("corpwx_userid", userId));
+                    if (cnt == 0) {
                         //创建企业负责人账号
                         log.info("===userDetail==" + userDetail.toJSONString());
                         Long id = SnowFlake.nextId();
@@ -422,105 +433,153 @@ public class WeiXinCorpController {
                                 .setId(id.toString())
                                 .setRole(1)
                                 .setName(data.getAuthUsername())
+                                .setName(userId)//使用wx userid作为姓名
+                                .setPassword(MD5Util.getPassword("000000"))
                                 .setCorpwxUserid(userId)
                                 .setColor(ColorUtil.randomColor())
                                 .setCompanyId(company.getId());
                         userMapper.insert(user);
                     }
-                    if (oldD == null) {
-                        data.setCompanyId(company.getId());
-                        wxCorpInfoMapper.insert(data);
-                    }
-                    int companyId = company.getId();
-                    //获取公司根部门人员,也就是没有分配部门的人员
-                    int companyRootDeptId = 1;
-                    JSONArray unAssignedUserList = getDeptUserSimple(curCorpAccessToken, companyRootDeptId);
-                    for (int m=0;m<unAssignedUserList.size(); m++) {
-                        JSONObject userJson = unAssignedUserList.getJSONObject(m);
-                        String curUserid = userJson.getString("userid");
-                        log.info("userid="+curUserid+", name=" + userJson.getString("name")+", mobile="+userJson.getString("mobile"));
-                        //不存在的人员, 进行插入
-                        User user = new User();
-
-                        user.setId(SnowFlake.nextId()+"")
-                                .setRole(0)//默认普通员工
-                                .setCompanyId(companyId)
-                                .setName(userJson.getString("name"))
-                                .setCorpwxUserid(curUserid)
-                                .setColor(ColorUtil.randomColor());
-
-                        //检查用户是否已经存在
-                        if (userMapper.selectCount(new QueryWrapper<User>().eq("corpwx_userid", curUserid)) == 0) {
-                            userMapper.insert(user);
-                        }
-                    }
+
+//                    int companyId = company.getId();
+//                    //获取公司根部门人员,也就是没有分配部门的人员
+//                    int companyRootDeptId = 1;
+//                    JSONArray unAssignedUserList = getDeptUserSimple(curCorpAccessToken, companyRootDeptId);
+//                    for (int m=0;m<unAssignedUserList.size(); m++) {
+//                        JSONObject userJson = unAssignedUserList.getJSONObject(m);
+//                        String curUserid = userJson.getString("userid");
+//                        log.info("userid="+curUserid+", name=" + userJson.getString("name")+", mobile="+userJson.getString("mobile"));
+//                        //不存在的人员, 进行插入
+//                        User user = new User();
+//
+//                        user.setId(SnowFlake.nextId()+"")
+//                                .setRole(0)//默认普通员工
+//                                .setCompanyId(companyId)
+//                                .setName(userJson.getString("name"))
+//                                .setCorpwxUserid(curUserid)
+//                                .setColor(ColorUtil.randomColor());
+//
+//                        //检查用户是否已经存在
+//                        if (userMapper.selectCount(new QueryWrapper<User>().eq("corpwx_userid", curUserid)) == 0) {
+//                            userMapper.insert(user);
+//                        }
+//                    }
 
                     //获取部门
-                    JSONObject deptObj = getDepartments(curCorpAccessToken);
-                    JSONArray deptObjJSONArray = deptObj.getJSONArray("department");
-
-                    List<Department> sysDeptList = new ArrayList<>();
-                    for (int i=0;i<deptObjJSONArray.size(); i++) {
-                        int deptId = deptObjJSONArray.getJSONObject(i).getIntValue("id");
-                        Department department = new Department();
-                        department.setDepartmentName(deptObjJSONArray.getJSONObject(i).getString("name"));
-                        department.setCompanyId(companyId);
-                        departmentMapper.insert(department);
-                        sysDeptList.add(department);
-                        deptObjJSONArray.getJSONObject(i).put("sys_dept_id", department.getDepartmentId());
-                        Integer departmentId = department.getDepartmentId();
-                        JSONArray userList = getDeptUserSimple(curCorpAccessToken, deptId);
-                        for (int m=0;m<userList.size(); m++) {
-                            JSONObject userJson = userList.getJSONObject(m);
-                            String curUserid = userJson.getString("userid");
-                            log.info("userid="+curUserid+", name=" + userJson.getString("name")+", mobile="+userJson.getString("mobile"));
-                            //不存在的人员, 进行插入
-                            User user = new User();
-
-                            user.setId(SnowFlake.nextId()+"")
-                                    .setRole(0)//默认普通员工
-                                    .setCompanyId(companyId)
-                                    .setDepartmentId(departmentId)
-                                    .setName(userJson.getString("name"))
-                                    .setCorpwxUserid(curUserid)
-                                    .setColor(ColorUtil.randomColor());
-
-                            //检查用户是否已经存在
-                            if (userMapper.selectCount(new QueryWrapper<User>().eq("corpwx_userid", curUserid)) == 0) {
-                                userMapper.insert(user);
-                            } else {
-                                //更新信息
-                                User oldUser = userMapper.selectList(new QueryWrapper<User>().eq("corpwx_userid", curUserid).eq("company_id", companyId).orderByDesc("create_time")).get(0);
-                                oldUser.setName(userJson.getString("name"));
-                                oldUser.setDepartmentId(departmentId);
-
-                                userMapper.updateById(oldUser);
-                            }
-                        }
-                    }
+//                    JSONObject deptObj = getDepartments(curCorpAccessToken);
+//                    JSONArray deptObjJSONArray = deptObj.getJSONArray("department");
+//
+//                    List<Department> sysDeptList = new ArrayList<>();
+//                    for (int i=0;i<deptObjJSONArray.size(); i++) {
+//                        int deptId = deptObjJSONArray.getJSONObject(i).getIntValue("id");
+//                        Department department = new Department();
+//                        department.setDepartmentName(deptObjJSONArray.getJSONObject(i).getString("name"));
+//                        department.setCompanyId(companyId);
+//                        departmentMapper.insert(department);
+//                        sysDeptList.add(department);
+//                        deptObjJSONArray.getJSONObject(i).put("sys_dept_id", department.getDepartmentId());
+//                        Integer departmentId = department.getDepartmentId();
+//                        JSONArray userList = getDeptUserSimple(curCorpAccessToken, deptId);
+//                        for (int m=0;m<userList.size(); m++) {
+//                            JSONObject userJson = userList.getJSONObject(m);
+//                            String curUserid = userJson.getString("userid");
+//                            log.info("userid="+curUserid+", name=" + userJson.getString("name")+", mobile="+userJson.getString("mobile"));
+//                            //不存在的人员, 进行插入
+//                            User user = new User();
+//
+//                            user.setId(SnowFlake.nextId()+"")
+//                                    .setRole(0)//默认普通员工
+//                                    .setCompanyId(companyId)
+//                                    .setDepartmentId(departmentId)
+//                                    .setName(userJson.getString("name"))
+//                                    .setCorpwxUserid(curUserid)
+//                                    .setColor(ColorUtil.randomColor());
+//
+//                            //检查用户是否已经存在
+//                            if (userMapper.selectCount(new QueryWrapper<User>().eq("corpwx_userid", curUserid)) == 0) {
+//                                userMapper.insert(user);
+//                            } else {
+//                                //更新信息
+//                                User oldUser = userMapper.selectList(new QueryWrapper<User>().eq("corpwx_userid", curUserid).eq("company_id", companyId).orderByDesc("create_time")).get(0);
+//                                oldUser.setName(userJson.getString("name"));
+//                                oldUser.setDepartmentId(departmentId);
+//
+//                                userMapper.updateById(oldUser);
+//                            }
+//                        }
+//                    }
 
                     //再来更新部门的层级关系
-                    List<Department> needUpdateDepts = new ArrayList<>();
-                    for (int i=0;i<deptObjJSONArray.size(); i++) {
-                        JSONObject deptJson = deptObjJSONArray.getJSONObject(i);
-                        int pid = deptJson.getInteger("parentid");
-                        if (pid != 1) {
-                            //根部门Id = 1
-                            int sysDeptId = deptJson.getInteger("sys_dept_id");
-                            Department department = sysDeptList.stream().filter(d -> d.getDepartmentId().equals(sysDeptId)).findFirst().get();
-                            //从deptjson数组中寻找parent item
-                            for (int m=0;m<deptObjJSONArray.size(); m++) {
-                                JSONObject item = deptObjJSONArray.getJSONObject(m);
-                                if (item.getInteger("id").equals(pid)) {
-                                    department.setSuperiorId(item.getInteger("sys_dept_id"));
-                                    break;
-                                }
-                            }
-                            needUpdateDepts.add(department);
+//                    List<Department> needUpdateDepts = new ArrayList<>();
+//                    for (int i=0;i<deptObjJSONArray.size(); i++) {
+//                        JSONObject deptJson = deptObjJSONArray.getJSONObject(i);
+//                        int pid = deptJson.getInteger("parentid");
+//                        if (pid != 1) {
+//                            //根部门Id = 1
+//                            int sysDeptId = deptJson.getInteger("sys_dept_id");
+//                            Department department = sysDeptList.stream().filter(d -> d.getDepartmentId().equals(sysDeptId)).findFirst().get();
+//                            //从deptjson数组中寻找parent item
+//                            for (int m=0;m<deptObjJSONArray.size(); m++) {
+//                                JSONObject item = deptObjJSONArray.getJSONObject(m);
+//                                if (item.getInteger("id").equals(pid)) {
+//                                    department.setSuperiorId(item.getInteger("sys_dept_id"));
+//                                    break;
+//                                }
+//                            }
+//                            needUpdateDepts.add(department);
+//                        }
+//                    }
+//                    if (needUpdateDepts.size() > 0) {
+//                        departmentService.updateBatchById(needUpdateDepts);
+//                    }
+                } else {
+                    //企业已经存在
+                    Integer companyId = data.getCompanyId();
+
+                    //更新企业信息
+                    data.setCorpid(corpId);
+                    String permanentCode = obj.getString("permanent_code");
+                    String curCorpAccessToken = obj.getString("access_token");
+                    LocalDateTime time = LocalDateTime.now();
+                    time = time.plusSeconds(obj.getLong("expires_in"));
+                    data.setAccessToken(curCorpAccessToken);
+                    data.setCorpFullName(corpInfo.getString("corp_full_name"));
+                    data.setCorpIndustry(corpInfo.getString("corp_industry"));
+                    data.setCorpName(corpInfo.getString("corp_name"));
+                    data.setCorpScale(corpInfo.getString("corp_scale"));
+                    data.setCorpSubIndustry(corpInfo.getString("corp_sub_industry"));
+                    data.setExpireTime(time);
+                    data.setLocation(corpInfo.getString("location"));
+                    data.setPermanentCode(permanentCode);
+                    JSONArray jsonArray = obj.getJSONObject("auth_info").getJSONArray("agent");
+                    for (int i=0;i<jsonArray.size(); i++) {
+                        JSONObject jsonObject = jsonArray.getJSONObject(i);
+                        if (jsonObject.getString("name").equals("工时管家")) {
+                            int agentId = jsonObject.getInteger("agentid");
+                            data.setAgentid(agentId);
                         }
                     }
-                    if (needUpdateDepts.size() > 0) {
-                        departmentService.updateBatchById(needUpdateDepts);
+                    wxCorpInfoMapper.updateById(data);
+
+                    //取到当前授权人,按照授权人与企业进行匹配
+                    JSONObject authUserInfo = obj.getJSONObject("auth_user_info");
+                    data.setAuthUsername(authUserInfo.getString("name"));
+                    String userId = authUserInfo.getString("userid");//授权人的userid
+                    //检查该授权人是否已经存在
+                    if (userMapper.selectCount(new QueryWrapper<User>().eq("corpwx_userid", userId)) == 0) {
+                        JSONObject userDetail = getUserInfo(curCorpAccessToken, userId);
+
+                        //授权人就当做是系统管理员吧
+                        log.info("===userDetail==" + userDetail.toJSONString());
+                        Long id = SnowFlake.nextId();
+                        User user = new User()
+                                .setId(id.toString())
+                                .setRole(0)
+                                .setName(data.getAuthUsername())
+                                .setCorpwxUserid(userId)
+                                .setColor(ColorUtil.randomColor())
+                                .setCompanyId(companyId);
+                        userMapper.insert(user);
                     }
                 }
             }
@@ -599,23 +658,75 @@ public class WeiXinCorpController {
     }
 
     @RequestMapping(value = "/bindCorpWeiXin", method = RequestMethod.GET)
-    public HttpRespMsg bindCorpWeiXin(String code, String token) {
+    public HttpRespMsg bindCorpWeiXin(String code, String userId) {
         HttpRespMsg msg = new HttpRespMsg();
-        User curUser = userMapper.selectOne(new QueryWrapper<User>().eq("head_imgurl", token));
+        User curUser = userMapper.selectById(userId);
         //https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token=ACCESS_TOKEN&code=CODE
-        int compId = curUser.getCompanyId();
-        WxCorpInfo wxCorpInfo = wxCorpInfoMapper.selectOne(new QueryWrapper<WxCorpInfo>().eq("company_id", compId));
 
         String url = GET_CORP_USERINFO_URL.replace("SUITE_ACCESS_TOKEN", getSuiteAccessToken()).replace("CODE", code);
         String forObject = this.restTemplate.getForObject(url, String.class);
         JSONObject obj = JSONObject.parseObject(forObject);
-        String userId = obj.getString("UserId");
+        String wxUserId = obj.getString("UserId");
         if (userId == null) {
             msg.setError("该用户企业未授权");
         } else {
-            curUser.setCorpwxUserid(userId);
+            curUser.setCorpwxUserid(wxUserId);
+
             userMapper.updateById(curUser);
-            msg.data = curUser;
+            Company company = companyMapper.selectOne(new QueryWrapper<Company>().eq("id", curUser.getCompanyId()));
+
+            //检测密码正确时
+            UserVO userVO = new UserVO().setCompanyName(company.getCompanyName());
+            userVO.setCompany(company);
+            BeanUtils.copyProperties(curUser, 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);
+            }
+            msg.data = userVO;
+
+        }
+        return msg;
+    }
+
+    //企业微信用户登录
+    @RequestMapping(value = "/corpWeiXinLogin", method = RequestMethod.GET)
+    public HttpRespMsg corpWeiXinLogin(String code) {
+        HttpRespMsg msg = new HttpRespMsg();
+
+        String url = GET_CORP_USERINFO_URL.replace("SUITE_ACCESS_TOKEN", getSuiteAccessToken()).replace("CODE", code);
+        String forObject = this.restTemplate.getForObject(url, String.class);
+        JSONObject obj = JSONObject.parseObject(forObject);
+        String wxUserId = obj.getString("UserId");
+        List<User> userList = userMapper.selectList(new QueryWrapper<User>().eq("corpwx_userid", wxUserId));
+        if (userList.size() > 0) {
+            //该用户已存在
+            User curUser = userList.get(0);
+            Company company = companyMapper.selectOne(new QueryWrapper<Company>().eq("id", curUser.getCompanyId()));
+
+            //检测密码正确时
+            UserVO userVO = new UserVO().setCompanyName(company.getCompanyName());
+            userVO.setCompany(company);
+            BeanUtils.copyProperties(curUser, 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);
+            }
+            msg.data = userVO;
+        } else {
+            msg.setError("该用户尚未绑定企业微信,需要通过账号密码登录");
         }
         return msg;
     }

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

@@ -16,7 +16,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2021-08-01
+ * @since 2021-08-28
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -97,6 +97,12 @@ public class Company extends Model<Company> {
     @TableField("package_customer")
     private Integer packageCustomer;
 
+    /**
+     * 工程专业
+     */
+    @TableField("package_engineering")
+    private Integer packageEngineering;
+
 
     @Override
     protected Serializable pkVal() {

+ 4 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/EarningSnapshot.java

@@ -8,9 +8,11 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import java.io.Serializable;
 import java.util.List;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
+import org.springframework.format.annotation.DateTimeFormat;
 
 /**
  * <p>
@@ -40,6 +42,8 @@ public class EarningSnapshot extends Model<EarningSnapshot> {
      * 发生时间
      */
     @TableField("indate")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd")
     private LocalDateTime indate;
 
     /**

+ 66 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/PpMembs.java

@@ -0,0 +1,66 @@
+package com.management.platform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+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-08-29
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class PpMembs extends Model<PpMembs> {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @TableField("pp_id")
+    private Integer ppId;
+
+    @TableField("project_id")
+    private Integer projectId;
+
+    @TableField("profession_id")
+    private Integer professionId;
+
+    @TableField("memb_id")
+    private String membId;
+
+    @TableField("memb_name")
+    private String membName;
+
+    /**
+     * 人员在本专业中的占比
+     */
+    @TableField("percentage")
+    private Integer percentage;
+
+    /**
+     * 进度%
+     */
+    @TableField("progress")
+    private Integer progress;
+
+
+    @TableField(exist = false)
+    private String professionName;
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

+ 42 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/Profession.java

@@ -0,0 +1,42 @@
+package com.management.platform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+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-08-28
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class Profession extends Model<Profession> {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @TableField("company_id")
+    private Integer companyId;
+
+    @TableField("name")
+    private String name;
+
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

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

@@ -21,7 +21,7 @@ import org.springframework.format.annotation.DateTimeFormat;
  * </p>
  *
  * @author Seyason
- * @since 2021-08-09
+ * @since 2021-09-13
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -209,6 +209,12 @@ public class Project extends Model<Project> {
     @TableField("customer_name")
     private String customerName;
 
+    /**
+     * 是否是公共项目
+     */
+    @TableField("is_public")
+    private Integer isPublic;
+
 
     @Override
     protected Serializable pkVal() {

+ 45 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ProjectNotifyUser.java

@@ -0,0 +1,45 @@
+package com.management.platform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+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-09-01
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class ProjectNotifyUser extends Model<ProjectNotifyUser> {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @TableField("project_id")
+    private Integer projectId;
+
+    @TableField("user_id")
+    private String userId;
+
+    @TableField("user_name")
+    private String userName;
+
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

+ 74 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ProjectProfession.java

@@ -0,0 +1,74 @@
+package com.management.platform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.extension.activerecord.Model;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableField;
+import java.io.Serializable;
+import java.util.List;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ * <p>
+ * 
+ * </p>
+ *
+ * @author Seyason
+ * @since 2021-08-28
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class ProjectProfession extends Model<ProjectProfession> {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @TableField("project_id")
+    private Integer projectId;
+
+    @TableField("profession_id")
+    private Integer professionId;
+
+    @TableField("profession_name")
+    private String professionName;
+
+    /**
+     * 专业负责人id
+     */
+    @TableField("incharger_id")
+    private String inchargerId;
+
+    /**
+     * 专业负责人姓名
+     */
+    @TableField("incharger_name")
+    private String inchargerName;
+
+    /**
+     * 专业占比
+     */
+    @TableField("percentage")
+    private Integer percentage;
+
+
+    @TableField(exist = false)
+    private String membListJson;
+
+    @TableField(exist = false)
+    private List<PpMembs> membList;
+
+    @TableField(exist = false)
+    private Integer progress;
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

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

@@ -20,7 +20,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2021-05-26
+ * @since 2021-08-31
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -136,6 +136,16 @@ public class Report extends Model<Report> {
     private List<SubProject> subProjectList;
     @TableField(exist = false)
     private List<UserRecentTask> taskList;
+    @TableField(exist = false)
+    private List<ReportProfessionProgress> professionProgressList;
+
+    /**
+     * 部门审核状态: -1 专业未审核,0-部门未审核,1-已通过,2-未通过
+     */
+    @TableField("department_audit_state")
+    private Integer departmentAuditState;
+
+
     @Override
     protected Serializable pkVal() {
         return this.id;

+ 57 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/ReportProfessionProgress.java

@@ -0,0 +1,57 @@
+package com.management.platform.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+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-08-31
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+public class ReportProfessionProgress extends Model<ReportProfessionProgress> {
+
+    private static final long serialVersionUID=1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @TableField("project_id")
+    private Integer projectId;
+
+    @TableField("report_id")
+    private Integer reportId;
+
+    @TableField("profession_id")
+    private Integer professionId;
+
+    @TableField("progress")
+    private Integer progress;
+
+    /**
+     * 0-待审核,1-审核通过,2-驳回
+     */
+    @TableField("audit_state")
+    private Integer auditState;
+
+
+    @TableField(exist = false)
+    private String professionName;
+
+    @Override
+    protected Serializable pkVal() {
+        return this.id;
+    }
+
+}

+ 4 - 4
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-05-31
+ * @since 2021-09-08
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -34,19 +34,19 @@ public class TimeType extends Model<TimeType> {
      * 全天时长
      */
     @TableField("allday")
-    private Integer allday;
+    private Float allday;
 
     /**
      * 上午时长
      */
     @TableField("am")
-    private Integer am;
+    private Float am;
 
     /**
      * 下午时长
      */
     @TableField("pm")
-    private Integer pm;
+    private Float pm;
 
     /**
      * 每月工作天数

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

@@ -15,7 +15,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2021-07-14
+ * @since 2021-09-05
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -75,6 +75,12 @@ public class WxCorpInfo extends Model<WxCorpInfo> {
     @TableField("company_id")
     private Integer companyId;
 
+    /**
+     * 授权企业内应用的id
+     */
+    @TableField("agentid")
+    private Integer agentid;
+
 
     @Override
     protected Serializable pkVal() {

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

@@ -0,0 +1,11 @@
+package com.management.platform.entity.vo;
+
+import java.util.List;
+import java.util.Map;
+
+public class UserMonthWork {
+    public String userId;
+    public String name;
+    public String departmentName;
+    public List<Map<String, Object>> worktimeList;
+}

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

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

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

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

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

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

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

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

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

@@ -30,6 +30,12 @@ public interface ReportMapper extends BaseMapper<Report> {
     //获取项目经理所管理的人员的报告
     List<Map<String, Object>> getInchargeReportByDate(@Param("date") String date, @Param("id") String id, @Param("state") Integer state);
 
+    //获取本人负责的专业的相关的日报
+    List<Map<String, Object>> getProfessionInchargeReportByDate(@Param("date") String date,
+                                                                @Param("id") String id,
+                                                                @Param("state") Integer state
+                                                                );
+
     List<Map<String, Object>> getUserReportByDate(@Param("date") String date, @Param("userIds") List<String> userIds);
 
     List<Map<String, Object>> getReportNameByDate(@Param("date") String date, @Param("companyId") Integer companyId, @Param("leaderId") String leaderId);
@@ -41,9 +47,20 @@ public interface ReportMapper extends BaseMapper<Report> {
 
     List<Map<String, Object>> getDetailByState(@Param("state") Integer state,
                                                @Param("companyId") Integer companyId,
-                                                @Param("leaderId") String leaderId);
+                                                @Param("leaderId") String leaderId,
+                                               @Param("isEngeering") Integer isEngeering
+                                                );
+
+    List<Map<String, Object>> getDetailByStateInMyProfession(@Param("state") Integer state,
+                                               @Param("companyId") Integer companyId,
+                                               @Param("leaderId") String leaderId);
 
     List<Map<String, Object>> getRealProjectTime(@Param("startDate") LocalDate startDate, @Param("endDate") LocalDate endDate, Integer companyId);
 
     List<Map<String, Object>> getReportFillStatus(String startDate, String endDate, String userId);
+
+    List<Map<String, Object>> getDepartmentDetailByState(@Param("departmentId") Integer departmentId,
+                                               @Param("companyId") Integer companyId);
+
+    List<Map<String, Object>> getUserDailyWorkTime(Integer companyId, String startDate, String endDate, List<Integer> deptIds, String leaderId);
 }

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

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

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

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

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

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

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

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

+ 24 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectProfessionService.java

@@ -0,0 +1,24 @@
+package com.management.platform.service;
+
+import com.management.platform.entity.ProjectProfession;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.management.platform.util.HttpRespMsg;
+
+/**
+ * <p>
+ *  服务类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2021-08-28
+ */
+public interface ProjectProfessionService extends IService<ProjectProfession> {
+
+    void modify(Integer projectId, String json);
+
+    HttpRespMsg get(Integer projectId);
+
+    HttpRespMsg getMyProfession(Integer projectId);
+
+    HttpRespMsg getProgressData(Integer projectId);
+}

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

@@ -22,6 +22,7 @@ public interface ProjectService extends IService<Project> {
     HttpRespMsg getProjectPage(Integer pageIndex, Integer pageSize, String keyword, Integer searchField, Integer status, HttpServletRequest request);
 
     HttpRespMsg editProject(Integer id, String name, String code, String[] userIds, String inchargerId,
+                            Integer isPublic,
                             String planStartDate,
                             String planEndDate,
                             Integer level,
@@ -29,6 +30,7 @@ public interface ProjectService extends IService<Project> {
                             String projectBaseCostData,
                             Double budget,
                             Integer customerId,
+                            String chosenLeaders,
                             HttpServletRequest request);
 
     HttpRespMsg deleteProject(Integer id);

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

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

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

@@ -49,4 +49,12 @@ public interface ReportService extends IService<Report> {
     HttpRespMsg cancelReport(String userId, String reportIds, HttpServletRequest request);
 
     HttpRespMsg getReportFillStatus(String startDate, String endDate, String userId, HttpServletRequest request);
+
+    HttpRespMsg listByStateProfession(Integer state, Integer departmentId, Integer projectId, String date, HttpServletRequest request);
+
+    HttpRespMsg listByStateDepartment(Integer state, Integer projectId, String date, HttpServletRequest request);
+
+    HttpRespMsg getUserDailyWorkTime(HttpServletRequest request, String month);
+
+    HttpRespMsg exportUserDailyWorkTime(HttpServletRequest request, String month);
 }

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

@@ -57,4 +57,8 @@ public interface UserService extends IService<User> {
     HttpRespMsg getHRList(HttpServletRequest request);
 
     HttpRespMsg changeSysManager(String toUserId, Integer myRoleId, HttpServletRequest request);
+
+    HttpRespMsg bindCorpWeiXin(String code, String userId);
+
+    HttpRespMsg loginByUserId(String userId);
 }

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

@@ -13,4 +13,5 @@ import com.baomidou.mybatisplus.extension.service.IService;
  */
 public interface WxCorpInfoService extends IService<WxCorpInfo> {
 
+    public void sendWXCorpMsg(WxCorpInfo corpInfo, String corpUserid, String msg);
 }

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

@@ -0,0 +1,20 @@
+package com.management.platform.service.impl;
+
+import com.management.platform.entity.PpMembs;
+import com.management.platform.mapper.PpMembsMapper;
+import com.management.platform.service.PpMembsService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ *  服务实现类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2021-08-28
+ */
+@Service
+public class PpMembsServiceImpl extends ServiceImpl<PpMembsMapper, PpMembs> implements PpMembsService {
+
+}

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

@@ -0,0 +1,20 @@
+package com.management.platform.service.impl;
+
+import com.management.platform.entity.Profession;
+import com.management.platform.mapper.ProfessionMapper;
+import com.management.platform.service.ProfessionService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ *  服务实现类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2021-08-28
+ */
+@Service
+public class ProfessionServiceImpl extends ServiceImpl<ProfessionMapper, Profession> implements ProfessionService {
+
+}

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

@@ -0,0 +1,20 @@
+package com.management.platform.service.impl;
+
+import com.management.platform.entity.ProjectNotifyUser;
+import com.management.platform.mapper.ProjectNotifyUserMapper;
+import com.management.platform.service.ProjectNotifyUserService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ *  服务实现类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2021-09-01
+ */
+@Service
+public class ProjectNotifyUserServiceImpl extends ServiceImpl<ProjectNotifyUserMapper, ProjectNotifyUser> implements ProjectNotifyUserService {
+
+}

+ 157 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectProfessionServiceImpl.java

@@ -0,0 +1,157 @@
+package com.management.platform.service.impl;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.management.platform.entity.PpMembs;
+import com.management.platform.entity.Profession;
+import com.management.platform.entity.ProjectProfession;
+import com.management.platform.entity.User;
+import com.management.platform.mapper.PpMembsMapper;
+import com.management.platform.mapper.ProfessionMapper;
+import com.management.platform.mapper.ProjectProfessionMapper;
+import com.management.platform.mapper.UserMapper;
+import com.management.platform.service.PpMembsService;
+import com.management.platform.service.ProjectProfessionService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.management.platform.util.HttpRespMsg;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * <p>
+ *  服务实现类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2021-08-28
+ */
+@Service
+@Transactional
+public class ProjectProfessionServiceImpl extends ServiceImpl<ProjectProfessionMapper, ProjectProfession> implements ProjectProfessionService {
+    @Resource
+    ProjectProfessionMapper projectProfessionMapper;
+    @Resource
+    UserMapper userMapper;
+    @Resource
+    PpMembsService ppMembsService;
+    @Resource
+    HttpServletRequest request;
+    @Resource
+    PpMembsMapper ppMembsMapper;
+    @Resource
+    ProfessionMapper professionMapper;
+
+
+    @Override
+    public void modify(Integer projectId, String json) {
+        System.out.println(json);
+        List<ProjectProfession> professionList = new ArrayList<>();
+        JSONArray array = JSONArray.parseArray(json);
+        for (int i=0;i<array.size(); i++) {
+            JSONObject jsonObject = array.getJSONObject(i);
+            ProjectProfession projectProfession = JSONObject.toJavaObject(jsonObject, ProjectProfession.class);
+            projectProfession.setProjectId(projectId);
+            professionList.add(projectProfession);
+        }
+//        //之前的数据都删掉
+        projectProfessionMapper.delete(new QueryWrapper<ProjectProfession>().eq("project_id", projectId));
+//        //插入新的数据
+        if (professionList.size() > 0) {
+            saveBatch(professionList);
+        }
+        ppMembsService.remove(new QueryWrapper<PpMembs>().eq("project_id", projectId));
+        //生成专业人员表数据
+        List<PpMembs> ppList = new ArrayList<>();
+        professionList.forEach(p->{
+            List<PpMembs> membList = p.getMembList();
+            for (int i=0;i<membList.size(); i++) {
+                PpMembs item = membList.get(i);
+                item.setPpId(p.getId());
+                item.setProjectId(projectId);
+                item.setProfessionId(p.getProfessionId());
+                ppList.add(item);
+            }
+        });
+        if (ppList.size() > 0) {
+            ppMembsService.saveBatch(ppList);
+        }
+    }
+
+    @Override
+    public HttpRespMsg get(Integer projectId) {
+        List<ProjectProfession> list = projectProfessionMapper.selectList(new QueryWrapper<ProjectProfession>().eq("project_id", projectId));
+        List<PpMembs> membsList = ppMembsService.list(new QueryWrapper<PpMembs>().eq("project_id", projectId));
+        list.forEach(p->{
+            p.setMembList(membsList.stream().filter(m -> m.getPpId().equals(p.getId())).collect(Collectors.toList()));
+            List<PpMembs> membList = p.getMembList();
+            //计算专业的进度
+            int totalProgress = 0;
+            for (int i = 0; i < membList.size(); i++) {
+                PpMembs m = membList.get(i);
+                totalProgress += m.getPercentage()*m.getProgress();
+            }
+            totalProgress = totalProgress/10000;
+            p.setProgress(totalProgress);
+        });
+        HttpRespMsg msg = new HttpRespMsg();
+        msg.data = list;
+        return msg;
+    }
+
+    @Override
+    public HttpRespMsg getMyProfession(Integer projectId) {
+        HttpRespMsg msg = new HttpRespMsg();
+        String token = request.getHeader("TOKEN");
+        List<PpMembs> membsList = ppMembsMapper.selectList(new QueryWrapper<PpMembs>().eq("project_id", projectId).eq("memb_id", token));
+        if (membsList.size() > 0) {
+            List<Integer> collect = membsList.stream().map(PpMembs::getProfessionId).collect(Collectors.toList());
+            List<Profession> professionList = professionMapper.selectList(new QueryWrapper<Profession>().in("id", collect));
+            membsList.stream().forEach(m->{
+                m.setProfessionName(professionList.stream().filter((p->p.getId().equals(m.getProfessionId()))).findFirst().get().getName());
+            });
+        }
+        msg.data = membsList;
+        return msg;
+    }
+
+    @Override
+    public HttpRespMsg getProgressData(Integer projectId) {
+        List<ProjectProfession> list = projectProfessionMapper.selectList(new QueryWrapper<ProjectProfession>().eq("project_id", projectId));
+        List<PpMembs> membsList = ppMembsService.list(new QueryWrapper<PpMembs>().eq("project_id", projectId));
+        int totalProjectProgress = 0;
+        for (ProjectProfession p: list) {
+            p.setMembList(membsList.stream().filter(m -> m.getPpId().equals(p.getId())).collect(Collectors.toList()));
+            List<PpMembs> membList = p.getMembList();
+            //计算专业的进度
+            int totalProgress = 0;
+            for (int i = 0; i < membList.size(); i++) {
+                PpMembs m = membList.get(i);
+                m.setProfessionName(m.getMembName());//前端会取professionName作为显示的列名称,此处进行设置
+                totalProgress += m.getPercentage()*m.getProgress();
+            }
+            totalProgress = totalProgress/100;
+            p.setProgress(totalProgress);
+            //计算总项目的进度
+            totalProjectProgress += totalProgress*p.getPercentage();
+        }
+        totalProjectProgress = totalProjectProgress/100;
+
+        HttpRespMsg msg = new HttpRespMsg();
+        HashMap<String, Object> map = new HashMap<>();
+        map.put("totalProjectProgress", totalProjectProgress);
+        map.put("professionList", list);
+        msg.data = map;
+        return msg;
+
+    }
+
+}

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

@@ -11,6 +11,7 @@ import com.management.platform.entity.vo.CustomerProject;
 import com.management.platform.entity.vo.GanttDataItem;
 import com.management.platform.entity.vo.ProjectVO;
 import com.management.platform.mapper.*;
+import com.management.platform.service.ProjectNotifyUserService;
 import com.management.platform.service.ProjectService;
 import com.management.platform.util.HttpRespMsg;
 import com.management.platform.util.ExcelUtil;
@@ -53,7 +54,7 @@ import java.util.stream.Collectors;
 public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> implements ProjectService {
     String pathPrefix = "/upload/";
     @Resource
-    private ProjectService projectService;
+    private ProjectNotifyUserService projectNotifyUserService;
     @Resource
     private ProjectMapper projectMapper;
     @Resource
@@ -77,6 +78,8 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
     @Resource
     ProjectBasecostMapper projectBasecostMapper;
     @Resource
+    CompanyMapper companyMapper;
+    @Resource
     private HttpServletResponse response;
 
     @Value(value = "${upload.path}")
@@ -123,7 +126,7 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                 }
                 final List<Integer> ids = projectIds;
                 queryWrapper = new QueryWrapper<Project>();
-                queryWrapper.and(wrapper->wrapper.in("id", ids).or().eq("creator_id", user.getId()));
+                queryWrapper.and(wrapper->wrapper.in("id", ids).or().eq("creator_id", user.getId()).or().eq("is_public", 1).eq("company_id", companyId));
             } else {
                 queryWrapper = new QueryWrapper<Project>().eq("company_id", companyId);
             }
@@ -172,6 +175,7 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
     //添加或编辑项目
     @Override
     public HttpRespMsg editProject(Integer id, String name, String code, String[] userIds, String inchargerId,
+                                   Integer isPublic,
                                    String planStartDate,
                                    String planEndDate,
                                    Integer level,
@@ -179,6 +183,7 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                                    String projectBaseCostData,
                                    Double budget,
                                    Integer customerId,
+                                   String chosenLeaders,
                                    HttpServletRequest request) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         User user = userMapper.selectById(request.getHeader("Token"));
@@ -198,6 +203,7 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                 } else {
                     Project project = new Project().setProjectName(name).setCompanyId(companyId).setProjectCode(code).setInchargerId(inchargerId)
                             .setLevel(level)
+                            .setIsPublic(isPublic)
                             .setCreatorId(user.getId())
                             .setCreatorName(user.getName())
                             .setCreateDate(LocalDate.now())
@@ -256,6 +262,7 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                 Project p = new Project();
                 p.setProjectName(name).setId(id).setCompanyId(companyId).setProjectCode(code).setInchargerId(inchargerId)
                         .setLevel(level)
+                        .setIsPublic(isPublic)
                         .setContractAmount(contractAmount)
                         .setBudget(budget)
                         .setCustomerId(customerId);
@@ -343,6 +350,23 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
                     participationMapper.insert(new Participation().setProjectId(id).setUserId(userId));
                 }
             }
+            //生成相关领导的表
+            if (companyMapper.selectById(companyId).getPackageEngineering() == 1) {
+                projectNotifyUserService.remove(new QueryWrapper<ProjectNotifyUser>().eq("project_id", id));
+
+                if (chosenLeaders != null && chosenLeaders.length() > 0) {
+                    JSONArray array = JSONArray.parseArray(chosenLeaders);
+                    List<ProjectNotifyUser> notifyUsers = new ArrayList<>();
+                    for (int i = 0;i<array.size(); i++) {
+                        JSONObject jsonObject = array.getJSONObject(i);
+                        ProjectNotifyUser projectNotifyUser = JSONObject.toJavaObject(jsonObject, ProjectNotifyUser.class);
+                        projectNotifyUser.setProjectId(id);
+                        notifyUsers.add(projectNotifyUser);
+                    }
+                    projectNotifyUserService.saveBatch(notifyUsers);
+                }
+            }
+            httpRespMsg.data = id;
         }
         return httpRespMsg;
     }

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

@@ -0,0 +1,20 @@
+package com.management.platform.service.impl;
+
+import com.management.platform.entity.ReportProfessionProgress;
+import com.management.platform.mapper.ReportProfessionProgressMapper;
+import com.management.platform.service.ReportProfessionProgressService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ *  服务实现类
+ * </p>
+ *
+ * @author Seyason
+ * @since 2021-08-29
+ */
+@Service
+public class ReportProfessionProgressServiceImpl extends ServiceImpl<ReportProfessionProgressMapper, ReportProfessionProgress> implements ReportProfessionProgressService {
+
+}

+ 677 - 24
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java

@@ -4,11 +4,20 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.management.platform.entity.*;
 import com.management.platform.entity.vo.DepartmentVO;
+import com.management.platform.entity.vo.UserMonthWork;
 import com.management.platform.mapper.*;
 import com.management.platform.service.DepartmentService;
+import com.management.platform.service.ReportProfessionProgressService;
 import com.management.platform.service.ReportService;
+import com.management.platform.service.WxCorpInfoService;
+import com.management.platform.util.ExcelUtil;
 import com.management.platform.util.HttpRespMsg;
 import com.management.platform.util.ListUtil;
+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.apache.poi.hssf.usermodel.*;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
@@ -26,6 +35,7 @@ import java.text.DecimalFormat;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
 import java.util.*;
@@ -42,6 +52,29 @@ import java.util.stream.Collectors;
 @Service
 public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> implements ReportService {
 
+    @Value("${wx.template_report_pass}")
+    public String TEMPLATE_REPORT_PASS;
+    @Value("${wx.app_id}")
+    public String appId;
+    @Value("${wx.app_secret}")
+    public String appSecret;
+
+    @Resource
+    WxCorpInfoService wxCorpInfoService;
+    @Resource
+    WxCorpInfoMapper wxCorpInfoMapper;
+    @Resource
+    private InformationServiceImpl informationService;
+    @Resource
+    private ProjectNotifyUserMapper projectNotifyUserMapper;
+    @Resource
+    private ProfessionMapper professionMapper;
+    @Resource
+    private ReportProfessionProgressService reportProfessionProgressService;
+    @Resource
+    private ProjectProfessionMapper projectProfessionMapper;
+    @Resource
+    private CompanyMapper companyMapper;
     @Resource
     private ReportMapper reportMapper;
     @Resource
@@ -64,6 +97,8 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     private TaskMapper taskMapper;
     @Resource
     private UserRecentTaskMapper userRecentTaskMapper;
+    @Resource
+    private PpMembsMapper ppMembsMapper;
 
     @Value(value = "${upload.path}")
     private String path;
@@ -173,9 +208,15 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                         //担任项目经理,查找相关的人员的日报
                         List<Map<String, Object>> puserNames = reportMapper.getReportNameByDate(date, user.getCompanyId(), leaderId);
                         List<Map<String, Object>> inchargeReportList= reportMapper.getInchargeReportByDate(date, leaderId, null);
-                        //先清空,防止自己填写的日报出现两次
-                        nameList = new ArrayList<>();
+
                         for (Map<String, Object> map2 : puserNames) {
+                            if (nameList.size() > 0) {
+                                String myUserId = (String)nameList.get(0).get("id");
+                                if (myUserId.equals(map2.get("id"))) {
+                                    //自己的报告,之前已经添加过了,排重
+                                    continue;
+                                }
+                            }
                             nameList.add(map2);
                             //再根据人分别获取当天的报告
                             List<Map<String, Object>> list2 =
@@ -243,7 +284,27 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                         }
                         DecimalFormat df = new DecimalFormat("0.00");
                         map.put("reportTime", df.format(reportTime));
+                    }
+                }
+            }
 
+            //处理项目专业进度的数据展示
+            if (companyMapper.selectById(user.getCompanyId()).getPackageEngineering() == 1) {
+                List<Profession> professions = professionMapper.selectList(new QueryWrapper<Profession>().eq("company_id", user.getCompanyId()));
+                if (professions.size() > 0) {
+                    List<Integer> collect = professions.stream().map(Profession::getId).collect(Collectors.toList());
+                    List<ReportProfessionProgress> allReportPPList = reportProfessionProgressService.list(new QueryWrapper<ReportProfessionProgress>().in("profession_id", collect));
+                    for (int i=0;i<nameList.size(); i++) {
+                        Map<String, Object> userItem = nameList.get(i);
+                        List<Map<String, Object>> data = (List<Map<String, Object>>)userItem.get("data");
+                        data.forEach(d->{
+                            int reportId = (int)d.get("id");
+                            List<ReportProfessionProgress> progressList = allReportPPList.stream().filter(a -> a.getReportId() == reportId).collect(Collectors.toList());
+                            progressList.forEach(p->{
+                                p.setProfessionName(professions.stream().filter(m->m.getId().equals(p.getProfessionId())).findFirst().get().getName());
+                            });
+                            d.put("professionProgress", progressList);
+                        });
                     }
                 }
             }
@@ -285,10 +346,18 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             List<SubProject> subProjectList = integerList.size() > 0?subProjectMapper.selectList(new QueryWrapper<SubProject>().in("project_id",integerList)):new ArrayList<>();
 
             List<UserRecentTask> taskList = integerList.size() > 0?userRecentTaskMapper.selectList(new QueryWrapper<UserRecentTask>().in("project_id", integerList).orderByDesc("id")):new ArrayList<>();
+            List<Profession> professions = professionMapper.selectList(new QueryWrapper<Profession>().eq("company_id", companyId));
+
             //获取当前项目的子项目列表,任务列表
             reports.forEach(r->{
                 r.setSubProjectList(subProjectList.stream().filter(s->s.getProjectId().equals(r.getProjectId())).collect(Collectors.toList()));
                 r.setTaskList(taskList.stream().filter(t->t.getProjectId().equals(r.getProjectId()) && t.getUserId().equals(r.getCreatorId())).collect(Collectors.toList()));
+                //获取当前项目的工程专业进度
+                List<ReportProfessionProgress> progressList = reportProfessionProgressService.list(new QueryWrapper<ReportProfessionProgress>().eq("report_id", r.getId()));
+                progressList.stream().forEach(p->{
+                    p.setProfessionName(professions.stream().filter(m->m.getId().equals(p.getProfessionId())).findFirst().get().getName());
+                });
+                r.setProfessionProgressList(progressList);
             });
             resultMap.put("report", reports);
             //顺便再获取一下可分配时间
@@ -391,6 +460,35 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                     .eq("creator_id", reportList.get(0).getCreatorId())
                     .notIn("id", idList));
         }
+
+        int enginerring = companyMapper.selectById(companyId).getPackageEngineering();
+        if (enginerring == 1) {
+            //生成项目专业进度数据,更新个人的项目专业进度
+            List<ReportProfessionProgress> allList = new ArrayList<>();
+            List<Integer> reportIds = new ArrayList<>();
+            List<Integer> skipProfession = new ArrayList<>();
+            for (Report report : reportList) {
+                reportIds.add(report.getId());
+                List<ReportProfessionProgress> professionProgressList = report.getProfessionProgressList();
+                if (professionProgressList.size() > 0) {
+                    professionProgressList.stream().forEach(p->p.setReportId(report.getId()));
+                    allList.addAll(professionProgressList);
+                } else {
+                    //没有相关的专业进度,直接进入部门审核
+                    skipProfession.add(report.getId());
+                }
+            }
+            reportProfessionProgressService.remove(new QueryWrapper<ReportProfessionProgress>().in("report_id", reportIds));
+            if (allList.size() > 0) {
+                reportProfessionProgressService.saveBatch(allList);
+            }
+            if (skipProfession.size() > 0) {
+                Report r = new Report();
+                r.setDepartmentAuditState(0);
+                reportMapper.update(r, new QueryWrapper<Report>().in("id", skipProfession));
+            }
+        }
+
         return httpRespMsg;
     }
 
@@ -398,8 +496,18 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     @Override
     public HttpRespMsg deleteReport(String userId, String date) {
         HttpRespMsg httpRespMsg = new HttpRespMsg();
+        Company company = companyMapper.selectById(userMapper.selectById(userId).getCompanyId());
         //某人删除自己某天的全部报告
-        if (reportMapper.delete(new QueryWrapper<Report>().eq("creator_id", userId).eq("create_date", date)) == 0) {
+        QueryWrapper<Report> queryWrapper = new QueryWrapper<Report>().eq("creator_id", userId).eq("create_date", date);
+        List<Report> reportList = reportMapper.selectList(queryWrapper);
+        List<Integer> collect = reportList.stream().map(Report::getId).collect(Collectors.toList());
+        //删除日报相关的专业进度
+        if (company.getPackageEngineering() == 1) {
+            if (collect.size() > 0) {
+                reportProfessionProgressService.remove(new QueryWrapper<ReportProfessionProgress>().in("report_id", collect));
+            }
+        }
+        if (reportMapper.delete(queryWrapper) == 0) {
             httpRespMsg.setError("操作失败");
         }
         return httpRespMsg;
@@ -415,12 +523,13 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
             User curUser = userMapper.selectById(request.getHeader("Token"));
             Integer companyId = curUser.getCompanyId();
+            Integer isEngeering = companyMapper.selectById(companyId).getPackageEngineering();
             String leaderId = null;
             if (curUser.getRole() == 0) {//普通员工
                 leaderId = curUser.getId();
             }
             //根据权限,管理员查看全部人员的,各个项目负责人只看自己负责的项目参与人员的日报
-            List<Map<String, Object>> nameList = reportMapper.getDetailByState(state, companyId, leaderId);
+            List<Map<String, Object>> nameList = reportMapper.getDetailByState(state, companyId, leaderId, isEngeering);
             //按部门过滤
             if (departmentId != null) {
                 nameList = nameList.stream().filter(map->((Long)map.get("departmentId")) == departmentId.longValue()).collect(Collectors.toList());
@@ -428,7 +537,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());
             }
@@ -437,7 +545,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             });
 
             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++) {
                 Map<String, Object> map2 = nameList.get(index);
                 java.sql.Date createDate = (java.sql.Date)map2.get("date");
@@ -460,6 +568,15 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                     nameList.remove(index);
                     index--;
                 } else {
+                    //项目专业进度
+                    for (Map<String, Object> map : list2) {
+                        //获取当前项目的工程专业进度
+                        List<ReportProfessionProgress> progressList = reportProfessionProgressService.list(new QueryWrapper<ReportProfessionProgress>().eq("report_id", (int)map.get("id")));
+                        progressList.stream().forEach(p->{
+                            p.setProfessionName(professions.stream().filter(m->m.getId().equals(p.getProfessionId())).findFirst().get().getName());
+                        });
+                        map.put("professionProgressList", progressList);
+                    }
                     map2.put("data", list2);
                     double reportTime = 0;
                     BigDecimal total = new BigDecimal(0);
@@ -488,8 +605,67 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         HttpRespMsg httpRespMsg = new HttpRespMsg();
         try {
             User user = userMapper.selectById(request.getHeader("Token"));
-            reportMapper.update(new Report().setState(1),
-                    new QueryWrapper<Report>().in("id", ListUtil.convertIdsArrayToList(reportIds)));
+            Company company = companyMapper.selectById(user.getCompanyId());
+            final List<Long> ids = ListUtil.convertIdsArrayToList(reportIds);
+            if (company.getPackageEngineering() == 1) {
+                //检查是否有专业进度待审核
+                List<ReportProfessionProgress> list = reportProfessionProgressService.list(new QueryWrapper<ReportProfessionProgress>().eq("report_id", ids.get(0)).eq("audit_state", 0));
+                if (list.size() > 0) {
+                    //专业待审核的状态,进行专业审核
+                    List<Report> reportList = reportMapper.selectList(new QueryWrapper<Report>().in("id", ids));
+                    List<Integer> projectIds = reportList.stream().map(Report::getProjectId).collect(Collectors.toList());
+
+                    List<ProjectProfession> myProfessionList = projectProfessionMapper.selectList(new QueryWrapper<ProjectProfession>().eq("incharger_id", user.getId()).in("project_id", projectIds));
+                    if (myProfessionList.size() == 0) {
+                        httpRespMsg.setError("只有专业负责人才能进行专业审核");
+                    } else {
+                        List<Integer> collect = myProfessionList.stream().map(ProjectProfession::getProfessionId).collect(Collectors.toList());
+
+                        ReportProfessionProgress item = new ReportProfessionProgress();
+                        item.setAuditState(1);
+                        reportProfessionProgressService.update(item, new QueryWrapper<ReportProfessionProgress>().in("report_id", ids).in("profession_id", collect));
+
+                        //全部的专业都审核通过的情况下,更新部门待审核状态
+                        int count = reportProfessionProgressService.count(new QueryWrapper<ReportProfessionProgress>().in("report_id", ids).ne("audit_state", 1));
+                        if (count == 0) {
+                            Report report = new Report();
+                            report.setDepartmentAuditState(0);
+                            reportMapper.update(report, new QueryWrapper<Report>().in("id", ids));
+                        }
+                    }
+                } else {
+                    Report report = reportMapper.selectById(ids.get(0));
+                    //部门待审核,部门审核通过
+                    if (report.getDepartmentAuditState() == 0) {
+                        report = new Report();
+                        report.setDepartmentAuditState(1);
+                        reportMapper.update(report, new QueryWrapper<Report>().in("id", ids));
+                    } else {
+                        //最终进行项目经理审核
+                        reportMapper.update(new Report().setState(1),
+                                new QueryWrapper<Report>().in("id", ids));
+                        List<Report> finalReportList = reportMapper.selectList(new QueryWrapper<Report>().in("id", ids));
+                        List<ReportProfessionProgress> professionProgressList = reportProfessionProgressService.list(new QueryWrapper<ReportProfessionProgress>().in("report_id", ids));
+
+                        //审核通过时,才会更新到个人的专业进度上去
+                        professionProgressList.forEach(pro->{
+                            PpMembs memb = new PpMembs();
+                            memb.setProgress(pro.getProgress());
+                            Report report1 = finalReportList.stream().filter(f -> f.getId().equals(pro.getReportId())).findFirst().get();
+                            Integer projectId = report1.getProjectId();
+                            //更新个人在项目中的专业进度
+                            ppMembsMapper.update(memb, new QueryWrapper<PpMembs>().eq("project_id", projectId)
+                                    .eq("memb_id", report1.getCreatorId()).eq("profession_id", pro.getProfessionId()));
+                        });
+                        notifyLeaders(finalReportList);
+                    }
+                }
+            } else {
+                //直接进行项目经理审核
+                reportMapper.update(new Report().setState(1),
+                        new QueryWrapper<Report>().in("id", ids));
+            }
+            
         } catch (NullPointerException e) {
             httpRespMsg.setError("验证失败");
             return httpRespMsg;
@@ -497,15 +673,130 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         return httpRespMsg;
     }
 
+    //审核通过需要通知相关领导
+    private void notifyLeaders(List<Report> reportList) {
+        List<Integer> collect = reportList.stream().map(Report::getProjectId).collect(Collectors.toList());
+        List<Project> projectList = projectMapper.selectList(new QueryWrapper<Project>().in("id", collect));
+        List<ProjectNotifyUser> notifyUsers = projectNotifyUserMapper.selectList(new QueryWrapper<ProjectNotifyUser>().in("project_id", collect));
+        //没有设置相关领导
+        if (notifyUsers.size() == 0) {
+            return;
+        }
+        DateTimeFormatter dft = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+        List<Information> informationList = new ArrayList<>();
+        List<String> userIdList = new ArrayList<>();
+        notifyUsers.forEach(n->{
+            Information information = new Information();
+            information.setUserId(n.getUserId());
+            information.setType(3);
+            String projectName = projectList.stream().filter(p->p.getId().equals(n.getProjectId())).findFirst().get().getProjectName();
+            LocalDate createDate = reportList.stream().filter(r -> r.getProjectId().equals(n.getProjectId())).findFirst().get().getCreateDate();
+            String format = dft.format(createDate);
+            information.setContent(format);//日报的日期
+            information.setMsg(projectName+"项目,日报审批通过");
+            informationList.add(information);
+
+            userIdList.add(n.getUserId());
+
+        });
+        informationService.saveBatch(informationList);
+
+        //微信推送通知
+        List<User> userList = userMapper.selectList(new QueryWrapper<User>().in("id", userIdList));
+        List<WxCorpInfo> companyWXList = wxCorpInfoMapper.selectList(new QueryWrapper<WxCorpInfo>().eq("company_id", projectList.get(0).getCompanyId()));
+        final WxCorpInfo wxCorpInfo = companyWXList.size()>0?companyWXList.get(0):null;
+
+        projectList.stream().forEach(p->{
+            final String msg = p.getProjectName() + "项目,日报审核通过";
+            notifyUsers.stream().filter(n -> n.getProjectId().equals(p.getId()))
+                    .forEach(m->{
+                        String uid = m.getUserId();
+                        User user = userList.stream().filter(u -> u.getId().equals(uid)).findFirst().get();
+                        //优先企业微信推送消息
+                        if (wxCorpInfo != null && user.getCorpwxUserid() != null) {
+                            wxCorpInfoService.sendWXCorpMsg(wxCorpInfo, user.getCorpwxUserid(), msg);
+                        } else if (user.getWxOpenid() != null){
+                            push(p.getProjectName(), user);
+                        }
+                    });
+        });
+    }
+
+
+    public boolean push(String projectName, User 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(user.getWxOpenid())//要推送的用户openid
+                .templateId(TEMPLATE_REPORT_PASS)//模版id
+                .url("http://mobworktime.ttkuaiban.com/")//点击模版消息要访问的网址
+                .build();
+        //3,如果是正式版发送模版消息,这里需要配置你的信息
+        templateMessage.addData(new WxMpTemplateData("first", projectName + "项目,日报审核通过", "#FF00FF"));
+        templateMessage.addData(new WxMpTemplateData("keyword1", "工时报告审核", "#000000"));
+        templateMessage.addData(new WxMpTemplateData("keyword2", "审核通过", "#000000"));
+        templateMessage.addData(new WxMpTemplateData("keyword3", DateTimeFormatter.ofPattern("MM-dd HH:mm").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;
+    }
+
     //审核未通过 以及 撤销审核某天某人
     @Override
     public HttpRespMsg denyReport(String id, String date, String reportIds, 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 (company.getPackageEngineering() == 1) {
+                //检查是否有专业进度待审核
+                List<ReportProfessionProgress> list = reportProfessionProgressService.list(new QueryWrapper<ReportProfessionProgress>().eq("report_id", ids.get(0)).eq("audit_state", 0));
+
+                if (list.size() > 0) {
+                    //只能是自己负责的专业
+                    List<Report> reportList = reportMapper.selectList(new QueryWrapper<Report>().in("id", ids));
+                    List<Integer> projectIds = reportList.stream().map(Report::getProjectId).collect(Collectors.toList());
+
+                    List<ProjectProfession> myProfessionList = projectProfessionMapper.selectList(new QueryWrapper<ProjectProfession>().eq("incharger_id", user.getId()).in("project_id", projectIds));
+                    if (myProfessionList.size() == 0) {
+                        httpRespMsg.setError("只有专业负责人才能进行专业审核");
+                    } else {
+                        List<Integer> collect = myProfessionList.stream().map(ProjectProfession::getProfessionId).collect(Collectors.toList());
+                        //专业待审核的状态,进行专业审核驳回
+                        ReportProfessionProgress item = new ReportProfessionProgress();
+                        item.setAuditState(2);
+                        reportProfessionProgressService.update(item, new QueryWrapper<ReportProfessionProgress>().in("report_id", ids).in("profession_id", collect));
+                    }
+                } else {
+                    Report report = reportMapper.selectById(ids.get(0));
+                    //部门待审核,部门审核驳回
+                    if (report.getDepartmentAuditState() == 0) {
+                        report = new Report();
+                        report.setDepartmentAuditState(2);
+                        reportMapper.update(report, new QueryWrapper<Report>().in("id", ids));
+                    }
+                }
+            }
+            //直接进行项目经理审核驳回
             reportMapper.update(new Report().setState(2),
                     new QueryWrapper<Report>().in("id", ListUtil.convertIdsArrayToList(reportIds)));
             informationMapper.insert(new Information().setType(0).setContent(date).setUserId(id));
+
         } catch (NullPointerException e) {
             httpRespMsg.setError("验证失败");
             return httpRespMsg;
@@ -616,28 +907,102 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
-    public HttpRespMsg batchApproveReport(String ids, HttpServletRequest request) {
-        Report report = new Report();
-        report.setState(1);
-        int num = reportMapper.update(report, new QueryWrapper<Report>().in("id", ListUtil.convertIdsArrayToList(ids)));
-        HttpRespMsg msg = new HttpRespMsg();
-        if (num <= 0) {
-            msg.setError("无数据更新");
+    public HttpRespMsg batchApproveReport(String reportIds, HttpServletRequest request) {
+        User user = userMapper.selectById(request.getHeader("Token"));
+        Company company = companyMapper.selectById(user.getCompanyId());
+        HttpRespMsg httpRespMsg = new HttpRespMsg();
+        final List<Long> ids = ListUtil.convertIdsArrayToList(reportIds);
+        if (company.getPackageEngineering() == 1) {
+            //检查是否有专业进度待审核
+            List<ReportProfessionProgress> list = reportProfessionProgressService.list(new QueryWrapper<ReportProfessionProgress>().eq("report_id", ids.get(0)).eq("audit_state", 0));
+            if (list.size() > 0) {
+                //专业待审核的状态,进行专业审核
+                List<Report> reportList = reportMapper.selectList(new QueryWrapper<Report>().in("id", ids));
+                List<Integer> projectIds = reportList.stream().map(Report::getProjectId).collect(Collectors.toList());
+
+                List<ProjectProfession> myProfessionList = projectProfessionMapper.selectList(new QueryWrapper<ProjectProfession>().eq("incharger_id", user.getId()).in("project_id", projectIds));
+                if (myProfessionList.size() == 0) {
+                    httpRespMsg.setError("只能专业负责人才能进行专业审核");
+                } else {
+                    List<Integer> collect = myProfessionList.stream().map(ProjectProfession::getProfessionId).collect(Collectors.toList());
+
+                    ReportProfessionProgress item = new ReportProfessionProgress();
+                    item.setAuditState(1);
+                    reportProfessionProgressService.update(item, new QueryWrapper<ReportProfessionProgress>().in("report_id", ids).in("profession_id", collect));
+
+                    //全部的专业都审核通过的情况下,更新部门待审核状态
+                    int count = reportProfessionProgressService.count(new QueryWrapper<ReportProfessionProgress>().in("report_id", ids).ne("audit_state", 1));
+                    if (count == 0) {
+                        Report report = new Report();
+                        report.setDepartmentAuditState(0);
+                        reportMapper.update(report, new QueryWrapper<Report>().in("id", ids));
+                    }
+                }
+            } else {
+                Report report = reportMapper.selectById(ids.get(0));
+                //部门待审核,部门审核通过
+                if (report.getDepartmentAuditState() == 0) {
+                    report = new Report();
+                    report.setDepartmentAuditState(1);
+                    reportMapper.update(report, new QueryWrapper<Report>().in("id", ids));
+                } else {
+                    //直接进行项目经理审核
+                    reportMapper.update(new Report().setState(1),
+                            new QueryWrapper<Report>().in("id", ids));
+                    List<Report> finalReportList = reportMapper.selectList(new QueryWrapper<Report>().in("id", ids));
+                    notifyLeaders(finalReportList);
+                }
+            }
+        } else {
+            //直接进行项目经理审核
+            reportMapper.update(new Report().setState(1),
+                    new QueryWrapper<Report>().in("id", ids));
         }
-        return msg;
+        return httpRespMsg;
     }
 
 
     @Override
-    public HttpRespMsg batchDenyReport(String ids, HttpServletRequest request) {
-        Report report = new Report();
-        report.setState(2);
-        int num = reportMapper.update(report, new QueryWrapper<Report>().in("id", ListUtil.convertIdsArrayToList(ids)));
-        HttpRespMsg msg = new HttpRespMsg();
-        if (num <= 0) {
-            msg.setError("无数据更新");
+    public HttpRespMsg batchDenyReport(String reportIds, HttpServletRequest request) {
+        User user = userMapper.selectById(request.getHeader("Token"));
+        Company company = companyMapper.selectById(user.getCompanyId());
+        final List<Long> ids = ListUtil.convertIdsArrayToList(reportIds);
+        HttpRespMsg httpRespMsg = new HttpRespMsg();
+        if (company.getPackageEngineering() == 1) {
+            //检查是否有专业进度待审核
+            List<ReportProfessionProgress> list = reportProfessionProgressService.list(new QueryWrapper<ReportProfessionProgress>().eq("report_id", ids.get(0)).eq("audit_state", 0));
+
+            if (list.size() > 0) {
+                //只能是自己负责的专业
+                List<Report> reportList = reportMapper.selectList(new QueryWrapper<Report>().in("id", ids));
+                List<Integer> projectIds = reportList.stream().map(Report::getProjectId).collect(Collectors.toList());
+
+                List<ProjectProfession> myProfessionList = projectProfessionMapper.selectList(new QueryWrapper<ProjectProfession>().eq("incharger_id", user.getId()).in("project_id", projectIds));
+                if (myProfessionList.size() == 0) {
+                    httpRespMsg.setError("只能专业负责人才能进行专业审核");
+                } else {
+                    List<Integer> collect = myProfessionList.stream().map(ProjectProfession::getProfessionId).collect(Collectors.toList());
+                    //专业待审核的状态,进行专业审核驳回
+                    ReportProfessionProgress item = new ReportProfessionProgress();
+                    item.setAuditState(2);
+                    reportProfessionProgressService.update(item, new QueryWrapper<ReportProfessionProgress>().in("report_id", ids).in("profession_id", collect));
+                }
+
+            } else {
+                Report report = reportMapper.selectById(ids.get(0));
+                //部门待审核,部门审核驳回
+                if (report.getDepartmentAuditState() == 0) {
+                    report = new Report();
+                    report.setDepartmentAuditState(2);
+                    reportMapper.update(report, new QueryWrapper<Report>().in("id", ids));
+                }
+            }
         }
-        return msg;
+        //直接进行项目经理审核驳回
+        reportMapper.update(new Report().setState(2),
+                new QueryWrapper<Report>().in("id", ListUtil.convertIdsArrayToList(reportIds)));
+
+        return httpRespMsg;
     }
 
     @Override
@@ -673,6 +1038,294 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
         return msg;
     }
 
+    @Override
+    public HttpRespMsg listByStateProfession(Integer state, Integer departmentId, Integer projectId, String date, HttpServletRequest request) {
+        HttpRespMsg httpRespMsg = new HttpRespMsg();
+        try {
+            SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
+            User curUser = userMapper.selectById(request.getHeader("Token"));
+            Integer companyId = curUser.getCompanyId();
+            String leaderId = curUser.getId();
+
+            //项目的专业负责人进行专业的审核
+            List<Map<String, Object>> nameList = reportMapper.getDetailByStateInMyProfession(state, companyId, leaderId);
+            //按部门过滤
+            if (departmentId != null) {
+                nameList = nameList.stream().filter(map->((Long)map.get("departmentId")) == departmentId.longValue()).collect(Collectors.toList());
+            }
+            //按日期过滤
+            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());
+            }
+            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++) {
+                Map<String, Object> map2 = nameList.get(index);
+                java.sql.Date createDate = (java.sql.Date)map2.get("date");
+                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());
+                }
+
+                if (list2.size() == 0) {
+                    //被项目过滤掉了,没有数据。
+                    nameList.remove(index);
+                    index--;
+                } else {
+                    //项目专业进度
+                    for (Map<String, Object> map : list2) {
+                        //获取当前项目的工程专业进度
+                        List<ReportProfessionProgress> progressList = reportProfessionProgressService.list(new QueryWrapper<ReportProfessionProgress>().eq("report_id", (int)map.get("id")));
+                        progressList.stream().forEach(p->{
+                            p.setProfessionName(professions.stream().filter(m->m.getId().equals(p.getProfessionId())).findFirst().get().getName());
+                        });
+                        map.put("professionProgressList", progressList);
+                    }
+
+                    map2.put("data", list2);
+                    double reportTime = 0;
+                    BigDecimal total = new BigDecimal(0);
+                    for (Map<String, Object> m : list2) {
+                        double t = (double) m.get("time");
+                        reportTime += t;
+                        total = total.add((BigDecimal)m.get("cost"));
+                    }
+                    DecimalFormat df = new DecimalFormat("0.00");
+                    map2.put("reportTime", df.format(reportTime));
+                    map2.put("cost", total);
+                    map2.put("state", list2.get(0).get("state"));
+                }
+            }
+            httpRespMsg.data = nameList;
+        } catch (NullPointerException e) {
+            httpRespMsg.setError("验证失败");
+            return httpRespMsg;
+        }
+        return httpRespMsg;
+    }
+
+    @Override
+    public HttpRespMsg listByStateDepartment(Integer state, Integer projectId, String date, HttpServletRequest request) {
+        HttpRespMsg httpRespMsg = new HttpRespMsg();
+        try {
+            SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
+            User curUser = userMapper.selectById(request.getHeader("Token"));
+            Integer departmentId = curUser.getManageDeptId();
+            Integer companyId = curUser.getCompanyId();
+
+            //根据权限,管理员查看全部人员的,各个项目负责人只看自己负责的项目参与人员的日报
+            List<Map<String, Object>> nameList = reportMapper.getDepartmentDetailByState(departmentId, companyId);
+
+            //按日期过滤
+            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());
+            }
+            nameList.forEach(n->{
+                n.put("dateStr",sdf.format((java.sql.Date)n.get("date")));
+            });
+
+            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++) {
+                Map<String, Object> map2 = nameList.get(index);
+                java.sql.Date createDate = (java.sql.Date)map2.get("date");
+                List<Map<String, Object>> list2 = null;
+                list2 = reportMapper.getReportByDate(createDate.toString(), (String)map2.get("id"));
+                //按项目过滤
+                if (projectId != null) {
+                    list2 = list2.stream().filter(report->(((Integer)report.get("projectId")).equals(projectId))).collect(Collectors.toList());
+                }
+                if (list2.size() == 0) {
+                    //被项目过滤掉了,没有数据。
+                    nameList.remove(index);
+                    index--;
+                } else {
+                    //项目专业进度
+                    for (Map<String, Object> map : list2) {
+                        //获取当前项目的工程专业进度
+                        List<ReportProfessionProgress> progressList = reportProfessionProgressService.list(new QueryWrapper<ReportProfessionProgress>().eq("report_id", (int)map.get("id")));
+                        progressList.stream().forEach(p->{
+                            p.setProfessionName(professions.stream().filter(m->m.getId().equals(p.getProfessionId())).findFirst().get().getName());
+                        });
+                        map.put("professionProgressList", progressList);
+                    }
+
+                    map2.put("data", list2);
+                    double reportTime = 0;
+                    BigDecimal total = new BigDecimal(0);
+                    for (Map<String, Object> m : list2) {
+                        double t = (double) m.get("time");
+                        reportTime += t;
+                        total = total.add((BigDecimal)m.get("cost"));
+                    }
+                    DecimalFormat df = new DecimalFormat("0.00");
+                    map2.put("reportTime", df.format(reportTime));
+                    map2.put("cost", total);
+                    map2.put("state", list2.get(0).get("state"));
+                }
+            }
+            httpRespMsg.data = nameList;
+        } catch (NullPointerException e) {
+            httpRespMsg.setError("验证失败");
+            return httpRespMsg;
+        }
+        return httpRespMsg;
+    }
+
+    @Override
+    public HttpRespMsg getUserDailyWorkTime(HttpServletRequest request, String month) {
+        String token = request.getHeader("TOKEN");
+        User user = userMapper.selectById(token);
+        Integer companyId = user.getCompanyId();
+
+        String startDate = month + "-01";
+        LocalDate ld = LocalDate.parse(startDate, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+        int year = ld.getYear();
+        System.out.println("year = "+year);
+        boolean isLerpYear = false;
+        if (year%4==0&&year%100!=0) {
+            isLerpYear = true;
+        } else if (year%400==0) {
+            isLerpYear = true;
+        }
+
+        int dm = ld.getMonthValue();
+        int maxDaysOfMonth = 28;
+        if (dm == 1 || dm == 3 || dm == 5 || dm == 7 || dm == 8 || dm == 10 || dm == 12) {
+            maxDaysOfMonth = 31;
+        } else if (dm == 2) {
+            if (isLerpYear) {
+                maxDaysOfMonth = 29;
+            }
+        } else {
+            maxDaysOfMonth = 30;
+        }
+
+        String endDate = month + "-"+maxDaysOfMonth;
+        System.out.println("endDate=="+endDate);
+        HttpRespMsg msg = new HttpRespMsg();
+        List<Map<String, Object>> list = null;
+        //分角色权限:管理员看全部的,部门负责人看自己部门的,个人只能看自己的。
+        if (user.getRole() == 0) {
+            //检查是不是部门负责人
+            if (user.getManageDeptId() != null && user.getManageDeptId() != 0) {
+                List<Department> allDepts = departmentMapper.selectList(new QueryWrapper<Department>().eq("company_id", companyId));
+                Department dp = allDepts.stream().filter(d->d.getDepartmentId().equals(user.getManageDeptId())).findFirst().get();
+                List<Department> subDepts = getSubDepts(dp, allDepts);
+                subDepts.add(dp);
+                List<Integer> collect = subDepts.stream().map(Department::getDepartmentId).collect(Collectors.toList());
+                list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, collect, null);
+            } else {
+                //看自己的所负责的项目相关人员的
+                list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, null, user.getId());
+            }
+        } else {
+            list = reportMapper.getUserDailyWorkTime(companyId, startDate, endDate, null, null);
+        }
+        List<UserMonthWork> userMonthWorks = new ArrayList<UserMonthWork>();
+
+        String lastUserId = null;
+        UserMonthWork lastUserData = null;
+        for (Map<String, Object> data : list) {
+            String id = (String)data.get("id");
+            String name = (String)data.get("name");
+            String departmentName = (String) data.get("departmentName");
+            Map<String, Object> map = new HashMap<>();
+            String date = new SimpleDateFormat("yyyy-MM-dd").format((Date)data.get("createDate"));
+
+            map.put("createDate", date);
+            map.put("workingTime", data.get("workingTime"));
+            if (id.equals(lastUserId)) {
+                //同一个用户的数据
+                lastUserData.worktimeList.add(map);
+            } else {
+                //生成新的数据
+                lastUserData = new UserMonthWork();
+                lastUserData.userId = id;
+                lastUserData.name = name;
+                lastUserData.departmentName = departmentName;
+                lastUserData.worktimeList = new ArrayList<>();
+                lastUserData.worktimeList.add(map);
+                userMonthWorks.add(lastUserData);
+            }
+            lastUserId = id;
+        }
+        HashMap map = new HashMap();
+        List<Integer> days = new ArrayList<>();
+        for (int i=1;i<=maxDaysOfMonth;i++) {
+            days.add(i);
+        }
+        map.put("days", days);
+        map.put("list", userMonthWorks);
+        msg.data = map;
+        return msg;
+    }
+
+    @Override
+    public HttpRespMsg exportUserDailyWorkTime(HttpServletRequest request, String month) {
+        HttpRespMsg msg = getUserDailyWorkTime(request, month);
+        String[] weekDayCHN = {"周一","周二","周三","周四","周五","周六","周日"};
+        HashMap map = (HashMap) msg.data;
+        List<Integer> days = (List<Integer>)map.get("days");
+        List<UserMonthWork> userMonthWorks = (List<UserMonthWork>) map.get("list");
+        List<List<String>> dataList = new ArrayList<>();
+        List<String> titleList = new ArrayList<>();
+        titleList.add("序号");
+        titleList.add("姓名");
+        days.forEach(d->{
+            String dateStr = month + "-" + (d<10?"0"+d:d);
+            LocalDate date = LocalDate.parse(dateStr, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+            String chn = weekDayCHN[date.getDayOfWeek().getValue()-1];
+            String m = month.split("-")[1];
+            if (m.startsWith("0")) {
+                m = m.substring(1);
+            }
+            titleList.add(m+"."+d+"/"+chn);
+        });
+        dataList.add(titleList);
+        for (int i=0;i<userMonthWorks.size(); i++) {
+            UserMonthWork userMonthWork = userMonthWorks.get(i);
+            List<String> dataItem = new ArrayList<>();
+            dataItem.add(""+(i+1));
+            dataItem.add(userMonthWork.name);
+            //找到那一天的工作时间
+            List<Map<String, Object>> worktimeList = userMonthWork.worktimeList;
+            days.forEach(d->{
+                String dateStr = month + "-" + (d<10?"0"+d:d);
+                Optional<Map<String, Object>> op = worktimeList.stream().filter(m -> ((String) m.get("createDate")).equals(dateStr)).findFirst();
+                if (op.isPresent()) {
+                    Map<String, Object> createDateHour = op.get();
+                    dataItem.add(""+createDateHour.get("workingTime"));
+                } else {
+                    dataItem.add("0");
+                }
+            });
+            dataList.add(dataItem);
+        }
+        //生成excel文件导出
+        String fileName = "人员每日工时统计_"+month+"月"+System.currentTimeMillis();
+        String resp = ExcelUtil.exportGeneralExcelByTitleAndList(fileName , dataList, path);
+        HttpRespMsg httpRespMsg = new HttpRespMsg();
+        httpRespMsg.data = resp;
+        return httpRespMsg;
+    }
+
     private void fillDeptUser(List<DepartmentVO> list, List<HashMap> userList) {
         list.forEach(l->{
             List<HashMap> collect = userList.stream().filter(u -> u.get("departmentId").equals(l.getId())).collect(Collectors.toList());

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

@@ -1094,6 +1094,128 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
         return msg;
     }
 
+    @Override
+    public HttpRespMsg bindCorpWeiXin(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.setCorpwxUserid(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;
+    }
+
+    @Override
+    public HttpRespMsg loginByUserId(String userId) {
+        HttpRespMsg httpRespMsg = new HttpRespMsg();
+        User user = userMapper.selectById(userId);
+        //查看该公司非会员公司,只能允许试用三天,超时不可登录
+        Company company = companyMapper.selectOne(new QueryWrapper<Company>().eq("id", user.getCompanyId()));
+        //公司未办理会员
+        if (null != company.getExpirationDate()) {
+            if (0 == company.getSetMeal()) {
+                //未办理会员
+                if (company.getExpirationDate().isBefore(LocalDateTime.now())) {
+                    httpRespMsg.setError("账号试用已到期,请联系客服。");
+                    return httpRespMsg;
+                }
+            } else {
+                if (company.getExpirationDate().isBefore(LocalDateTime.now())) {
+                    httpRespMsg.setError("账号会员已到期,请联系客服。");
+                    return httpRespMsg;
+                }
+            }
+        }
+
+        UserVO userVO = new UserVO().setCompanyName(company.getCompanyName());
+        userVO.setCompany(company);
+        BeanUtils.copyProperties(user, 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);
+        }
+        httpRespMsg.data = userVO;
+        return httpRespMsg;
+    }
+
 
     public boolean push(User user, String date) {
         //1,配置

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

@@ -1,10 +1,23 @@
 package com.management.platform.service.impl;
 
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.management.platform.controller.WeiXinCorpController;
+import com.management.platform.entity.SysConfig;
 import com.management.platform.entity.WxCorpInfo;
+import com.management.platform.mapper.SysConfigMapper;
 import com.management.platform.mapper.WxCorpInfoMapper;
 import com.management.platform.service.WxCorpInfoService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.*;
 import org.springframework.stereotype.Service;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.Resource;
+import java.time.LocalDateTime;
 
 /**
  * <p>
@@ -15,6 +28,123 @@ import org.springframework.stereotype.Service;
  * @since 2021-07-14
  */
 @Service
+@Slf4j
 public class WxCorpInfoServiceImpl extends ServiceImpl<WxCorpInfoMapper, WxCorpInfo> implements WxCorpInfoService {
+    public static String URL_SEND_WXCORP_MSG = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=ACCESS_TOKEN";
+
+    @Value("${suitId}")
+    private String suitId;
+    @Value("${suitSecret}")
+    private String suitSecret;
+    @Autowired
+    RestTemplate restTemplate;
+
+    @Resource
+    WxCorpInfoMapper wxCorpInfoMapper;
+
+    @Resource
+    SysConfigMapper sysConfigMapper;
+
+    @Override
+    public void sendWXCorpMsg(WxCorpInfo corpInfo, String corpUserid, String msg) {
+        try {
+            log.info("发送企业微信消息==="+corpUserid);
+            System.out.println("发送企业微信消息==="+corpUserid);
+            String accessToken = getCorpAccessToken(corpInfo);
+            String url = URL_SEND_WXCORP_MSG.replaceAll("ACCESS_TOKEN", accessToken);
+            HttpHeaders headers = new HttpHeaders();
+            headers.setContentType(MediaType.APPLICATION_JSON);
+            JSONObject reqParam = new JSONObject();
+            reqParam.put("touser",  corpUserid);
+            reqParam.put("msgtype",  "text");
+            reqParam.put("agentid",  corpInfo.getAgentid());
+            JSONObject contentJson = new JSONObject();
+            contentJson.put("content", msg);
+            reqParam.put("text",  contentJson);
+
+            HttpEntity<String> requestEntity = new HttpEntity<String>(reqParam.toJSONString(), headers);
+            ResponseEntity<String> responseEntity = this.restTemplate.exchange(url,
+                    HttpMethod.POST, requestEntity, String.class);
+            if (responseEntity.getStatusCode() == HttpStatus.OK) {
+                String resp = responseEntity.getBody();
+                log.info("发送企业微信消息返回结果=="+resp);
+                System.out.println("发送企业微信消息返回结果=="+resp);
+                JSONObject json = JSONObject.parseObject(resp);
+                if (json.getIntValue("errcode") == 0) {
+                    //发送成功
+
+                } else {
+                    throw new Exception(json.toJSONString());
+                }
+            } else {
+                log.error("发送失败:"+responseEntity.getStatusCode()+", "+responseEntity.getBody());
+                System.err.println("发送失败:"+responseEntity.getStatusCode()+", "+responseEntity.getBody());
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+
+
+    //获取第三方应用临时凭证
+    private String getSuiteAccessToken() {
+        if (WeiXinCorpController.SUITE_ACCESS_TOKEN == null || WeiXinCorpController.suiteTokenExpireTime < System.currentTimeMillis()) {
+            //失效了,需要重新获取
+            HttpHeaders headers = new HttpHeaders();
+            headers.setContentType(MediaType.APPLICATION_JSON);
+            JSONObject reqParam = new JSONObject();
+            reqParam.put("suite_id",  suitId);
+            reqParam.put("suite_secret", suitSecret);
+            SysConfig param = sysConfigMapper.selectOne(new QueryWrapper<SysConfig>().eq("param_key", "wx_suite_ticket"));
+            if (param != null) {
+                reqParam.put("suite_ticket",param.getParamValue());
+            }
+
+            HttpEntity<String> requestEntity = new HttpEntity<String>(reqParam.toJSONString(), headers);
+            ResponseEntity<String> responseEntity = this.restTemplate.exchange(WeiXinCorpController.GET_SUITE_ACCESS_TOKEN_URL,
+                    HttpMethod.POST, requestEntity, String.class);
+            if (responseEntity.getStatusCode() == HttpStatus.OK) {
+                String resp = responseEntity.getBody();
+                JSONObject obj = JSONObject.parseObject(resp);
+                if (obj.getIntValue("errcode") == 0) {
+                    WeiXinCorpController.SUITE_ACCESS_TOKEN = obj.getString("suite_access_token");
+                    WeiXinCorpController.suiteTokenExpireTime = System.currentTimeMillis() + obj.getIntValue("expires_in")*1000;
+                }
+            }
+        }
+        return WeiXinCorpController.SUITE_ACCESS_TOKEN;
+    }
+
+    //获取企业AccessToken
+    private String getCorpAccessToken(WxCorpInfo corpInfo) throws Exception {
+        if (corpInfo.getExpireTime().isBefore(LocalDateTime.now())) {
+            String url = WeiXinCorpController.GET_CORP_ACCESSTOKEN_URL.replace("SUITE_ACCESS_TOKEN", getSuiteAccessToken());
+            HttpHeaders headers = new HttpHeaders();
+            headers.setContentType(MediaType.APPLICATION_JSON);
+            JSONObject reqParam = new JSONObject();
+            reqParam.put("auth_corpid",  corpInfo.getCorpid());
+            reqParam.put("permanent_code",  corpInfo.getPermanentCode());
+            HttpEntity<String> requestEntity = new HttpEntity<String>(reqParam.toJSONString(), headers);
+            ResponseEntity<String> responseEntity = this.restTemplate.exchange(url,
+                    HttpMethod.POST, requestEntity, String.class);
+            if (responseEntity.getStatusCode() == HttpStatus.OK) {
+                String resp = responseEntity.getBody();
+                JSONObject json = JSONObject.parseObject(resp);
+                if (json.getIntValue("errcode") == 0) {
+                    String access_token = json.getString("access_token");
+                    corpInfo.setAccessToken(access_token);
+                    LocalDateTime now = LocalDateTime.now();
+                    now = now.plusSeconds(7200);
+                    corpInfo.setExpireTime(now);
+                    wxCorpInfoMapper.updateById(corpInfo);
+                } else {
+                    throw new Exception(json.toJSONString());
+                }
+            }
+        }
+        return corpInfo.getAccessToken();
+    }
 
 }

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

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.management.platform.constant.Constant;
 import com.management.platform.entity.*;
 import com.management.platform.mapper.*;
+import com.management.platform.service.WxCorpInfoService;
 import com.management.platform.util.AuthService;
 import com.management.platform.util.CheckPicUtil;
 import com.management.platform.util.RedisUtil;
@@ -53,7 +54,10 @@ public class TimingTask {
     private DepartmentMapper departmentMapper;
     @Resource
     private ReportMapper reportMapper;
-
+    @Resource
+    private WxCorpInfoService wxCorpInfoService;
+    @Resource
+    private WxCorpInfoMapper wxCorpInfoMapper;
     @Value(value = "${upload.path}")
     private String path;
 
@@ -76,13 +80,24 @@ public class TimingTask {
             if (str.equals(t.getAlertTime())) {
                 //发送推送提醒
                 List<Map<String, Object>> userList = userMapper.getPushUserList(t.getCompanyId());
+                List<WxCorpInfo> cpList = wxCorpInfoMapper.selectList(new QueryWrapper<WxCorpInfo>().eq("company_id", t.getCompanyId()));
+
                 userList.forEach(u->{
-                    push((u));
+                    if (u.get("corpwxUserid") != null) {
+                        //推送到企业微信
+                        String corpUid = (String) u.get("corpwxUserid");
+                        wxCorpInfoService.sendWXCorpMsg(cpList.get(0), corpUid, "请及时填写今日的工作报告哦");
+                    } else {
+                        push((u));
+                    }
+
                 });
             }
         });
     }
 
+
+
     public void push(Map<String, Object> user) {
         //1,配置
         WxMpInMemoryConfigStorage wxStorage = new WxMpInMemoryConfigStorage();

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

@@ -91,6 +91,7 @@ wx:
   template_report_fill: lhwkaW9BKwCvMtCuoAxLw4lZoGgMaucL0Ap0Vz-5KOY
   app_id: wx749c84daac654e1e
   app_secret: aacbd046ec1c790836f4f684c96fe585
+  template_report_pass: dbMuR2v7lxXLwRaorIWQ4T6ilvn0vzqmDDkD_3ZsaXc
 ##actuator健康检查配置
 management:
   security:

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

@@ -16,11 +16,12 @@
         <result column="package_etimecard" property="packageEtimecard" />
         <result column="package_expense" property="packageExpense" />
         <result column="package_customer" property="packageCustomer" />
+        <result column="package_engineering" property="packageEngineering" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id, company_name, staff_count_max, expiration_date, set_meal, package_worktime, package_project, package_contract, package_oa, package_etimecard, package_expense, package_customer
+        id, company_name, staff_count_max, expiration_date, set_meal, package_worktime, package_project, package_contract, package_oa, package_etimecard, package_expense, package_customer, package_engineering
     </sql>
 
 </mapper>

+ 22 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/PpMembsMapper.xml

@@ -0,0 +1,22 @@
+<?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.PpMembsMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.PpMembs">
+        <id column="id" property="id" />
+        <result column="pp_id" property="ppId" />
+        <result column="project_id" property="projectId" />
+        <result column="profession_id" property="professionId" />
+        <result column="memb_id" property="membId" />
+        <result column="memb_name" property="membName" />
+        <result column="percentage" property="percentage" />
+        <result column="progress" property="progress" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, pp_id, project_id, profession_id, memb_id, memb_name, percentage, progress
+    </sql>
+
+</mapper>

+ 17 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProfessionMapper.xml

@@ -0,0 +1,17 @@
+<?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.ProfessionMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.Profession">
+        <id column="id" property="id" />
+        <result column="company_id" property="companyId" />
+        <result column="name" property="name" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, company_id, name
+    </sql>
+
+</mapper>

+ 11 - 10
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectMapper.xml

@@ -31,13 +31,13 @@
         <result column="fee_man" property="feeMan" />
         <result column="customer_id" property="customerId" />
         <result column="customer_name" property="customerName" />
+        <result column="is_public" property="isPublic" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        id, project_name, company_id, project_code, incharger_id, plan_start_date, plan_end_date, progress, level, status, finish_date, creator_id, creator_name, create_date, contract_amount, budget, base_man, base_outsourcing, base_risk1, base_risk2, base_fee, fee_normal, fee_travel, fee_outsourcing, fee_man, customer_id, customer_name
+        id, project_name, company_id, project_code, incharger_id, plan_start_date, plan_end_date, progress, level, status, finish_date, creator_id, creator_name, create_date, contract_amount, budget, base_man, base_outsourcing, base_risk1, base_risk2, base_fee, fee_normal, fee_travel, fee_outsourcing, fee_man, customer_id, customer_name, is_public
     </sql>
-
     <resultMap id="CustomerResultMap" type="com.management.platform.entity.vo.CustomerProject" >
         <result column="customer_id" property="customerId" />
         <result column="customer_name" property="customerName" />
@@ -61,6 +61,7 @@
             WHERE user_id = #{userId}
         ) or incharger_id = #{userId}
         or creator_id = #{userId}
+        or is_public = 1
         ORDER BY id DESC
     </select>
 
@@ -174,14 +175,14 @@
     </foreach>
     </select>
     <select id="getGanttData" resultType="java.util.Map">
-    SELECT participation.`user_id`, user.`name`,project.id, project.`project_name`, project.`plan_start_date` as start_date, project.`plan_end_date`,
-    TIMESTAMPDIFF(DAY,project.`plan_start_date`, project.`plan_end_date`) AS duration FROM participation
-    LEFT JOIN user ON user.id = participation.`user_id`
-    LEFT JOIN project ON project.`id` = participation.`project_id`
-    WHERE participation.`user_id` IN
-    <foreach collection="userIds" close=")" open="(" separator="," index="" item="item">
-        #{item}
-    </foreach> AND project.`status` = 1 and project.plan_start_date is not null and project.plan_end_date is not null
+        SELECT participation.`user_id`, user.`name`,project.id, project.`project_name`, project.`plan_start_date` as start_date, project.`plan_end_date`,
+        TIMESTAMPDIFF(DAY,project.`plan_start_date`, project.`plan_end_date`) AS duration FROM participation
+        LEFT JOIN user ON user.id = participation.`user_id`
+        LEFT JOIN project ON project.`id` = participation.`project_id`
+        WHERE participation.`user_id` IN
+        <foreach collection="userIds" close=")" open="(" separator="," index="" item="item">
+            #{item}
+        </foreach> AND project.`status` = 1 and project.plan_start_date is not null and project.plan_end_date is not null
         ORDER BY participation.user_id, project.`plan_start_date`
     </select>
 </mapper>

+ 18 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectNotifyUserMapper.xml

@@ -0,0 +1,18 @@
+<?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.ProjectNotifyUserMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.ProjectNotifyUser">
+        <id column="id" property="id" />
+        <result column="project_id" property="projectId" />
+        <result column="user_id" property="userId" />
+        <result column="user_name" property="userName" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, project_id, user_id, user_name
+    </sql>
+
+</mapper>

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

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.management.platform.mapper.ProjectProfessionMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.ProjectProfession">
+        <id column="id" property="id" />
+        <result column="project_id" property="projectId" />
+        <result column="profession_id" property="professionId" />
+        <result column="profession_name" property="professionName" />
+        <result column="incharger_id" property="inchargerId" />
+        <result column="incharger_name" property="inchargerName" />
+        <result column="percentage" property="percentage" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, project_id, profession_id, profession_name, incharger_id, incharger_name, percentage
+    </sql>
+
+</mapper>

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

@@ -21,17 +21,19 @@
         <result column="task_id" property="taskId" />
         <result column="is_overtime" property="isOvertime" />
         <result column="progress" property="progress" />
+        <result column="department_audit_state" property="departmentAuditState" />
     </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
+        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
     </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.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
         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
@@ -57,7 +59,8 @@
     <select id="getReportByDate" resultType="java.util.Map">
         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.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
         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
@@ -75,7 +78,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.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
         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
@@ -91,12 +95,34 @@
         ORDER BY a.creator_id ASC
     </select>
 
+    <!--根据专业负责人id,日期获取相关项目的全部报告信息-->
+    <select id="getProfessionInchargeReportByDate" resultType="java.util.Map">
+        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
+        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
+        left join task on task.id = a.task_id
+        WHERE 1=1
+        <if test="date != null and date != ''">
+            AND a.create_date=#{date}
+        </if>
+        AND a.id in(select report_id from report_profession_progress, project_profession where
+        project_profession.incharger_id = #{id}
+        and report_profession_progress.profession_id = project_profession.profession_id
+        and report_profession_progress.audit_state = 0)
+        ORDER BY a.creator_id ASC
+    </select>
+
     <!-- 批量获取员工某天的报告 -->
     <select id="getUserReportByDate" resultType="java.util.Map">
         SELECT a.id, b.project_name AS project, a.working_time AS time, a.content, a.state, a.time_type as timeType, a.creator_id as creatorId, 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,
         b.incharger_id as inchargerId,
-        a.is_overtime as isOvertime,a.progress as progress
+        a.is_overtime as isOvertime,a.progress as progress,
+        a.department_audit_state as departmentAuditState
         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
@@ -114,7 +140,7 @@
 
     <!--根据日期获取报告上传人-->
     <select id="getReportNameByDate" resultType="java.util.Map">
-        SELECT DISTINCT b.id, b.name, a.state, a.cost
+        SELECT DISTINCT b.id, b.name, a.state, a.department_audit_state as departmentAuditState
         FROM report AS a
         JOIN user AS b ON a.creator_id=b.id
         WHERE 1=1
@@ -162,6 +188,37 @@
             AND a.creator_id in (select user_id from participation where project_id in(select id from project where incharger_id = #{leaderId}))
             AND a.project_id in (select id from project where incharger_id = #{leaderId})
         </if>
+        <if test="isEngeering == 1">
+            AND a.department_audit_state = 1
+        </if>
+
+        ORDER BY a.create_date DESC
+    </select>
+
+    <!--专业待审核的报告列表-->
+    <select id="getDetailByStateInMyProfession" resultType="java.util.Map">
+        SELECT DISTINCT b.id, b.name, cast(b.department_id as SIGNED) as departmentId,a.create_date AS date
+        FROM report AS a
+        JOIN user AS b ON a.creator_id=b.id
+        WHERE b.company_id=#{companyId}
+        <if test="leaderId != null">
+            AND a.creator_id in (select memb_id from pp_membs where pp_id in(select id from project_profession where incharger_id = #{leaderId}))
+            AND a.project_id in (select project_id from project_profession where incharger_id = #{leaderId})
+            AND a.id in (select report_id from report_profession_progress, project_profession where
+            project_profession.incharger_id = #{leaderId}
+            and report_profession_progress.profession_id = project_profession.profession_id
+            and report_profession_progress.audit_state = 0)
+        </if>
+        ORDER BY a.create_date DESC
+    </select>
+
+    <!--部门待审核的报告列表-->
+    <select id="getDepartmentDetailByState" resultType="java.util.Map">
+        SELECT DISTINCT b.id, b.name, cast(b.department_id as SIGNED) as departmentId,a.create_date AS date
+        FROM report AS a
+        JOIN user AS b ON a.creator_id=b.id
+        WHERE a.department_audit_state = 0 AND b.company_id=#{companyId}
+        AND a.creator_id in (select id from user where department_id = #{departmentId})
         ORDER BY a.create_date DESC
     </select>
     <select id="getRealProjectTime" resultType="java.util.Map">
@@ -183,5 +240,27 @@
         SELECT DATE_FORMAT(create_date,'%Y-%m-%d') as createDate, MAX(state) AS state FROM report
         WHERE create_date BETWEEN #{startDate} AND #{endDate} AND creator_id = #{userId} GROUP BY create_date
     </select>
-
+    <select id="getUserDailyWorkTime" resultType="java.util.Map">
+        SELECT user.id, user.name, department.department_name as departmentName, report.create_date as createDate, sum(working_time) as workingTime
+        FROM user
+        left join report on user.id = report.creator_id
+        left join department on department.department_id = user.department_id
+        WHERE report.state = 1
+        and user.company_id = #{companyId}
+        <if test="deptIds != null">
+            AND user.department_id in <foreach collection="deptIds" separator="," index="index" item="item" close=")" open="(">
+                item
+            </foreach>
+        </if>
+        <if test="startDate != null">
+            AND report.create_date &gt;= #{startDate}
+        </if>
+        <if test="endDate != null">
+            AND report.create_date &lt;= #{endDate}
+        </if>
+        <if test="leaderId != null">
+            AND user.id in (select user_id from participation where project_id in (select id from project where incharger_id = #{leaderId}))
+        </if>
+        GROUP BY user.id, report.create_date;
+    </select>
 </mapper>

+ 20 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ReportProfessionProgressMapper.xml

@@ -0,0 +1,20 @@
+<?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.ReportProfessionProgressMapper">
+
+    <!-- 通用查询映射结果 -->
+    <resultMap id="BaseResultMap" type="com.management.platform.entity.ReportProfessionProgress">
+        <id column="id" property="id" />
+        <result column="project_id" property="projectId" />
+        <result column="report_id" property="reportId" />
+        <result column="profession_id" property="professionId" />
+        <result column="progress" property="progress" />
+        <result column="audit_state" property="auditState" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, project_id, report_id, profession_id, progress, audit_state
+    </sql>
+
+</mapper>

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

@@ -82,10 +82,10 @@
 
 
     <select id="getPushUserList" resultType="java.util.Map">
-        SELECT a.id, a.wx_openid as wxOpenid, a.name, a.phone, b.department_name AS departmentName
+        SELECT a.id, a.wx_openid as wxOpenid, a.corpwx_userid as corpwxUserid, 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.wx_openid IS NOT NULL or corpwx_userid 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>

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

@@ -16,11 +16,12 @@
         <result column="permanent_code" property="permanentCode" />
         <result column="auth_username" property="authUsername" />
         <result column="company_id" property="companyId" />
+        <result column="agentid" property="agentid" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        corpid, corp_name, corp_full_name, corp_scale, corp_industry, corp_sub_industry, location, access_token, expire_time, permanent_code, auth_username, company_id
+        corpid, corp_name, corp_full_name, corp_scale, corp_industry, corp_sub_industry, location, access_token, expire_time, permanent_code, auth_username, company_id, agentid
     </sql>
 
 </mapper>

+ 141 - 3
fhKeeper/formulahousekeeper/timesheet/src/assets/myfont/demo_index.html

@@ -54,6 +54,42 @@
       <div class="content unicode" style="display: block;">
           <ul class="icon_lists dib-box">
           
+            <li class="dib">
+              <span class="icon iconfont">&#xe614;</span>
+                <div class="name">工时统计</div>
+                <div class="code-name">&amp;#xe614;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6a0;</span>
+                <div class="name">审核通过</div>
+                <div class="code-name">&amp;#xe6a0;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6c6;</span>
+                <div class="name">审核驳回</div>
+                <div class="code-name">&amp;#xe6c6;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe612;</span>
+                <div class="name">待办  等待 审核</div>
+                <div class="code-name">&amp;#xe612;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe61e;</span>
+                <div class="name">建筑工程类</div>
+                <div class="code-name">&amp;#xe61e;</div>
+              </li>
+          
+            <li class="dib">
+              <span class="icon iconfont">&#xe6a1;</span>
+                <div class="name">甘特图</div>
+                <div class="code-name">&amp;#xe6a1;</div>
+              </li>
+          
             <li class="dib">
               <span class="icon iconfont">&#xe60f;</span>
                 <div class="name">客户管理</div>
@@ -378,9 +414,9 @@
 <pre><code class="language-css"
 >@font-face {
   font-family: 'iconfont';
-  src: url('iconfont.woff2?t=1628309598492') format('woff2'),
-       url('iconfont.woff?t=1628309598492') format('woff'),
-       url('iconfont.ttf?t=1628309598492') format('truetype');
+  src: url('iconfont.woff2?t=1631157632503') format('woff2'),
+       url('iconfont.woff?t=1631157632503') format('woff'),
+       url('iconfont.ttf?t=1631157632503') format('truetype');
 }
 </code></pre>
           <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
@@ -406,6 +442,60 @@
       <div class="content font-class">
         <ul class="icon_lists dib-box">
           
+          <li class="dib">
+            <span class="icon iconfont firerock-icongongshitongji"></span>
+            <div class="name">
+              工时统计
+            </div>
+            <div class="code-name">.firerock-icongongshitongji
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont firerock-iconshenhetongguo"></span>
+            <div class="name">
+              审核通过
+            </div>
+            <div class="code-name">.firerock-iconshenhetongguo
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont firerock-iconshenhebohui"></span>
+            <div class="name">
+              审核驳回
+            </div>
+            <div class="code-name">.firerock-iconshenhebohui
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont firerock-icondaibandengdaishenhe"></span>
+            <div class="name">
+              待办  等待 审核
+            </div>
+            <div class="code-name">.firerock-icondaibandengdaishenhe
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont firerock-iconjianzhugongchenglei"></span>
+            <div class="name">
+              建筑工程类
+            </div>
+            <div class="code-name">.firerock-iconjianzhugongchenglei
+            </div>
+          </li>
+          
+          <li class="dib">
+            <span class="icon iconfont firerock-icongantetu"></span>
+            <div class="name">
+              甘特图
+            </div>
+            <div class="code-name">.firerock-icongantetu
+            </div>
+          </li>
+          
           <li class="dib">
             <span class="icon iconfont firerock-iconkehuguanli"></span>
             <div class="name">
@@ -892,6 +982,54 @@
       <div class="content symbol">
           <ul class="icon_lists dib-box">
           
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#firerock-icongongshitongji"></use>
+                </svg>
+                <div class="name">工时统计</div>
+                <div class="code-name">#firerock-icongongshitongji</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#firerock-iconshenhetongguo"></use>
+                </svg>
+                <div class="name">审核通过</div>
+                <div class="code-name">#firerock-iconshenhetongguo</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#firerock-iconshenhebohui"></use>
+                </svg>
+                <div class="name">审核驳回</div>
+                <div class="code-name">#firerock-iconshenhebohui</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#firerock-icondaibandengdaishenhe"></use>
+                </svg>
+                <div class="name">待办  等待 审核</div>
+                <div class="code-name">#firerock-icondaibandengdaishenhe</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#firerock-iconjianzhugongchenglei"></use>
+                </svg>
+                <div class="name">建筑工程类</div>
+                <div class="code-name">#firerock-iconjianzhugongchenglei</div>
+            </li>
+          
+            <li class="dib">
+                <svg class="icon svg-icon" aria-hidden="true">
+                  <use xlink:href="#firerock-icongantetu"></use>
+                </svg>
+                <div class="name">甘特图</div>
+                <div class="code-name">#firerock-icongantetu</div>
+            </li>
+          
             <li class="dib">
                 <svg class="icon svg-icon" aria-hidden="true">
                   <use xlink:href="#firerock-iconkehuguanli"></use>

+ 27 - 3
fhKeeper/formulahousekeeper/timesheet/src/assets/myfont/iconfont.css

@@ -1,8 +1,8 @@
 @font-face {
   font-family: "iconfont"; /* Project id 2390497 */
-  src: url('iconfont.woff2?t=1628309598492') format('woff2'),
-       url('iconfont.woff?t=1628309598492') format('woff'),
-       url('iconfont.ttf?t=1628309598492') format('truetype');
+  src: url('iconfont.woff2?t=1631157632503') format('woff2'),
+       url('iconfont.woff?t=1631157632503') format('woff'),
+       url('iconfont.ttf?t=1631157632503') format('truetype');
 }
 
 .iconfont {
@@ -13,6 +13,30 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.firerock-icongongshitongji:before {
+  content: "\e614";
+}
+
+.firerock-iconshenhetongguo:before {
+  content: "\e6a0";
+}
+
+.firerock-iconshenhebohui:before {
+  content: "\e6c6";
+}
+
+.firerock-icondaibandengdaishenhe:before {
+  content: "\e612";
+}
+
+.firerock-iconjianzhugongchenglei:before {
+  content: "\e61e";
+}
+
+.firerock-icongantetu:before {
+  content: "\e6a1";
+}
+
 .firerock-iconkehuguanli:before {
   content: "\e60f";
 }

Файловите разлики са ограничени, защото са твърде много
+ 1 - 1
fhKeeper/formulahousekeeper/timesheet/src/assets/myfont/iconfont.js


+ 42 - 0
fhKeeper/formulahousekeeper/timesheet/src/assets/myfont/iconfont.json

@@ -5,6 +5,48 @@
   "css_prefix_text": "firerock-icon",
   "description": "",
   "glyphs": [
+    {
+      "icon_id": "6023953",
+      "name": "工时统计",
+      "font_class": "gongshitongji",
+      "unicode": "e614",
+      "unicode_decimal": 58900
+    },
+    {
+      "icon_id": "2318254",
+      "name": "审核通过",
+      "font_class": "shenhetongguo",
+      "unicode": "e6a0",
+      "unicode_decimal": 59040
+    },
+    {
+      "icon_id": "9144552",
+      "name": "审核驳回",
+      "font_class": "shenhebohui",
+      "unicode": "e6c6",
+      "unicode_decimal": 59078
+    },
+    {
+      "icon_id": "17483402",
+      "name": "待办  等待 审核",
+      "font_class": "daibandengdaishenhe",
+      "unicode": "e612",
+      "unicode_decimal": 58898
+    },
+    {
+      "icon_id": "5149104",
+      "name": "建筑工程类",
+      "font_class": "jianzhugongchenglei",
+      "unicode": "e61e",
+      "unicode_decimal": 58910
+    },
+    {
+      "icon_id": "16187171",
+      "name": "甘特图",
+      "font_class": "gantetu",
+      "unicode": "e6a1",
+      "unicode_decimal": 59041
+    },
     {
       "icon_id": "3686260",
       "name": "客户管理",

BIN
fhKeeper/formulahousekeeper/timesheet/src/assets/myfont/iconfont.ttf


BIN
fhKeeper/formulahousekeeper/timesheet/src/assets/myfont/iconfont.woff


BIN
fhKeeper/formulahousekeeper/timesheet/src/assets/myfont/iconfont.woff2


+ 9 - 5
fhKeeper/formulahousekeeper/timesheet/src/main.js

@@ -45,10 +45,10 @@ import 'nprogress/nprogress.css'
 
 var addRouFlag = false;
 //角色权限对应关系
-var userModules = [{role:0, modules:["工时报告","自动计时","项目管理"]},
+var userModules = [{role:0, modules:["工时报告","专业审核","部门审核","自动计时","项目管理"]},
             {role:3, modules:["工时报告","工时成本统计","项目报表服务","费用报销","项目管理"]},
             {role:4, modules:["工时报告","财务核算成本", "组织架构"]},
-            {role:5, modules:["工时报告","自动计时","项目管理"]},
+            {role:5, modules:["工时报告","自动计时","项目管理","专业管理"]},
             {role:6, modules:["工时报告","工时成本统计","财务核算成本","项目报表服务","费用报销","项目管理"]},];
 router.beforeEach((to, from, next) => {
     NProgress.start();
@@ -75,11 +75,15 @@ router.beforeEach((to, from, next) => {
                 var getRoutes = null;
                 var filterRouter = allRouters;
                 if (user.company.packageExpense == 0) {
-                    filterRouter = allRouters.filter(r=>{return r.name != '费用报销' && r.name != '项目报表服务'});
+                    filterRouter = filterRouter.filter(r=>{return r.name != '费用报销' && r.name != '项目报表服务'});
                 }
                 if (user.company.packageCustomer == 0) {
                     //没有客户管理功能的,需要去掉
-                    filterRouter = allRouters.filter(r=>{return r.name != '客户管理'});
+                    filterRouter = filterRouter.filter(r=>{return r.name != '客户管理'});
+                }
+                if (user.company.packageEngineering == 0) {
+                    //非工程类的,去掉专业管理
+                    filterRouter = filterRouter.filter(r=>{return r.name != '工程专业管理' && r.name != '专业审核' && r.name != '部门审核'});
                 }
 
                 if (user.role == 1 || user.role == 2) {
@@ -87,7 +91,7 @@ router.beforeEach((to, from, next) => {
                 } else {
                     var modules = userModules.filter(u=>u.role == user.role)[0].modules;
                     if (user.role == 0 && user.leader) {
-                        modules.push(2, "报告审核");
+                        modules.push(2, "项目报告审核");
                     }
                     getRoutes = filterRouter.filter(r=>{
                         return modules.filter(m=>m == r.name).length > 0;

+ 40 - 8
fhKeeper/formulahousekeeper/timesheet/src/routes.js

@@ -17,6 +17,8 @@ import statistics from './views/workReport/statistics.vue'
 import daily from './views/workReport/daily.vue'
 import review from './views/workReport/list.vue'
 import timer from './views/workReport/timer.vue'
+import reviewProfession from './views/workReport/list_profession.vue'
+import reviewDepartment from './views/workReport/list_department.vue'
 // 项目管理
 import list from './views/project/list.vue'
 import cost from './views/project/cost.vue'
@@ -42,6 +44,8 @@ import PdfView from './views/pdf/pdfview';
 import expense from './views/expense/expense';
 //客户管理
 import customer from './views/customer/list';
+//专业管理
+import profession from './views/profession/list';
 //企业报表
 import corpReport from './views/corpreport/list';
 
@@ -113,28 +117,47 @@ export const allRouters = [//组织架构
             { path: '/daily', component: daily, name: '工时报告' },
         ]
     },
+    // {
+    //     path: '/',
+    //     component: Home,
+    //     name: '自动计时',
+    //     iconCls: 'iconfont firerock-iconmiaobiao',
+    //     leaf: true,
+    //     children: [
+    //         { path: '/timer', component: timer, name: '自动计时' },
+    //     ]
+    // },
+    //工时审核
     {
         path: '/',
         component: Home,
-        name: '自动计时',
-        iconCls: 'iconfont firerock-iconmiaobiao',
+        name: '专业审核',
+        iconCls: 'iconfont firerock-iconshenhe',
         leaf: true,
         children: [
-            { path: '/timer', component: timer, name: '自动计时' },
+            { path: '/reviewProfession', component: reviewProfession, name: '专业审核' },
         ]
     },
-    //工时审核
     {
         path: '/',
         component: Home,
-        name: '报告审核',
+        name: '部门审核',
         iconCls: 'iconfont firerock-iconshenhe',
         leaf: true,
         children: [
-            { path: '/review', component: review, name: '报告审核' },
+            { path: '/reviewDepartment', component: reviewDepartment, name: '部门审核' },
+        ]
+    },
+    {
+        path: '/',
+        component: Home,
+        name: '项目报告审核',
+        iconCls: 'iconfont firerock-iconshenhe',
+        leaf: true,
+        children: [
+            { path: '/review', component: review, name: '项目报告审核' },
         ]
     },
-    
     //成本统计
     {
         path: '/',
@@ -193,7 +216,16 @@ export const allRouters = [//组织架构
             { path: '/customer', component: customer, name: '客户管理' }
         ]
     },
-    
+    {
+        path: '/',
+        component: Home,
+        name: '工程专业管理',
+        iconCls: 'iconfont firerock-iconjianzhugongchenglei',
+        leaf: true,
+        children: [
+            { path: '/profession', component: profession, name: '工程专业管理' }
+        ]
+    },
     // 费用报销模块
     {
         path: '/',

+ 14 - 10
fhKeeper/formulahousekeeper/timesheet/src/views/Home.vue

@@ -36,22 +36,22 @@
                 <el-badge class="itemNew" :value="num" :hidden="num == 0">
                     <i class="el-icon-message-solid" style="font-size:24px" v-popover:popover1 @click="drawer = true"></i>
                 </el-badge>
-                <el-drawer title="消息中心" :visible.sync="drawer" direction="rtl" :with-header="false" size="30%">
-                    <el-table :data="popoverData" :height="tableHeight">
-                        <el-table-column property="type" label="消息中心" align="center">
+                <el-drawer title="消息中心" :visible.sync="drawer" direction="rtl" :with-header="false" size="35%">
+                    <el-table :data="popoverData" :height="tableHeight" size="small">
+                        <el-table-column property="type" label="消息内容" align="left">
                             <template slot-scope="scope">
                                 <el-link type="primary" :underline="false" @click="locationHerf(scope.row.id,scope.row.content, scope.row.type)">
-                                    {{msgTypeTxt[scope.row.type]}}
+                                    <span style="font-size:13px;">{{scope.row.msg==null?msgTypeTxt[scope.row.type]:scope.row.msg}}</span>
                                 </el-link>
                             </template>
                         </el-table-column>
-                        <el-table-column property="type" label="状态" align="center">
+                        <el-table-column property="type" label="状态" align="center" width="60">
                             <template slot-scope="scope">
                                 <span v-if="scope.row.checked == 0" style="color:red">未读</span>
                                 <span v-else style="color:green">已读</span>
                             </template>
                         </el-table-column>
-                        <el-table-column property="time" label="时间" align="center"></el-table-column>
+                        <el-table-column property="time" label="时间" align="center" width="120"></el-table-column>
                     </el-table>
                 </el-drawer>                
                 <el-dropdown trigger="hover" style="margin-left:10px;">
@@ -69,7 +69,9 @@
         </el-col>
 
         <el-col :span="24" class="main">
+
             <aside :class="collapsed?'menu-collapsed':'menu-expanded'">
+                <el-scrollbar style="height:100%">
                 <!--导航菜单-->
                 <el-menu :default-active="$route.path" class="el-menu-vertical-demo" unique-opened router v-if="!collapsed">
                     <template v-for="(item,index) in $router.options.routes" v-if="!item.hidden">
@@ -107,6 +109,7 @@
                         </template>
                     </li>
                 </ul>
+                </el-scrollbar>
             </aside>
 
             <section class="content-container">
@@ -167,7 +170,7 @@
                 collapsed: sessionStorage.collapsed!=null?(sessionStorage.collapsed=='true'?true:false):false,
                 sysUserName: "",
                 menu: [],
-                msgTypeTxt:["审批未通过","有新任务啦","任务有新进展"],
+                msgTypeTxt:["审批未通过","有新任务啦","任务有新进展","项目日报审核通过"],
                 editInformation: false,
                 editPassWord: false,
                 editLoading: false,
@@ -364,18 +367,19 @@
                 res => {
                     if (res.code == "ok") {
                         this.loadNotice();
-                        if (type == 0) {
-                            //审批未通过的消息
+                        if (type == 0 || type == 3) {
+                            //审批未通过的消息, 也包括审批通过的通知
                             sessionStorage.msg = date;
                             sessionStorage.from = 1;
                             //本页面再点的话强制转移一下
                             var currentRoute = this.$route.path.split("/");
                             if (currentRoute[1] == "daily") {
                                 this.$router.go(0);
+                                this.drawer = false;
                                 return false;
                             }
                             this.$router.push("/daily");
-                            this.drawer = true;
+                            this.drawer = false;
                         } else if (type == 1) {
                             //1- 有新任务待执行
                             this.$router.push("/projectInside/"+date);

+ 119 - 15
fhKeeper/formulahousekeeper/timesheet/src/views/Login.vue

@@ -47,6 +47,7 @@
     export default {
         data() {
             return {
+                isCorpWX: false,
                 dialogVisible: false,
                 logining: false,
                 // 登录信息
@@ -75,6 +76,10 @@
             }
         },
         mounted() {
+            var ua = navigator.userAgent.toLowerCase();
+            if (ua.indexOf("wxwork") > 0) {
+                this.isCorpWX = true;
+            } 
             if (localStorage.userInfo != null) {
                 var user = JSON.parse(localStorage.userInfo);
                 if (user.role == 3) {
@@ -87,24 +92,123 @@
                     this.$router.push({ path: '/daily' });
                 }
             } else {
-                //检查环境,如果是钉钉有$CORPID$
-                var key = '?corpid=';
-                var url = location.href;
-                var that = this;
-                if (url.indexOf(key) > 0) {
-                    var corpId = url.substring(url.indexOf(key)+key.length,url.indexOf('#'));
-                    dd.ready(function() {
-                        dd.runtime.permission.requestAuthCode({
-                            corpId: corpId, // 企业id
-                            onSuccess: function (info) {
-                                var code = info.code // 通过该免登授权码可以获取用户身份
-                                that.loginByCode(code, corpId);
-                            }});
-                    });
-                } 
+                if (this.isCorpWX) {
+                    //企业微信环境下,尝试自动登录
+                    let href = window.location.href;
+                    //判断企业微信,是否存在授权
+                    //尝试自动登录
+                    if (href.indexOf('hasTriedAutoLogin') == -1) {
+                        this.tryAutoLogin();
+                    } else if (href.indexOf("userId") > 0) {
+                        //后台经过验证后,重定向过来带上了userId
+                        var loginUserId = href.substring(href.indexOf("userId=")+"userId=".length);
+                        if (loginUserId.includes('#/')) {
+                            loginUserId = loginUserId.substring(0, loginUserId.indexOf('#/'));
+                        }
+                        this.loginByUserId(loginUserId);
+                    }
+                } else {
+                    //检查环境,如果是钉钉有$CORPID$
+                    var key = '?corpid=';
+                    var url = location.href;
+                    var that = this;
+                    if (url.indexOf(key) > 0) {
+                        var corpId = url.substring(url.indexOf(key)+key.length,url.indexOf('#'));
+                        dd.ready(function() {
+                            dd.runtime.permission.requestAuthCode({
+                                corpId: corpId, // 企业id
+                                onSuccess: function (info) {
+                                    var code = info.code // 通过该免登授权码可以获取用户身份
+                                    that.loginByCode(code, corpId);
+                                }});
+                        });
+                    } 
+                }
             }
         },
         methods: {
+             bindIfNessary() {
+                let href = window.location.href;
+                
+                if (this.isCorpWX) {
+                    // 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);
+                        urlRight = urlRight.substring(0, urlRight.indexOf('#/'));
+                        // window.location = urlLeft + "#/home" + urlRight;//拼接跳转
+                        //获取code
+                        var code = urlRight.substring('?code='.length,urlRight.indexOf('&state='));
+                        var passUserId = urlRight.substring(urlRight.indexOf('&state=')+'&state='.length);
+                        if (passUserId == '1') {
+                            //自动登录的回调
+                            this.$axios.get('/wxcorp/corpWeiXinLogin', {params:{code:code}})
+                            .then(res => {
+                                if (res == null) {
+                                    
+                                } else if(res.errcode != null) {
+                                    //报错了
+                                    console.log(res.errmsg);
+                                } else {
+                                    //获取openId
+                                    if (res.data != null && ((this.isWX && res.data.wxOpenid != undefined)
+                                                || (this.isCorpWX && res.data.corpwxUserid != undefined))) {
+                                        localStorage.userInfo = JSON.stringify(res.data);
+                                        console.log('登录成功');
+                                        this.user = res.data;
+                                        window.location.href = '/#/index';
+                                    }
+                                }
+                            }).catch(err=> {
+                                alert('err=' + err);
+                            });
+                        } else {
+                        }
+                        
+                    } 
+                }
+            },
+            tryAutoLogin() {
+                var appId = "ww4e237fd6abb635af";//企业微信第三方的SUIT ID
+                var url = "http://mobworktime.ttkuaiban.com/api/corpWXAuth";//授权回调页面
+                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";
+                window.location.href = weixinUrl;
+            },
+            loginByUserId(userId) {
+                this.http.post("/user/loginByUserId", {userId:userId} , res => {
+                            if (res.code == "ok") {
+                                var user = res.data;
+                                localStorage.user = JSON.stringify(res.data);
+                                sessionStorage.setItem('user', JSON.stringify(res.data));
+                                if (user.role == 3) {
+                                    //公司高层
+                                    this.$router.push({ path: '/cost' });
+                                } else if (user.role == 4) {
+                                    //人事管理员
+                                    this.$router.push({ path: '/team' });
+                                } else {
+                                    this.$router.push({ path: '/daily' });
+                                }
+                            } else {
+                                this.$message({
+                                    message: res.msg,
+                                    type: 'error'
+                                });
+                            }
+                        }, error => {
+                            this.$message({
+                                message: error,
+                                type: 'error'
+                            });
+                        })
+            },
             loginByCode(code, corpId) {
                 this.http.post("/dingding/getUserByCode", {code:code, corpid:corpId} , res => {
                             if (res.code == "ok") {

+ 34 - 6
fhKeeper/formulahousekeeper/timesheet/src/views/corpreport/list.vue

@@ -40,8 +40,15 @@
             <!--项目报表 -->
             <el-table v-if="ins == 0" border :data="list" highlight-current-row v-loading="listLoading" :height="tableHeight" style="width: 100%;">
                 <el-table-column  prop="projectCode" label="项目编码"  width="120"></el-table-column>
-                <el-table-column  prop="projectName" label="项目名称" ></el-table-column>
+                <el-table-column  prop="projectName" label="项目名称" >
+                  <template slot-scope="scope" >
+                    {{scope.row.projectName}}
+                  </template>
+                </el-table-column>
                 <el-table-column prop="inchargerName" label="负责人"  width="80">
+                  <template slot-scope="scope" >
+                    {{scope.row.inchargerName}}
+                  </template>
                 </el-table-column>
                 <el-table-column prop="contractAmount" label="合同金额(元)"  width="150" align="right">
                   <template slot-scope="scope">
@@ -54,8 +61,14 @@
                     </template>
                 </el-table-column>
                 <el-table-column prop="planStartDate" label="计划开始时间"  width="120">
+                    <template slot-scope="scope">
+                        {{scope.row.planStartDate}}
+                    </template>
                 </el-table-column>
                 <el-table-column prop="planEndDate" label="计划结束时间"  width="120">
+                    <template slot-scope="scope">
+                        {{scope.row.planEndDate}}
+                    </template>
                 </el-table-column>
                 <el-table-column prop="progress" label="完成度" width="100"  >
                     <template slot-scope="scope">
@@ -67,8 +80,15 @@
             <!-- 项目任务报表 -->
             <el-table v-if="ins == 1" border :data="list" highlight-current-row v-loading="listLoading" :height="tableHeight" style="width: 100%;">
                 <el-table-column  prop="project_code" label="项目编码"  width="120"></el-table-column>
-                <el-table-column  prop="project_name" label="项目名称" width="200"></el-table-column>
+                <el-table-column  prop="project_name" label="项目名称" width="200">
+                  <template slot-scope="scope" >
+                    {{scope.row.project_name}}
+                  </template>
+                </el-table-column>
                 <el-table-column prop="name" label="任务名称"  width="300">
+                  <template slot-scope="scope" >
+                    {{scope.row.name}}
+                  </template>
                 </el-table-column>
                 <el-table-column prop="plan_hours" label="计划工时(h)"  width="150">
                   <template slot-scope="scope">
@@ -77,7 +97,7 @@
                 </el-table-column>
                 <el-table-column prop="real_hours" label="实际工时(h)"  width="150">
                    <template slot-scope="scope">
-                        {{scope.row.real_hours == null? 0:scope.row.real_hours.toFixed(1)}}
+                        <font :style="'color:'+(scope.row.real_hours > scope.row.plan_hours?'red':'')">{{scope.row.real_hours == null? 0:scope.row.real_hours.toFixed(1)}}</font>
                     </template>
                 </el-table-column>
                 <el-table-column prop="task_status" label="状态" width="80" >
@@ -100,7 +120,11 @@
             <!--项目成本报表 -->
             <el-table v-if="ins == 2" border :data="list" highlight-current-row v-loading="listLoading" :height="tableHeight" style="width: 100%;">
                 <el-table-column  prop="projectCode" label="项目编码"  width="120"></el-table-column>
-                <el-table-column  prop="projectName" label="项目名称" ></el-table-column>
+                <el-table-column  prop="projectName" label="项目名称" >
+                  <template slot-scope="scope" >
+                    {{scope.row.projectName}}
+                  </template>
+                </el-table-column>
                 <el-table-column prop="feeMan" label="人工成本"  width="100"  align="right">
                   <template slot-scope="scope">
                         {{scope.row.feeMan==null?0:scope.row.feeMan.toFixed(2)}}
@@ -130,7 +154,11 @@
             <!--项目收支平衡表 -->
             <el-table v-if="ins == 3" border :data="list" highlight-current-row v-loading="listLoading" :height="tableHeight" style="width: 100%;">
                 <el-table-column prop="projectCode" label="项目编码"  width="120"></el-table-column>
-                <el-table-column prop="projectName" label="项目名称" ></el-table-column>
+                <el-table-column prop="projectName" label="项目名称" >
+                  <template slot-scope="scope" >
+                    {{scope.row.projectName}}
+                  </template>
+                </el-table-column>
                 <el-table-column prop="contractAmount" label="合同金额" width="100" align="right">
                   <template slot-scope="scope">
                         {{scope.row.contractAmount.toFixed(2)}}
@@ -173,7 +201,7 @@
                 </el-table-column>
             </el-table>
             <!--客户项目报表 -->
-            <el-table v-if="ins == 4" border :data="list" 
+            <el-table v-if="ins == 4" border :data="list"
             highlight-current-row v-loading="listLoading" :height="tableHeight" 
              style="width: 100%;">
                 <el-table-column prop="customerName" label="客户名称" ></el-table-column>

+ 293 - 0
fhKeeper/formulahousekeeper/timesheet/src/views/profession/list.vue

@@ -0,0 +1,293 @@
+<template>
+    <section>
+        <!--工具条-->
+        <el-col :span="24" class="toolbar" style="padding-bottom: 0px;">
+            <el-form :inline="true">
+                <el-form-item label="工程专业列表">
+                    
+                </el-form-item>
+                <el-form-item >
+                    <div>
+                    <el-input style="float:left;" v-model="keyword" class="input-with-select" placeholder="请输入专业名称关键字" clearable="true">
+                        <el-button slot="append" @click="searchList" icon="el-icon-search"></el-button>
+                    </el-input>
+                    </div>
+                </el-form-item>
+                <el-form-item style="float:right;" v-if="user.role == 1||user.role == 2||user.role == 5">
+                    <el-link type="primary" :underline="false" @click="handleAdd(-1,null)">新增专业</el-link>
+                </el-form-item>
+            </el-form>
+        </el-col>
+
+        <!--列表-->
+        <el-table :data="list" highlight-current-row v-loading="listLoading" :height="tableHeight" style="width: 100%;">
+            <el-table-column type="index" width="60">
+                <template slot-scope="scope" >
+                        {{scope.$index+1+(page-1)*size}}
+                    </template>
+            </el-table-column>
+            <el-table-column prop="name" label="专业名称" >
+            </el-table-column>
+            
+            <el-table-column label="操作" width="150" v-if="user.role == 1||user.role == 2||user.role == 5">
+                <template slot-scope="scope">
+                    <el-button size="mini" v-if="user.role>0" type="primary" @click="handleAdd(scope.$index, scope.row)">编辑</el-button>
+                    <el-button v-if="user.role>0" size="mini"  @click="deletePro(scope.$index, scope.row)">删除</el-button>
+                </template>
+            </el-table-column>
+        </el-table>
+
+        <!--工具条-->
+        <el-col :span="24" class="toolbar">
+            <el-pagination
+                @size-change="handleSizeChange"
+                @current-change="handleCurrentChange"
+                :page-sizes="[20 , 50 , 80 , 100]"
+                :page-size="20"
+                layout="total, sizes, prev, pager, next"
+                :total="total"
+                style="float:right;"
+            ></el-pagination>
+        </el-col>
+
+        <!--新增界面-->
+        <el-dialog :title="title" v-if="addFormVisible" :visible.sync="addFormVisible" :close-on-click-modal="false" customClass="customWidth" width="600px">
+            <el-form ref="form1" :model="addForm" :rules="rules" label-width="120px">
+                <el-form-item label="专业名称" prop="name">
+                    <el-input v-model="addForm.name" :max="20" :disabled="user.role==0" placeholder="请输入专业名称" clearable></el-input>
+                </el-form-item>
+            </el-form>
+            <div slot="footer" class="dialog-footer;">
+                <el-button @click.native="addFormVisible = false">取消</el-button>
+                <el-button type="primary" @click="submitInsert" :loading="addLoading">提交</el-button>
+            </div>
+        </el-dialog>
+    </section>
+</template>
+<style scoped>
+.input-with-select .el-input-group__prepend {
+    background-color: #fff;
+  }
+.line {
+    padding:10px;
+}
+.line span{
+    font-size:15px;
+}
+.line span:nth-child(even){
+    float:right;
+}
+</style>
+<script>
+    import util from "../../common/js/util";
+    export default {
+        data() {
+            return {
+                
+                keyword:null,
+                user: JSON.parse(sessionStorage.getItem("user")),
+                userDetailVisible: false,
+                userDetail:{},
+                date: new Date(),
+                users: [],
+                participator:[],
+                tableHeight: 0,
+                listLoading: false,
+                total: 0,
+                page: 1,
+                size: 20,
+                list: [],
+                addFormVisible: false,
+                addLoading: false,
+                addUp: 0, // 合计
+                title: "",
+                addForm: {
+                    name: ''
+                },
+                rules: {
+                    name: [{ required: true, message: "请输入专业名称", trigger: "blur" }],
+                }
+            };
+        },
+        // 过滤器
+        filters: {
+            numberToCurrency(value) {
+                if (!value) return '0.00'
+                // 将数值截取,保留两位小数
+                value = value.toFixed(2)
+                // 获取整数部分
+                const intPart = Math.trunc(value)
+                // 整数部分处理,增加,
+                const intPartFormat = intPart.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,')
+                // 预定义小数部分
+                let floatPart = '.00'
+                // 将数值截取为小数部分和整数部分
+                const valueArray = value.toString().split('.')
+                if (valueArray.length === 2) { // 有小数部分
+                floatPart = valueArray[1].toString() // 取得小数部分
+                return intPartFormat + '.' + floatPart
+                }
+                return intPartFormat + floatPart
+            }
+        },
+        methods: {
+            
+            number(){  
+        //      this.addForm.budget = this.addForm.budget.replace(/[^\.\d]/g,'');
+        //         this.addForm.budget = this.addForm.budget.replace('.','');
+          },
+            
+            searchList() {
+                this.page = 1;
+                this.getList();
+            },
+           
+            //分页
+            handleCurrentChange(val) {
+                this.page = val;
+                this.getList();
+            },
+
+            handleSizeChange(val) {
+                this.size = val;
+                this.getList();
+            },
+
+            //获取项目列表
+            getList() {
+                this.listLoading = true;
+                this.http.post('/profession/list', {
+                    pageIndex: this.page,
+                    pageSize: this.size,
+                    keyword:this.keyword
+                },
+                res => {
+                    this.listLoading = false;
+                    if (res.code == "ok") {
+                        var list = res.data.records;
+                        this.list = list;
+                        this.total = res.data.total;
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.listLoading = false;
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
+
+            //显示新增界面
+            handleAdd(i, item) {
+                if(i == -1) {
+                    this.title = "新增专业";
+                    this.addForm = {
+                    }
+                } else {
+                    this.title = "修改专业";
+                    this.addForm = JSON.parse(JSON.stringify(item));
+                }
+                this.addFormVisible = true;
+            },
+
+            submitInsert() {
+                this.$refs.form1.validate(valid => {
+                    if (valid) {
+                        this.addLoading = true;
+                        this.http.post('/profession/addOrMod', this.addForm,
+                        res => {
+                            this.addLoading = false;
+                            if (res.code == "ok") {
+                                this.addFormVisible = false;
+                                this.getList();
+                            } else {
+                                this.$message({
+                                    message: res.msg,
+                                    type: "error"
+                                });
+                            }
+                        },
+                        error => {
+                            this.addLoading = false;
+                            this.$message({
+                                message: error,
+                                type: "error"
+                            });
+                        });
+                        }
+                });
+            },
+
+            // 删除
+            deletePro(i, item) {
+                this.$confirm("确定要专业" + item.customerName + "吗?","删除专业", {
+                    confirmButtonText: "确定",
+                    cancelButtonText: "取消",
+                    type: "warning"
+                })
+                .then(() => {
+                    this.listLoading = true;
+                    this.http.post('/profession/delete',{ 
+                        id: item.id 
+                    },
+                    res => {
+                        this.listLoading = false;
+                        if (res.code == "ok") {
+                            this.$message({
+                                message: "删除成功",
+                                type: "success"
+                            });
+                            this.getList();
+                        } else {
+                            this.$message({
+                                message: res.msg,
+                                type: "error"
+                            });
+                        }
+                    },
+                    error => {
+                        this.listLoading = false;
+                        this.$message({
+                            message: error,
+                            type: "error"
+                        });
+                        }
+                    );
+                })
+                .catch(() => {});
+            },
+        },
+        created() {
+            let height = window.innerHeight;
+            this.tableHeight = height - 195;
+            const that = this;
+            window.onresize = function temp() {
+                that.tableHeight = window.innerHeight - 195;
+            };
+        },
+        mounted() {
+            this.getList();
+        }
+    };
+</script>
+
+<style lang="scss" scoped>
+.rg_span{
+    display: inline-block;
+}
+.rg_span span {
+    text-align: right;
+    box-sizing: border-box;
+    padding-right: 10px;
+}
+.el-dialog__title {
+    display: inline-table;
+    margin-top: 20px;
+}
+</style>

+ 2 - 2
fhKeeper/formulahousekeeper/timesheet/src/views/project/cost.vue

@@ -25,12 +25,12 @@
         <el-dialog title="工时报表导出" v-if="exportDialog" :visible.sync="exportDialog" :close-on-click-modal="false" customClass="customWidth" width="500px">
             <el-form ref="form3" :model="exportParam" >
                 <el-form-item prop="projectId" label="选择项目" v-if="radio != '人员'">
-                    <el-select v-model="exportParam.projectId" placeholder="全部项目"  clearable style="width:350px;">
+                    <el-select v-model="exportParam.projectId" placeholder="全部项目"  clearable style="width:350px;" filterable="true">
                         <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="userIds" label="选择人员" v-if="radio == '人员'">
-                    <el-select v-model="exportParam.userIds" placeholder="全部人员" multiple="true"  clearable style="width:350px;">
+                    <el-select v-model="exportParam.userIds" placeholder="全部人员" multiple="true"  clearable style="width:350px;" filterable="true">
                         <el-option v-for="item in hasReportUserList"  :key="item.id" :label="item.name" :value="item.id"></el-option>
                     </el-select>
                 </el-form-item>

+ 545 - 37
fhKeeper/formulahousekeeper/timesheet/src/views/project/info.vue

@@ -1,21 +1,22 @@
 <template>
-        <div :style="'padding:20px 50px;background:#f7f7f7;min-height:'+tableHeight+'px;'">
-        <div style="margin: 0 auto;width:900px;">
+        <div :style="'padding:20px 30px;background:#f7f7f7;min-height:'+tableHeight+'px;'">
+        <div style="margin: 0 auto;width:1000px;">
             <el-row :gutter="20">
-            <el-col :span="16" >
+            <el-col :span="17" >
                 <div class="box info">
                     <label>基本信息<el-link v-if="user.id == project.creatorId || user.id == project.inchargerId" @click="showEdit" style="float:right;"><i class="el-icon-edit"  ></i></el-link></label>
                     <el-row :gutter="10" >
                         <el-col :span="5" ><span class="gray_label">项目名称:</span></el-col><el-col :span="19" ><span >{{project.projectName}}</span></el-col>
                     </el-row>
                     <el-row :gutter="10">
-                        <el-col :span="5" ><span class="gray_label">状态:</span></el-col><el-col :span="7" ><span >{{statusTxt[project.status]}}</span></el-col>
+                        <el-col :span="5" ><span class="gray_label">状态:</span></el-col><el-col :span="7" ><span >{{project.status==null?'-':statusTxt[project.status]}}</span></el-col>
                         <el-col :span="5" ><span class="gray_label">完成度: </span></el-col><el-col :span="7" ><span>
-                            <el-progress :percentage="project.progress"></el-progress></span></el-col>
+                            <el-progress  :percentage="project.progress == null?0:project.progress"></el-progress></span></el-col>
                     </el-row>
                     <el-row :gutter="10">
                         <el-col :span="5" ><span class="gray_label">项目编码: </span></el-col><el-col :span="7" ><span >{{project.projectCode}}&nbsp;</span></el-col>
-                        <el-col :span="5" ><span class="gray_label">合同金额:</span></el-col><el-col :span="7" >
+                        <el-col :span="5" v-if="user.role>0 || user.id == project.creatorId || user.id == project.inchargerId"><span class="gray_label">合同金额:</span></el-col>
+                        <el-col :span="7" v-if="user.role>0 || user.id == project.creatorId || user.id == project.inchargerId">
                         <span >{{project.contractAmount == null?'-':project.contractAmount | numberToCurrency}} 元</span></el-col>
                     </el-row>
                     <el-row :gutter="10">
@@ -39,13 +40,13 @@
                     </div>
                     <div style="margin-top:10px;color:#999;">负责人</div>
                     <div><el-link style="margin:10px" @click="showUser(project.inchargerId)">{{project.inchargerName}}</el-link></div>
-                    <div style="color:#999;">参与人</div>
-                    <div>
+                    <div v-show="project.isPublic == 0" style="color:#999;">参与人</div>
+                    <div v-show="project.isPublic == 0" >
                         <el-link v-for="item in project.participationList" :key="item.id" style="margin:10px;" @click="showUser(item.id)">{{item.name}}</el-link>
                         <el-button class="el-icon-plus" @click="addMembVisible=true" size="mini"></el-button>
                     </div>
                 </div>
-                <div class="box info" style="margin-top:10px;">
+                <div class="box info" style="margin-top:10px;" v-if="user.id == project.creatorId || user.id == project.inchargerId || user.role > 0">
                     <div><label>成本基线<el-link v-if="user.id == project.creatorId || user.id == project.inchargerId" @click="showEditBase" style="float:right;"><i class="el-icon-edit"  ></i></el-link></label>
                     <el-row :gutter="10">
                         <div v-for="item in projectBaseCostData" :key="item.id">
@@ -53,21 +54,35 @@
                         <el-col :span="7" align="right" ><span style="padding-right:20px;">
                             ¥{{item.baseAmount==null?'-':item.baseAmount | numberToCurrency}}</span></el-col>
                         </div>
-                        <!-- <el-col :span="5" ><span class="gray_label">人工成本:</span></el-col><el-col :span="7" ><span>
-                            ¥{{project.baseMan==null?'-':project.baseMan | numberToCurrency}}</span></el-col>
-                        <el-col :span="5" ><span class="gray_label">费用:</span></el-col><el-col :span="7" ><span>
-                        ¥{{project.baseFee==null?'-':project.baseFee | numberToCurrency}}</span></el-col></el-row>
-                        <el-row :gutter="10"><el-col :span="5" ><span class="gray_label">外包费用:</span></el-col><el-col :span="7" ><span>
-                        ¥{{project.baseOutsourcing==null?'-':project.baseOutsourcing | numberToCurrency}}</span></el-col>
-                        <el-col :span="5" ><span class="gray_label">风险预留金额1:</span></el-col><el-col :span="7" ><span>
-                        ¥{{project.baseRisk1==null?'-':project.baseRisk1 | numberToCurrency}}</span></el-col></el-row>
-                        <el-row :gutter="10"><el-col :span="5" ><span class="gray_label">风险预留金额2:</span></el-col><el-col :span="7" ><span>
-                        ¥{{project.baseRisk2==null?'-':project.baseRisk2 | numberToCurrency}}</span></el-col>
-                        <el-col :span="5" ><span class="gray_label">总成本:</span></el-col><el-col :span="7" ><span>
-                        ¥{{project.budget==null?'-':project.budget | numberToCurrency}}</span></el-col> -->
                     </el-row>
                     </div>
                 </div>
+                <!--项目相关工程专业 -->
+                <div class="box info" style="margin-top:10px;" v-if="user.company.packageEngineering == 1">
+                    <div><label>相关工程专业<el-link v-if="user.id == project.creatorId || user.id == project.inchargerId" @click="showEditProfession" style="float:right;"><i class="el-icon-edit"  ></i></el-link></label>
+                    <el-row :gutter="10" v-for="item in projectProfessionListOnPage" :key="item.id">
+                        <el-col :span="5" ><span >{{item.professionName}}</span></el-col>
+                        <el-col :span="2" ><span >{{item.percentage}}%</span></el-col>
+                        <el-col :span="14" ><span style="margin: 0 5px;font-size:13px;" v-for="memb in item.membList" :key="memb">{{memb.membName}}({{memb.percentage}}%)</span></el-col>
+                        <el-col :span="3" ><span >{{item.inchargerName}}</span></el-col>
+                    </el-row>
+                    </div>
+                </div>
+
+                <!--项目相关领导 -->
+                <div class="box info" style="margin-top:10px;" v-if="user.company.packageEngineering == 1">
+                    <div><label>相关领导<el-link v-if="user.id == project.creatorId || user.id == project.inchargerId" @click="showChooseLeaderTree" style="float:right;"><i class="el-icon-edit"  ></i></el-link></label>
+                    
+                    </div>
+                    <el-row :gutter="10">
+                        <el-col :span="24" ><span v-for="item in chosenLeaders" :key="item.userId" style="margin-right:10px;">
+                            <el-link @click="showUser(item.userId)">{{item.userName}}</el-link>
+                        </span>
+                        </el-col>
+                    </el-row>
+                    
+                </div>
+
                 <div class="box" style="margin-top:10px;">
                     <label>项目统计</label>
                     <div>
@@ -100,27 +115,48 @@
                     </div>
                 </div>
             </el-col>
-            <el-col :span="8" style="background:#fff;border: 1px solid #eeeeee;border-radius:5px;min-height:547px;">
-                <div>
-                    <p><i class="el-icon-trophy"></i><span style="margin-left:5px;">里程碑</span></p>
+            <el-col :span="7">
+                <!--进度显示 -->
+                <div v-if="user.company.packageEngineering == 1" style="margin-bottom:10px;background:#fff;border: 1px solid #eeeeee;border-radius:5px;min-height:305px;padding-left:5px;padding-right:5px;">
+                    <p><i class="el-icon-odometer"></i><span style="margin-left:5px;">完成情况</span></p>
+                    <el-divider></el-divider>
+                    <el-row><el-col :span="12"><span>项目总进度</span></el-col>
+                        <el-col :span="12"><el-progress :percentage="progressData.totalProjectProgress" ></el-progress></el-col></el-row>
+                    <p style="font-size:12px;color:#666;">各专业进度</p>
+                    <el-table :show-header="false" :data="progressData.professionList" row-key="id" size="small"  :tree-props="{children: 'membList', hasChildren: 'hasChildren'}">
+                        <el-table-column
+                        prop="professionName">
+                        </el-table-column>
+                        <el-table-column
+                        prop="progress"
+                        width="100">
+                        <template slot-scope="scope">
+                            <el-progress :percentage="scope.row.progress" ></el-progress>
+                        </template>
+                        </el-table-column>
+                    </el-table>
                 </div>
-                <div>
-                <el-timeline :reverse="reverse" style="padding-left: 3px;">
-                    <el-timeline-item
-                    v-for="(task, index) in mileStoneList"
-                    :key="index"
-                    :color="task.taskStatus==0?'#ddd':'#830BE2'"
-                    size="large"
-                    :timestamp="task.endDate">
-                    {{task.name}}
-                    </el-timeline-item>
-                </el-timeline>
+                
+                <div style="background:#fff;border: 1px solid #eeeeee;border-radius:5px;min-height:547px;padding-left:5px;padding-right:5px;">
+                    <div>
+                    <p><i class="el-icon-trophy"></i><span style="margin-left:5px;">里程碑</span></p>
+                    </div>
+                    <el-timeline :reverse="reverse" style="padding-left: 3px;">
+                        <el-timeline-item
+                        v-for="(task, index) in mileStoneList"
+                        :key="index"
+                        :color="task.taskStatus==0?'#ddd':'#830BE2'"
+                        size="large"
+                        :timestamp="task.endDate">
+                        {{task.name}}
+                        </el-timeline-item>
+                    </el-timeline>
                 </div>
             </el-col>
             </el-row>
         </div>
         <!--用户详细信息弹出框-->
-        <el-dialog title="查看详情" v-if="userDetailVisible" :visible.sync="userDetailVisible" :close-on-click-modal="false" customClass="customWidth" width="400px">
+        <el-dialog title="查看详情" v-if="userDetailVisible" :visible.sync="userDetailVisible" :close-on-click-modal="false" customClass="customWidth" width="500px">
             <div class="line"><span>姓名</span><span>{{userDetail.name}}</span></div>
             <div class="line"><span>手机号码</span><span>{{userDetail.phone}}</span></div>
             <div class="line"><span>角色</span><span>{{roleArray[userDetail.role]}}</span></div>
@@ -219,6 +255,105 @@
                 <el-button type="primary" @click="submitAddMemb" :loading="addLoading">提交</el-button>
             </div>
         </el-dialog>
+
+        <!--修改工程专业界面-->
+        <el-dialog title="工程专业" v-if="editProfessionDialog" :visible.sync="editProfessionDialog" 
+        :close-on-click-modal="false" customClass="customWidth" width="1000px">
+            <el-table :data="projectProfessionList" size="small" max-height="400" :key="Math.random()">
+                <el-table-column prop="professionId" width="200">
+                                   <template slot-scope="scope">
+                                       <el-select v-model="scope.row.professionId" >
+                                           <el-option v-for="item in professionList" :key="item.id" :label="item.name" :value="item.id"/>
+                                       </el-select>
+                                   </template>
+                                   <template slot="header" >
+                                       <span style="font-size:14px;font-weight:normal;">专业名称</span>
+                                   </template>
+                               </el-table-column>
+                               <el-table-column prop="percentage" width="100" label="占比">
+                                   <template slot-scope="scope">
+                                       <el-input type="number" v-model="scope.row.percentage"></el-input>
+                                   </template>
+                               </el-table-column>
+                               
+                               <el-table-column prop="membNames" label="相关人员及占比">
+                                   <template slot-scope="scope">
+                                       <span style="margin:0 5px;" v-for="item in scope.row.membList" :key="item.membId">{{item.membName}}({{item.percentage}}%)</span>
+                                       <el-link @click="showEditPpMembs(scope.row)">{{(scope.row.membList == null || scope.row.membList.length == 0)?'设置专业参与人员':'设置'}}</el-link>
+                                   </template>
+                               </el-table-column>
+                               <el-table-column prop="inchargerName" width="120" label="负责人">
+                                   <template slot-scope="scope">
+                                       <el-select v-model="scope.row.inchargerId" >
+                                           <el-option v-for="item in participator" :key="item.id" :label="item.name" :value="item.id">
+                                               
+                                           </el-option>
+                                       </el-select>
+                                   </template>
+                               </el-table-column>
+                               <el-table-column  width="80">
+                                   <template slot-scope="scope">
+                                       <el-button icon="el-icon-delete" size="mini" style="margin-left:10px;" @click.stop.native="deleteItem(scope.$index)"></el-button>
+                                   </template>
+                                   <template slot="header" >
+                                       <el-link type="primary" :underline="false" @click="addItem">添加</el-link>
+                                   </template>
+                               </el-table-column>
+            </el-table>
+            <div slot="footer" class="dialog-footer">
+                <el-button @click.native="editProfessionDialog = false">取消</el-button>
+                <el-button type="primary" @click="saveProjectProfessions" :loading="addLoading">提交</el-button>
+            </div>
+        </el-dialog>
+
+        <!-- 项目专业人员的设置 -->
+        <el-dialog title="设置项目专业人员"  v-if="editPpMembDialog" :visible.sync="editPpMembDialog" :close-on-click-modal="false" customClass="customWidth" width="600px">
+            <el-table :data="curProfessionRow.membList" height="400">
+                <el-table-column prop="name" label="专业人员">
+                    <template slot-scope="scope">
+                        <el-select v-model="scope.row.membId"  filterable placeholder="请选择专业参与人" style="width:100%;" >
+                            <el-option v-for="item in participator" :key="item.id" :label="item.name" :value="item.id">
+                            </el-option>
+                        </el-select>
+                    </template>
+                </el-table-column>
+                <el-table-column prop="percentage" width="120" label="占比(%)">
+                    <template slot-scope="scope">
+                        <el-input type="number" v-model="scope.row.percentage"></el-input>
+                    </template>
+                </el-table-column>
+                <el-table-column  width="80">
+                    <template slot-scope="scope">
+                        <el-button icon="el-icon-delete" size="mini" style="margin-left:10px;" @click.stop.native="deleteMembItem(scope.$index)"></el-button>
+                    </template>
+                    <template slot="header" >
+                        <el-link @click="addMembItem">添加</el-link>
+                    </template>
+                </el-table-column>
+            </el-table>
+            <div slot="footer" class="dialog-footer">
+                <el-button  @click="editPpMembDialog = false" >取消</el-button>
+                <el-button type="primary" @click="addPpMemb" >确定</el-button>
+            </div>
+        </el-dialog>
+
+        
+        <!-- 按部门选择相关领导 -->
+        <el-dialog title="选择相关领导"  v-if="chooseLeaderVisible" :visible.sync="chooseLeaderVisible" :close-on-click-modal="false" customClass="customWidth" width="500px">
+            <!-- <el-input style="width:100%" v-model="filterName" placeholder="请输入姓名搜索" @change="findUserInTree"></el-input> -->
+            <div class="tree" style="height:400px">
+                <el-scrollbar style="height:100%">
+                <el-tree :data="deptMembData" show-checkbox :props="defaultProps" node-key="id"
+                    ref="chooseLeaderTree" @check-change="onLeaderTreeItemChange" :default-checked-keys="addForm.notifyUserIds"
+                    highlight-current ></el-tree>
+                </el-scrollbar>
+            </div>
+            <div>已选中&nbsp;{{chosenMembCount}}&nbsp;人</div>
+            <div slot="footer" class="dialog-footer">
+                <el-button  @click="chooseLeaderVisible = false" >取消</el-button>
+                <el-button type="primary" @click="chooseLeader()" >确定</el-button>
+            </div>
+        </el-dialog>
     </div>
 </template>
 <style scoped>
@@ -254,6 +389,22 @@
     export default {
         data() {
             return {
+                progressData:{},
+                deptMembData: [
+                    {
+                        id: 0,
+                        label: '未分配',
+                    }
+                ],
+                chosenMembCount:0,
+                chosenLeaders:[],
+                chooseLeaderVisible:false,
+                projectProfessionItem:null,
+                curProfessionRow:null,
+                editPpMembDialog:false,
+                professionList:[],
+                editProfessionDialog: false,
+                projectProfessionList:[],
                 modBaseCostData:[],
                 projectBaseCostData:[],
                 roleArray:["普通员工","超级管理员", "系统管理员", "公司高层","人事管理员", "项目管理员"],
@@ -306,6 +457,125 @@
             }
         },
         methods: {
+            getProgressData() {
+                this.http.post("/project-profession/getProgressData", {projectId: this.curProjectId},
+                    res => {
+                        if (res.code == "ok") {
+                            this.progressData = res.data;
+                        } else {
+                            this.$message({
+                                message: res.msg,
+                                type: "error"
+                            });
+                        }
+                    },
+                    error => {
+                        this.$message({
+                            message: error,
+                            type: "error"
+                        });
+                    });
+            },
+            setUserToDept(list) {
+                for (var i in list) {
+                    if (list[i].children != null) {
+                        this.setUserToDept(list[i].children);
+                    }
+                    
+                    if (list[i].userList != null) {
+                        if (list[i].children == null) {
+                            list[i].children = [];
+                        }
+                        list[i].userList.forEach(element => {
+                            var obj = {id: element.id, label:element.name, parentId:element.departmentId, isUser:1};
+                            list[i].children.push(obj);
+                        });
+                    }
+                }
+            },
+            
+            // 获取部门列表
+            getDepartment() {
+                this.http.post("/department/listAllMemb", {},
+                res => {
+                    if (res.code == "ok") {
+                        var list = res.data;
+                        //设置员工到部门下面
+                        this.setUserToDept(list);
+                        this.deptMembData = list;
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
+            getProjectNotifyUserList(projectId) {
+                this.http.post("/project-notify-user/get", {projectId: projectId},
+                    res => {
+                        if (res.code == "ok") {
+                            var chosenLeaderIds = [];
+                            var chosenLeaderNames = '';
+                            var leaderList = res.data;
+                            this.chosenLeaders = leaderList;
+                            for(var j in leaderList) {
+                                chosenLeaderIds.push(leaderList[j].userId)
+                                chosenLeaderNames += leaderList[j].userName+',';
+                            }
+                            if (chosenLeaderNames.length > 0) {
+                                chosenLeaderNames = chosenLeaderNames.substring(0, chosenLeaderNames.length -1);
+                            }
+                            this.addForm.notifyUserIds = chosenLeaderIds;
+                            this.addForm.notifyUserNames = chosenLeaderNames;
+                        } else {
+                            this.$message({
+                                message: res.msg,
+                                type: "error"
+                            });
+                        }
+                    },
+                    error => {
+                        this.$message({
+                            message: error,
+                            type: "error"
+                        });
+                    });
+            },
+            showChooseLeaderTree() {
+                this.chosenMembCount = this.chosenLeaders.length;
+                this.chooseLeaderVisible = true;
+            },
+            onLeaderTreeItemChange() {
+                var chosenList = this.$refs.chooseLeaderTree.getCheckedNodes();
+                var list = chosenList.filter(item=>item.isUser == 1);
+                this.chosenMembCount = list.length;
+            },
+            //选中相关领导
+            chooseLeader() {
+                this.chooseLeaderVisible = false;
+                var chosenList = this.$refs.chooseLeaderTree.getCheckedNodes();
+                var chosenMembList = chosenList.filter(item=>item.isUser == 1);
+                this.addForm.notifyUserNames = '';
+                this.addForm.notifyUserIds = [];
+                this.chosenLeaders = [];
+                for (var i=0;i<chosenMembList.length; i++) {
+                    this.addForm.notifyUserIds.push(chosenMembList[i].id);
+                    this.addForm.notifyUserNames += chosenMembList[i].label+',';
+                    var item = {userId:chosenMembList[i].id, userName:chosenMembList[i].label};
+                    this.chosenLeaders.push(item);
+                }
+                if (this.addForm.notifyUserNames.length > 0) {
+                    this.addForm.notifyUserNames = this.addForm.notifyUserNames.substring(0, this.addForm.notifyUserNames.length-1);
+                }
+            },
+
             restrictNumber(targetId) {
                 let inpu = document.getElementById(targetId);
                 inpu.value = inpu.value.replace(/[^\d.]/g, "");  //仅保留数字和"."
@@ -316,6 +586,234 @@
                     inpu.value = parseFloat(inpu.value);
                 }
             },
+            addPpMemb() {
+                //检查合计比例是否到达100%
+                let p = 0;
+                let hasNoMemb = false;
+
+                this.curProfessionRow.membList.forEach(m=>{
+                    p += parseInt(m.percentage);
+                    //检查人员是否选择
+                    if (m.membId == null) {
+                        hasNoMemb = true;
+                    }
+                });
+                if (hasNoMemb) {
+                    this.$message({
+                            message: '专业人员不能为空',
+                            type: "error"
+                        });
+                    return;
+                }
+                //检查人员不能重复
+                let hasDuplicate = false;
+                this.curProfessionRow.membList.forEach(m=>{
+                    if (this.curProfessionRow.membList.filter(innerM=>innerM.membId == m.membId).length>1) {
+                        hasDuplicate = true;
+                    }
+                });
+                if (hasDuplicate) {
+                    this.$message({
+                            message: '专业人员不能重复',
+                            type: "error"
+                        });
+                    return;
+                }
+                if (p != 100) {
+                    this.$message({
+                            message: '占比合计必须是100%',
+                            type: "error"
+                        });
+                    return;
+                }
+                
+
+                this.editPpMembDialog = false;
+                this.curProfessionRow.membList.forEach(m=>{
+                    m.membName = this.participator.filter(p=>p.id == m.membId)[0].name;
+                });
+                this.projectProfessionItem.membList = JSON.parse(JSON.stringify(this.curProfessionRow.membList));
+            },
+            //删除专业人员
+            deleteMembItem(index) {
+                this.curProfessionRow.membList.splice(index,1);
+            },
+            //添加专业人员
+            addMembItem() {
+                if (this.curProfessionRow.membList == null) {
+                    this.curProfessionRow.membList = [{membId:null, percentage:100}];
+                } else {
+                    let p = 0;
+                    this.curProfessionRow.membList.forEach(m=>{
+                        p += parseInt(m.percentage);
+                    });
+                    this.curProfessionRow.membList.push({membId:null, percentage:100-p});
+                }
+                this.$forceUpdate();
+            },
+            showEditPpMembs(row) {
+                this.projectProfessionItem = row;
+                this.curProfessionRow = JSON.parse(JSON.stringify(row));
+                if (this.curProfessionRow.membList == null || this.curProfessionRow.membList.length == 0) {
+                    this.curProfessionRow.membList = [{membId:null, percentage:100}];
+                } 
+                this.editPpMembDialog = true;
+            },
+            checkProjectProfession() {
+                //检查合计比例是否到达100%
+                let p = 0;
+                let hasNoMemb = false;
+                let hasNoProfession = false;
+                let hasNoMembList = false;
+                this.projectProfessionList.forEach(m=>{
+                    p += parseInt(m.percentage);
+                    //检查人员是否选择
+                    if (m.inchargerId == null) {
+                        hasNoMemb = true;
+                    }
+                    if (m.professionId == null) {
+                        hasNoProfession = true;
+                    }
+                    if (m.membList == null) {
+                        hasNoMembList = true;
+                    }
+                });
+                
+                if (hasNoProfession) {
+                    this.$message({
+                            message: '专业不能为空',
+                            type: "error"
+                        });
+                    return false;
+                }
+                if (hasNoMembList) {
+                    this.$message({
+                            message: '专业相关人员不能为空',
+                            type: "error"
+                        });
+                    return false;
+                }
+                
+                if (hasNoMemb) {
+                    this.$message({
+                            message: '专业负责人不能为空',
+                            type: "error"
+                        });
+                    return false;
+                }
+                //检查专业不能重复
+                let hasDuplicate = false;
+                this.projectProfessionList.forEach(m=>{
+                    if (this.projectProfessionList.filter(innerM=>innerM.professionId == m.professionId).length>1) {
+                        hasDuplicate = true;
+                    }
+                });
+                if (hasDuplicate) {
+                    this.$message({
+                            message: '专业不能重复',
+                            type: "error"
+                        });
+                    return false;
+                }
+                if (p != 100) {
+                    this.$message({
+                            message: '专业占比合计必须是100%',
+                            type: "error"
+                        });
+                    return false;
+                }
+                return true;
+            },
+            showEditProfession() {
+                if (this.professionList.length == 0) {
+                    this.getProfessionList();
+                }
+                this.projectProfessionList = JSON.parse(JSON.stringify(this.projectProfessionListOnPage));
+                this.editProfessionDialog = true;
+            },
+            getProjectProfessionList() {
+                this.http.post("/project-profession/get", {projectId: this.curProjectId},
+                    res => {
+                        if (res.code == "ok") {
+                            this.projectProfessionListOnPage = res.data;
+                        } else {
+                            this.$message({
+                                message: res.msg,
+                                type: "error"
+                            });
+                        }
+                    },
+                    error => {
+                        this.$message({
+                            message: error,
+                            type: "error"
+                        });
+                    });
+            },
+            //保存项目专业
+            saveProjectProfessions() {
+                if (!this.checkProjectProfession()) {//检查不通过,直接返回
+                    return;
+                }
+                let projectId = this.curProjectId;
+                var list = this.projectProfessionList.filter(p=>p.professionId != null);
+                list.forEach(p=>p.inchargerName = this.participator.filter(m=>m.id == p.inchargerId)[0].name);
+                list.forEach(p=>p.professionName = this.professionList.filter(m=>m.id == p.professionId)[0].name);
+                this.http.post("/project-profession/modify", {projectId: projectId, json: JSON.stringify(list)},
+                res => {
+                    if (res.code == "ok") {
+                        this.editProfessionDialog = false;
+                        this.projectProfessionListOnPage = res.data;
+                        this.$message({
+                            message: '修改成功',
+                            type: "success"
+                        });
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
+            //删除项目专业
+            deleteItem(index) {
+                this.projectProfessionList.splice(index,1);
+            },
+            //添加项目专业
+            addItem() {
+                let p = 0;
+                this.projectProfessionList.forEach(m=>{
+                    p += parseInt(m.percentage);
+                });
+                this.projectProfessionList.push( { professionId:null, inchargerId:null,membIds:[], percentage: 100-p});
+            },
+            getProfessionList() {
+                this.http.post("/profession/getAll", {},
+                res => {
+                    if (res.code == "ok") {
+                        var list = res.data;
+                        this.professionList = list;
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
             getProjectBaseData(projectId) {
                 this.http.post('/project-basecost/get',{projectId: projectId},
                         res => {
@@ -582,6 +1080,7 @@
                         }
                 });
             },
+            
             showEditBase() {
                 this.addForm = JSON.parse(JSON.stringify(this.project));
                 var list = this.project.participationList , arr = [];
@@ -748,6 +1247,7 @@
                     this.listLoading = false;
                     if (res.code == "ok") {
                         this.project = res.data;
+                        this.participator = this.project.participationList;
                     } else {
                         this.$message({
                         message: res.msg,
@@ -762,12 +1262,16 @@
                         type: "error"
                     });
                 });
+                this.getProjectProfessionList();
             },
             refreshPage() {
                 this.curProjectId = parseInt(this.$route.params.id);
                 this.getMileStoneList();
                 this.getProjectInfo();
                 this.getProjectTaskSum();
+                this.getProjectBaseData(this.curProjectId);
+                this.getProjectNotifyUserList(this.curProjectId);
+                this.getProgressData();
             }
         },
         created() {
@@ -782,11 +1286,15 @@
         mounted() {
             console.log('mounted===');
             this.curProjectId = parseInt(this.$route.params.id);
+            this.getDepartment();
+            this.getUsers();
+
             this.getMileStoneList();
             this.getProjectInfo();
-            this.getUsers();
             this.getProjectTaskSum();
             this.getProjectBaseData(this.curProjectId);
+            this.getProjectNotifyUserList(this.curProjectId);
+            this.getProgressData();
         }
     };
 </script>

+ 472 - 65
fhKeeper/formulahousekeeper/timesheet/src/views/project/list.vue

@@ -113,7 +113,7 @@
         </el-col>
 
         <!--新增界面-->
-        <el-dialog :title="title" v-if="addFormVisible" :visible.sync="addFormVisible" :close-on-click-modal="false" customClass="customWidth" width="800px">
+        <el-dialog :title="title" v-if="addFormVisible" :visible.sync="addFormVisible" :close-on-click-modal="false" customClass="customWidth" width="960px">
             <el-form ref="form1" :model="addForm" :rules="rules" label-width="120px">
                 <el-form-item label="项目编号" >
                     <el-input v-model="addForm.code" :disabled="user.role==0" placeholder="请输入项目编号" clearable></el-input>
@@ -121,19 +121,29 @@
                 <el-form-item label="项目名称" prop="name">
                     <el-input v-model="addForm.name" :disabled="user.role==0" placeholder="请输入项目名称" clearable></el-input>
                 </el-form-item>
-                <el-form-item label="客户" v-if="user.company.packageCustomer == 1">
-                    <el-select v-model="addForm.customerId" clearable="true" filterable placeholder="请选择客户" style="width:100%;" >
+                <el-form-item label="项目类型" prop="isPublic">
+                    <el-select v-model="addForm.isPublic" style="width:32%;" @change="selectPublic">
+                        <el-option :value="0" label="普通项目"></el-option>
+                        <el-option :value="1" label="公共项目"></el-option>
+                    </el-select>
+                    <el-tooltip effect="dark" content="普通项目只对参与人员开放,公共项目对所有成员开放" placement="top-start">
+                    <i class="el-icon-question"></i>
+                    </el-tooltip>
+                    <span style="margin-left:63px;margin-right:10px;" v-if="user.company.packageCustomer == 1">客户</span>
+                    <el-select v-model="addForm.customerId" clearable="true" filterable placeholder="请选择客户" style="width:33%;" >
                         <el-option v-for="item in customerList" :key="item.id" :label="item.customerName" :value="item.id"></el-option>
                     </el-select>
                 </el-form-item>
-                <el-form-item label="全部参与者">
+                <!-- <el-form-item label="客户" v-if="user.company.packageCustomer == 1">
+                    <el-select v-model="addForm.customerId" clearable="true" filterable placeholder="请选择客户" style="width:100%;" >
+                        <el-option v-for="item in customerList" :key="item.id" :label="item.customerName" :value="item.id"></el-option>
+                    </el-select>
+                </el-form-item> -->
+                <el-form-item label="全部参与者" v-show="addForm.isPublic == 0">
                     <el-input  @focus="showChooseMembTree" v-model="addForm.userNames"></el-input>
-                    <!-- <el-select v-model="addForm.userId" multiple filterable placeholder="请选择参与者" style="width:100%;" @change="changeParticipator">
-                        <el-option v-for="item in users" :key="item.id" :label="item.name" :value="item.id"></el-option>
-                    </el-select> -->
                 </el-form-item>
                 <el-form-item label="负责人" >
-                    <el-select v-model="addForm.inchargerId" :disabled="addForm.userId.length==0 ||  user.role==0" filterable placeholder="请选择负责人" style="width:100%;" >
+                    <el-select v-model="addForm.inchargerId" :disabled="(addForm.userId.length==0 ||  user.role==0) && addForm.isPublic == 0" filterable placeholder="请选择负责人" style="width:100%;" >
                         <el-option v-for="item in participator" :key="item.id" :label="item.name" :value="item.id"></el-option>
                     </el-select>
                 </el-form-item>
@@ -142,79 +152,103 @@
                     <el-select v-model="addForm.level"  placeholder="请选择级别" style="width:32%;" >
                         <el-option v-for="item in importanceList" :key="item.id" :label="item.label" :value="item.id"></el-option>
                     </el-select>
-                    <!-- <span style="margin-left:50px;margin-right:10px;" v-if="user.company.packageProject==1">项目预算</span>
-                    <el-input v-model="addForm.budget"    style="width:32%;"
-                    placeholder="整数" clearable @keyup.native="number"></el-input><span style="margin-left:10px;">元</span> -->
-
                 <!-- 增加合同金额字段 -->
                         <span style="margin-left:63px;margin-right:10px;" v-if="user.company.packageProject==1">合同金额</span>
                         <el-input id="contractAmount" v-model="addForm.contractAmount" style="width:33%;"
                         placeholder="整数" clearable  @keyup.native="restrictNumber('contractAmount')" ></el-input><span style="margin-left:10px;">元</span>
-                <!-- 增加合同金额字段 -->   
-
+                <!-- 增加合同金额字段 -->
                 </el-form-item>
-                <el-form-item label="开始日期" prop="planStartDate" v-if="user.company.packageProject==1" style="float: left" >
+                <el-form-item label="开始日期" prop="planStartDate" v-if="user.company.packageProject==1"  >
                     <el-date-picker v-model="addForm.planStartDate" 
-                     :editable="false" 
+                     :editable="false" style="width:32%;" 
                      format="yyyy-MM-dd" 
                      value-format="yyyy-MM-dd"
                      :clearable="false" type="date" 
                      placeholder="选择日期"></el-date-picker>
-                </el-form-item>
-                <el-form-item label="截止日期" prop="planEndDate" v-if="user.company.packageProject==1" style="float: left" >
-                    <el-date-picker v-model="addForm.planEndDate" 
+
+                    <span style="margin-left:63px;margin-right:10px;" >截止日期</span>
+                    <el-date-picker v-model="addForm.planEndDate" style="width:33%;"
                      :editable="false" 
                      format="yyyy-MM-dd" 
                      value-format="yyyy-MM-dd"
                      :clearable="false" type="date" 
                      placeholder="选择日期"></el-date-picker>
                 </el-form-item>
-            <!-- 项目基线 -->
-                <!-- <div style="width: 100%;border: 1px solid #ddd"></div> -->
                 
-                <div style="margin: 90px 0 30px 0" >
-                    <el-divider></el-divider>
-                    <span class="el-dialog__title">成本基线</span>
-                </div>
-            <!--新版 -->
-            <span class="rg_span" v-for="(item, index) in projectBaseCostData" :key="item.id">
-                <span style="width:120px;display: inline-block;" v-if="user.company.packageProject==1">{{item.baseName}}</span>
-                <el-input :id="'baseCost'+index" @input="addUpfun()" v-model="item.baseAmount" style="width:200px; margin-bottom: 20px"
-                placeholder="整数" clearable  @keyup.native="restrictNumber('baseCost'+index)"></el-input><span style="margin-left:10px;">元</span>
-            </span>    
+                <!-- 项目基线 -->
+                <div style="margin: 10px 0 30px 0;min-height:200px;" >
+                    <el-tabs v-model="activeName" @tab-click="handleClick">
+                    <el-tab-pane label="成本基线" name="baseCostPanel" >
+                        <div style="padding-top:10px;">
+                            <!--新版 -->
+                            <span class="rg_span" v-for="(item, index) in projectBaseCostData" :key="item.id">
+                                <span style="width:120px;display: inline-block;" v-if="user.company.packageProject==1">{{item.baseName}}</span>
+                                <el-input :id="'baseCost'+index" @input="addUpfun()" v-model="item.baseAmount" style="width:200px; margin-bottom: 20px"
+                                placeholder="整数" clearable  @keyup.native="restrictNumber('baseCost'+index)"></el-input><span style="margin-left:10px;">元</span>
+                            </span>    
 
-            <!-- <span class="rg_span">
-                <span style="width:120px;display: inline-block;" v-if="user.company.packageProject==1">人工成本</span>
-                <el-input @input="addUpfun(addForm.baseMan)" v-model="addForm.baseMan" style="width:200px; margin-bottom: 20px"
-                placeholder="整数" clearable  @keyup.native="addForm.baseMan=addForm.baseMan.replace(/[^\d]/g,'');"></el-input><span style="margin-left:10px;">元</span>
-            </span>
-            <span class="rg_span">
-                <span style="width:120px;display: inline-block;" v-if="user.company.packageProject==1">费用</span>
-                <el-input @input="addUpfun(addForm.baseFee)" v-model="addForm.baseFee" style="width:200px;"
-                placeholder="整数" clearable  @keyup.native="addForm.baseFee=addForm.baseFee.replace(/[^\d]/g,'');"></el-input><span style="margin-left:10px;">元</span>
-            </span>
-            <div class="rg_span" style="margin-bottom: 20px;">
-                <span style=" width:120px;display: inline-block;" v-if="user.company.packageProject==1">外包费用</span>
-                <el-input @input="addUpfun(addForm.baseOutsourcing)" v-model="addForm.baseOutsourcing" style="width:200px;"
-                placeholder="整数" clearable @keyup.native="addForm.baseOutsourcing=addForm.baseOutsourcing.replace(/[^\d]/g,'');"></el-input><span style="margin-left:10px;">元</span>
-            </div>
-            <div style="display: inline-block;" class="rg_span">
-                <span style="width:120px;display: inline-block;text-align: right;" v-if="user.company.packageProject==1">预留风险金额1</span>
-                <el-input @input="addUpfun(addForm.baseRisk1)" v-model="addForm.baseRisk1" style="width:200px;"
-                placeholder="整数" clearable @keyup.native="addForm.baseRisk1=addForm.baseRisk1.replace(/[^\d]/g,'');"></el-input><span style="margin-left:10px;">元</span>
-            </div>
-            <div class="rg_span">
-                <span style="width:120px;display: inline-block;text-align: right;" v-if="user.company.packageProject==1">预留风险金额2</span>
-                <el-input @input="addUpfun(addForm.baseRisk2)" v-model="addForm.baseRisk2" style="width:200px;"
-                placeholder="整数" clearable @keyup.native="addForm.baseRisk2=addForm.baseRisk2.replace(/[^\d]/g,'');"></el-input><span style="margin-left:10px;">元</span>
-            </div> -->
-            <!-- 合计 -->
-            <div style="margin-top: 10px;float:right;">
-                <span style="margin-right:50px;margin-right:10px;" v-if="user.company.packageProject==1">合计</span>
-                 <span v-if="addForm.budget <= 0 || addForm.budget == undefined">0</span>
-                 <span v-else>{{addForm.budget | numberToCurrency}}</span>
-                <span style="margin-right:50px;margin-left:10px;">元</span>
-            </div>
+                            <!-- 合计 -->
+                            <div style="margin-top: 10px;float:right;">
+                                <span style="margin-right:50px;margin-right:10px;" v-if="user.company.packageProject==1">合计</span>
+                                <span v-if="addForm.budget <= 0 || addForm.budget == undefined">0</span>
+                                <span v-else>{{addForm.budget | numberToCurrency}}</span>
+                                <span style="margin-right:50px;margin-left:10px;">元</span>
+                            </div>
+                        </div>
+                    </el-tab-pane>
+                    <el-tab-pane label="工程专业" name="engineeringProfession" >
+                       <div style="padding-top:10px;">
+                           <el-table :data="projectProfessionList" size="small" :key="Math.random()">
+                               <el-table-column prop="professionId" width="200">
+                                   <template slot-scope="scope">
+                                       <el-select v-model="scope.row.professionId" >
+                                           <el-option v-for="item in professionList" :key="item.id" :label="item.name" :value="item.id"/>
+                                       </el-select>
+                                   </template>
+                                   <template slot="header" >
+                                       <span style="font-size:14px;font-weight:normal;">专业名称</span>
+                                   </template>
+                               </el-table-column>
+                               <el-table-column prop="percentage" width="120" label="占比(%)">
+                                   <template slot-scope="scope">
+                                       <div><el-input type="number" v-model="scope.row.percentage"></el-input></div>
+                                   </template>
+                               </el-table-column>
+                               
+                               <el-table-column prop="membNames" label="相关人员及占比">
+                                   <template slot-scope="scope">
+                                       <span style="margin:0 5px;" v-for="item in scope.row.membList" :key="item.membId">{{item.membName}}({{item.percentage}}%)</span>
+                                       <el-link @click="showEditPpMembs(scope.row)">{{(scope.row.membList == null || scope.row.membList.length == 0)?'设置专业参与人员':'设置'}}</el-link>
+                                   </template>
+                               </el-table-column>
+                               <el-table-column prop="inchargerName" width="120" label="负责人">
+                                   <template slot-scope="scope">
+                                       <el-select v-model="scope.row.inchargerId" >
+                                           <el-option v-for="item in participator" :key="item.id" :label="item.name" :value="item.id">
+                                               
+                                           </el-option>
+                                       </el-select>
+                                   </template>
+                               </el-table-column>
+                               <el-table-column  width="80">
+                                   <template slot-scope="scope">
+                                       <el-button icon="el-icon-delete" size="mini" style="margin-left:10px;" @click.stop.native="deleteItem(scope.$index)"></el-button>
+                                   </template>
+                                   <template slot="header" >
+                                       <el-link type="primary" :underline="false" @click="addItem">添加</el-link>
+                                   </template>
+                               </el-table-column>
+                           </el-table>
+                       </div>
+                    </el-tab-pane>
+                    <el-tab-pane label="相关领导" name="leaders" >
+                       <div style="padding-top:10px;">
+                           <el-input @focus="showChooseLeaderTree" v-model="addForm.notifyUserNames" placeholder="请选择需要接收审核通知的相关领导"></el-input>
+                       </div>
+                    </el-tab-pane>
+                    </el-tabs>
+                </div>
+            
             </el-form>
             <div slot="footer" class="dialog-footer;">
                 <el-button @click.native="addFormVisible = false">取消</el-button>
@@ -322,6 +356,53 @@
             </div>
         </el-dialog>
 
+        <!-- 按部门选择相关领导 -->
+        <el-dialog title="选择相关领导"  v-if="chooseLeaderVisible" :visible.sync="chooseLeaderVisible" :close-on-click-modal="false" customClass="customWidth" width="500px">
+            <!-- <el-input style="width:100%" v-model="filterName" placeholder="请输入姓名搜索" @change="findUserInTree"></el-input> -->
+            <div class="tree" style="height:400px">
+                <el-scrollbar style="height:100%">
+                <el-tree :data="deptMembData" show-checkbox :props="defaultProps" node-key="id"
+                    ref="chooseLeaderTree" @check-change="onLeaderTreeItemChange" :default-checked-keys="addForm.notifyUserIds"
+                    highlight-current ></el-tree>
+                </el-scrollbar>
+            </div>
+            <div>已选中&nbsp;{{chosenMembCount}}&nbsp;人</div>
+            <div slot="footer" class="dialog-footer">
+                <el-button  @click="chooseLeaderVisible = false" >取消</el-button>
+                <el-button type="primary" @click="chooseLeader()" >确定</el-button>
+            </div>
+        </el-dialog>
+
+        <!-- 项目专业人员的设置 -->
+        <el-dialog title="设置项目专业人员"  v-if="editPpMembDialog" :visible.sync="editPpMembDialog" :close-on-click-modal="false" customClass="customWidth" width="600px">
+            <el-table :data="curProfessionRow.membList" height="400">
+                <el-table-column prop="name" label="专业人员">
+                    <template slot-scope="scope">
+                        <el-select v-model="scope.row.membId"  filterable placeholder="请选择专业参与人" style="width:100%;" >
+                            <el-option v-for="item in participator" :key="item.id" :label="item.name" :value="item.id">
+                            </el-option>
+                        </el-select>
+                    </template>
+                </el-table-column>
+                <el-table-column prop="percentage" width="120" label="占比(%)">
+                    <template slot-scope="scope">
+                        <el-input type="number" v-model="scope.row.percentage"></el-input>
+                    </template>
+                </el-table-column>
+                <el-table-column  width="80">
+                    <template slot-scope="scope">
+                        <el-button icon="el-icon-delete" size="mini" style="margin-left:10px;" @click.stop.native="deleteMembItem(scope.$index)"></el-button>
+                    </template>
+                    <template slot="header" >
+                        <el-link @click="addMembItem">添加</el-link>
+                    </template>
+                </el-table-column>
+            </el-table>
+            <div slot="footer" class="dialog-footer">
+                <el-button  @click="editPpMembDialog = false" >取消</el-button>
+                <el-button type="primary" @click="addPpMemb" >确定</el-button>
+            </div>
+        </el-dialog>
     </section>
 </template>
 <style scoped>
@@ -343,6 +424,14 @@
     export default {
         data() {
             return {
+                chosenLeaders:[],
+                chooseLeaderVisible:false,
+                projectProfessionItem:null,
+                curProfessionRow:null,
+                editPpMembDialog:false,
+                professionList:[],
+                projectProfessionList:[],
+                activeName:"baseCostPanel",
                 chosenMembCount:0,
                 chosenMembList:[],//选中的人员
                 allMembData:[],
@@ -426,6 +515,257 @@
             }
         },
         methods: {
+            selectPublic() {
+                if (this.addForm.isPublic == 1) {
+                    this.participator = this.users;
+                } else {
+                    this.participator = [];
+                }
+            },
+            addPpMemb() {
+                //检查合计比例是否到达100%
+                let p = 0;
+                let hasNoMemb = false;
+
+                this.curProfessionRow.membList.forEach(m=>{
+                    p += parseInt(m.percentage);
+                    //检查人员是否选择
+                    if (m.membId == null) {
+                        hasNoMemb = true;
+                    }
+                });
+                if (hasNoMemb) {
+                    this.$message({
+                            message: '专业人员不能为空',
+                            type: "error"
+                        });
+                    return;
+                }
+                //检查人员不能重复
+                let hasDuplicate = false;
+                this.curProfessionRow.membList.forEach(m=>{
+                    if (this.curProfessionRow.membList.filter(innerM=>innerM.membId == m.membId).length>1) {
+                        hasDuplicate = true;
+                    }
+                });
+                if (hasDuplicate) {
+                    this.$message({
+                            message: '专业人员不能重复',
+                            type: "error"
+                        });
+                    return;
+                }
+                if (p != 100) {
+                    this.$message({
+                            message: '占比合计必须是100%',
+                            type: "error"
+                        });
+                    return;
+                }
+                
+
+                this.editPpMembDialog = false;
+                this.curProfessionRow.membList.forEach(m=>{
+                    m.membName = this.participator.filter(p=>p.id == m.membId)[0].name;
+                });
+                this.projectProfessionItem.membList = JSON.parse(JSON.stringify(this.curProfessionRow.membList));
+            },
+            //删除专业人员
+            deleteMembItem(index) {
+                this.curProfessionRow.membList.splice(index,1);
+            },
+            //添加专业人员
+            addMembItem() {
+                if (this.curProfessionRow.membList == null) {
+                    this.curProfessionRow.membList = [{membId:null, percentage:100}];
+                } else {
+                    let p = 0;
+                    this.curProfessionRow.membList.forEach(m=>{
+                        p += parseInt(m.percentage);
+                    });
+                    this.curProfessionRow.membList.push({membId:null, percentage:100-p});
+                }
+                this.$forceUpdate();
+            },
+            showEditPpMembs(row) {
+                this.projectProfessionItem = row;
+                this.curProfessionRow = JSON.parse(JSON.stringify(row));
+                if (this.curProfessionRow.membList == null || this.curProfessionRow.membList.length == 0) {
+                    this.curProfessionRow.membList = [{membId:null, percentage:100}];
+                } 
+                this.editPpMembDialog = true;
+            },
+            getProjectNotifyUserList(projectId) {
+                this.http.post("/project-notify-user/get", {projectId: projectId},
+                    res => {
+                        if (res.code == "ok") {
+                            var chosenLeaderIds = [];
+                            var chosenLeaderNames = '';
+                            var leaderList = res.data;
+                            this.chosenLeaders = leaderList;
+                            for(var j in leaderList) {
+                                chosenLeaderIds.push(leaderList[j].userId)
+                                chosenLeaderNames += leaderList[j].userName+',';
+                            }
+                            if (chosenLeaderNames.length > 0) {
+                                chosenLeaderNames = chosenLeaderNames.substring(0, chosenLeaderNames.length -1);
+                            }
+                            this.addForm.notifyUserIds = chosenLeaderIds;
+                            this.addForm.notifyUserNames = chosenLeaderNames;
+                        } else {
+                            this.$message({
+                                message: res.msg,
+                                type: "error"
+                            });
+                        }
+                    },
+                    error => {
+                        this.$message({
+                            message: error,
+                            type: "error"
+                        });
+                    });
+            },
+            getProjectProfessions(projectId) {
+                this.http.post("/project-profession/get", {projectId: projectId},
+                    res => {
+                        if (res.code == "ok") {
+                            this.projectProfessionList = res.data;
+                        } else {
+                            this.$message({
+                                message: res.msg,
+                                type: "error"
+                            });
+                        }
+                    },
+                    error => {
+                        this.$message({
+                            message: error,
+                            type: "error"
+                        });
+                    });
+            },
+
+            checkProjectProfession() {
+                //检查合计比例是否到达100%
+                let p = 0;
+                let hasNoMemb = false;
+                let hasNoProfession = false;
+                let hasNoMembList = false;
+                this.projectProfessionList.forEach(m=>{
+                    p += parseInt(m.percentage);
+                    //检查人员是否选择
+                    if (m.inchargerId == null) {
+                        hasNoMemb = true;
+                    }
+                    if (m.professionId == null) {
+                        hasNoProfession = true;
+                    }
+                    if (m.membList == null) {
+                        hasNoMembList = true;
+                    }
+                });
+                
+                if (hasNoProfession) {
+                    this.$message({
+                            message: '专业不能为空',
+                            type: "error"
+                        });
+                    return false;
+                }
+                if (hasNoMembList) {
+                    this.$message({
+                            message: '专业相关人员不能为空',
+                            type: "error"
+                        });
+                    return false;
+                }
+                if (hasNoMemb) {
+                    this.$message({
+                            message: '专业负责人不能为空',
+                            type: "error"
+                        });
+                    return false;
+                }
+                //检查专业不能重复
+                let hasDuplicate = false;
+                this.projectProfessionList.forEach(m=>{
+                    if (this.projectProfessionList.filter(innerM=>innerM.professionId == m.professionId).length>1) {
+                        hasDuplicate = true;
+                    }
+                });
+                if (hasDuplicate) {
+                    this.$message({
+                            message: '专业不能重复',
+                            type: "error"
+                        });
+                    return false;
+                }
+                if (this.projectProfessionList.length > 0 && p != 100) {
+                    this.$message({
+                            message: '专业占比合计必须是100%',
+                            type: "error"
+                        });
+                    return false;
+                }
+                return true;
+            },
+            //保存项目专业
+            saveProjectProfessions(projectId) {
+                var list = this.projectProfessionList.filter(p=>p.professionId != null);
+                list.forEach(p=>p.inchargerName = this.participator.filter(m=>m.id == p.inchargerId)[0].name);
+                list.forEach(p=>p.professionName = this.professionList.filter(m=>m.id == p.professionId)[0].name);
+                this.http.post("/project-profession/modify", {projectId: projectId, json: JSON.stringify(list)},
+                res => {
+                    if (res.code == "ok") {
+                        
+                    return false;
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
+            //删除项目专业
+            deleteItem(index) {
+                this.projectProfessionList.splice(index,1);
+            },
+            //添加项目专业
+            addItem() {
+                let p = 0;
+                this.projectProfessionList.forEach(m=>{
+                    p += parseInt(m.percentage);
+                });
+                this.projectProfessionList.push( { professionId:null, inchargerId:null,membIds:[], percentage: 100-p});
+            },
+            getProfessionList() {
+                this.http.post("/profession/getAll", {},
+                res => {
+                    if (res.code == "ok") {
+                        var list = res.data;
+                        this.professionList = list;
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
             restrictNumber(targetId) {
                 let inpu = document.getElementById(targetId);
                 inpu.value = inpu.value.replace(/[^\d.]/g, "");  //仅保留数字和"."
@@ -436,6 +776,16 @@
                     inpu.value = parseFloat(inpu.value);
                 }
             },
+            showChooseLeaderTree() {
+                this.chosenMembCount = this.chosenLeaders.length;
+                this.chooseLeaderVisible = true;
+            },
+            onLeaderTreeItemChange() {
+                var chosenList = this.$refs.chooseLeaderTree.getCheckedNodes();
+                var list = chosenList.filter(item=>item.isUser == 1);
+                this.chosenMembCount = list.length;
+            },
+
             showChooseMembTree() {
                 this.chosenMembCount = this.participator.length;
                 this.chooseParticipVisible = true;
@@ -490,6 +840,26 @@
                     this.addForm.userNames = this.addForm.userNames.substring(0, this.addForm.userNames.length-1);
                 }
             },
+
+            //选中相关领导
+            chooseLeader() {
+                this.chooseLeaderVisible = false;
+                var chosenList = this.$refs.chooseLeaderTree.getCheckedNodes();
+                var chosenMembList = chosenList.filter(item=>item.isUser == 1);
+                this.addForm.notifyUserNames = '';
+                this.addForm.notifyUserIds = [];
+                this.chosenLeaders = [];
+                for (var i=0;i<chosenMembList.length; i++) {
+                    this.addForm.notifyUserIds.push(chosenMembList[i].id);
+                    this.addForm.notifyUserNames += chosenMembList[i].label+',';
+                    var item = {userId:chosenMembList[i].id, userName:chosenMembList[i].label};
+                    this.chosenLeaders.push(item);
+                }
+                if (this.addForm.notifyUserNames.length > 0) {
+                    this.addForm.notifyUserNames = this.addForm.notifyUserNames.substring(0, this.addForm.notifyUserNames.length-1);
+                }
+            },
+
             // 获取部门列表
             getDepartment() {
                 this.http.post("/department/listAllMemb", {},
@@ -889,12 +1259,15 @@
                     this.title = "新增项目";
                     this.addForm = {
                         name: '',
+                        isPublic:0,
                         userId: [],
                         userNames:'',
                         code:'',
                         inchargerId:null,
                         level:1,
                         customerId:null,
+                        notifyUserNames:'',
+                        chosenLeaders:[],
                     }
                     this.projectBaseCostData = [];
                     for (var m=0;m<this.baseCostItemList.length; m++) {
@@ -902,7 +1275,11 @@
                     }
                 } else {
                     this.title = "修改项目";
-                    var list = item.participator , arr = [];
+                    var list = item.participator;
+                    if (item.isPublic == 1) {
+                        list = this.users;
+                    }
+                    var arr = [];
                     var names = '';
                     for(var j in list) {
                         arr.push(list[j].id)
@@ -911,9 +1288,11 @@
                     if (names.length > 0) {
                         names = names.substring(0, names.length -1);
                     }
+                    
                     this.addForm = {
                         id: item.id,
                         name: item.projectName,
+                        isPublic: item.isPublic,
                         userId: arr,
                         userNames:names,
                         code:item.projectCode,
@@ -932,8 +1311,19 @@
                     }
                     this.changeParticipator();
                     this.getProjectBaseData(item.id);
+                    if (this.user.company.packageEngineering == 1) {
+                        this.getProjectProfessions(item.id);
+                        //获取项目的相关领导
+                        this.getProjectNotifyUserList(item.id);
+                    }
                 }
                 this.addFormVisible = true;
+                if (this.user.company.packageEngineering == 1) {
+                    if (this.professionList.length == 0) {
+                        this.getProfessionList();
+                    }
+                }
+
             },
 
             getProjectBaseData(projectId) {
@@ -1016,13 +1406,21 @@
             submitInsert() {
                 this.$refs.form1.validate(valid => {
                     if (valid) {
+                        if (this.user.company.packageEngineering == 1) {
+                            if (!this.checkProjectProfession()) {//检查不通过,直接返回
+                                return;
+                            }
+                        }
                         this.addLoading = true;
                         let formData = new FormData();
                         formData.append("name", this.addForm.name);
                         if(this.addForm.id != null) {
                             formData.append("id", this.addForm.id);
                         }
-                        if(this.addForm.userId.length != 0) {
+                        if(this.addForm.isPublic != null) {
+                            formData.append("isPublic", this.addForm.isPublic);
+                        }
+                        if(this.addForm.userId.length != 0 && this.addForm.isPublic == 0) {
                             for(var j in this.addForm.userId) {
                                 formData.append("userId", this.addForm.userId[j]);
                             }
@@ -1058,6 +1456,11 @@
                         } else {
                             formData.append("customerId", this.addForm.customerId);
                         }
+                        if (this.user.company.packageEngineering == 1) {
+                            if (this.chosenLeaders != null && this.chosenLeaders.length > 0) {
+                                formData.append("chosenLeaders", JSON.stringify(this.chosenLeaders));
+                            }
+                        }
                         
                         this.http.uploadFile(this.port.project.add,formData,
                         res => {
@@ -1069,6 +1472,9 @@
                                 });
                                 this.addFormVisible = false;
                                 this.getList();
+                                if (this.user.company.packageEngineering == 1) {
+                                    this.saveProjectProfessions(res.data);
+                                }
                             } else {
                                 this.$message({
                                     message: res.msg,
@@ -1144,6 +1550,7 @@
             this.getUsers();
             this.getCustomerList();
             this.getProjectBaseConfigList();
+            
         }
     };
 </script>

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

@@ -224,7 +224,7 @@
             <el-tab-pane label="文件中心" name="files"><FileCenter ref="fileCenter"></FileCenter></el-tab-pane>
             <el-tab-pane label="项目概览" name="info"><ProjectInfo ref="projectInfo" @basecost-change="changeBase"></ProjectInfo></el-tab-pane>
             <el-tab-pane label="数据统计" name="summary"><Summary ref="summary"></Summary></el-tab-pane>
-            <el-tab-pane label="挣值分析" name="earning" v-if="user.role >= 1 && user.role<=3"><Earning ref="earning"></Earning></el-tab-pane>
+            <el-tab-pane label="挣值分析" name="earning" v-if="user.id == currentProject.creator || user.id == currentProject.incharger_id || user.role == 1 || user.role == 2 || user.role == 3 || user.role == 6"><Earning ref="earning"></Earning></el-tab-pane>
         </el-tabs>
         
         <!--新增任务界面-->
@@ -347,7 +347,7 @@
                             <i class="el-icon-success" style="color: #fd4d47;" v-else></i>
                             <span v-if="item.status == 0">状态正常</span>
                             <span v-else-if="item.status == 1">状态正常</span>
-                            <span v-else>进展期</span>
+                            <span v-else>进展期</span>
 
                             <el-dropdown trigger="click" style="float:right;cursor:pointer; float: right;">
                                     <i class="el-icon-more" ></i>

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

@@ -19,7 +19,7 @@
             </el-form-item>    
             <el-form-item label="每日正常工作时长" prop="allday">
                 <el-select v-model="timeType.allday" placeholder="请选择工作时长" style="width:120px;" @change="timeChange">
-                    <el-option v-for="item in times" :key="item" :label="item" :value="item"></el-option>
+                    <el-option v-for="item in times" :key="item" :label="item.toFixed(1)" :value="item"></el-option>
                 </el-select>
                 小时
             </el-form-item>
@@ -46,19 +46,19 @@
         <span style="color:#999;">请设置时长</span>
         <el-form-item label="全天时长" prop="allday">
             <el-select v-model="timeType.allday" placeholder="请选择工作时长" style="width:150px;" @change="timeChange">
-                <el-option v-for="item in times" :key="item" :label="item" :value="item"></el-option>
+                <el-option v-for="item in times" :key="item" :label="item.toFixed(1)" :value="item"></el-option>
             </el-select>
             小时
         </el-form-item>
         <el-form-item label="上午时长" prop="am" >
             <el-select v-model="timeType.am" placeholder="请选择工作时长" style="width:150px;" @change="timeChange">
-                <el-option v-for="item in halfTime" :key="item" :label="item" :value="item"></el-option>
+                <el-option v-for="item in halfTime" :key="item" :label="item.toFixed(1)" :value="item"></el-option>
             </el-select>
             小时
         </el-form-item>
         <el-form-item label="下午时长" prop="pm" >
             <el-select v-model="timeType.pm" disabled placeholder="请选择工作时长" style="width:150px;" >
-                <el-option v-for="item in halfTime" :key="item" :label="item" :value="item"></el-option>
+                <el-option v-for="item in halfTime" :key="item" :label="item.toFixed(1)" :value="item"></el-option>
             </el-select>
             小时
         </el-form-item>
@@ -207,7 +207,7 @@
             },
             timeChange() {
                 this.halfTime = [];
-                for (var i=1;i<=this.timeType.allday; i++) {
+                for (var i=1.0;i<=this.timeType.allday; i+=0.5) {
                     this.halfTime.push(i);
                 }
                 if (this.timeType.allday < this.timeType.am) {
@@ -217,9 +217,9 @@
                
             },
             initTime() {
-                for (var i=1; i<=24; i++) {
+                for (var i=1.0; i<=12.0; i+=0.5) {
                    this.times.push(i);
-                   if ( i <= 12) {
+                   if ( i <= 6) {
                        this.halfTime.push(i);
                    }
                 }

+ 171 - 9
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue

@@ -3,10 +3,12 @@
         <!--列表-->
         <div>
             <el-card class="box-card daily" shadow="never">
-                <div slot="header" class="clearfix" id="clearfix" style="padding-left: 195px;">
+                <div slot="header" class="clearfix" id="clearfix" style="padding-left: 255px;">
                     <div class="jjk" style="display:inline-block;position:fixed;top:70px;background:#fff;left:250px;">
-                        <el-date-picker size="small" v-model="date" :editable="false" format="yyyy-MM" value-format="yyyy-MM" style="width:190px;"
+                        <el-date-picker size="small" v-model="date" :editable="false" format="yyyy-MM" value-format="yyyy-MM"
+                         style="width:190px;"
                          @change="changeMonthOut" :clearable="false" type="month" placeholder="选择月份"></el-date-picker>
+                         <el-button style="margin-left:10px;" icon="iconfont firerock-icongongshitongji" size="mini"  @click="showMonthWorkTime"></el-button>
                     </div>
                         <span v-for="(item,index) in allDate" :id="'day'+index" :class="index==choseDay?'chooseDate date_item':'date_item'" 
                         @click="choseDate(index, item)" :key="index" >
@@ -111,10 +113,34 @@
                                     <el-timeline-item v-for="(item2,index2) in item1.data" :key="index2">
                                         <el-card shadow="never">
                                             <p>项目:<b>{{item2.project}}</b><span v-if="item2.subProjectName != null"> / {{item2.subProjectName}}</span>
+                                            <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 == 3">[ 已撤回 ]</span>
+                                            </span>
+                                            <span v-if="user.company.packageEngineering == 1">
+                                                <span style="margin-left:15px;color:#DAA520;" v-if="item2.state == 0 && item2.departmentAuditState == -1">[ 待专业审核 ]</span>
+                                                <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 == 3">[ 已撤回 ]</span>
+                                            </span>
+                                            </p>
+                                            <p v-if="user.company.packageEngineering == 1">
+                                                专业进度:
+                                                <span style="margin-right:10px;" v-for="progressItem in item2.professionProgress" :key="progressItem.id">{{progressItem.professionName}}({{progressItem.progress}}%) 
+                                                    <el-tooltip v-if="progressItem.auditState == 0"  content="待审核" effect="light" placement="top">
+                                                    <i class="iconfont firerock-icondaibandengdaishenhe"></i>
+                                                    </el-tooltip>
+                                                    <el-tooltip v-if="progressItem.auditState == 1" content="已通过" effect="light" placement="top">
+                                                    <i  class="iconfont firerock-iconshenhetongguo"></i>
+                                                    </el-tooltip>
+                                                    <el-tooltip v-if="progressItem.auditState == 2" content="不通过" effect="light" placement="top">
+                                                    <i  class="iconfont firerock-iconshenhebohui"></i>
+                                                    </el-tooltip>
+                                                    </span>
                                             </p>
                                             <p v-if="item2.taskId != null">任务:{{item2.taskName}}
                                             </p>
@@ -214,7 +240,8 @@
                     
                     <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" @change="selectProject(domain, index)"
+                        <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>
@@ -237,10 +264,18 @@
                         </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 + '.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%;" 
+                    <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>
@@ -333,6 +368,54 @@
             </div>
         </el-dialog>
 
+        <el-dialog title="员工每日填报工时数"   v-if="monthWorkTimeDialog" :visible.sync="monthWorkTimeDialog" :close-on-click-modal="false" customClass="customWidth" width="90%">
+            <!-- <el-input style="width:100%" v-model="filterName" placeholder="请输入姓名搜索" @change="findUserInTree"></el-input> -->
+            <el-table :data="monthWorkData" border :height="500"   highlight-current-row  style="width: 100%;">
+            <el-table-column width="60" type="index" fixed="left" label="序号">
+                <template slot-scope="scope" >
+                        {{scope.$index+1}}
+                    </template>
+            </el-table-column>
+            
+            <el-table-column width="100" prop="name" fixed="left" label="姓名">
+
+            </el-table-column>
+            <el-table-column width="90" v-for="(item,index) in allDate" :key="index" :label="item.showDate+'/'+item.weekDay" align="center">
+                <template slot-scope="scope">
+                    <div style="color:red;" 
+                    v-if="scope.row.worktimeList.filter(w=>w.createDate.split('-')[2] == item.date.split('月')[1].replace('日','') 
+                        || w.createDate.split('-')[2] == '0'+item.date.split('月')[1].replace('日','') ).length > 0 && scope.row.worktimeList.filter(w=>w.createDate.split('-')[2] == item.date.split('月')[1].replace('日','') 
+                                || w.createDate.split('-')[2] == '0'+item.date.split('月')[1].replace('日','') )[0].workingTime < reportTimeType.allday">
+                                {{scope.row.worktimeList.filter(w=>w.createDate.split('-')[2] == item.date.split('月')[1].replace('日','') 
+                                    || w.createDate.split('-')[2] == '0'+item.date.split('月')[1].replace('日','') )[0].workingTime
+                        }}</div>
+                    <div style="color:#20a0ff;" 
+                     v-if="scope.row.worktimeList.filter(w=>w.createDate.split('-')[2] == item.date.split('月')[1].replace('日','') 
+                        || w.createDate.split('-')[2] == '0'+item.date.split('月')[1].replace('日','') ).length > 0 && scope.row.worktimeList.filter(w=>w.createDate.split('-')[2] == item.date.split('月')[1].replace('日','') 
+                                || w.createDate.split('-')[2] == '0'+item.date.split('月')[1].replace('日','') )[0].workingTime > reportTimeType.allday">
+                                {{scope.row.worktimeList.filter(w=>w.createDate.split('-')[2] == item.date.split('月')[1].replace('日','') 
+                                    || w.createDate.split('-')[2] == '0'+item.date.split('月')[1].replace('日','') )[0].workingTime
+                        }}</div>
+                    <div  v-if="scope.row.worktimeList.filter(w=>w.createDate.split('-')[2] == item.date.split('月')[1].replace('日','') 
+                        || w.createDate.split('-')[2] == '0'+item.date.split('月')[1].replace('日','') ).length > 0 && scope.row.worktimeList.filter(w=>w.createDate.split('-')[2] == item.date.split('月')[1].replace('日','') 
+                                || w.createDate.split('-')[2] == '0'+item.date.split('月')[1].replace('日','') )[0].workingTime == reportTimeType.allday">
+                                {{scope.row.worktimeList.filter(w=>w.createDate.split('-')[2] == item.date.split('月')[1].replace('日','') 
+                                    || w.createDate.split('-')[2] == '0'+item.date.split('月')[1].replace('日','') )[0].workingTime
+                        }}</div>
+                    <div v-if="scope.row.worktimeList.filter(w=>w.createDate.split('-')[2] == item.date.split('月')[1].replace('日','') 
+                        || w.createDate.split('-')[2] == '0'+item.date.split('月')[1].replace('日','') ).length == 0">
+                        0
+                    </div>
+                </template>
+            </el-table-column>
+            </el-table>
+            <div slot="title" class="dialog-title">
+                <label style="font-size:16px;">员工每日填报工时数</label>
+                <el-link type="primary" style="float:right;margin-right:60px;" @click="exportMembWorkHours()" >导出数据</el-link>
+                <!-- <el-button >导出</el-button> -->
+            </div>
+        </el-dialog>
+        
     </section>
 </template>
 
@@ -342,6 +425,7 @@
     export default {
         data() {
             return {
+                monthWorkTimeDialog: false,
                 isSubstitude:false,
                 isFill:false,
                 unFillList:[],
@@ -437,6 +521,55 @@
             };
         },
         methods: {
+            //导出员工每日填报工时数
+            exportMembWorkHours() {
+                this.http.post('/report/exportUserDailyWorkTime',{ 
+                        month: this.date
+                    },
+                    res => {
+                        if (res.code == "ok") {
+                            var url = '/upload/'+res.data;
+                            this.downloadByA("人员每日工时统计.xlxs", url);
+                        } 
+                    },
+                    error => {
+                        this.$message({
+                            message: error,
+                            type: "error"
+                        });
+                        }
+                    );
+            },
+            downloadByA(name, url) {
+                const a = document.createElement('a'); // 创建a标签
+                a.setAttribute('download', name);// download属性
+                a.setAttribute('href', url);// href链接
+                a.click();// 自执行点击事件
+                a.remove();
+            },
+            showMonthWorkTime() {
+                this.monthWorkTimeDialog = true;
+                this.http.post('/report/getUserDailyWorkTime',{ 
+                        month: this.date
+                    },
+                    res => {
+                        if (res.code == "ok") {
+                            this.monthWorkData = res.data.list;
+                            console.log('获取到数据:'+this.monthWorkData[0].name+', daytime='+this.reportTimeType.allday);
+                            this.$forceUpdate();
+                        } 
+                    },
+                    error => {
+                        this.$message({
+                            message: error,
+                            type: "error"
+                        });
+                        }
+                    );
+            },
+            onProgressChange() {
+                this.$forceUpdate();
+            },
             showChooseMembTree() {
                 this.chosenMembCount = this.participator.length;
                 this.chooseParticipVisible = true;
@@ -674,7 +807,25 @@
                         }
                     );
                 }
-                
+                //获取项目相关专业
+                if (this.user.company.packageEngineering == 1) {
+                    this.getProjectProfessions(domain, index);
+                }
+            },
+            getProjectProfessions(domain, index) {
+                this.http.post("/project-profession/getMyProfession", {projectId: domain.projectId},
+                    res => {
+                        if (res.code == "ok") {
+                            this.workForm.domains[index].professionProgress = res.data;
+                            this.$forceUpdate();
+                        }
+                    },
+                    error => {
+                        this.$message({
+                            message: error,
+                            type: "error"
+                        });
+                    });
             },
             //删除自己的日报
            deleteReport() {
@@ -1329,6 +1480,7 @@
                                     endTime: list.report[i].endTime,
                                     isOvertime: flg,
                                     progress:list.report[i].progress,
+                                    professionProgress: list.report[i].professionProgressList
                                 })
                                 if (list.report[i].state >= 2) {
                                     this.canEdit = true;
@@ -1527,6 +1679,7 @@
                         }
                         
                         this.listLoading = true;
+                        
                         let formData = new FormData();
                         for(var i in this.workForm.domains) {
                             if (this.workForm.domains[i].id != null) {
@@ -1585,8 +1738,7 @@
                                 this.workForm.domains[i].isOvertime = '1'
                                 formData.append("isOvertime", this.workForm.domains[i].isOvertime);
                             }
-                            //代填的情况
-                            console.log('===@@===');
+                            
                             if (this.workForm.userId != null) {
                                 var targetUids = '';
                                 this.workForm.userId.forEach(u=>{
@@ -1597,6 +1749,16 @@
                                     formData.append("targetUids", targetUids);
                                 }
                             }
+                            //项目专业进度
+                            if (this.workForm.domains[i].professionProgress != null) {
+                                var m = JSON.stringify(this.workForm.domains[i].professionProgress);
+                                
+                                m = m.replace(/,/g,"@");//replaceAll(',','@');企业微信不兼容replaceAll
+                                
+                                formData.append("professionProgress", m);
+                            } else {
+                                formData.append("professionProgress", "[]");
+                            }
                         }
                         this.http.uploadFile( this.port.report.editPort, formData,
                         res => {

+ 16 - 1
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/list.vue

@@ -4,7 +4,7 @@
         <el-col :span="24" class="toolbar" style="padding-bottom: 0px;">
             <el-form :inline="true">
                 <el-form-item label="项目:">
-                    <el-select v-model="search.projectId" placeholder="请选择" clearable @change="getList()">
+                    <el-select v-model="search.projectId" placeholder="请选择" clearable @change="getList()" filterable="true">
                         <el-option v-for="item in projectList" :key="item.id" :label="item.projectName" :value="item.id"></el-option>
                     </el-select>
                 </el-form-item>
@@ -35,6 +35,21 @@
                         <el-timeline-item v-for="(item,index) in props.row.data" :key="index">
                             <el-card shadow="never">
                                 <p>项目:<b>{{item.project}}<span v-if="item.subProjectName != null"> / {{item.subProjectName}}</span></b></p>
+                                <p v-if="user.company.packageEngineering == 1">
+                                    专业进度:
+                                    <span style="margin-right:10px;" v-for="progressItem in item.professionProgressList" :key="progressItem.id">
+                                        {{progressItem.professionName}}({{progressItem.progress}}%)
+                                        <el-tooltip v-if="progressItem.auditState == 0"  content="待审核" effect="light" placement="top">
+                                        <i class="iconfont firerock-icondaibandengdaishenhe"></i>
+                                        </el-tooltip>
+                                        <el-tooltip v-if="progressItem.auditState == 1" content="已通过" effect="light" placement="top">
+                                        <i  class="iconfont firerock-iconshenhetongguo"></i>
+                                        </el-tooltip>
+                                        <el-tooltip v-if="progressItem.auditState == 2" content="不通过" effect="light" placement="top">
+                                        <i  class="iconfont firerock-iconshenhebohui"></i>
+                                        </el-tooltip>
+                                        </span>
+                                </p>
                                 <p v-if="item.taskId != null">任务:{{item.taskName}}</p>
                                 <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></p>

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

@@ -0,0 +1,354 @@
+<template>
+    <section>
+        <!--工具条-->
+        <el-col :span="24" class="toolbar" style="padding-bottom: 0px;">
+            <el-form :inline="true">
+                <el-form-item label="项目:">
+                    <el-select v-model="search.projectId" placeholder="请选择" clearable @change="getList()" filterable="true">
+                        <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 label="日期:" style="margin-left:20px;">
+                    <el-date-picker v-model="search.date" :editable="false" format="yyyy-MM-dd" value-format="yyyy-MM-dd" 
+                    @change="getList()" :clearable="true" type="date" placeholder="选择工作日期"></el-date-picker>
+                </el-form-item>
+                <el-form-item   style="margin-left:20px;">
+                    <el-button @click="batchApprove(true)" style="margin-left:10px;" :disabled="multipleSelection.length==0">批量通过</el-button>
+                <el-button @click="batchApprove(false)"  :disabled="multipleSelection.length==0">批量驳回</el-button>
+                </el-form-item>
+            </el-form>
+        </el-col>
+        <!--列表-->
+        <el-table :data="list" ref="multipleTable" highlight-current-row v-loading="listLoading" :height="tableHeight" style="width: 100%;"
+            @selection-change="handleSelectionChange">
+            <el-table-column type="selection" width="55"></el-table-column>
+            <el-table-column type="expand">
+                <template slot-scope="props">
+                    <el-timeline>
+                        <el-timeline-item v-for="(item,index) in props.row.data" :key="index">
+                            <el-card shadow="never">
+                                <p>项目:<b>{{item.project}}<span v-if="item.subProjectName != null"> / {{item.subProjectName}}</span></b></p>
+                                <p v-if="user.company.packageEngineering == 1">
+                                    专业进度:
+                                    <span style="margin-right:10px;" v-for="progressItem in item.professionProgressList" :key="progressItem.id">
+                                        {{progressItem.professionName}}({{progressItem.progress}}%)
+                                        <el-tooltip v-if="progressItem.auditState == 0"  content="待审核" effect="light" placement="top">
+                                        <i class="iconfont firerock-icondaibandengdaishenhe"></i>
+                                        </el-tooltip>
+                                        <el-tooltip v-if="progressItem.auditState == 1" content="已通过" effect="light" placement="top">
+                                        <i  class="iconfont firerock-iconshenhetongguo" ></i>
+                                        </el-tooltip>
+                                        <el-tooltip v-if="progressItem.auditState == 2" content="不通过" effect="light" placement="top">
+                                        <i  class="iconfont firerock-iconshenhebohui"></i>
+                                        </el-tooltip>
+                                        </span>
+                                </p>
+                                <p v-if="item.taskId != null">任务:{{item.taskName}}</p>
+                                <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></p>
+                                <p v-if="user.role>=1&&user.role<=3">成本:{{item.cost}}元</p>
+                                <p>事项:<span v-html="item.content"></span></p>
+                            </el-card>
+                        </el-timeline-item>
+                    </el-timeline>
+                </template>
+            </el-table-column>
+            
+            <el-table-column prop="name" label="姓名" sortable></el-table-column>
+            <el-table-column prop="dateStr" label="日期" sortable>
+            </el-table-column>
+            <el-table-column prop="reportTime" label="工作时长(h)" >
+            </el-table-column>
+            <el-table-column prop="state" label="状态" sortable>
+                <template slot-scope="scope">
+                    <span v-if="scope.row.state == 0" style="color:#DAA520;">待审核</span>
+                    <span v-else-if="scope.row.state == 1" style="color:#32CD32;">已通过</span>
+                    <span v-else-if="scope.row.state == 2" style="color:#FF0000;">已驳回</span>
+                </template>
+            </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>
+                </template>
+            </el-table-column>
+        </el-table>
+        
+        <!--工具条-->
+        <!-- <el-col v-if="search.value != -1" :span="24" class="toolbar">
+            <el-pagination
+                @size-change="handleSizeChange"
+                @current-change="handleCurrentChange"
+                :page-sizes="[20 , 50 , 80 , 100]"
+                :page-size="20"
+                layout="total, sizes, prev, pager, next"
+                :total="total"
+                style="float:right;"
+            ></el-pagination>
+        </el-col> -->
+    </section>
+</template>
+
+<script>
+    import util from "../../common/js/util";
+
+    export default {
+        data() {
+            return {
+                isAllSelect:false,
+                user: JSON.parse(sessionStorage.getItem("user")),
+
+                search: {
+                    projectId:null,
+                    departmentIdArray: null,
+                    departmentId:null,
+                    date: null,
+                    state:0,
+                },
+
+                users: [],
+                option:[],
+                tableHeight: 0,
+                listLoading: false,
+                total: 0,
+                page: 1,
+                size: 20,
+                list: [],
+                logining: false,
+                multipleSelection: [],
+            };
+        },
+        methods: {
+            // 获取部门列表
+            getDepartment() {
+                this.http.post( this.port.manage.depList, {},
+                res => {
+                    if (res.code == "ok") {
+                        var list1 = JSON.parse(JSON.stringify(res.data));
+                        
+                        this.option = this.changeArr(list1);
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
+
+            handleSelectionChange(val) {
+                this.multipleSelection = val;
+            },
+            batchApprove(isPass) {
+                console.log(this.multipleSelection);
+                var ids = '';
+                for (var i=0;i<this.multipleSelection.length; i++) {
+                    var line = this.multipleSelection[i];
+                    var array = line.data;
+                    for (var m=0;m<array.length; m++) {
+                        ids += array[m].id+',';
+                    }
+                }
+                if (ids.length > 0) {
+                    ids = ids.substring(0, ids.length-1);
+                }
+                this.http.post(isPass?'/report/batchApproveReport':'/report/batchDenyReport', {ids: ids},
+                res => {
+                    this.listLoading = false;
+                    if (res.code == "ok") {
+                        this.getList();
+                    } else {
+                        this.$message({
+                        message: res.msg,
+                        type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.listLoading = false;
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
+            //分页
+            handleCurrentChange(val) {
+                this.page = val;
+                this.getList();
+            },
+
+            handleSizeChange(val) {
+                this.size = val;
+                this.getList();
+            },
+
+            //获取待审核的数据列表
+            getList() {
+                this.listLoading = true;
+                if (this.search.departmentIdArray == null) {
+                    this.search.departmentId = null;
+                } else {
+                    var length = this.search.departmentIdArray.length;
+                    this.search.departmentId = this.search.departmentIdArray[length-1];
+                }
+                this.http.post('/report/listByStateDepartment', this.search,
+                res => {
+                    this.listLoading = false;
+                    if (res.code == "ok") {
+                        this.list = res.data;
+                    } else {
+                        this.$message({
+                        message: res.msg,
+                        type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.listLoading = false;
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
+
+            // 通过日报
+            approve(id,date, item) {
+                console.log(item);
+                this.logining = true;
+                
+                var ids = '';
+                var data = item.data;
+                data.forEach(element => {
+                    ids +=(element.id+',');
+                });
+
+                this.http.post(this.port.report.approve, {id: id ,reportIds: ids},
+                res => {
+                    this.logining = false;
+                    if (res.code == "ok") {
+                        this.$message({
+                            message: "审核成功",
+                            type: "success"
+                        });
+                        this.getList();
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.logining = false;
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
+// 修改数组
+            changeArr(arr) {
+                for (var i = 0; i < arr.length; i++) {
+                    if(arr[i].id != -1 && arr[i].id != 0) {
+                        if (arr[i].children != null && arr[i].children.length>0) {
+                            arr[i].children = this.changeArr(arr[i].children);
+                        }
+                        arr[i].id && (arr[i].value = arr[i].id);
+                        delete arr[i].id;
+                    }
+                }
+                for(var i in arr) {
+                    if(arr[i].id == -1 || arr[i].id == 0) {
+                        arr.splice(i,1)
+                    }    
+                }
+                return arr;
+            },
+            
+            //获取项目列表
+            getProjectList() {
+                this.listLoading = true;
+                this.http.post( this.port.project.list, {},
+                res => {
+                    this.listLoading = false;
+                    if (res.code == "ok") {
+                        this.projectList = res.data;
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.listLoading = false;
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
+            // 未通过日报
+            deny(id,i,date, item) {
+                this.logining = true;
+                var time = date;
+                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},
+                res => {
+                    this.logining = false;
+                    if (res.code == "ok") {
+                        this.$message({
+                            message: i==0?"驳回成功":"撤销成功",
+                            type: "success"
+                        });
+                        this.getList();
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.logining = false;
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            }
+        },
+        created() {
+            let height = window.innerHeight;
+            this.tableHeight = height - 125;
+            const that = this;
+            window.onresize = function temp() {
+                that.tableHeight = window.innerHeight - 125;
+            };
+        },
+        mounted() {
+            this.getList();
+            this.getDepartment();
+            this.getProjectList();
+        }
+    };
+</script>
+
+<style lang="scss" scoped>
+.propsbtn {
+    display: inline-block;
+    padding-left: 20px;
+}
+</style>

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

@@ -0,0 +1,360 @@
+<template>
+    <section>
+        <!--工具条-->
+        <el-col :span="24" class="toolbar" style="padding-bottom: 0px;">
+            <el-form :inline="true">
+                <el-form-item label="项目:">
+                    <el-select v-model="search.projectId" placeholder="请选择" clearable @change="getList()" filterable="true">
+                        <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 label="部门:">
+                    <el-cascader v-model="search.departmentIdArray" placeholder="请选择部门" style="width: 100%"
+                    :options="option" :props="{ checkStrictly: false,expandTrigger: 'hover' }" :show-all-levels="false" clearable
+                    @change="getList()"
+                    ></el-cascader>
+                </el-form-item>
+                <el-form-item label="日期:" style="margin-left:20px;">
+                    <el-date-picker v-model="search.date" :editable="false" format="yyyy-MM-dd" value-format="yyyy-MM-dd" 
+                    @change="getList()" :clearable="true" type="date" placeholder="选择工作日期"></el-date-picker>
+                </el-form-item>
+                <el-form-item   style="margin-left:20px;">
+                    <el-button @click="batchApprove(true)" style="margin-left:10px;" :disabled="multipleSelection.length==0">批量通过</el-button>
+                <el-button @click="batchApprove(false)"  :disabled="multipleSelection.length==0">批量驳回</el-button>
+                </el-form-item>
+            </el-form>
+        </el-col>
+        <!--列表-->
+        <el-table :data="list" ref="multipleTable" highlight-current-row v-loading="listLoading" :height="tableHeight" style="width: 100%;"
+            @selection-change="handleSelectionChange">
+            <el-table-column type="selection" width="55"></el-table-column>
+            <el-table-column type="expand">
+                <template slot-scope="props">
+                    <el-timeline>
+                        <el-timeline-item v-for="(item,index) in props.row.data" :key="index">
+                            <el-card shadow="never">
+                                <p>项目:<b>{{item.project}}<span v-if="item.subProjectName != null"> / {{item.subProjectName}}</span></b></p>
+                                <p v-if="user.company.packageEngineering == 1">
+                                    专业进度:
+                                    <span style="margin-right:10px;" v-for="progressItem in item.professionProgressList" :key="progressItem.id">
+                                        {{progressItem.professionName}}({{progressItem.progress}}%)
+                                        <el-tooltip v-if="progressItem.auditState == 0"  content="待审核" effect="light" placement="top">
+                                        <i class="iconfont firerock-icondaibandengdaishenhe"></i>
+                                        </el-tooltip>
+                                        <el-tooltip v-if="progressItem.auditState == 1" content="已通过" effect="light" placement="top">
+                                        <i  class="iconfont firerock-iconshenhetongguo"></i>
+                                        </el-tooltip>
+                                        <el-tooltip v-if="progressItem.auditState == 2" content="不通过" effect="light" placement="top">
+                                        <i  class="iconfont firerock-iconshenhebohui"></i>
+                                        </el-tooltip>
+                                        </span>
+                                </p>
+                                <p v-if="item.taskId != null">任务:{{item.taskName}}</p>
+                                <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></p>
+                                <p v-if="user.role>=1&&user.role<=3">成本:{{item.cost}}元</p>
+                                <p>事项:<span v-html="item.content"></span></p>
+                            </el-card>
+                        </el-timeline-item>
+                    </el-timeline>
+                </template>
+            </el-table-column>
+            
+            <el-table-column prop="name" label="姓名" sortable></el-table-column>
+            <el-table-column prop="dateStr" label="日期" sortable>
+            </el-table-column>
+            <el-table-column prop="reportTime" label="工作时长(h)" >
+            </el-table-column>
+            <el-table-column prop="state" label="状态" sortable>
+                <template slot-scope="scope">
+                    <span v-if="scope.row.state == 0" style="color:#DAA520;">待审核</span>
+                    <span v-else-if="scope.row.state == 1" style="color:#32CD32;">已通过</span>
+                    <span v-else-if="scope.row.state == 2" style="color:#FF0000;">已驳回</span>
+                </template>
+            </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>
+                </template>
+            </el-table-column>
+        </el-table>
+        
+        <!--工具条-->
+        <!-- <el-col v-if="search.value != -1" :span="24" class="toolbar">
+            <el-pagination
+                @size-change="handleSizeChange"
+                @current-change="handleCurrentChange"
+                :page-sizes="[20 , 50 , 80 , 100]"
+                :page-size="20"
+                layout="total, sizes, prev, pager, next"
+                :total="total"
+                style="float:right;"
+            ></el-pagination>
+        </el-col> -->
+    </section>
+</template>
+
+<script>
+    import util from "../../common/js/util";
+
+    export default {
+        data() {
+            return {
+                isAllSelect:false,
+                user: JSON.parse(sessionStorage.getItem("user")),
+
+                search: {
+                    projectId:null,
+                    departmentIdArray: null,
+                    departmentId:null,
+                    date: null,
+                    state:0,
+                },
+
+                users: [],
+                option:[],
+                tableHeight: 0,
+                listLoading: false,
+                total: 0,
+                page: 1,
+                size: 20,
+                list: [],
+                logining: false,
+                multipleSelection: [],
+            };
+        },
+        methods: {
+            // 获取部门列表
+            getDepartment() {
+                this.http.post( this.port.manage.depList, {},
+                res => {
+                    if (res.code == "ok") {
+                        var list1 = JSON.parse(JSON.stringify(res.data));
+                        
+                        this.option = this.changeArr(list1);
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
+
+            handleSelectionChange(val) {
+                this.multipleSelection = val;
+            },
+            batchApprove(isPass) {
+                console.log(this.multipleSelection);
+                var ids = '';
+                for (var i=0;i<this.multipleSelection.length; i++) {
+                    var line = this.multipleSelection[i];
+                    var array = line.data;
+                    for (var m=0;m<array.length; m++) {
+                        ids += array[m].id+',';
+                    }
+                }
+                if (ids.length > 0) {
+                    ids = ids.substring(0, ids.length-1);
+                }
+                this.http.post(isPass?'/report/batchApproveReport':'/report/batchDenyReport', {ids: ids},
+                res => {
+                    this.listLoading = false;
+                    if (res.code == "ok") {
+                        this.getList();
+                    } else {
+                        this.$message({
+                        message: res.msg,
+                        type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.listLoading = false;
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
+            //分页
+            handleCurrentChange(val) {
+                this.page = val;
+                this.getList();
+            },
+
+            handleSizeChange(val) {
+                this.size = val;
+                this.getList();
+            },
+
+            //获取待审核的数据列表
+            getList() {
+                this.listLoading = true;
+                if (this.search.departmentIdArray == null) {
+                    this.search.departmentId = null;
+                } else {
+                    var length = this.search.departmentIdArray.length;
+                    this.search.departmentId = this.search.departmentIdArray[length-1];
+                }
+                this.http.post('/report/listByStateProfession', this.search,
+                res => {
+                    this.listLoading = false;
+                    if (res.code == "ok") {
+                        this.list = res.data;
+                    } else {
+                        this.$message({
+                        message: res.msg,
+                        type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.listLoading = false;
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
+
+            // 通过日报
+            approve(id,date, item) {
+                console.log(item);
+                this.logining = true;
+                
+                var ids = '';
+                var data = item.data;
+                data.forEach(element => {
+                    ids +=(element.id+',');
+                });
+
+                this.http.post(this.port.report.approve, {id: id ,reportIds: ids},
+                res => {
+                    this.logining = false;
+                    if (res.code == "ok") {
+                        this.$message({
+                            message: "审核成功",
+                            type: "success"
+                        });
+                        this.getList();
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.logining = false;
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
+// 修改数组
+            changeArr(arr) {
+                for (var i = 0; i < arr.length; i++) {
+                    if(arr[i].id != -1 && arr[i].id != 0) {
+                        if (arr[i].children != null && arr[i].children.length>0) {
+                            arr[i].children = this.changeArr(arr[i].children);
+                        }
+                        arr[i].id && (arr[i].value = arr[i].id);
+                        delete arr[i].id;
+                    }
+                }
+                for(var i in arr) {
+                    if(arr[i].id == -1 || arr[i].id == 0) {
+                        arr.splice(i,1)
+                    }    
+                }
+                return arr;
+            },
+            
+            //获取项目列表
+            getProjectList() {
+                this.listLoading = true;
+                this.http.post( this.port.project.list, {},
+                res => {
+                    this.listLoading = false;
+                    if (res.code == "ok") {
+                        this.projectList = res.data;
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.listLoading = false;
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
+            // 未通过日报
+            deny(id,i,date, item) {
+                this.logining = true;
+                var time = date;
+                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},
+                res => {
+                    this.logining = false;
+                    if (res.code == "ok") {
+                        this.$message({
+                            message: i==0?"驳回成功":"撤销成功",
+                            type: "success"
+                        });
+                        this.getList();
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.logining = false;
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            }
+        },
+        created() {
+            let height = window.innerHeight;
+            this.tableHeight = height - 125;
+            const that = this;
+            window.onresize = function temp() {
+                that.tableHeight = window.innerHeight - 125;
+            };
+        },
+        mounted() {
+            this.getList();
+            this.getDepartment();
+            this.getProjectList();
+        }
+    };
+</script>
+
+<style lang="scss" scoped>
+.propsbtn {
+    display: inline-block;
+    padding-left: 20px;
+}
+</style>

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

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

+ 14 - 0
fhKeeper/formulahousekeeper/timesheet_h5/src/router/index.js

@@ -60,6 +60,20 @@ const router = new Router({
         },
         component: () => import("@/views/review/index")
     },
+    {
+        path: "/profession_review",
+        meta: {
+            title: "专业审核"
+        },
+        component: () => import("@/views/review/profession_list")
+    },
+    {
+        path: "/department_review",
+        meta: {
+            title: "部门审核"
+        },
+        component: () => import("@/views/review/department_list")
+    },
     {
         path: "/msg",
         meta: {

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

@@ -28,6 +28,17 @@
                     <van-popup v-model="showPickerProject" position="bottom">
                         <van-picker show-toolbar :columns="project" value-key="projectName" @confirm="choseProject" @cancel="showPickerProject = false" />
                     </van-popup>
+                    <van-cell title="专业进度" v-if="user.company.packageEngineering == 1">
+                    </van-cell>
+                    <van-field :disabled="!canEdit"
+                        type="number"  :name="'progress_'+pItem.professionId" input-align="right" 
+                        v-for="pItem in item.professionProgress" :key="pItem.professionId"
+                        :label="'--    '+pItem.professionName"  
+                    >
+                    <template slot="input">
+                        <van-stepper v-model="pItem.progress" integer min="0" max="100" />%
+                    </template>
+                    </van-field>
                     <van-field v-if="user.company.packageProject == 1"  readonly name="taskId" :value="item.taskName" label="关联任务" placeholder="请选择关联任务" @click="clickPickerTask(index)"
                     />
                     <van-popup v-model="showPickerTask" position="bottom">
@@ -402,6 +413,7 @@
                                     taskList: list[i].taskList,
                                     taskId: list[i].taskId,
                                     taskName:tname,
+                                    professionProgress:list[i].professionProgressList,
                                 })
                                 if (list[i].state >= 2) {
                                     this.canEdit = true;
@@ -423,6 +435,7 @@
                                 state: 2,
                                 progress:100,
                                 isOvertime:false,
+                                professionProgress:[],
                             }]
                             this.canEdit = true;
                         }
@@ -465,6 +478,24 @@
                 this.form.domains[this.clickIndex].projectName = value.projectName;
                 this.showPickerProject = false;
                 this.getTaskList(value.id);
+                
+                //加载项目相关的工程进度
+                if (this.user.company.packageEngineering == 1) {
+                    this.getProjectProfessions(this.form.domains[this.clickIndex], index);
+                }
+            },
+
+            getProjectProfessions(domain, index) {
+                this.$axios.post("/project-profession/getMyProfession", {projectId: domain.projectId})
+                        .then(res => {
+                            if(res.code == "ok") {
+                                this.form.domains[this.clickIndex].professionProgress = res.data;
+                                
+                                this.$forceUpdate();
+                            } else {
+                                this.$toast.fail('获取失败');
+                            }
+                        }).catch(err=> {toast.clear();});
             },
 
             getTaskList(projectId) {
@@ -610,6 +641,15 @@
                     }
                     formData.append("createDate", this.form.createDate);
                     formData.append("isOvertime", this.form.domains[i].isOvertime?1:0);
+
+                    //项目专业进度
+                    if (this.form.domains[i].professionProgress != null) {
+                        let m = JSON.stringify(this.form.domains[i].professionProgress);
+                        m = m.replace(/,/g,"@");//replaceAll(',','@');企业微信不兼容replaceAll
+                        formData.append("professionProgress", m);
+                    } else {
+                        formData.append("professionProgress", "[]");
+                    }
                 }
                 this.$axios.post("/report/editReport", formData)
                 .then(res => {
@@ -677,8 +717,10 @@
             margin-right: 5px;
         }
     }
+    
 </style>
 <style lang="less">
+    
     .van-nav-bar .van-icon , .van-nav-bar__text {
         color: #20a0ff;
     }

+ 116 - 67
fhKeeper/formulahousekeeper/timesheet_h5/src/views/index/index.vue

@@ -25,6 +25,8 @@
     export default {
         data() {
             return {
+                isCorpWX:false,
+                isWX:false,
                 user: JSON.parse(localStorage.userInfo),
                 unreadNum:0,
                 images: [
@@ -38,12 +40,14 @@
                         url: '/calendar',
                         icon: 'description'
                     },
+                    
                     {
                         name: '填写日报',
                         url: '/edit',
                         icon: 'edit'
                     },
                     
+                    
                 ],
             };
         },
@@ -53,25 +57,50 @@
             
         },
         mounted() {
-            console.log('user role==' + this.user.role);
+            var ua = navigator.userAgent.toLowerCase();
+            if (ua.indexOf("wxwork") > 0) {
+                this.isCorpWX = true;
+            } else if (ua.indexOf("micromessenger") > 0) {
+                this.isWX = true;
+            }
+
+            if (this.user.company.packageEngineering == 1) {
+                this.routers.push(
+                {
+                    name: '专业审核',
+                    url: '/profession_review',
+                    icon: 'todo-list-o'
+                });
+            }
             if (this.user.role == 0) {
                 //普通员工
+                if (this.user.manageDeptId != 0 && this.user.company.packageEngineering == 1) {
+                    this.routers.push(
+                    {
+                        name: '部门审核',
+                        url: '/department_review',
+                        icon: 'todo-list-o'
+                    });
+                }
                 if (this.user.leader) {
                     this.routers.push({
-                        name: '审核日报',
+                        name: '项目报告审核',
                         url: '/review',
                         icon: 'todo-list-o'
                     });
-                    this.routers.push({
-                                name: '消息记录',
-                                url: '/msg',
-                                icon: 'todo-list-o',
-                                info: this.unreadNum
-                            });
                 } 
-            } else {
+            } else if (this.user.role == 1 || this.user.role == 2){
+                if (this.user.company.packageEngineering == 1) {
+                    this.routers.push(
+                    {
+                        name: '部门审核',
+                        url: '/department_review',
+                        icon: 'todo-list-o'
+                    });
+                }
+                
                 this.routers.push({
-                        name: '审核日报',
+                        name: '项目报告审核',
                         url: '/review',
                         icon: 'todo-list-o'
                     });
@@ -80,71 +109,91 @@
                         url: '/project',
                         icon: 'label-o'
                     });
-                this.routers.push({
-                            name: '消息记录',
-                            url: '/msg',
-                            icon: 'todo-list-o',
-                            info: this.unreadNum
-                        });
+            } else {
+                if (this.user.manageDeptId != 0 && this.user.company.packageEngineering == 1) {
+                    this.routers.push(
+                    {
+                        name: '部门审核',
+                        url: '/department_review',
+                        icon: 'todo-list-o'
+                    });
+                }
+                if (this.user.role == 5) {//项目管理员
+                    this.routers.push({
+                        name: '项目管理',
+                        url: '/project',
+                        icon: 'label-o'
+                    });
+                }
             }
-
+            this.routers.push({
+                                name: '消息记录',
+                                url: '/msg',
+                                icon: 'todo-list-o',
+                                info: this.unreadNum
+                            });
             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';
+            this.bindIfNessary();
+        },
+        components: {
+            Footer
+        },
+        methods: {
+            bindIfNessary() {
+                let href = window.location.href;
+                var requestUrl = "";
+                if (this.isCorpWX && (this.user.corpwxUserid != null|| this.user.corpwxUserid == undefined || this.user.corpwxUserid == 'undefined')) {//优先检查企业微信环境
+                    requestUrl = "/wxcorp/bindCorpWeiXin";
+                } else if (this.isWX && (this.user.wxOpenid == null || this.user.wxOpenid == undefined || this.user.wxOpenid == 'undefined')) {
+                    requestUrl = "/wechat/bindWeiXin";
+                } 
                 
-                if (href.includes("com/?code")) {  //url包括 com/?code 证明为从微信跳转回来的
-                    var url = href; //vue自动在末尾加了 #/ 符号,截取去掉
-                    var jingPosit = url.indexOf("com/") + 4; //获取域名结束的位置
+                if (requestUrl.length > 0) {
+                    // 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'));
-                    }
+                        // 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);
+                        //调用后台接口,注册用户
+                        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;
-                                    // }
+                        this.$axios.get(requestUrl, {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 && ((this.isWX && res.data.wxOpenid != undefined)
+                                                || (this.isCorpWX && res.data.corpwxUserid != undefined))) {
+                                        localStorage.userInfo = JSON.stringify(res.data);
+                                        console.log('绑定成功');
+                                        this.user = res.data;
+                                        window.location.href = '/#/my/center';
+                                    }
                                 }
-                            }
-                        }).catch(err=> {
-                            alert('err=' + err);
-                        });
+                            }).catch(err=> {
+                                alert('err=' + err);
+                            });
+                    }
                 }
-            } else {
-                
-            }
-        },
-        components: {
-            Footer
-        },
-        methods: {
+            },
             getGoods() {
                 let url = "/goods"; // /api/goods
                 this.$axios

+ 148 - 16
fhKeeper/formulahousekeeper/timesheet_h5/src/views/login/index.vue

@@ -10,7 +10,7 @@
             <div class="form_btn" style="margin: 16px;">
                 <van-button round block type="info" native-type="submit"> 登录 </van-button>
             </div>
-            <div class="form_jump" v-on:click="jumpTo">企业注册</div>
+            <div class="form_jump" v-on:click="jumpTo" v-if="!isCorpWX">企业注册</div>
         </van-form>
     </header>
 </template>
@@ -21,6 +21,8 @@
     export default {
         data() {
             return {
+                isCorpWX:false,
+                isWX:false,
                 defaultHeight: '0',  //默认屏幕高度
                 nowHeight:  '0',  //实时屏幕高度
                 form: {
@@ -78,6 +80,110 @@
                                 }
                             }).catch(err=> {toast.clear();});
             },
+            bindIfNessary() {
+                let href = window.location.href;
+                var requestUrl = "";
+                if (this.isCorpWX) {//优先检查企业微信环境
+                    requestUrl = "/wxcorp/bindCorpWeiXin";
+                } else if (this.isWX) {
+                    requestUrl = "/wechat/bindWeiXin";
+                } 
+                
+                if (requestUrl.length > 0) {
+                    // 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);
+                        urlRight = urlRight.substring(0, urlRight.indexOf('#/'));
+                        // window.location = urlLeft + "#/home" + urlRight;//拼接跳转
+                        //获取code
+                        var code = urlRight.substring('?code='.length,urlRight.indexOf('&state='));
+                        var passUserId = urlRight.substring(urlRight.indexOf('&state=')+'&state='.length);
+                        if (passUserId == '0') {
+                            //自动登录的回调
+                            this.$axios.get('/wxcorp/corpWeiXinLogin', {params:{code:code}})
+                            .then(res => {
+                                if (res == null) {
+                                    
+                                } else if(res.errcode != null) {
+                                    //报错了
+                                    console.log(res.errmsg);
+                                } else {
+                                    //获取openId
+                                    if (res.data != null && ((this.isWX && res.data.wxOpenid != undefined)
+                                                || (this.isCorpWX && res.data.corpwxUserid != undefined))) {
+                                        localStorage.userInfo = JSON.stringify(res.data);
+                                        console.log('登录成功');
+                                        this.user = res.data;
+                                        window.location.href = '/#/index';
+                                    }
+                                }
+                            }).catch(err=> {
+                                alert('err=' + err);
+                            });
+                        } else {
+                            //绑定微信账号的回调
+                            //调用后台接口,注册用户
+                        this.$axios.get(requestUrl, {params:{code:code, userId: passUserId}})
+                            .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 && ((this.isWX && res.data.wxOpenid != undefined)
+                                                || (this.isCorpWX && res.data.corpwxUserid != undefined))) {
+                                        localStorage.userInfo = JSON.stringify(res.data);
+                                        console.log('绑定成功');
+                                        this.user = res.data;
+                                        window.location.href = '/#/my/center';
+                                    }
+                                }
+                            }).catch(err=> {
+                                alert('err=' + err);
+                            });
+                        
+                        }
+                        
+                    } 
+                }
+            },
+            tryAutoLogin() {
+                var appId = "ww4e237fd6abb635af";//企业微信第三方的SUIT ID
+                var url = "http://mobworktime.ttkuaiban.com/api/corpWXAuth";//授权回调页面
+                var weixinUrl="https://open.weixin.qq.com/connect/oauth2/authorize?appid="+appId+"&redirect_uri="+encodeURI(url)+"&response_type=code&scope=snsapi_base&state=0#wechat_redirect";
+                window.location.href = weixinUrl;
+            },
+            loginByUserId(userId) {
+                this.$axios.get("/user/loginByUserId", {params:{userId:userId}})
+                    .then(res => {
+                        console.log(res);
+                        if (res == null) {
+
+                        } else if(res.errcode != null) {
+                            
+                        } else {
+                            //获取openId
+                            if (res.data != null) {
+                                localStorage.userInfo = JSON.stringify(res.data);
+                                this.user = res.data;
+                                window.location.href = '/#/index';
+                            }
+                        }
+                    }).catch(err=> {
+                        alert('err=' + err);
+                    });
+            },
         },
         created() {
             if (localStorage.userInfo != null) {
@@ -85,24 +191,50 @@
             }
         },
         mounted() {
+            var ua = navigator.userAgent.toLowerCase();
+            if (ua.indexOf("wxwork") > 0) {
+                this.isCorpWX = true;
+            } else if (ua.indexOf("micromessenger") > 0) {
+                this.isWX = true;
+            }
             if (localStorage.userInfo != null) {
                 this.$router.push("/index");
             } else {
-                //检查环境,如果是钉钉有$CORPID$
-                var key = '?corpid=';
-                var url = location.href;
-                var that = this;
-                if (url.indexOf(key) > 0) {
-                    var corpId = url.substring(url.indexOf(key)+key.length,url.indexOf('#'));
-                    dd.ready(function() {
-                        dd.runtime.permission.requestAuthCode({
-                            corpId: corpId, // 企业id
-                            onSuccess: function (info) {
-                                var code = info.code // 通过该免登授权码可以获取用户身份
-                                that.loginByCode(code, corpId);
-                            }});
-                    });
-                } 
+                if (this.isCorpWX || this.isWX) {
+                    let href = window.location.href;
+                    //判断企业微信,是否存在授权
+                    if (href.includes("com/?code")) {
+                        this.bindIfNessary();
+                    } else {
+                        if (href.indexOf('hasTriedAutoLogin') == -1) {
+                            this.tryAutoLogin();
+                        } else if (href.indexOf("userId") > 0) {
+                            //后台经过验证后,重定向过来带上了userId
+                            var loginUserId = href.substring(href.indexOf("userId=")+"userId=".length);
+                            if (loginUserId.includes('#/')) {
+                                loginUserId = loginUserId.substring(0, loginUserId.indexOf('#/'));
+                            }
+                            this.loginByUserId(loginUserId);
+                        }
+                    }
+                    
+                } else {
+                    //检查环境,如果是钉钉有$CORPID$
+                    var key = '?corpid=';
+                    var url = location.href;
+                    var that = this;
+                    if (url.indexOf(key) > 0) {
+                        var corpId = url.substring(url.indexOf(key)+key.length,url.indexOf('#'));
+                        dd.ready(function() {
+                            dd.runtime.permission.requestAuthCode({
+                                corpId: corpId, // 企业id
+                                onSuccess: function (info) {
+                                    var code = info.code // 通过该免登授权码可以获取用户身份
+                                    that.loginByCode(code, corpId);
+                                }});
+                        });
+                    } 
+                }
             }
         }
     };

+ 26 - 13
fhKeeper/formulahousekeeper/timesheet_h5/src/views/my/children/center.vue

@@ -16,10 +16,10 @@
                 <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="绑定微信后可接收工时填报提醒">
+            <van-cell :title="'绑定'+(isCorpWX?'企业':'')+'微信'" v-if="isCorpWX || isWX" @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>
+                    <span v-if="(isCorpWX && userInfo.corpwxUserid == null) || (isWX && userInfo.wxOpenid == null)" style="color:#ff0000;">未绑定</span>
+                    <span v-if="(isCorpWX && userInfo.corpwxUserid != null) || (isWX && userInfo.wxOpenid != null)" style="color:#7CCD7C;">已绑定</span>
                 </template>
             </van-cell>
             <van-button class="logout" @click="logout" block round type="danger" >退出登录</van-button>
@@ -44,6 +44,8 @@
         data() {
             return {
                 userInfo: JSON.parse(localStorage.userInfo),
+                isCorpWX:false,
+                isWX:false,
             }
         },
 
@@ -54,26 +56,37 @@
                 this.$router.push("/login");
             },
             bindWeiXin(){
-                if (this.userInfo.wxOpenid != null) {
+                var ua = navigator.userAgent.toLowerCase();
+                //企业微信
+                if (ua.indexOf("wxwork") > 0 && this.userInfo.corpwxUserid != null) {
                     return;
                 }
-                var url = "http://mobworktime.ttkuaiban.com/";//授权回调页面
+                //微信
+                if (ua.indexOf("micromessenger") > 0 && this.userInfo.wxOpenid != null) {
+                    return;
+                }
+
                 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);
+                if (this.isCorpWX) {
+                    appId = "ww4e237fd6abb635af"; //企业微信第三方的SUIT ID
+                } 
+
+                var url = "http://mobworktime.ttkuaiban.com/";//授权回调页面
+                var weixinUrl="https://open.weixin.qq.com/connect/oauth2/authorize?appid="+appId+"&redirect_uri="+encodeURI(url)+"&response_type=code&scope=snsapi_base&state="+this.userInfo.id+"#wechat_redirect";
                 window.location.href = weixinUrl;
             },
+
         },
         create() {
             
         },
         mounted() {
-            // if (localStorage.openId == null || localStorage.openId == undefined || localStorage.openId == 'undefined') {
-            //     localStorage.openId = 'oBCQbt2yf7d-OxFuAF4tTJYbiI1I';//测试账号
-            //     // localStorage.openId = 'osp3lt91BuJ_JkwoqawsCI5b8IZM';//测试账号
-            // }
-            
-            
+            var ua = navigator.userAgent.toLowerCase();
+            if (ua.indexOf("wxwork") > 0) {
+                this.isCorpWX = true;
+            } else if (ua.indexOf("micromessenger") > 0) {
+                this.isWX = true;
+            }
         }
     };
 </script>

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

@@ -49,7 +49,7 @@
                 userName:null,
                 total: 0,
                 page: 1,
-                size: 20,
+                size: 1000,
                 list: [],
                 loading: false,
                 finished: false,
@@ -273,7 +273,7 @@
                             this.getProject();
                         } else {
                             toast.clear();
-                            this.$toast.fail('删除失败');
+                            this.$toast.fail(res.msg);
                         }
                     }).catch(err=> {toast.clear();});
                 }).catch(() => {});

+ 217 - 0
fhKeeper/formulahousekeeper/timesheet_h5/src/views/review/department_list.vue

@@ -0,0 +1,217 @@
+<template>
+    <div>
+        <van-nav-bar title="部门审核" left-text="返回" @click-left="back" fixed left-arrow/>
+        <div class="login_form">
+            <van-skeleton  v-for="(item,index) in report" :key="index" title avatar :row="3" :loading="false">
+                <van-panel class="one_report" :title="item.name" status="待部门审核">
+                    <div class="form_text">
+                        <span style="margin-left:5px;">工作日期: {{item.dateStr}}</span>
+                        <span style="float:right;">
+                            <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>
+                        <!-- <span>系统智能统计:{{item.calculateTime}}h</span> -->
+                    </div>
+                    <div v-for="(item1,index1) in item.data" :key="index1" class="one_report_data">
+                        <div class="project_title">项目:{{item1.project}}</div>
+                        <div class="project_title" v-if="user.company.packageEngineering == 1">
+                            专业进度:
+                            <span style="margin-right:10px;" v-for="progressItem in item1.professionProgressList" :key="progressItem.id">{{progressItem.professionName}}({{progressItem.progress}}%) 
+                            </span>
+                        </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>
+                        <van-divider />
+                    </div>
+                    <div class="form_btn" slot="footer">
+                        <van-button size="small" type="info" @click="approve(item.id, item)">通过</van-button>
+                        <van-button size="small" type="danger" @click="deny(item.id,1, item)">驳回</van-button>
+                    </div>
+                </van-panel>
+            </van-skeleton>
+        </div>
+    </div>
+</template>
+
+<script>
+    export default {
+        data() {
+            return {
+                user: JSON.parse(localStorage.userInfo),
+                minDate: new Date(2010, 0, 1),
+                maxDate: new Date(new Date().getFullYear(),new Date().getMonth(),new Date().getDate()),
+                currentDate: new Date(),
+                nowTime: this.format(new Date(new Date()),"yyyy-MM-dd"),
+                showPicker: false,
+                report: [],
+            };
+        },
+        created() {
+        },
+        methods: {
+            // 返回
+            back() {
+                history.back();
+            },
+
+             // 时间转换
+            format(date, pattern) {
+                pattern = pattern || "yyyy-MM-dd";
+                var _this = this;
+                return pattern.replace(/([yMdhsm])(\1*)/g, function ($0) {
+                    switch ($0.charAt(0)) {
+                        case 'y': return _this.padding(date.getFullYear(), $0.length);
+                        case 'M': return _this.padding(date.getMonth() + 1, $0.length);
+                        case 'd': return _this.padding(date.getDate(), $0.length);
+                        case 'w': return date.getDay() + 1;
+                        case 'h': return _this.padding(date.getHours(), $0.length);
+                        case 'm': return _this.padding(date.getMinutes(), $0.length);
+                        case 's': return _this.padding(date.getSeconds(), $0.length);
+                    }
+                });
+            },
+
+            padding(s, len) {
+                var len = len - (s + '').length;
+                for (var i = 0; i < len; i++) { s = '0' + s; }
+                return s;
+            },
+
+            // 改变时间
+            changeTime(time) {
+                this.nowTime = this.format(new Date(time),"yyyy-MM-dd");
+                this.currentDate = time;
+                this.showPicker = false;
+                this.getReport();
+            },
+
+            // 获取待专业审核的日报列表
+            getReport() {
+                const toast = this.$toast.loading({
+                    forbidClick: true,
+                    duration: 0
+                });
+                this.$axios.post("/report/listByStateDepartment", {state:0})
+                .then(res => {
+                    if(res.code == "ok") {
+                        toast.clear();
+                        this.report = res.data;
+                    } else {
+                        toast.clear();
+                        this.$toast.fail('获取失败:'+res.msg);
+                    }
+                }).catch(err=> {toast.clear();});
+            },
+
+            approve(id, item) {
+                const toast = this.$toast.loading({
+                    forbidClick: true,
+                    duration: 0
+                });
+                var ids = '';
+                var data = item.data;
+                data.forEach(element => {
+                    if (element.id != null && element.id != '') {
+                        ids +=(element.id+',');
+                    }
+                });
+                this.$axios.post("/report/approve", {id: id , date: this.nowTime, reportIds: ids})
+                .then(res => {
+                    if(res.code == "ok") {
+                        toast.clear();
+                        this.$toast.success('审核成功');
+                        this.getReport();
+                    } else {
+                        toast.clear();
+                        this.$toast.fail('审核失败');
+                    }
+                }).catch(err=> {toast.clear();});
+            },
+
+            deny(id,i, item) {
+                const toast = this.$toast.loading({
+                    forbidClick: true,
+                    duration: 0
+                });
+                var ids = '';
+                var data = item.data;
+                data.forEach(element => {
+                    if (element.id != null && element.id != '') {
+                        ids +=(element.id+',');
+                    }
+                });
+                this.$axios.post("/report/deny", {id: id , date: this.nowTime, reportIds: ids})
+                .then(res => {
+                    if(res.code == "ok") {
+                        toast.clear();
+                        this.$toast.success(i==0?'驳回成功':'撤销成功');
+                        this.getReport();
+                    } else {
+                        toast.clear();
+                        this.$toast.fail(i==0?'驳回失败':'撤销失败');
+                    }
+                }).catch(err=> {toast.clear();});
+            }
+        },
+
+        mounted() {
+            this.getReport();
+        }
+    };
+</script>
+
+<style lang="less" scoped>
+    .login_form {
+        margin-top: 46px;
+    }
+
+    .one_report {
+        margin-bottom: 15px;
+        font-size:14px;
+    }
+
+    .form_text {
+        margin: 10px 0 10px;
+        padding: 0 12px;
+    }
+    
+    .form_btn {
+        text-align: right;
+    }
+
+    .form_btn button {
+        margin-left: 10px;
+    }
+
+    .one_report_data {
+        margin-bottom: 10px;
+        padding: 0 22px;
+        div {
+            line-height: 30px;
+        }
+    }
+</style>
+<style lang="less">
+    .van-nav-bar .van-icon , .van-nav-bar__text {
+        color: #20a0ff;
+    }
+    // 显示加班样式
+    .one_span {
+        width: 40px;
+        height: 25px;
+        line-height: 25px;
+        text-align: center;
+        box-sizing: border-box;
+        display: inline-block;
+        border: 1px solid #fde2e2;
+        background: #fef0f0;
+        color: #f56c6c;
+        font-size: 12px;
+        border-radius: 5px;
+        // margin-left: 40px;
+        float: right;
+    }
+</style>

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

@@ -3,16 +3,16 @@
         <van-nav-bar title="审核日报" left-text="返回" @click-left="back" fixed left-arrow/>
         
         <div class="login_form">
-            <van-sticky :offset-top="46">
-            <van-field readonly clickable name="datetimePicker" :value="nowTime" label="时间选择" placeholder="点击选择时间" @click="showPicker = true"/>
-            </van-sticky>
-            <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-skeleton  v-for="(item,index) in report" :key="index" title avatar :row="3" :loading="false">
-                <van-panel class="one_report" :title="item.name" :status="item.state==0?'待审核':item.state==1?'已通过':'已驳回'">
+                <van-panel class="one_report" :title="item.name" :status="item.state==0?(user.company.packageEngineering == 1 
+                                                    ?(item.data[0].departmentAuditState==-1?'待专业审核':
+                                                    (item.data[0].departmentAuditState==0?'待部门审核':
+                                                    '待项目经理审核')
+                                                    )
+                                                    :'待审核'):item.state==1?'已通过':'已驳回'">
                     <div class="form_text">
-                        <span style="margin-right:20px;">
+                        <span style="margin-left:5px;">工作日期: {{item.dateStr}}</span>
+                        <span style="float:right;">
                             <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>
@@ -21,6 +21,12 @@
                     </div>
                     <div v-for="(item1,index1) in item.data" :key="index1" class="one_report_data">
                         <div class="project_title">项目:{{item1.project}}</div>
+                        <div class="project_title" v-if="user.company.packageEngineering == 1">
+                            专业进度:
+                            <span style="margin-right:10px;" v-for="progressItem in item1.professionProgressList" :key="progressItem.id">{{progressItem.professionName}}({{progressItem.progress}}%) 
+                            </span>
+                        </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>
@@ -95,7 +101,7 @@
                     forbidClick: true,
                     duration: 0
                 });
-                this.$axios.post("/report/getReportList", {date: this.nowTime})
+                this.$axios.post("/report/listByState", {state: 0})
                 .then(res => {
                     if(res.code == "ok") {
                         toast.clear();
@@ -175,7 +181,7 @@
     }
 
     .form_text {
-        margin: 15px 0 30px;
+        margin: 10px 0 10px;
         padding: 0 12px;
     }
     

+ 218 - 0
fhKeeper/formulahousekeeper/timesheet_h5/src/views/review/profession_list.vue

@@ -0,0 +1,218 @@
+<template>
+    <div>
+        <van-nav-bar title="专业审核" left-text="返回" @click-left="back" fixed left-arrow/>
+        
+        <div class="login_form">
+            <van-skeleton  v-for="(item,index) in report" :key="index" title avatar :row="3" :loading="false">
+                <van-panel class="one_report" :title="item.name" status="待专业审核">
+                    <div class="form_text">
+                        <span style="margin-left:5px;">工作日期: {{item.dateStr}}</span>
+                        <span style="float:right;">
+                            <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>
+                        <!-- <span>系统智能统计:{{item.calculateTime}}h</span> -->
+                    </div>
+                    <div v-for="(item1,index1) in item.data" :key="index1" class="one_report_data">
+                        <div class="project_title">项目:{{item1.project}}</div>
+                        <div class="project_title" v-if="user.company.packageEngineering == 1">
+                            专业进度:
+                            <span style="margin-right:10px;" v-for="progressItem in item1.professionProgressList" :key="progressItem.id">{{progressItem.professionName}}({{progressItem.progress}}%) 
+                            </span>
+                        </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>
+                        <van-divider />
+                    </div>
+                    <div class="form_btn" slot="footer">
+                        <van-button size="small" type="info" @click="approve(item.id, item)">通过</van-button>
+                        <van-button size="small" type="danger" @click="deny(item.id,1, item)">驳回</van-button>
+                    </div>
+                </van-panel>
+            </van-skeleton>
+        </div>
+    </div>
+</template>
+
+<script>
+    export default {
+        data() {
+            return {
+                user: JSON.parse(localStorage.userInfo),
+                minDate: new Date(2010, 0, 1),
+                maxDate: new Date(new Date().getFullYear(),new Date().getMonth(),new Date().getDate()),
+                currentDate: new Date(),
+                nowTime: this.format(new Date(new Date()),"yyyy-MM-dd"),
+                showPicker: false,
+                report: [],
+            };
+        },
+        created() {
+        },
+        methods: {
+            // 返回
+            back() {
+                history.back();
+            },
+
+             // 时间转换
+            format(date, pattern) {
+                pattern = pattern || "yyyy-MM-dd";
+                var _this = this;
+                return pattern.replace(/([yMdhsm])(\1*)/g, function ($0) {
+                    switch ($0.charAt(0)) {
+                        case 'y': return _this.padding(date.getFullYear(), $0.length);
+                        case 'M': return _this.padding(date.getMonth() + 1, $0.length);
+                        case 'd': return _this.padding(date.getDate(), $0.length);
+                        case 'w': return date.getDay() + 1;
+                        case 'h': return _this.padding(date.getHours(), $0.length);
+                        case 'm': return _this.padding(date.getMinutes(), $0.length);
+                        case 's': return _this.padding(date.getSeconds(), $0.length);
+                    }
+                });
+            },
+
+            padding(s, len) {
+                var len = len - (s + '').length;
+                for (var i = 0; i < len; i++) { s = '0' + s; }
+                return s;
+            },
+
+            // 改变时间
+            changeTime(time) {
+                this.nowTime = this.format(new Date(time),"yyyy-MM-dd");
+                this.currentDate = time;
+                this.showPicker = false;
+                this.getReport();
+            },
+
+            // 获取待专业审核的日报列表
+            getReport() {
+                const toast = this.$toast.loading({
+                    forbidClick: true,
+                    duration: 0
+                });
+                this.$axios.post("/report/listByStateProfession", {state:0})
+                .then(res => {
+                    if(res.code == "ok") {
+                        toast.clear();
+                        this.report = res.data;
+                    } else {
+                        toast.clear();
+                        this.$toast.fail('获取失败:'+res.msg);
+                    }
+                }).catch(err=> {toast.clear();});
+            },
+
+            approve(id, item) {
+                const toast = this.$toast.loading({
+                    forbidClick: true,
+                    duration: 0
+                });
+                var ids = '';
+                var data = item.data;
+                data.forEach(element => {
+                    if (element.id != null && element.id != '') {
+                        ids +=(element.id+',');
+                    }
+                });
+                this.$axios.post("/report/approve", {id: id , date: this.nowTime, reportIds: ids})
+                .then(res => {
+                    if(res.code == "ok") {
+                        toast.clear();
+                        this.$toast.success('审核成功');
+                        this.getReport();
+                    } else {
+                        toast.clear();
+                        this.$toast.fail('审核失败');
+                    }
+                }).catch(err=> {toast.clear();});
+            },
+
+            deny(id,i, item) {
+                const toast = this.$toast.loading({
+                    forbidClick: true,
+                    duration: 0
+                });
+                var ids = '';
+                var data = item.data;
+                data.forEach(element => {
+                    if (element.id != null && element.id != '') {
+                        ids +=(element.id+',');
+                    }
+                });
+                this.$axios.post("/report/deny", {id: id , date: this.nowTime, reportIds: ids})
+                .then(res => {
+                    if(res.code == "ok") {
+                        toast.clear();
+                        this.$toast.success(i==0?'驳回成功':'撤销成功');
+                        this.getReport();
+                    } else {
+                        toast.clear();
+                        this.$toast.fail(i==0?'驳回失败':'撤销失败');
+                    }
+                }).catch(err=> {toast.clear();});
+            }
+        },
+
+        mounted() {
+            this.getReport();
+        }
+    };
+</script>
+
+<style lang="less" scoped>
+    .login_form {
+        margin-top: 46px;
+    }
+
+    .one_report {
+        margin-bottom: 15px;
+        font-size:14px;
+    }
+
+    .form_text {
+        margin: 10px 0 10px;
+        padding: 0 12px;
+    }
+    
+    .form_btn {
+        text-align: right;
+    }
+
+    .form_btn button {
+        margin-left: 10px;
+    }
+
+    .one_report_data {
+        margin-bottom: 10px;
+        padding: 0 22px;
+        div {
+            line-height: 30px;
+        }
+    }
+</style>
+<style lang="less">
+    .van-nav-bar .van-icon , .van-nav-bar__text {
+        color: #20a0ff;
+    }
+    // 显示加班样式
+    .one_span {
+        width: 40px;
+        height: 25px;
+        line-height: 25px;
+        text-align: center;
+        box-sizing: border-box;
+        display: inline-block;
+        border: 1px solid #fde2e2;
+        background: #fef0f0;
+        color: #f56c6c;
+        font-size: 12px;
+        border-radius: 5px;
+        // margin-left: 40px;
+        float: right;
+    }
+</style>

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

@@ -22,6 +22,12 @@
                     </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="user.company.packageEngineering == 1">
+                            专业进度:
+                            <span style="margin-right:10px;" v-for="progressItem in item1.professionProgress" :key="progressItem.id">{{progressItem.professionName}}({{progressItem.progress}}%) 
+                            </span>
+                        </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>