Przeglądaj źródła

登录绑定解绑及日志添加、查询

S0025136190 1 rok temu
rodzic
commit
dc079be416
28 zmienionych plików z 739 dodań i 147 usunięć
  1. 23 0
      Backend/sample/src/main/java/com/dji/sample/common/util/UserRequest.java
  2. 56 0
      Backend/sample/src/main/java/com/dji/sample/manage/controller/DeviceOprLogsController.java
  3. 2 2
      Backend/sample/src/main/java/com/dji/sample/manage/controller/LoginController.java
  4. 12 0
      Backend/sample/src/main/java/com/dji/sample/manage/dao/IDeviceOprLogsMapper.java
  5. 6 1
      Backend/sample/src/main/java/com/dji/sample/manage/model/dto/DeviceOprLogsDTO.java
  6. 2 0
      Backend/sample/src/main/java/com/dji/sample/manage/model/dto/UserLoginDTO.java
  7. 9 0
      Backend/sample/src/main/java/com/dji/sample/manage/model/entity/DeviceOprLogsEntity.java
  8. 45 0
      Backend/sample/src/main/java/com/dji/sample/manage/model/enums/LogOprTypeEnum.java
  9. 32 0
      Backend/sample/src/main/java/com/dji/sample/manage/model/param/DeviceOprLogsQueryParam.java
  10. 44 0
      Backend/sample/src/main/java/com/dji/sample/manage/service/IDeviceOprLogsService.java
  11. 13 0
      Backend/sample/src/main/java/com/dji/sample/manage/service/IDeviceService.java
  12. 1 1
      Backend/sample/src/main/java/com/dji/sample/manage/service/IUserService.java
  13. 143 0
      Backend/sample/src/main/java/com/dji/sample/manage/service/impl/DeviceOprLogsServiceImpl.java
  14. 52 21
      Backend/sample/src/main/java/com/dji/sample/manage/service/impl/DeviceServiceImpl.java
  15. 19 1
      Backend/sample/src/main/java/com/dji/sample/manage/service/impl/UserServiceImpl.java
  16. 1 1
      Backend/sample/src/main/resources/application-prd.yml
  17. 1 1
      Backend/sample/src/main/resources/application.yml
  18. 22 3
      Web/src/api/custom/index.ts
  19. 2 0
      Web/src/api/manage.ts
  20. 8 8
      Web/src/components/MediaPanel.vue
  21. 7 7
      Web/src/components/common/nav.vue
  22. 32 70
      Web/src/components/devices/changeRecord/index.vue
  23. 161 3
      Web/src/components/devices/feedbackRecord/index.vue
  24. 25 14
      Web/src/pages/page-pilot/pilot-home.vue
  25. 9 2
      Web/src/pages/page-pilot/pilot-index.vue
  26. 1 1
      Web/src/pages/page-web/projects/devices.vue
  27. 4 4
      Web/src/pages/page-web/projects/media/index.vue
  28. 7 7
      Web/src/pages/page-web/projects/members.vue

+ 23 - 0
Backend/sample/src/main/java/com/dji/sample/common/util/UserRequest.java

@@ -0,0 +1,23 @@
+package com.dji.sample.common.util;
+
+import com.dji.sample.common.model.CustomClaim;
+import com.dji.sample.component.AuthInterceptor;
+import org.springframework.http.server.ServletServerHttpRequest;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+
+import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM;
+/**
+ * @author hqjiang
+ * @version 1.0
+ * @date 2024/6/26
+ */
+public class UserRequest {
+    public static CustomClaim getCurrentUser() {
+        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+        CustomClaim customClaim = (CustomClaim)request.getAttribute(TOKEN_CLAIM);
+        return customClaim;
+    }
+}

+ 56 - 0
Backend/sample/src/main/java/com/dji/sample/manage/controller/DeviceOprLogsController.java

@@ -0,0 +1,56 @@
+package com.dji.sample.manage.controller;
+
+import com.dji.sample.manage.model.dto.DeviceDTO;
+import com.dji.sample.manage.model.dto.DeviceOprLogsDTO;
+import com.dji.sample.manage.model.param.DeviceOprLogsQueryParam;
+import com.dji.sample.manage.service.IDeviceOprLogsService;
+import com.dji.sample.manage.service.IDeviceService;
+import com.dji.sdk.common.HttpResultResponse;
+import com.dji.sdk.common.PaginationData;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Optional;
+
+/**
+ * @author hqjiang
+ * @version 1.0
+ * @date 2024/6/26
+ */
+@RestController
+@Slf4j
+@RequestMapping("${url.manage.prefix}${url.manage.version}/oprlogs")
+public class DeviceOprLogsController {
+
+    @Autowired
+    private IDeviceOprLogsService deviceOprLogsService;
+
+    @Autowired
+    private IDeviceService deviceService;
+
+    /**
+     * 查询工作空间下设备操作日志
+     * @param workspaceId
+     * @param param
+     * @return
+     */
+    @GetMapping("/{workspace_id}/devices/logs")
+    public HttpResultResponse getDeviceOprLogs(DeviceOprLogsQueryParam param, @PathVariable("workspace_id") String workspaceId) {
+        PaginationData<DeviceOprLogsDTO> data = deviceOprLogsService.getDeviceOprLogs(workspaceId,param);
+        //添加子设备
+        if(data.getList() != null && !data.getList().isEmpty()) {
+            for(DeviceOprLogsDTO logsDTO : data.getList()) {
+                Optional<DeviceDTO> deviceOpt = deviceService.getDeviceBySn(logsDTO.getDeviceSn());
+                if(deviceOpt.isPresent()) {
+                    logsDTO.setChildren(deviceService.setChild(deviceOpt.get()));
+                }
+            }
+        }
+        return HttpResultResponse.success(data);
+    }
+
+}

+ 2 - 2
Backend/sample/src/main/java/com/dji/sample/manage/controller/LoginController.java

@@ -1,6 +1,7 @@
 package com.dji.sample.manage.controller;
 package com.dji.sample.manage.controller;
 
 
 import com.dji.sample.common.error.CommonErrorEnum;
 import com.dji.sample.common.error.CommonErrorEnum;
+import com.dji.sample.component.AuthInterceptor;
 import com.dji.sample.manage.model.dto.UserDTO;
 import com.dji.sample.manage.model.dto.UserDTO;
 import com.dji.sample.manage.model.dto.UserLoginDTO;
 import com.dji.sample.manage.model.dto.UserLoginDTO;
 import com.dji.sample.manage.service.IUserService;
 import com.dji.sample.manage.service.IUserService;
@@ -27,10 +28,9 @@ public class LoginController {
 
 
     @PostMapping("/login")
     @PostMapping("/login")
     public HttpResultResponse login(@RequestBody UserLoginDTO loginDTO) {
     public HttpResultResponse login(@RequestBody UserLoginDTO loginDTO) {
-
         String username = loginDTO.getUsername();
         String username = loginDTO.getUsername();
         String password = loginDTO.getPassword();
         String password = loginDTO.getPassword();
-        return userService.userLogin(username, password, loginDTO.getFlag());
+        return userService.userLogin(username, password, loginDTO.getFlag(),loginDTO.getGateway_sn());
     }
     }
 
 
     @PostMapping("/token/refresh")
     @PostMapping("/token/refresh")

+ 12 - 0
Backend/sample/src/main/java/com/dji/sample/manage/dao/IDeviceOprLogsMapper.java

@@ -0,0 +1,12 @@
+package com.dji.sample.manage.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.dji.sample.manage.model.entity.DeviceOprLogsEntity;
+
+/**
+ * @author hqjiang
+ * @version 1.0
+ * @date 2024/6/25
+ */
+public interface IDeviceOprLogsMapper extends BaseMapper<DeviceOprLogsEntity> {
+}

+ 6 - 1
Backend/sample/src/main/java/com/dji/sample/manage/model/dto/DeviceOprLogsDTO.java

@@ -24,12 +24,15 @@ import java.util.List;
 @NoArgsConstructor
 @NoArgsConstructor
 public class DeviceOprLogsDTO {
 public class DeviceOprLogsDTO {
 
 
+
     private String username;
     private String username;
 
 
     private String deviceSn;
     private String deviceSn;
 
 
     private String deviceName;
     private String deviceName;
 
 
+    private String nickName;
+
     private String logInfo;
     private String logInfo;
 
 
     private String workspaceId;
     private String workspaceId;
@@ -38,9 +41,11 @@ public class DeviceOprLogsDTO {
 
 
     private Integer type;
     private Integer type;
 
 
+    private String typeName;
+
     private String bindCode;
     private String bindCode;
 
 
-    private Long createTime;
+    private LocalDateTime createTime;
 
 
     private DeviceDTO children;
     private DeviceDTO children;
 
 

+ 2 - 0
Backend/sample/src/main/java/com/dji/sample/manage/model/dto/UserLoginDTO.java

@@ -18,4 +18,6 @@ public class UserLoginDTO {
 
 
     @NonNull
     @NonNull
     private Integer flag;
     private Integer flag;
+
+    private String gateway_sn;
 }
 }

+ 9 - 0
Backend/sample/src/main/java/com/dji/sample/manage/model/entity/DeviceOprLogsEntity.java

@@ -31,12 +31,21 @@ public class DeviceOprLogsEntity implements Serializable {
     @TableField("device_sn")
     @TableField("device_sn")
     private String deviceSn;
     private String deviceSn;
 
 
+    @TableField("device_name")
+    private String deviceName;
+
+    @TableField("nickname")
+    private String nickname;
+
     @TableField("log_info")
     @TableField("log_info")
     private String logInfo;
     private String logInfo;
 
 
     @TableField("workspace_id")
     @TableField("workspace_id")
     private String workspaceId;
     private String workspaceId;
 
 
+    @TableField("workspace_name")
+    private String workspaceName;
+
     @TableField("type")
     @TableField("type")
     private Integer type;
     private Integer type;
 
 

+ 45 - 0
Backend/sample/src/main/java/com/dji/sample/manage/model/enums/LogOprTypeEnum.java

@@ -0,0 +1,45 @@
+package com.dji.sample.manage.model.enums;
+
+/**
+ * @author hqjiang
+ * @version 1.0
+ * @date 2024/6/25
+ */
+public enum LogOprTypeEnum {
+
+    BIND(1, "绑定"),
+
+    UNBIND(2, "解绑"),
+
+    DEL(3, "删除设备");
+
+    private int val;
+
+    private String desc;
+
+    LogOprTypeEnum(int val, String desc) {
+        this.val = val;
+        this.desc = desc;
+    }
+
+    public int getVal() {
+        return this.val;
+    }
+
+    public String getDesc() {
+        return this.desc;
+    }
+
+    public static LogOprTypeEnum find(int val) {
+        if (val == BIND.val) {
+            return BIND;
+        }
+        if (val == UNBIND.val) {
+            return UNBIND;
+        }
+        if (val == DEL.val) {
+            return DEL;
+        }
+        return null;
+    }
+}

+ 32 - 0
Backend/sample/src/main/java/com/dji/sample/manage/model/param/DeviceOprLogsQueryParam.java

@@ -0,0 +1,32 @@
+package com.dji.sample.manage.model.param;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * @author hqjiang
+ * @version 1.0
+ * @date 2024/6/25
+ */
+@Data
+public class DeviceOprLogsQueryParam {
+
+    private Long page;
+
+    @JsonProperty("page_size")
+    private Long pageSize;
+
+    @JsonProperty("begin_time")
+    private Long beginTime;
+
+    @JsonProperty("end_time")
+    private Long endTime;
+
+    @JsonProperty("device_name")
+    private String deviceName;
+
+    @JsonProperty("logs_information")
+    private String logsInformation;
+
+
+}

+ 44 - 0
Backend/sample/src/main/java/com/dji/sample/manage/service/IDeviceOprLogsService.java

@@ -0,0 +1,44 @@
+package com.dji.sample.manage.service;
+
+import com.dji.sample.common.model.CustomClaim;
+import com.dji.sample.manage.model.dto.DeviceDTO;
+import com.dji.sample.manage.model.dto.DeviceLogsDTO;
+import com.dji.sample.manage.model.dto.DeviceOprLogsDTO;
+import com.dji.sample.manage.model.enums.LogOprTypeEnum;
+import com.dji.sample.manage.model.param.DeviceLogsCreateParam;
+import com.dji.sample.manage.model.param.DeviceLogsQueryParam;
+import com.dji.sample.manage.model.param.DeviceOprLogsQueryParam;
+import com.dji.sdk.cloudapi.log.FileUploadUpdateRequest;
+import com.dji.sdk.cloudapi.log.LogModuleEnum;
+import com.dji.sdk.common.HttpResultResponse;
+import com.dji.sdk.common.PaginationData;
+
+import java.net.URL;
+import java.util.List;
+
+/**
+ * @author hqjiang
+ * @version 1.0
+ * @date 2024/6/25
+ */
+public interface IDeviceOprLogsService {
+
+    /**
+     * 添加设备操作日志
+     * @param logsDTO
+     * @return 日志ID
+     */
+    Long insertDeviceOprLogs(DeviceOprLogsDTO logsDTO);
+
+    /**
+     * 获取设备日志信息
+     * @param workspaceId
+     * @param param
+     * @return
+     */
+    PaginationData<DeviceOprLogsDTO> getDeviceOprLogs(String workspaceId, DeviceOprLogsQueryParam param);
+
+    void addDeviceOprLogsDTO(DeviceDTO redisDevice, LogOprTypeEnum oprTypeEnum);
+
+    void addDeviceOprLogsDTO(DeviceDTO redisDevice, LogOprTypeEnum oprTypeEnum, CustomClaim customClaim);
+}

+ 13 - 0
Backend/sample/src/main/java/com/dji/sample/manage/service/IDeviceService.java

@@ -1,5 +1,6 @@
 package com.dji.sample.manage.service;
 package com.dji.sample.manage.service;
 
 
+import com.dji.sample.common.model.CustomClaim;
 import com.dji.sample.component.websocket.model.BizCodeEnum;
 import com.dji.sample.component.websocket.model.BizCodeEnum;
 import com.dji.sample.manage.model.dto.DeviceDTO;
 import com.dji.sample.manage.model.dto.DeviceDTO;
 import com.dji.sample.manage.model.dto.DeviceFirmwareUpgradeDTO;
 import com.dji.sample.manage.model.dto.DeviceFirmwareUpgradeDTO;
@@ -130,12 +131,22 @@ public interface IDeviceService {
      */
      */
     PaginationData<DeviceDTO> getBoundDevicesWithDomain(String workspaceId, Long page, Long pageSize, Integer domain);
     PaginationData<DeviceDTO> getBoundDevicesWithDomain(String workspaceId, Long page, Long pageSize, Integer domain);
 
 
+    DeviceDTO setChild(DeviceDTO device);
+
     /**
     /**
      * Unbind device base on device's sn.
      * Unbind device base on device's sn.
      * @param deviceSn
      * @param deviceSn
      */
      */
     void unbindDevice(String deviceSn);
     void unbindDevice(String deviceSn);
 
 
+    /**
+     * Unbind device base on device's sn.
+     * @param deviceSn
+     */
+    void unbindDevice(String deviceSn,CustomClaim customClaim);
+
+
+
     /**
     /**
      * Get device information based on device's sn.
      * Get device information based on device's sn.
      * @param sn device's sn
      * @param sn device's sn
@@ -143,6 +154,8 @@ public interface IDeviceService {
      */
      */
     Optional<DeviceDTO> getDeviceBySn(String sn);
     Optional<DeviceDTO> getDeviceBySn(String sn);
 
 
+    Optional<DeviceDTO> getDevice(String workspaceId, Integer domain, String childSn);
+
     /**
     /**
      * Create job for device firmware updates.
      * Create job for device firmware updates.
      * @param workspaceId
      * @param workspaceId

+ 1 - 1
Backend/sample/src/main/java/com/dji/sample/manage/service/IUserService.java

@@ -24,7 +24,7 @@ public interface IUserService {
      * @param flag
      * @param flag
      * @return
      * @return
      */
      */
-    HttpResultResponse userLogin(String username, String password, Integer flag);
+    HttpResultResponse userLogin(String username, String password, Integer flag, String gatewaySn);
 
 
     /**
     /**
      * Create a user object containing a new token.
      * Create a user object containing a new token.

+ 143 - 0
Backend/sample/src/main/java/com/dji/sample/manage/service/impl/DeviceOprLogsServiceImpl.java

@@ -0,0 +1,143 @@
+package com.dji.sample.manage.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.dji.sample.common.model.CustomClaim;
+import com.dji.sample.common.util.UserRequest;
+import com.dji.sample.manage.dao.IDeviceOprLogsMapper;
+import com.dji.sample.manage.model.dto.DeviceDTO;
+import com.dji.sample.manage.model.dto.DeviceOprLogsDTO;
+import com.dji.sample.manage.model.dto.WorkspaceDTO;
+import com.dji.sample.manage.model.entity.DeviceOprLogsEntity;
+import com.dji.sample.manage.model.enums.LogOprTypeEnum;
+import com.dji.sample.manage.model.param.DeviceOprLogsQueryParam;
+import com.dji.sample.manage.service.IDeviceOprLogsService;
+import com.dji.sample.manage.service.IWorkspaceService;
+import com.dji.sdk.common.Pagination;
+import com.dji.sdk.common.PaginationData;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * @author hqjiang
+ * @version 1.0
+ * @date 2024/6/25
+ */
+@Service
+@Transactional
+@Slf4j
+public class DeviceOprLogsServiceImpl implements IDeviceOprLogsService {
+
+    @Autowired
+    private IDeviceOprLogsMapper mapper;
+
+    @Autowired
+    private IWorkspaceService workspaceService;
+
+    @Override
+    public Long insertDeviceOprLogs(DeviceOprLogsDTO logsDTO) {
+        DeviceOprLogsEntity entity = dto2Entity(logsDTO);
+        return mapper.insert(entity) > 0 ? entity.getId() : -1;
+    }
+
+    @Override
+    public PaginationData<DeviceOprLogsDTO> getDeviceOprLogs(String workspaceId, DeviceOprLogsQueryParam param) {
+        LambdaQueryWrapper<DeviceOprLogsEntity> queryWrapper = new LambdaQueryWrapper<DeviceOprLogsEntity>()
+                .eq(DeviceOprLogsEntity::getWorkspaceId, workspaceId)
+                .between(Objects.nonNull(param.getBeginTime()) && Objects.nonNull(param.getEndTime()),
+                        DeviceOprLogsEntity::getCreateTime, param.getBeginTime(), param.getEndTime())
+                .and(StringUtils.hasText(param.getLogsInformation()),
+                        wrapper -> wrapper .like( DeviceOprLogsEntity::getLogInfo, param.getLogsInformation())
+                                .or().like( DeviceOprLogsEntity::getDeviceSn, param.getLogsInformation())
+                                .or().like(DeviceOprLogsEntity::getWorkspaceName, param.getLogsInformation())
+                )
+                .orderByDesc(DeviceOprLogsEntity::getCreateTime);
+
+        Page<DeviceOprLogsEntity> pagination = mapper.selectPage(new Page<>(param.getPage(), param.getPageSize()), queryWrapper);
+
+        List<DeviceOprLogsDTO> deviceOprLogsList = pagination.getRecords().stream().map(this::entity2Dto).collect(Collectors.toList());
+
+        return new PaginationData<DeviceOprLogsDTO>(deviceOprLogsList, new Pagination(pagination.getCurrent(), pagination.getSize(), pagination.getTotal()));
+    }
+
+
+    @Override
+    public void addDeviceOprLogsDTO(DeviceDTO redisDevice, LogOprTypeEnum oprTypeEnum) {
+        CustomClaim customClaim = UserRequest.getCurrentUser();
+        addDeviceOprLog(redisDevice,oprTypeEnum,customClaim);
+    }
+
+    @Override
+    public void addDeviceOprLogsDTO(DeviceDTO redisDevice, LogOprTypeEnum oprTypeEnum, CustomClaim customClaim) {
+        if(customClaim == null) {
+            customClaim = UserRequest.getCurrentUser();
+        }
+        addDeviceOprLog(redisDevice,oprTypeEnum,customClaim);
+    }
+
+    private void addDeviceOprLog(DeviceDTO redisDevice, LogOprTypeEnum oprTypeEnum,CustomClaim customClaim) {
+        WorkspaceDTO workspaceDTO = workspaceService.getWorkspaceByWorkspaceId(redisDevice.getWorkspaceId()).get();
+        DeviceOprLogsDTO deviceOprLogsDTO =
+                DeviceOprLogsDTO.builder().username(customClaim.getUsername())
+                        .deviceSn(redisDevice.getDeviceSn())
+                        .deviceName(redisDevice.getDeviceName())
+                        .nickName(redisDevice.getNickname())
+                        .logInfo(oprTypeEnum.getDesc() + "成功")
+                        .workspaceId(redisDevice.getWorkspaceId())
+                        .workspaceName(workspaceDTO.getWorkspaceName())
+                        .type(oprTypeEnum.getVal())
+                        .bindCode(workspaceDTO.getBindCode())
+                        .createTime(LocalDateTime.now())
+                        .build();
+        insertDeviceOprLogs(deviceOprLogsDTO);
+    }
+
+    private DeviceOprLogsDTO entity2Dto(DeviceOprLogsEntity entity) {
+        if (Objects.isNull(entity)) {
+            return null;
+        }
+        return DeviceOprLogsDTO.builder()
+                .username(entity.getUsername())
+                .deviceSn(entity.getDeviceSn())
+                .deviceName(entity.getDeviceName())
+                .nickName(entity.getNickname())
+                .logInfo(entity.getLogInfo())
+                .workspaceId(entity.getWorkspaceId())
+                .workspaceName(entity.getWorkspaceName())
+                .type(entity.getType())
+                .typeName(LogOprTypeEnum.find(entity.getType()).getDesc())
+                .bindCode(entity.getBindCode())
+                .createTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getCreateTime()), ZoneId.systemDefault()))
+                .build();
+    }
+
+
+    private DeviceOprLogsEntity dto2Entity(DeviceOprLogsDTO dto) {
+        if (dto == null) {
+            return DeviceOprLogsEntity.builder().build();
+        }
+
+        return DeviceOprLogsEntity.builder()
+                .username(dto.getUsername())
+                .deviceName(dto.getDeviceName())
+                .deviceSn(dto.getDeviceSn())
+                .nickname(dto.getNickName())
+                .logInfo(dto.getLogInfo())
+                .workspaceId(dto.getWorkspaceId())
+                .workspaceName(dto.getWorkspaceName())
+                .type(dto.getType())
+                .bindCode(dto.getBindCode())
+                .createTime(dto.getCreateTime() != null ? dto.getCreateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : null)
+                .build();
+    }
+}

+ 52 - 21
Backend/sample/src/main/java/com/dji/sample/manage/service/impl/DeviceServiceImpl.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.dji.sample.common.error.CommonErrorEnum;
 import com.dji.sample.common.error.CommonErrorEnum;
+import com.dji.sample.common.model.CustomClaim;
 import com.dji.sample.component.mqtt.model.EventsReceiver;
 import com.dji.sample.component.mqtt.model.EventsReceiver;
 import com.dji.sample.component.websocket.model.BizCodeEnum;
 import com.dji.sample.component.websocket.model.BizCodeEnum;
 import com.dji.sample.component.websocket.service.IWebSocketMessageService;
 import com.dji.sample.component.websocket.service.IWebSocketMessageService;
@@ -12,6 +13,7 @@ import com.dji.sample.manage.dao.IDeviceMapper;
 import com.dji.sample.manage.model.dto.*;
 import com.dji.sample.manage.model.dto.*;
 import com.dji.sample.manage.model.entity.DeviceEntity;
 import com.dji.sample.manage.model.entity.DeviceEntity;
 import com.dji.sample.manage.model.enums.DeviceFirmwareStatusEnum;
 import com.dji.sample.manage.model.enums.DeviceFirmwareStatusEnum;
+import com.dji.sample.manage.model.enums.LogOprTypeEnum;
 import com.dji.sample.manage.model.enums.PropertySetFieldEnum;
 import com.dji.sample.manage.model.enums.PropertySetFieldEnum;
 import com.dji.sample.manage.model.enums.UserTypeEnum;
 import com.dji.sample.manage.model.enums.UserTypeEnum;
 import com.dji.sample.manage.model.param.DeviceQueryParam;
 import com.dji.sample.manage.model.param.DeviceQueryParam;
@@ -127,7 +129,10 @@ public class DeviceServiceImpl implements IDeviceService {
     private AbstractFirmwareService abstractFirmwareService;
     private AbstractFirmwareService abstractFirmwareService;
 
 
     @Autowired
     @Autowired
-    private IUserService userService;
+    private IDeviceOprLogsService deviceOprLogsService;
+
+//    @Autowired
+//    private IUserService userService;
 
 
     @Override
     @Override
     public void subDeviceOffline(String deviceSn) {
     public void subDeviceOffline(String deviceSn) {
@@ -452,9 +457,9 @@ public class DeviceServiceImpl implements IDeviceService {
         }
         }
 
 
         //更新登录用户绑定项目工作空间
         //更新登录用户绑定项目工作空间
-        if(StringUtils.hasText(device.getUserId())) {
-            userService.updateUserWorkspace(device.getWorkspaceId(), device.getUserId());
-        }
+//        if(StringUtils.hasText(device.getUserId())) {
+//            userService.updateUserWorkspace(device.getWorkspaceId(), device.getUserId());
+//        }
 
 
         DeviceDTO redisDevice = deviceOpt.get();
         DeviceDTO redisDevice = deviceOpt.get();
         redisDevice.setWorkspaceId(device.getWorkspaceId());
         redisDevice.setWorkspaceId(device.getWorkspaceId());
@@ -471,9 +476,14 @@ public class DeviceServiceImpl implements IDeviceService {
 
 
         pushDeviceOnlineTopo(device.getWorkspaceId(), gatewaySn, deviceSn);
         pushDeviceOnlineTopo(device.getWorkspaceId(), gatewaySn, deviceSn);
         subDeviceOnlineSubscribeTopic(SDKManager.getDeviceSDK(gatewaySn));
         subDeviceOnlineSubscribeTopic(SDKManager.getDeviceSDK(gatewaySn));
+
+        //添加绑定日志
+        deviceOprLogsService.addDeviceOprLogsDTO(redisDevice,LogOprTypeEnum.BIND);
+
         return true;
         return true;
     }
     }
 
 
+
     @Override
     @Override
     public PaginationData<DeviceDTO> getBoundDevicesWithDomain(String workspaceId, Long page,
     public PaginationData<DeviceDTO> getBoundDevicesWithDomain(String workspaceId, Long page,
                                                                Long pageSize, Integer domain) {
                                                                Long pageSize, Integer domain) {
@@ -486,30 +496,46 @@ public class DeviceServiceImpl implements IDeviceService {
         List<DeviceDTO> devicesList = pagination.getRecords().stream().map(this::deviceEntityConvertToDTO)
         List<DeviceDTO> devicesList = pagination.getRecords().stream().map(this::deviceEntityConvertToDTO)
                 .peek(device -> {
                 .peek(device -> {
                     device.setStatus(deviceRedisService.checkDeviceOnline(device.getDeviceSn()));
                     device.setStatus(deviceRedisService.checkDeviceOnline(device.getDeviceSn()));
-                    if (StringUtils.hasText(device.getChildDeviceSn())) {
-                        Optional<DeviceDTO> childOpt = this.getDeviceBySn(device.getChildDeviceSn());
-                        childOpt.ifPresent(child -> {
-                            child.setStatus(deviceRedisService.checkDeviceOnline(child.getDeviceSn()));
-                            child.setWorkspaceName(device.getWorkspaceName());
-                            device.setChildren(child);
-                        });
-                    }
-                    //遥控器作为child
-                    if(DeviceDomainEnum.DRONE == device.getDomain()) {
-                        Optional<DeviceDTO> childOpt = getDevice(device.getWorkspaceId(), DeviceDomainEnum.REMOTER_CONTROL.getDomain(), device.getDeviceSn());
-                        childOpt.ifPresent(child -> {
-                            child.setWorkspaceName(device.getWorkspaceName());
-                            device.setChildren(child);
-                        });
-                    }
+                    setChild(device);
                 })
                 })
                 .collect(Collectors.toList());
                 .collect(Collectors.toList());
         return new PaginationData<DeviceDTO>(devicesList, new Pagination(pagination.getCurrent(), pagination.getSize(), pagination.getTotal()));
         return new PaginationData<DeviceDTO>(devicesList, new Pagination(pagination.getCurrent(), pagination.getSize(), pagination.getTotal()));
     }
     }
 
 
+
+    @Override
+    public  DeviceDTO setChild(DeviceDTO device) {
+        if (StringUtils.hasText(device.getChildDeviceSn())) {
+            Optional<DeviceDTO> childOpt = getDeviceBySn(device.getChildDeviceSn());
+            childOpt.ifPresent(child -> {
+                child.setStatus(deviceRedisService.checkDeviceOnline(child.getDeviceSn()));
+                child.setWorkspaceName(device.getWorkspaceName());
+                device.setChildren(child);
+            });
+        }
+        //遥控器作为child
+        if(DeviceDomainEnum.DRONE == device.getDomain()) {
+            Optional<DeviceDTO> childOpt = getDevice(device.getWorkspaceId(), DeviceDomainEnum.REMOTER_CONTROL.getDomain(), device.getDeviceSn());
+            childOpt.ifPresent(child -> {
+                child.setWorkspaceName(device.getWorkspaceName());
+                device.setChildren(child);
+            });
+        }
+
+        return device;
+    }
+
     @Override
     @Override
     public void unbindDevice(String deviceSn) {
     public void unbindDevice(String deviceSn) {
+        unbind(deviceSn,null);
+    }
 
 
+    @Override
+    public void unbindDevice(String deviceSn, CustomClaim customClaim) {
+        unbind(deviceSn,customClaim);
+    }
+
+    private void unbind(String deviceSn, CustomClaim customClaim) {
         Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(deviceSn);
         Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(deviceSn);
         if (deviceOpt.isPresent()) {
         if (deviceOpt.isPresent()) {
             subDeviceOffline(deviceSn);
             subDeviceOffline(deviceSn);
@@ -526,6 +552,10 @@ public class DeviceServiceImpl implements IDeviceService {
                 .boundStatus(false)
                 .boundStatus(false)
                 .build();
                 .build();
         this.updateDevice(device);
         this.updateDevice(device);
+        //添加解绑日志
+        if(StringUtils.hasText(deviceOpt.get().getWorkspaceId()) ) {
+            deviceOprLogsService.addDeviceOprLogsDTO(deviceOpt.get(), LogOprTypeEnum.UNBIND, customClaim);
+        }
     }
     }
 
 
     @Override
     @Override
@@ -539,7 +569,8 @@ public class DeviceServiceImpl implements IDeviceService {
         return Optional.of(device);
         return Optional.of(device);
     }
     }
 
 
-    private Optional<DeviceDTO> getDevice(String workspaceId, Integer domain, String childSn) {
+    @Override
+    public Optional<DeviceDTO> getDevice(String workspaceId, Integer domain, String childSn) {
         List<Integer> domainList = new ArrayList<>();
         List<Integer> domainList = new ArrayList<>();
         domainList.add(domain);
         domainList.add(domain);
         List<DeviceDTO> devicesList = this.getDevicesByParams(DeviceQueryParam.builder().workspaceId(workspaceId).childSn(childSn).domains(domainList).build());
         List<DeviceDTO> devicesList = this.getDevicesByParams(DeviceQueryParam.builder().workspaceId(workspaceId).childSn(childSn).domains(domainList).build());

+ 19 - 1
Backend/sample/src/main/java/com/dji/sample/manage/service/impl/UserServiceImpl.java

@@ -11,11 +11,13 @@ import com.dji.sample.common.model.CustomClaim;
 import com.dji.sample.common.util.JwtUtil;
 import com.dji.sample.common.util.JwtUtil;
 import com.dji.sample.component.mqtt.config.MqttPropertyConfiguration;
 import com.dji.sample.component.mqtt.config.MqttPropertyConfiguration;
 import com.dji.sample.manage.dao.IUserMapper;
 import com.dji.sample.manage.dao.IUserMapper;
+import com.dji.sample.manage.model.dto.DeviceDTO;
 import com.dji.sample.manage.model.dto.UserDTO;
 import com.dji.sample.manage.model.dto.UserDTO;
 import com.dji.sample.manage.model.dto.UserListDTO;
 import com.dji.sample.manage.model.dto.UserListDTO;
 import com.dji.sample.manage.model.dto.WorkspaceDTO;
 import com.dji.sample.manage.model.dto.WorkspaceDTO;
 import com.dji.sample.manage.model.entity.UserEntity;
 import com.dji.sample.manage.model.entity.UserEntity;
 import com.dji.sample.manage.model.enums.UserTypeEnum;
 import com.dji.sample.manage.model.enums.UserTypeEnum;
+import com.dji.sample.manage.service.IDeviceService;
 import com.dji.sample.manage.service.IUserService;
 import com.dji.sample.manage.service.IUserService;
 import com.dji.sample.manage.service.IWorkspaceService;
 import com.dji.sample.manage.service.IWorkspaceService;
 import com.dji.sdk.common.HttpResultResponse;
 import com.dji.sdk.common.HttpResultResponse;
@@ -48,6 +50,9 @@ public class UserServiceImpl implements IUserService {
     @Autowired
     @Autowired
     private IWorkspaceService workspaceService;
     private IWorkspaceService workspaceService;
 
 
+    @Autowired
+    private IDeviceService deviceService;
+
     @Override
     @Override
     public HttpResultResponse getUserByUsername(String username, String workspaceId) {
     public HttpResultResponse getUserByUsername(String username, String workspaceId) {
 
 
@@ -65,7 +70,7 @@ public class UserServiceImpl implements IUserService {
     }
     }
 
 
     @Override
     @Override
-    public HttpResultResponse userLogin(String username, String password, Integer flag) {
+    public HttpResultResponse userLogin(String username, String password, Integer flag, String gatewaySn) {
         // check user
         // check user
         UserEntity userEntity = this.getUserByUsername(username);
         UserEntity userEntity = this.getUserByUsername(username);
         if (userEntity == null) {
         if (userEntity == null) {
@@ -100,6 +105,19 @@ public class UserServiceImpl implements IUserService {
         userDTO.setMqttAddr(MqttPropertyConfiguration.getBasicMqttAddress());
         userDTO.setMqttAddr(MqttPropertyConfiguration.getBasicMqttAddress());
         userDTO.setAccessToken(token);
         userDTO.setAccessToken(token);
         userDTO.setWorkspaceId(workspaceOpt.get().getWorkspaceId());
         userDTO.setWorkspaceId(workspaceOpt.get().getWorkspaceId());
+
+        //飞行员登录,如果设备原绑定工作空间不一致,则解绑
+        if(UserTypeEnum.PILOT.getVal() == flag.intValue() && StringUtils.hasText(gatewaySn)) {
+            Optional<DeviceDTO> optDevice = deviceService.getDeviceBySn(gatewaySn);
+            if(optDevice.isPresent() && !userDTO.getWorkspaceId().equals(optDevice.get().getWorkspaceId())) {
+                if(StringUtils.hasText(optDevice.get().getChildDeviceSn())) {
+                    deviceService.unbindDevice(optDevice.get().getChildDeviceSn(),customClaim);
+                } else {
+                    deviceService.unbindDevice(gatewaySn,customClaim);
+                }
+            }
+        }
+
         return HttpResultResponse.success(userDTO);
         return HttpResultResponse.success(userDTO);
     }
     }
 
 

+ 1 - 1
Backend/sample/src/main/resources/application-prd.yml

@@ -118,7 +118,7 @@ oss:
   endpoint: http://xia0miduo.gicp.net:9000
   endpoint: http://xia0miduo.gicp.net:9000
   access-key: fileadmin
   access-key: fileadmin
   secret-key: fileadmin
   secret-key: fileadmin
-  bucket: dji-bucket
+  bucket: papbtest
   expire: 3600
   expire: 3600
   region: us-east-1
   region: us-east-1
   object-dir-prefix: wayline
   object-dir-prefix: wayline

+ 1 - 1
Backend/sample/src/main/resources/application.yml

@@ -120,7 +120,7 @@ oss:
   endpoint: http://xia0miduo.gicp.net:9000
   endpoint: http://xia0miduo.gicp.net:9000
   access-key: fileadmin
   access-key: fileadmin
   secret-key: fileadmin
   secret-key: fileadmin
-  bucket: dji-bucket
+  bucket: papbtest
   expire: 3600
   expire: 3600
   region: us-east-1
   region: us-east-1
   object-dir-prefix: wayline
   object-dir-prefix: wayline

+ 22 - 3
Web/src/api/custom/index.ts

@@ -1,14 +1,26 @@
 import request from '/@/api/http/request';
 import request from '/@/api/http/request';
+import {ELocalStorageKey} from "/@/types";
 
 
+const workspaceId: string = localStorage.getItem(ELocalStorageKey.WorkspaceId) || ''
 // Api参数类型
 // Api参数类型
 export type FetchFeedbackRecordListApiParams = {
 export type FetchFeedbackRecordListApiParams = {
     page: number,
     page: number,
     page_size: number,
     page_size: number,
 };
 };
 
 
+export type FetchChangeRecordListApiParams = {
+    page: number,
+    page_size: number,
+    begin_time?: number, // 开始时间
+    end_time?: number, // 结束时间
+    device_name?: string,//设备类型
+    logsInformation?: string,//模糊查询内容
+};
+
 // Api函数类型
 // Api函数类型
 export type FetchFeedbackRecordListApi = (data: FetchFeedbackRecordListApiParams) => Promise<any>;
 export type FetchFeedbackRecordListApi = (data: FetchFeedbackRecordListApiParams) => Promise<any>;
-export type FetchChangeRecordListApi = () => Promise<any>;
+export type FetchChangeRecordListApi = (params: FetchFeedbackRecordListApiParams) => Promise<any>;
+export type FetchProjectListApi = () => Promise<any>;
 
 
 // 获取反馈记录列表
 // 获取反馈记录列表
 const fetchFeedbackRecordListApi: FetchFeedbackRecordListApi = async (data) => {
 const fetchFeedbackRecordListApi: FetchFeedbackRecordListApi = async (data) => {
@@ -17,12 +29,19 @@ const fetchFeedbackRecordListApi: FetchFeedbackRecordListApi = async (data) => {
 };
 };
 
 
 // 获取变化记录列表
 // 获取变化记录列表
-const fetchChangeRecordListApi: FetchChangeRecordListApi = async () => {
-    const res = await request.get('/manage/api/v1/workspaces/current');
+const fetchChangeRecordListApi: FetchChangeRecordListApi = async (params) => {
+    const res = await request.get('/manage/api/v1/oprlogs/'+workspaceId+'/devices/logs',{ params: params });
+    return res.data;
+};
+
+// 获取项目列表
+const fetchProjectListApi: FetchProjectListApi = async () => {
+    const res = await request.get('/manage/api/v1/workspaces/list');
     return res.data;
     return res.data;
 };
 };
 
 
 export const apis = {
 export const apis = {
     fetchFeedbackRecordList: fetchFeedbackRecordListApi,
     fetchFeedbackRecordList: fetchFeedbackRecordListApi,
     fetchChangeRecordList: fetchChangeRecordListApi,
     fetchChangeRecordList: fetchChangeRecordListApi,
+    fetchProjectList: fetchProjectListApi,
 };
 };

+ 2 - 0
Web/src/api/manage.ts

@@ -9,6 +9,7 @@ export interface LoginBody {
  username: string,
  username: string,
  password: string,
  password: string,
  flag: number,
  flag: number,
+ gateway_sn?: string
 }
 }
 export interface BindBody {
 export interface BindBody {
   device_sn: string,
   device_sn: string,
@@ -29,6 +30,7 @@ export interface HmsQueryBody {
 }
 }
 
 
 export const login = async function (body: LoginBody): Promise<IWorkspaceResponse<any>> {
 export const login = async function (body: LoginBody): Promise<IWorkspaceResponse<any>> {
+  console.log("LoginBody",body)
   const url = `${HTTP_PREFIX}/login`
   const url = `${HTTP_PREFIX}/login`
   const result = await request.post(url, body)
   const result = await request.post(url, body)
   return result.data
   return result.data

+ 8 - 8
Web/src/components/MediaPanel.vue

@@ -1,5 +1,5 @@
 <template>
 <template>
-  <div class="header">Media Files</div>
+  <div class="header">照片视频</div>
   <a-spin :spinning="loading" :delay="1000" tip="downloading" size="large">
   <a-spin :spinning="loading" :delay="1000" tip="downloading" size="large">
     <div class="media-panel-wrapper">
     <div class="media-panel-wrapper">
       <a-table class="media-table" :columns="columns" :data-source="mediaData.data" row-key="fingerprint"
       <a-table class="media-table" :columns="columns" :data-source="mediaData.data" row-key="fingerprint"
@@ -39,13 +39,13 @@ const loading = ref(false)
 
 
 const columns = [
 const columns = [
   {
   {
-    title: 'File Name',
+    title: '文件名称',
     dataIndex: 'file_name',
     dataIndex: 'file_name',
     ellipsis: true,
     ellipsis: true,
     slots: { customRender: 'name' }
     slots: { customRender: 'name' }
   },
   },
   {
   {
-    title: 'File Path',
+    title: '目录',
     dataIndex: 'file_path',
     dataIndex: 'file_path',
     ellipsis: true,
     ellipsis: true,
     slots: { customRender: 'path' }
     slots: { customRender: 'path' }
@@ -55,24 +55,24 @@ const columns = [
   //   dataIndex: 'size',
   //   dataIndex: 'size',
   // },
   // },
   {
   {
-    title: 'Drone',
+    title: '设备',
     dataIndex: 'drone'
     dataIndex: 'drone'
   },
   },
   {
   {
-    title: 'Payload Type',
+    title: '拍摄负载',
     dataIndex: 'payload'
     dataIndex: 'payload'
   },
   },
   {
   {
-    title: 'Original',
+    title: '原图',
     dataIndex: 'is_original',
     dataIndex: 'is_original',
     slots: { customRender: 'original' }
     slots: { customRender: 'original' }
   },
   },
   {
   {
-    title: 'Created',
+    title: '上传时间',
     dataIndex: 'create_time'
     dataIndex: 'create_time'
   },
   },
   {
   {
-    title: 'Action',
+    title: '下载',
     slots: { customRender: 'action' }
     slots: { customRender: 'action' }
   }
   }
 ]
 ]

+ 7 - 7
Web/src/components/common/nav.vue

@@ -16,15 +16,15 @@
       <a-menu-item :key="'/' + ERouterName.MEDIA">
       <a-menu-item :key="'/' + ERouterName.MEDIA">
         <PictureOutlined />
         <PictureOutlined />
         <span>
         <span>
-          照片管理
-        </span>
-      </a-menu-item>
-      <a-menu-item :key="'/' + ERouterName.REPLAY">
-        <VideoCameraOutlined />
-        <span>
-          视频回放
+          媒体管理
         </span>
         </span>
       </a-menu-item>
       </a-menu-item>
+<!--      <a-menu-item :key="'/' + ERouterName.REPLAY">-->
+<!--        <VideoCameraOutlined />-->
+<!--        <span>-->
+<!--          视频回放-->
+<!--        </span>-->
+<!--      </a-menu-item>-->
       <a-menu-item :key="'/' + ERouterName.TRAJECTORY">
       <a-menu-item :key="'/' + ERouterName.TRAJECTORY">
         <GatewayOutlined />
         <GatewayOutlined />
         <span>
         <span>

+ 32 - 70
Web/src/components/devices/changeRecord/index.vue

@@ -1,24 +1,11 @@
 <template>
 <template>
   <div class="changeRecord">
   <div class="changeRecord">
     <div class="changeRecord-table">
     <div class="changeRecord-table">
-      <a-table :scroll="{ x: 'max-content', y: 600 }" rowKey="id" :loading="state.listLoading" :columns="columns"
+      <a-table :scroll="{ x: '100%', y: 600 }" rowKey="id" :loading="state.listLoading" :columns="columns"
         :dataSource="state.list" :pagination="paginationConfig">
         :dataSource="state.list" :pagination="paginationConfig">
-        <!-- 操作 -->
-        <template #operation="{ record }">
-          <div class="editable-row-operations">
-            <div class="flex-align-center flex-row" style="color: #2d8cf0">
-              <a-tooltip title="详情">
-                <FileSearchOutlined style="margin-right: 10px;" @click="onClickEdit(record.id)" />
-              </a-tooltip>
-              <a-tooltip title="删除">
-                <DeleteOutlined @click="onClickDelete(record.id)" />
-              </a-tooltip>
-            </div>
-          </div>
-        </template>
+
       </a-table>
       </a-table>
     </div>
     </div>
-    <Drawer :id="state.currentId" :visible="state.drawerVisible" :onClose="drawerOnClickClose" />
   </div>
   </div>
 </template>
 </template>
 
 
@@ -28,6 +15,7 @@ import { Modal } from 'ant-design-vue';
 import { DeleteOutlined, FileSearchOutlined } from '@ant-design/icons-vue';
 import { DeleteOutlined, FileSearchOutlined } from '@ant-design/icons-vue';
 import Drawer from './components/Drawer.vue';
 import Drawer from './components/Drawer.vue';
 import { apis } from '/@/api/custom/index';
 import { apis } from '/@/api/custom/index';
+import {IPage} from "/@/api/http/type";
 
 
 interface State {
 interface State {
   listLoading: boolean,
   listLoading: boolean,
@@ -52,36 +40,18 @@ const paginationConfig = reactive({
   total: 0
   total: 0
 })
 })
 
 
+
 onMounted(async () => {
 onMounted(async () => {
   state.listLoading = true;
   state.listLoading = true;
   try {
   try {
-    const res = await apis.fetchChangeRecordList();
+    const res = await apis.fetchChangeRecordList({page : paginationConfig.current, page_size: paginationConfig.pageSize});
     console.log(res, 'list');
     console.log(res, 'list');
-    const list = [
-      {
-        id: '1',
-        time: '2021-01-01',
-        name: '1',
-        deviceType: 'aa',
-        sn: '123',
-        deviceName: '1',
-        firmware_version: '1',
-        description: '1',
-        status: '1',
-      },
-      {
-        id: '2',
-        time: '2021-01-01',
-        name: '1',
-        deviceType: 'aa',
-        sn: '123',
-        deviceName: '1',
-        firmware_version: '1',
-        description: '1',
-        status: '1',
-      },
-    ]
-    state.list = list;
+    if(res.code === 0) {
+      paginationConfig.total = res.data.pagination.total
+      paginationConfig.current = res.data.pagination.page
+      paginationConfig.pageSize = res.data.pagination.page_size
+    }
+    state.list = res.data.list;
   } catch (e) {
   } catch (e) {
     console.error(e);
     console.error(e);
   } finally {
   } finally {
@@ -91,49 +61,41 @@ onMounted(async () => {
 
 
 const columns = [
 const columns = [
   {
   {
-    title: '反馈时间',
-    dataIndex: 'time',
-    sorter: (a: any, b: any) => a.time - b.time,
-  },
-  {
-    title: '反馈人',
-    dataIndex: 'name',
+    title: '时间',
+    dataIndex: 'create_time',
+    sorter: (a: any, b: any) => a.create_time - b.create_time,
   },
   },
   {
   {
-    title: '设备型号',
-    dataIndex: 'deviceType',
-    sorter: (a: any, b: any) => a.deviceType - b.deviceType,
+    title: '操作用户',
+    dataIndex: 'username',
   },
   },
   {
   {
-    title: '设备SN',
-    dataIndex: 'sn',
+    title: '绑定项目',
+    dataIndex: 'workspace_name',
   },
   },
   {
   {
-    title: '设备名称',
-    dataIndex: 'deviceName',
-    sorter: (a: any, b: any) => a.deviceName - b.deviceName,
+    title: '绑定码',
+    dataIndex: 'bind_code',
   },
   },
   {
   {
-    title: '固件版本',
-    dataIndex: 'firmware_version',
+    title: '变化描述',
+    dataIndex: 'log_info',
+    sorter: (a: any, b: any) => a.log_info - b.log_info,
   },
   },
   {
   {
-    title: '设备异常描述',
-    dataIndex: 'description',
+    title: '设备型号',
+    dataIndex: 'device_name',
+    sorter: (a: any, b: any) => a.device_name - b.device_name,
   },
   },
   {
   {
-    title: '上传状态',
-    dataIndex: 'status',
-    sorter: (a: any, b: any) => a.time - b.time,
+    title: '设备SN',
+    dataIndex: 'device_sn',
   },
   },
   {
   {
-    title: '操作',
-    dataIndex: 'operation',
-    fixed: 'right',
-    width: 100,
-    className: 'titleStyle',
-    slots: { customRender: 'operation' }
-  },
+    title: '设备名称',
+    dataIndex: 'nick_name',
+    sorter: (a: any, b: any) => a.nick_name - b.nick_name,
+  }
 ]
 ]
 
 
 // 点击编辑
 // 点击编辑

+ 161 - 3
Web/src/components/devices/feedbackRecord/index.vue

@@ -1,11 +1,169 @@
 <template>
 <template>
-  <div>
-    参考设备变化记录代码
+  <div class="changeRecord">
+    <div class="changeRecord-table">
+      <a-table :scroll="{ x: 'max-content', y: 600 }" rowKey="id" :loading="state.listLoading" :columns="columns"
+        :dataSource="state.list" :pagination="paginationConfig">
+        <!-- 操作 -->
+        <template #operation="{ record }">
+          <div class="editable-row-operations">
+            <div class="flex-align-center flex-row" style="color: #2d8cf0">
+              <a-tooltip title="详情">
+                <FileSearchOutlined style="margin-right: 10px;" @click="onClickEdit(record.id)" />
+              </a-tooltip>
+              <a-tooltip title="删除">
+                <DeleteOutlined @click="onClickDelete(record.id)" />
+              </a-tooltip>
+            </div>
+          </div>
+        </template>
+      </a-table>
+    </div>
+    <Drawer :id="state.currentId" :visible="state.drawerVisible" :onClose="drawerOnClickClose" />
   </div>
   </div>
 </template>
 </template>
 
 
 <script lang="ts" setup>
 <script lang="ts" setup>
+import { reactive, onMounted } from 'vue';
+import { Modal } from 'ant-design-vue';
+import { DeleteOutlined, FileSearchOutlined } from '@ant-design/icons-vue';
+import Drawer from './components/Drawer.vue';
+import { apis } from '/@/api/custom/index';
 
 
+interface State {
+  listLoading: boolean,
+  list: any[],
+  currentId: string,
+  drawerVisible: boolean,
+};
+
+const state: State = reactive({
+  listLoading: false,
+  list: [],
+  currentId: '',
+  drawerVisible: false,
+});
+
+const paginationConfig = reactive({
+  pageSizeOptions: ['20', '50', '100'],
+  showQuickJumper: true,
+  showSizeChanger: true,
+  pageSize: 50,
+  current: 1,
+  total: 0
+})
+
+onMounted(async () => {
+  state.listLoading = true;
+  try {
+    //const res = await apis.fetchChangeRecordList();
+    //console.log(res, 'list');
+    const list = [
+      {
+        id: '1',
+        time: '2021-01-01',
+        name: '1',
+        deviceType: 'aa',
+        sn: '123',
+        deviceName: '1',
+        firmware_version: '1',
+        description: '1',
+        status: '1',
+      },
+      {
+        id: '2',
+        time: '2021-01-01',
+        name: '1',
+        deviceType: 'aa',
+        sn: '123',
+        deviceName: '1',
+        firmware_version: '1',
+        description: '1',
+        status: '1',
+      },
+    ]
+    state.list = list;
+  } catch (e) {
+    console.error(e);
+  } finally {
+    state.listLoading = false;
+  }
+})
+
+const columns = [
+  {
+    title: '反馈时间',
+    dataIndex: 'time',
+    sorter: (a: any, b: any) => a.time - b.time,
+  },
+  {
+    title: '反馈人',
+    dataIndex: 'name',
+  },
+  {
+    title: '设备型号',
+    dataIndex: 'deviceType',
+    sorter: (a: any, b: any) => a.deviceType - b.deviceType,
+  },
+  {
+    title: '设备SN',
+    dataIndex: 'sn',
+  },
+  {
+    title: '设备名称',
+    dataIndex: 'deviceName',
+    sorter: (a: any, b: any) => a.deviceName - b.deviceName,
+  },
+  {
+    title: '固件版本',
+    dataIndex: 'firmware_version',
+  },
+  {
+    title: '设备异常描述',
+    dataIndex: 'description',
+  },
+  {
+    title: '上传状态',
+    dataIndex: 'status',
+    sorter: (a: any, b: any) => a.time - b.time,
+  },
+  {
+    title: '操作',
+    dataIndex: 'operation',
+    fixed: 'right',
+    width: 100,
+    className: 'titleStyle',
+    slots: { customRender: 'operation' }
+  },
+]
+
+// 点击编辑
+const onClickEdit = (id: string) => {
+  state.currentId = id;
+  state.drawerVisible = true;
+}
+
+const drawerOnClickClose = () => {
+  state.drawerVisible = false;
+}
+
+// 点击删除
+const onClickDelete = (id: number) => {
+  Modal.confirm({
+    title: '提示',
+    content: '确定删除吗?',
+    onOk: async () => {
+
+    },
+  });
+}
 </script>
 </script>
 
 
-<style lang="scss" scoped></style>
+<style lang="scss" scoped>
+.changeRecord {
+  &-table {
+    background-color: white;
+    margin: 20px;
+    padding: 0 20px;
+  }
+}
+</style>

+ 25 - 14
Web/src/pages/page-pilot/pilot-home.vue

@@ -11,13 +11,13 @@
             <div style="height: 50%;">
             <div style="height: 50%;">
               <a-dropdown :trigger="['click']">
               <a-dropdown :trigger="['click']">
                 <span style="font-size: 16px; font-weight: bolder">{{ workspaceName }}</span>
                 <span style="font-size: 16px; font-weight: bolder">{{ workspaceName }}</span>
-                <template #overlay>
-                  <a-menu v-model:selectedKeys="state.selectedKeys" @select="onSelectMenu">
-                    <a-menu-item v-for="item in state.projectList" :key="item.id">
-                      {{ item.name }}
-                    </a-menu-item>
-                  </a-menu>
-                </template>
+<!--                <template #overlay>-->
+<!--                  <a-menu v-model:selectedKeys="state.selectedKeys" @select="handSelectMenu">-->
+<!--                    <a-menu-item v-for="item in state.projectList" :key="item.id">-->
+<!--                      {{ item.workspace_name }}-->
+<!--                    </a-menu-item>-->
+<!--                  </a-menu>-->
+<!--                </template>-->
               </a-dropdown>
               </a-dropdown>
               <RightOutlined style="float: right; margin-top: 5px; color: #8894a0" @click="showStatus" />
               <RightOutlined style="float: right; margin-top: 5px; color: #8894a0" @click="showStatus" />
             </div>
             </div>
@@ -128,7 +128,7 @@
 <script lang="ts" setup>
 <script lang="ts" setup>
 import { message } from 'ant-design-vue'
 import { message } from 'ant-design-vue'
 import { onMounted, reactive, ref } from 'vue'
 import { onMounted, reactive, ref } from 'vue'
-import { BindBody, bindDevice, getDeviceBySn, getPlatformInfo, getUserInfo } from '/@/api/manage'
+import {BindBody, bindDevice, getDeviceBySn, getPlatformInfo, getUserInfo, unbindDevice} from '/@/api/manage'
 import apiPilot, { ApiParam, MapParam, ThingParam, WsParam } from '/@/api/pilot-bridge'
 import apiPilot, { ApiParam, MapParam, ThingParam, WsParam } from '/@/api/pilot-bridge'
 import { getRoot } from '/@/root'
 import { getRoot } from '/@/root'
 import { EBizCode, EComponentName, EDownloadOwner, ELocalStorageKey, ERouterName, EStatusValue } from '/@/types'
 import { EBizCode, EComponentName, EDownloadOwner, ELocalStorageKey, ERouterName, EStatusValue } from '/@/types'
@@ -179,8 +179,18 @@ const state = reactive({
   projectList: [],
   projectList: [],
 })
 })
 
 
-const onSelectMenu = (items: any) => {
-  console.log(items);
+const handSelectMenu = (items: any) => {
+  console.log("selectMenu====")
+  state.selectedKeys = items
+  localStorage.setItem(ELocalStorageKey.WorkspaceName,items[0].workspace_name)
+  localStorage.setItem(ELocalStorageKey.WorkspaceId,items[0].id)
+  //解绑设备
+  unbindDevice(device.data.gateway_sn).then(unbindRes => {
+    if (unbindRes.code !== 0) {
+      message.error(unbindRes.message)
+      console.error(unbindRes.message)
+    }
+  })
 }
 }
 
 
 const bindParam: BindBody = {
 const bindParam: BindBody = {
@@ -261,10 +271,11 @@ useConnectWebSocket(messageHandler)
 
 
 let bindNum: any
 let bindNum: any
 
 
-onMounted(async () => {
-  const res = await apis.fetchChangeRecordList();
-  state.projectList = res.data
-  state.selectedKeys = res.data
+onMounted(() => {
+  // const res = await apis.fetchProjectList();
+  // console.log(res)
+  // state.projectList = res.data
+  //state.selectedKeys = res.data
 
 
   apiPilot.onBackClickReg()
   apiPilot.onBackClickReg()
   apiPilot.onStopPlatform()
   apiPilot.onStopPlatform()

+ 9 - 2
Web/src/pages/page-pilot/pilot-index.vue

@@ -45,6 +45,7 @@ const formState: UnwrapRef<LoginBody> = reactive({
   username: 'pilot',
   username: 'pilot',
   password: 'pilot123',
   password: 'pilot123',
   flag: EUserType.Pilot,
   flag: EUserType.Pilot,
+  gateway_sn: apiPilot.getRemoteControllerSN()
 })
 })
 const isVerified = ref<boolean>(false)
 const isVerified = ref<boolean>(false)
 onMounted(async () => {
 onMounted(async () => {
@@ -59,16 +60,21 @@ onMounted(async () => {
   if (token) {
   if (token) {
     await refreshToken({})
     await refreshToken({})
       .then(res => {
       .then(res => {
+        console.log('1111111111111111');
         apiPilot.setComponentParam(EComponentName.Api, {
         apiPilot.setComponentParam(EComponentName.Api, {
-          host: CURRENT_CONFIG.baseURL,
+          host: CURRENT_CONFIG.apiURL,
           token: res.data.access_token
           token: res.data.access_token
         })
         })
+        console.log('22222222222222');
         const jsres = apiPilot.loadComponent(EComponentName.Api, apiPilot.getComponentParam(EComponentName.Api))
         const jsres = apiPilot.loadComponent(EComponentName.Api, apiPilot.getComponentParam(EComponentName.Api))
+        console.log('33333333',jsres+"aaaaaaa");
         if (!jsres) {
         if (!jsres) {
           message.error('加载api模块失败')
           message.error('加载api模块失败')
           return
           return
         }
         }
+        console.log('444444');
         apiPilot.setToken(res.data.access_token)
         apiPilot.setToken(res.data.access_token)
+        console.log('5555555');
         localStorage.setItem(ELocalStorageKey.Token, res.data.access_token)
         localStorage.setItem(ELocalStorageKey.Token, res.data.access_token)
         root.$router.push(ERouterName.PILOT_HOME)
         root.$router.push(ERouterName.PILOT_HOME)
       })
       })
@@ -78,6 +84,7 @@ onMounted(async () => {
   }
   }
 })
 })
 const onSubmit = async (e: any) => {
 const onSubmit = async (e: any) => {
+  console.log("formState",formState)
   await login(formState)
   await login(formState)
     .then(res => {
     .then(res => {
       if (!isVerified.value) {
       if (!isVerified.value) {
@@ -87,7 +94,7 @@ const onSubmit = async (e: any) => {
       console.log('login res:', res)
       console.log('login res:', res)
       if (res.code === 0) {
       if (res.code === 0) {
         apiPilot.setComponentParam(EComponentName.Api, {
         apiPilot.setComponentParam(EComponentName.Api, {
-          host: CURRENT_CONFIG.baseURL,
+          host: CURRENT_CONFIG.apiURL,
           token: res.data.access_token
           token: res.data.access_token
         })
         })
         const jsres = apiPilot.loadComponent(
         const jsres = apiPilot.loadComponent(

+ 1 - 1
Web/src/pages/page-web/projects/devices.vue

@@ -7,7 +7,7 @@
       机场异常反馈记录
       机场异常反馈记录
     </a-menu-item>
     </a-menu-item>
     <a-menu-item :key="3">
     <a-menu-item :key="3">
-      所有设备变化记录
+      设备变化记录
     </a-menu-item>
     </a-menu-item>
   </a-menu>
   </a-menu>
   <div v-if="state.selectedKeys[0] === 1">
   <div v-if="state.selectedKeys[0] === 1">

+ 4 - 4
Web/src/pages/page-web/projects/media/index.vue

@@ -1,11 +1,11 @@
 <template>
 <template>
-  <div>
-    照片管理
-  </div>
+    <div class="project-media-wrapper">
+      <MediaPanel />
+    </div>
 </template>
 </template>
 
 
 <script lang="ts" setup>
 <script lang="ts" setup>
-
+import MediaPanel from '/@/components/MediaPanel.vue'
 </script>
 </script>
 
 
 <style lang="scss" scoped></style>
 <style lang="scss" scoped></style>

+ 7 - 7
Web/src/pages/page-web/projects/members.vue

@@ -57,13 +57,13 @@ interface MemberData {
   member: Member[]
   member: Member[]
 }
 }
 const columns = [
 const columns = [
-  { title: 'Account', dataIndex: 'username', width: 150, sorter: (a: Member, b: Member) => a.username.localeCompare(b.username), className: 'titleStyle' },
-  { title: 'User Type', dataIndex: 'user_type', width: 150, className: 'titleStyle' },
-  { title: 'Workspace Name', dataIndex: 'workspace_name', width: 150, className: 'titleStyle' },
-  { title: 'Mqtt Username', dataIndex: 'mqtt_username', width: 150, className: 'titleStyle', slots: { customRender: 'mqtt_username' } },
-  { title: 'Mqtt Password', dataIndex: 'mqtt_password', width: 150, className: 'titleStyle', slots: { customRender: 'mqtt_password' } },
-  { title: 'Joined', dataIndex: 'create_time', width: 150, sorter: (a: Member, b: Member) => a.create_time.localeCompare(b.create_time), className: 'titleStyle' },
-  { title: 'Action', dataIndex: 'action', width: 100, className: 'titleStyle', slots: { customRender: 'action' } },
+  { title: '用户名称', dataIndex: 'username', width: 150, sorter: (a: Member, b: Member) => a.username.localeCompare(b.username), className: 'titleStyle' },
+  { title: '用户类型', dataIndex: 'user_type', width: 150, className: 'titleStyle' },
+  { title: '项目名称', dataIndex: 'workspace_name', width: 150, className: 'titleStyle' },
+  { title: 'Mqtt 用户', dataIndex: 'mqtt_username', width: 150, className: 'titleStyle', slots: { customRender: 'mqtt_username' } },
+  { title: 'Mqtt 密码', dataIndex: 'mqtt_password', width: 150, className: 'titleStyle', slots: { customRender: 'mqtt_password' } },
+  { title: '创建时间', dataIndex: 'create_time', width: 150, sorter: (a: Member, b: Member) => a.create_time.localeCompare(b.create_time), className: 'titleStyle' },
+  { title: '操作', dataIndex: 'action', width: 100, className: 'titleStyle', slots: { customRender: 'action' } },
 ]
 ]
 
 
 const data = reactive<MemberData>({
 const data = reactive<MemberData>({