Jelajahi Sumber

新增 WebSocket 实现电子围栏违规事件实时推送

- 新增 WebSocket 服务器,用于实时推送电子围栏违规事件
- 在前端添加 WebSocket 客户端组件,实现消息接收
- 修改电子围栏服务,集成 WebSocket 消息发送
- 更新相关环境配置,支持新的 WebSocket 地址
chen.cheng 9 bulan lalu
induk
melakukan
7ca53b0ff0

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

@@ -8,6 +8,7 @@ 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;
@@ -23,7 +24,6 @@ import org.springframework.util.CollectionUtils;
 
 import javax.annotation.PostConstruct;
 import javax.annotation.Resource;
-import java.util.ArrayList;
 import java.util.List;
 
 @Service
@@ -49,6 +49,9 @@ public class FenceBreakInEngine extends EvtFusionEngine {
      */
     private static final int MAX_EVT_TIME_GAP = 300;
 
+    @Autowired
+    private FenceVioEvtSocketServer fenceVioEvtSocketServer;
+
     @PostConstruct
     public void init() {
         super.init();
@@ -56,7 +59,7 @@ public class FenceBreakInEngine extends EvtFusionEngine {
         if (!redisCache.hasKey(BDConst.REDIS_KEY.FENCE)) {
             List<BdFenceInfo> bdFenceInfos = fenceInfoService.selectBdFenceInfoList(new BdFenceInfo());
             if (CollectionUtils.isEmpty(bdFenceInfos)) {
-                bdFenceInfos = new ArrayList<>();
+                return;
             }
             bdFenceInfos.removeIf(item -> ObjectUtils.isEmpty(item.getPoly()) || ObjectUtils.isEmpty(GeoUtils.getPolygon(item.getPoly())));
 
@@ -103,10 +106,14 @@ public class FenceBreakInEngine extends EvtFusionEngine {
 
     @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);
+        if (CollectionUtils.isEmpty(cacheList)) {
+            return;
+        }
         Polygon polygon;
         for (BdFenceInfo fenceInfo : cacheList) {
             polygon = GeoUtils.getPolygon(fenceInfo.getPoly());

+ 111 - 0
bd-location/src/main/java/com/ruoyi/bd/socket/FenceVioEvtSocketServer.java

@@ -0,0 +1,111 @@
+package com.ruoyi.bd.socket;
+
+import com.alibaba.fastjson2.JSON;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.annotation.CrossOrigin;
+
+import javax.annotation.PostConstruct;
+import javax.websocket.OnClose;
+import javax.websocket.OnError;
+import javax.websocket.OnMessage;
+import javax.websocket.OnOpen;
+import javax.websocket.Session;
+import javax.websocket.server.PathParam;
+import javax.websocket.server.ServerEndpoint;
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@ServerEndpoint("/ws/evt/{device}")
+@CrossOrigin(origins = "*")
+@Component
+public class FenceVioEvtSocketServer {
+    private static final Logger logger = LoggerFactory.getLogger(FenceVioEvtSocketServer.class);
+    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
+    private static AtomicInteger onlineNum = new AtomicInteger();
+    //concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。
+    private static ConcurrentHashMap<String, Session> sessionPools = new ConcurrentHashMap<>();
+
+    @PostConstruct
+    public void init() {
+        logger.info("WebSocketServer init");
+    }
+
+    //发送消息
+    public void sendMessage(Session session, String message) throws IOException {
+        if (session != null) {
+            synchronized (session) {
+                logger.info("发送数据:{}", message);
+                session.getBasicRemote().sendText(message);
+            }
+        }
+    }
+
+    //给指定用户发送信息
+    public void sendInfo(String userName, String message) {
+        Session session = sessionPools.get(userName);
+        if (session == null) return;
+        try {
+            sendMessage(session, message);
+        } catch (Exception e) {
+            logger.info("server get {}", e.getMessage());
+        }
+    }
+
+    // 群发消息
+    public void broadcast(Object message) {
+        for (Session session : sessionPools.values()) {
+            try {
+                sendMessage(session, JSON.toJSONString(message));
+            } catch (Exception e) {
+                logger.info("server get {}", e.getMessage());
+            }
+        }
+    }
+
+    //建立连接成功调用
+    @OnOpen
+    public void onOpen(Session session, @PathParam(value = "device") String device) {
+        sessionPools.put(device, session);
+        addOnlineCount();
+    }
+
+    //关闭连接时调用
+    @OnClose
+    public void onClose(@PathParam(value = "device") String device) {
+        sessionPools.remove(device);
+        subOnlineCount();
+        logger.info("{}断开webSocket连接!当前人数为:{}", device, onlineNum);
+    }
+
+    //收到客户端信息后,根据接收人的username把消息推下去或者群发
+    // to=-1群发消息
+    @OnMessage
+    public void onMessage(String message) throws IOException {
+        logger.info("server get {}", message);
+    }
+
+    //错误时调用
+    @OnError
+    public void onError(Session session, Throwable throwable) {
+        logger.info("server get {}", throwable.getMessage());
+    }
+
+    public static void addOnlineCount() {
+        onlineNum.incrementAndGet();
+    }
+
+    public static void subOnlineCount() {
+        onlineNum.decrementAndGet();
+    }
+
+    public static AtomicInteger getOnlineNumber() {
+        return onlineNum;
+    }
+
+    public static ConcurrentHashMap<String, Session> getSessionPools() {
+        return sessionPools;
+    }
+}

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

@@ -52,7 +52,7 @@ spring:
     # 国际化资源文件路径
     basename: i18n/messages
   profiles:
-    active: druid
+    active: hm
   # 文件上传
   servlet:
     multipart:

+ 1 - 1
ruoyi-admin/src/main/resources/application.yml

@@ -52,7 +52,7 @@ spring:
     # 国际化资源文件路径
     basename: i18n/messages
   profiles:
-    active: druid
+    active: hm
   # 文件上传
   servlet:
     multipart:

+ 10 - 1
ruoyi-ui/.env.development

@@ -7,6 +7,15 @@ ENV = 'development'
 # 若依管理系统/开发环境
 VUE_APP_BASE_API = '/dev-api'
 
-VUE_APP_BASE_URL = 'https://www.lj-info.com:8090/prod-api'
+VUE_APP_BASE_URL = 'http://127.0.0.1:18080/tfc'
+
+VUE_APP_WS_URL = 'ws://127.0.0.1:18080/tfc'
+
+VUE_APP_DOMAIN_BASE_URL = 'http://127.0.0.1'
+
+VUE_APP_BD_BASE_API = '/dev-db-api'
+
+VUE_APP_BD_BASE_URL = 'http://200.200.19.253:31838/bdgis'
+
 # 路由懒加载
 VUE_CLI_BABEL_TRANSPILE_MODULES = true

+ 2 - 0
ruoyi-ui/.env.production

@@ -8,4 +8,6 @@ NODE_ENV = production
 # 若依管理系统/生产环境
 VUE_APP_BASE_API = '/prod-api'
 
+VUE_APP_BD_BASE_API = '/prod-bd-api'
+
 VUE_APP_BASE_URL = 'http://172.192.13.80:8080/tfc'

+ 2 - 0
ruoyi-ui/.env.test

@@ -9,4 +9,6 @@ NODE_ENV = production
 # 若依管理系统/生产环境
 VUE_APP_BASE_API = '/prod-api'
 
+VUE_APP_BD_BASE_API = '/prod-bd-api'
+
 VUE_APP_BASE_URL = 'http://172.192.13.80:8080/tfc':

+ 2 - 0
ruoyi-ui/package.json

@@ -38,10 +38,12 @@
     "url": "https://gitee.com/y_project/RuoYi-Vue.git"
   },
   "dependencies": {
+    "@fingerprintjs/fingerprintjs": "^4.5.1",
     "@riophae/vue-treeselect": "0.4.0",
     "axios": "0.28.1",
     "clipboard": "2.0.8",
     "core-js": "3.37.1",
+    "dayjs": "^1.11.13",
     "echarts": "5.4.0",
     "element-ui": "2.15.14",
     "file-saver": "2.0.5",

+ 11 - 0
ruoyi-ui/src/assets/icons/svg/bd_evt.svg

@@ -0,0 +1,11 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+        "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg t="1729307496337" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6498"
+     xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48">
+    <path d="M243.2 889.6H166.4c-57.6 0-102.4-44.8-102.4-102.4V236.8C64 179.2 108.8 128 166.4 128h518.4c57.6 0 102.4 44.8 102.4 102.4v76.8c0 19.2-12.8 32-32 32s-32-12.8-32-32V236.8c0-25.6-19.2-44.8-38.4-44.8H166.4c-19.2 0-38.4 19.2-38.4 44.8v550.4c0 19.2 19.2 38.4 38.4 38.4h76.8c19.2 0 32 12.8 32 32s-12.8 32-32 32z"
+          fill="#ffffff" p-id="6499"></path>
+    <path d="M524.8 377.6H236.8c-19.2 0-32-12.8-32-32s12.8-32 32-32h281.6c19.2 0 32 12.8 32 32s-12.8 32-25.6 32zM448 550.4H236.8c-19.2 0-32-12.8-32-32s12.8-32 32-32H448c19.2 0 32 12.8 32 32s-12.8 32-32 32zM345.6 704H230.4c-19.2 0-32-12.8-32-32s12.8-32 32-32h121.6c19.2 0 32 12.8 32 32s-19.2 32-38.4 32zM928 896H364.8c-12.8 0-19.2-6.4-25.6-19.2s-6.4-19.2 0-32l281.6-480c12.8-19.2 44.8-19.2 57.6 0l281.6 480c6.4 12.8 6.4 19.2 0 32-12.8 12.8-19.2 19.2-32 19.2z m-512-64h454.4l-224-384-230.4 384z"
+          fill="#ffffff" p-id="6500"></path>
+    <path d="M646.4 723.2c-19.2 0-32-12.8-32-32V576c0-19.2 12.8-32 32-32s32 12.8 32 32v115.2c0 19.2-12.8 32-32 32zM646.4 742.4c-19.2 0-38.4 19.2-38.4 38.4s19.2 38.4 38.4 38.4 38.4-19.2 38.4-38.4c-6.4-19.2-19.2-38.4-38.4-38.4z"
+          fill="#ffffff" p-id="6501"></path>
+</svg>

+ 7 - 0
ruoyi-ui/src/assets/icons/svg/bd_fence.svg

@@ -0,0 +1,7 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+        "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg t="1729307445723" class="icon" viewBox="0 0 1070 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5383"
+     xmlns:xlink="http://www.w3.org/1999/xlink" width="50.15625" height="48">
+    <path d="M93.090909 93.090909h884.363636v93.090909H93.090909V93.090909z m0 744.727273h884.363636v93.090909H93.090909v-93.090909zM93.090909 93.090909h93.090909v837.818182H93.090909V93.090909z m791.272727 0h93.090909v837.818182h-93.090909V93.090909zM0 0h279.272727v279.272727H0V0z m791.272727 0h279.272728v279.272727h-279.272728V0zM0 744.727273h279.272727v279.272727H0v-279.272727z m791.272727 0h279.272728v279.272727h-279.272728v-279.272727zM372.363636 186.181818h93.090909L279.272727 837.818182H186.181818z m232.727273 0h93.090909l-186.181818 651.636364H418.909091z m186.181818 0h93.090909l-186.181818 651.636364h-93.090909z"
+          fill="#ffffff" p-id="5384"></path>
+</svg>

File diff ditekan karena terlalu besar
+ 4 - 0
ruoyi-ui/src/assets/icons/svg/bd_location.svg


+ 9 - 0
ruoyi-ui/src/assets/icons/svg/bd_real_time.svg

@@ -0,0 +1,9 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+        "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg t="1729320983633" class="icon" viewBox="0 0 1260 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5173"
+     xmlns:xlink="http://www.w3.org/1999/xlink" width="59.0625" height="48">
+    <path d="M1260.798133 851.908044V184.439256L1009.28633 8.071432 741.660949 187.367932 474.037032 97.722611 206.411651 247.130503v717.17575l53.528883-29.881286 6.056503-3.924426 7.321691-3.545163 200.718304-112.062875 237.850992 75.321165 20.541736-48.526705-258.392728-86.557032-215.361686 113.525749V271.006538l215.361686-113.521356 267.623917 89.645321L1009.28633 67.835468l200.716839 134.473107v592.569353l-126.523215-0.437837 0.070288 58.185479"
+          p-id="5174" fill="#ffffff"></path>
+    <path d="M474.037032 334.160517v298.826035l33.449878 0.594521 24.67117-0.598914V334.160517M693.687764 331.23184v260.590701l33.451342 0.518375 24.671171-0.522769V331.23184M982.162393 200.905739v343.892508l33.451342 0.685311 24.67117-0.689704V200.905739M898.872299 800.862678c-45.772284 0-82.880079-37.503166-82.880078-83.675214s37.107795-83.675214 82.880078-83.675214 82.880079 37.503166 82.880079 83.675214-37.107795 83.675214-82.880079 83.675214z m0-223.134393c-76.302271 0-138.114916 62.407166-138.114916 139.459179s138.114916 306.811072 138.114916 306.811072 138.114916-229.759059 138.114917-306.811072-61.812645-139.459179-138.114917-139.459179z m0 0M90.557604 146.30496c-30.011612 0-54.341591-24.589167-54.341591-54.862896s24.329979-54.862896 54.341591-54.862895 54.341591 24.589167 54.341591 54.862895S120.567751 146.30496 90.557604 146.30496z m0-146.302031C40.528489 0.002929 0 40.920931 0 91.442064c0 50.519668 90.557604 201.164927 90.557604 201.164927s90.557604-150.645258 90.557604-201.164927C181.115208 40.920931 140.586719 0.002929 90.557604 0.002929z m0 0"
+          p-id="5175" fill="#ffffff"></path>
+</svg>

+ 69 - 0
ruoyi-ui/src/components/WebsocketMessage/index.vue

@@ -0,0 +1,69 @@
+<template>
+  <div></div>
+</template>
+
+<script>
+const prefix = process.env.VUE_APP_WS_URL;
+export default {
+  name: 'SocketMessage',
+  props: {
+    onMessage: {
+      type: Function,
+      default: function () {
+      },
+    },
+    ws: {
+      type: String,
+      default: '',
+    },
+  },
+  watch: {
+    ws(val) {
+      this.init();
+    },
+  },
+  data() {
+    return {
+      socket: null,
+      url: prefix,
+    };
+  },
+  // 组件卸载前清空图层信息
+  beforeDestroy() {
+    this.socket.close();
+  },
+  created() {
+
+  },
+  mounted() {
+    this.init();
+  },
+  methods: {
+    init() {
+      if (!this.ws) {
+        return;
+      }
+      if (this.ws && this.socket) {
+        this.socket.close();
+        return;
+      }
+      this.socket = new WebSocket(`${prefix}${this.ws}`);
+      this.socket.onopen = () => {
+        console.log('连接成功:', this.ws);
+      };
+      // 监听socket错误信息
+      this.socket.onerror = (e) => {
+        console.log('%c FXY', 'color:#f6b2b1;font-size:50px', e);
+        console.log('socket连接失败');
+      };
+      // 监听socket关闭监听
+      this.socket.onclose = () => {
+        console.log('socket连接关闭');
+      };
+      this.socket.onmessage = (a) => {
+        this.onMessage(a);
+      };
+    },
+  },
+};
+</script>

+ 14 - 10
ruoyi-ui/src/views/bd/fence/index.vue

@@ -116,7 +116,9 @@ export default {
   // 组件卸载前清空图层信息
   beforeDestroy() {
     window.map.removeLayersById('vl');
-    this.drawtool.disabled();
+    window.map.removeLayersById('drawLayer');
+    window.map.removeLayersById('distanceLayer');
+    this.drawtool?.disable();
   },
   created() {
     // 地图绘制工具
@@ -169,8 +171,8 @@ export default {
           defenceName,
           poly,
         } = item;
-        console.log(this.polygonToCoordinates(poly));
         const polygon = this.drawPoly({
+          name: defenceName,
           coordinates: this.polygonToCoordinates(poly),
           bizAttr: {
             id,
@@ -186,12 +188,12 @@ export default {
       });
       this.editingDrawGeom = null;
       this.fenceList = result;
-      window.map.flyToPoint([118.86318437, 31.52265586], {
-        zoom: 13,
-        pitch: 0,
-        bearing: 20,
-        duration: 5000,
-      });
+      // window.map.flyToPoint([118.86318437, 31.52265586], {
+      //   zoom: 13,
+      //   pitch: 0,
+      //   bearing: 20,
+      //   duration: 5000,
+      // });
     },
     polygonToCoordinates(polygon) {
       // 正则表达式匹配坐标点
@@ -287,6 +289,7 @@ export default {
      * @returns {BDLayers.Lib.Overlays.Polygon}
      */
     drawPoly({
+      name = '多边形',
       coordinates,
       symbol = {},
       bizAttr = {},
@@ -301,8 +304,9 @@ export default {
           polygonOpacity: 0.4,
         }, symbol),
         labelSymbol: Object.assign({
-          labelText: '多边形',
-          labelTextSize: 10,
+          labelText: name,
+          labelColor: '#fefefe',
+          labelTextSize: 20,
         }, labelSymbol),
         bizAttr: bizAttr,
       });

+ 66 - 234
ruoyi-ui/src/views/bd/fenceEvt/index.vue

@@ -8,18 +8,19 @@
     </template>
     <template v-slot:content>
       <div class="location-list">
-        <template v-for="fence in fenceList">
-          <div :key="`fence_${fence.id}`" class="list-item">
+        <template v-for="evt in evtList">
+          <div :key="`evt_${evt.id}`" class="list-item">
           <span class="over-flow-hidden" style="width: 40%">
-            {{ fence.name }}
+            {{ evt.evtDesc }}
           </span>
             <span class="over-flow-hidden" style="width: 40%">
-           {{ fence.updateTime }}
+           {{ dayjs(evt.evtTime).format('YYYY-MM-DD HH:mm') }}
           </span>
             <span class="over-flow-hidden" style="width: 20%">
           </span>
           </div>
         </template>
+        <socket-message :onMessage="onMessage" :ws="ws"></socket-message>
       </div>
     </template>
   </pannel>
@@ -27,254 +28,85 @@
 
 <script>
 
+import { listFenceVioEvt } from '@/api/bd/fenceVioEvt';
+import SocketMessage from '@/components/WebsocketMessage/index.vue';
 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 FingerprintJS from '@fingerprintjs/fingerprintjs';
+import dayjs from 'dayjs';
+
 export default {
-  name: 'fence',
-  components: { Pannel },
+  name: 'fenceVioEvt',
+  components: {
+    SocketMessage,
+    Pannel,
+  },
   data() {
     return {
-      playItem: {},
-      editState: false,
-      editPolyInfo: {},
-      layer: null,
-      dialogVisible: false,
-      editingDrawGeom: null,
-      drawState: false,
-      drawtool: null,
-      form: {
-        name: '',
-      },
-      rules: {
-        name: [
-          {
-            required: true,
-            message: '请输入围栏名称',
-            trigger: 'blur',
-          },
-        ],
-      },
-      fenceList: [],
+      evtList: [],
+      fp: null,
+      ws: null,
     };
   },
   // 组件卸载前清空图层信息
   beforeDestroy() {
-    window.map.removeLayersById('vl');
-    this.drawtool.disabled();
   },
   created() {
-    // 地图绘制工具
-    this.drawtool = new BDLayers.Lib.Tools.CBDrawTool('myTool', window.map, 'Polygon', true);
-    // 监听图形编辑
-    this.drawtool.on('selectDraw', geom => {
-      this.editingDrawGeom = geom.target.geometry ? geom.target.geometry : geom.target.geom
-          ? geom.target.geom
-          : geom.target;
-      if (this.editingDrawGeom.isEditing && this.editingDrawGeom.isEditing()) {
-        // 点击地图 图形取消编辑状态
-        window.map.map.once('click', () => {
-          this.editingDrawGeom.endEdit();
-          this.dialogVisible = true;
-        });
-      } else {
-        window.map.map.once('click', () => {
-          this.editingDrawGeom = null;
-        });
-      }
-    });
-    this.drawtool.on('drawend', (geom) => {
-      this.editingDrawGeom = geom.target.geometry ? geom.target.geometry : geom.target.geom
-          ? geom.target.geom
-          : geom.target;
-      this.dialogVisible = true;
-      this.drawState = false;
-    });
+
   },
   mounted() {
-    this.getFenceList();
+    this.getFenceVioEvtList();
+    this.getFingerprint();
   },
   methods: {
-    getFenceList() {
-      if (!this.layer) {
-        this.layer = new BDLayers.Lib.Layer.CBVectorLayer('vl');
-        window.map.addCustomLayers(this.layer);
-      }
-      const result = [
-        {
-          id: 1,
-          name: '高空施工作业危险区域',
-        }, {
-          id: 2,
-          name: '危化品堆放区域',
-        },
-      ];
-      this.editingDrawGeom = null;
-      const polygon = this.drawPoly({
-        coordinates: [
-          [
-            [118.86318437, 31.52265586],
-            [118.86620514, 31.52541921],
-            [118.86520697, 31.52406319],
-            [118.86318437, 31.52265586],
-          ],
-        ],
-        bizAttr: {
-          name: 'test',
-        },
-      });
-      this.layer.addGeometry(polygon);
-      result[0].polygon = polygon;
-      const polygon1 = this.drawPoly({
-        coordinates: [
-          [
-            [118.86318437, 31.52265586],
-            [118.86620514, 31.52541921],
-            [118.86520697, 31.52406319],
-            [118.86318437, 31.52265586],
-          ],
-        ],
-        bizAttr: {
-          name: 'test',
-        },
-      });
-      this.layer.addGeometry(polygon1);
-      result[1].polygon = polygon1;
-      this.fenceList = result;
-      window.map.flyToPoint([118.86318437, 31.52265586], {
-        zoom: 13,
-        pitch: 0,
-        bearing: 20,
-        duration: 5000,
-      });
-    },
-    cancelEdit() {
-      this.drawtool.clear();
-      this.dialogVisible = false;
-    },
-    saveEdit() {
-      this.$refs.form.validate((valid) => {
-        if (valid) {
-          const resultCoor = [];
-          const coordinates = [];
-          this.editingDrawGeom._coordinates.forEach(coor => {
-            const {
-              x,
-              y,
-            } = coor;
-            resultCoor.push(`${x} ${y}`);
-            coordinates.push([x, y]);
-          });
-          resultCoor.push(resultCoor[0]);
-          this.form.poly = `POLYGON((${resultCoor.join(',')}))`;
-          this.$message({
-            type: 'success',
-            message: '保存!',
-          });
-          this.dialogVisible = false;
-          this.drawtool.clear();
-          const polygon = this.drawPoly({
-            coordinates: coordinates,
-            labelSymbol: {
-              labelText: this.form.name,
-            },
-            bizAttr: this.form,
-          });
-          this.layer.addGeometry(polygon);
-          this.editingDrawGeom = null;
-          this.editState = false;
-        } else {
-          return false;
-        }
+    dayjs,
+    async getFenceVioEvtList() {
+      const { rows } = await listFenceVioEvt({
+        pageNum: 1,
+        pageSize: 10,
       });
+      this.evtList = rows;
+      debugger
     },
-    startDraw() {
-      this.editingDrawGeom = null;
-      this.drawState = true;
-      this.drawtool.enable();
+    async getFingerprint() {
+      // 初始化FingerprintJS
+      const fp = await FingerprintJS.load();
+      // 获取访问者的指纹
+      const result = await fp.get();
+      // 配置
+      const {
+        osCpu,
+        webGlBasics,
+        languages,
+        audioBaseLatency,
+        reducedTransparency,
+        vendor,
+        vendorFlavors,
+        fonts,
+        fontPreferences,
+        plugins,
+        forcedColors,
+        domBlockers,
+        pdfViewerEnabled,
+        audio,
+        canvas,
+        webGlExtensions,
+        math,
+        ...components
+      } = result.components;
+      const extendedComponents = {
+        ...components,
+      };
+      const fingerprintId = FingerprintJS.hashComponents(extendedComponents);
+      this.ws = `/ws/evt/${fingerprintId}`;
     },
-    /**
-     *
-     * @param coordinates
-     *  [
-     *           [
-     *             [118.86318437, 31.52265586],
-     *             [118.86620514, 31.52541921],
-     *             [118.86520697, 31.52406319],
-     *             [118.86318437, 31.52265586],
-     *           ],
-     *         ]
-     * @param symbol
-     * @param bizAttr
-     * @param labelSymbol
-     * @returns {BDLayers.Lib.Overlays.Polygon}
-     */
-    drawPoly({
-      coordinates,
-      symbol = {},
-      bizAttr = {},
-      labelSymbol = {},
-    }) {
-      const polygon = new BDLayers.Lib.Overlays.Polygon('p1', {
-        coordinates: coordinates,
-        symbol: Object.assign({
-          lineColor: '#34495e',
-          lineWidth: 2,
-          polygonFill: '#1bbc9b',
-          polygonOpacity: 0.4,
-        }, symbol),
-        labelSymbol: Object.assign({
-          labelText: '多边形',
-          labelTextSize: 10,
-        }, labelSymbol),
-        bizAttr: bizAttr,
-      });
-      //多边形的点击事件
-      polygon.on('click', (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
-            ? data.target.geom
-            : data.target;
-        this.$confirm('检测到选中了围栏,请选择操作类型?', '提示', {
-          confirmButtonText: '编辑围栏',
-          cancelButtonText: '删除围栏',
-          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;
-        });
+    onMessage(a) {
+      const data = JSON.parse(a.data);
+      this.$notify({
+        title: '警告',
+        message: `${data.msg.fenceName}发生闯禁事件。`,
+        type: 'warning',
       });
-      return polygon;
-    },
-    delFence(fence) {
-      // polygon.geom.startEdit();
-      fence.polygon.geom.remove();
-    },
-    editFence(fence) {
-
+      this.getFenceVioEvtList();
     },
   },
 };

+ 48 - 4
ruoyi-ui/src/views/bd/index.scss

@@ -37,10 +37,54 @@
   .right-container {
     position: absolute;
     top: $header-height;
-    right: 0;
-    width: 20%;
-    height: calc(100% - #{$header-height});
+    right: 14px;
+    width: 22%;
     box-sizing: border-box;
-    background: linear-gradient(180deg, #060624ba 50%, #06062400 100%);
+    background: #060624ba;
+    border-radius: 10px;
+    padding: 14px;
+  }
+
+  .menus {
+    position: absolute;
+    top: 50%;
+    transform: translateY(-50%);
+    left: 0;
+    width: 150px;
+    height: 400px;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    flex-direction: column;
+
+    .menu-item {
+      height: 80px;
+      width: 70px;
+      background: rgba(0, 150, 253, 0.6);
+      border-radius: 5px;
+      display: flex;
+      align-items: center;
+      justify-content: space-evenly;
+      flex-direction: column;
+      cursor: pointer;
+      color: #fefefe;
+      font-size: 14px;
+      font-weight: bolder;
+      box-shadow: #000000 4px 1px 12px 0px;
+
+      &:hover {
+        background: rgba(0, 150, 253, 1);
+      }
+
+      &.active {
+        background: rgba(0, 150, 253, 1);
+      }
+
+      .svg-icon {
+        width: 30px;
+        height: 30px;
+        flex-shrink: 0;
+      }
+    }
   }
 }

+ 52 - 5
ruoyi-ui/src/views/bd/index.vue

@@ -2,12 +2,23 @@
   <div class="bd-container">
     <bd-map :loaded="loaded" />
     <div class="header">北斗定位融合</div>
-    <div class="left-container" v-if="mapLoaded">
-      <location />
-      <fence />
+    <div class="menus">
+      <template v-for="item in Object.keys(menus)">
+        <div
+            :key="item"
+            :class="{'menu-item':true,'active': activeMenu === menus[item].name} "
+            :data-type="item"
+            @click="menuClick"
+        >
+          <svg-icon :icon-class="menus[item].icon"></svg-icon>
+          {{ menus[item].title }}
+        </div>
+      </template>
     </div>
-    <div class="right-container">
-
+    <div class="right-container" v-if="mapLoaded">
+      <location v-if="activeMenu === menus.location_tail.name" />
+      <fence v-if="activeMenu=== menus.fence.name" />
+      <fence-vio-evt v-if="activeMenu=== menus.evt.name" />
     </div>
   </div>
 </template>
@@ -15,19 +26,51 @@
 <script>
 
 import Fence from '@/views/bd/fence/index.vue';
+import FenceVioEvt from '@/views/bd/fenceEvt/index.vue';
 import Location from '@/views/bd/location/index.vue';
 import BdMap from '@/views/bd/map/index.vue';
+import menu from '@/views/system/menu/index.vue';
 
+const menus = {
+  evt: {
+    name: 'evt',
+    title: '围栏闯禁',
+    icon: 'bd_evt',
+  },
+  fence: {
+    name: 'fence',
+    title: '围栏管理',
+    icon: 'bd_fence',
+  },
+  location_tail: {
+    name: 'location_tail',
+    title: '轨迹回放',
+    icon: 'bd_location',
+  },
+  realtime_location: {
+    name: 'realtime_location',
+    title: '实时定位',
+    icon: 'bd_real_time',
+  },
+};
 export default {
   name: 'bd',
+  computed: {
+    menu() {
+      return menu;
+    },
+  },
   components: {
+    FenceVioEvt,
     Fence,
     Location,
     BdMap,
   },
   data() {
     return {
+      menus,
       mapLoaded: false,
+      activeMenu: 'evt',
     };
   },
   created() {
@@ -36,6 +79,10 @@ export default {
     loaded(map) {
       this.mapLoaded = true;
     },
+    menuClick(e) {
+      const type = e.currentTarget.dataset.type;
+      this.activeMenu = type;
+    },
   },
 };
 </script>

+ 5 - 3
ruoyi-ui/src/views/bd/map/index.vue

@@ -20,7 +20,7 @@ export default {
       const options = {
         mapType: BDLayers.Lib.Constant.BaseLayerType.Blank,
         mapModel: BDLayers.Lib.Constant.BaseLayerModel.Satellite,
-        center: [108.95965, 34.2189],
+        center: [118.8738802982145,32.010241883966096],
         defaultZoom: 14,
         showCenter: false,
         baseControl: true,
@@ -76,13 +76,14 @@ export default {
           from: 'GCJ02',
           to: 'WGS84',
         },
+        offset:[13.4,-3.5],
         mapView: this.mapView,
         maxCacheSize: 10000,
         services: [
           {
-            url: 'http://127.0.0.1:8800/bdgis/folder/zy9/tileset.json', //"http://resource.dvgis.cn/data/3dtiles/dayanta/tileset.json",
+            url: `${process.env.VUE_APP_DOMAIN_BASE_URL}${process.env.VUE_APP_BD_BASE_API}/folder/zy9/tileset.json`, //"http://resource.dvgis.cn/data/3dtiles/dayanta/tileset.json",
             maximumScreenSpaceError: 16.0,
-            heightOffset: 0, //-420,
+            heightOffset: -48, //-420,
           },
         ],
       });
@@ -98,6 +99,7 @@ export default {
         minZoom: [3, 3],
       });
       this.mapView.addCustomLayers(satelliteTileLayer, -1, true);
+      // this.mapView.addCustomLayers(d3tiles, -1, true);
       window.map = this.mapView;
       this.loaded(this.mapView);
     });

+ 7 - 0
ruoyi-ui/vue.config.js

@@ -39,6 +39,13 @@ module.exports = {
         pathRewrite: {
           ['^' + process.env.VUE_APP_BASE_API]: ''
         }
+      },
+      [process.env.VUE_APP_BD_BASE_API]: {
+        target: process.env.VUE_APP_BD_BASE_URL,
+        changeOrigin: true,
+        pathRewrite: {
+          ['^' + process.env.VUE_APP_BD_BASE_API]: ''
+        }
       }
     },
     disableHostCheck: true

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini