浏览代码

+ 增加室内、室外两个维度

chen.cheng 8 月之前
父节点
当前提交
0dc7f75e44
共有 30 个文件被更改,包括 960 次插入323 次删除
  1. 1 1
      bd-location/deploy.sh
  2. 52 38
      bd-location/src/main/java/com/ruoyi/bd/domain/BdFenceInfo.java
  3. 1 1
      bd-location/src/main/java/com/ruoyi/bd/service/engine/EvtFusionEngine.java
  4. 5 2
      bd-location/src/main/java/com/ruoyi/bd/service/engine/impl/FenceBreakInEngine.java
  5. 159 0
      bd-location/src/main/java/com/ruoyi/bd/service/engine/impl/RoomBreakInEngine.java
  6. 6 0
      bd-location/src/main/java/com/ruoyi/common/BDConst.java
  7. 3 1
      bd-location/src/main/java/com/ruoyi/common/enums/EvtType.java
  8. 22 0
      bd-location/src/main/java/com/ruoyi/common/enums/FenceType.java
  9. 14 0
      bd-location/src/main/java/com/ruoyi/mqtt/UWBLocationSubscribeListener.java
  10. 1 1
      bd-location/src/main/resources/application-druid.yml
  11. 4 1
      bd-location/src/main/resources/application.yml
  12. 35 16
      bd-location/src/main/resources/mapper/bd/BdFenceInfoMapper.xml
  13. 1 1
      bd-location/version
  14. 1 1
      ruoyi-ui/deploy.sh
  15. 2 1
      ruoyi-ui/package.json
  16. 55 0
      ruoyi-ui/src/main.js
  17. 22 0
      ruoyi-ui/src/utils/BDConst.js
  18. 12 0
      ruoyi-ui/src/views/bd/fence/index.scss
  19. 109 80
      ruoyi-ui/src/views/bd/fence/index.vue
  20. 55 15
      ruoyi-ui/src/views/bd/fenceEvt/index.vue
  21. 39 11
      ruoyi-ui/src/views/bd/map/maphandle.js
  22. 46 70
      ruoyi-ui/src/views/bd/realtimeLocation/index.vue
  23. 99 82
      ruoyi-ui/src/views/bd/roomlocation/roommap/index.vue
  24. 13 0
      ruoyi-ui/src/views/bd/roomlocation/roommap/video/index.scss
  25. 二进制
      ruoyi-ui/src/views/bd/roomlocation/roommap/video/output.mp4
  26. 二进制
      ruoyi-ui/src/views/bd/roomlocation/roommap/video/roomlocation.mp4
  27. 126 0
      ruoyi-ui/src/views/bd/roomlocation/roommap/video/video.vue
  28. 15 0
      ruoyi-ui/src/views/bd/rooms/index.scss
  29. 61 0
      ruoyi-ui/src/views/bd/rooms/index.vue
  30. 1 1
      ruoyi-ui/version

+ 1 - 1
bd-location/deploy.sh

@@ -37,7 +37,7 @@ fi
 # 更新版本号
 NEXT_VERSION="${CURRENT_MAJOR}.${CURRENT_MINOR}"
 echo $NEXT_VERSION > $VERSION_FILE
-echo "版本号:" $NEXT_VERSION
+echo  -e "\e[1;34;42m版本号:" $NEXT_VERSION " 日期:$(date +"%Y-%m-%d %H:%M:%S")" "\e[0m"
 # 用户选择执行的步骤
 echo "请选择要执行的步骤(输入数字):"
 echo "1. 编译bd-app-backend项目"

+ 52 - 38
bd-location/src/main/java/com/ruoyi/bd/domain/BdFenceInfo.java

@@ -12,73 +12,79 @@ import org.locationtech.jts.geom.Polygon;
  * @author ruoyi
  * @date 2024-10-14
  */
-public class BdFenceInfo extends BaseEntity
-{
+public class BdFenceInfo extends BaseEntity {
     private static final long serialVersionUID = 1L;
 
-    /**  */
+    /**
+     *
+     */
     private Long id;
 
-    /** 围栏名称 */
+    /**
+     * 围栏名称
+     */
     @Excel(name = "围栏名称")
     private String defenceName;
 
-    /** 围栏图形坐标 */
+    /**
+     * 围栏图形坐标
+     */
     private String poly;
 
-    /** 中心点 */
+    /**
+     * 中心点
+     */
     @Excel(name = "中心点")
     private Double centerLng;
 
-    /** 中心点 */
+    /**
+     * 中心点
+     */
     @Excel(name = "中心点")
     private Double centerLat;
 
     private Polygon polygon;
 
-    public void setId(Long id)
-    {
+    private String fenceType;
+
+
+    public void setId(Long id) {
         this.id = id;
     }
 
-    public Long getId()
-    {
+    public Long getId() {
         return id;
     }
-    public void setDefenceName(String defenceName)
-    {
+
+    public void setDefenceName(String defenceName) {
         this.defenceName = defenceName;
     }
 
-    public String getDefenceName()
-    {
+    public String getDefenceName() {
         return defenceName;
     }
-    public void setPoly(String poly)
-    {
+
+    public void setPoly(String poly) {
         this.poly = poly;
     }
 
-    public String getPoly()
-    {
+    public String getPoly() {
         return poly;
     }
-    public void setCenterLng(Double centerLng)
-    {
+
+    public void setCenterLng(Double centerLng) {
         this.centerLng = centerLng;
     }
 
-    public Double getCenterLng()
-    {
+    public Double getCenterLng() {
         return centerLng;
     }
-    public void setCenterLat(Double centerLat)
-    {
+
+    public void setCenterLat(Double centerLat) {
         this.centerLat = centerLat;
     }
 
-    public Double getCenterLat()
-    {
+    public Double getCenterLat() {
         return centerLat;
     }
 
@@ -90,18 +96,26 @@ public class BdFenceInfo extends BaseEntity
         this.polygon = polygon;
     }
 
+    public String getFenceType() {
+        return fenceType;
+    }
+
+    public void setFenceType(String fenceType) {
+        this.fenceType = fenceType;
+    }
+
     @Override
     public String toString() {
-        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
-            .append("id", getId())
-            .append("defenceName", getDefenceName())
-            .append("poly", getPoly())
-            .append("centerLng", getCenterLng())
-            .append("centerLat", getCenterLat())
-            .append("updateTime", getUpdateTime())
-            .append("createTime", getCreateTime())
-            .append("createBy", getCreateBy())
-            .append("updateBy", getUpdateBy())
-            .toString();
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+                .append("id", getId())
+                .append("defenceName", getDefenceName())
+                .append("poly", getPoly())
+                .append("centerLng", getCenterLng())
+                .append("centerLat", getCenterLat())
+                .append("updateTime", getUpdateTime())
+                .append("createTime", getCreateTime())
+                .append("createBy", getCreateBy())
+                .append("updateBy", getUpdateBy())
+                .toString();
     }
 }

+ 1 - 1
bd-location/src/main/java/com/ruoyi/bd/service/engine/EvtFusionEngine.java

@@ -21,7 +21,7 @@ public abstract class EvtFusionEngine implements IFusionEngine {
 
     private String engineName;
 
-    @Value("${evt-fusion.thread-pool-size:5}")
+    @Value("${evt-fusion.thread-pool-size:2}")
     private int threadPoolSize;
 
     @Resource(name = "threadPoolTaskExecutor")

+ 5 - 2
bd-location/src/main/java/com/ruoyi/bd/service/engine/impl/FenceBreakInEngine.java

@@ -13,6 +13,7 @@ import com.ruoyi.common.BDConst;
 import com.ruoyi.common.core.redis.RedisCache;
 import com.ruoyi.common.enums.EvtStatus;
 import com.ruoyi.common.enums.EvtType;
+import com.ruoyi.common.enums.FenceType;
 import com.ruoyi.common.utils.DateTimeUtil;
 import com.ruoyi.common.utils.geo.GeoUtils;
 import com.ruoyi.web.core.config.MqttCfg;
@@ -65,9 +66,11 @@ public class FenceBreakInEngine extends EvtFusionEngine {
     @PostConstruct
     public void init() {
         super.init();
-        this.setEngineName("围栏闯禁事件融合");
+        this.setEngineName("室外围栏闯禁事件融合");
         if (!redisCache.hasKey(BDConst.REDIS_KEY.FENCE)) {
-            List<BdFenceInfo> bdFenceInfos = fenceInfoService.selectBdFenceInfoList(new BdFenceInfo());
+            List<BdFenceInfo> bdFenceInfos = fenceInfoService.selectBdFenceInfoList(new BdFenceInfo() {{
+                setFenceType(FenceType.OUT_SIDE_FENCE_IN.getCode());
+            }});
             if (CollectionUtils.isEmpty(bdFenceInfos)) {
                 return;
             }

+ 159 - 0
bd-location/src/main/java/com/ruoyi/bd/service/engine/impl/RoomBreakInEngine.java

@@ -0,0 +1,159 @@
+package com.ruoyi.bd.service.engine.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.bd.domain.BdFenceInfo;
+import com.ruoyi.bd.domain.BdFenceVioEvt;
+import com.ruoyi.bd.service.IBdFenceInfoService;
+import com.ruoyi.bd.service.IBdFenceVioEvtService;
+import com.ruoyi.bd.service.engine.EvtFusionEngine;
+import com.ruoyi.bd.service.engine.LocationInfo;
+import com.ruoyi.bd.socket.FenceVioEvtSocketServer;
+import com.ruoyi.common.BDConst;
+import com.ruoyi.common.core.redis.RedisCache;
+import com.ruoyi.common.enums.EvtStatus;
+import com.ruoyi.common.enums.EvtType;
+import com.ruoyi.common.enums.FenceType;
+import com.ruoyi.common.utils.DateTimeUtil;
+import com.ruoyi.common.utils.geo.GeoUtils;
+import com.ruoyi.web.core.config.MqttCfg;
+import net.dreamlu.iot.mqtt.spring.client.MqttClientTemplate;
+import org.apache.commons.lang3.ObjectUtils;
+import org.locationtech.jts.geom.Polygon;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * The type Room break in engine.
+ */
+@Service
+@ConditionalOnBean(MqttCfg.class)
+public class RoomBreakInEngine extends EvtFusionEngine {
+    private static final Logger logger = LoggerFactory.getLogger(RoomBreakInEngine.class);
+
+    @Autowired
+    private IBdFenceInfoService fenceInfoService;
+
+    @Autowired
+    private IBdFenceVioEvtService vioEvtService;
+
+    @Resource
+    private RedisCache redisCache;
+
+    @Autowired
+    private MqttClientTemplate client;
+
+    @Value("${evt-fusion.fusion-time.room-location:300}")
+    private int MAX_EVT_TIME_GAP;
+
+
+    @Autowired
+    private FenceVioEvtSocketServer fenceVioEvtSocketServer;
+
+    private final List<LinkedBlockingQueue<JSONObject>> messageQueueList = new ArrayList<>(5);
+
+    @PostConstruct
+    public void init() {
+        super.init();
+        this.setEngineName("室内围栏闯禁事件融合");
+        if (!redisCache.hasKey(BDConst.REDIS_KEY.FENCE_ROOM)) {
+            List<BdFenceInfo> bdFenceInfos = fenceInfoService.selectBdFenceInfoList(new BdFenceInfo() {{
+                setFenceType(FenceType.INSIDE_FENCE_IN.getCode());
+            }});
+            if (CollectionUtils.isEmpty(bdFenceInfos)) {
+                return;
+            }
+            bdFenceInfos.removeIf(item -> ObjectUtils.isEmpty(item.getPoly()) || ObjectUtils.isEmpty(GeoUtils.getPolygon(item.getPoly())));
+
+            redisCache.setCacheList(BDConst.REDIS_KEY.FENCE_ROOM, bdFenceInfos);
+        }
+    }
+
+    @Override
+    public Boolean check(LocationInfo older, LocationInfo newer) {
+        long gap = newer.getSrcTimestamp() - older.getSrcTimestamp();
+        // 将gap 转换为秒
+        gap = gap / 1000;
+
+        String oldFenceId = older.getMsg().getString("fenceId");
+        String newerFenceId = newer.getMsg().getString("fenceId");
+        return gap <= MAX_EVT_TIME_GAP && oldFenceId.equals(newerFenceId);
+    }
+
+    @Override
+    public List<LinkedBlockingQueue<JSONObject>> getQueue() {
+        return messageQueueList;
+    }
+
+
+    /**
+     * Gets key. 获取融合对象在redis中的key
+     *
+     * @param msg the msg
+     * @return the key
+     * @author chen.cheng
+     */
+    @Override
+    public String getKey(LocationInfo msg) {
+        return BDConst.REDIS_KEY.FENCE_ROOM_BREAK_IN_KEY + msg.getMsg().getString("deviceId");
+    }
+
+    @Override
+    public void getBizId(LocationInfo msg) {
+        BdFenceVioEvt bdFenceVioEvt = new BdFenceVioEvt();
+        bdFenceVioEvt.setFenceId(msg.getMsg().getLong("fenceId"));
+        bdFenceVioEvt.setLat(msg.getLatitude());
+        bdFenceVioEvt.setLng(msg.getLongitude());
+        bdFenceVioEvt.setEvtStatus(EvtStatus.NEW.getCode());
+        bdFenceVioEvt.setEvtType(EvtType.FENCE_ROOM_IN.getCode());
+        bdFenceVioEvt.setEvtTime(DateTimeUtil.getDateFromMills(msg.getSrcTimestamp()));
+        bdFenceVioEvt.setEvtDesc(String.format("室内围栏闯禁: %s", msg.getMsg().getString("fenceName")));
+        vioEvtService.insertBdFenceVioEvt(bdFenceVioEvt);
+        msg.setBizId(bdFenceVioEvt.getId().toString());
+    }
+
+    @Override
+    public void processOlderData(LocationInfo msg) {
+        vioEvtService.updateBdFenceVioEvt(new BdFenceVioEvt() {
+            {
+                setFenceId(msg.getMsg().getLong("fenceId"));
+                setEvtStatus(EvtStatus.DISAPPEAR.getCode());
+            }
+        });
+    }
+
+    @Override
+    public void newEvtCallback(LocationInfo msg) {
+        fenceVioEvtSocketServer.broadcast(msg);
+    }
+
+    public void generateEvt(JSONObject msg, byte[] payload) {
+        List<BdFenceInfo> cacheList = redisCache.getCacheList(BDConst.REDIS_KEY.FENCE_ROOM);
+        if (CollectionUtils.isEmpty(cacheList)) {
+            return;
+        }
+        Polygon polygon;
+        logger.info("fence info size: {}", msg);
+        for (BdFenceInfo fenceInfo : cacheList) {
+            polygon = GeoUtils.getPolygon(fenceInfo.getPoly());
+            if (GeoUtils.isPointInGeoFence(polygon, msg.getString("longitude"), msg.getString("latitude"))) {
+                logger.info("?>>>>>fence info size: {}", msg);
+                msg.put("fenceId", fenceInfo.getId());
+                msg.put("fenceName", fenceInfo.getDefenceName());
+                client.publish(BDConst.MQTT_TOPIC.EVT_ROOM_LOCATION_TOPIC, JSON.toJSONBytes(msg));
+                break;
+            }
+        }
+    }
+}

+ 6 - 0
bd-location/src/main/java/com/ruoyi/common/BDConst.java

@@ -5,12 +5,18 @@ public interface BDConst {
         String BD_POINT_KEY = "bd:point:";
         String FENCE_BREAK_IN_KEY = "fence:break:in:";
         String FENCE = "fence";
+
+        String FENCE_ROOM = "fence:room";
+
+        String FENCE_ROOM_BREAK_IN_KEY = "fence:room:break:in:";
     }
 
     public interface MQTT_TOPIC {
         String POINT_LOCATION_TOPIC = "/uwb/point/#";
         String EVT_LOCATION_TOPIC = "/uwb/evt";
 
+        String EVT_ROOM_LOCATION_TOPIC = "/uwb/room/evt";
+
         String DEVICE_LOCATION_TOPIC = "/uwb/point/%s";
 
     }

+ 3 - 1
bd-location/src/main/java/com/ruoyi/common/enums/EvtType.java

@@ -1,7 +1,9 @@
 package com.ruoyi.common.enums;
 
 public enum EvtType {
-    FENCE_IN("1", "围栏闯禁");
+    FENCE_IN("1", "围栏闯禁"),
+
+    FENCE_ROOM_IN("2", "室内围栏闯禁");
 
     private final String code;
     private final String info;

+ 22 - 0
bd-location/src/main/java/com/ruoyi/common/enums/FenceType.java

@@ -0,0 +1,22 @@
+package com.ruoyi.common.enums;
+
+public enum FenceType {
+    OUT_SIDE_FENCE_IN("1", "室外围栏闯禁"),
+    INSIDE_FENCE_IN("2", "室外围栏闯禁");
+
+    private final String code;
+    private final String info;
+
+    FenceType(String code, String info) {
+        this.code = code;
+        this.info = info;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public String getInfo() {
+        return info;
+    }
+}

+ 14 - 0
bd-location/src/main/java/com/ruoyi/mqtt/UWBLocationSubscribeListener.java

@@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSON;
 import com.alibaba.fastjson2.JSONObject;
 import com.ruoyi.bd.service.engine.impl.FenceBreakInEngine;
 import com.ruoyi.bd.service.engine.impl.PointFusionEngine;
+import com.ruoyi.bd.service.engine.impl.RoomBreakInEngine;
 import com.ruoyi.common.BDConst;
 import com.ruoyi.web.core.config.MqttCfg;
 import net.dreamlu.iot.mqtt.codec.MqttQoS;
@@ -29,10 +30,14 @@ public class UWBLocationSubscribeListener {
     @Autowired
     private FenceBreakInEngine fenceBreakInEngine;
 
+    @Autowired
+    private RoomBreakInEngine roomBreakInEngine;
+
     @PostConstruct
     public void init() {
         pointFusionEngine.start();
         fenceBreakInEngine.start();
+        roomBreakInEngine.start();
     }
 
     @MqttClientSubscribe(value = BDConst.MQTT_TOPIC.POINT_LOCATION_TOPIC)
@@ -41,6 +46,7 @@ public class UWBLocationSubscribeListener {
         JSONObject jsonObject = JSON.parseObject(payload);
         pointFusionEngine.push(jsonObject);
         fenceBreakInEngine.generateEvt(JSON.parseObject(payload), payload);
+        roomBreakInEngine.generateEvt(JSON.parseObject(payload), payload);
     }
 
     @MqttClientSubscribe(value = BDConst.MQTT_TOPIC.EVT_LOCATION_TOPIC, qos = MqttQoS.QOS0)
@@ -48,4 +54,12 @@ public class UWBLocationSubscribeListener {
         logger.debug("topic:{} payload:{}", topic, new String(payload, StandardCharsets.UTF_8));
         fenceBreakInEngine.push(JSON.parseObject(payload));
     }
+
+    @MqttClientSubscribe(value = BDConst.MQTT_TOPIC.EVT_ROOM_LOCATION_TOPIC, qos = MqttQoS.QOS0)
+    public void subQos2(String topic, byte[] payload) {
+        logger.debug("topic:{} payload:{}", topic, new String(payload, StandardCharsets.UTF_8));
+        roomBreakInEngine.push(JSON.parseObject(payload));
+    }
+
+
 }

+ 1 - 1
bd-location/src/main/resources/application-druid.yml

@@ -7,7 +7,7 @@ spring:
     # 端口,默认为6379
     port: 30013
     # 数据库索引
-    database: 1
+    database: 2
     # 密码
     password:
     # 连接超时时间

+ 4 - 1
bd-location/src/main/resources/application.yml

@@ -121,5 +121,8 @@ xss:
 evt-fusion:
   # 默认为false
   enabled: false
-  thread-pool-size: 5
+  thread-pool-size: 2
+  # 默认为300,单位为秒
+  fusion-time:
+    room-location: 30
 

+ 35 - 16
bd-location/src/main/resources/mapper/bd/BdFenceInfoMapper.xml

@@ -1,29 +1,43 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 <!DOCTYPE mapper
-PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
-"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.ruoyi.bd.mapper.BdFenceInfoMapper">
 
     <resultMap type="BdFenceInfo" id="BdFenceInfoResult">
-        <result property="id"    column="id"    />
-        <result property="defenceName"    column="defence_name"    />
-        <result property="poly"    column="poly"    />
-        <result property="centerLng"    column="center_lng"    />
-        <result property="centerLat"    column="center_lat"    />
-        <result property="updateTime"    column="update_time"    />
-        <result property="createTime"    column="create_time"    />
-        <result property="createBy"    column="create_by"    />
-        <result property="updateBy"    column="update_by"    />
+        <result property="id" column="id"/>
+        <result property="defenceName" column="defence_name"/>
+        <result property="poly" column="poly"/>
+        <result property="centerLng" column="center_lng"/>
+        <result property="centerLat" column="center_lat"/>
+        <result property="fenceType" column="fence_type"/>
+        <result property="updateTime" column="update_time"/>
+        <result property="createTime" column="create_time"/>
+        <result property="createBy" column="create_by"/>
+        <result property="updateBy" column="update_by"/>
     </resultMap>
 
     <sql id="selectBdFenceInfoVo">
-        select id, defence_name, ST_AsText(poly) poly, center_lng, center_lat, update_time, create_time, create_by, update_by from bd_fence_info
+        select id,
+               defence_name,
+               ST_AsText(poly) poly,
+               center_lng,
+               center_lat,
+               fence_type,
+               update_time,
+               create_time,
+               create_by,
+               update_by
+        from bd_fence_info
     </sql>
 
     <select id="selectBdFenceInfoList" parameterType="BdFenceInfo" resultMap="BdFenceInfoResult">
         <include refid="selectBdFenceInfoVo"/>
         <where>
-            <if test="defenceName != null  and defenceName != ''"> and defence_name like concat('%', #{defenceName}, '%')</if>
+            <if test="defenceName != null  and defenceName != ''">and defence_name like concat('%', #{defenceName},
+                '%')
+            </if>
+            <if test="fenceType != null  and fenceType != ''">and fence_type = #{fenceType}</if>
         </where>
     </select>
 
@@ -39,21 +53,23 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="poly != null">poly,</if>
             <if test="centerLng != null">center_lng,</if>
             <if test="centerLat != null">center_lat,</if>
+            <if test="fenceType !=null">fence_type,</if>
             <if test="updateTime != null">update_time,</if>
             <if test="createTime != null">create_time,</if>
             <if test="createBy != null">create_by,</if>
             <if test="updateBy != null">update_by,</if>
-         </trim>
+        </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="defenceName != null">#{defenceName},</if>
             <if test="poly != null">ST_GeomFromText(#{poly}),</if>
             <if test="centerLng != null">#{centerLng},</if>
             <if test="centerLat != null">#{centerLat},</if>
+            <if test="fenceType != null">#{fenceType},</if>
             <if test="updateTime != null">#{updateTime},</if>
             <if test="createTime != null">#{createTime},</if>
             <if test="createBy != null">#{createBy},</if>
             <if test="updateBy != null">#{updateBy},</if>
-         </trim>
+        </trim>
     </insert>
 
     <update id="updateBdFenceInfo" parameterType="BdFenceInfo">
@@ -63,6 +79,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="poly != null">poly = ST_GeomFromText(#{poly}),</if>
             <if test="centerLng != null">center_lng = #{centerLng},</if>
             <if test="centerLat != null">center_lat = #{centerLat},</if>
+            <if test="fenceType != null">fence_type = #{fenceType},</if>
             <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="createTime != null">create_time = #{createTime},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
@@ -72,7 +89,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </update>
 
     <delete id="deleteBdFenceInfoById" parameterType="Long">
-        delete from bd_fence_info where id = #{id}
+        delete
+        from bd_fence_info
+        where id = #{id}
     </delete>
 
     <delete id="deleteBdFenceInfoByIds" parameterType="String">

+ 1 - 1
bd-location/version

@@ -1 +1 @@
-2.0
+2.3

+ 1 - 1
ruoyi-ui/deploy.sh

@@ -37,7 +37,7 @@ fi
 # 更新版本号
 NEXT_VERSION="${CURRENT_MAJOR}.${CURRENT_MINOR}"
 echo $NEXT_VERSION > $VERSION_FILE
-echo -e "\e[1;34;42m版本号:" $NEXT_VERSION "\e[0m"
+echo  -e "\e[1;34;42m版本号:" $NEXT_VERSION " 日期:$(date +"%Y-%m-%d %H:%M:%S")" "\e[0m"
 # 用户选择执行的步骤
 echo "请选择要执行的步骤(输入数字):"
 echo "1. 编译bd-app项目"

+ 2 - 1
ruoyi-ui/package.json

@@ -63,7 +63,8 @@
     "vue-json-editor": "^1.4.3",
     "vue-meta": "2.4.0",
     "vue-router": "3.4.9",
-    "vuedraggable": "2.24.3",
+    "vue-video-player": "^5.0.1",
+    "vuedraggable": "^2.24.3",
     "vuex": "3.6.0"
   },
   "devDependencies": {

+ 55 - 0
ruoyi-ui/src/main.js

@@ -38,6 +38,14 @@ import VueMeta from 'vue-meta'
 // 字典数据组件
 import DictData from '@/components/DictData'
 
+import VideoPlayer from 'vue-video-player'
+
+import 'video.js/dist/video-js.css'
+
+import 'vue-video-player/src/custom-theme.css'
+
+Vue.use(VideoPlayer)
+
 // 全局方法挂载
 Vue.prototype.getDicts = getDicts
 Vue.prototype.getConfigKey = getConfigKey
@@ -57,7 +65,54 @@ Vue.component('Editor', Editor)
 Vue.component('FileUpload', FileUpload)
 Vue.component('ImageUpload', ImageUpload)
 Vue.component('ImagePreview', ImagePreview)
+Vue.directive('drag', (el) => {
+  const oDiv = el // 当前元素
+  const minTop = oDiv.getAttribute('drag-min-top')
+  const ifMoveSizeArea = 20
+  oDiv.onmousedown = e => {
+    let target = oDiv
+    while (window.getComputedStyle(target).position !== 'absolute' && target !== document.body) {
+      target = target.parentElement
+    }
 
+    document.onselectstart = () => {
+      return false
+    }
+    if (!target.getAttribute('init_x')) {
+      target.setAttribute('init_x', target.offsetLeft)
+      target.setAttribute('init_y', target.offsetTop)
+    }
+
+    const initX = parseInt(target.getAttribute('init_x'))
+    const initY = parseInt(target.getAttribute('init_y'))
+
+    // 鼠标按下,计算当前元素距离可视区的距离
+    const disX = e.clientX - target.offsetLeft
+    const disY = e.clientY - target.offsetTop
+    document.onmousemove = e => {
+      // 通过事件委托,计算移动的距离
+      // 因为浏览器里并不能直接取到并且使用clientX、clientY,所以使用事件委托在内部做完赋值
+      const l = e.clientX - disX
+      const t = e.clientY - disY
+      const { marginTop: mt, marginLeft: ml } = window.getComputedStyle(target)
+      // 计算移动当前元素的位置,并且给该元素样式中的left和top值赋值
+      target.style.left = l - parseInt(ml) + 'px'
+      target.style.top = (t < minTop ? minTop : t) - parseInt(mt) + 'px'
+      if (Math.abs(l - initX) > ifMoveSizeArea || Math.abs(t - initY) > ifMoveSizeArea) {
+        target.setAttribute('dragged', '')
+      } else {
+        target.removeAttribute('dragged')
+      }
+    }
+    document.onmouseup = e => {
+      document.onmousemove = null
+      document.onmouseup = null
+      document.onselectstart = null
+    }
+    // return false不加的话可能导致黏连,拖到一个地方时div粘在鼠标上不下来,相当于onmouseup失效
+    return false
+  }
+})
 Vue.use(directive)
 Vue.use(plugins)
 Vue.use(VueMeta)

+ 22 - 0
ruoyi-ui/src/utils/BDConst.js

@@ -0,0 +1,22 @@
+export const FenceType = {
+  OUTSIDE: {
+    value: "1",
+    label: '室外围栏'
+  },
+  INSIDE: {
+    value: "2",
+    label: '室内围栏'
+  },
+}
+
+export const EvtType = {
+  OUTSIDE: {
+    value: "1",
+    label: '围栏闯禁'
+  },
+  INSIDE: {
+    value: "2",
+    label: '室内闯禁'
+  }
+
+}

+ 12 - 0
ruoyi-ui/src/views/bd/fence/index.scss

@@ -1,2 +1,14 @@
 .fence-location-container {
+  .el-tabs__item {
+    color: #fefefe;
+  }
+
+  .el-tabs__item.is-active {
+    color: #1890ff;
+  }
+
+  .el-tab-pane {
+
+  }
 }
+

+ 109 - 80
ruoyi-ui/src/views/bd/fence/index.vue

@@ -1,63 +1,70 @@
 <template>
   <pannel class="fence-location-container">
     <template v-slot:icon>
-      <svg-icon icon-class="bd_fence" />
+      <svg-icon icon-class="bd_fence"/>
     </template>
     <template v-slot:title>
       围栏闯禁
     </template>
     <template v-slot:action>
       <el-button
-          size="mini"
-          type="text"
-          icon="el-icon-plus"
-          round
-          @click="startDraw"
-          style="margin-left: auto"
-          :disabled="drawState"
+        size="mini"
+        type="text"
+        icon="el-icon-plus"
+        round
+        @click="startDraw"
+        style="margin-left: auto"
+        :disabled="drawState"
       >
         {{ drawState ? '绘制中...' : '绘制围栏' }}
       </el-button>
     </template>
     <template v-slot:content>
+      <el-tabs v-model="activeName" @tab-click="handleClick">
+        <el-tab-pane label="室外围栏" :name="Tabs.outside.id">
+        </el-tab-pane>
+        <el-tab-pane label="室内围栏" :name="Tabs.inside.id">
+          <room-layer v-if="activeName === Tabs.inside.id"/>
+        </el-tab-pane>
+      </el-tabs>
       <div class="location-list">
         <template v-for="fence in fenceList">
           <div :key="`fence_${fence.id}`" class="list-item">
-          <span class="over-flow-hidden" style="width: 40%">
-            {{ fence.name }}
-          </span>
             <span class="over-flow-hidden" style="width: 40%">
-           {{ fence.updateTime }}
-          </span>
+              {{ fence.name }}
+            </span>
+            <span class="over-flow-hidden" style="width: 40%">
+             {{ fence.updateTime }}
+            </span>
             <span class="over-flow-hidden" style="width: 20%">
               <el-popconfirm
-                  confirm-button-text='好的'
-                  cancel-button-text='不用了'
-                  icon="el-icon-info"
-                  icon-color="red"
-                  :title="`是否删除围栏【${fence.name}】?`"
-                  @confirm="()=>delFence(fence)"
+                confirm-button-text='好的'
+                cancel-button-text='不用了'
+                icon="el-icon-info"
+                icon-color="red"
+                :title="`是否删除围栏【${fence.name}】?`"
+                @confirm="()=>delFence(fence)"
               >
                <i
-                   slot="reference"
-                   class="el-icon-delete"
-                   title="删除围栏"
+                 slot="reference"
+                 class="el-icon-delete"
+                 title="删除围栏"
                ></i>
               </el-popconfirm>
-            <i
+              <i
                 class="el-icon-edit"
                 title="编辑围栏"
                 @click="()=>editFence(fence)"
-            />
-          </span>
+              />
+            </span>
           </div>
         </template>
       </div>
       <el-dialog
-          width="20%"
-          title="提示"
-          :visible="dialogVisible"
-          append-to-body>
+        width="20%"
+        title="提示"
+        :visible="dialogVisible"
+        append-to-body>
         <div>
           <div style="margin-bottom: 14px">
             检测到未保存的内容,是否保存修改?
@@ -79,22 +86,35 @@
 
 <script>
 
-import { addFenceInfo, listFenceInfo, updateFenceInfo } from '@/api/bd/fenceInfo';
+import {addFenceInfo, listFenceInfo, updateFenceInfo} from '@/api/bd/fenceInfo';
 import maphandle from '@/views/bd/map/maphandle';
 import Pannel from '@/views/bd/pannel/index.vue';
-// this.drawtool = new BDLayers.Lib.Tools.CBDrawTool('mytool', this.mapView, 'Rectangle', true); // 绘制矩形,参数1:id,参数2:地图,参数3:绘制类型,参数4:是否可拖拽编辑
-// this.drawtool.enable(); // 开始绘制
-// this.drawtool.disable(); // 结束绘制
-// this.drawtool.clear(); // 清除绘制内容
-// this.drawtool.setDrawMode(type); // 设置绘制类型
-// this.drawtool.on('drawend', (geom) => {}) // 绘制结束事件
-// this.drawtool.on('selectDraw', geom => {}) // 绘制完毕后选择绘制图形
+import {FenceType} from "@/utils/BDConst";
+import RoomLayer from "@/views/bd/rooms/index.vue";
+
+const Tabs = {
+  outside: {
+    id: 'outside',
+    param: {
+      fenceType: FenceType.OUTSIDE.value,
+    },
+  },
+  inside: {
+    id: 'inside',
+    param: {
+      fenceType: FenceType.INSIDE.value,
+    },
+  },
+}
+
 export default {
   name: 'fence',
-  components: { Pannel },
+  components: {RoomLayer, Pannel},
   mixins: [maphandle],
   data() {
     return {
+      Tabs,
+      activeName: Tabs.outside.id,
       playItem: {},
       editState: false,
       editPolyInfo: {},
@@ -129,8 +149,8 @@ export default {
     // 监听图形编辑
     this.drawtool.on('selectDraw', geom => {
       this.editingDrawGeom = geom.target.geometry ? geom.target.geometry : geom.target.geom
-          ? geom.target.geom
-          : geom.target;
+        ? geom.target.geom
+        : geom.target;
       if (this.editingDrawGeom.isEditing && this.editingDrawGeom.isEditing()) {
         // 点击地图 图形取消编辑状态
         window.map.map.once('click', () => {
@@ -145,25 +165,30 @@ export default {
     });
     this.drawtool.on('drawend', (geom) => {
       this.editingDrawGeom = geom.target.geometry ? geom.target.geometry : geom.target.geom
-          ? geom.target.geom
-          : geom.target;
+        ? geom.target.geom
+        : geom.target;
       this.dialogVisible = true;
       this.drawState = false;
     });
+
+
   },
   mounted() {
-    this.getFenceList();
+    this.getFenceList({});
   },
   methods: {
-    async getFenceList() {
-      const { rows } = await listFenceInfo({
+    async getFenceList(params) {
+      const {rows} = await listFenceInfo({
         pageNum: 1,
         pageSize: 10,
+        fenceType: 1,
+        ...params
       });
       if (!rows || rows.length < 1) {
         return;
       }
       const result = [];
+      this.clearLayer();
       rows.forEach(item => {
         const {
           id,
@@ -269,6 +294,10 @@ export default {
       this.drawState = true;
       this.drawtool.enable();
     },
+    handleClick(tab) {
+      this.activeName = tab.name;
+      this.getFenceList(Tabs[tab.name].param);
+    },
     /**
      *
      * @param coordinates
@@ -286,12 +315,12 @@ export default {
      * @returns {BDLayers.Lib.Overlays.Polygon}
      */
     custDrawPoly({
-      name = '多边形',
-      coordinates,
-      symbol = {},
-      bizAttr = {},
-      labelSymbol = {},
-    }) {
+                   name = '多边形',
+                   coordinates,
+                   symbol = {},
+                   bizAttr = {},
+                   labelSymbol = {},
+                 }) {
       return this.drawPoly({
         name,
         coordinates,
@@ -299,37 +328,37 @@ export default {
         bizAttr,
         labelSymbol,
         polyOnClick: (data) => {
-        console.log(data.target.options);
-        if (this.editState) {
-          return;
-        }
-        this.form.name = data.target.options.bizAttr.name;
-        this.editingDrawGeom = data.target.geometry ? data.target.geometry : data.target.geom
+          console.log(data.target.options);
+          if (this.editState) {
+            return;
+          }
+          this.form.name = data.target.options.bizAttr.name;
+          this.editingDrawGeom = data.target.geometry ? data.target.geometry : data.target.geom
             ? data.target.geom
             : data.target;
-        this.$confirm('检测到选中了围栏,请选择操作类型?', '提示', {
-          confirmButtonText: '编辑围栏',
-          cancelButtonText: '删除围栏',
-          distinguishCancelAndClose: true,
-          type: 'warning',
-        }).then(() => {
-          // 开始编辑围栏
-          this.editState = true;
-          this.editingDrawGeom.startEdit();
-        }).catch(() => {
-          this.editingDrawGeom.remove();
-          this.editState = false;
-          this.editingDrawGeom = null;
-          this.$message({
-            type: 'info',
-            message: '删除围栏',
+          this.$confirm('检测到选中了围栏,请选择操作类型?', '提示', {
+            confirmButtonText: '编辑围栏',
+            cancelButtonText: '删除围栏',
+            distinguishCancelAndClose: true,
+            type: 'warning',
+          }).then(() => {
+            // 开始编辑围栏
+            this.editState = true;
+            this.editingDrawGeom.startEdit();
+          }).catch(() => {
+            this.editingDrawGeom.remove();
+            this.editState = false;
+            this.editingDrawGeom = null;
+            this.$message({
+              type: 'info',
+              message: '删除围栏',
+            });
+          });
+          // 点击地图 图形取消编辑状态
+          window.map.map.once('click', () => {
+            this.editingDrawGeom.endEdit();
+            this.dialogVisible = true;
           });
-        });
-        // 点击地图 图形取消编辑状态
-        window.map.map.once('click', () => {
-          this.editingDrawGeom.endEdit();
-          this.dialogVisible = true;
-        });
         },
       })
     },
@@ -343,4 +372,4 @@ export default {
   },
 };
 </script>
-<style lang="scss" src="./index.scss" />
+<style lang="scss" src="./index.scss"/>

+ 55 - 15
ruoyi-ui/src/views/bd/fenceEvt/index.vue

@@ -1,7 +1,7 @@
 <template>
   <pannel class="fence-location-container">
     <template v-slot:icon>
-      <svg-icon icon-class="bd_evt" />
+      <svg-icon icon-class="bd_evt"/>
     </template>
     <template v-slot:title>
       围栏闯禁事件
@@ -10,6 +10,13 @@
 
     </template>
     <template v-slot:content>
+      <el-tabs v-model="activeName" @tab-click="handleClick">
+        <el-tab-pane label="室外围栏" :name="Tabs.outside.id">
+        </el-tab-pane>
+        <el-tab-pane label="室内围栏" :name="Tabs.inside.id">
+          <room-layer v-if="activeName === Tabs.inside.id"/>
+        </el-tab-pane>
+      </el-tabs>
       <div class="location-list">
         <template v-for="evt in evtList">
           <div :key="`evt_${evt.id}`" class="list-item">
@@ -19,8 +26,8 @@
             <span class="over-flow-hidden" style="width: 40%">
            {{ dayjs(evt.evtTime).format('YYYY-MM-DD HH:mm') }}
           </span>
-<!--            <span class="over-flow-hidden" style="width: 20%">-->
-<!--          </span>-->
+            <!--            <span class="over-flow-hidden" style="width: 20%">-->
+            <!--          </span>-->
           </div>
         </template>
         <socket-message :onMessage="onMessage" :ws="ws"></socket-message>
@@ -31,28 +38,50 @@
 
 <script>
 
-import { listFenceInfo } from '@/api/bd/fenceInfo';
-import { listFenceVioEvt } from '@/api/bd/fenceVioEvt';
+import {listFenceInfo} from '@/api/bd/fenceInfo';
+import {listFenceVioEvt} from '@/api/bd/fenceVioEvt';
 import SocketMessage from '@/components/WebsocketMessage/index.vue';
 import maphandle from '@/views/bd/map/maphandle';
 import Pannel from '@/views/bd/pannel/index.vue';
 import FingerprintJS from '@fingerprintjs/fingerprintjs';
 import dayjs from 'dayjs';
 import evt_vio from './icon/evt_vio.svg';
+import RoomLayer from "@/views/bd/rooms/index.vue";
+import {EvtType, FenceType} from "@/utils/BDConst";
 
+const Tabs = {
+  outside: {
+    id: 'outside',
+    param: {
+      evtType: EvtType.OUTSIDE.value,
+      fenceType: FenceType.OUTSIDE.value
+    },
+  },
+  inside: {
+    id: 'inside',
+    param: {
+      evtType: EvtType.INSIDE.value,
+      fenceType: FenceType.INSIDE.value,
+    },
+  },
+}
 export default {
   name: 'fenceVioEvt',
   mixins: [maphandle],
   components: {
+    RoomLayer,
     SocketMessage,
     Pannel,
   },
   data() {
     return {
+      Tabs,
+      activeName: Tabs.outside.id,
       evtList: [],
       fp: null,
       ws: null,
       markLayer: null,
+      listParam: Tabs.outside.param,
     };
   },
   // 组件卸载前清空图层信息
@@ -61,7 +90,7 @@ export default {
   },
   created() {
     this.markLayer = new BDLayers.Lib.Layer.CBVectorLayer('real_time_markerLayer', true);
-    window.map.addCustomLayers(this.markLayer,5);
+    window.map.addCustomLayers(this.markLayer, 5);
   },
   mounted() {
     this.getFenceVioEvtList();
@@ -70,10 +99,18 @@ export default {
   },
   methods: {
     dayjs,
+    handleClick(tab) {
+      this.listParam = Tabs[tab.name].param;
+      this.getFenceVioEvtList();
+      this.getFenceList();
+    },
     async getFenceVioEvtList() {
-      const { rows } = await listFenceVioEvt({
+      this.markLayer.removeAll();
+      const {rows} = await listFenceVioEvt({
         pageNum: 1,
         pageSize: 10,
+        fenceType: 1,
+        ...this.listParam,
       });
       this.evtList = rows;
       this.addEvtToMap();
@@ -85,20 +122,23 @@ export default {
           lat,
         } = item;
         var marker = new BDLayers.Lib.Overlays.MarkerImg(
-            `marker0${i}`,
-            [lng, lat],
-            {
-              imgurl: evt_vio,
-              iconSize: [45, 45],
-            },
+          `marker0${i}`,
+          [lng, lat],
+          {
+            imgurl: evt_vio,
+            iconSize: [45, 45],
+          },
         );
         this.markLayer.addMarker(marker);
       });
     },
     async getFenceList() {
-      const { rows } = await listFenceInfo({
+      this.clearLayer()
+      const {rows} = await listFenceInfo({
         pageNum: 1,
         pageSize: 10,
+        fenceType: 1,
+        ...this.listParam
       });
       if (!rows || rows.length < 1) {
         return;
@@ -176,4 +216,4 @@ export default {
   },
 };
 </script>
-<style lang="scss" src="./index.scss" />
+<style lang="scss" src="./index.scss"/>

+ 39 - 11
ruoyi-ui/src/views/bd/map/maphandle.js

@@ -1,30 +1,32 @@
-import { uuid } from '@/utils';
+import {uuid} from '@/utils';
 
 export default {
   data() {
     return {
       polyLayer: null,
       layerId: 'vl',
+
     };
   },
   mounted() {
     this.layerId = uuid();
     if (!this.polyLayer) {
       this.polyLayer = new BDLayers.Lib.Layer.CBVectorLayer(
-        `poly_layer${this.layerId}`, { enableAltitude: true });
+        `poly_layer${this.layerId}`, {enableAltitude: true});
       window.map.addCustomLayers(this.polyLayer, 1);
     }
   },
   methods: {
     drawPoly({
-      name = '多边形',
-      coordinates,
-      altitude = 0,
-      symbol = {},
-      bizAttr = {},
-      labelSymbol = {},
-      polyOnClick = () => {},
-    }) {
+               name = '多边形',
+               coordinates,
+               altitude = 0,
+               symbol = {},
+               bizAttr = {},
+               labelSymbol = {},
+               polyOnClick = () => {
+               },
+             }, polyLayer) {
       let symbolParam = {
         lineColor: '#34495e',
         lineWidth: 2,
@@ -47,7 +49,11 @@ export default {
       });
       //多边形的点击事件
       polygon.on('click', polyOnClick);
-      this.polyLayer.addGeometry(polygon);
+      if (!polyLayer) {
+        this.polyLayer.addGeometry(polygon);
+      } else {
+        polyLayer.addGeometry(polygon);
+      }
       return polygon;
     },
     polygonToCoordinates(polygon) {
@@ -63,6 +69,28 @@ export default {
       }
       return [];
     },
+    createLayer(mapIns) {
+      const uid = uuid();
+      const layerId = `poly_layer${uid}`;
+      const polyLayer = new BDLayers.Lib.Layer.CBVectorLayer(
+        layerId, {enableAltitude: true});
+      polyLayer.cust = {
+        layerId: uid,
+        mapIns,
+        clearLayer: (layer) => {
+          mapIns.removeLayersById(layerId);
+        }
+      }
+      mapIns.addCustomLayers(polyLayer, 1);
+      return polyLayer;
+    },
+    clearLayer(layer) {
+      if (layer) {
+        layer.removeAll();
+        return;
+      }
+      this.polyLayer && this.polyLayer.removeAll();
+    },
   },
   beforeDestroy() {
     this.polyLayer && window.map.removeLayersById(`poly_layer${this.layerId}`);

+ 46 - 70
ruoyi-ui/src/views/bd/realtimeLocation/index.vue

@@ -1,7 +1,7 @@
 <template>
   <pannel class="fence-location-container">
     <template v-slot:icon>
-      <svg-icon icon-class="bd_real_time" />
+      <svg-icon icon-class="bd_real_time"/>
     </template>
     <template v-slot:title>
       实时坐标
@@ -43,7 +43,7 @@
 
 <script>
 
-import { pushDevcLocation } from '@/api/bd/fenceInfo';
+import {pushDevcLocation} from '@/api/bd/fenceInfo';
 import SocketMessage from '@/components/WebsocketMessage/index.vue';
 import item from '@/layout/components/Sidebar/Item.vue';
 import Pannel from '@/views/bd/pannel/index.vue';
@@ -158,80 +158,56 @@ export default {
     },
     play() {
       this.playInterval && clearInterval(this.playInterval);
-      const gps = [
-        [118.868474555524, 32.013913750075],
-        [118.868459917135, 32.0138560330253],
-        [118.868460650064, 32.0137600494265],
-        [118.868469791963, 32.0135491295906],
-        [118.868450597046, 32.0135969285238],
-        [118.868451821878, 32.0134372342673],
-        [118.868369085172, 32.0134271059761],
-        [118.868229813542, 32.0134261784667],
-        [118.868037844538, 32.0134249000077],
-        [118.867988911263, 32.013424574126],
-        [118.867990212059, 32.0132809893746],
-        [118.867986825628, 32.0132395072755],
-        [118.867987028075, 32.0132171885197],
-        [118.867987432888, 32.0131725599655],
-        [118.867984539806, 32.0130769425047],
-        [118.867977454441, 32.0130291167663],
-        [118.86797791828, 32.0129781711549],
-        [118.867978150146, 32.0129527041872],
-        [118.867974855578, 32.0129017568948],
-        [118.868046176274, 32.0129117791912],
-        [118.868121507934, 32.0128931867922],
-        [118.868117943319, 32.0128708893801],
-        [118.868126616248, 32.012737367597],
-        [118.868127634971, 32.0126197858525],
-        [118.868127662494, 32.0126166091195],
-        [118.868165320753, 32.0126041528561],
-        [118.868173101262, 32.0125724412675],
-        [118.868240681514, 32.0125728913369],
-        [118.868308235972, 32.0125765173023],
-        [118.868334874981, 32.0125322352762],
-        [118.868324305199, 32.012446455205],
-        [118.868361835438, 32.0124467051483],
-        [118.868364679658, 32.0125610134717],
-        [118.868492254263, 32.0125713907483],
-        [118.868541014618, 32.0125780675563],
-        [118.868732580782, 32.012566639437],
-        [118.868841456955, 32.0125673645282],
-        [118.868976574771, 32.0125746162152],
-        [118.869070453844, 32.0125720654824],
-        [118.869096734551, 32.0125722405062],
-        [118.869156822422, 32.0125694647922],
-        [118.869179313588, 32.0125759664106],
-        [118.869201684821, 32.0126047016424],
-        [118.869186178487, 32.0126935647542],
-        [118.869193065594, 32.0128080658432],
-        [118.869192510216, 32.0129098661745],
-        [118.86919198924, 32.0130053604978],
-        [118.869179854563, 32.0131613771548],
-        [118.869190250492, 32.0133240705021],
-        [118.869185964264, 32.0134197772962],
-        [118.86917071489, 32.0134547924615],
-        [118.869159014503, 32.0135281642937],
-        [118.869166174307, 32.0135953030259],
-        [118.869165521059, 32.0137135723353],
-        [118.869187426642, 32.0138416759025],
-        [118.869153188005, 32.0138990608231],
-        [118.868990969654, 32.0139203908971],
-        [118.868768561862, 32.0139189097324],
-        [118.868572541435, 32.0139176042992],
-        [118.868557462941, 32.0139175038812],
-        [118.868474555524, 32.013913750075],
-      ];
+      const gps =
+          [
+            [118.868446580271, 32.0123378608057],
+            [118.868406952344, 32.0123370689339],
+            [118.868366815026, 32.0123546053505],
+            [118.868358676065, 32.0123880632266],
+            [118.868350452205, 32.0124245758027],
+            [118.86834900893, 32.0124765058939],
+            [118.868344642304, 32.012503926295],
+            [118.868332136717, 32.0125648044994],
+            [118.868320565014, 32.0125920808978],
+            [118.86830547567, 32.0126162305943],
+            [118.868297336709, 32.0126496883748],
+            [118.868296572622, 32.0126771807122],
+            [118.868295214246, 32.0127260559583],
+            [118.868293601174, 32.0127840952791],
+            [118.868289064751, 32.0128176249864],
+            [118.868281265384, 32.012838863901],
+            [118.868280671094, 32.0128602467866],
+            [118.868290375029, 32.0129001738105],
+            [118.86833686844, 32.0129133284373],
+            [118.868358313875, 32.0129198697561],
+            [118.868408240027, 32.0129392057582],
+            [118.868415360206, 32.0129424044289],
+            [118.868425828228, 32.012954839171],
+            [118.86843277861, 32.0129641472296],
+            [118.868435786859, 32.0129856020738],
+            [118.868442737241, 32.0129949101293],
+            [118.868445915288, 32.0130102555814],
+            [118.868445066302, 32.0130408025022],
+            [118.868447395363, 32.013086694852],
+            [118.868446716175, 32.0131111323691],
+            [118.868446036987, 32.0131355698797],
+            [118.868444933306, 32.0131752808204],
+            [118.868447517062, 32.0132120090459],
+            [118.868446668077, 32.0132425558994],
+            [118.868445479497, 32.0132853214773],
+            [118.868463492191, 32.0132856814153],
+            [118.868506722657, 32.0132865452666]
+          ];
       let i = 0;
       this.playInterval = setInterval(() => {
+        if (i === gps.length - 1) {
+          return;
+        }
         pushDevcLocation({
           devcKey: 'uwb001',
           lat: gps[i][1],
           lng: gps[i][0],
         });
-        if (i === gps.length - 1) {
-          i = 0;
-          return;
-        }
         i++;
       }, 2000);
 
@@ -239,4 +215,4 @@ export default {
   },
 };
 </script>
-<style lang="scss" src="./index.scss" />
+<style lang="scss" src="./index.scss"/>

+ 99 - 82
ruoyi-ui/src/views/bd/roomlocation/roommap/index.vue

@@ -6,30 +6,33 @@
       @closed="onDialogClose"
       @opened="play"
       center>
-    <div style="width: 100%;height: 70vh;">
-      <bd-map :loaded="loaded" map-id="room-map" />
+    <div style="width: 100%;height: 70vh;position: relative">
+      <bd-map :loaded="loaded" map-id="room-map"/>
+      <video-p/>
       <socket-message
           v-if="centerDialogVisible"
           :key="`device_uwb001`" :onMessage="onMessage"
           :ws="`/ws/point/uwb001`"></socket-message>
     </div>
-
   </el-dialog>
 </template>
 
 <script>
 
-import { pushDevcLocation } from '@/api/bd/fenceInfo';
+import {listFenceInfo, pushDevcLocation} from '@/api/bd/fenceInfo';
 import SocketMessage from '@/components/WebsocketMessage/index.vue';
 import BdMap from '@/views/bd/map/index.vue';
 import maphandle from '@/views/bd/map/maphandle';
 import position from '@/views/bd/realtimeLocation/icon/position.png';
 import dayjs from 'dayjs';
+import VideoP from "@/views/bd/roomlocation/roommap/video/video.vue";
+import {uuid} from "@/utils";
 
 export default {
   name: 'room-map',
   mixins: [maphandle],
   components: {
+    VideoP,
     BdMap,
     SocketMessage,
   },
@@ -39,18 +42,19 @@ export default {
       locationMarkers: {},
       mapIns: null,
       playInterval: null,
+      fencePolyLayer: null,
     };
   },
   // 组件卸载前清空图层信息
   beforeDestroy() {
-    this.markLayer && window.map.removeLayersById('markerLayer');
+    this.markLayer && this.mapIns.removeLayersById('markerLayer');
+    this.fencePolyLayer && this.fencePolyLayer.cust.clearLayer()
     this.playInterval && clearInterval(this.playInterval);
   },
   created() {
 
   },
   mounted() {
-
   },
   methods: {
     dayjs,
@@ -68,7 +72,9 @@ export default {
       map.addCustomLayers(this.markLayer);
       this.mapIns = map;
       map.setPitch(0);
-      map.setCenter([118.86895,32.01326]);
+      map.setCenter([118.86895, 32.01326]);
+      this.fencePolyLayer = this.createLayer(map);
+      this.getFenceList();
     },
     show() {
       this.centerDialogVisible = true;
@@ -97,6 +103,43 @@ export default {
       this.markLayer.addMarker(marker);
       return marker;
     },
+    async getFenceList() {
+      const {rows} = await listFenceInfo({
+        pageNum: 1,
+        pageSize: 10,
+        fenceType: 2,
+      });
+      if (!rows || rows.length < 1) {
+        return;
+      }
+      const result = [];
+      rows.forEach(item => {
+        const {
+          id,
+          defenceName,
+          poly,
+        } = item;
+        const polygon = this.drawPoly({
+          name: defenceName,
+          coordinates: this.polygonToCoordinates(poly),
+          symbol: {
+            lineColor: 'rgba(241,0,23,0.49)',
+            lineWidth: 2,
+            polygonFill: 'rgba(241,0,23,0.49)',
+            polygonOpacity: 0.4,
+          },
+          bizAttr: {
+            id,
+            name: defenceName,
+          },
+        }, this.fencePolyLayer);
+        result.push({
+          id,
+          name: defenceName,
+          polygon,
+        });
+      });
+    },
     onMessage(a) {
       const data = JSON.parse(a.data);
       const {
@@ -117,81 +160,41 @@ export default {
       }
       this.mapIns.setZoom(22)
       const gps = [
-        [118.869125003231, 32.0132579193923],
-        [118.869114609672, 32.0132582036815],
-        [118.869115950776, 32.013238303438],
-        [118.869116286052, 32.0132195403474],
-        [118.869116286052, 32.0132144231402],
-        [118.869113939119, 32.0132064630396],
-        [118.869105557216, 32.0132021986996],
-        [118.869093822552, 32.0132021986996],
-        [118.869086111201, 32.0132021986996],
-        [118.869077394021, 32.0132021986996],
-        [118.869073370708, 32.0132021986996],
-        [118.869070688499, 32.0132044730143],
-        [118.869070353223, 32.013218118901],
-        [118.869070353223, 32.0132323333641],
-        [118.869071359051, 32.0132479692711],
-        [118.869069012118, 32.0132536550547],
-        [118.869064988805, 32.0132579193923],
-        [118.869048224998, 32.0132570665248],
-        [118.869028778983, 32.0132570665248],
-        [118.869008327139, 32.0132525178981],
-        [118.868998939408, 32.0132579193923],
-        [118.868995586647, 32.0132604779947],
-        [118.868992233885, 32.0132604779947],
-        [118.868991228057, 32.0132621837296],
-        [118.868982510878, 32.0132601937055],
-        [118.868976140631, 32.0132601937055],
-        [118.868968764556, 32.0132607622839],
-        [118.868963735414, 32.0132607622839],
-        [118.868965411795, 32.0132525178981],
-        [118.868965747071, 32.0132394405949],
-        [118.868965747071, 32.0132334705211],
-        [118.868966082347, 32.0132127174044],
-        [118.868965747071, 32.0132036201463],
-        [118.868976811183, 32.0132002086742],
-        [118.868985193087, 32.0132056101716],
-        [118.868987204743, 32.0132175503224],
-        [118.86898485781, 32.0132354605458],
-        [118.868985528363, 32.0132402934625],
-        [118.868999274684, 32.0132445578007],
-        [118.869013021005, 32.0132439892223],
-        [118.869029784811, 32.0132454106683],
-        [118.869033472849, 32.0132431363547],
-        [118.869036155058, 32.0132391563057],
-        [118.869036155058, 32.0132320490749],
-        [118.869037160886, 32.0132215303724],
-        [118.869035484506, 32.0132067473289],
-        [118.869035484506, 32.0132002086742],
-        [118.86902039708, 32.0131987872275],
-        [118.869002962721, 32.0131993558062],
-        [118.868989551676, 32.0131993558062],
-        [118.868975805355, 32.0131982186489],
-        [118.86896239431, 32.0131976500702],
-        [118.868952336026, 32.0131976500702],
-        [118.868940266086, 32.0131996400956],
-        [118.868936913324, 32.0131996400956],
-        [118.868934231115, 32.0131996400956],
-        [118.868921155346, 32.0131999243849],
-        [118.868917132033, 32.0132010615423],
-        [118.868910091234, 32.013200777253],
-        [118.868910091234, 32.0132138545616],
-        [118.868909085406, 32.0132272161577],
-        [118.868909755958, 32.0132391563057],
-        [118.868910091234, 32.0132488221386],
-        [118.868910761786, 32.013257350814],
-        [118.868909755958, 32.0132636051754],
-        [118.868942277742, 32.0132627523079],
-        [118.868973123146, 32.0132644580428],
-        [118.869001621617, 32.0132638894645],
-        [118.869058618558, 32.0132636051754],
-        [118.869081082059, 32.0132624680188],
-        [118.869094157828, 32.0132624680188],
-        [118.869112933291, 32.0132624680188],
-        [118.869124332679, 32.0132604779947],
-        [118.869125003231, 32.0132579193923],
-      ];
+        [118.868928794571, 32.0132512031322],
+        [118.868928459294, 32.0132364200935],
+        [118.868929129847, 32.0132159512668],
+        [118.868929129847, 32.0132108340593],
+        [118.868929129847, 32.0132057168517],
+        [118.868930135675, 32.0132017368011],
+        [118.868963998564, 32.0132008839331],
+        [118.868967686601, 32.0132025896691],
+        [118.868967351325, 32.0132116869273],
+        [118.868968021877, 32.0132185098704],
+        [118.868967016049, 32.0132259013915],
+        [118.868966680773, 32.0132313028872],
+        [118.868967016049, 32.0132440959023],
+        [118.868967016049, 32.0132500659755],
+        [118.868967016049, 32.0132526245781],
+        [118.868974056848, 32.0132523402889],
+        [118.86897908599, 32.0132523402889],
+        [118.868979756542, 32.0132458016376],
+        [118.868979756542, 32.0132406844318],
+        [118.86898076237, 32.0132392629857],
+        [118.868983444579, 32.0132352829366],
+        [118.868986462064, 32.0132369886719],
+        [118.868990820654, 32.0132375572504],
+        [118.868992497035, 32.0132440959023],
+        [118.868998196729, 32.0132449487699],
+        [118.869004566975, 32.0132483602403],
+        [118.869004902251, 32.0132543303132],
+        [118.869005572804, 32.013257457494],
+        [118.869014625259, 32.0132580260723],
+        [118.8690266952, 32.0132577417831],
+        [118.869029712685, 32.0132577417831],
+        [118.869031724342, 32.0132577417831],
+        [118.869032394894, 32.0132625746988],
+        [118.868924100705, 32.0132523402889]
+      ]
       let i = 0;
       this.playInterval && clearInterval(this.playInterval);
       this.playInterval = setInterval(() => {
@@ -200,6 +203,20 @@ export default {
           lat: gps[i][1],
           lng: gps[i][0],
         });
+        // this.onMessage({
+        //       data: JSON.stringify({
+        //         latitude: gps[i][1],
+        //         longitude: gps[i][0],
+        //         deviceId: 'uwb001',
+        //       })
+        //     }
+        // );
+        // var marker = new BDLayers.Lib.Overlays.MarkerIcon(`marker${uuid()}`, gps[i], {
+        //   iconSize: 45,
+        //   iconColor: 'red',
+        //   labelText: `${i}`
+        // });
+        // marker.getMarker().addTo(this.mapIns.map);
         if (i === gps.length - 1) {
           i = 0;
           return;

+ 13 - 0
ruoyi-ui/src/views/bd/roomlocation/roommap/video/index.scss

@@ -0,0 +1,13 @@
+.drag-video-container {
+  position: absolute;
+  border-radius: 10px;
+  top: 0px;
+  .video-title {
+    height: 30px;
+    width: 100%;
+    background: #fefefe;
+    padding: 0 15px;
+    line-height: 30px;
+    cursor: move;
+  }
+}

二进制
ruoyi-ui/src/views/bd/roomlocation/roommap/video/output.mp4


二进制
ruoyi-ui/src/views/bd/roomlocation/roommap/video/roomlocation.mp4


+ 126 - 0
ruoyi-ui/src/views/bd/roomlocation/roommap/video/video.vue

@@ -0,0 +1,126 @@
+<template>
+  <div v-drag class="drag-video-container">
+    <div class="video-title" style="">
+      西南角展厅监控
+    </div>
+    <video-player
+      style="width: 600px;height: 300px;"
+      ref="videoPlayer"
+      class="player-video"
+      :playsinline="false"
+      :options="playOptions"
+      @ready="onPlayerReady"
+      @play="onPlayerPlay($event)"
+      @pause="onPlayerPause($event)"
+      @ended="onPlayerEnd($event)"
+      @waiting="onPlayerWaiting($event)"
+      @playing="onPlayerPlaying($event)"
+      @loadeddata="onPlayerLoadeddata($event)"
+      @timeupdate="onPlayerTimeupdate($event)"
+      @statechanged="playerStateChanged($event)"
+    />
+  </div>
+</template>
+
+<script>
+
+export default {
+  name: "video-p",
+  props: {
+    path: {  // 传入的地址
+      type: String,
+      default: "",
+    },
+    lastTime: {  // 传入的上次播放位置
+      type: Number,
+      default: 0,
+    },
+  },
+  data() {
+    return {
+      playedTime: this.lastTime,
+      currentTime: 0,
+      maxTime: 0,
+      playOptions: {
+        height: "100px",
+        width: "200px",
+        playbackRates: [0.5], // 可选的播放速度
+        autoplay: true, // 如果为true,浏览器准备好时开始回放
+        muted: false, // 默认情况下静音播放
+        loop: false, // 是否视频一结束就重新开始
+        preload: "auto", // 建议浏览器在<video>加载元素后是否应该开始下载视频数据,auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
+        language: "zh-CN",
+        aspectRatio: "16:9", // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值,值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
+        fluid: true, // 当true时,Video.js player将拥有流体大小,换句话说,它将按比例缩放以适应其容器
+        sources: [
+          {
+            type: "video/mp4", // 类型
+            src: require("./output.mp4"), // url地址,在使用本地的资源时,需要用require()引入,否则控制台会报错
+          },
+        ],
+        notSupportedMessage: "此视频暂无法播放,请稍后再试", // 允许覆盖Video.js无法播放媒体源时显示的默认信息
+        controlBar: {
+          currentTimeDisplay: true,
+          progressControl: true,  // 是否显示进度条
+          playbackRateMenuButton: true, // 是否显示调整播放倍速按钮
+          timeDivider: true, // 当前时间和持续时间的分隔符
+          durationDisplay: true, // 显示持续时间
+          remainingTimeDisplay: true, // 是否显示剩余时间功能
+          fullscreenToggle: true, // 是否显示全屏按钮
+        },
+      },
+    };
+  },
+  computed: {},
+  mounted() {
+  },
+  methods: {
+    update() {
+
+    },
+    // 准备好了
+    onPlayerReady() {
+      console.log("准备好了");
+    },
+    // 视频播放
+    onPlayerPlay(player) {
+
+    },
+    // 视频暂停的
+    onPlayerPause(player) {
+
+    },
+    // 视频播放完
+    onPlayerEnd(player) {
+
+    },
+    // DOM元素上的readyState更改导致播放停止
+    onPlayerWaiting(player) {
+
+    },
+    // 视频已开始播放
+    onPlayerPlaying(player) {
+
+    },
+    // 当播放器在当前播放位置下载数据时触发
+    onPlayerLoadeddata(player) {
+
+    },
+    // 当前播放位置发生变化时触发
+    onPlayerTimeupdate(player) {
+
+    },
+    //播放状态改变
+    playerStateChanged(playerCurrentState) {
+      console.log("播放状态变化了");
+      console.log(playerCurrentState);
+    },
+    // 手动暂停视频的播放
+    pause() {
+      // 视频播放器的方法调用,要使用this.$refs.videoPlayer.player这个对象去调用
+      this.$refs.videoPlayer.player.pause()
+    }
+  },
+};
+</script>
+<style lang="scss" src="./index.scss"/>

+ 15 - 0
ruoyi-ui/src/views/bd/rooms/index.scss

@@ -0,0 +1,15 @@
+.room-layers-container {
+  .el-tag {
+    margin-right: 10px;
+    background-color: transparent;
+    cursor: pointer;
+    border-color: #d1e9ff80;
+
+    &:hover, &.selected {
+      background-color: rgba(24, 144, 255, 0.85);
+      color: #fff;
+    }
+  }
+
+  margin-bottom: 14px;
+}

+ 61 - 0
ruoyi-ui/src/views/bd/rooms/index.vue

@@ -0,0 +1,61 @@
+<template>
+  <div class="room-layers-container">
+    <template v-for="roomKey in Object.keys(Layers)">
+      <el-tag
+        @click="()=>roomTagClick(Layers[roomKey])"
+        :class="Layers[roomKey].id === activeTag? 'selected':''"
+      >
+        {{ Layers[roomKey].label }}
+      </el-tag>
+    </template>
+  </div>
+</template>
+
+<script>
+const Layers = {
+  cyy: {
+    id: 'cyy',
+    label: '创研院',
+    layer: "http://bd.xt.wenhq.top:8083/geoserver/gwc/service/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&LAYER=bdlayers:bdl_cyy_pingmian&STYLE=&TILEMATRIX=EPSG:900913:{z}&TILEMATRIXSET=EPSG:900913&Format=image/jpeg&TILECOL={x}&TILEROW={y}",
+  },
+  cyy1: {
+    id: 'cyy1',
+    label: '创研院1',
+    layer: "http://bd.xt.wenhq.top:8083/geoserver/gwc/service/wmts?REQUEST=GetTile&SERVICE=WMTS&VERSION=1.0.0&LAYER=bdlayers:bdl_cyy_pingmian&STYLE=&TILEMATRIX=EPSG:900913:{z}&TILEMATRIXSET=EPSG:900913&Format=image/jpeg&TILECOL={x}&TILEROW={y}",
+  }
+}
+export default {
+  name: 'room-layer',
+  components: {},
+  data() {
+    return {
+      Layers,
+      layerId: 'blayer_ng_s',
+      activeTag: '',
+    };
+  },
+  // 组件卸载前清空图层信息
+  beforeDestroy() {
+    window.map.removeLayersById(this.layerId);
+  },
+  created() {
+
+  },
+  mounted() {
+  },
+  methods: {
+    roomTagClick(layer) {
+      window.map.removeLayersById(this.layerId);
+      const vtLayer = new BDLayers.Lib.Layer.CBTileLayer(this.layerId, {
+        url: layer.layer,
+        maxZoom: 24,
+        minZoom: 15,
+      });
+      window.map.addCustomLayers(vtLayer);
+      window.map.setPitch(0);
+      this.activeTag = layer.id;
+    },
+  },
+};
+</script>
+<style lang="scss" src="./index.scss"/>

+ 1 - 1
ruoyi-ui/version

@@ -1 +1 @@
-2.2
+2.5