Browse Source

Merge branch 'master' of http://47.100.37.243:10191/wutt/manHourHousekeeper into master

seyason 2 years ago
parent
commit
74dcfb83cf
24 changed files with 915 additions and 63 deletions
  1. 212 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/FeishuInfoController.java
  2. 10 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectController.java
  3. 8 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/TaskController.java
  4. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/WeiXinCorpController.java
  5. 7 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/Department.java
  6. 1 28
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/FeishuInfo.java
  7. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/Report.java
  8. 9 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/User.java
  9. 2 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/mapper/ProjectMapper.java
  10. 11 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/FeishuInfoService.java
  11. 2 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ProjectService.java
  12. 219 5
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/FeishuInfoServiceImpl.java
  13. 14 0
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java
  14. 3 1
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/DepartmentMapper.xml
  15. 1 6
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/FeishuInfoMapper.xml
  16. 21 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/ProjectMapper.xml
  17. 118 2
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/UserMapper.xml
  18. 40 3
      fhKeeper/formulahousekeeper/timesheet/src/components/taskComponent.vue
  19. 16 9
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/daily.vue
  20. 92 0
      fhKeeper/formulahousekeeper/timesheet/static/css/index.css
  21. 61 0
      fhKeeper/formulahousekeeper/timesheet/static/js/index.js
  22. 17 0
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/index.vue
  23. 26 3
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/weekEdit.vue
  24. 23 1
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/task/editask.vue

+ 212 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/FeishuInfoController.java

@@ -1,12 +1,29 @@
 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.constant.Constant;
+import com.management.platform.entity.*;
+import com.management.platform.mapper.*;
 import com.management.platform.service.FeishuInfoService;
+import com.management.platform.service.SysRoleService;
+import com.management.platform.service.UserService;
+import com.management.platform.util.*;
+import com.taobao.api.ApiException;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.RequestMapping;
 
 import org.springframework.web.bind.annotation.RestController;
 
 import javax.annotation.Resource;
+import java.time.LocalDateTime;
+import java.util.*;
 
 /**
  * <p>
@@ -22,6 +39,201 @@ public class FeishuInfoController {
 
     @Resource
     private FeishuInfoService feishuInfoService;
+    @Resource
+    private FeishuInfoMapper feishuInfoMapper;
+    @Resource
+    private CompanyMapper companyMapper;
+    @Resource
+    private TimeTypeMapper timeTypeMapper;
+    @Resource
+    private SysRoleService sysRoleService;
+    @Resource
+    private ProjectBasecostSettingMapper projectBasecostSettingMapper;
+    @Resource
+    private CompanyReportMapper companyReportMapper;
+    @Resource
+    private DepartmentFeishuMapper departmentFeishuMapper;
+    @Resource
+    private DepartmentMapper departmentMapper;
+    @Resource
+    private SysRoleMapper sysRoleMapper;
+    @Resource
+    private UserService userService;
+
+
+    /**
+     * 初始化内部应用的系统数据
+     * @return
+     */
+    @RequestMapping("/initSystem")
+    public HttpRespMsg initSystem(String appId) throws Exception {
+        //初始化数据生成公司
+        System.out.println("========接收到initSystem请求===appid="+appId);
+        HttpRespMsg msg = new HttpRespMsg();
+        //查找公司对应的记录是否已经添加
+        FeishuInfo feishuInfo = feishuInfoMapper.selectOne(new QueryWrapper<FeishuInfo>().eq("app_id",appId));
+        if (feishuInfo == null) {
+            //msg.setError("请在company_dingding表中增加corpid:" + corpid);
+            msg.setError("请在company_dingding表中增加appid:" + appId);
+        } else if (feishuInfo.getCompanyId() != null) {
+            //msg.setError("companyId已存在,如需重新初始化请先重置company_dingding中该条数据的companyId为null");
+            msg.setError("companyId已存在,如需重新初始化请先重置feishu_info中该条数据的companyId为null");
+        } else {
+            /*todo 根据appId 获取企业信息*/
+            JSONObject corpInfo = feishuInfoService.getCorpInfo(feishuInfo);
+            String corpName = corpInfo.getString("name");
+            String corpId = corpInfo.getString("display_id");
+            String corpTag = corpInfo.getString("tenant_tag");
+            corpInfo.getString("tenant_key");
+            Company company = new Company().setCompanyName(corpName)
+                    .setExpirationDate(LocalDateTime.now().plusDays(15));
+            company.setPackageWorktime(1);
+            companyMapper.insert(company);
+            feishuInfo.setCorpid(corpId);
+            feishuInfo.setCorpName(corpName);
+            feishuInfo.setCompanyId(company.getId());
+            feishuInfoMapper.updateById(feishuInfo);
+            //生成工作时长
+            TimeType timeType = new TimeType();
+            timeType.setCompanyId(company.getId());
+            timeTypeMapper.insert(timeType);
+            SysRole smanager = sysRoleService.generateDefaultRoles(company.getId());
+            //生成项目的成本基线默认条目
+            String[] array = Constant.DEFAULT_BASE_COST_ITEMS;
+            for (String baseItem : array) {
+                ProjectBasecostSetting setting = new ProjectBasecostSetting();
+                setting.setName(baseItem);
+                setting.setCompanyId(company.getId());
+                projectBasecostSettingMapper.insert(setting);
+            }
+            //todo: 生成项目报表服务默认条目
+            Integer[] arrayInteger=new Integer[]{1,2,3,4,7};
+            for (Integer integerItem : arrayInteger) {
+                CompanyReport companyReport=new CompanyReport();
+                companyReport.setCompanyId(company.getId());
+                companyReport.setReportFormId(integerItem);
+                companyReportMapper.insert(companyReport);
+            }
+            //获取应用可用范围内的部门
+            JSONArray availableRangeIds = feishuInfoService.getAvailableRange(feishuInfo,null);
+            JSONArray departmentInfoArrays=new JSONArray();
+            for (int i = 0; i < availableRangeIds.size(); i++) {
+                String dpId = availableRangeIds.getString(i);
+                JSONArray departmentInfo = feishuInfoService.getDepartmentInfo(feishuInfo, dpId);
+                if(departmentInfo!=null&&departmentInfo.size()>0){
+                    departmentInfoArrays.addAll(departmentInfo);
+                }
+                JSONArray subDepartmentList = feishuInfoService.getSubDepartmentList(feishuInfo, dpId, null);
+                if(subDepartmentList!=null&&subDepartmentList.size()>0){
+                    departmentInfoArrays.addAll(subDepartmentList);
+                }
+            }
+            for (int j = 0; j < departmentInfoArrays.size(); j++) {
+                JSONObject ob = departmentInfoArrays.getJSONObject(j);
+                String departmentName = String.valueOf(ob.get("name"));
+                System.out.println("synchronizationDP========="+departmentName);
+                String departmentId = String.valueOf(ob.getString("department_id"));
+                String openDepartmentId = String.valueOf(ob.getString("open_department_id"));
+                String departmentParentId =String.valueOf(ob.getString("parent_department_id"));
+                Integer cut = departmentFeishuMapper.selectCount(new QueryWrapper<DepartmentFeishu>().eq("corpid",corpId).eq("feishu_deptid",departmentId));
+                if(cut==0&&!departmentId.equals("0")){
+                    System.out.println("join===========");
+                    DepartmentFeishu departmentFeishu=new DepartmentFeishu();
+                    departmentFeishu.setCorpid(corpId)
+                            .setName(departmentName)
+                            .setFeishuParentid(departmentParentId)
+                            .setFeishuDeptid(departmentId)
+                            .setFeishuOpenDeptid(openDepartmentId);
+                    Department department = new Department();
+                    department.setCompanyId(company.getId());
+                    department.setDepartmentName(departmentName);
+                    department.setFeishuDeptid(departmentId);
+                    departmentMapper.insert(department);
+                    departmentFeishu.setSysDeptid(department.getDepartmentId());
+                    departmentFeishuMapper.insert(departmentFeishu);
+                }
+            }
+            SysRole role = sysRoleMapper.selectOne(new QueryWrapper<SysRole>().eq("company_id", company.getId()).eq("rolename","普通员工"));
+            List<Department> departmentList = departmentMapper.selectList(new QueryWrapper<Department>().eq("company_id", company.getId()));
+            List<DepartmentFeishu> departmentFeishuList = departmentFeishuMapper.selectList(new QueryWrapper<DepartmentFeishu>().eq("corpid", corpId));
+            List<User> userList=new ArrayList<>();
+            for (DepartmentFeishu departmentFeishu : departmentFeishuList) {
+                Optional<Department> first = departmentList.stream().filter(dl -> dl.getDepartmentId().equals(departmentFeishu.getSysDeptid())).findFirst();
+                //获取到当前部门的上级部门
+                Optional<DepartmentFeishu> wx = departmentFeishuList.stream().filter(dl -> dl.getFeishuDeptid().equals(departmentFeishu.getFeishuParentid())).findFirst();
+                if(first.isPresent()){
+                    if(wx.isPresent()){
+                        Optional<Department> dp = departmentList.stream().filter(dl -> dl.getDepartmentId().equals(wx.get().getSysDeptid())).findFirst();
+                        if(dp.isPresent()){
+                            first.get().setSuperiorId(dp.get().getDepartmentId());
+                            departmentMapper.updateById(first.get());
+                        }
+                    }
+                }
+                //处理部门下的人员
+                JSONArray userInfoWithDepartment=feishuInfoService.getUserInfoWithDepartment(feishuInfo,departmentFeishu.getFeishuOpenDeptid(),null);
+                for (int m=0;m<userInfoWithDepartment.size(); m++) {
+                    JSONObject userJson = userInfoWithDepartment.getJSONObject(m);
+                    String curUserid = userJson.getString("user_id");
+                    String openUserid = userJson.getString("open_id");
+                    if(!userJson.getJSONObject("status").getBoolean("is_activated")){
+                        continue;
+                    }
+                    List<String> departments = (List<String>) userJson.get("department_ids");
+                    System.out.println("user info======:"+userJson.toString());
+                    //不存在的人员, 进行插入
+                    User user = new User();
+                    //在当前部门下的员工
+                    user.setId(SnowFlake.nextId()+"")
+                            .setRoleId(role.getId())//默认普通员工
+                            .setRoleName(role.getRolename())
+                            .setCompanyId(company.getId())
+                            .setName(userJson.getString("name"))
+                            .setCorpwxUserid(openUserid)
+                            .setCorpwxRealUserid(curUserid)
+                            .setColor(ColorUtil.randomColor())
+                            .setJobNumber(curUserid)
+                            .setPassword(MD5Util.getPassword("000000"))
+                            .setFeishuDeptid(departmentFeishu.getFeishuDeptid());
+                    String max = departments.get(0);
+                    Optional<DepartmentFeishu> dpFs = departmentFeishuList.stream().filter(dl -> dl.getFeishuDeptid().equals(max)).findFirst();
+                    if(dpFs.isPresent()){
+                        Optional<Department> dp = departmentList.stream().filter(dl -> dl.getDepartmentId().equals(dpFs.get().getSysDeptid())).findFirst();
+                        user.setDepartmentName(dp.get().getDepartmentName());
+                        user.setDepartmentId(dp.get().getDepartmentId());
+                        user.setDepartmentCascade(convertDepartmentIdToCascade(dp.get().getDepartmentId(),departmentList));
+                    }
+                    boolean b = userList.stream().anyMatch(ul ->ul.getCorpwxUserid()!=null&&ul.getCorpwxUserid().equals(user.getCorpwxUserid()));
+                    if(!b){
+                        userList.add(user);
+                    }
+                }
+            }
+            System.out.println("resutlList========="+userList);
+            userService.saveBatch(userList);
+        }
+        return msg;
+    }
+
+    //将部门id转换为部门层级
+    private String convertDepartmentIdToCascade(Integer id, List<Department> allDeptList) {
+        StringBuilder cascade = new StringBuilder();
+        if (id == 0) {
+            cascade.append("0");
+        } else {
+            cascade.append(id);
+            id = findById(id, allDeptList).getSuperiorId();
+            while (id != null) {
+                cascade.append(",").append(id);
+                id = findById(id, allDeptList).getSuperiorId();
+            }
+        }
+        return cascade.toString();
+    }
+
+    private Department findById(int id, List<Department> allList) {
+        return allList.stream().filter(all->all.getDepartmentId().intValue() == id).findFirst().get();
+    }
 
 }
 

+ 10 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ProjectController.java

@@ -1185,5 +1185,15 @@ public class ProjectController {
     public HttpRespMsg timeCostAndExpenseByProject(HttpServletRequest request,String startDate,String endDate,Integer projectId){
         return projectService.timeCostAndExpenseByProject(request,startDate,endDate,projectId);
     }
+
+    /**
+     * 获取最近填写的三个项目
+     * @param request
+     * @return
+     */
+    @RequestMapping("/nearProject")
+    public HttpRespMsg nearProject(HttpServletRequest request){
+        return projectService.nearProject(request);
+    }
 }
 

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

@@ -934,7 +934,15 @@ public class TaskController {
         Task task = taskMapper.selectById(taskId);
         DateTimeFormatter df=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
         LocalDateTime start = LocalDateTime.parse(startTime, df);
+        if(start.isBefore(LocalDateTime.now())){
+            httpRespMsg.setError("开始时间必须大于当前时间");
+            return httpRespMsg;
+        }
         LocalDateTime end = LocalDateTime.parse(endTime, df);
+        if(end.isBefore(start)){
+            httpRespMsg.setError("结束时间必须大于开始时间");
+            return httpRespMsg;
+        }
         Duration between = Duration.between(start, end);
         List<TaskExecutor> taskExecutorList = taskExecutorMapper.selectList(new QueryWrapper<TaskExecutor>().eq("task_id", task.getId()));
         if(taskExecutorList.size()>0){

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

@@ -3581,7 +3581,7 @@ public class WeiXinCorpController {
                                     .setRoleName(role.getRolename())
                                     .setCompanyId(company.getId())
                                     .setName(userJson.getString("name"))
-                                    .setCorpwxUserid(curUserid)
+                                    .setCorpwxUserid(openUserid)
                                     .setCorpwxRealUserid(curUserid)
                                     .setColor(ColorUtil.randomColor())
                                     .setJobNumber(curUserid)

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

@@ -16,7 +16,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2023-01-17
+ * @since 2023-03-01
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -87,6 +87,12 @@ public class Department extends Model<Department> {
     @TableField("dd_deptid")
     private Integer ddDeptid;
 
+    /**
+     * 飞书的部门id
+     */
+    @TableField("feishu_deptid")
+    private String feishuDeptid;
+
 
     @Override
     protected Serializable pkVal() {

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

@@ -15,7 +15,7 @@ import lombok.experimental.Accessors;
  * </p>
  *
  * @author Seyason
- * @since 2023-02-28
+ * @since 2023-03-01
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -30,9 +30,6 @@ public class FeishuInfo extends Model<FeishuInfo> {
     @TableField("corp_name")
     private String corpName;
 
-    @TableField("location")
-    private String location;
-
     /**
      * 企业token
      */
@@ -45,12 +42,6 @@ public class FeishuInfo extends Model<FeishuInfo> {
     @TableField("expire_time")
     private LocalDateTime expireTime;
 
-    /**
-     * 授权人姓名
-     */
-    @TableField("auth_username")
-    private String authUsername;
-
     /**
      * 系统内部的公司id
      */
@@ -63,24 +54,6 @@ public class FeishuInfo extends Model<FeishuInfo> {
     @TableField("agentid")
     private Integer agentid;
 
-    /**
-     * 通讯录ApiSecret
-     */
-    @TableField("contact_secret")
-    private String contactSecret;
-
-    /**
-     * 企业通讯录服务器ip和端口
-     */
-    @TableField("contact_server")
-    private String contactServer;
-
-    /**
-     * 开通应用时的授权方式:0-管理员,1-成员授权
-     */
-    @TableField("auth_mode")
-    private Integer authMode;
-
     /**
      * 飞书企业自建应用APPID
      */

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

@@ -52,7 +52,7 @@ public class Report extends Model<Report> {
     private Integer projectId;
 
     /**
-     * 日期
+     * 工作日期
      */
     @TableField("create_date")
     @DateTimeFormat(pattern = "yyyy-MM-dd")

+ 9 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/entity/User.java

@@ -21,7 +21,7 @@ import org.springframework.format.annotation.DateTimeFormat;
  * </p>
  *
  * @author Seyason
- * @since 2023-02-28
+ * @since 2023-03-01
  */
 @Data
 @EqualsAndHashCode(callSuper = false)
@@ -228,6 +228,7 @@ public class User extends Model<User> {
      */
     @TableField("is_ops")
     private Integer isOps;
+
     /**
      * 员工工号,公司内唯一
      */
@@ -275,10 +276,16 @@ public class User extends Model<User> {
      */
     @TableField("feishu_userid")
     private String feishuUserid;
+    /**
+     * 飞书部门id
+     */
+    @TableField("feishu_deptid")
+    private String feishuDeptid;
+
     /**
      * 是否存在日报
      * 0-否 1-是
-     */
+    */
     @TableField(exist = false)
     private Integer isExistsReport;
 

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

@@ -151,4 +151,6 @@ public interface ProjectMapper extends BaseMapper<Project> {
     List<Map<String, Object>> getExportDegreeCost(Integer companyId, String startDate, String endDate,Integer projectId, List<Integer> deptIds, List<Integer> filterDeptIds, List<Integer> deptRelatedProjectIds);
 
     List<Map<String, Object>> getDegreeDetailCost(Integer companyId, String startDate, String endDate, Integer projectId,int curProjectId, List<Integer> finalDeptIds, List<Integer> filterDeptIds, List<Integer> deptIds);
+
+    List<Project> selectNearProject(String userId);
 }

+ 11 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/FeishuInfoService.java

@@ -1,7 +1,10 @@
 package com.management.platform.service;
 
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import com.management.platform.entity.FeishuInfo;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.management.platform.util.HttpRespMsg;
 
 /**
  * <p>
@@ -14,5 +17,13 @@ import com.baomidou.mybatisplus.extension.service.IService;
 public interface FeishuInfoService extends IService<FeishuInfo> {
 
 
+    JSONObject getCorpInfo(FeishuInfo feishuInfo);
 
+    JSONArray getAvailableRange(FeishuInfo feishuInfo,String pageToken);
+
+    JSONArray getSubDepartmentList(FeishuInfo feishuInfo, String departmentId, String pageToken);
+
+    JSONArray getDepartmentInfo(FeishuInfo feishuInfo, String departmentId);
+
+    JSONArray getUserInfoWithDepartment(FeishuInfo feishuInfo, String feishuDeptid, String pageToken);
 }

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

@@ -225,4 +225,6 @@ public interface ProjectService extends IService<Project> {
     HttpRespMsg exportDegreeCost(String startDate, String endDate, Integer projectId,Integer deptId, HttpServletRequest request);
 
     HttpRespMsg timeCostAndExpenseByProject(HttpServletRequest request, String startDate, String endDate, Integer projectId);
+
+    HttpRespMsg nearProject(HttpServletRequest request);
 }

+ 219 - 5
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/FeishuInfoServiceImpl.java

@@ -1,20 +1,26 @@
 package com.management.platform.service.impl;
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.google.gson.JsonObject;
 import com.management.platform.entity.FeishuInfo;
 import com.management.platform.mapper.FeishuInfoMapper;
 import com.management.platform.service.FeishuInfoService;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.management.platform.util.HttpRespMsg;
+import org.apache.http.client.methods.HttpGet;
 import org.springframework.http.*;
 import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
 import org.springframework.web.client.RestTemplate;
 
+import javax.annotation.Resource;
 import java.time.LocalDateTime;
 import java.time.ZoneOffset;
 import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
+import java.util.*;
 
 /**
  * <p>
@@ -32,6 +38,20 @@ public class FeishuInfoServiceImpl extends ServiceImpl<FeishuInfoMapper, FeishuI
         public static final String  GET_APP_ACCESS_TOKEN= "https://open.feishu.cn/open-apis/auth/v3/app_access_token/internal";
         /*自建应用获取 user_access_token*/
         public static final String  GET_USER_ACCESS_TOKEN= "https://open.feishu.cn/open-apis/authen/v1/access_token";
+        /*获取企业信息*/
+        public static final String GET_CORP_INFO="https://open.feishu.cn/open-apis/tenant/v2/tenant/query";
+        /*获取通讯录授权范围*/
+        public static final String GET_AVAILABLE_RANGE="https://open.feishu.cn/open-apis/contact/v3/scopes";
+        /*获取子部门列表*/
+        public static final String GET_SUB_DEPARTMENT_LIST="https://open.feishu.cn/open-apis/contact/v3/departments/:department_id/children";
+        /*获取部门直属用户列表*/
+        public static final String GET_USER_LIST="https://open.feishu.cn/open-apis/contact/v3/users/find_by_department";
+        /*获取单个部门信息*/
+        public static final String GET_DEPARTMENT_INFO="https://open.feishu.cn/open-apis/contact/v3/departments/:department_id";
+
+
+        @Resource
+        FeishuInfoMapper feishuInfoMapper;
 
         public String getAppAccessToken(FeishuInfo feishuInfo){
                 String result="";
@@ -40,7 +60,6 @@ public class FeishuInfoServiceImpl extends ServiceImpl<FeishuInfoMapper, FeishuI
                 RestTemplate restTemplate = new RestTemplate();
                 MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
                 headers.setContentType(type);
-                headers.add("Accept", MediaType.APPLICATION_JSON.toString());
                 JSONObject requestMap = new JSONObject();
                 requestMap.put("app_id",feishuInfo.getAppId());
                 requestMap.put("app_secret",feishuInfo.getAppSecret());
@@ -50,7 +69,202 @@ public class FeishuInfoServiceImpl extends ServiceImpl<FeishuInfoMapper, FeishuI
                         String resp = ResponseEntity.getBody();
                         JSONObject respJson = JSONObject.parseObject(resp);
                         if (respJson.getInteger("code")==0){
-                                result+=respJson.getString("app_access_token");
+                                result=respJson.getString("app_access_token");
+                        }
+                }
+                return result;
+        }
+
+
+        public String getTenantAccessToken(String appId){
+                String result="";
+                FeishuInfo feishuInfo = getOne(new QueryWrapper<FeishuInfo>().eq("app_id", appId));
+                if(feishuInfo!=null){
+                        if(feishuInfo.getExpireTime().isBefore(LocalDateTime.now())){
+                                String url = GET_TENANT_ACCESS_TOKEN;
+                                HttpHeaders headers = new HttpHeaders();
+                                RestTemplate restTemplate = new RestTemplate();
+                                MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
+                                headers.setContentType(type);
+                                JSONObject requestMap = new JSONObject();
+                                requestMap.put("app_id",feishuInfo.getAppId());
+                                requestMap.put("app_secret",feishuInfo.getAppSecret());
+                                HttpEntity<JSONObject> entity = new HttpEntity<>(requestMap, headers);
+                                ResponseEntity<String> ResponseEntity = restTemplate.postForEntity(url, entity, String.class);
+                                if (ResponseEntity.getStatusCode() == HttpStatus.OK) {
+                                        String resp = ResponseEntity.getBody();
+                                        JSONObject respJson = JSONObject.parseObject(resp);
+                                        if (respJson.getInteger("code")==0){
+                                                result=respJson.getString("tenant_access_token");
+                                                feishuInfo.setAccessToken(result);
+                                                LocalDateTime now = LocalDateTime.now();
+                                                now = now.plusSeconds(7200);
+                                                feishuInfo.setExpireTime(now);
+                                                feishuInfoMapper.updateById(feishuInfo);
+                                        }
+                                }
+                        }
+                }
+                return result;
+        }
+
+        @Override
+        public JSONObject getCorpInfo(FeishuInfo feishuInfo){
+                JSONObject result=new JSONObject();
+                String url = GET_CORP_INFO;
+                HttpHeaders headers = new HttpHeaders();
+                RestTemplate restTemplate = new RestTemplate();
+                MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
+                headers.setContentType(type);
+                headers.add("Authorization","Bearer "+feishuInfo.getAccessToken());
+                HttpEntity<JSONObject> httpEntity = new HttpEntity<>(null, headers);
+                ResponseEntity<String> ResponseEntity = restTemplate.exchange(url,HttpMethod.GET,httpEntity,String.class);
+                if (ResponseEntity.getStatusCode() == HttpStatus.OK) {
+                        String resp = ResponseEntity.getBody();
+                        JSONObject respJson = JSONObject.parseObject(resp);
+                        if (respJson.getInteger("code")==0){
+                                JSONObject data = respJson.getJSONObject("data");
+                                result=data.getJSONObject("tenant");
+                        }
+                }
+                return result;
+        }
+
+        @Override
+        public JSONArray getAvailableRange(FeishuInfo feishuInfo,String pageToken){
+                JSONArray result=new JSONArray();
+                String url = GET_AVAILABLE_RANGE;
+                HttpHeaders headers = new HttpHeaders();
+                RestTemplate restTemplate = new RestTemplate();
+                MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
+                headers.setContentType(type);
+                headers.add("Authorization","Bearer "+feishuInfo.getAccessToken());
+                HttpEntity<JSONObject> httpEntity = new HttpEntity<>(null, headers);
+                Map<String,Object> map=new HashMap<>();
+                map.put("page_size",50);
+                if(pageToken!=null){
+                        map.put("page_token",pageToken);
+                }
+                ResponseEntity<String> ResponseEntity = restTemplate.exchange(url,HttpMethod.GET,httpEntity,String.class,map);
+                if (ResponseEntity.getStatusCode() == HttpStatus.OK) {
+                        String resp = ResponseEntity.getBody();
+                        JSONObject respJson = JSONObject.parseObject(resp);
+                        if (respJson.getInteger("code")==0){
+                                JSONObject data = respJson.getJSONObject("data");
+                                JSONArray departmentIds = data.getJSONArray("department_ids");
+                                if(departmentIds!=null&&departmentIds.size()>0){
+                                        result.addAll(departmentIds);
+                                }
+                                if(data.getBoolean("has_more")){
+                                        JSONArray array = getAvailableRange(feishuInfo, data.getString("page_token"));
+                                        if(array!=null&&array.size()>0){
+                                                result.addAll(array);
+                                        }
+                                }
+                        }
+                }
+                return result;
+        }
+
+        @Override
+        public JSONArray getSubDepartmentList(FeishuInfo feishuInfo, String departmentId, String pageToken){
+                JSONArray result=new JSONArray();
+                String url = GET_SUB_DEPARTMENT_LIST.replace(":department_id",departmentId);
+                HttpHeaders headers = new HttpHeaders();
+                RestTemplate restTemplate = new RestTemplate();
+                MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
+                headers.setContentType(type);
+                headers.add("Authorization","Bearer "+feishuInfo.getAccessToken());
+                HttpEntity<JSONObject> httpEntity = new HttpEntity<>(null, headers);
+                Map<String,Object> map=new HashMap<>();
+                map.put("fetch_child",0);
+                map.put("page_size",50);
+                if(pageToken!=null){
+                    map.put("page_token",pageToken);
+                }
+                ResponseEntity<String> ResponseEntity = restTemplate.exchange(url,HttpMethod.GET,httpEntity,String.class,map);
+                if (ResponseEntity.getStatusCode() == HttpStatus.OK) {
+                        String resp = ResponseEntity.getBody();
+                        System.out.println(resp);
+                        JSONObject respJson = JSONObject.parseObject(resp);
+                        if (respJson.getInteger("code")==0){
+                                JSONObject data = respJson.getJSONObject("data");
+                                JSONArray items = data.getJSONArray("items");
+                                if(items!=null&&items.size()>0){
+                                        result.addAll(items);
+                                }
+                                if(data.getBoolean("has_more")){
+                                        JSONArray array = getSubDepartmentList(feishuInfo, departmentId, data.getString("page_token"));
+                                        if(array!=null&&array.size()>0){
+                                                result.addAll(array);
+                                        }
+                                }
+                        }
+                }
+                return result;
+        }
+
+
+        @Override
+        public JSONArray getDepartmentInfo(FeishuInfo feishuInfo, String departmentId){
+                JSONArray result=new JSONArray();
+                String url = GET_DEPARTMENT_INFO.replace(":department_id",departmentId);
+                HttpHeaders headers = new HttpHeaders();
+                RestTemplate restTemplate = new RestTemplate();
+                MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
+                headers.setContentType(type);
+                headers.add("Authorization","Bearer "+feishuInfo.getAccessToken());
+                HttpEntity<JSONObject> httpEntity = new HttpEntity<>(null, headers);
+                ResponseEntity<String> ResponseEntity = restTemplate.exchange(url,HttpMethod.GET,httpEntity,String.class);
+                if (ResponseEntity.getStatusCode() == HttpStatus.OK) {
+                        String resp = ResponseEntity.getBody();
+                        System.out.println(resp);
+                        JSONObject respJson = JSONObject.parseObject(resp);
+                        if (respJson.getInteger("code")==0){
+                                JSONObject data = respJson.getJSONObject("data");
+                                JSONObject items = data.getJSONObject("department");
+                                if(items!=null){
+                                        result.add(items);
+                                }
+                        }
+                }
+                return result;
+        }
+
+        @Override
+        public JSONArray getUserInfoWithDepartment(FeishuInfo feishuInfo, String feishuDeptid, String pageToken) {
+                JSONArray result=new JSONArray();
+                String url = GET_USER_LIST;
+                HttpHeaders headers = new HttpHeaders();
+                RestTemplate restTemplate = new RestTemplate();
+                MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
+                headers.setContentType(type);
+                headers.add("Authorization","Bearer "+feishuInfo.getAccessToken());
+                HttpEntity<JSONObject> httpEntity = new HttpEntity<>(null, headers);
+                Map<String,Object> map=new HashMap<>();
+                map.put("page_size",50);
+                map.put("department_id",feishuDeptid);
+                ResponseEntity<String> ResponseEntity;
+                if(pageToken!=null){
+                        map.put("page_token",pageToken);
+                        ResponseEntity = restTemplate.exchange(url+"?department_id={department_id}&page_size={page_size}&page_token={page_token}",HttpMethod.GET,httpEntity,String.class,map);
+                }
+                ResponseEntity = restTemplate.exchange(url+"?department_id={department_id}&page_size={page_size}",HttpMethod.GET,httpEntity,String.class,map);
+                if (ResponseEntity.getStatusCode() == HttpStatus.OK) {
+                        String resp = ResponseEntity.getBody();
+                        JSONObject respJson = JSONObject.parseObject(resp);
+                        if (respJson.getInteger("code")==0){
+                                JSONObject data = respJson.getJSONObject("data");
+                                JSONArray items = data.getJSONArray("items");
+                                if(items!=null&&items.size()>0){
+                                        result.addAll(items);
+                                }
+                                if(data.getBoolean("has_more")){
+                                        JSONArray array = getUserInfoWithDepartment(feishuInfo, feishuDeptid, data.getString("page_token"));
+                                        if(array!=null&&array.size()>0){
+                                                result.addAll(array);
+                                        }
+                                }
                         }
                 }
                 return result;

+ 14 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ProjectServiceImpl.java

@@ -9345,4 +9345,18 @@ public class ProjectServiceImpl extends ServiceImpl<ProjectMapper, Project> impl
         httpRespMsg.data = map;
         return httpRespMsg;
     }
+
+    /**
+     * 获取最新填写的三个项目
+     * @param request
+     * @return
+     */
+    @Override
+    public HttpRespMsg nearProject(HttpServletRequest request) {
+        HttpRespMsg httpRespMsg = new HttpRespMsg();
+        User user = userMapper.selectById(request.getHeader("token"));
+        List<Project> projects = projectMapper.selectNearProject(user.getId());
+        httpRespMsg.data = projects;
+        return httpRespMsg;
+    }
 }

+ 3 - 1
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/DepartmentMapper.xml

@@ -6,6 +6,7 @@
     <resultMap id="BaseResultMap" type="com.management.platform.entity.Department">
         <id column="department_id" property="departmentId" />
         <result column="department_name" property="departmentName" />
+        <result column="seq" property="seq" />
         <result column="superior_id" property="superiorId" />
         <result column="company_id" property="companyId" />
         <result column="manager_id" property="managerId" />
@@ -13,11 +14,12 @@
         <result column="corpwx_deptid" property="corpwxDeptid" />
         <result column="corpwx_deptpid" property="corpwxDeptpid" />
         <result column="dd_deptid" property="ddDeptid" />
+        <result column="feishu_deptid" property="feishuDeptid" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        department_id, department_name, superior_id, company_id, manager_id, report_audit_userid, corpwx_deptid, corpwx_deptpid, dd_deptid
+        department_id, department_name, seq, superior_id, company_id, manager_id, report_audit_userid, corpwx_deptid, corpwx_deptpid, dd_deptid, feishu_deptid
     </sql>
     <!--根据部门获取成本-->
     <select id="getCostByDepartment" resultType="java.util.Map">

+ 1 - 6
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/FeishuInfoMapper.xml

@@ -6,22 +6,17 @@
     <resultMap id="BaseResultMap" type="com.management.platform.entity.FeishuInfo">
         <id column="corpid" property="corpid" />
         <result column="corp_name" property="corpName" />
-        <result column="location" property="location" />
         <result column="access_token" property="accessToken" />
         <result column="expire_time" property="expireTime" />
-        <result column="auth_username" property="authUsername" />
         <result column="company_id" property="companyId" />
         <result column="agentid" property="agentid" />
-        <result column="contact_secret" property="contactSecret" />
-        <result column="contact_server" property="contactServer" />
-        <result column="auth_mode" property="authMode" />
         <result column="app_id" property="appId" />
         <result column="app_secret" property="appSecret" />
     </resultMap>
 
     <!-- 通用查询结果列 -->
     <sql id="Base_Column_List">
-        corpid, corp_name, location, access_token, expire_time, auth_username, company_id, agentid, contact_secret, contact_server, auth_mode, app_id, app_secret
+        corpid, corp_name, access_token, expire_time, company_id, agentid, app_id, app_secret
     </sql>
 
 </mapper>

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

@@ -1570,4 +1570,25 @@
         GROUP BY a.project_id,b.id,a.degree_id
         ORDER BY b.id,a.degree_id ASC
     </select>
+
+    <select id="selectNearProject" resultType="com.management.platform.entity.Project">
+        select * from project
+        where id in (
+            SELECT project_id
+            from (
+                SELECT project_id
+                FROM (
+                        SELECT DISTINCT *
+                        FROM report
+                        WHERE creator_id = #{userId}
+                        order by create_time
+                        DESC
+                    ) p1
+                    GROUP BY project_id
+                    order by create_time
+                    DESC
+                    LIMIT 3
+            ) p2
+        )
+    </select>
 </mapper>

File diff suppressed because it is too large
+ 118 - 2
fhKeeper/formulahousekeeper/management-platform/src/main/resources/mapper/UserMapper.xml


+ 40 - 3
fhKeeper/formulahousekeeper/timesheet/src/components/taskComponent.vue

@@ -4,10 +4,18 @@
         <el-form ref="form1" :model="addForm" :rules="taskRules" label-width="120px">
             <el-form-item label="所属项目" v-if="showOrNot" prop="projectId">
                 <el-select v-model="addForm.projectId" :placeholder="$t('defaultText.pleaseChoose')" @change="agentCreatesEvents(1)" filterable="true" style="width:100%;">
-                    <el-option v-for="item in projectList" :key="item.id" :label="item.projectName + item.projectCode" :value="item.id">
+
+                    <!-- <el-option v-for="item in projectList" :key="item.id" :label="item.projectName + item.projectCode" :value="item.id">
                         <span style="float: left;color: #8492a6;">{{ item.projectCode }}</span>
                         <span style="float: right;font-size: 13px;">{{ item.projectName }}</span>
-                    </el-option>
+                    </el-option> -->
+                    <el-option-group v-for="group in integrationProjectList" :key="group.label" :label="group.label">
+                        <el-option v-for="item in group.peojectList" :key="item.id" :label="item.projectName + '\u3000' + item.projectCode" :value="item.id">
+                            <span style="float: left; color: #8492a6; font-size: 13px;">{{ item.projectCode }}</span>
+                            <span style="float: right;">{{ item.projectName }}</span>
+                        </el-option>
+                    </el-option-group>
+
                 </el-select>
             </el-form-item>
             <el-form-item label="所属任务分组" v-if="showOrNot" prop="groupId">
@@ -817,7 +825,8 @@ export default {
         taskListTotal: 0,
         dynamicTab: true,
         dailyList: [],
-        meetingId: ''
+        meetingId: '',
+        integrationProjectList: []
     };
   },
   computed: {},
@@ -887,6 +896,7 @@ export default {
         res => {
             if (res.code == "ok") {
                 this.projectList = res.data;
+                this.getRecentlyProject()
             } else {
                 this.$message({
                     message: res.msg,
@@ -901,6 +911,33 @@ export default {
             });
         });
     },
+    // 获取最近项目列表
+    getRecentlyProject() {
+        this.http.post('/project/nearProject',{},res => {
+            if(res.code == 'ok'){
+                let topObj = {
+                    label: '最近选择项目',
+                    peojectList: res.data 
+                }
+                let botomObj = {
+                    label: '全部项目',
+                    peojectList: this.projectList
+                }
+                this.integrationProjectList = [topObj, botomObj]
+                console.log(this.integrationProjectList, '整合')
+            }else {
+                this.$message({
+                    message: res.msg,
+                    type: 'error'
+                })
+            }
+        },err => {
+            this.$message({
+                message: err,
+                type: 'error'
+            })
+        })
+    },
     // 获取任务分组
     getTaskGrouping() {
         this.http.post('/task-group/list', {projectId:this.addForm.projectId},

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

@@ -430,17 +430,17 @@
                             @change="selectProject(domain, index)"
                             :disabled="workForm.domains.length==0?true:(workForm.domains[index].state>=2?false:true)" popper-class="projectSelectPopperClass">
                             
-                                <el-option v-for="item in fillProjectList" :disabled="item.status!=1 && item.status!=4" :key="item.id" :label="item.projectName + '\u3000' + item.projectCode" :value="item.id">
+                                <!-- <el-option v-for="item in fillProjectList" :disabled="item.status!=1 && item.status!=4" :key="item.id" :label="item.projectName + '\u3000' + item.projectCode" :value="item.id">
                                     <span style="float: left; color: #8492a6; font-size: 13px;">{{ item.projectCode }}</span>
                                     <span style="float: right;">{{ item.projectName }}</span>
-                                </el-option>
+                                </el-option> -->
 
-                                <!-- <el-option-group v-for="group in integrationProjectList" :key="group.label" :label="group.label">
+                                <el-option-group v-for="group in integrationProjectList" :key="group.label" :label="group.label">
                                     <el-option v-for="item in group.peojectList" :key="item.id" :label="item.projectName  + '\u3000' + item.projectCode" :value="item.id" :disabled="item.status!=1 && item.status!=4">
                                         <span style="float: left; color: #8492a6; font-size: 13px;">{{ item.projectCode }}</span>
                                         <span style="float: right;">{{ item.projectName }}</span>
                                     </el-option>
-                                </el-option-group> -->
+                                </el-option-group>
 
                             </el-select>
                             <template v-if="user.timeType.mainProjectState != 1">
@@ -593,17 +593,17 @@
                                 <el-select v-model="domain.projectId" :placeholder="$t('defaultText.pleaseSelectSnItem')" style="width:200px;" clearable="true"  filterable="true" value-key="id"
                                 @change="selectProject(domain, index)"
                                 :disabled="workForm.domains.length==0?true:(workForm.domains[index].state>=2?false:true)" popper-class="projectSelectPopperClass">
-                                    <el-option v-for="item in fillProjectList" :disabled="item.status!=1 && item.status!=4" :key="item.id" :label="item.projectName + '\u3000' + item.projectCode" :value="item.id">
+                                    <!-- <el-option v-for="item in fillProjectList" :disabled="item.status!=1 && item.status!=4" :key="item.id" :label="item.projectName + '\u3000' + item.projectCode" :value="item.id">
                                         <span style="float: left; color: #8492a6; font-size: 13px;">{{ item.projectCode }}</span>
                                         <span style="float: right;">{{ item.projectName }}</span>
-                                    </el-option>
+                                    </el-option> -->
 
-                                    <!-- <el-option-group v-for="group in integrationProjectList" :key="group.label" :label="group.label">
+                                    <el-option-group v-for="group in integrationProjectList" :key="group.label" :label="group.label">
                                         <el-option v-for="item in group.peojectList" :key="item.id" :label="item.projectName + '\u3000' + item.projectCode" :value="item.id" :disabled="item.status!=1 && item.status!=4">
                                             <span style="float: left; color: #8492a6; font-size: 13px;">{{ item.projectCode }}</span>
                                             <span style="float: right;">{{ item.projectName }}</span>
                                         </el-option>
-                                    </el-option-group> -->
+                                    </el-option-group>
 
                                 </el-select>
                                 <template v-if="user.timeType.mainProjectState != 1">
@@ -1994,6 +1994,7 @@
                 for(let i in domains){
                     if(domains[i].projectId){
                         if(this.reportTimeType.multiWorktime == 1){
+                            console.log('进一')
                             for(let m in domains[i].worktimeList){
                                 if(domains[i].worktimeList[m].startTime && domains[i].worktimeList[m].endTime){
                                     hours += this.getHour(domains[i].worktimeList[m].startTime, domains[i].worktimeList[m].endTime)
@@ -2001,6 +2002,7 @@
                             }
                         }else{
                             if(this.user.timeType.type == 2){
+                            console.log('进2')
                                 if(domains[i].startTime && domains[i].endTime){
                                     // let selectionTime = this.getHourMinutes(domains[i].startTime, domains[i].endTime)
                                     let selectionTime = this.getHour(domains[i].startTime, domains[i].endTime)
@@ -2016,11 +2018,13 @@
                                     // hours += this.getHour(domains[i].startTime, domains[i].endTime)
                                 }
                             }else{
+                            console.log('进3')
                                 hours += domains[i].workingTime ? parseFloat(domains[i].workingTime) : 0
                             }
                         }
                     }
                 }
+                console.log(hours, '需要返回的数据')
                 return hours.toFixed(2)
             },
         },
@@ -2056,7 +2060,6 @@
                     that.dataLoading = that.$store.state.dataLoading
                 }, 1000)
             }
-            // this.getRecentlyProject() // 近期选择的项目
         },
         methods: {
             ...mapMutations(['upDataLoading']),
@@ -4472,6 +4475,7 @@
                             }
                         }
                         this.projectList = res.data;
+                        
                         // console.log("项目列表",this.projectList);
                     } else {
                         this.$message({
@@ -4500,6 +4504,7 @@
                             }
                         }
                         this.fillProjectList = res.data;
+                        this.getRecentlyProject() 
                     } else {
                         this.$message({
                             message: res.msg,
@@ -5730,6 +5735,7 @@
                                 // 关闭弹窗 并 清空
                                 this.selProjectList = []
                                 this.selCon = []
+                                this.getRecentlyProject()
                             } else {
                                 this.$message({
                                     message: res.msg,
@@ -6358,6 +6364,7 @@
                                 this.jsTime = 0
                                 this.getReportList();
                                 this.getDepartment();
+                                this.getRecentlyProject()
                             } else {
                                 this.$message({
                                     message: (this.isDraft==0?this.$t('message.SubmissionFailed') + ':':this.$t('message.Stagingfailed') + ':')+res.msg,

+ 92 - 0
fhKeeper/formulahousekeeper/timesheet/static/css/index.css

@@ -0,0 +1,92 @@
+* {
+    margin: 0;
+    padding: 0;
+}
+
+body {
+    background-color: #ebf1fd;
+}
+
+.header {
+    display: flex;
+    flex-direction: column;
+    background-color: white;
+}
+
+.header .time-message {
+    display: flex;
+    height: 44px;
+    align-items: center;
+    padding: 0 33.5px;
+    justify-content: space-between;
+}
+
+.header .title {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    height: 44px;
+}
+
+.header .title span {
+    font-weight: 500;
+    font-size: 17px;
+}
+
+.img {
+    width: 120px;
+    height: 239px;
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+    display: flex;
+    align-items: center;
+    flex-direction: column;
+}
+
+.img_div {
+    border-radius: 50%;
+    overflow: hidden;
+    width: 88px;
+    height: 88px;
+    border: 3px white solid;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+}
+
+.hello_text {
+    font-size: 26px;
+    font-weight: 600;
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+}
+
+.hello_text_name {
+    font-size: 20px;
+    color: #3370ff;
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, 50%);
+    text-align: center;
+}
+
+.hello_text_welcome {
+    position: absolute;
+    bottom: 0;
+    size: 20px;
+    font-weight: 500;
+    text-align: center;
+    white-space: nowrap;
+}
+
+.icon {
+    position: absolute;
+    bottom: 44px;
+    left: 50%;
+    transform: translate(-50%, 0);
+}

+ 61 - 0
fhKeeper/formulahousekeeper/timesheet/static/js/index.js

@@ -0,0 +1,61 @@
+let lang = window.navigator.language;
+console.log(lang);
+
+
+function apiAuth() {
+    if (!window.h5sdk) {
+        console.log('invalid h5sdk')
+        // alert('please open in feishu')
+        return
+    }
+
+    // 通过服务端的Route: get_appid获取app_id
+    // 服务端Route: get_appid的具体内容请参阅服务端模块server.py的get_appid()函数
+    // 为了安全,app_id不应对外泄露,尤其不应在前端明文书写,因此此处从服务端获取
+    fetch(`/get_appid`).then(response1 => response1.json().then(res1 => {
+        console.log("get appid succeed: ", res1.appid);
+        // 通过error接口处理API验证失败后的回调
+        window.h5sdk.error(err => {
+            throw('h5sdk error:', JSON.stringify(err));
+        });
+        // 通过ready接口确认环境准备就绪后才能调用API
+        window.h5sdk.ready(() => {
+            console.log("window.h5sdk.ready");
+            console.log("url:", window.location.href);
+            // 调用JSAPI tt.requestAuthCode 获取 authorization code
+            tt.requestAuthCode({
+                appId: res1.appid,
+                // 获取成功后的回调
+                success(res) {
+                    console.log("getAuthCode succeed");
+                    //authorization code 存储在 res.code
+                    // 此处通过fetch把code传递给接入方服务端Route: callback,并获得user_info
+                    // 服务端Route: callback的具体内容请参阅服务端模块server.py的callback()函数
+                    fetch(`/callback?code=${res.code}`).then(response2 => response2.json().then(res2 => {
+                        console.log("getUserInfo succeed");
+                        // 示例Demo中单独定义的函数showUser,用于将用户信息展示在前端页面上
+                        showUser(res2);}
+                        )
+                    ).catch(function (e) {console.error(e)})
+                },
+                // 获取失败后的回调
+                fail(err) {
+                    console.log(`getAuthCode failed, err:`, JSON.stringify(err));
+                }
+            })
+        }
+        )
+    })).catch(function (e) { // 从服务端获取app_id失败后的处理
+        console.error(e)
+        })
+}
+
+function showUser(res) {
+    // 展示用户信息
+    // 头像
+    $('#img_div').html(`<img src="${res.avatar_url}" width="100%" height=""100%/>`);
+    // 名称
+    $('#hello_text_name').text(lang === "zh_CN" || lang === "zh-CN" ? `${res.name}` : `${res.en_name}`);
+    // 欢迎语
+    $('#hello_text_welcome').text(lang === "zh_CN" || lang === "zh-CN" ? "欢迎使用飞书" : "welcome to Feishu");
+}

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

@@ -384,6 +384,13 @@
             <!-- <van-search v-model="userName" placeholder="输入项目名称搜索" @clear="sea()" @blur="sea()" @search="sea()"></van-search> -->
             <van-search v-model="userName" placeholder="请输入项目名称/编号" @clear="sea()" @blur="sea()" @search="sea()" @input="sea()"/>
             <div style="minHeight:300px;">
+                <div class="ryuan" style="color: rgb(185 185 185);">近期选择项目</div>
+                <div v-for="(item, index) in integrationProjectList" :key="index" class="ryuan" @click="fZr(item, index)">
+                    <p>{{item.projectName}}</p>
+                    <p style="margin-top: 5px;color: #9697B2;">{{item.projectCode}}</p> 
+                </div>
+
+                <div class="ryuan" style="color: rgb(185 185 185);">全部项目</div>
                 <div v-for="(item, index) in projectss" :key="item.id" class="ryuan" @click="fZr(item, index)">
                     <p>{{item.projectName}}</p>
                     <p style="margin-top: 5px;color: #9697B2;">{{item.projectCode}}</p> 
@@ -398,6 +405,7 @@ import timetoolVue from '../timetool/timetool.vue';
     export default {
         data() {
             return {
+                integrationProjectList: [],
                 cardRefLoading: false,
                 today: '',
 
@@ -532,6 +540,14 @@ import timetoolVue from '../timetool/timetool.vue';
         },
 
         methods: {
+            getRecentlyProject() {
+                this.$axios.post('/project/nearProject',{})
+                .then(res => {
+                    if(res.code == 'ok'){
+                        this.integrationProjectList = res.data
+                    }
+                }).catch(err => {this.$toast.clear();this.cardRefLoading = false;})
+            },
             // 判断两个时间段是否重叠
             timeOverlap(idx, dateAr) {
                 let zhi = 0
@@ -675,6 +691,7 @@ import timetoolVue from '../timetool/timetool.vue';
                         this.project = res.data;
                         this.projectss = this.projectss.filter(p=>p.status == 1 || p.status == 4);
                         this.proads = res.data
+                        this.getRecentlyProject()
                     } else {
                         this.$toast.fail('获取失败:'+res.msg);
                     }

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

@@ -347,9 +347,21 @@
         <van-popup v-model="showPickerUserddp" position="bottom" style="height: 80%">
             <van-search v-model="userName" placeholder="请输入项目名称/编号" @clear="sea()" @blur="sea()" @search="sea()" @input="sea()"/>
             <div style="minHeight:300px;">
-                <div v-for="(item, index) in projectss" :key="item.id" class="ryuan" @click="fZr(item, index)">
+                <!-- <div v-for="(item, index) in projectss" :key="item.id" class="ryuan" @click="fZr(item, index)">
                     <p>{{item.projectName}}</p> 
                     <p style="margin-top: 5px;color: #9697B2;">{{item.projectCode}}</p> 
+                </div> -->
+
+                <div class="ryuan" style="color: rgb(185 185 185);">近期选择项目</div>
+                <div v-for="(item, index) in integrationProjectList" :key="index" class="ryuan" @click="fZr(item, index)">
+                    <p>{{item.projectName}}</p>
+                    <p style="margin-top: 5px;color: #9697B2;">{{item.projectCode}}</p> 
+                </div>
+
+                <div class="ryuan" style="color: rgb(185 185 185);">全部项目</div>
+                <div v-for="(item, index) in projectss" :key="item.id" class="ryuan" @click="fZr(item, index)">
+                    <p>{{item.projectName}}</p>
+                    <p style="margin-top: 5px;color: #9697B2;">{{item.projectCode}}</p> 
                 </div>
             </div>
         </van-popup> 
@@ -448,11 +460,20 @@
                     searchText: '',
                     item: {},
                     list: [],
-                    searchList: []
-                }
+                    searchList: [],
+                },
+                integrationProjectList: []
             };
         },
         methods: {
+            getRecentlyProject() {
+                this.$axios.post('/project/nearProject',{})
+                .then(res => {
+                    if(res.code == 'ok'){
+                        this.integrationProjectList = res.data
+                    }
+                }).catch(err => {this.$toast.clear();this.cardRefLoading = false;})
+            },
             auditorClick(domainIndex,auditorIndex){
                 this.auditor.index = domainIndex
                 this.auditor.auditorIndex = auditorIndex
@@ -747,6 +768,8 @@
                         this.projectss = res.data;
                         this.projectss = this.projectss.filter(p=>p.status == 1);
                         this.proads = res.data
+
+                        this.getRecentlyProject()
                     } else {
                         this.$toast.fail('获取失败:'+res.msg);
                     }

+ 23 - 1
fhKeeper/formulahousekeeper/timesheet_h5/src/views/task/editask.vue

@@ -9,6 +9,13 @@
                         <van-search v-model.trim="select_project_show_searchText" placeholder="输入项目名称搜索" @input="onSearchProject"></van-search>
                         <div style="minHeight:300px;">
                             <van-radio-group v-model="taskform.projectId">
+                                <div class="ewProjectlist">近期选择项目</div>
+                                <van-radio v-for="(uitem, index) in integrationProjectList" :key="index" :name="uitem" style="padding:10px">
+                                    <span>{{uitem.projectName}}</span>
+                                </van-radio>
+                            </van-radio-group>
+                            <van-radio-group v-model="taskform.projectId">
+                                <div class="ewProjectlist">全部项目</div>
                                 <van-radio v-for="uitem in select_project_array" :key="uitem.id" :name="uitem" style="padding:10px">
                                     <span>{{uitem.projectName}}</span>
                                 </van-radio>
@@ -209,8 +216,9 @@ export default {
             select_project_array_tow: [], 
             select_grouping_array: [],
             select_list_array: [],
-            select_project_show_searchText: ''
+            select_project_show_searchText: '',
 
+            integrationProjectList: []
         }
     },
     mounted() {
@@ -293,11 +301,20 @@ export default {
                 if(res.code == "ok") {
                     this.select_project_array = res.data
                     this.select_project_array_tow = res.data
+                    this.getRecentlyProject()
                 } else {
                     this.$toast.fail('失败');
                 }
             }).catch(err=> {this.$toast.clear();console.log(err)});
         },
+        getRecentlyProject() {
+            this.$axios.post('/project/nearProject',{})
+            .then(res => {
+                if(res.code == 'ok'){
+                    this.integrationProjectList = res.data
+                }
+            }).catch(err => {this.$toast.clear();this.cardRefLoading = false;})
+        },
         getTaskGrouping() {
             this.$axios.post("/task-group/list", {projectId:this.taskform.projectId})
             .then(res => {
@@ -568,6 +585,11 @@ export default {
     display: inline-block;
     float: right;
 }
+.ewProjectlist {
+    padding: 10px 10px 10px 20px;
+    border-bottom: 1px solid #666;
+    color: #a5a5a5;
+}
 </style>
 <style>
     .editaskBox .van-radio__label {