Răsfoiți Sursa

获取token/添加/删除设备

licailing 5 ani în urmă
părinte
comite
91a59c8986
38 a modificat fișierele cu 1597 adăugiri și 128 ștergeri
  1. 31 0
      src/main/java/com/izouma/wenlvju/domain/ExamRoom.java
  2. 33 0
      src/main/java/com/izouma/wenlvju/domain/EzvizToken.java
  3. 26 14
      src/main/java/com/izouma/wenlvju/domain/Record.java
  4. 48 0
      src/main/java/com/izouma/wenlvju/domain/RecordSpecialty.java
  5. 6 3
      src/main/java/com/izouma/wenlvju/domain/Video.java
  6. 8 1
      src/main/java/com/izouma/wenlvju/enums/RecordStatus.java
  7. 16 0
      src/main/java/com/izouma/wenlvju/repo/ExamRoomRepo.java
  8. 19 0
      src/main/java/com/izouma/wenlvju/repo/EzvizTokenRepo.java
  9. 1 0
      src/main/java/com/izouma/wenlvju/repo/RecordRepo.java
  10. 16 0
      src/main/java/com/izouma/wenlvju/repo/RecordSpecialtyRepo.java
  11. 62 0
      src/main/java/com/izouma/wenlvju/service/ExamRoomService.java
  12. 60 0
      src/main/java/com/izouma/wenlvju/service/EzvizTokenService.java
  13. 20 0
      src/main/java/com/izouma/wenlvju/service/RecordSpecialtyService.java
  14. 1 14
      src/main/java/com/izouma/wenlvju/service/sms/NjwlSmsService.java
  15. 60 0
      src/main/java/com/izouma/wenlvju/web/ExamRoomController.java
  16. 60 0
      src/main/java/com/izouma/wenlvju/web/EzvizTokenController.java
  17. 12 6
      src/main/java/com/izouma/wenlvju/web/RecordController.java
  18. 60 0
      src/main/java/com/izouma/wenlvju/web/RecordSpecialtyController.java
  19. 1 1
      src/main/java/com/izouma/wenlvju/web/RegulatoryController.java
  20. 4 1
      src/main/resources/application.yaml
  21. 1 0
      src/main/resources/genjson/ExamRoom.json
  22. 1 0
      src/main/resources/genjson/EzvizToken.json
  23. 1 0
      src/main/resources/genjson/RecordSpecialty.json
  24. 82 75
      src/main/vue/package-lock.json
  25. 2 1
      src/main/vue/package.json
  26. 45 0
      src/main/vue/src/components/EZUIKitJs.vue
  27. 56 5
      src/main/vue/src/router.js
  28. 105 0
      src/main/vue/src/views/ExamRoomEdit.vue
  29. 160 0
      src/main/vue/src/views/ExamRoomList.vue
  30. 110 0
      src/main/vue/src/views/RecordSpecialtyEdit.vue
  31. 162 0
      src/main/vue/src/views/RecordSpecialtyList.vue
  32. 1 1
      src/main/vue/src/views/Video.vue
  33. 2 2
      src/main/vue/src/views/record/RecordEdit.vue
  34. 249 0
      src/main/vue/src/views/record/RecordOrganizationList.vue
  35. 6 3
      src/main/vue/src/widgets/Video1.vue
  36. 6 1
      src/main/vue/yarn.lock
  37. 23 0
      src/test/java/com/izouma/wenlvju/service/ExamRoomServiceTest.java
  38. 41 0
      src/test/java/com/izouma/wenlvju/service/EzvizTokenServiceTest.java

+ 31 - 0
src/main/java/com/izouma/wenlvju/domain/ExamRoom.java

@@ -0,0 +1,31 @@
+package com.izouma.wenlvju.domain;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Entity;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@Entity
+@ApiModel(value = "考场")
+public class ExamRoom extends BaseEntity {
+    private Long organizationId;
+
+    private String name;
+
+    @ApiModelProperty(value = "设备序列号")
+    private String deviceSerial;
+
+    @ApiModelProperty(value = "设备验证码")
+    private String validateCode;
+
+    private String address;
+
+}

+ 33 - 0
src/main/java/com/izouma/wenlvju/domain/EzvizToken.java

@@ -0,0 +1,33 @@
+package com.izouma.wenlvju.domain;
+
+import com.alibaba.excel.annotation.ExcelIgnore;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import java.time.LocalDateTime;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@Entity
+@ApiModel(value = "萤石云token获取")
+public class EzvizToken {
+    @Id
+    @GeneratedValue(strategy = GenerationType.AUTO)
+    @ExcelIgnore
+    private Long id;
+
+    private String token;
+
+    @ApiModelProperty(value = "过期时间")
+    private LocalDateTime expiredAt;
+}

+ 26 - 14
src/main/java/com/izouma/wenlvju/domain/Record.java

@@ -46,11 +46,11 @@ public class Record extends BaseEntity {
 //    private Long organizationId;
 
     @ApiModelProperty(value = "统一社会信用代码")
-    private String creditCode;
+    private String uscc;
 
-    @ApiModelProperty(value = "考级地点")
+//    @ApiModelProperty(value = "考级地点")
 //    @Convert(converter = LongArrayConverter.class)
-    private Long examinationDistrict;
+//    private Long examinationDistrict;
 
     @ApiModelProperty(value = "考级详细地址")
     private String examinationAddress;
@@ -85,19 +85,31 @@ public class Record extends BaseEntity {
 
     private String district;
 
-    @ApiModelProperty(value = "法人姓名")
-    private String privacyPolicy;
+    @ApiModelProperty(value = "考试简章")
+    private String examinationGuide;
 
-    @ApiModelProperty(value = "证件号码")
-    private String IDNo;
+    /*
+    考后备案
+     */
+    @ApiModelProperty(value = "考试人数")
+    private int numOfCandidates;
 
+    @ApiModelProperty(value = "合格人数")
+    private int numOfQualified;
+
+//    @ApiModelProperty(value = "法人姓名")
+//    private String privacyPolicy;
+//
+//    @ApiModelProperty(value = "证件号码")
+//    private String IDNo;
+//
     @ApiModelProperty(value = "监管人员")
     private Long supervisorUserId;
-
-    @ApiModelProperty(value = "监管状态")
-    @Enumerated(EnumType.STRING)
-    private RegulatoryStatus regulatoryStatus;
-
-    @Transient
-    private String supervisorNickname;
+//
+//    @ApiModelProperty(value = "监管状态")
+//    @Enumerated(EnumType.STRING)
+//    private RegulatoryStatus regulatoryStatus;
+//
+//    @Transient
+//    private String supervisorNickname;
 }

+ 48 - 0
src/main/java/com/izouma/wenlvju/domain/RecordSpecialty.java

@@ -0,0 +1,48 @@
+package com.izouma.wenlvju.domain;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Entity;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Builder
+@Entity
+@ApiModel("备案专业")
+public class RecordSpecialty extends BaseEntity {
+    private Long recordId;
+
+//    @ApiModelProperty(value = "专业id/艺术分类id")
+//    private Long artTypeId;
+
+    private String name;
+
+    private String code;
+
+    @ApiModelProperty(value = "总级数")
+    private int level;
+
+//    private String status;
+
+    @ApiModelProperty(value = "考场数量")
+    private int numOfExam;
+
+    @ApiModelProperty(value = "考官人数")
+    private int examinerQuantity;
+
+    /*
+    考后备案
+    */
+    @ApiModelProperty(value = "考试人数")
+    private int numOfCandidates;
+
+    @ApiModelProperty(value = "合格人数")
+    private int numOfQualified;
+
+}

+ 6 - 3
src/main/java/com/izouma/wenlvju/domain/Video.java

@@ -1,6 +1,7 @@
 package com.izouma.wenlvju.domain;
 
 
+import com.izouma.wenlvju.converter.LongArrayConverter;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
@@ -14,9 +15,11 @@ import javax.persistence.Entity;
 @Builder
 @Entity
 public class Video extends BaseEntity {
-    private Long regulatoryId;
+    private Long recordId;
 
-    private Long url;
+    private Long examRoomId;
 
-    private Long name;
+//    @Column(columnDefinition = "TEXT")
+//    @Convert(converter = LongArrayConverter.class)
+//    private List<Long> artTypeId;
 }

+ 8 - 1
src/main/java/com/izouma/wenlvju/enums/RecordStatus.java

@@ -4,7 +4,14 @@ public enum RecordStatus {
     /*
     正常
      */
-    NORMAL("正常");
+    NORMAL("正常"),
+
+    /*
+    已撤销
+     */
+    WITHDRAWN("已撤销")
+
+    ;
     private final String spec;
     RecordStatus(String spec){
         this.spec = spec;

+ 16 - 0
src/main/java/com/izouma/wenlvju/repo/ExamRoomRepo.java

@@ -0,0 +1,16 @@
+package com.izouma.wenlvju.repo;
+
+import com.izouma.wenlvju.domain.ExamRoom;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+
+import javax.transaction.Transactional;
+
+public interface ExamRoomRepo extends JpaRepository<ExamRoom, Long>, JpaSpecificationExecutor<ExamRoom> {
+    @Query("update ExamRoom t set t.del = true where t.id = ?1")
+    @Modifying
+    @Transactional
+    void softDelete(Long id);
+}

+ 19 - 0
src/main/java/com/izouma/wenlvju/repo/EzvizTokenRepo.java

@@ -0,0 +1,19 @@
+package com.izouma.wenlvju.repo;
+
+import com.izouma.wenlvju.domain.EzvizToken;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+
+import javax.transaction.Transactional;
+import java.time.LocalDateTime;
+
+public interface EzvizTokenRepo extends JpaRepository<EzvizToken, Long>, JpaSpecificationExecutor<EzvizToken> {
+//    @Query("update EzvizToken t set t.del = true where t.id = ?1")
+//    @Modifying
+//    @Transactional
+//    void softDelete(Long id);
+
+    EzvizToken findFirstByExpiredAtAfter(LocalDateTime expiredAt);
+}

+ 1 - 0
src/main/java/com/izouma/wenlvju/repo/RecordRepo.java

@@ -18,4 +18,5 @@ public interface RecordRepo extends JpaRepository<Record, Long>, JpaSpecificatio
     Record findFirstByDelIsTrue();
 
     List<Record> findAllBySupervisorUserId(Long supervisorUserId);
+
 }

+ 16 - 0
src/main/java/com/izouma/wenlvju/repo/RecordSpecialtyRepo.java

@@ -0,0 +1,16 @@
+package com.izouma.wenlvju.repo;
+
+import com.izouma.wenlvju.domain.RecordSpecialty;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Modifying;
+import org.springframework.data.jpa.repository.Query;
+
+import javax.transaction.Transactional;
+
+public interface RecordSpecialtyRepo extends JpaRepository<RecordSpecialty, Long>, JpaSpecificationExecutor<RecordSpecialty> {
+    @Query("update RecordSpecialty t set t.del = true where t.id = ?1")
+    @Modifying
+    @Transactional
+    void softDelete(Long id);
+}

+ 62 - 0
src/main/java/com/izouma/wenlvju/service/ExamRoomService.java

@@ -0,0 +1,62 @@
+package com.izouma.wenlvju.service;
+
+import com.alibaba.fastjson.JSONObject;
+import com.github.kevinsawicki.http.HttpRequest;
+import com.izouma.wenlvju.domain.ExamRoom;
+import com.izouma.wenlvju.dto.PageQuery;
+import com.izouma.wenlvju.exception.BusinessException;
+import com.izouma.wenlvju.repo.ExamRoomRepo;
+import com.izouma.wenlvju.utils.JpaUtils;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.env.Environment;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+@AllArgsConstructor
+public class ExamRoomService {
+
+    private final ExamRoomRepo      examRoomRepo;
+    private final EzvizTokenService ezvizTokenService;
+
+    public Page<ExamRoom> all(PageQuery pageQuery) {
+        return examRoomRepo.findAll(JpaUtils.toSpecification(pageQuery, ExamRoom.class), JpaUtils.toPageRequest(pageQuery));
+    }
+
+    public void addDevice(ExamRoom examRoom) {
+        String uri = "https://open.ys7.com/api/lapp/device/add";
+        String accessToken = ezvizTokenService.getToken();
+        String deviceSerial = examRoom.getDeviceSerial();
+        String validateCode = examRoom.getValidateCode();
+        String send = "accessToken=" + accessToken + "&deviceSerial=" + deviceSerial + "&validateCode=" + validateCode;
+        log.info("萤石添加设备--->" + send);
+
+        String body = HttpRequest.post(uri).send(send).body();
+        log.info("萤石添加设备返回--->" + body);
+
+        JSONObject json = JSONObject.parseObject(body);
+        if (!"200".equals(json.getString("code"))){
+            throw new BusinessException(json.getString("msg"));
+        }
+    }
+
+    public void delDevice(ExamRoom examRoom) {
+        String uri = "https://open.ys7.com/api/lapp/device/delete";
+
+        String accessToken = ezvizTokenService.getToken();
+        String deviceSerial = examRoom.getDeviceSerial();
+
+        String send = "accessToken=" + accessToken + "&deviceSerial=" + deviceSerial;
+        log.info("萤石删除设备--->" + send);
+
+        String body = HttpRequest.post(uri).send(send).body();
+        log.info("萤石删除设备返回--->" + body);
+
+        JSONObject json = JSONObject.parseObject(body);
+        if (!"200".equals(json.getString("code"))){
+            throw new BusinessException(json.getString("msg"));
+        }
+    }
+}

+ 60 - 0
src/main/java/com/izouma/wenlvju/service/EzvizTokenService.java

@@ -0,0 +1,60 @@
+package com.izouma.wenlvju.service;
+
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.github.kevinsawicki.http.HttpRequest;
+import com.izouma.wenlvju.domain.EzvizToken;
+import com.izouma.wenlvju.dto.PageQuery;
+import com.izouma.wenlvju.exception.BusinessException;
+import com.izouma.wenlvju.repo.EzvizTokenRepo;
+import com.izouma.wenlvju.utils.JpaUtils;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.env.Environment;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+
+@Slf4j
+@Service
+@AllArgsConstructor
+public class EzvizTokenService {
+
+    private final EzvizTokenRepo ezvizTokenRepo;
+    private final Environment    env;
+
+    public Page<EzvizToken> all(PageQuery pageQuery) {
+        return ezvizTokenRepo.findAll(JpaUtils.toSpecification(pageQuery, EzvizToken.class), JpaUtils.toPageRequest(pageQuery));
+    }
+
+    public String getToken() {
+        LocalDateTime now = LocalDateTime.now();
+        EzvizToken ezvizToken = ezvizTokenRepo.findFirstByExpiredAtAfter(now);
+        if (ObjectUtil.isNotNull(ezvizToken)) {
+            return ezvizToken.getToken();
+        }
+        String uri = "https://open.ys7.com/api/lapp/token/get";
+        String send = "appKey=" + env.getProperty("ezviz.app_id") + "&appSecret=" + env.getProperty("ezviz.app_secret");
+        log.info("萤石获取token--->" + send);
+
+        String body = HttpRequest.post(uri).send(send).body();
+        log.info("萤石获取token返回--->" + body);
+
+        JSONObject json = JSONObject.parseObject(body);
+        if (!"200".equals(json.getString("code"))) {
+            throw new BusinessException("获取accessToken失败,请稍后再试");
+        }
+
+        JSONObject data = json.getJSONObject("data");
+        String token = data.getString("accessToken");
+        //过期时间
+        LocalDateTime expireTime = Convert.toLocalDateTime(data.getLong("expireTime"));
+        ezvizTokenRepo.save(EzvizToken.builder()
+                .token(token)
+                .expiredAt(expireTime)
+                .build());
+        return token;
+    }
+}

+ 20 - 0
src/main/java/com/izouma/wenlvju/service/RecordSpecialtyService.java

@@ -0,0 +1,20 @@
+package com.izouma.wenlvju.service;
+
+import com.izouma.wenlvju.domain.RecordSpecialty;
+import com.izouma.wenlvju.dto.PageQuery;
+import com.izouma.wenlvju.repo.RecordSpecialtyRepo;
+import com.izouma.wenlvju.utils.JpaUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Service;
+
+@Service
+@AllArgsConstructor
+public class RecordSpecialtyService {
+
+    private RecordSpecialtyRepo recordSpecialtyRepo;
+
+    public Page<RecordSpecialty> all(PageQuery pageQuery) {
+        return recordSpecialtyRepo.findAll(JpaUtils.toSpecification(pageQuery, RecordSpecialty.class), JpaUtils.toPageRequest(pageQuery));
+    }
+}

+ 1 - 14
src/main/java/com/izouma/wenlvju/service/sms/NjwlSmsService.java

@@ -95,20 +95,7 @@ public class NjwlSmsService {
 
         String responseBody = writer.toString();
         log.info(responseBody);
-//        MessageRecord.builder()
-//                .requestContent(body)
-//                .responseContent(responseBody)
-//                .build();
-//        Map<String, Object> formMap = JSON.parseObject(responseBody, LinkedHashMap.class, Feature.OrderedField);//关键的地方,转化为有序map
-//
-//        Object success = formMap.get("Code");
-//        String ok = "";
-//        if (success instanceof String) {
-//            ok = (String) success;
-//        }
-//        if (!"200".equals(ok)) {
-//            throw new BusinessException("发送失败,请稍后重试!");
-//        }
+
     }
 
 

+ 60 - 0
src/main/java/com/izouma/wenlvju/web/ExamRoomController.java

@@ -0,0 +1,60 @@
+package com.izouma.wenlvju.web;
+import com.izouma.wenlvju.domain.ExamRoom;
+import com.izouma.wenlvju.service.ExamRoomService;
+import com.izouma.wenlvju.dto.PageQuery;
+import com.izouma.wenlvju.exception.BusinessException;
+import com.izouma.wenlvju.repo.ExamRoomRepo;
+import com.izouma.wenlvju.utils.ObjUtils;
+import com.izouma.wenlvju.utils.excel.ExcelUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+@RestController
+@RequestMapping("/examRoom")
+@AllArgsConstructor
+public class ExamRoomController extends BaseController {
+    private ExamRoomService examRoomService;
+    private ExamRoomRepo examRoomRepo;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public ExamRoom save(@RequestBody ExamRoom record) {
+        if (record.getId() != null) {
+            ExamRoom orig = examRoomRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return examRoomRepo.save(orig);
+        }
+        return examRoomRepo.save(record);
+    }
+
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/all")
+    public Page<ExamRoom> all(@RequestBody PageQuery pageQuery) {
+        return examRoomService.all(pageQuery);
+    }
+
+    @GetMapping("/get/{id}")
+    public ExamRoom get(@PathVariable Long id) {
+        return examRoomRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+    }
+
+    @PostMapping("/del/{id}")
+    public void del(@PathVariable Long id) {
+        examRoomRepo.softDelete(id);
+    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<ExamRoom> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+}
+

+ 60 - 0
src/main/java/com/izouma/wenlvju/web/EzvizTokenController.java

@@ -0,0 +1,60 @@
+package com.izouma.wenlvju.web;
+import com.izouma.wenlvju.domain.EzvizToken;
+import com.izouma.wenlvju.service.EzvizTokenService;
+import com.izouma.wenlvju.dto.PageQuery;
+import com.izouma.wenlvju.exception.BusinessException;
+import com.izouma.wenlvju.repo.EzvizTokenRepo;
+import com.izouma.wenlvju.utils.ObjUtils;
+import com.izouma.wenlvju.utils.excel.ExcelUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+@RestController
+@RequestMapping("/ezvizToken")
+@AllArgsConstructor
+public class EzvizTokenController extends BaseController {
+    private final EzvizTokenService ezvizTokenService;
+    private final EzvizTokenRepo    ezvizTokenRepo;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public EzvizToken save(@RequestBody EzvizToken record) {
+        if (record.getId() != null) {
+            EzvizToken orig = ezvizTokenRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return ezvizTokenRepo.save(orig);
+        }
+        return ezvizTokenRepo.save(record);
+    }
+
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/all")
+    public Page<EzvizToken> all(@RequestBody PageQuery pageQuery) {
+        return ezvizTokenService.all(pageQuery);
+    }
+
+    @GetMapping("/get/{id}")
+    public EzvizToken get(@PathVariable Long id) {
+        return ezvizTokenRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+    }
+
+//    @PostMapping("/del/{id}")
+//    public void del(@PathVariable Long id) {
+//        ezvizTokenRepo.softDelete(id);
+//    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<EzvizToken> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+}
+

+ 12 - 6
src/main/java/com/izouma/wenlvju/web/RecordController.java

@@ -45,12 +45,18 @@ public class RecordController extends BaseController {
     }
 
 
-    //@PreAuthorize("hasRole('ADMIN')")
+    @PreAuthorize("hasAnyRole('ADMIN','DISTRICT','DISTRICT_STAFF','EXPERT')")
     @PostMapping("/all")
     public Page<Record> all(@RequestBody PageQuery pageQuery) {
         return recordService.all2(pageQuery);
     }
 
+    @PreAuthorize("hasRole('ORGANIZER')")
+    @PostMapping("/byOrganization")
+    public Page<Record> byOrganization(@RequestBody PageQuery pageQuery) {
+        return recordService.all(pageQuery);
+    }
+
     @GetMapping("/get/{id}")
     public Record get(@PathVariable Long id) {
         return recordRepo.findById(id).orElseThrow(new BusinessException("无记录"));
@@ -88,11 +94,11 @@ public class RecordController extends BaseController {
         Map<Long, String> userMap = userRepo.findAllByAuthoritiesContainsAndDelFalse(Authority.get(AuthorityName.ROLE_SUPERVISOR))
                 .stream()
                 .collect(Collectors.toMap(User::getId, User::getNickname));
-        return recordService.all(pageQuery)
-                .map(record -> {
-                    record.setSupervisorNickname(userMap.get(record.getSupervisorUserId()));
-                    return record;
-                });
+        return recordService.all(pageQuery);
+//                .map(record -> {
+//                    record.setSupervisorNickname(userMap.get(record.getSupervisorUserId()));
+//                    return record;
+//                });
     }
 
     @GetMapping("/supervisor")

+ 60 - 0
src/main/java/com/izouma/wenlvju/web/RecordSpecialtyController.java

@@ -0,0 +1,60 @@
+package com.izouma.wenlvju.web;
+import com.izouma.wenlvju.domain.RecordSpecialty;
+import com.izouma.wenlvju.service.RecordSpecialtyService;
+import com.izouma.wenlvju.dto.PageQuery;
+import com.izouma.wenlvju.exception.BusinessException;
+import com.izouma.wenlvju.repo.RecordSpecialtyRepo;
+import com.izouma.wenlvju.utils.ObjUtils;
+import com.izouma.wenlvju.utils.excel.ExcelUtils;
+import lombok.AllArgsConstructor;
+import org.springframework.data.domain.Page;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.List;
+
+@RestController
+@RequestMapping("/recordSpecialty")
+@AllArgsConstructor
+public class RecordSpecialtyController extends BaseController {
+    private RecordSpecialtyService recordSpecialtyService;
+    private RecordSpecialtyRepo recordSpecialtyRepo;
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/save")
+    public RecordSpecialty save(@RequestBody RecordSpecialty record) {
+        if (record.getId() != null) {
+            RecordSpecialty orig = recordSpecialtyRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
+            ObjUtils.merge(orig, record);
+            return recordSpecialtyRepo.save(orig);
+        }
+        return recordSpecialtyRepo.save(record);
+    }
+
+
+    //@PreAuthorize("hasRole('ADMIN')")
+    @PostMapping("/all")
+    public Page<RecordSpecialty> all(@RequestBody PageQuery pageQuery) {
+        return recordSpecialtyService.all(pageQuery);
+    }
+
+    @GetMapping("/get/{id}")
+    public RecordSpecialty get(@PathVariable Long id) {
+        return recordSpecialtyRepo.findById(id).orElseThrow(new BusinessException("无记录"));
+    }
+
+    @PostMapping("/del/{id}")
+    public void del(@PathVariable Long id) {
+        recordSpecialtyRepo.softDelete(id);
+    }
+
+    @GetMapping("/excel")
+    @ResponseBody
+    public void excel(HttpServletResponse response, PageQuery pageQuery) throws IOException {
+        List<RecordSpecialty> data = all(pageQuery).getContent();
+        ExcelUtils.export(response, data);
+    }
+}
+

+ 1 - 1
src/main/java/com/izouma/wenlvju/web/RegulatoryController.java

@@ -38,7 +38,7 @@ public class RegulatoryController extends BaseController {
         }
         if (record.getRecordId() != null) {
             Record record1 = recordRepo.findById(record.getRecordId()).orElseThrow(new BusinessException("无记录"));
-            record1.setRegulatoryStatus(RegulatoryStatus.SUBMITTED);
+//            record1.setRegulatoryStatus(RegulatoryStatus.SUBMITTED);
             recordRepo.save(record1);
         }
         record.setTime(LocalDate.now());

+ 4 - 1
src/main/resources/application.yaml

@@ -14,7 +14,7 @@ spring:
     profiles:
         active: dev
     datasource:
-        url: jdbc:mysql://mysql.izouma.com/exam?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2b8
+        url: jdbc:mysql://mysql.izouma.com/exam_test?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2b8
         username: microball
         password: 2wsx@WSX#EDC
         hikari:
@@ -86,6 +86,9 @@ njwl:
     app_id: 189610
     app_key: 3e8d47d8-e6de-4b75-aa07-02b0d6c83678
     url:  http://sms.njlyw.cn/ApiSmsSer/NjuSms/SubmitMultipleMessage
+ezviz:
+    app_id: 7851448260f84d6a83585adfff7301d8
+    app_secret: 7bd9a092d6ae0f373e9168d85d238efe
 general:
     host: http://wlj.izouma.com
 ---

+ 1 - 0
src/main/resources/genjson/ExamRoom.json

@@ -0,0 +1 @@
+{"tableName":"ExamRoom","className":"ExamRoom","remark":"考场管理","genTable":true,"genClass":true,"genList":true,"genForm":true,"genRouter":true,"javaPath":"/Users/qiufangchao/Desktop/project/wenlvju/src/main/java/com/izouma/wenlvju","viewPath":"/Users/qiufangchao/Desktop/project/wenlvju/src/main/vue/src/views","routerPath":"/Users/qiufangchao/Desktop/project/wenlvju/src/main/vue/src","resourcesPath":"/Users/qiufangchao/Desktop/project/wenlvju/src/main/resources","dataBaseType":"Mysql","fields":[{"name":"name","modelName":"name","remark":"考场名称","showInList":true,"showInForm":true,"formType":"singleLineText"},{"name":"deviceSerial","modelName":"deviceSerial","remark":"设备序列号","showInList":true,"showInForm":true,"formType":"singleLineText"},{"name":"validateCode","modelName":"validateCode","remark":"设备验证码","showInList":true,"showInForm":true,"formType":"singleLineText"},{"name":"address","modelName":"address","remark":"地址","showInList":true,"showInForm":true,"formType":"singleLineText"}],"readTable":false,"dataSourceCode":"dataSource","genJson":"","subtables":[],"update":false,"basePackage":"com.izouma.wenlvju","tablePackage":"com.izouma.wenlvju.domain.ExamRoom"}

+ 1 - 0
src/main/resources/genjson/EzvizToken.json

@@ -0,0 +1 @@
+{"tableName":"EzvizToken","className":"EzvizToken","remark":"萤石云","genTable":true,"genClass":true,"genList":false,"genForm":false,"genRouter":false,"javaPath":"/Users/qiufangchao/Desktop/project/wenlvju/src/main/java/com/izouma/wenlvju","viewPath":"/Users/qiufangchao/Desktop/project/wenlvju/src/main/vue/src/views","routerPath":"/Users/qiufangchao/Desktop/project/wenlvju/src/main/vue/src","resourcesPath":"/Users/qiufangchao/Desktop/project/wenlvju/src/main/resources","dataBaseType":"Mysql","fields":[{"name":"id","modelName":"id","remark":"id","showInList":true,"showInForm":true,"formType":"number"},{"name":"token","modelName":"token","remark":"token","showInList":true,"showInForm":true,"formType":"singleLineText"},{"name":"expiredAt","modelName":"expiredAt","remark":"过期时间","showInList":true,"showInForm":true,"formType":"datetime"}],"readTable":false,"dataSourceCode":"dataSource","genJson":"","subtables":[],"update":false,"basePackage":"com.izouma.wenlvju","tablePackage":"com.izouma.wenlvju.domain.EzvizToken"}

+ 1 - 0
src/main/resources/genjson/RecordSpecialty.json

@@ -0,0 +1 @@
+{"tableName":"RecordSpecialty","className":"RecordSpecialty","remark":"开考专业","genTable":true,"genClass":true,"genList":true,"genForm":true,"genRouter":true,"javaPath":"/Users/qiufangchao/Desktop/project/wenlvju/src/main/java/com/izouma/wenlvju","viewPath":"/Users/qiufangchao/Desktop/project/wenlvju/src/main/vue/src/views","routerPath":"/Users/qiufangchao/Desktop/project/wenlvju/src/main/vue/src","resourcesPath":"/Users/qiufangchao/Desktop/project/wenlvju/src/main/resources","dataBaseType":"Mysql","fields":[{"name":"name","modelName":"name","remark":"专业名称","showInList":true,"showInForm":true,"formType":"singleLineText"},{"name":"code","modelName":"code","remark":"专业代码","showInList":true,"showInForm":true,"formType":"singleLineText"},{"name":"level","modelName":"level","remark":"总级数","showInList":true,"showInForm":true,"formType":"singleLineText"},{"name":"numOfExam","modelName":"numOfExam","remark":"考场数量","showInList":true,"showInForm":true,"formType":"number"},{"name":"examinerQuantity","modelName":"examinerQuantity","remark":"考官人数","showInList":true,"showInForm":true,"formType":"number"},{"name":"numOfCandidates","modelName":"numOfCandidates","remark":"考试人数","showInList":true,"showInForm":true,"formType":"number"},{"name":"numOfQualified","modelName":"numOfQualified","remark":"合格人数","showInList":true,"showInForm":true,"formType":"number"}],"readTable":false,"dataSourceCode":"dataSource","genJson":"","subtables":[],"update":false,"basePackage":"com.izouma.wenlvju","tablePackage":"com.izouma.wenlvju.domain.RecordSpecialty"}

+ 82 - 75
src/main/vue/package-lock.json

@@ -2004,16 +2004,6 @@
           "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
           "dev": true
         },
-        "ansi-styles": {
-          "version": "4.3.0",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "color-convert": "^2.0.1"
-          }
-        },
         "cacache": {
           "version": "13.0.1",
           "resolved": "https://registry.npmjs.org/cacache/-/cacache-13.0.1.tgz",
@@ -2040,27 +2030,6 @@
             "unique-filename": "^1.1.1"
           }
         },
-        "chalk": {
-          "version": "4.1.1",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
-          "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "ansi-styles": "^4.1.0",
-            "supports-color": "^7.1.0"
-          }
-        },
-        "color-convert": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "color-name": "~1.1.4"
-          }
-        },
         "debug": {
           "version": "4.3.1",
           "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz",
@@ -2070,25 +2039,6 @@
             "ms": "2.1.2"
           }
         },
-        "has-flag": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-          "dev": true,
-          "optional": true
-        },
-        "loader-utils": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
-          "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "big.js": "^5.2.2",
-            "emojis-list": "^3.0.0",
-            "json5": "^2.1.2"
-          }
-        },
         "ms": {
           "version": "2.1.2",
           "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@@ -2111,16 +2061,6 @@
             "minipass": "^3.1.1"
           }
         },
-        "supports-color": {
-          "version": "7.2.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "has-flag": "^4.0.0"
-          }
-        },
         "terser-webpack-plugin": {
           "version": "2.3.8",
           "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz",
@@ -2137,18 +2077,6 @@
             "terser": "^4.6.12",
             "webpack-sources": "^1.4.3"
           }
-        },
-        "vue-loader-v16": {
-          "version": "npm:vue-loader@16.2.0",
-          "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.2.0.tgz",
-          "integrity": "sha512-TitGhqSQ61RJljMmhIGvfWzJ2zk9m1Qug049Ugml6QP3t0e95o0XJjk29roNEiPKJQBEi8Ord5hFuSuELzSp8Q==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "chalk": "^4.1.0",
-            "hash-sum": "^2.0.0",
-            "loader-utils": "^2.0.0"
-          }
         }
       }
     },
@@ -5108,9 +5036,9 @@
       }
     },
     "element-ui": {
-      "version": "2.15.1",
-      "resolved": "https://registry.npmjs.org/element-ui/-/element-ui-2.15.1.tgz",
-      "integrity": "sha512-TqlScAKGH97XndSScUDeEHIzL1x7yg7DvQdKPEOUdiDcyIz3y3FJJBlpHYaJT96FOn1xpIcUZb+I2FJeU9EcrQ==",
+      "version": "2.15.2",
+      "resolved": "https://registry.npmjs.org/element-ui/-/element-ui-2.15.2.tgz",
+      "integrity": "sha512-S66VvOt1AR698uzxAnJeajnIPI1eIzlkKXHx2F2v94PAs/8JuyRcLOUGy100mJHIIaSkRlxVOtI+As2XTB8Oew==",
       "requires": {
         "async-validator": "~1.8.1",
         "babel-helper-vue-jsx-merge-props": "^2.0.0",
@@ -5939,6 +5867,11 @@
       "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
       "dev": true
     },
+    "ezuikit-js": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/ezuikit-js/-/ezuikit-js-0.2.4.tgz",
+      "integrity": "sha512-ro/vEgUvIc+7qrGloxsdxLdgD7V9hCVAgxddy9pZzMxJKm+Gy6qNjwGVLt0TmZ8HhcVvVMZpRoxWE2538tinYA=="
+    },
     "fast-deep-equal": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -12104,6 +12037,80 @@
         }
       }
     },
+    "vue-loader-v16": {
+      "version": "npm:vue-loader@16.2.0",
+      "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.2.0.tgz",
+      "integrity": "sha512-TitGhqSQ61RJljMmhIGvfWzJ2zk9m1Qug049Ugml6QP3t0e95o0XJjk29roNEiPKJQBEi8Ord5hFuSuELzSp8Q==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "chalk": "^4.1.0",
+        "hash-sum": "^2.0.0",
+        "loader-utils": "^2.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
+        "chalk": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz",
+          "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true,
+          "optional": true
+        },
+        "loader-utils": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
+          "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "big.js": "^5.2.2",
+            "emojis-list": "^3.0.0",
+            "json5": "^2.1.2"
+          }
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
     "vue-router": {
       "version": "3.5.1",
       "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.1.tgz",

+ 2 - 1
src/main/vue/package.json

@@ -20,7 +20,8 @@
     "date-fns": "^2.14.0",
     "echarts": "^5.1.1",
     "echarts-gl": "^2.0.4",
-    "element-ui": "2.15.2",
+    "element-ui": "^2.15.2",
+    "ezuikit-js": "^0.2.4",
     "normalize.css": "^8.0.1",
     "qs": "^6.10.1",
     "resolve-url": "^0.2.1",

+ 45 - 0
src/main/vue/src/components/EZUIKitJs.vue

@@ -0,0 +1,45 @@
+<template>
+    <div class="hello-ezuikit-js">
+        <div id="video-container" style="width:510px;height:340px"></div>
+    </div>
+</template>
+
+<script>
+import EZUIKit from 'ezuikit-js';
+
+export default {
+    name: 'HelloWorld',
+    props: {
+        msg: String
+    },
+    mounted: () => {
+        console.group('mounted 组件挂载完毕状态===============》');
+        var player = new EZUIKit.EZUIKitPlayer({
+            autoplay: false,
+            id: 'video-container',
+            accessToken: 'at.bgdp2ewu1btz9gl47vco9rckdexx5p32-8oic3wp6h3-0manyiu-oc0vsrvpy',
+            url: 'ezopen://open.ys7.com/F75663984/1.live',
+            template: 'security', // simple - 极简版;standard-标准版;security - 安防版(预览回放);voice-语音版;
+            // 视频上方头部控件
+            // header: ['capturePicture', 'save', 'zoom'], // 如果templete参数不为simple,该字段将被覆盖
+            // plugin: ['talk'], // 加载插件,talk-对讲
+            // 视频下方底部控件
+            // footer: ['talk', 'broadcast', 'hd', 'fullScreen'], // 如果template参数不为simple,该字段将被覆盖
+            // audio: 0, // 是否默认开启声音 0 - 关闭 1 - 开启
+            // openSoundCallBack: data => console.log('开启声音回调', data),
+            // closeSoundCallBack: data => console.log('关闭声音回调', data),
+            // startSaveCallBack: data => console.log('开始录像回调', data),
+            // stopSaveCallBack: data => console.log('录像回调', data),
+            // capturePictureCallBack: data => console.log('截图成功回调', data),
+            // fullScreenCallBack: data => console.log('全屏回调', data),
+            // getOSDTimeCallBack: data => console.log('获取OSDTime回调', data),
+            width: 500,
+            height: 330
+        });
+        console.log('player', player);
+        // setTimeout(()=>{
+        //   player.stop(); // 方法调用示例,10秒后关闭视频
+        // },10000)
+    }
+};
+</script>

+ 56 - 5
src/main/vue/src/router.js

@@ -88,6 +88,14 @@ const router = new Router({
                         title: '用户管理'
                     }
                 },
+                {
+                    path: '/echarts',
+                    name: 'echarts',
+                    component: () => import('@/views/Echarts.vue'),
+                    meta: {
+                        title: '地图图表'
+                    }
+                },
                 {
                     path: '/video',
                     name: 'video',
@@ -169,6 +177,23 @@ const router = new Router({
                         title: '备案管理编辑'
                     }
                 },
+                {
+                    path: '/recordOrganizationList',
+                    name: 'RecordOrganizationList',
+                    component: () =>
+                        import(/* webpackChunkName: "recordList" */ '@/views/record/RecordOrganizationList.vue'),
+                    meta: {
+                        title: '备案管理'
+                    }
+                },
+                {
+                    path: '/recordList2',
+                    name: 'RecordList2',
+                    component: () => import(/* webpackChunkName: "recordList" */ '@/views/record/RecordList2.vue'),
+                    meta: {
+                        title: '监管管理'
+                    }
+                },
                 {
                     path: '/recordDistrictList2',
                     name: 'RecordDistrictList2',
@@ -479,15 +504,41 @@ const router = new Router({
                         title: '异常日志'
                     }
                 },
-                /**INSERT_LOCATION**/
                 {
-                    path: '/echarts',
-                    name: 'echarts',
-                    component: () => import('@/views/Echarts.vue'),
+                    path: '/examRoomEdit',
+                    name: 'ExamRoomEdit',
+                    component: () => import(/* webpackChunkName: "examRoomEdit" */ '@/views/ExamRoomEdit.vue'),
                     meta: {
-                        title: '地图图表'
+                        title: '考场管理编辑'
+                    }
+                },
+                {
+                    path: '/examRoomList',
+                    name: 'ExamRoomList',
+                    component: () => import(/* webpackChunkName: "examRoomList" */ '@/views/ExamRoomList.vue'),
+                    meta: {
+                        title: '考场管理'
+                    }
+                },
+                {
+                    path: '/recordSpecialtyEdit',
+                    name: 'RecordSpecialtyEdit',
+                    component: () =>
+                        import(/* webpackChunkName: "recordSpecialtyEdit" */ '@/views/RecordSpecialtyEdit.vue'),
+                    meta: {
+                        title: '开考专业编辑'
+                    }
+                },
+                {
+                    path: '/recordSpecialtyList',
+                    name: 'RecordSpecialtyList',
+                    component: () =>
+                        import(/* webpackChunkName: "recordSpecialtyList" */ '@/views/RecordSpecialtyList.vue'),
+                    meta: {
+                        title: '开考专业'
                     }
                 }
+                /**INSERT_LOCATION**/
             ]
         },
         {

+ 105 - 0
src/main/vue/src/views/ExamRoomEdit.vue

@@ -0,0 +1,105 @@
+<template>
+    <div class="edit-view">
+        <el-form
+            :model="formData"
+            :rules="rules"
+            ref="form"
+            label-width="94px"
+            label-position="right"
+            size="small"
+            style="max-width: 500px;"
+        >
+            <el-form-item prop="name" label="考场名称">
+                <el-input v-model="formData.name"></el-input>
+            </el-form-item>
+            <el-form-item prop="deviceSerial" label="设备序列号">
+                <el-input v-model="formData.deviceSerial"></el-input>
+            </el-form-item>
+            <el-form-item prop="validateCode" label="设备验证码">
+                <el-input v-model="formData.validateCode"></el-input>
+            </el-form-item>
+            <el-form-item prop="address" label="地址">
+                <el-input v-model="formData.address"></el-input>
+            </el-form-item>
+            <el-form-item>
+                <el-button @click="onSave" :loading="saving" type="primary">保存</el-button>
+                <el-button @click="onDelete" :loading="saving" type="danger" v-if="formData.id">删除 </el-button>
+                <el-button @click="$router.go(-1)">取消</el-button>
+            </el-form-item>
+        </el-form>
+    </div>
+</template>
+<script>
+import { mapState } from 'vuex';
+export default {
+    name: 'ExamRoomEdit',
+    created() {
+        if (this.$route.query.id) {
+            this.$http
+                .get('examRoom/get/' + this.$route.query.id)
+                .then(res => {
+                    this.formData = res;
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.$message.error(e.error);
+                });
+        }
+    },
+    data() {
+        return {
+            saving: false,
+            formData: {},
+            rules: {}
+        };
+    },
+    computed: {
+        ...mapState(['organization'])
+    },
+    methods: {
+        onSave() {
+            this.$refs.form.validate(valid => {
+                if (valid) {
+                    this.submit();
+                } else {
+                    return false;
+                }
+            });
+        },
+        submit() {
+            let data = { ...this.formData };
+            data.organizationId = this.organization.id;
+            this.saving = true;
+            this.$http
+                .post('/examRoom/save', data, { body: 'json' })
+                .then(res => {
+                    this.saving = false;
+                    this.$message.success('成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.saving = false;
+                    this.$message.error(e.error);
+                });
+        },
+        onDelete() {
+            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/examRoom/del/${this.formData.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        console.log(e);
+                        this.$message.error((e || {}).error || '删除失败');
+                    }
+                });
+        }
+    }
+};
+</script>
+<style lang="less" scoped></style>

+ 160 - 0
src/main/vue/src/views/ExamRoomList.vue

@@ -0,0 +1,160 @@
+<template>
+    <div class="list-view">
+        <div class="filters-container">
+            <el-input placeholder="输入关键字" v-model="search" clearable class="filter-item"></el-input>
+            <el-button @click="getData" type="primary" icon="el-icon-search" class="filter-item">搜索 </el-button>
+            <el-button @click="addRow" type="primary" icon="el-icon-plus" class="filter-item">添加 </el-button>
+            <el-button
+                @click="download"
+                type="primary"
+                icon="el-icon-download"
+                :loading="downloading"
+                class="filter-item"
+                >导出EXCEL
+            </el-button>
+        </div>
+        <el-table
+            :data="tableData"
+            row-key="id"
+            ref="table"
+            header-row-class-name="table-header-row"
+            header-cell-class-name="table-header-cell"
+            row-class-name="table-row"
+            cell-class-name="table-cell"
+            :height="tableHeight"
+        >
+            <el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
+            <el-table-column prop="id" label="ID" width="100"> </el-table-column>
+            <el-table-column prop="name" label="考场名称"> </el-table-column>
+            <el-table-column prop="deviceSerial" label="设备序列号"> </el-table-column>
+            <el-table-column prop="validateCode" label="设备验证码"> </el-table-column>
+            <el-table-column prop="address" label="地址"> </el-table-column>
+            <el-table-column label="操作" align="center" fixed="right" min-width="150">
+                <template slot-scope="{ row }">
+                    <el-button @click="editRow(row)" type="success" size="mini" plain>监控</el-button>
+                    <el-button @click="editRow(row)" type="primary" size="mini" plain>编辑</el-button>
+                    <el-button @click="deleteRow(row)" type="danger" size="mini" plain>删除</el-button>
+                </template>
+            </el-table-column>
+        </el-table>
+        <div class="pagination-wrapper">
+            <!-- <div class="multiple-mode-wrapper">
+                <el-button v-if="!multipleMode" @click="toggleMultipleMode(true)">批量编辑</el-button>
+                <el-button-group v-else>
+                    <el-button @click="operation1">批量操作1</el-button>
+                    <el-button @click="operation2">批量操作2</el-button>
+                    <el-button @click="toggleMultipleMode(false)">取消</el-button>
+                </el-button-group>
+            </div> -->
+            <el-pagination
+                background
+                @size-change="onSizeChange"
+                @current-change="onCurrentChange"
+                :current-page="page"
+                :page-sizes="[10, 20, 30, 40, 50]"
+                :page-size="pageSize"
+                layout="total, sizes, prev, pager, next, jumper"
+                :total="totalElements"
+            >
+            </el-pagination>
+        </div>
+    </div>
+</template>
+<script>
+import { mapState } from 'vuex';
+import pageableTable from '@/mixins/pageableTable';
+
+export default {
+    name: 'ExamRoomList',
+    mixins: [pageableTable],
+    data() {
+        return {
+            multipleMode: false,
+            search: '',
+            url: '/examRoom/all',
+            downloading: false
+        };
+    },
+    computed: {
+        selection() {
+            return this.$refs.table.selection.map(i => i.id);
+        }
+    },
+    methods: {
+        beforeGetData() {
+            return { search: this.search };
+        },
+        toggleMultipleMode(multipleMode) {
+            this.multipleMode = multipleMode;
+            if (!multipleMode) {
+                this.$refs.table.clearSelection();
+            }
+        },
+        addRow() {
+            this.$router.push({
+                path: '/examRoomEdit',
+                query: {
+                    ...this.$route.query
+                }
+            });
+        },
+        editRow(row) {
+            this.$router.push({
+                path: '/examRoomEdit',
+                query: {
+                    id: row.id
+                }
+            });
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get('/examRoom/excel', {
+                    responseType: 'blob',
+                    params: { size: 10000 }
+                })
+                .then(res => {
+                    console.log(res);
+                    this.downloading = false;
+                    const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
+                    const link = document.createElement('a');
+                    link.href = downloadUrl;
+                    link.setAttribute('download', res.headers['content-disposition'].split('filename=')[1]);
+                    document.body.appendChild(link);
+                    link.click();
+                    link.remove();
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.downloading = false;
+                    this.$message.error(e.error);
+                });
+        },
+        operation1() {
+            this.$notify({
+                title: '提示',
+                message: this.selection
+            });
+        },
+        operation2() {
+            this.$message('操作2');
+        },
+        deleteRow(row) {
+            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/examRoom/del/${row.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.getData();
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        this.$message.error(e.error);
+                    }
+                });
+        }
+    }
+};
+</script>
+<style lang="less" scoped></style>

+ 110 - 0
src/main/vue/src/views/RecordSpecialtyEdit.vue

@@ -0,0 +1,110 @@
+<template>
+    <div class="edit-view">
+        <el-form
+            :model="formData"
+            :rules="rules"
+            ref="form"
+            label-width="80px"
+            label-position="right"
+            size="small"
+            style="max-width: 500px;"
+        >
+            <el-form-item prop="name" label="专业名称">
+                <el-input v-model="formData.name"></el-input>
+            </el-form-item>
+            <el-form-item prop="code" label="专业代码">
+                <el-input v-model="formData.code"></el-input>
+            </el-form-item>
+            <el-form-item prop="level" label="总级数">
+                <el-input v-model="formData.level"></el-input>
+            </el-form-item>
+            <el-form-item prop="numOfExam" label="考场数量">
+                <el-input-number type="number" v-model="formData.numOfExam"></el-input-number>
+            </el-form-item>
+            <el-form-item prop="examinerQuantity" label="考官人数">
+                <el-input-number type="number" v-model="formData.examinerQuantity"></el-input-number>
+            </el-form-item>
+            <el-form-item prop="numOfCandidates" label="考试人数">
+                <el-input-number type="number" v-model="formData.numOfCandidates"></el-input-number>
+            </el-form-item>
+            <el-form-item prop="numOfQualified" label="合格人数">
+                <el-input-number type="number" v-model="formData.numOfQualified"></el-input-number>
+            </el-form-item>
+            <el-form-item>
+                <el-button @click="onSave" :loading="saving" type="primary">保存</el-button>
+                <el-button @click="onDelete" :loading="saving" type="danger" v-if="formData.id">删除 </el-button>
+                <el-button @click="$router.go(-1)">取消</el-button>
+            </el-form-item>
+        </el-form>
+    </div>
+</template>
+<script>
+export default {
+    name: 'RecordSpecialtyEdit',
+    created() {
+        if (this.$route.query.id) {
+            this.$http
+                .get('recordSpecialty/get/' + this.$route.query.id)
+                .then(res => {
+                    this.formData = res;
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.$message.error(e.error);
+                });
+        }
+    },
+    data() {
+        return {
+            saving: false,
+            formData: {},
+            rules: {}
+        };
+    },
+    methods: {
+        onSave() {
+            this.$refs.form.validate(valid => {
+                if (valid) {
+                    this.submit();
+                } else {
+                    return false;
+                }
+            });
+        },
+        submit() {
+            let data = { ...this.formData };
+
+            this.saving = true;
+            this.$http
+                .post('/recordSpecialty/save', data, { body: 'json' })
+                .then(res => {
+                    this.saving = false;
+                    this.$message.success('成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.saving = false;
+                    this.$message.error(e.error);
+                });
+        },
+        onDelete() {
+            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/recordSpecialty/del/${this.formData.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.$router.go(-1);
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        console.log(e);
+                        this.$message.error((e || {}).error || '删除失败');
+                    }
+                });
+        }
+    }
+};
+</script>
+<style lang="less" scoped></style>

+ 162 - 0
src/main/vue/src/views/RecordSpecialtyList.vue

@@ -0,0 +1,162 @@
+<template>
+    <div class="list-view">
+        <div class="filters-container">
+            <el-input placeholder="输入关键字" v-model="search" clearable class="filter-item"></el-input>
+            <el-button @click="getData" type="primary" icon="el-icon-search" class="filter-item">搜索 </el-button>
+            <el-button @click="addRow" type="primary" icon="el-icon-plus" class="filter-item">添加 </el-button>
+            <el-button
+                @click="download"
+                type="primary"
+                icon="el-icon-download"
+                :loading="downloading"
+                class="filter-item"
+                >导出EXCEL
+            </el-button>
+        </div>
+        <el-table
+            :data="tableData"
+            row-key="id"
+            ref="table"
+            header-row-class-name="table-header-row"
+            header-cell-class-name="table-header-cell"
+            row-class-name="table-row"
+            cell-class-name="table-cell"
+            :height="tableHeight"
+        >
+            <el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
+            <el-table-column prop="id" label="ID" width="100"> </el-table-column>
+            <el-table-column prop="name" label="专业名称"> </el-table-column>
+            <el-table-column prop="code" label="专业代码"> </el-table-column>
+            <el-table-column prop="level" label="总级数"> </el-table-column>
+            <el-table-column prop="numOfExam" label="考场数量"> </el-table-column>
+            <el-table-column prop="examinerQuantity" label="考官人数"> </el-table-column>
+            <el-table-column prop="numOfCandidates" label="考试人数"> </el-table-column>
+            <el-table-column prop="numOfQualified" label="合格人数"> </el-table-column>
+            <el-table-column label="操作" align="center" fixed="right" min-width="150">
+                <template slot-scope="{ row }">
+                    <el-button @click="editRow(row)" type="primary" size="mini" plain>编辑</el-button>
+                    <el-button @click="deleteRow(row)" type="danger" size="mini" plain>删除</el-button>
+                </template>
+            </el-table-column>
+        </el-table>
+        <div class="pagination-wrapper">
+            <!-- <div class="multiple-mode-wrapper">
+                <el-button v-if="!multipleMode" @click="toggleMultipleMode(true)">批量编辑</el-button>
+                <el-button-group v-else>
+                    <el-button @click="operation1">批量操作1</el-button>
+                    <el-button @click="operation2">批量操作2</el-button>
+                    <el-button @click="toggleMultipleMode(false)">取消</el-button>
+                </el-button-group>
+            </div> -->
+            <el-pagination
+                background
+                @size-change="onSizeChange"
+                @current-change="onCurrentChange"
+                :current-page="page"
+                :page-sizes="[10, 20, 30, 40, 50]"
+                :page-size="pageSize"
+                layout="total, sizes, prev, pager, next, jumper"
+                :total="totalElements"
+            >
+            </el-pagination>
+        </div>
+    </div>
+</template>
+<script>
+import { mapState } from 'vuex';
+import pageableTable from '@/mixins/pageableTable';
+
+export default {
+    name: 'RecordSpecialtyList',
+    mixins: [pageableTable],
+    data() {
+        return {
+            multipleMode: false,
+            search: '',
+            url: '/recordSpecialty/all',
+            downloading: false
+        };
+    },
+    computed: {
+        selection() {
+            return this.$refs.table.selection.map(i => i.id);
+        }
+    },
+    methods: {
+        beforeGetData() {
+            return { search: this.search };
+        },
+        toggleMultipleMode(multipleMode) {
+            this.multipleMode = multipleMode;
+            if (!multipleMode) {
+                this.$refs.table.clearSelection();
+            }
+        },
+        addRow() {
+            this.$router.push({
+                path: '/recordSpecialtyEdit',
+                query: {
+                    ...this.$route.query
+                }
+            });
+        },
+        editRow(row) {
+            this.$router.push({
+                path: '/recordSpecialtyEdit',
+                query: {
+                    id: row.id
+                }
+            });
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get('/recordSpecialty/excel', {
+                    responseType: 'blob',
+                    params: { size: 10000 }
+                })
+                .then(res => {
+                    console.log(res);
+                    this.downloading = false;
+                    const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
+                    const link = document.createElement('a');
+                    link.href = downloadUrl;
+                    link.setAttribute('download', res.headers['content-disposition'].split('filename=')[1]);
+                    document.body.appendChild(link);
+                    link.click();
+                    link.remove();
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.downloading = false;
+                    this.$message.error(e.error);
+                });
+        },
+        operation1() {
+            this.$notify({
+                title: '提示',
+                message: this.selection
+            });
+        },
+        operation2() {
+            this.$message('操作2');
+        },
+        deleteRow(row) {
+            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/recordSpecialty/del/${row.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.getData();
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        this.$message.error(e.error);
+                    }
+                });
+        }
+    }
+};
+</script>
+<style lang="less" scoped></style>

+ 1 - 1
src/main/vue/src/views/Video.vue

@@ -44,7 +44,7 @@ export default {
         return {
             layout: [
                 { x: 0, y: 0, w: 4, h: 17.5, i: '0', name: 'Video3' },
-                { x: 0, y: 0, w: 4, h: 16.5, i: '2', name: 'Video1' },
+                { x: 0, y: 0, w: 4, h: 20, i: '2', name: 'Video1' },
                 { x: 4, y: 4, w: 4, h: 16.5, i: '3', name: 'Video4' },
                 { x: 8, y: 4, w: 3, h: 24, i: '1', name: 'Video2' },
                 { x: 4, y: 12.5, w: 4, h: 17.5, i: '4', name: 'Video5' }

+ 2 - 2
src/main/vue/src/views/record/RecordEdit.vue

@@ -48,8 +48,8 @@
             <el-form-item prop="organizer" label="承办单位名称">
                 <el-input v-model="formData.organizer"></el-input>
             </el-form-item>
-            <el-form-item prop="creditCode" label="统一社会信用代码">
-                <el-input v-model="formData.creditCode"></el-input>
+            <el-form-item prop="uscc" label="统一社会信用代码">
+                <el-input v-model="formData.uscc"></el-input>
             </el-form-item>
             <el-form-item prop="examinationDistrict" label="考级地点">
                 <el-select v-model="formData.district">

+ 249 - 0
src/main/vue/src/views/record/RecordOrganizationList.vue

@@ -0,0 +1,249 @@
+<template>
+    <div class="list-view">
+        <div class="filters-container">
+            <div style="padding-left: 20px">
+                <el-button
+                    @click="download"
+                    type="primary"
+                    icon="el-icon-download"
+                    :loading="downloading"
+                    class="filter-item"
+                    :disabled="totalElements <= 0"
+                    >导出EXCEL
+                </el-button>
+            </div>
+        </div>
+        <el-table
+            :data="tableData"
+            row-key="id"
+            ref="table"
+            header-row-class-name="table-header-row"
+            header-cell-class-name="table-header-cell"
+            row-class-name="table-row"
+            cell-class-name="table-cell"
+            :height="tableHeight"
+        >
+            <el-table-column v-if="multipleMode" align="center" type="selection" width="50"> </el-table-column>
+            <!-- <el-table-column prop="id" label="ID" width="100"> </el-table-column> -->
+            <el-table-column prop="examinationName" label="考级活动名称"> </el-table-column>
+            <el-table-column prop="examinationStartTime" label="考级活动时间" show-overflow-tooltip>
+                <template slot-scope="{ row }">
+                    <span>{{ row.examinationStartTime }} 至 {{ row.examinationEndTime }}</span>
+                </template>
+            </el-table-column>
+            <el-table-column prop="examinationAgency" label="考级机构名称"> </el-table-column>
+            <el-table-column prop="organizer" label="承办单位名称"> </el-table-column>
+            <el-table-column prop="district" label="考级地点"> </el-table-column>
+            <el-table-column prop="recordTime" label="备案时间"> </el-table-column>
+            <el-table-column prop="status" label="状态" :formatter="statusFormatter" width="50"> </el-table-column>
+            <el-table-column label="操作" align="center" fixed="right" min-width="80">
+                <template slot-scope="{ row }">
+                    <el-button @click="editRow(row)" type="primary" size="mini" plain>查看</el-button>
+                    <!-- <el-button @click="deleteRow(row)" type="danger" size="mini" plain>删除</el-button> -->
+                </template>
+            </el-table-column>
+        </el-table>
+        <div class="pagination-wrapper">
+            <!-- <div class="multiple-mode-wrapper">
+                <el-button v-if="!multipleMode" @click="toggleMultipleMode(true)">批量编辑</el-button>
+                <el-button-group v-else>
+                    <el-button @click="operation1">批量操作1</el-button>
+                    <el-button @click="operation2">批量操作2</el-button>
+                    <el-button @click="toggleMultipleMode(false)">取消</el-button>
+                </el-button-group>
+            </div> -->
+            <el-pagination
+                background
+                @size-change="onSizeChange"
+                @current-change="onCurrentChange"
+                :current-page="page"
+                :page-sizes="[10, 20, 30, 40, 50]"
+                :page-size="pageSize"
+                layout="total, sizes, prev, pager, next, jumper"
+                :total="totalElements"
+            >
+            </el-pagination>
+        </div>
+    </div>
+</template>
+<script>
+import pageableTable from '@/mixins/pageableTable';
+import { mapState } from 'vuex';
+
+export default {
+    name: 'RecordList',
+    mixins: [pageableTable],
+    created() {
+        this.$http
+            .get('/district/NJ')
+            .then(res => {
+                this.district = res;
+            })
+            .catch(e => {
+                console.log(e);
+                this.$message.error(e.error);
+            });
+    },
+    data() {
+        return {
+            multipleMode: false,
+            search: '',
+            url: '/record/byOrganization',
+            downloading: false,
+            categoryOptions: [{ label: '承办单位', value: 'ORGANIZER' }],
+            statusOptions: [{ label: '正常', value: 'NORMAL' }],
+            district: [],
+            districtId: '',
+            province: '江苏省',
+            city: '南京市',
+            status: '',
+            recordDateRange: '',
+            agency: '',
+            organizer: '',
+            dateRange: ''
+        };
+    },
+    computed: {
+        selection() {
+            return this.$refs.table.selection.map(i => i.id);
+        },
+        ...mapState(['organization'])
+    },
+    methods: {
+        categoryFormatter(row, column, cellValue, index) {
+            let selectedOption = this.categoryOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        statusFormatter(row, column, cellValue, index) {
+            let selectedOption = this.statusOptions.find(i => i.value === cellValue);
+            if (selectedOption) {
+                return selectedOption.label;
+            }
+            return '';
+        },
+        beforeGetData() {
+            return {
+                // search: this.search,
+                sort: 'recordTime,desc',
+                query: {
+                    uscc: this.organization.uscc,
+                    del: false
+                }
+            };
+        },
+        toggleMultipleMode(multipleMode) {
+            this.multipleMode = multipleMode;
+            if (!multipleMode) {
+                this.$refs.table.clearSelection();
+            }
+        },
+        addRow() {
+            this.$router.push({
+                path: '/recordEdit',
+                query: {
+                    ...this.$route.query
+                }
+            });
+        },
+        editRow(row) {
+            this.$router.push({
+                path: '/recordEdit',
+                query: {
+                    id: row.id
+                }
+            });
+        },
+        download() {
+            this.downloading = true;
+            this.$axios
+                .get('/record/excel', {
+                    responseType: 'blob',
+                    params: {
+                        size: 10000,
+                        query: {
+                            del: false
+                        }
+                    }
+                })
+                .then(res => {
+                    console.log(res);
+                    this.downloading = false;
+                    const downloadUrl = window.URL.createObjectURL(new Blob([res.data]));
+                    const link = document.createElement('a');
+                    link.href = downloadUrl;
+                    link.setAttribute('download', res.headers['content-disposition'].split('filename=')[1]);
+                    document.body.appendChild(link);
+                    link.click();
+                    link.remove();
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.downloading = false;
+                    this.$message.error(e.error);
+                });
+        },
+        operation1() {
+            this.$notify({
+                title: '提示',
+                message: this.selection
+            });
+        },
+        operation2() {
+            this.$message('操作2');
+        },
+        deleteRow(row) {
+            this.$alert('删除将无法恢复,确认要删除么?', '警告', { type: 'error' })
+                .then(() => {
+                    return this.$http.post(`/record/del/${row.id}`);
+                })
+                .then(() => {
+                    this.$message.success('删除成功');
+                    this.getData();
+                })
+                .catch(e => {
+                    if (e !== 'cancel') {
+                        this.$message.error(e.error);
+                    }
+                });
+        },
+        distribute() {
+            this.$alert('已分发到各区县!', '分发', {
+                confirmButtonText: '确定'
+            });
+        },
+        update() {
+            this.$http
+                .get('/record/update')
+                .then(res => {
+                    this.getData();
+                })
+                .catch(e => {
+                    console.log(e);
+                    this.$message.error(e.error);
+                });
+        }
+    }
+};
+</script>
+<style lang="less" scoped>
+.span-size {
+    font-size: 14px;
+    color: #565b66;
+    line-height: 15px;
+    padding: 0 10px 0 10px;
+}
+.span-width {
+    width: 330px;
+    display: inline-block;
+    text-align: right;
+}
+.span-width2 {
+    width: 500px;
+    // float: left;
+    text-align: right;
+    display: inline-block;
+}
+</style>

+ 6 - 3
src/main/vue/src/widgets/Video1.vue

@@ -7,7 +7,7 @@
             <div class="num">4,258</div>
         </div> -->
         <div>
-            <video
+            <!-- <video
                 :src="videoUrl"
                 controlsList="nodownload noremote footbar"
                 controls="controls"
@@ -18,12 +18,14 @@
                 loop
             >
                 您的浏览器不支持 video 标签。
-            </video>
+            </video> -->
+            <EZUIKitJs />
         </div>
     </widget-card>
 </template>
 <script>
 import WidgetCard from './WidgetCard';
+import EZUIKitJs from '../components/EZUIKitJs.vue';
 
 export default {
     data() {
@@ -35,7 +37,8 @@ export default {
         };
     },
     components: {
-        WidgetCard
+        WidgetCard,
+        EZUIKitJs
     }
 };
 </script>

+ 6 - 1
src/main/vue/yarn.lock

@@ -3257,7 +3257,7 @@ element-resize-detector@^1.1.15:
   dependencies:
     batch-processor "1.0.0"
 
-element-ui@2.15.2:
+element-ui@^2.15.2:
   version "2.15.2"
   resolved "https://registry.yarnpkg.com/element-ui/-/element-ui-2.15.2.tgz#1b4c4af582a37061de7d8146047a34f0099b52cc"
   integrity sha512-S66VvOt1AR698uzxAnJeajnIPI1eIzlkKXHx2F2v94PAs/8JuyRcLOUGy100mJHIIaSkRlxVOtI+As2XTB8Oew==
@@ -3730,6 +3730,11 @@ extsprintf@^1.2.0:
   resolved "https://registry.npm.taobao.org/extsprintf/download/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
   integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
 
+ezuikit-js@^0.2.4:
+  version "0.2.4"
+  resolved "https://registry.yarnpkg.com/ezuikit-js/-/ezuikit-js-0.2.4.tgz#d70fdee515d89418a1ebe5bafc0747a7f7ab714e"
+  integrity sha512-ro/vEgUvIc+7qrGloxsdxLdgD7V9hCVAgxddy9pZzMxJKm+Gy6qNjwGVLt0TmZ8HhcVvVMZpRoxWE2538tinYA==
+
 fast-deep-equal@^3.1.1:
   version "3.1.3"
   resolved "https://registry.npm.taobao.org/fast-deep-equal/download/fast-deep-equal-3.1.3.tgz?cache=0&sync_timestamp=1591599604098&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ffast-deep-equal%2Fdownload%2Ffast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"

+ 23 - 0
src/test/java/com/izouma/wenlvju/service/ExamRoomServiceTest.java

@@ -0,0 +1,23 @@
+package com.izouma.wenlvju.service;
+
+import com.izouma.wenlvju.ApplicationTests;
+import com.izouma.wenlvju.domain.ExamRoom;
+import com.izouma.wenlvju.repo.ExamRoomRepo;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+public class ExamRoomServiceTest extends ApplicationTests {
+
+    @Autowired
+    private ExamRoomService examRoomService;
+    @Autowired
+    private ExamRoomRepo    examRoomRepo;
+
+
+    @Test
+    public void addDevice() {
+        ExamRoom examRoom = examRoomRepo.findById(874L).orElse(null);
+        examRoomService.addDevice(examRoom);
+
+    }
+}

+ 41 - 0
src/test/java/com/izouma/wenlvju/service/EzvizTokenServiceTest.java

@@ -0,0 +1,41 @@
+package com.izouma.wenlvju.service;
+
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.date.DateUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.izouma.wenlvju.ApplicationTests;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+public class EzvizTokenServiceTest extends ApplicationTests {
+
+    @Autowired
+    private EzvizTokenService ezvizTokenService;
+
+    @Test
+    public void getToken() {
+        System.out.println(ezvizTokenService.getToken());
+    }
+
+    @Test
+    public void test() {
+        System.out.println(DateUtil.date(1624525723010L));
+        System.out.println(Convert.toLocalDateTime(1624525723010L));
+    }
+
+    @Test
+    public void test1() {
+        String str = "{\"msg\":\"操作成功!\",\"code\":\"200\",\"data\":{\"accessToken\":\"at.4jrsvi0u34xzz9rs8mz3uj04bzn489q6-230iyqmr9e-0yn79qd-7ezu9nfog\",\"expireTime\":1624525723010}}";
+
+        JSONObject json = JSONObject.parseObject(str);
+        String code = json.getString("code");
+        System.out.println(code);
+        JSONObject data = json.getJSONObject("data");
+        System.out.println(data.getString("accessToken"));
+        System.out.println(data.getLong("expireTime"));
+    }
+}