Browse Source

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

seyason 8 months ago
parent
commit
f7683e99f9
25 changed files with 17367 additions and 627 deletions
  1. 8 2
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/ReportController.java
  2. 11 3
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserCorpwxTimeController.java
  3. 1 1
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/ReportService.java
  4. 145 24
      fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/service/impl/ReportServiceImpl.java
  5. 1 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/i18n/messages.properties
  6. 1 0
      fhKeeper/formulahousekeeper/management-platform/src/main/resources/i18n/messages_en_US.properties
  7. 178 14
      fhKeeper/formulahousekeeper/timesheet/package-lock.json
  8. 1 0
      fhKeeper/formulahousekeeper/timesheet/package.json
  9. 2 1
      fhKeeper/formulahousekeeper/timesheet/src/i18n/en.json
  10. 2 1
      fhKeeper/formulahousekeeper/timesheet/src/i18n/zh.json
  11. 10 0
      fhKeeper/formulahousekeeper/timesheet/src/main.js
  12. 16 0
      fhKeeper/formulahousekeeper/timesheet/src/routes.js
  13. 2 2
      fhKeeper/formulahousekeeper/timesheet/src/views/contract/index.vue
  14. 243 0
      fhKeeper/formulahousekeeper/timesheet/src/views/deviceManagement/deviceManagement.vue
  15. 4 0
      fhKeeper/formulahousekeeper/timesheet/src/views/project/cost.vue
  16. 59 4
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/list.vue
  17. 4 2
      fhKeeper/formulahousekeeper/timesheet/src/views/workReport/newWeeklyCustomization.vue
  18. 16140 572
      fhKeeper/formulahousekeeper/timesheet_h5/package-lock.json
  19. 1 0
      fhKeeper/formulahousekeeper/timesheet_h5/package.json
  20. 259 0
      fhKeeper/formulahousekeeper/timesheet_h5/src/components/qrcode.vue
  21. 14 0
      fhKeeper/formulahousekeeper/timesheet_h5/src/router/index.js
  22. 25 0
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/deviceManagement/usageHistory.vue
  23. 230 0
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/deviceManagement/useRegistration.vue
  24. 0 1
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/edit/index.vue
  25. 10 0
      fhKeeper/formulahousekeeper/timesheet_h5/src/views/index/index.vue

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

@@ -1263,7 +1263,7 @@ public class ReportController {
                     Optional<Project> first = targetProjectList.stream().filter(tl -> tl.getId().equals(report.getProjectId())).findFirst();
                     if(first.isPresent()){
                         double nowReport = targetCheckList.stream().filter(rl -> rl.getCreateDate().equals(report.getCreateDate()) && rl.getCreatorId().equals(report.getCreatorId())).mapToDouble(Report::getWorkingTime).sum();
-                        if(first.get().getManDay()!=null){
+                        if(first.get().getManDay()!=null && first.get().getManDay()>0){
                             //已填报的工时情况
                             double sum;
                             if(first.get().getManDayStartDate()!=null){
@@ -2195,7 +2195,13 @@ public class ReportController {
     @RequestMapping("/batchDenyHisReport")
     public HttpRespMsg batchDenyHisReport(@RequestParam Integer reportAuditLogId,
                                      String reason, HttpServletRequest request) {
-        return reportService.batchDenyHisReport(reportAuditLogId, reason, request);
+        try {
+            return reportService.batchDenyHisReport(reportAuditLogId, reason, request);
+        } catch (Exception e) {
+            HttpRespMsg msg = new HttpRespMsg();
+            msg.setError("批量审核失败:"+e.getMessage());
+            return msg;
+        }
     }
 
     @RequestMapping("/getMembList")

+ 11 - 3
fhKeeper/formulahousekeeper/management-platform/src/main/java/com/management/platform/controller/UserCorpwxTimeController.java

@@ -21,6 +21,7 @@ import org.springframework.format.annotation.DateTimeFormat;
 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 org.springframework.web.multipart.MultipartFile;
 
@@ -338,7 +339,7 @@ public class UserCorpwxTimeController {
 
     //导出带考勤数据的模板
     @RequestMapping("/exportCheckInExcel")
-    public HttpRespMsg exportCheckInExcel(String startDate, String endDate) throws Exception {
+    public HttpRespMsg exportCheckInExcel(String startDate, String endDate, @RequestParam(required = false, defaultValue = "true") Boolean onlySelfData) throws Exception {
         HttpRespMsg msg = new HttpRespMsg();
         String token = request.getHeader("TOKEN");
         User user = userMapper.selectById(token);
@@ -346,8 +347,13 @@ public class UserCorpwxTimeController {
         WxCorpInfo wxCorpInfo = wxCorpInfoMapper.selectOne(new QueryWrapper<WxCorpInfo>().eq("company_id", companyId));
         CompanyDingding dingding = companyDingdingMapper.selectOne(new LambdaQueryWrapper<CompanyDingding>().eq(CompanyDingding::getCompanyId, companyId));
         Integer manageDeptId = user.getManageDeptId();
+        HttpRespMsg ret = null;
+        if (onlySelfData) {
+            ret = getMyData(startDate, endDate);
+        } else {
+            ret = getMyDeptMembsData(startDate, endDate);
+        }
 
-        HttpRespMsg ret = getMyDeptMembsData(startDate, endDate);
         HashMap map = (HashMap)ret.data;
         List<Map> list = (List<Map>) map.get("list");
         List<String> projects = (List) map.get("projects");
@@ -371,7 +377,9 @@ public class UserCorpwxTimeController {
 //        titles.add("请假时长");
 //        titles.add("补卡/外出时长");
         //titles.add("实际工作时长");
+        //工作内容
         titles.add(MessageUtils.message("entry.actualWorkTime"));
+        titles.add("工作内容");
         //添加项目名称作为列名
         for (String p : projects) {
             titles.add(p);
@@ -421,7 +429,7 @@ public class UserCorpwxTimeController {
             fileName = departmentName;
         }
         //fileName += "_人员工时统计模板"+startDate+"至"+endDate;
-        fileName += MessageUtils.message("fileName.workHour",startDate,endDate);
+        fileName += MessageUtils.message("fileName.workHour",startDate,endDate+"("+user.getName()+")");
         return excelExportService.exportGeneralExcelByTitleAndList(wxCorpInfo,dingding, fileName, allData, path);
     }
 

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

@@ -166,5 +166,5 @@ public interface ReportService extends IService<Report> {
 
     HttpRespMsg transferReport(String reportIds, Integer projectId, Integer groupId, Integer stageId);
 
-    HttpRespMsg batchDenyHisReport(Integer reportAuditLogId, String reason, HttpServletRequest request);
+    HttpRespMsg batchDenyHisReport(Integer reportAuditLogId, String reason, HttpServletRequest request) throws Exception;
 }

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

@@ -576,7 +576,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             List<ReportExtraDegree> degreeList = reportExtraDegreeMapper.selectList(new QueryWrapper<ReportExtraDegree>().eq("company_id", companyId));
             List<TaskGroup> taskGroups = integerList.size() > 0?taskGroupMapper.selectList(new QueryWrapper<TaskGroup>().in("project_id", integerList)):new ArrayList<>();
             List<Stages> stagesList = integerList.size() > 0?stagesMapper.selectList(new QueryWrapper<Stages>().in("project_id", integerList)) : new ArrayList<>();
-
+            TimeType timeType = timeTypeMapper.selectById(companyId);
             //获取当前项目的子项目列表,任务分组,任务列表,项目相关维度列表
             reports.forEach(r->{
                 //设置项目名称
@@ -676,11 +676,32 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 }
                 //项目的审核人
                 if (reportAuditType == 0) {
-                    r.setAuditUserList(auditorList.stream().filter(au->au.getProjectId().equals(r.getProjectId())).collect(Collectors.toList()));
-                    if (r.getProjectAuditorId() != null) {
-                        Optional<ProjectAuditor> auItem = auditorList.stream().filter(au->au.getAuditorId().equals(r.getProjectAuditorId())).findFirst();
-                        if (auItem.isPresent()) {
-                            r.setProjectAuditorName(auItem.get().getAuditorName());
+                    //对于简单模式的非项目,直接获取部门主管作为审核人
+                    if (company.getNonProjectSimple() == 1) {
+                        User user = userMapper.selectById(r.getCreatorId());
+                        Department department = departmentMapper.selectById(user.getDepartmentId());
+                        if (department != null) {
+                            User deptManager = userMapper.selectById(department.getManagerId());
+                            List<ProjectAuditor> auditorList1 = new ArrayList<>();
+                            ProjectAuditor auditor = new ProjectAuditor();
+                            auditor.setAuditorId(deptManager.getId());
+                            auditor.setAuditorName(deptManager.getName());
+                            auditorList1.add(auditor);
+                            r.setAuditUserList(auditorList1);
+                        }
+                        if (r.getProjectAuditorId() != null) {
+                            Optional<ProjectAuditor> auItem = r.getAuditUserList().stream().filter(au->au.getAuditorId().equals(r.getProjectAuditorId())).findFirst();
+                            if (auItem.isPresent()) {
+                                r.setProjectAuditorName(auItem.get().getAuditorName());
+                            }
+                        }
+                    } else {
+                        r.setAuditUserList(auditorList.stream().filter(au->au.getProjectId().equals(r.getProjectId())).collect(Collectors.toList()));
+                        if (r.getProjectAuditorId() != null) {
+                            Optional<ProjectAuditor> auItem = auditorList.stream().filter(au->au.getAuditorId().equals(r.getProjectAuditorId())).findFirst();
+                            if (auItem.isPresent()) {
+                                r.setProjectAuditorName(auItem.get().getAuditorName());
+                            }
                         }
                     }
                 } else if (reportAuditType == 1 || reportAuditType == 2) {
@@ -768,7 +789,6 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 }
             });
             resultMap.put("report", reports);
-            TimeType timeType = timeTypeMapper.selectById(companyId);
             boolean showRefresh = false;
             if (timeType.getShowDdCardtime() == 1) {
                 List<UserDingdingTime> dingdingTimes = userDingdingTimeMapper.selectList(new QueryWrapper<UserDingdingTime>()
@@ -1385,11 +1405,12 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             ReportLog log = new ReportLog();
             log.setCreatorId(report.getCreatorId());
             log.setCreateDate(report.getCreateDate());
+            String msg = opName+"审核通过了日报";
             //去重
             Optional<ReportLog> oldLog = addLogList.stream().filter(add -> add.getCreateDate().isEqual(log.getCreateDate()) && add.getCreatorId().equals(log.getCreatorId())).findFirst();
             if (!oldLog.isPresent()) {
                 log.setOperatorId(operatorId);
-                log.setMsg(opName+"审核通过了日报");
+                log.setMsg(msg);
                 log.setCompanyId(companyId);
                 log.setReportIds(report.getId()+"");
                 addLogList.add(log);
@@ -1400,10 +1421,10 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             ReportLogDetail detail = new ReportLogDetail();
             detail.setReportId(report.getId());
             detail.setWorkDate(log.getCreateDate());
-            detail.setOperatorId(log.getOperatorId());
+            detail.setOperatorId(operatorId);
             detail.setOperateDate(log.getOperateDate());
             detail.setCompanyId(companyId);
-            detail.setMsg(log.getMsg());
+            detail.setMsg(msg);
             addLogDetailList.add(detail);
         }
         reportLogService.saveBatch(addLogList);
@@ -4976,7 +4997,7 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
             }
             int deptColumnExtra = (hasDept?1:0);
             //下标从0开始
-            int projectNameStartIndex = (withCheckIn==null?2:(6 + deptColumnExtra));
+            int projectNameStartIndex = (withCheckIn==null?2:(7 + deptColumnExtra));
             List<User> targetUserList=new ArrayList<>();
             HttpRespMsg respMsg=new HttpRespMsg();
             if(wxCorpInfo!=null&&wxCorpInfo.getSaasSyncContact()==1&&userNameList.size()>0){
@@ -5059,6 +5080,23 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 } else {
                     dataCount++;
                     //数据行
+                    //读取工作内容
+                    String workContent = null;
+                    if (withCheckIn!=null) {
+                        int workContentIndex = projectNameStartIndex -1;
+                        if (row.getCell(workContentIndex) == null) {
+                            //msg.setError("第"+dataCount+"行缺少工作内容");工作内容为必填项
+                            msg.setError(MessageUtils.message("data.LackWorkContentByRow",dataCount));
+                            return msg;
+                        }
+                        workContent = row.getCell(workContentIndex).getStringCellValue().trim();
+                        if (StringUtils.isEmpty(workContent)) {
+                            //msg.setError("第"+dataCount+"行缺少工作内容");
+                            msg.setError(MessageUtils.message("data.LackWorkContentByRow",dataCount));
+                            return msg;
+                        }
+                    }
+
                     for (int i=1;i<projectNameStartIndex+projectList.size(); i++) {
                         if (row.getCell(i) != null) {
                             row.getCell(i).setCellType(CellType.STRING);
@@ -5110,6 +5148,10 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                                 report.setCreatorId(reportCreator.getId());
                                 report.setDeptId(reportCreator.getDepartmentId());
                                 report.setProjectId(project.getId());
+                                if (withCheckIn != null) {
+                                    report.setContent(workContent);
+                                }
+
                                 //子项目
                                 if (!StringUtils.isEmpty(subPName)) {
                                     Optional<SubProject> first = allSubProjectList.stream()
@@ -6095,13 +6137,14 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                         item.add("");
                     }
                 }
+                String createDate = null;
                 if (map.get("createDate") instanceof java.sql.Date) {
-                    item.add(new SimpleDateFormat("yyyy-MM-dd")
-                            .format((java.sql.Date) map.get("createDate")));
+                    createDate = new SimpleDateFormat("yyyy-MM-dd")
+                            .format((java.sql.Date) map.get("createDate"));
                 } else {
-                    item.add((String)map.get("createDate"));
+                    createDate = (String) map.get("createDate");
                 }
-
+                item.add(createDate);
 //                item.add(new SimpleDateFormat("yyyy-MM-dd")
 //                        .format((java.sql.Date) map.get("createDate")));
                 item.add(map.get("duration").toString());
@@ -6435,8 +6478,8 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
                 }
 
                 if (timeType.getEnableNewWeeklyfill() == 1) {
-                    String createDate = new SimpleDateFormat("yyyy-MM-dd")
-                            .format((java.sql.Date) map.get("createDate"));
+//                    String createDate = new SimpleDateFormat("yyyy-MM-dd")
+//                            .format((java.sql.Date) map.get("createDate"));
                     ReportBatch batchItem = reportBatchMapper.selectOne(new QueryWrapper<ReportBatch>().eq("creator_id", map.get("creatorId")).eq("start_date", createDate).last("limit 1"));
                     if (batchItem != null) {
                         item.add(batchItem.getSummary());
@@ -9879,15 +9922,93 @@ public class ReportServiceImpl extends ServiceImpl<ReportMapper, Report> impleme
     }
 
     @Override
-    public HttpRespMsg batchDenyHisReport(Integer reportAuditLogId, String reason, HttpServletRequest request) {
-        List<ReportAlogMembdate> list = reportAlogMembdateMapper.selectList(new QueryWrapper<ReportAlogMembdate>().eq("rlog_id", reportAuditLogId));
-        for (ReportAlogMembdate logMembItem : list) {
-            HttpRespMsg msg = denyHisReport(logMembItem.getId(), reason, request);
-            if (msg.code.equals("error")) {
-                //异常需要跳出
-                return msg;
+    @Transactional(rollbackFor = Exception.class)
+    public HttpRespMsg batchDenyHisReport(Integer reportAuditLogId, String reason, HttpServletRequest request) throws Exception {
+        List<ReportAlogMembdate> dataList = reportAlogMembdateMapper.selectList(new QueryWrapper<ReportAlogMembdate>().eq("rlog_id", reportAuditLogId));
+        for (ReportAlogMembdate item : dataList) {
+            int hisId = item.getId();
+            ReportAlogMembdate log = reportAlogMembdateMapper.selectById(hisId);
+            String userId = log.getUserId();
+            String createDate = DateTimeFormatter.ofPattern("yyyy-MM-dd").format(log.getCreateDate());
+            if (log.getState() != 1) {
+                //已通过的直接跳过
+                continue;
+            }
+            User user = userMapper.selectById(request.getHeader("Token"));
+            Company company = companyMapper.selectById(user.getCompanyId());
+            if (reason == null) {
+                reason = "-";
+            }
+            QueryWrapper<Report> eq = new QueryWrapper<Report>().eq("create_date", createDate).eq("creator_id", userId);
+            List<Report> list = reportMapper.selectList(eq);
+            Report oneReport = null;
+            if (list.size() == 0) {
+                //httpRespMsg.setError("日报已不存在");
+                throw new Exception(MessageUtils.message("profession.alreadyNull"));
+            } else  {
+                if (list.get(0).getState() == 3) {
+                    //只有已通过的历史记录才能撤销
+                    continue;
+                } else if (list.get(0).getState() == 2) {
+                    //只有已通过的历史记录才能撤销
+                    //httpRespMsg.setError("该日报已被撤销,无法重复操作");
+                    continue;
+                }
+            }
+            oneReport = list.get(0);
+            //直接进行项目经理审核驳回
+            reportMapper.update(new Report().setState(2)
+                            .setRejectReason(reason).setRejectUserid(user.getId()).setRejectUsername(user.getName()),eq);
+
+            TimeType timeType = timeTypeMapper.selectById(company.getId());
+            if (timeType.getReportAuditType() == 2 || timeType.getReportAuditType() == 9) {
+                List<Report> rList = list;
+                //退回任务分组审核状态
+                List<Report> newList = new ArrayList<>();
+                for (Report r : rList) {
+                    Report upR = new Report();
+                    upR.setId(r.getId());
+                    upR.setGroupAuditState(0);
+                    String inchargerId = taskGroupMapper.selectById(r.getGroupId()).getInchargerId();
+                    upR.setProjectAuditorId(inchargerId);
+                    upR.setProjectAuditorName(userMapper.selectById(inchargerId).getName());
+                    newList.add(upR);
+                }
+                updateBatchById(newList);
+            }
+
+            //修改审核记录的状态
+            log.setState(3);
+            reportAlogMembdateMapper.updateById(log);
+
+            saveDenyReportLog(list, user.getId(), user.getName(), reason);
+
+            List<Integer> collect = list.stream().map(Report::getProjectId).collect(Collectors.toList());
+            List<Project> projectList = projectMapper.selectList(new QueryWrapper<Project>().in("id", collect));
+            String pNames = projectList.stream().map(Project::getProjectName).collect(Collectors.joining(", ", "[", "]"));
+            //String str = "您"+createDate+"填写的日报中"+pNames+"项目被["+user.getName()+"]驳回。原因:" + reason;;
+            String str = MessageUtils.message("report.dailyReject",createDate,pNames,user.getName(),reason);
+            String fillUserId = oneReport.getCreatorId();
+
+            informationMapper.insert(new Information().setType(0).setContent(createDate).setUserId(fillUserId).setMsg(str));
+
+            //发送企业微信通知消息
+            User reporter = userMapper.selectById(fillUserId);
+            String corpwxUserid = reporter.getCorpwxUserid();
+            //先判断钉钉
+            if (reporter.getDingdingUserid() != null) {
+                projectMapper.selectById(oneReport.getProjectId()).getProjectName();
+                companyDingdingService.sendRejectReportMsg(reporter.getCompanyId(), createDate, pNames, reason, user.getName(), reporter.getDingdingUserid());
+            }
+            if (corpwxUserid != null) {
+                WxCorpInfo info = wxCorpInfoMapper.selectOne(new QueryWrapper<WxCorpInfo>().eq("company_id", company.getId()));
+                wxCorpInfoService.sendWXCorpMsg(info, corpwxUserid, str, null, WxCorpInfoServiceImpl.TEXT_CARD_MSG_REPORT_DENY);
+            } else if (reporter.getWxOpenid() != null){
+                //发送个人微信通知
+                pushReject(str, reporter, user.getName(), reason);
             }
         }
+
         return new HttpRespMsg();
     }
 }

+ 1 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/i18n/messages.properties

@@ -105,6 +105,7 @@ date.startThanEnd=开始日期不能大于结束日期
 date.dateThan365=日期间隔不得超过365天
 date.formatError=日期格式有误
 data.NullErrorByRow=第{0}行缺少日期
+data.LackWorkContentByRow=第{0}行缺少工作内容
 # 公司相关
 Company.nullNameError=名称不能为空
 Company.nameRepeat=该名称已存在

+ 1 - 0
fhKeeper/formulahousekeeper/management-platform/src/main/resources/i18n/messages_en_US.properties

@@ -105,6 +105,7 @@ date.startThanEnd=The start date cannot be later than the end date.
 date.dateThan365=The date interval shall not exceed 365 days.
 date.formatError=Incorrect date format
 data.NullErrorByRow=Missing date in row '{0}'
+data.LackWorkContentByRow=Missing work content in row '{0}'
 # 公司相关
 Company.nullNameError=Name cannot be empty
 Company.nameRepeat=The name already exists.

+ 178 - 14
fhKeeper/formulahousekeeper/timesheet/package-lock.json

@@ -2624,8 +2624,7 @@
     "decamelize": {
       "version": "1.2.0",
       "resolved": "https://registry.npmmirror.com/decamelize/-/decamelize-1.2.0.tgz",
-      "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
-      "dev": true
+      "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="
     },
     "decode-uri-component": {
       "version": "0.2.2",
@@ -2798,6 +2797,11 @@
         }
       }
     },
+    "dijkstrajs": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
+      "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="
+    },
     "dingtalk-jsapi": {
       "version": "2.15.4",
       "resolved": "https://registry.npmmirror.com/dingtalk-jsapi/-/dingtalk-jsapi-2.15.4.tgz",
@@ -3579,8 +3583,7 @@
     "get-caller-file": {
       "version": "2.0.5",
       "resolved": "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz",
-      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
-      "dev": true
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
     },
     "get-intrinsic": {
       "version": "1.2.1",
@@ -5926,6 +5929,11 @@
         "find-up": "^1.0.0"
       }
     },
+    "pngjs": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmmirror.com/pngjs/-/pngjs-5.0.0.tgz",
+      "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw=="
+    },
     "posix-character-classes": {
       "version": "0.1.1",
       "resolved": "https://registry.npmmirror.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
@@ -7128,6 +7136,167 @@
       "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==",
       "dev": true
     },
+    "qrcode": {
+      "version": "1.5.4",
+      "resolved": "https://registry.npmmirror.com/qrcode/-/qrcode-1.5.4.tgz",
+      "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
+      "requires": {
+        "dijkstrajs": "^1.0.1",
+        "pngjs": "^5.0.0",
+        "yargs": "^15.3.1"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "5.0.1",
+          "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz",
+          "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="
+        },
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "camelcase": {
+          "version": "5.3.1",
+          "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz",
+          "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
+        },
+        "cliui": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmmirror.com/cliui/-/cliui-6.0.0.tgz",
+          "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+          "requires": {
+            "string-width": "^4.2.0",
+            "strip-ansi": "^6.0.0",
+            "wrap-ansi": "^6.2.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+        },
+        "emoji-regex": {
+          "version": "8.0.0",
+          "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz",
+          "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+        },
+        "find-up": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmmirror.com/find-up/-/find-up-4.1.0.tgz",
+          "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+          "requires": {
+            "locate-path": "^5.0.0",
+            "path-exists": "^4.0.0"
+          }
+        },
+        "is-fullwidth-code-point": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+        },
+        "locate-path": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-5.0.0.tgz",
+          "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+          "requires": {
+            "p-locate": "^4.1.0"
+          }
+        },
+        "p-limit": {
+          "version": "2.3.0",
+          "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz",
+          "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+          "requires": {
+            "p-try": "^2.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-4.1.0.tgz",
+          "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+          "requires": {
+            "p-limit": "^2.2.0"
+          }
+        },
+        "p-try": {
+          "version": "2.2.0",
+          "resolved": "https://registry.npmmirror.com/p-try/-/p-try-2.2.0.tgz",
+          "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
+        },
+        "path-exists": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz",
+          "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
+        },
+        "string-width": {
+          "version": "4.2.3",
+          "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
+          "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+          "requires": {
+            "emoji-regex": "^8.0.0",
+            "is-fullwidth-code-point": "^3.0.0",
+            "strip-ansi": "^6.0.1"
+          }
+        },
+        "strip-ansi": {
+          "version": "6.0.1",
+          "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz",
+          "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+          "requires": {
+            "ansi-regex": "^5.0.1"
+          }
+        },
+        "wrap-ansi": {
+          "version": "6.2.0",
+          "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+          "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+          "requires": {
+            "ansi-styles": "^4.0.0",
+            "string-width": "^4.1.0",
+            "strip-ansi": "^6.0.0"
+          }
+        },
+        "yargs": {
+          "version": "15.4.1",
+          "resolved": "https://registry.npmmirror.com/yargs/-/yargs-15.4.1.tgz",
+          "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+          "requires": {
+            "cliui": "^6.0.0",
+            "decamelize": "^1.2.0",
+            "find-up": "^4.1.0",
+            "get-caller-file": "^2.0.1",
+            "require-directory": "^2.1.1",
+            "require-main-filename": "^2.0.0",
+            "set-blocking": "^2.0.0",
+            "string-width": "^4.2.0",
+            "which-module": "^2.0.0",
+            "y18n": "^4.0.0",
+            "yargs-parser": "^18.1.2"
+          }
+        },
+        "yargs-parser": {
+          "version": "18.1.3",
+          "resolved": "https://registry.npmmirror.com/yargs-parser/-/yargs-parser-18.1.3.tgz",
+          "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+          "requires": {
+            "camelcase": "^5.0.0",
+            "decamelize": "^1.2.0"
+          }
+        }
+      }
+    },
     "qs": {
       "version": "6.11.2",
       "resolved": "https://registry.npmmirror.com/qs/-/qs-6.11.2.tgz",
@@ -7553,8 +7722,7 @@
     "require-directory": {
       "version": "2.1.1",
       "resolved": "https://registry.npmmirror.com/require-directory/-/require-directory-2.1.1.tgz",
-      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
-      "dev": true
+      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="
     },
     "require-from-string": {
       "version": "1.2.1",
@@ -7565,8 +7733,7 @@
     "require-main-filename": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/require-main-filename/-/require-main-filename-2.0.0.tgz",
-      "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
-      "dev": true
+      "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
     },
     "requires-port": {
       "version": "1.0.0",
@@ -7796,8 +7963,7 @@
     "set-blocking": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/set-blocking/-/set-blocking-2.0.0.tgz",
-      "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
-      "dev": true
+      "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
     },
     "set-value": {
       "version": "2.0.1",
@@ -9869,8 +10035,7 @@
     "which-module": {
       "version": "2.0.1",
       "resolved": "https://registry.npmmirror.com/which-module/-/which-module-2.0.1.tgz",
-      "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
-      "dev": true
+      "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="
     },
     "wide-align": {
       "version": "1.1.5",
@@ -9986,8 +10151,7 @@
     "y18n": {
       "version": "4.0.3",
       "resolved": "https://registry.npmmirror.com/y18n/-/y18n-4.0.3.tgz",
-      "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
-      "dev": true
+      "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
     },
     "yallist": {
       "version": "2.1.2",

+ 1 - 0
fhKeeper/formulahousekeeper/timesheet/package.json

@@ -21,6 +21,7 @@
     "font-awesome": "^4.7.0",
     "jquery": "^3.4.1",
     "nprogress": "^0.2.0",
+    "qrcode": "^1.5.4",
     "tinymce": "^5.7.1",
     "v-distpicker": "^1.2.13",
     "v-viewer": "^1.6.4",

+ 2 - 1
fhKeeper/formulahousekeeper/timesheet/src/i18n/en.json

@@ -30,7 +30,8 @@
     "basicDataManagement": "Basic data management",
     "budgetReview": "Estimated working hours review",
     "projectFormSettings": "Project Form Settings",
-    "userGroupManagement": "User group management"
+    "userGroupManagement": "User group management",
+    "deviceManagement": "deviceManagement"
   },
   "role": {
     "ordinaryEmployees": "Ordinary employees",

+ 2 - 1
fhKeeper/formulahousekeeper/timesheet/src/i18n/zh.json

@@ -30,7 +30,8 @@
     "budgetReview": "预估工时审核",
     "gongshitongji": "工时统计表",
     "caiwushenhe": "财务审核",
-    "userGroupManagement": "用户分组管理"
+    "userGroupManagement": "用户分组管理",
+    "deviceManagement": "设备管理"
   },
   "role": {
     "ordinaryEmployees": "普通员工",

+ 10 - 0
fhKeeper/formulahousekeeper/timesheet/src/main.js

@@ -119,6 +119,16 @@ router.beforeEach((to, from, next) => {
     }
 
     let user = JSON.parse(sessionStorage.getItem('user'));
+
+    // 添加临时路由
+    if(user && user.moduleList && user.companyId == 10 && user.moduleList.length > 0) {
+        const modulOne = JSON.parse(JSON.stringify(user.moduleList[0] || [])) 
+        console.log(modulOne, '<===== modulOne')
+        modulOne.name = '设备管理'
+        modulOne.path = '/deviceManagement'
+        user.moduleList.splice(2, 0, modulOne);
+    }
+
     if (!user && to.path != '/login') {
         next({ path: '/login' })
     } else {

+ 16 - 0
fhKeeper/formulahousekeeper/timesheet/src/routes.js

@@ -99,6 +99,9 @@ import financeAudit from './views/financeAudit/financeAudit.vue'
 // 用户分组管理
 import userGrouping from './views/userGrouping/userGrouping.vue'
 
+// 设备管理
+import deviceManagement from './views/deviceManagement/deviceManagement'
+
 Vue.use(Router)
 
 export const fixedRouter = [
@@ -514,6 +517,19 @@ export const allRouters = [//组织架构
         // 其他信息
         meta: { text: 'navigation.basicDataManagement' }
     },
+    // 设备管理
+    {
+        path: '/',
+        component: Home,
+        name: '设备管理',
+        iconCls: 'iconfont firerock-icondaibanshixiang',
+        leaf: true,//只有一个节点
+        children: [
+            { path: '/deviceManagement', component: deviceManagement, name: '设备管理' },
+        ],
+        // 其他信息
+        meta: { text: 'navigation.deviceManagement' } 
+    },
     {
         path: '/404',
         component: NotFound,

+ 2 - 2
fhKeeper/formulahousekeeper/timesheet/src/views/contract/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div>
-    <template v-if="true">
+    <template v-if="user.companyId != '4215'">
       <ContractManagement />
     </template>
     <!-- 定制的合同 -->
@@ -21,7 +21,7 @@ export default {
   },
   data() {
     return {
-
+      user: JSON.parse(sessionStorage.getItem("user")),
     };
   },
   computed: {},

+ 243 - 0
fhKeeper/formulahousekeeper/timesheet/src/views/deviceManagement/deviceManagement.vue

@@ -0,0 +1,243 @@
+<template>
+  <div class="deviceManagement">
+    <div class="deviceManagement-title">
+      <div class="deviceManagement-title-left">
+        <div>设备管理</div>
+        <el-input placeholder="请输入设备编码" v-model.trim="equipmentCode" size="small" style="width: 300px;" clearable>
+          <template slot="append">
+            <el-button @click="searchForDevices">搜索</el-button>
+          </template>
+        </el-input>
+      </div>
+
+      <div class="deviceManagement-title-right">
+        <el-button type="primary" size="small" @click="editDevice({}, true)">添加设备</el-button>
+      </div>
+    </div>
+
+    <div class="deviceManagement-table">
+      <el-table :data="deviceManagementList" border style="width: 100%" height="calc(100% - 10px)">
+        <el-table-column prop="equipmentNumber" label="设备编号"></el-table-column>
+        <el-table-column prop="equipmentName" label="设备名称"></el-table-column>
+        <el-table-column prop="equipmentModel" label="设备型号"></el-table-column>
+        <el-table-column prop="manufacturer" label="制造商"></el-table-column>
+        <el-table-column prop="purchaseDate" label="购置日期" width="200"></el-table-column>
+        <el-table-column prop="maintenanceDate" label="维保日期" width="200"></el-table-column>
+        <el-table-column prop="operation" label="操作" width="200">
+          <template slot-scope="scope">
+            <el-button type="text" @click="editDevice(scope.row, true)">编辑</el-button>
+            <el-button type="text" @click="deleteDevice(scope.$index, scope.row)">删除</el-button>
+            <el-button type="text" @click="seeDevice(scope.$index, scope.row)">查看二维码</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+    <!-- 新增编辑 -->
+    <el-dialog title="新增/修改设备" :visible.sync="equipmentVisable" width="780px" top="7.8vh" :before-close="handleClose">
+      <div class="modifyEquipment">
+        <el-form :model="equipmentForm" :rules="equipmentRules" ref="equipmentruleForm" label-width="100px"
+          class="demo-ruleForm">
+          <el-form-item label="设备编号" prop="equipmentNumber">
+            <el-input v-model.trim="equipmentForm.equipmentNumber" placeholder="请输入设备编码"></el-input>
+          </el-form-item>
+          <el-form-item label="设备名称" prop="equipmentName">
+            <el-input v-model.trim="equipmentForm.equipmentName" placeholder="请输入设备名称"></el-input>
+          </el-form-item>
+          <el-form-item label="设备型号" prop="equipmentModel">
+            <el-input v-model.trim="equipmentForm.equipmentModel" placeholder="请输入设备型号"></el-input>
+          </el-form-item>
+          <el-form-item label="制造商" prop="manufacturer">
+            <el-input v-model.trim="equipmentForm.manufacturer" placeholder="请输入制造商"></el-input>
+          </el-form-item>
+          <el-form-item label="购置日期" prop="purchaseDate">
+            <el-date-picker v-model="equipmentForm.purchaseDate" value-format="yyyy-MM-dd" type="date"
+              placeholder="选择日期" style="width: 100%;"></el-date-picker>
+          </el-form-item>
+          <el-form-item label="维保日期" prop="maintenanceDate">
+            <el-date-picker v-model="equipmentForm.maintenanceDate" value-format="yyyy-MM-dd" type="date"
+              placeholder="选择日期" style="width: 100%;"></el-date-picker>
+          </el-form-item>
+        </el-form>
+      </div>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="equipmentVisable = false">取 消</el-button>
+        <el-button type="primary" @click="submitForm('equipmentruleForm')">确 定</el-button>
+      </span>
+    </el-dialog>
+
+    <!-- 查看二维码 -->
+    <el-dialog :title="`${equipmentItem.equipmentNumber} - ${equipmentItem.equipmentName}`"
+      :visible.sync="equipmentQrCodeVisable" width="600px" top="7.8vh" :before-close="handleClose">
+      <div class="equipmentQrCode">
+        <div class="equipmentQrCodeImg">
+          <img :src="qrCodeURL" alt="二维码" />
+        </div>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import QRCode from 'qrcode';
+export default {
+  data() {
+    return {
+      equipmentCode: '',
+      equipmentVisable: false,
+      equipmentQrCodeVisable: false,
+      equipmentForm: {},
+      equipmentItem: {},
+      qrCodeURL: '',
+      equipmentRules: {
+        equipmentNumber: [{ required: true, message: '请输入设备编码', trigger: 'blur' }],
+        equipmentName: [{ required: true, message: '请输入设备名称', trigger: 'blur' }],
+        equipmentModel: [{ required: true, message: '请输入设备型号', trigger: 'blur' }],
+        manufacturer: [{ required: true, message: '请输入制造商', trigger: 'blur' }],
+        purchaseDate: [{ required: true, message: '请选择购置日期', trigger: 'blur' }],
+        maintenanceDate: [{ required: true, message: '请选择维保日期', trigger: 'blur' }]
+      },
+      deviceManagementList: [
+        { equipmentNumber: '123456789', equipmentName: '设备名称1(在使用)', equipmentModel: '设备型号', manufacturer: '制造商', purchaseDate: '2019-01-01', maintenanceDate: '2020-01-01', status: 1, projectName: '展示项目', durationOfUse: 1, usageTime: '2024-09-03 10:11:11' },
+        { equipmentNumber: '987654321', equipmentName: '设备名称2', equipmentModel: '设备型号', manufacturer: '制造商', purchaseDate: '2019-01-01', maintenanceDate: '2020-01-01' },
+        { equipmentNumber: 'aabbccas', equipmentName: '设备名称3', equipmentModel: '设备型号', manufacturer: '制造商', purchaseDate: '2019-01-01', maintenanceDate: '2020-01-01' },
+        { equipmentNumber: 'jkjkjkjkjk', equipmentName: '设备名称4(在使用)', equipmentModel: '设备型号', manufacturer: '制造商', purchaseDate: '2019-01-01', maintenanceDate: '2020-01-01', status: 1, projectName: '小小神射手', durationOfUse: 15, usageTime: '2024-08-07 08:09:11' },
+      ],
+      deviceManagementListTwo: [
+        { equipmentNumber: '123456789', equipmentName: '设备名称1(在使用)', equipmentModel: '设备型号', manufacturer: '制造商', purchaseDate: '2019-01-01', maintenanceDate: '2020-01-01', status: 1, projectName: '展示项目', durationOfUse: 1, usageTime: '2024-09-03 10:11:11' },
+        { equipmentNumber: '987654321', equipmentName: '设备名称2', equipmentModel: '设备型号', manufacturer: '制造商', purchaseDate: '2019-01-01', maintenanceDate: '2020-01-01' },
+        { equipmentNumber: 'aabbccas', equipmentName: '设备名称3', equipmentModel: '设备型号', manufacturer: '制造商', purchaseDate: '2019-01-01', maintenanceDate: '2020-01-01' },
+        { equipmentNumber: 'jkjkjkjkjk', equipmentName: '设备名称4(在使用)', equipmentModel: '设备型号', manufacturer: '制造商', purchaseDate: '2019-01-01', maintenanceDate: '2020-01-01', status: 1, projectName: '小小神射手', durationOfUse: 15, usageTime: '2024-08-07 08:09:11' },
+      ]
+    };
+  },
+  mounted() {
+  },
+  methods: {
+    searchForDevices() {
+      console.log('点击搜索')
+      const newList = JSON.parse(JSON.stringify(this.deviceManagementListTwo))
+      this.deviceManagementList = newList.filter(item => item.equipmentNumber.indexOf(this.equipmentCode) > -1)
+    },
+    seeDevice(i, row) {
+      QRCode.toDataURL(JSON.stringify(row))
+        .then(url => {
+          this.qrCodeURL = url;
+        })
+        .catch(err => {
+          console.error(err);
+        });
+      this.equipmentItem = row
+      this.equipmentQrCodeVisable = true
+    },
+    editDevice(row, flag) {
+      this.equipmentForm = row
+      this.equipmentVisable = flag
+    },
+    deleteDevice(i, row) {
+      const { equipmentNumber } = row
+      this.$confirm(`此操作将删除设备编码为【${equipmentNumber}】的设备, 是否继续?`, '删除设备', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.deviceManagementList.splice(i, 1)
+        this.deviceManagementListTwo.splice(i, 1)
+        this.$message({
+          type: 'success',
+          message: '删除成功!'
+        });
+      }).catch(() => { });
+    },
+    submitForm(formName) {
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          console.log('submit!');
+          const { equipmentNumber } = this.equipmentForm
+          const numverList = this.deviceManagementList.map(item => item.equipmentNumber == equipmentNumber)
+          if (numverList.length > 0) {
+            this.$message({
+              message: '设备编号重复',
+              type: 'error'
+            })
+            return
+          }
+          this.deviceManagementList.push(this.equipmentForm)
+          this.deviceManagementListTwo.push(this.equipmentForm)
+          this.$message({
+            message: '提交成功',
+            type: 'success'
+          });
+          this.equipmentVisable = false
+        } else {
+          console.log('error submit!!');
+          return false;
+        }
+      });
+    }
+  },
+};
+</script>
+
+<style lang="scss" scoped>
+.deviceManagement {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+
+  .deviceManagement-title {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    background: #F2F2F2;
+    padding: 10px 10px;
+
+    .deviceManagement-title-left {
+      display: flex;
+      align-items: center;
+      margin-right: 20px;
+
+      &>div {
+        width: 80px;
+      }
+    }
+  }
+
+  .deviceManagement-table {
+    flex: 1;
+    overflow: auto;
+  }
+
+  .modifyEquipment {
+    height: 378px;
+    overflow: auto;
+    padding: 0 20px;
+  }
+
+  .equipmentQrCodeImg {
+    width: 400px;
+    height: 400px;
+    background: red;
+
+    img {
+      width: 100%;
+      height: 100%;
+    }
+  }
+
+  .equipmentQrCode {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    flex-wrap: wrap;
+    width: 100%;
+    height: 500px;
+  }
+}
+</style>
+
+<style lang="scss">
+.deviceManagement .el-dialog__body {
+  padding: 0;
+}
+</style>

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

@@ -351,6 +351,10 @@
             filterCategory() {
                 this.exportParam.projectId = ''
                 const id = this.exportParam.projectCategoryId
+                if(!id) {
+                    this.projectList = JSON.parse(JSON.stringify(this.newProjectList))
+                    return
+                }
                 const list = JSON.parse(JSON.stringify(this.newProjectList))
                 this.projectList = list.filter(item => item.category == id)
             },

+ 59 - 4
fhKeeper/formulahousekeeper/timesheet/src/views/workReport/list.vue

@@ -234,6 +234,11 @@
                         {{scope.row.cardHours?scope.row.cardHours.toFixed(1):'-'}}
                     </template>
                 </el-table-column>
+                <el-table-column :label="$t('other.matters')" v-if="user.companyId == 469">
+                    <template slot-scope="scope">
+                        {{scope.row.data.length == 0?'':scope.row.data[0].content}}
+                    </template>
+                </el-table-column>
                 <el-table-column prop="state" :label="$t('state.states')" sortable>
                     <template slot-scope="scope">
                         <span v-if="scope.row.state == 0" style="color:#DAA520;">
@@ -341,7 +346,7 @@
 
         
         <!-- 审核记录弹窗 -->
-        <el-dialog :title="$t('Auditrecords')" :visible.sync="recordDialogVisible" width="1000px" :before-close="handleClose">
+        <el-dialog :title="$t('Auditrecords')" :visible.sync="recordDialogVisible" width="1200px" :before-close="handleClose">
             <div style="height: 430px">
                 <el-table :data="recordLists" style="width: 100%" height="400">
                     <el-table-column prop="userName" :label="$t('other.operator')" width="120">
@@ -354,7 +359,6 @@
                             </div>
                         </template>
                     </el-table-column>
-                    
                     <el-table-column prop="indate" :label="$t('AuditTime')" width="240"></el-table-column>
                     <el-table-column prop="result" :label="$t('Reviewtheresults')" width="120" show-overflow-tooltip></el-table-column>
                     <el-table-column prop="date" :label="$t('EmployeeDate')">
@@ -404,7 +408,7 @@
                         </template>
                     </el-table-column>
                     <el-table-column prop="projectName" :label="$t('other.project')" width="200" show-overflow-tooltip></el-table-column>
-                    <el-table-column prop="date" :label="$t('operation')" width="100">
+                    <el-table-column prop="date" :label="$t('operation')" width="180">
                         <template slot-scope="scope">
                             <div>
                                 <template v-if="scope.row.membdateList.length < 2 && scope.row.flg">
@@ -412,6 +416,7 @@
                                     <el-link type="info" v-else :underline="false">{{scope.row.membdateList[0].state == 2 ? $t('state.rejected') : $t('state.undone')}}</el-link>
                                 </template>
                                 <el-button size="mini" v-if="scope.row.membdateList.length >= 2 && scope.row.flg" @click="detailsClick(scope.row, scope.$index)">{{ $t('details') }}</el-button>
+                                <el-button size="mini" v-if="scope.row.membdateList.length >= 2 && scope.row.flg" @click="batchRevokeClick(scope.row, scope.$index)">批量撤销</el-button>
                             </div>
                         </template>
                     </el-table-column>
@@ -439,6 +444,16 @@
                 <el-button type="primary" @click="clickCancel()" :loading="undoFormLoading">{{ $t('btn.determine') }}</el-button>
             </div>
         </el-dialog>
+        <!-- 审核记录批量撤销 -->
+        <el-dialog :title="$t('defaultText.pleaseEnterTheReason')"  v-if="undoBathFormDialog" :visible.sync="undoBathFormDialog" :close-on-click-modal="false" customClass="customWidth" width="500px">
+            <div>
+                <el-input type="textarea" v-model="undoBathForm.reason" rows="2" :placeholder="$t('yourdecisiontorevoke')" />
+            </div>
+            <div slot="footer" class="dialog-footer">
+                <el-button  @click="undoBathFormDialog = false" >{{ $t('btn.cancel') }}</el-button>
+                <el-button type="primary" @click="clickBathCancel()" :loading="undoBathFormLoading">{{ $t('btn.determine') }}</el-button>
+            </div>
+        </el-dialog>
         <!-- 审核记录详情列表 -->
         <el-dialog :title="$t('Auditrecords')"  v-if="detailsDialog" :visible.sync="detailsDialog" :close-on-click-modal="false" customClass="customWidth" width="500px">
             <div>
@@ -570,7 +585,12 @@
                     reviewTableIndex: 1,
                     reviewTableSize: +(localStorage.getItem("reviewTableSize") || '50'),
                 },
-                listBackup: []
+                listBackup: [],
+                undoBathFormDialog: false,
+                undoBathForm: {
+                    reason: ''
+                },
+                undoBathFormLoading: false
             };
         },
         filters: {
@@ -581,6 +601,41 @@
             }
         },
         methods: {
+            batchRevokeClick(row, index) {
+                console.log(row, index, '<===== 点击了撤销')
+                this.undoBathForm = {
+                    reason: '',
+                    reportAuditLogId: row.id
+                }
+                this.undoBathFormDialog = true
+            },
+            clickBathCancel() {
+                this.undoBathFormLoading = true
+                this.http.post('/report/batchDenyHisReport', this.undoBathForm,
+                res => {
+                    this.undoBathFormLoading = false
+                    if (res.code == "ok") {
+                        this.$message({
+                            message: this.$t('Revocationofsuccess'),
+                            type: "success"
+                        });
+                        this.undoBathFormDialog = false
+                        this.recordList()
+                    } else {
+                        this.$message({
+                            message: res.msg,
+                            type: "error"
+                        });
+                    }
+                },
+                error => {
+                    this.undoBathFormLoading = false
+                    this.$message({
+                        message: error,
+                        type: "error"
+                    });
+                });
+            },
             viewOneReport(r) {
                 this.http.post("/report/getAuditWorkflowList", {reportId:r.id},
                     res => {

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

@@ -17,10 +17,12 @@
                     <el-table-column :label="$t('other.project')" width="220">
                         <template slot-scope="scope">
                             <el-select v-model="scope.row.projectId" size="small" :placeholder="$t('defaultText.pleaseSelectSnItem')" clearable
-                                @change="changeProject(scope.row.projectId, scope.$index)"
+                                @change="changeProject(scope.row.projectId, scope.$index)" filterable
                                 :disabled="scope.row.state == 1 || scope.row.state == 0 || !scope.row.canFill">
-                                <el-option v-for="item in projectList" :key="item.id" :label="item.projectName"
+                                <el-option v-for="item in projectList" :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;padding-left: 10px">{{ item.projectName }}</span>
                                 </el-option>
                             </el-select>
                         </template>

File diff suppressed because it is too large
+ 16140 - 572
fhKeeper/formulahousekeeper/timesheet_h5/package-lock.json


+ 1 - 0
fhKeeper/formulahousekeeper/timesheet_h5/package.json

@@ -12,6 +12,7 @@
   },
   "dependencies": {
     "@babel/plugin-syntax-dynamic-import": "^7.8.3",
+    "@zxing/library": "^0.21.3",
     "babel-plugin-import": "^1.13.3",
     "core-js": "^2.6.12",
     "css-loader": "^3.6.0",

+ 259 - 0
fhKeeper/formulahousekeeper/timesheet_h5/src/components/qrcode.vue

@@ -0,0 +1,259 @@
+<template>
+	<div class="page-scan">
+		<div class="scan-box">
+			<video ref="video" id="video" class="scan-video" autoplay></video>
+			<div class="qr-scanner">
+				<div class="box">
+					<div class="line"></div>
+					<div class="angle"></div>
+				</div>
+			</div>
+			<div class="scan-tip">{{ scanTextData.tipMsg }}</div>
+		</div>
+		<!-- <div @click.stop="destroyed">重置摄像头</div> -->
+     <div class="clonsIcon" @click="cloneQrcde()"><van-icon name="clear" /></div>
+	</div>
+</template>
+
+<script>
+import { BrowserMultiFormatReader } from '@zxing/library';
+// import { $verification } from '@/api';
+export default {
+	name: 'scanCodePage',
+	data() {
+		return {
+			scanTextData: {
+				codeReader: null,
+				tipMsg: '扫描设备码',
+				// 这个,就是当前调用的摄像头的索引,为什么是6,我会在后面说的 华为手机是鸿蒙系统有8个摄像头
+				num: 5,
+				// 这个就是扫描到的摄像头的数量
+				videoLength: '',
+			},
+			hasBind: false,
+		};
+	},
+	props: {
+		show: {
+			type: Boolean,
+			default: false,
+		},
+	},
+	mounted() {
+		this.scanTextData.codeReader = new BrowserMultiFormatReader();
+		this.openScan(); // 打开摄像头
+	},
+	watch: {
+		show(val) {
+			if (!val) {
+				// 关闭摄像头
+				if (!document.getElementById('video')) {
+					alert('请授权');
+					return;
+				}
+				let thisVideo = document.getElementById('video');
+				thisVideo.srcObject.getTracks()[0].stop();
+				this.scanTextData.codeReader.reset();
+			} else {
+				if (this.scanTextData.codeReader === null) {
+					this.scanTextData.codeReader = new BrowserMultiFormatReader();
+				}
+				this.openScan();
+			}
+		},
+	},
+	methods: {
+    cloneQrcde() {
+      this.$emit('closeQrcode')
+    },
+		async openScan() {
+			this.scanTextData.codeReader
+				.getVideoInputDevices()
+				.then((videoInputDevices) => {
+					// 默认获取第一个摄像头设备id
+					let firstDeviceId = videoInputDevices[0].deviceId;
+					console.log('手机摄像头的数量', videoInputDevices.length, videoInputDevices);
+					// 获取第一个摄像头设备的名称
+					const videoInputDeviceslablestr = JSON.stringify(videoInputDevices[0].label);
+					if (videoInputDevices.length > 1) {
+						// 华为手机有6个摄像头,前三个是前置,后三个是后置,第6个摄像头最清晰
+						if (videoInputDevices.length > 5) {
+							firstDeviceId = videoInputDevices[5].deviceId;
+						} else {
+							// 判断是否后置摄像头
+							if (videoInputDeviceslablestr.indexOf('back') > -1) {
+								firstDeviceId = videoInputDevices[0].deviceId;
+							} else {
+								firstDeviceId = videoInputDevices[1].deviceId;
+							}
+						}
+					}
+					this.decodeFromInputVideoFunc(firstDeviceId);
+				})
+				.catch((err) => {
+					console.error(err);
+				});
+		},
+		// 解析 二维码
+		decodeFromInputVideoFunc(firstDeviceId) {
+			this.scanTextData.codeReader.reset();
+			this.scanTextData.codeReader.decodeFromInputVideoDeviceContinuously(
+				firstDeviceId,
+				'video',
+				(result, err) => {
+					if (result && result.text) {
+						this.handleResult(result.text);
+					}
+					if (err && !err) {
+						console.log(err);
+						this.$toast.fail(err);
+					}
+				}
+			);
+		},
+		// 解析二维码成功后 执行的回调 (逻辑处理或直接返回扫码结果)
+		async handleResult(scanResult) {
+			console.log('逻辑处理或直接返回扫码结果', scanResult);
+      this.$emit('determineQrcode', scanResult)
+		},
+	},
+	destroyed() {
+		this.scanTextData.codeReader.reset(); // 重置摄像头
+	},
+};
+</script>
+
+<style lang="less" scoped>
+.scan-box {
+	position: fixed;
+	top: 0;
+	left: 0;
+	height: 100%;
+	width: 100vw;
+	background-image: linear-gradient(
+			0deg,
+			transparent 24%,
+			rgba(32, 255, 77, 0.1) 25%,
+			rgba(32, 255, 77, 0.1) 26%,
+			transparent 27%,
+			transparent 74%,
+			rgba(32, 255, 77, 0.1) 75%,
+			rgba(32, 255, 77, 0.1) 76%,
+			transparent 77%,
+			transparent
+		),
+		linear-gradient(
+			90deg,
+			transparent 24%,
+			rgba(32, 255, 77, 0.1) 25%,
+			rgba(32, 255, 77, 0.1) 26%,
+			transparent 27%,
+			transparent 74%,
+			rgba(32, 255, 77, 0.1) 75%,
+			rgba(32, 255, 77, 0.1) 76%,
+			transparent 77%,
+			transparent
+		);
+	background-size: 3rem 3rem;
+	background-position: -1rem -1rem;
+}
+
+.scan-video {
+	height: 100vh;
+	width: 100vw;
+	object-fit: cover;
+}
+
+.qr-scanner .box {
+	width: 213px;
+	height: 213px;
+	position: absolute;
+	left: 50%;
+	top: 50%;
+	transform: translate(-50%, -50%);
+	overflow: hidden;
+	border: 0.1rem solid rgba(0, 255, 51, 0.2);
+	/* background: url('http://resource.beige.world/imgs/gongconghao.png') no-repeat center center; */
+}
+
+.qr-scanner .line {
+	height: calc(100% - 2px);
+	width: 100%;
+	background: linear-gradient(180deg, rgba(0, 255, 51, 0) 43%, #00ff33 211%);
+	border-bottom: 3px solid #00ff33;
+	transform: translateY(-100%);
+	animation: radar-beam 2s infinite alternate;
+	animation-timing-function: cubic-bezier(0.53, 0, 0.43, 0.99);
+	animation-delay: 1.4s;
+}
+
+.qr-scanner .box:after,
+.qr-scanner .box:before,
+.qr-scanner .angle:after,
+.qr-scanner .angle:before {
+	content: '';
+	display: block;
+	position: absolute;
+	width: 3vw;
+	height: 3vw;
+	border: 0.2rem solid transparent;
+}
+
+.qr-scanner .box:after,
+.qr-scanner .box:before {
+	top: 0;
+	border-top-color: #00ff33;
+}
+
+.qr-scanner .angle:after,
+.qr-scanner .angle:before {
+	bottom: 0;
+	border-bottom-color: #00ff33;
+}
+
+.qr-scanner .box:before,
+.qr-scanner .angle:before {
+	left: 0;
+	border-left-color: #00ff33;
+}
+
+.qr-scanner .box:after,
+.qr-scanner .angle:after {
+	right: 0;
+	border-right-color: #00ff33;
+}
+
+@keyframes radar-beam {
+	0% {
+		transform: translateY(-100%);
+	}
+
+	100% {
+		transform: translateY(0);
+	}
+}
+
+.scan-tip {
+	width: 100vw;
+	text-align: center;
+	margin-bottom: 5vh;
+	color: white;
+	font-size: 5vw;
+	position: absolute;
+	bottom: 50px;
+	left: 0;
+	color: #fff;
+}
+
+.page-scan {
+	overflow-y: hidden;
+}
+
+.clonsIcon {
+  position: absolute;
+  top: 20px;
+  right: 20px;
+  z-index: 999;
+  font-size: 26px;
+}
+</style>

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

@@ -259,6 +259,20 @@ const router = new Router({
             title: "成本"
         }
     },
+    {
+        path: "/useRegistration",
+        component: () => import("@/views/deviceManagement/useRegistration"),
+        meta: {
+            title: "设备使用登记"
+        }
+    },
+    {
+        path: "/usageHistory",
+        component: () => import("@/views/deviceManagement/usageHistory"),
+        meta: {
+            title: "设备使用记录"
+        }
+    },
     {
         path: "/pdf",
         meta: {

+ 25 - 0
fhKeeper/formulahousekeeper/timesheet_h5/src/views/deviceManagement/usageHistory.vue

@@ -0,0 +1,25 @@
+<template>
+  <div class="useRegistration">
+    <van-nav-bar title="设备使用记录" left-text="返回" @click-left="back" fixed left-arrow style="z-index:1000" />
+
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+
+    };
+  },
+  mounted() {
+  },
+  methods: {
+    back() {
+      history.back();
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 230 - 0
fhKeeper/formulahousekeeper/timesheet_h5/src/views/deviceManagement/useRegistration.vue

@@ -0,0 +1,230 @@
+<template>
+  <div class="useRegistration">
+    <van-nav-bar title="设备使用登记" left-text="返回" @click-left="back()" left-arrow style="z-index:1000">
+      <van-icon name="scan" slot="right" class="tabIcon" @click="scanClick()" />
+    </van-nav-bar>
+    <div class="useRegistration-content">
+      <van-cell-group>
+        <van-field v-model="equipmentForm.code" label="设备编号" placeholder="请扫码" readonly clearable :label-width="100" />
+        <van-field v-model="equipmentForm.code" label="设备名称" placeholder="请扫码" readonly clearable :label-width="100" />
+        <van-field readonly v-model="equipmentForm.projectName" clickable type="textarea" autosize :label="'选择项目'"
+          :placeholder="'请选择项目'" @click="showPickerUserddp = true" />
+        <van-field v-model="equipmentForm.time" label="开始使用时间" placeholder="请选择使用时间" @click="endDateShow = true"
+          readonly clickable></van-field>
+        <van-field v-model="durationOfUse" label="已使用时长(小时)" placeholder="请扫码" readonly clearable :label-width="120"
+          v-if="status == 1" />
+      </van-cell-group>
+      <!-- 选择时间 -->
+      <van-popup v-model="endDateShow" position="bottom">
+        <van-datetime-picker type="datetime" title="选择截止时间" v-model="currentDate2" @confirm="endDateChange"
+          @cancel="endDateShow = false; $forceUpdate();" />
+      </van-popup>
+      <!-- 选择项目 -->
+      <van-popup v-model="showPickerUserddp" position="bottom" style="height: 84%">
+        <div class="popupDiv">
+          <div class="popupSearch">
+            <van-search v-model="userName" shape="round" background="#F4F4F4" placeholder="请输入项目名称/编号" @clear="sea()"
+              @blur="sea()" @search="sea()" @input="sea()" />
+          </div>
+
+          <div class="popupCon">
+            <div class="popupTitle">近期选择项目</div>
+
+            <div v-for="(item, index) in integrationProjectList" :key="index" class="popupItem paddingDiv"
+              @click="fZr(item, index)">
+              <div class="popupItemOne" v-if="item.projectName">{{ item.projectName }}</div>
+              <p class="popupItemTwo" v-if="item.projectCode">{{ item.projectCode }}</p>
+            </div>
+
+            <div class="popupTitle marginTop">全部项目</div>
+
+            <div v-for="(item, index) in projectss" :key="item.id" class="popupItem paddingDiv"
+              @click="fZr(item, index)">
+              <div class="popupItemOne" v-if="item.projectName">{{ item.projectName }}</div>
+              <p class="popupItemTwo" v-if="item.projectCode">{{ item.projectCode }}</p>
+            </div>
+
+            <!-- <div class="popupTitle paddingDiv">全部项目</div> -->
+          </div>
+        </div>
+      </van-popup>
+    </div>
+    <div class="useRegistration-footer">
+      <van-button type="primary" size="info" round block @click="operation" :disabled="!this.equipmentForm.code">{{
+        status == 0 ? '确定开始使用' : '确定结束使用' }}</van-button>
+    </div>
+    <!-- 扫码 -->
+    <div class="qrcodeClass" v-if="showQrcode">
+      <Qrcode @closeQrcode="closeQrcode" @determineQrcode="determineQrcode" />
+    </div>
+  </div>
+</template>
+
+<script>
+import Qrcode from '../../components/qrcode.vue'
+export default {
+  components: { Qrcode },
+  data() {
+    return {
+      equipmentForm: {
+        code: '',
+        name: '',
+        projectName: '',
+        time: this.getToday()
+      },
+      endDateShow: false,
+      currentDate2: this.getToday(),
+      showPickerUserddp: false,
+      userName: '',
+      integrationProjectList: [],
+      projectss: [],
+      project: [],
+      proads: [],
+      showQrcode: false,
+      status: 0, // 0: 新增,1:编辑
+      durationOfUse: 0
+    };
+  },
+  mounted() {
+    this.reset()
+    this.getRecentlyProject()
+    this.getPeoject()
+  },
+  methods: {
+    operation() {
+      this.equipmentForm = {
+        code: '',
+        name: '',
+        projectName: '',
+        time: this.getToday()
+      },
+        this.status = 0
+      this.$toast.success('操作成功')
+    },
+    closeQrcode() {
+      this.showQrcode = false
+    },
+    scanClick() {
+      this.showQrcode = true
+    },
+    determineQrcode(val) {
+      console.log(val, '<=========== val')
+      if (val != '') {
+        const newVal = JSON.parse(val)
+        this.equipmentForm = {
+          code: newVal.equipmentNumber,
+          name: newVal.equipmentName,
+          projectName: newVal.projectName,
+          time: newVal.usageTime
+        }
+        this.durationOfUse = newVal.durationOfUse || 0
+        this.status = newVal.status || 0
+      } else {
+        this.$toast.fail('无效的二维码')
+      }
+      this.closeQrcode()
+    },
+    reset() {
+      this.equipmentForm = {
+        code: '',
+        name: '',
+        projectName: '',
+        time: this.getToday()
+      }
+    },
+    fZr(row, index) {
+      this.equipmentForm.projectName = row.projectName
+      this.showPickerUserddp = false
+    },
+    sea() {
+      if (this.userName.length > 0) {
+        let data = this.proads.filter(item => { return item.projectName.indexOf(this.userName.toUpperCase()) != '-1' });
+        let dataList = this.proads.filter(item => { return item.projectCode.indexOf(this.userName.toUpperCase()) != '-1' });
+        let dataTree = data.concat(dataList)
+        let arrList = Array.from(new Set(dataTree))
+        this.projectss = arrList
+      } else {
+        this.projectss = this.proads
+      }
+    },
+    getToday() {
+      return this.formatDate(new Date())
+    },
+    formatDate(date) {
+      const year = date.getFullYear();
+      const month = String(date.getMonth() + 1).padStart(2, '0'); // 获取月份,并确保两位数
+      const day = String(date.getDate()).padStart(2, '0'); // 获取日期,并确保两位数
+      const hours = String(date.getHours()).padStart(2, '0'); // 获取小时,并确保两位数
+      const minutes = String(date.getMinutes()).padStart(2, '0'); // 获取分钟,并确保两位数
+      const seconds = String(date.getSeconds()).padStart(2, '0'); // 获取秒钟,并确保两位数
+
+      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+    },
+    endDateChange(date) {
+      this.equipmentForm.time = this.formatDate(date)
+      this.endDateShow = false
+    },
+    back() {
+      history.back();
+    },
+    getRecentlyProject() {
+      this.$axios.post('/project/nearProject', {})
+        .then(res => {
+          if (res.code == 'ok') {
+            this.integrationProjectList = res.data;
+          }
+        }).catch(err => { this.$toast.clear(); this.cardRefLoading = false; })
+    },
+    getPeoject() {
+      this.$axios.post("/project/getProjectList", { forReport: 1 })
+        .then(res => {
+          if (res.code == "ok") {
+            for (var i in res.data) {
+              if (res.data[i].projectCode == 'null' || res.data[i].projectCode == null) {
+                res.data[i].projectCode = ' '
+              }
+            }
+            this.projectss = res.data;
+            this.project = res.data;
+            this.projectss = this.projectss.filter(p => p.status == 1 || p.status == 4);
+            this.proads = res.data
+          } else {
+            this.$toast.fail('获取失败:' + res.msg);
+          }
+        }).catch(err => { this.$toast.clear(); });
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.useRegistration {
+  width: 100%;
+  height: 100vh;
+  display: flex;
+  flex-direction: column;
+  background: #fff;
+
+  .useRegistration-content {
+    flex: 1;
+    overflow: auto;
+  }
+
+  .useRegistration-footer {
+    width: 100%;
+    padding: 20px 20px 20px 20px;
+  }
+
+  .tabIcon {
+    font-size: 20px;
+    font-weight: bold;
+  }
+
+  .qrcodeClass {
+    position: absolute;
+    width: 100%;
+    height: 100vh;
+    z-index: 9999;
+  }
+}
+</style>

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

@@ -1530,7 +1530,6 @@ export default {
                     if(aiReportData.length != 0) {
                         var arr = [];
                         var list = aiReportData;
-                        console.log('11111111')
                         for(var i in list) {
                             var subProjectName = null;
                             if (list[i].subProjectId) {

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

@@ -299,6 +299,16 @@
                         icon: 'todo-list-o'
                     });
                 }
+
+                // 设备管理
+                if(this.user.companyId == 10) {
+                    this.routers.push(
+                    {
+                        name: '设备管理',
+                        url: '/useRegistration',
+                        icon: 'label-o'
+                    });
+                }
             },
 
             // 获取企业微信参数