瀏覽代碼

WebSocketClient及相关配置调整

fbw 1 周之前
父節點
當前提交
d4310b21a1

+ 90 - 88
src/components/ImageVideo/CanvasVideo.vue

@@ -10,7 +10,8 @@
 </template>
 
 <script>
-import { debounce } from 'lodash-es'
+import { debounce } from "lodash-es";
+import { WebSocketClient } from "@/utils/WebSocketClient";
 
 // WebSocket 相关配置
 const WS_CONFIG = {
@@ -19,10 +20,10 @@ const WS_CONFIG = {
     WIDTH: 960,
     HEIGHT: 540,
   },
-}
+};
 
 export default {
-  name: 'CanvasVideo',
+  name: "CanvasVideo",
   props: {
     cameraData: {
       type: Object,
@@ -40,7 +41,7 @@ export default {
 
   data() {
     return {
-      url: '',
+      url: "",
       lastUrl: null,
       ws: null,
       lockReconnect: true,
@@ -51,51 +52,51 @@ export default {
       imgRatioCache: 0,
       wsEventHandlers: {
         onopen: () => {
-          const { id } = this.cameraData
+          const { id } = this.cameraData;
           try {
-            this.websocketSend({ id })
+            this.websocketSend({ id });
           } catch (error) {
-            console.error('WebSocket发送失败:', error)
+            console.error("WebSocket发送失败:", error);
           }
         },
 
-        onmessage: e => {
+        onmessage: (e) => {
           try {
-            if (typeof e.data === 'string') {
-              console.log(JSON.parse(e.data).Msg)
-              return
+            if (typeof e.data === "string") {
+              console.log(JSON.parse(e.data).Msg);
+              return;
             }
 
-            this.handleBinaryMessage(e.data)
+            this.handleBinaryMessage(e.data);
           } catch (error) {
-            console.error('处理WebSocket消息失败:', error)
+            console.error("处理WebSocket消息失败:", error);
           }
         },
 
         onerror: () => {
-          this.reconnect()
+          this.reconnect();
         },
 
-        onclose: e => {
-          this.reconnect()
+        onclose: (e) => {
+          this.reconnect();
         },
       },
-    }
+    };
   },
 
   mounted() {
     if (this.cameraData.id) {
-      this.initCanvas()
-      this.initEventListeners()
-      this.initWebSocket()
+      this.initCanvas();
+      this.initEventListeners();
+      this.initWebSocket();
     } else {
-      this.$watch('cameraData.id', newId => {
+      this.$watch("cameraData.id", (newId) => {
         if (newId) {
-          this.initCanvas()
-          this.initEventListeners()
-          this.initWebSocket()
+          this.initCanvas();
+          this.initEventListeners();
+          this.initWebSocket();
         }
-      })
+      });
     }
   },
 
@@ -113,163 +114,164 @@ export default {
     // 处理参数更新
     handleParamUpdate(data, state) {
       if (data.id === this.cameraData.id) {
-        this.websocketSend(data)
+        this.websocketSend(data);
       }
     },
 
     // 初始化画布
     initCanvas() {
-      this.canvas = this.$refs.canvas
+      this.canvas = this.$refs.canvas;
       if (!this.canvas) {
-        console.error('Canvas元素未找到')
-        return
+        console.error("Canvas元素未找到");
+        return;
       }
-      this.img = new Image()
+      this.img = new Image();
       // 缓存画布比例
-      this.canvasRatioCache = this.canvas.width / this.canvas.height
+      this.canvasRatioCache = this.canvas.width / this.canvas.height;
     },
 
     // 初始化WebSocket
     initWebSocket() {
       try {
-        const host = VUE_APP_BASE_WS() + '/VideoOverWebSocket'
+        // const host = VUE_APP_BASE_WS() + '/VideoOverWebSocket'
+        // this.ws = new WebSocket(host)
         // const host = 'wss://' + window.location.host + BASE_URL + '/VideoOverWebSocket'
-        this.ws = new WebSocket(host)
+        this.ws = new WebSocketClient("/websocket/video");
 
         Object.entries(this.wsEventHandlers).forEach(([event, handler]) => {
-          this.ws[event] = handler
-        })
+          this.ws[event] = handler;
+        });
       } catch (error) {
-        console.error('WebSocket初始化失败:', error)
-        this.reconnect()
+        console.error("WebSocket初始化失败:", error);
+        this.reconnect();
       }
     },
 
     // 处理二进制消息
     handleBinaryMessage(data) {
-      if (this.url === this.lastUrl) return
+      if (this.url === this.lastUrl) return;
 
       // 清理旧的URL
-      if (this.lastUrl) URL.revokeObjectURL(this.lastUrl)
-      this.lastUrl = this.url
-      if (this.url) URL.revokeObjectURL(this.url)
+      if (this.lastUrl) URL.revokeObjectURL(this.lastUrl);
+      this.lastUrl = this.url;
+      if (this.url) URL.revokeObjectURL(this.url);
 
       // 创建新的URL
-      this.url = URL.createObjectURL(data)
+      this.url = URL.createObjectURL(data);
 
       if (!this.updatePending) {
-        this.updatePending = true
-        this.updateUrl()
+        this.updatePending = true;
+        this.updateUrl();
       }
     },
 
     // 发送WebSocket消息
     websocketSend(text) {
       try {
-        const config = { ...text, ...this.imgConfig }
-        this.ws.send(JSON.stringify(config))
+        const config = { ...text, ...this.imgConfig };
+        this.ws.send(JSON.stringify(config));
       } catch (err) {
-        console.error('WebSocket发送失败:', err)
+        console.error("WebSocket发送失败:", err);
       }
     },
 
     // 重连机制
     reconnect() {
-      if (this.lockReconnect) return
-      this.lockReconnect = true
+      if (this.lockReconnect) return;
+      this.lockReconnect = true;
 
       setTimeout(() => {
-        console.info('尝试重连...')
-        this.initWebSocket()
-        this.lockReconnect = false
-      }, WS_CONFIG.RECONNECT_TIMEOUT)
+        console.info("尝试重连...");
+        this.initWebSocket();
+        this.lockReconnect = false;
+      }, WS_CONFIG.RECONNECT_TIMEOUT);
     },
 
     // 更新画布
     updateUrl() {
       if (!this.canvas) {
-        console.warn('Canvas未初始化')
-        return
+        console.warn("Canvas未初始化");
+        return;
       }
 
-      const ctx = this.canvas.getContext('2d')
+      const ctx = this.canvas.getContext("2d");
       if (!ctx) {
-        console.error('无法获取canvas上下文')
-        return
+        console.error("无法获取canvas上下文");
+        return;
       }
 
-      this.img.src = this.url
-      this.img.onload = () => this.drawImage(ctx)
+      this.img.src = this.url;
+      this.img.onload = () => this.drawImage(ctx);
     },
 
     // 绘制图像
     drawImage(ctx) {
       if (!this.canvas || !this.img) {
-        console.warn('Canvas或Image未初始化')
-        return
+        console.warn("Canvas或Image未初始化");
+        return;
       }
 
       if (!this.imgRatioCache) {
-        this.imgRatioCache = this.img.width / this.img.height
+        this.imgRatioCache = this.img.width / this.img.height;
       }
 
-      const dimensions = this.calculateImageDimensions()
-      if (!dimensions) return
+      const dimensions = this.calculateImageDimensions();
+      if (!dimensions) return;
 
-      const { newWidth, newHeight, offsetX, offsetY } = dimensions
+      const { newWidth, newHeight, offsetX, offsetY } = dimensions;
 
-      ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
-      ctx.drawImage(this.img, offsetX, offsetY, newWidth, newHeight)
+      ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
+      ctx.drawImage(this.img, offsetX, offsetY, newWidth, newHeight);
 
-      this.updatePending = false
+      this.updatePending = false;
     },
 
     // 计算图像尺寸
     calculateImageDimensions() {
       if (!this.canvas || !this.img) {
-        console.warn('Canvas或Image未初始化')
-        return null
+        console.warn("Canvas或Image未初始化");
+        return null;
       }
 
-      const canvasRatio = this.canvasRatioCache
-      const imgRatio = this.imgRatioCache
+      const canvasRatio = this.canvasRatioCache;
+      const imgRatio = this.imgRatioCache;
 
       if (imgRatio > canvasRatio) {
-        const newWidth = this.canvas.width
-        const newHeight = newWidth / imgRatio
+        const newWidth = this.canvas.width;
+        const newHeight = newWidth / imgRatio;
         return {
           newWidth,
           newHeight,
           offsetX: 0,
           offsetY: (this.canvas.height - newHeight) / 2,
-        }
+        };
       } else {
-        const newHeight = this.canvas.height
-        const newWidth = newHeight * imgRatio
+        const newHeight = this.canvas.height;
+        const newWidth = newHeight * imgRatio;
         return {
           newWidth,
           newHeight,
           offsetX: (this.canvas.width - newWidth) / 2,
           offsetY: 0,
-        }
+        };
       }
     },
 
     // 清理资源
     cleanup() {
-      if (this.url) URL.revokeObjectURL(this.url)
-      if (this.lastUrl) URL.revokeObjectURL(this.lastUrl)
-      this.url = ''
-      this.lastUrl = null
-      this.canvas = null
-      this.img = null
+      if (this.url) URL.revokeObjectURL(this.url);
+      if (this.lastUrl) URL.revokeObjectURL(this.lastUrl);
+      this.url = "";
+      this.lastUrl = null;
+      this.canvas = null;
+      this.img = null;
       if (this.ws) {
-        this.ws.close()
-        this.ws = null
+        this.ws.close();
+        this.ws = null;
       }
     },
   },
-}
+};
 </script>
 
 <style lang="less" scoped>

+ 3 - 2
src/components/ImageVideo/index.vue

@@ -11,6 +11,7 @@
 </template>
 <script>
 import { debounce, throttle } from 'lodash-es'
+import { WebSocketClient } from '@/utils/WebSocketClient'
 export default {
   props: {
     cameraData: {
@@ -55,10 +56,10 @@ export default {
   },
   methods: {
     initWebSocket() {
-      const host = VUE_APP_BASE_WS() + '/VideoOverWebSocket'
+      // const host = VUE_APP_BASE_WS() + '/VideoOverWebSocket'
       // const host = 'wss://' + window.location.host + BASE_URL + '/VideoOverWebSocket'
       // WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https
-      this.ws = new WebSocket(host)
+      this.ws = new WebSocketClient('/websocket/video')
       this.ws.onopen = this.websocketOnopen
       this.ws.onerror = this.websocketOnerror
       this.ws.onmessage = this.websocketOnmessage

+ 0 - 459
src/components/ImageVideoORI/index.vue

@@ -1,459 +0,0 @@
-<template>
-  <div :style="dialogStyle" class="dialog-container" v-show="visible">
-    <div class="dialog-header" v-if="false">
-      <span class="el-icon-video-camera"></span>
-      <span style="margin-left: 10px">{{ cameraData.name || "" }}</span>
-      <span class="el-icon-close close-icon" @click="closeHandler"></span>
-      <span class="el-icon-search zoom-icon" @click="zoomHandler"></span>
-    </div>
-    <div class="dialog-body">
-      <div :style="imgStyle">
-        <img
-          style="width: 100%; height: 100%; object-fit: contain"
-          :src="url"
-          v-if="url"
-        />
-        <div class="image-slot" v-else>
-          <i class="el-icon-video-camera"></i>
-        </div>
-      </div>
-      <div class="ptz-panel" v-if="cameraData.ptzEnable">
-        <div style="padding-top: 20px; padding-left: 15px">
-          <el-button
-            style="margin-left: 0px"
-            type="info"
-            icon="el-icon-top-left"
-            size="mini"
-            @mousedown.native="ptzCommHandle(25, false)"
-            @mouseup.native="ptzCommHandle(25, true)"
-          ></el-button>
-          <el-button
-            style="margin-left: 0px"
-            type="info"
-            icon="el-icon-top"
-            size="mini"
-            @mousedown.native="ptzCommHandle(21, false)"
-            @mouseup.native="ptzCommHandle(21, true)"
-          ></el-button>
-          <el-button
-            style="margin-left: 0px"
-            type="info"
-            icon="el-icon-top-right"
-            size="mini"
-            @mousedown.native="ptzCommHandle(26, false)"
-            @mouseup.native="ptzCommHandle(26, true)"
-          ></el-button>
-          <el-button
-            style="margin-left: 0px"
-            type="info"
-            icon="el-icon-back"
-            size="mini"
-            @mousedown.native="ptzCommHandle(23, false)"
-            @mouseup.native="ptzCommHandle(23, true)"
-          ></el-button>
-          <el-button
-            style="margin-left: 0px"
-            type="info"
-            icon="el-icon-rank"
-            size="mini"
-            disabled
-          ></el-button>
-          <el-button
-            style="margin-left: 0px"
-            type="info"
-            icon="el-icon-right"
-            size="mini"
-            @mousedown.native="ptzCommHandle(24, false)"
-            @mouseup.native="ptzCommHandle(24, true)"
-          ></el-button>
-          <el-button
-            style="margin-left: 0px"
-            type="info"
-            icon="el-icon-bottom-left"
-            size="mini"
-            @mousedown.native="ptzCommHandle(27, false)"
-            @mouseup.native="ptzCommHandle(27, true)"
-          ></el-button>
-          <el-button
-            style="margin-left: 0px"
-            type="info"
-            icon="el-icon-bottom"
-            size="mini"
-            @mousedown.native="ptzCommHandle(22, false)"
-            @mouseup.native="ptzCommHandle(22, true)"
-          ></el-button>
-          <el-button
-            style="margin-left: 0px"
-            type="info"
-            icon="el-icon-bottom-right"
-            size="mini"
-            @mousedown.native="ptzCommHandle(28, false)"
-            @mouseup.native="ptzCommHandle(28, true)"
-          ></el-button>
-          <el-button
-            style="margin-left: 20px; margin-top: 10px"
-            type="info"
-            icon="el-icon-zoom-in"
-            size="mini"
-            @mousedown.native="ptzCommHandle(11, false)"
-            @mouseup.native="ptzCommHandle(11, true)"
-          ></el-button>
-          <el-button
-            style="margin-left: 10px; margin-top: 10px"
-            type="info"
-            icon="el-icon-zoom-out"
-            size="mini"
-            @mousedown.native="ptzCommHandle(12, false)"
-            @mouseup.native="ptzCommHandle(12, true)"
-          ></el-button>
-          <br />
-          <a>速度:</a>
-          <el-select
-            style="margin-top: 10px; width: 70px"
-            size="mini"
-            v-model="speed"
-            placeholder="请选择速度"
-          >
-            <el-option
-              v-for="item in Array.from(
-                { length: 7 },
-                (item, index) => index + 1
-              )"
-              :key="item"
-              :label="item"
-              :value="item"
-            ></el-option>
-          </el-select>
-        </div>
-        <div style="padding-top: 20px; padding-left: 15px">
-          <a>预置点:</a>
-          <el-select
-            style="width: 70px"
-            size="mini"
-            v-model="presetIndex"
-            placeholder="请选择预置点"
-          >
-            <el-option
-              v-for="item in Array.from(
-                { length: 255 },
-                (item, index) => index + 1
-              )"
-              :key="item"
-              :label="item"
-              :value="item"
-            ></el-option>
-          </el-select>
-          <el-button
-            style="margin-left: 10px; margin-top: 10px"
-            type="info"
-            size="mini"
-            @click="ptzPresetHandle(8)"
-            >设置</el-button
-          >
-          <el-button
-            style="margin-left: 10px; margin-top: 10px"
-            type="info"
-            size="mini"
-            @click="ptzPresetHandle(39)"
-            >转到</el-button
-          >
-        </div>
-      </div>
-      <div class="playback-panel" v-if="cameraData.canPlayBack">
-        <el-date-picker
-          v-model="startTime"
-          type="datetime"
-          placeholder="选择回放开始时间"
-        ></el-date-picker>
-        <el-button
-          style="margin-left: 10px"
-          type="success"
-          @click="playBackHandle"
-          >回放</el-button
-        >
-        <el-button type="primary" @click="realTimekHandle">实时</el-button>
-      </div>
-    </div>
-  </div>
-</template>
-
-<script>
-import Dayjs from "dayjs";
-
-export default {
-  name: "ImageVideo",
-  components: {},
-  data() {
-    return {
-      url: "",
-      lastUrl: "",
-      ws: null,
-      zoom: false,
-      addWidth: 0,
-      addHeight: 0,
-      startTime: null,
-      speed: 4,
-      presetIndex: 1,
-    };
-  },
-  props: {
-    visible: false,
-    cameraData: {
-      type: Object,
-      required: true,
-    },
-    size: {
-      type: Object,
-      default: () => {
-        return { height: 420, width: 680 };
-      },
-    },
-  },
-  computed: {
-    dialogStyle() {
-      return { width: "100%", height: "100%" };
-      if (this.zoom) {
-        let clientHeight = document.documentElement.clientHeight;
-        return {
-          height: clientHeight - 174 + this.addHeight + "px",
-          width:
-            ((clientHeight - 234) / (this.size.height - 60)) *
-              (this.size.width - 40) +
-            40 +
-            this.addWidth +
-            "px",
-        };
-      } else {
-        return {
-          height: this.size.height + this.addHeight + "px",
-          width: this.size.width + this.addWidth + "px",
-        };
-      }
-    },
-    imgStyle() {
-      return {
-        position: "absolute",
-        left: "0px",
-        top: "0px",
-        right: this.addWidth + "px",
-        bottom: this.addHeight + "px",
-        "object-fit": "contain",
-      };
-    },
-  },
-  watch: {
-    cameraData(val) {
-      if (val.id) {
-        //console.log(val)
-        if (val.canPlayBack) this.addHeight = 50;
-        else this.addHeight = 0;
-        if (val.ptzEnable) this.addWidth = 150;
-        else this.addWidth = 0;
-        if (this.ws && this.ws.readyState == 1) {
-          this.ws.send(JSON.stringify({ id: val.id }));
-        } else {
-          this.init(JSON.stringify({ id: val.id }));
-        }
-        setTimeout(() => {
-          this.url = "";
-          if (this.lastUrl) URL.revokeObjectURL(this.lastUrl);
-          this.lastUrl = "";
-        }, 500);
-      }
-    },
-  },
-  mounted() {},
-  created() {
-    //this.init()
-  },
-  methods: {
-    init(str) {
-      this.ws = new WebSocket(VUE_APP_BASE_WS() + "/VideoOverWebSocket");
-      // this.ws = new WebSocket("wss://" + window.location.host + process.env.VUE_APP_BASE_API + "/VideoOverWebSocket");
-      let _this = this;
-      this.ws.onmessage = function (evt) {
-        if (typeof evt.data === "string") {
-          console.log(JSON.parse(evt.data).Msg);
-        } else {
-          let currUrl = URL.createObjectURL(evt.data);
-          if (_this.lastUrl) URL.revokeObjectURL(_this.lastUrl);
-          _this.lastUrl = currUrl;
-          _this.url = currUrl;
-        }
-      };
-      if (str) {
-        setTimeout(() => {
-          this.ws.send(str);
-        }, 500);
-      }
-    },
-    zoomHandler() {
-      this.zoom = this.zoom ? false : true;
-    },
-    closeHandler() {
-      if (this.ws && this.ws.readyState == 1) {
-        this.ws.send(JSON.stringify({ id: "0" }));
-      }
-      setTimeout(() => {
-        this.ws.close();
-        if (this.url) URL.revokeObjectURL(this.url);
-        this.url = "";
-        this.lastUrl = "";
-        this.zoom = false;
-        this.$emit("update:cameraData", {});
-        this.$emit("update:visible", false);
-      }, 500);
-    },
-    playBackHandle() {
-      if (this.ws && this.ws.readyState == 1) {
-        let time = Dayjs(this.startTime).format("YYYY-MM-DD HH:mm:ss");
-        this.ws.send(
-          JSON.stringify({ id: this.cameraData.id, startTime: time })
-        );
-      } else {
-        this.init(JSON.stringify({ id: this.cameraData.id, startTime: time }));
-      }
-      setTimeout(() => {
-        this.url = "";
-        if (this.lastUrl) URL.revokeObjectURL(this.lastUrl);
-        this.lastUrl = "";
-      }, 500);
-    },
-    realTimekHandle() {
-      if (this.ws && this.ws.readyState == 1) {
-        this.ws.send(JSON.stringify({ id: this.cameraData.id }));
-      } else {
-        this.init(JSON.stringify({ id: this.cameraData.id }));
-      }
-      setTimeout(() => {
-        this.url = "";
-        if (this.lastUrl) URL.revokeObjectURL(this.lastUrl);
-        this.lastUrl = "";
-      }, 500);
-    },
-    ptzCommHandle(comm, stop) {
-      if (this.ws && this.ws.readyState == 1) {
-        this.ws.send(
-          JSON.stringify({
-            id: this.cameraData.id,
-            ptzCommand: comm,
-            ptzStop: stop,
-            ptzSpeed: this.speed,
-          })
-        );
-      } else {
-        this.init(
-          JSON.stringify({
-            id: this.cameraData.id,
-            ptzCommand: comm,
-            ptzStop: stop,
-            ptzSpeed: this.speed,
-          })
-        );
-      }
-    },
-    ptzPresetHandle(comm) {
-      if (this.ws && this.ws.readyState == 1) {
-        this.ws.send(
-          JSON.stringify({
-            id: this.cameraData.id,
-            ptzPreset: comm,
-            ptzPresetIndex: this.presetIndex,
-          })
-        );
-      } else {
-        this.init(
-          JSON.stringify({
-            id: this.cameraData.id,
-            ptzPreset: comm,
-            ptzPresetIndex: this.presetIndex,
-          })
-        );
-      }
-    },
-  },
-};
-</script>
-
-<style lang="less" scoped>
-// .dialog-container {
-//   position: absolute;
-//   right: 20px;
-//   bottom: 20px;
-//   color: white;
-//   background-color: rgba(48, 65, 86, 0.8);
-//   box-sizing: border-box;
-//   border-radius: 20px;
-// }
-.dialog-container {
-  position: relative;
-  right: 0;
-  bottom: 0;
-  color: white;
-  background-color: rgba(48, 65, 86, 0.8);
-  box-sizing: border-box;
-  // border-radius: 20px;
-}
-.dialog-header {
-  position: absolute;
-  left: 20px;
-  top: 0px;
-  right: 20px;
-  height: 40px;
-  font-size: 20px;
-  padding-top: 10px;
-  float: left;
-}
-.close-icon {
-  margin-left: 10px;
-  float: right;
-}
-.close-icon:hover {
-  color: rgb(28, 188, 156);
-}
-.zoom-icon {
-  float: right;
-}
-.zoom-icon:hover {
-  color: rgb(28, 188, 156);
-}
-// .dialog-body {
-//   position: absolute;
-//   left: 20px;
-//   top: 40px;
-//   right: 20px;
-//   bottom: 20px;
-// }
-.dialog-body {
-  position: absolute;
-  left: 0;
-  top: 0;
-  right: 0;
-  bottom: 0;
-}
-.ptz-panel {
-  position: absolute;
-  top: 0px;
-  right: 0px;
-  bottom: 0px;
-  width: 150px;
-}
-.playback-panel {
-  position: absolute;
-  left: 0px;
-  right: 0px;
-  bottom: 0px;
-  height: 50px;
-  padding-top: 15px;
-}
-::v-deep .image-slot {
-  width: 100%;
-  height: 100%;
-  background-color: #f5f7fa;
-  color: #909399;
-  font-size: 30px;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-}
-</style>

+ 103 - 0
src/utils/WebSocketClient.js

@@ -0,0 +1,103 @@
+import { getWsUrl, getToken } from '@/utils'
+
+export class WebSocketClient {
+  //WebSocket URL
+  #url
+  //WebSocket实例
+  #ws
+  //重连间隔
+  #reconnectInterval
+  //心跳间隔
+  #heartbeatInterval
+  //心跳包内容
+  #heartbeatPacket
+  //心跳包定时器
+  #heartbeatTimer
+  //是否关闭连接
+  #closeWs = false
+
+  get readyState() {
+    return this.#ws ? this.#ws.readyState : undefined
+  }
+
+  constructor(url, reconnectInterval = 1000, heartbeatInterval = 30000, heartbeatPacket = '{}') {
+    this.#url = url
+    this.#reconnectInterval = reconnectInterval
+    this.#heartbeatInterval = heartbeatInterval
+    this.#heartbeatPacket = heartbeatPacket
+    this.connect()
+  }
+
+  connect() {
+    this.#closeWs = false
+    if (this.#ws && this.#ws.readyState === WebSocket.OPEN) {
+      return
+    }
+    this.#ws = new WebSocket(getWsUrl() + this.#url, [getToken()])
+    this.#ws.onopen = (event) => {
+      this.#startHeartbeat()
+      this.onopen(event)
+    }
+    this.#ws.onclose = (event) => {
+      console.log('onclose')
+      if (!this.#closeWs) this.#reconnect()
+      this.onclose(event)
+    }
+    this.#ws.onmessage = (event) => {
+      this.onmessage(event)
+    }
+    this.#ws.onerror = (event) => {
+      console.log('onerror')
+      this.#closeHeartbeat()
+      this.onerror(event)
+    }
+  }
+
+  send(message) {
+    if (this.#ws && this.#ws.readyState === WebSocket.OPEN) {
+      this.#ws.send(message)
+    } else {
+      console.error('[WebSocket] 未连接')
+    }
+  }
+
+  #reconnect() {
+    if (!this.#closeWs && this.#reconnectInterval > 0) {
+      console.log(`等待${this.#reconnectInterval}ms后重连`)
+      setTimeout(() => {
+        this.connect()
+      }, this.#reconnectInterval)
+    }
+  }
+
+  #startHeartbeat() {
+    if (this.#closeWs || !this.#heartbeatInterval || this.#heartbeatInterval <= 0) return
+    this.#closeHeartbeat()
+    this.#heartbeatTimer = setInterval(() => {
+      if (this.#ws && this.#ws.readyState === WebSocket.OPEN) {
+        this.#ws.send(this.#heartbeatPacket)
+      }
+    }, this.#heartbeatInterval)
+  }
+
+  #closeHeartbeat() {
+    if (this.#heartbeatTimer) {
+      clearInterval(this.#heartbeatTimer)
+      this.#heartbeatTimer = undefined
+    }
+  }
+
+  close() {
+    this.#closeWs = true
+    this.#closeHeartbeat()
+    if (this.#ws) {
+      this.#ws.close()
+      this.#ws = null
+    }
+  }
+
+  onopen(event) { }
+  onmessage(event) { }
+  onclose(event) { }
+  onerror(event) { }
+}

+ 17 - 1
src/utils/index.js

@@ -254,7 +254,23 @@ export const mapParams = () => {
   } //模拟
   return param1
 }
-
+//获取 ws URL
+export const getWsUrl = () => {
+  let BASE_URL = '/yapi' //接口代理
+  if (window.self !== window.top) {
+    // 在iframe中,使用iframe的地址
+    const str = window.self.location.protocol === 'https:' ? 'wss://' : 'ws://'
+    return str + window.self.location.host + BASE_URL
+  } else {
+    // 不在iframe中,使用当前地址
+    const str = window.location.protocol === 'https:' ? 'wss://' : 'ws://'
+    return str + window.location.host + BASE_URL
+  }
+}
+//获取token
+export const getToken = () => {
+  return localStorage.getItem("token");
+}
 /**
  * 批量导入组件
  */

+ 79 - 47
src/views/basePage/components/videoWindow/VideoImageWindow.vue

@@ -339,6 +339,7 @@ import Dayjs from "dayjs";
 import { mapGetters } from "vuex";
 import { getViewer } from "@/store/map/mapService";
 import { getCameraList } from "@/API/custom";
+import { WebSocketClient } from "@/utils/WebSocketClient";
 
 export default {
   name: "VideoImageWindow",
@@ -367,7 +368,7 @@ export default {
   watch: {
     cameraId: {
       handler(id) {
-        console.log(id);
+        console.log("摄像头参数", id, this.cameraData);
         this.baseDialogConfig.show = !!id;
         if (this.num > 0) {
           this.baseDialogConfig.left = 500;
@@ -418,6 +419,7 @@ export default {
         zIndex: 1,
       },
       ws: null,
+      ptzws: null,
       url: "",
       lastUrl: "",
       // 视频控制相关 start
@@ -452,7 +454,7 @@ export default {
     this.closeHandler();
   },
   mounted() {
-    this.init();
+    // this.init();
   },
   methods: {
     getTagValue(tag) {
@@ -489,43 +491,51 @@ export default {
     },
     init(str) {
       // this.socketList[`ws_${this.cameraId}`];
-      // this.ws = new WebSocket('ws://' + window.location.host + BASE_URL + '/VideoOverWebSocket')
-      this.ws = new WebSocket(VUE_APP_BASE_WS() + "/VideoOverWebSocket");
-      // this.ws = new WebSocket('wss://' + window.location.host + BASE_URL + '/VideoOverWebSocket')
+      // this.ws = new WebSocket(VUE_APP_BASE_WS() + "/VideoOverWebSocket");
+      this.ws = new WebSocketClient("/websocket/video");
+      this.ws.onopen = (evt) => {
+        if (str) {
+          this.ws.send(str);
+        }
+      };
       this.ws.onmessage = (evt) => {
         if (typeof evt.data === "string") {
           console.log(JSON.parse(evt.data).Msg);
         } else {
-          let _this = this;
-          // const updateUrl = _.debounce(function () {
           let currUrl = URL.createObjectURL(evt.data);
-          if (_this.lastUrl) URL.revokeObjectURL(_this.lastUrl);
-          _this.lastUrl = currUrl;
-          _this.url = currUrl;
-          // }, 500);
-          // updateUrl();
+          if (this.lastUrl) URL.revokeObjectURL(this.lastUrl);
+          this.lastUrl = currUrl;
+          this.url = currUrl;
         }
       };
-      this.handleSend(str);
-      // console.log(window);
     },
-    handleSend(str) {
-      if (this.ws.readyState === WebSocket.OPEN) {
-        this.ws.send(str);
-      } else if (this.ws.readyState == WebSocket.CONNECTING) {
-        // Wait for the open event, maybe do something with promises
-        // depending on your use case. I believe in you developer!
-        this.ws.addEventListener("open", () => this.handleSend(str));
-      } else {
-        // etc.
-      }
+    initPtz(str) {
+      // this.socketList[`ws_${this.cameraId}`];
+      // this.ws = new WebSocket(VUE_APP_BASE_WS() + "/VideoOverWebSocket");
+      this.ptzws = new WebSocketClient("/websocket/cameraPtz");
+      this.ptzws.onopen = (evt) => {
+        if (str) {
+          this.ptzws.send(str);
+        }
+      };
+      this.ptzws.onmessage = (evt) => {
+        if (typeof evt.data === "string") {
+          console.log(JSON.parse(evt.data).Msg);
+        } else {
+          let currUrl = URL.createObjectURL(evt.data);
+          if (this.lastUrl) URL.revokeObjectURL(this.lastUrl);
+          this.lastUrl = currUrl;
+          this.url = currUrl;
+        }
+      };
     },
     updateEvent(data, state) {
       const param = JSON.stringify(data);
       if (this.ws && this.ws.readyState == 1) {
-        this.handleSend(param);
+        this.ws.send(JSON.stringify(param));
       } else {
         this.init(param);
+        this.initPtz(param);
       }
       if (state) {
         setTimeout(() => {
@@ -536,38 +546,60 @@ export default {
       }
     },
     closeHandler() {
-      this.ws && this.ws.close();
+      if (this.ws && this.ws.readyState == 1) {
+        this.ws.send(JSON.stringify({ id: "0" }));
+      }
       setTimeout(() => {
+        this.ws.close();
         if (this.url) URL.revokeObjectURL(this.url);
         this.url = "";
         this.lastUrl = "";
-        this.ws &&
-          (this.ws.onclose = (evt) => {
-            // this.ws = null;
-            console.log("websocket已关闭");
-          });
       }, 500);
     },
     changePtzCommParam(comm, stop) {
-      if (!comm) return;
-      let param = {
-        id: this.cameraId,
-        canControl: true,
-        ptzCommand: comm,
-        ptzStop: stop,
-        ptzSpeed: this.speed,
-      };
-      this.updateEvent(param);
+      if (this.ptzws && this.ptzws.readyState == 1) {
+        this.ptzws.send(
+          JSON.stringify({
+            id: this.cameraId,
+            canControl: true,
+            ptzCommand: comm,
+            ptzStop: stop,
+            ptzSpeed: this.speed,
+          })
+        );
+      } else {
+        this.initPtz(
+          JSON.stringify({
+            id: this.cameraId,
+            canControl: true,
+            ptzCommand: comm,
+            ptzStop: stop,
+            ptzSpeed: this.speed,
+          })
+        );
+      }
     },
     changePtzPresetParam(comm) {
       if (!comm) return;
-      let param = {
-        id: this.cameraId,
-        canControl: true,
-        ptzPreset: comm,
-        ptzPresetIndex: this.presetIndex,
-      };
-      this.updateEvent(param);
+      if (this.ptzws && this.ptzws.readyState == 1) {
+        this.ptzws.send(
+          JSON.stringify({
+            id: this.cameraId,
+            canControl: true,
+            ptzPreset: comm,
+            ptzPresetIndex: this.presetIndex,
+          })
+        );
+      } else {
+        this.initPtz(
+          JSON.stringify({
+            id: this.cameraId,
+            canControl: true,
+            ptzPreset: comm,
+            ptzPresetIndex: this.presetIndex,
+          })
+        );
+      }
     },
     /**
      * state  true代表回放模式

+ 9 - 15
src/views/components/dialog/JGYTControlDialog/VideoImageWindow.vue

@@ -31,6 +31,7 @@
 </template>
 <script>
 import Dayjs from "dayjs";
+import { WebSocketClient } from "@/utils/WebSocketClient";
 import { mapGetters } from "vuex";
 export default {
   name: "VideoImageWindow",
@@ -151,8 +152,14 @@ export default {
     // 放大缩小
     init(str) {
       // this.socketList[`ws_${this.cameraId}`];
-      this.ws = new WebSocket(VUE_APP_BASE_WS() + "/VideoOverWebSocket");
+      this.ws = new WebSocketClient("/websocket/video");
+      // this.ws = new WebSocket(VUE_APP_BASE_WS() + "/VideoOverWebSocket");
       // this.ws = new WebSocket('wss://' + window.location.host + BASE_URL + '/VideoOverWebSocket')
+      this.ws.onopen = (evt) => {
+        if (str) {
+          this.ws.send(str);
+        }
+      };
       this.ws.onmessage = (evt) => {
         if (typeof evt.data === "string") {
           // console.log(JSON.parse(evt.data).Msg)
@@ -167,25 +174,12 @@ export default {
           // updateUrl();
         }
       };
-      this.handleSend(str);
-      // console.log(window);
-    },
-    handleSend(str) {
-      if (this.ws.readyState === WebSocket.OPEN) {
-        this.ws.send(str);
-      } else if (this.ws.readyState == WebSocket.CONNECTING) {
-        // Wait for the open event, maybe do something with promises
-        // depending on your use case. I believe in you developer!
-        this.ws.addEventListener("open", () => this.handleSend(str));
-      } else {
-        // etc.
-      }
     },
     updateEvent(data, state) {
       const param = JSON.stringify(data);
       console.log("云台控制传参内容", param, state);
       if (this.ws && this.ws.readyState == 1) {
-        this.handleSend(param);
+        this.ws.send(param);
       } else {
         this.init(param);
       }

+ 63 - 64
src/views/components/dialog/Onekey110Dialog.vue

@@ -91,6 +91,7 @@ import BaseDragBgDialog from "@/views/components/base/BaseDragBgDialog.vue";
 import { dealAlarm } from "@/API/common";
 import { getDataList } from "@/API/report";
 import { getSipList } from "@/API/sip";
+import { WebSocketClient } from "@/utils/WebSocketClient";
 import { mapGetters } from "vuex";
 export default {
   name: "oneKey110Dialog",
@@ -103,7 +104,9 @@ export default {
           this.getPhoneDataList();
           this.init();
         } else {
-          this.closeCall();
+          if (this.phones.length > 0) {
+            this.closeCall();
+          }
         }
       },
       // deep: true,
@@ -233,7 +236,7 @@ export default {
       });
     },
     beforeDialogClose() {
-      console.log("beforeDialogClose");
+      // console.log('beforeDialogClose')
       this.dispalyDialog = false;
     },
     click() {
@@ -253,61 +256,47 @@ export default {
       phone.ws.send(JSON.stringify({ cmd: 3 }));
       this.$refs.phone_trying.pause();
       this.$refs.phone_ringing.pause();
-      this.stopPeer();
     },
     async startPeer(phone) {
       if (this.pc) this.pc.close();
       this.pc = new RTCPeerConnection();
 
-      this.pc.ontrack = (evt) => {
-        console.log("ontrack");
-        this.$refs.callAudio.srcObject = evt.streams[0];
-      };
-
       this.localStream = await navigator.mediaDevices.getUserMedia({
         video: false,
         audio: true,
       });
       this.localStream.getTracks().forEach((track) => {
-        console.log("add local track " + track.kind + " to peer connection.");
-        console.log(track);
+        // console.log('add local track ' + track.kind + ' to peer connection.')
+        // console.log(track)
         this.pc.addTrack(track, this.localStream);
       });
 
-      this.pc.onicegatheringstatechange = () => {
-        console.log("onicegatheringstatechange: " + this.pc.iceGatheringState);
+      this.pc.ontrack = (evt) => {
+        // console.log('ontrack')
+        this.$refs.callAudio.srcObject = evt.streams[0];
       };
 
-      this.pc.oniceconnectionstatechange = () => {
-        console.log(
-          "oniceconnectionstatechange: " + this.pc.iceConnectionState
-        );
-        if (this.pc.iceConnectionState === "disconnected") {
-          this.handleHungup(phone);
-        }
-      };
+      // this.pc.onicegatheringstatechange = () => {
+      //   console.log('onicegatheringstatechange: ' + this.pc.iceGatheringState)
+      // }
 
-      this.pc.onconnectionstatechange = () => {
-        console.log("onconnectionstatechange: " + this.pc.connectionState);
-      };
+      // this.pc.oniceconnectionstatechange = () => {
+      //   console.log('oniceconnectionstatechange: ' + this.pc.iceConnectionState)
+      // }
 
-      this.pc.onsignalingstatechange = () => {
-        console.log("onsignalingstatechange: " + this.pc.signalingState);
-        // if (this.pc.signalingState == 'have-remote-offer') {
-        //   this.pc.createAnswer().then((answer) => {
-        //     this.pc.setLocalDescription(answer)
-        //     console.log('Sending answer SDP.')
-        //     console.log('SDP: ' + answer)
-        //     phone.ws.send(JSON.stringify({ cmd: 5, body: JSON.stringify(answer) }))
-        //   })
-        // }
-      };
+      // this.pc.onconnectionstatechange = () => {
+      //   console.log('onconnectionstatechange: ' + this.pc.connectionState)
+      // }
+
+      // this.pc.onsignalingstatechange = () => {
+      //   console.log('onsignalingstatechange: ' + this.pc.signalingState)
+      // }
 
       this.pc.onicecandidate = (event) => {
         if (event.candidate) {
-          console.log("new-ice-candidate:");
-          console.log(event.candidate.candidate);
-          console.log(event.candidate);
+          // console.log('new-ice-candidate:')
+          // console.log(event.candidate.candidate)
+          // console.log(event.candidate)
           phone.ws.send(
             JSON.stringify({ cmd: 4, body: JSON.stringify(event.candidate) })
           );
@@ -333,14 +322,16 @@ export default {
       }
     },
     async init() {
-      this.clientId = Math.random().toString(16).substring(2, 8);
+      this.clientId = Math.random().toString(16).substring(2, 8).slice();
       try {
         const res = await getSipList({ enable: true });
         if (res.code === 20000) {
           this.sips = res.data.content;
-          if (this.sips) {
+
+          if (this.sips && this.sips.length > 0) {
+            this.visible = true;
             this.phones = this.sips.map((a) => {
-              const ws = new WebSocket(VUE_APP_BASE_WS() + "/SipOverWebSocket");
+              const ws = new WebSocketClient("/websocket/sip");
               const obj = {
                 name: a.name,
                 no: "",
@@ -349,34 +340,44 @@ export default {
                 enableHungup: false,
                 ws,
               };
-              const _this = this;
+              ws.onopen = () => {
+                ws.send(
+                  JSON.stringify({ sipId: a.id, clientId: this.clientId })
+                );
+              };
               ws.onmessage = (evt) => {
                 const resp = JSON.parse(evt.data);
-                console.info(resp);
+                console.info("onmessage:", resp);
                 switch (resp.type) {
                   case "invite":
-                    this.phoneNumber = resp.no;
                     obj.no = resp.no;
                     obj.enableCall = false;
                     obj.enableAnswer = true;
                     obj.enableHungup = true;
-                    console.log(this.$refs.phone_ringing);
+                    this.activeName = obj.name;
+                    this.dispalyDialog = true;
                     this.$refs.phone_ringing.currentTime = 0;
                     this.$refs.phone_ringing.play();
                     break;
                   case "trying":
-                    this.$refs.phone_trying.currentTime = 0;
-                    this.$refs.phone_trying.play();
+                    // this.$refs.phone_trying.currentTime = 0
+                    // this.$refs.phone_trying.play()
                     break;
                   case "ringing":
-                    this.$refs.phone_ringing.currentTime = 0;
-                    this.$refs.phone_ringing.play();
+                    this.$refs.phone_trying.currentTime = 0;
+                    this.$refs.phone_trying.play();
                     break;
                   case "failed":
-                    this.$message.error(resp.msg);
+                    if (resp.msg == "Busy Here") this.$message.error("对方忙");
+                    else if (resp.msg != "Call cancelled by user.")
+                      this.$message.error(resp.msg);
                     this.stopPeer();
                     this.$refs.phone_trying.pause();
                     this.$refs.phone_ringing.pause();
+                    obj.no = "";
+                    obj.enableCall = true;
+                    obj.enableAnswer = false;
+                    obj.enableHungup = false;
                     break;
                   case "answered":
                     this.$refs.phone_trying.pause();
@@ -429,9 +430,15 @@ export default {
                       this.$message.error(resp.msg);
                       break;
                     }
-                    obj.enableCall = false;
-                    obj.enableAnswer = false;
-                    obj.enableHungup = true;
+                    if (resp.isSelf) {
+                      obj.enableCall = false;
+                      obj.enableAnswer = false;
+                      obj.enableHungup = true;
+                    } else {
+                      obj.enableCall = true;
+                      obj.enableAnswer = false;
+                      obj.enableHungup = false;
+                    }
                     break;
                   case "hungup":
                     this.stopPeer();
@@ -448,17 +455,15 @@ export default {
                     break;
                   case "getOffer":
                     const offer = JSON.parse(resp.body);
-                    console.log(
-                      "get offer: " + offer.type + " " + offer.sdp + "."
-                    );
+                    // console.log('get offer: ' + offer.type + ' ' + offer.sdp + '.')
                     this.startPeer(obj).then(() => {
                       this.pc.setRemoteDescription(
                         new RTCSessionDescription(offer)
                       );
                       this.pc.createAnswer().then((answer) => {
                         this.pc.setLocalDescription(answer);
-                        console.log("Sending answer SDP.");
-                        console.log("SDP: " + answer);
+                        // console.log('Sending answer SDP.')
+                        // console.log('SDP: ' + JSON.stringify(answer))
                         ws.send(
                           JSON.stringify({
                             cmd: 5,
@@ -469,17 +474,11 @@ export default {
                     });
                     break;
                   case "getIcecandidate":
-                    console.log("get icecandidate: ", resp.body);
+                    // console.log('get icecandidate: ', resp.body)
                     this.pc.addIceCandidate(JSON.parse(resp.body));
                     break;
                 }
               };
-              setTimeout(() => {
-                ws.send(
-                  JSON.stringify({ sipId: a.id, clientId: this.clientId })
-                );
-              }, 500);
-
               return obj;
             });
 

+ 0 - 591
src/views/components/dialog/VideoImageWindow copy.vue

@@ -1,591 +0,0 @@
-<template>
-  <div
-    class="video_container"
-    :class="largeScreen ? 'large_screen' : 'normal_screen'"
-  >
-    <!-- 弹窗内容 -->
-    <div class="dialog_box">
-      <!-- 头部 -->
-      <div class="dialog_header_box">
-        <div class="dialog_header_title">
-          {{ cameraData ? cameraData.name : "监控视频" }}
-        </div>
-      </div>
-      <!-- 内容 -->
-      <div
-        class="dialog_content_box"
-        :class="{ large_content: largeScreen }"
-        v-show="!!cameraId"
-      >
-        <div class="content_box">
-          <div
-            class="img_box"
-            :class="{ comm_ptz: largeScreen && cameraData.ptzEnable }"
-          >
-            <img :src="url" v-if="url" />
-            <div class="image_slot" v-else>
-              <i class="el-icon-video-camera"></i>
-              <span>无信号</span>
-            </div>
-          </div>
-          <div class="control_box" v-if="largeScreen && cameraData.ptzEnable">
-            <div class="video-opt">
-              <div class="comm-panel">
-                <div
-                  class="btn-box"
-                  v-for="item of commandConfig.direction"
-                  :key="item.type"
-                >
-                  <el-button
-                    type="primary"
-                    :icon="`el-icon-${item.type}`"
-                    size="mini"
-                    @mousedown.native="changePtzCommParam(item.command, false)"
-                    @mouseup.native="changePtzCommParam(item.command, true)"
-                  ></el-button>
-                </div>
-              </div>
-              <div class="zoom-panel">
-                <div
-                  class="btn-box"
-                  v-for="item of commandConfig.zoom"
-                  :key="item.type"
-                >
-                  <el-button
-                    type="primary"
-                    :icon="`el-icon-${item.type}`"
-                    size="mini"
-                    @mousedown.native="changePtzCommParam(item.command, false)"
-                    @mouseup.native="changePtzCommParam(item.command, true)"
-                  ></el-button>
-                </div>
-              </div>
-              <div class="speed-panel">
-                <span class="form-label">速度:</span>
-                <el-select
-                  class="myInput"
-                  size="mini"
-                  v-model="speed"
-                  placeholder="请选择速度"
-                >
-                  <el-option
-                    v-for="item in Array.from(
-                      { length: 7 },
-                      (item, index) => index + 1
-                    )"
-                    :key="item"
-                    :label="item"
-                    :value="item"
-                  ></el-option>
-                </el-select>
-              </div>
-              <div class="preset-panel">
-                <span class="form-label">预置点:</span>
-                <el-select
-                  class="myInput"
-                  size="mini"
-                  v-model="presetIndex"
-                  placeholder="请选择预置点"
-                >
-                  <el-option
-                    v-for="item in Array.from(
-                      { length: 255 },
-                      (item, index) => index + 1
-                    )"
-                    :key="item"
-                    :label="item"
-                    :value="item"
-                  ></el-option>
-                </el-select>
-                <el-button
-                  type="primary"
-                  size="mini"
-                  @click="changePtzPresetParam(8)"
-                  >设置</el-button
-                >
-                <el-button
-                  type="primary"
-                  size="mini"
-                  @click="changePtzPresetParam(39)"
-                  >转到</el-button
-                >
-              </div>
-              <div class="playback-panel">
-                <span class="form-label">时间:</span>
-                <el-date-picker
-                  style="max-width: 170px"
-                  class="myInput"
-                  v-model="startTime"
-                  type="datetime"
-                  placeholder="选择回放开始时间"
-                  size="mini"
-                ></el-date-picker>
-                <el-button
-                  type="success"
-                  size="mini"
-                  @click="changePlayTypeParam(true)"
-                  >回放</el-button
-                >
-                <el-button
-                  type="primary"
-                  size="mini"
-                  @click="changePlayTypeParam(false)"
-                  >实时</el-button
-                >
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-      <div
-        class="dialog_content_box"
-        :class="{ large_content: largeScreen }"
-        v-show="cameraData && !cameraId"
-        style="color: #fff"
-      >
-        <!-- 暂无数据 -->
-        <!-- <img :src="require(`@/assets/imgs/maps/video_img_${cameraData.id}.png`)" alt="" /> -->
-      </div>
-      <div class="info_box">
-        <span class="label">浓度</span>
-        <span class="value">{{ ndValue || 0 }}</span>
-        <span class="unit">ppm.m</span>
-      </div>
-    </div>
-  </div>
-</template>
-<script>
-import Dayjs from "dayjs";
-import { mapGetters } from "vuex";
-export default {
-  name: "VideoImageWindow",
-  props: {
-    cameraId: {
-      type: String,
-      require: true,
-    },
-    cameraData: {
-      type: Object,
-      default: () => ({}),
-    },
-  },
-  computed: {
-    ...mapGetters(["rtData"]),
-  },
-  watch: {
-    rtData: {
-      handler(newVal) {
-        console.log(newVal);
-        if (!!this.cameraData.tags) {
-          if (!!this.cameraData.dataList) {
-            this.cameraData.dataList.forEach((item) => {
-              if (!!item.tag && !!newVal[item.tag]) {
-                item.value = newVal[item.tag].value + " " + item.unit;
-              }
-              if (item.isTag && !!newVal[item.value]) {
-              }
-            });
-          }
-        }
-      },
-      deep: true,
-    },
-  },
-  data() {
-    return {
-      largeScreen: false,
-      ws: null,
-      url: "",
-      lastUrl: "",
-      // 视频控制相关 start
-      commandConfig: {
-        //视频控制开关配置
-        direction: [
-          //方向
-          { type: "top-left", name: "左上", command: 25 },
-          { type: "top", name: "上", command: 21 },
-          { type: "top-right", name: "右上", command: 26 },
-          { type: "back", name: "左", command: 23 },
-          { type: "rank", name: "中", command: 0 },
-          { type: "right", name: "右", command: 24 },
-          { type: "bottom-left", name: "左下", command: 27 },
-          { type: "bottom", name: "下", command: 22 },
-          { type: "bottom-right", name: "右下", command: 28 },
-        ],
-        zoom: [
-          //放大缩小
-          { type: "zoom-in", name: "放大", command: 11 },
-          { type: "zoom-out", name: "缩小", command: 12 },
-        ],
-      },
-      startTime: null,
-      speed: 4,
-      presetIndex: 1,
-      // 视频控制 end
-      ndValue: "0",
-    };
-  },
-  beforeDestroy() {
-    this.closeHandler();
-  },
-  created() {
-    this.init(JSON.stringify({ id: this.cameraId }));
-  },
-  mounted() {
-    const ndTopic = "DataCommunication/TagData/" + this.cameraData.tags.ndTag;
-    const _this = this;
-    this.$store.dispatch("mqtt/subscribe", {
-      topic: [ndTopic],
-      onMessage: (topic, message, packet) => {
-        // console.log(topic);
-        const content = message.toString();
-        if (topic === ndTopic) {
-          //console.log(new Date(), content);
-          const data = JSON.parse(content);
-          _this.ndValue = data.Value;
-        }
-      },
-    });
-    this.$once("hook:beforeDestroy", () => {
-      this.$store.dispatch("mqtt/unsubscribe", [ndTopic]);
-    });
-  },
-  methods: {
-    getTagValue(tag) {
-      return this.rtData[this.cameraData.tags[tag]]?.Value || 0;
-    },
-    // 放大缩小
-    showVideoPopup(data) {
-      this.largeScreen = data;
-      this.$nextTick(() => {
-        if (data) {
-          this.baseDialogConfig.width = 937;
-          this.baseDialogConfig.height = 598;
-          this.baseDialogConfig.center = true;
-        } else {
-          this.baseDialogConfig.width = 420;
-          this.baseDialogConfig.height = 252;
-          this.baseDialogConfig.center = false;
-        }
-      });
-    },
-    init(str) {
-      // this.socketList[`ws_${this.cameraId}`];
-      this.ws = new WebSocket(
-        VUE_APP_BASE_WS() + "/VideoOverWebSocket"
-        // "wss://" + window.location.host + BASE_URL + "/VideoOverWebSocket"
-      );
-      this.ws.onmessage = (evt) => {
-        if (typeof evt.data === "string") {
-          console.log(JSON.parse(evt.data).Msg);
-        } else {
-          let _this = this;
-          // const updateUrl = _.debounce(function () {
-          let currUrl = URL.createObjectURL(evt.data);
-          if (_this.lastUrl) URL.revokeObjectURL(_this.lastUrl);
-          _this.lastUrl = currUrl;
-          _this.url = currUrl;
-          // }, 500);
-          // updateUrl();
-        }
-      };
-      this.handleSend(str);
-      // console.log(window);
-    },
-    handleSend(str) {
-      if (this.ws.readyState === WebSocket.OPEN) {
-        this.ws.send(str);
-      } else if (this.ws.readyState == WebSocket.CONNECTING) {
-        // Wait for the open event, maybe do something with promises
-        // depending on your use case. I believe in you developer!
-        this.ws.addEventListener("open", () => this.handleSend(str));
-      } else {
-        // etc.
-      }
-    },
-    updateEvent(data, state) {
-      const param = JSON.stringify(data);
-      if (this.ws && this.ws.readyState == 1) {
-        this.handleSend(param);
-      } else {
-        this.init(param);
-      }
-      if (state) {
-        setTimeout(() => {
-          this.url = "";
-          if (this.lastUrl) URL.revokeObjectURL(this.lastUrl);
-          this.lastUrl = "";
-        }, 500);
-      }
-    },
-    closeHandler() {
-      this.ws && this.ws.close();
-      setTimeout(() => {
-        if (this.url) URL.revokeObjectURL(this.url);
-        this.url = "";
-        this.lastUrl = "";
-        this.ws &&
-          (this.ws.onclose = (evt) => {
-            // this.ws = null;
-            console.log("websocket已关闭");
-          });
-      }, 500);
-    },
-    changePtzCommParam(comm, stop) {
-      if (!comm) return;
-      let param = {
-        id: this.cameraId,
-        ptzCommand: comm,
-        ptzStop: stop,
-        ptzSpeed: this.speed,
-      };
-      this.updateEvent(param);
-    },
-    changePtzPresetParam(comm) {
-      if (!comm) return;
-      let param = {
-        id: this.cameraId,
-        ptzPreset: comm,
-        ptzPresetIndex: this.presetIndex,
-      };
-      this.updateEvent(param);
-    },
-    /**
-     * state  true代表回放模式
-     */
-    changePlayTypeParam(state) {
-      let param = { id: this.cameraId };
-      if (state) {
-        if (!this.startTime) {
-          this.$message.warning("请选择回放开始时间!");
-          return;
-        }
-        param["startTime"] = Dayjs(this.startTime).format(
-          "YYYY-MM-DD HH:mm:ss"
-        );
-      }
-      this.updateEvent(param, true);
-    },
-  },
-};
-</script>
-<style lang="less" scoped>
-.normal_screen {
-  background: url("~@/assets/imgs/maps/img_SPJK_bg@2x.png") center center
-    no-repeat;
-  background-size: 100% 100%;
-  width: 1.984375rem /* 381/192 */;
-  height: 1.385417rem /* 266/192 */;
-}
-.large_screen {
-  background: url("~@/assets/imgs/dialog/pop_enlarge_TW@2x.png") center center
-    no-repeat;
-  background-size: 100% 100%;
-  width: 4.880208rem /* 937/192 */;
-  height: 3.114583rem /* 598/192 */;
-}
-.video_container {
-  pointer-events: auto;
-  .dialog_box {
-    width: 100%;
-    height: 100%;
-    .dialog_header_box {
-      position: relative;
-      .dialog_header_title {
-        width: 100%;
-        height: 35px;
-        line-height: 40px;
-        // font-family: 时尚中黑简体;
-        font-size: 16px;
-        padding-left: 24px;
-        color: #fff;
-      }
-      .dialog_header_retract {
-        // line-height: 40px;
-        position: absolute;
-        top: -0.041667rem /* 8/192 */;
-        right: 0.182292rem /* 35/192 */;
-        cursor: pointer;
-        img {
-          width: 20px;
-          height: 20px;
-        }
-      }
-      .dialog_header_close {
-        // line-height: 40px;
-        position: absolute;
-        top: -0.041667rem /* 8/192 */;
-        right: 0.052083rem /* 10/192 */;
-        img {
-          width: 20px;
-          height: 20px;
-        }
-        cursor: pointer;
-      }
-    }
-    .dialog_content_box {
-      box-sizing: border-box;
-      width: 342px;
-      height: 173px;
-      margin: 0 auto;
-      display: flex;
-      justify-content: center;
-      align-items: center;
-      .content_box {
-        width: 100%;
-        height: 100%;
-        display: flex;
-        .img_box {
-          width: 100%;
-          height: 100%;
-          &.comm_ptz {
-            width: 75%;
-          }
-        }
-        .control_box {
-          width: 25%;
-          height: 100%;
-          border-left: 1px solid #7bacaa;
-        }
-      }
-
-      img {
-        width: 100%;
-        height: 100%;
-        object-fit: fill;
-        // display: block;
-      }
-      .image_slot {
-        width: 100%;
-        height: 100%;
-        // background-color: #f5f7fa;
-        color: #f5f7fa;
-        font-size: 24px;
-        display: flex;
-        flex-direction: column;
-        justify-content: center;
-        align-items: center;
-        font-weight: 500;
-        span {
-          font-size: 16px;
-        }
-      }
-
-      &.large_content {
-        width: 4.630208rem /* 889/192 */;
-        height: 2.770833rem /* 532/192 */;
-      }
-    }
-    .info_box {
-      width: 100%;
-      height: 32px;
-      margin-top: 6px;
-      display: flex;
-      justify-content: center;
-      align-items: center;
-      font-size: 14px;
-      color: #fff;
-      .value {
-        padding: 0 8px;
-        color: #ffc800;
-      }
-    }
-  }
-}
-.video-opt {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  padding: 10px;
-  .comm-panel {
-    width: 180px;
-    display: flex;
-    flex-wrap: wrap;
-    .el-button {
-      background: #51c6c1;
-      border-color: #51c6c1;
-    }
-  }
-  .zoom-panel {
-    width: 120px;
-    display: flex;
-    .el-button {
-      background: #51c6c1;
-      border-color: #51c6c1;
-    }
-  }
-  .speed-panel {
-    margin-top: 10px;
-    display: flex;
-    align-items: center;
-  }
-  .preset-panel {
-    margin-top: 10px;
-    display: flex;
-    align-items: center;
-    flex-wrap: wrap;
-    .el-select {
-      width: 150px;
-    }
-    .el-button {
-      margin-top: 10px;
-      background: rgba(0, 60, 57, 0.3);
-      border: 1px solid #65f1ea;
-      color: #7cd6dd;
-    }
-  }
-  .playback-panel {
-    margin-top: 10px;
-    display: flex;
-    align-items: center;
-    flex-wrap: wrap;
-    .form-label {
-      margin-bottom: 10px;
-    }
-    .el-button {
-      margin-top: 15px;
-      background: rgba(0, 60, 57, 0.3);
-      border: 1px solid #65f1ea;
-      color: #7cd6dd;
-    }
-  }
-  .btn-box {
-    flex-shrink: 0;
-    width: 60px;
-    height: 40px;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-  }
-  .form-label {
-    color: #7cd6dd;
-    display: inline-block;
-    width: 50px;
-    flex-shrink: 0;
-    // text-align: right;
-  }
-  .myInput {
-    ::v-deep .el-input__inner {
-      // width: 110px;
-      height: 30px;
-      border-radius: 0;
-      background: rgba(0, 20, 23, 0.6);
-      border: 0.5px solid #51c6c1;
-      color: #7cd6dd;
-      // padding-right: 35px;
-    }
-    ::v-deep .el-input__inner:focus {
-      border: 0.5px solid #51c6c1 !important;
-    }
-    ::v-deep .el-input.is-focus .el-input__inner {
-      border: 0.5px solid #51c6c1 !important;
-    }
-    ::v-deep .el-input__suffix {
-      right: 5px;
-    }
-    ::v-deep .el-input__icon {
-      line-height: 30px;
-    }
-  }
-}
-</style>

+ 76 - 76
src/views/components/dialog/VideoImageWindow.vue

@@ -8,14 +8,21 @@
           <div class="img_box">
             <img :src="url" v-if="url" />
             <div class="image_slot" v-else>
-              <img :src="require(`@/assets/imgs/panels/img_video@2x.png`)" alt="" />
+              <img
+                :src="require(`@/assets/imgs/panels/img_video@2x.png`)"
+                alt=""
+              />
               <!-- <i class="el-icon-video-camera"></i> -->
               <span>无信号</span>
             </div>
           </div>
         </div>
       </div>
-      <div class="dialog_content_box" v-show="cameraData && !cameraId" style="color: #fff">
+      <div
+        class="dialog_content_box"
+        v-show="cameraData && !cameraId"
+        style="color: #fff"
+      >
         <!-- 暂无数据 -->
         <!-- <img :src="require(`@/assets/imgs/maps/video_img_${cameraData.id}.png`)" alt="" /> -->
       </div>
@@ -23,10 +30,11 @@
   </div>
 </template>
 <script>
-import Dayjs from 'dayjs'
-import { mapGetters } from 'vuex'
+import Dayjs from "dayjs";
+import { WebSocketClient } from "@/utils/WebSocketClient";
+import { mapGetters } from "vuex";
 export default {
-  name: 'VideoImageWindow',
+  name: "VideoImageWindow",
   props: {
     cameraId: {
       type: String,
@@ -38,13 +46,13 @@ export default {
     },
   },
   computed: {
-    ...mapGetters(['rtData']),
+    ...mapGetters(["rtData"]),
   },
   watch: {
     cameraId: {
       handler(id) {
         if (!!id) {
-          this.updateEvent({ id }, true)
+          this.updateEvent({ id }, true);
         }
       },
       deep: true,
@@ -55,13 +63,13 @@ export default {
         // console.log(newVal)
         if (!!this.cameraData.tags) {
           if (!!this.cameraData.dataList) {
-            this.cameraData.dataList.forEach(item => {
+            this.cameraData.dataList.forEach((item) => {
               if (!!item.tag && !!newVal[item.tag]) {
-                item.value = newVal[item.tag].value + ' ' + item.unit
+                item.value = newVal[item.tag].value + " " + item.unit;
               }
               if (item.isTag && !!newVal[item.value]) {
               }
-            })
+            });
           }
         }
       },
@@ -72,126 +80,118 @@ export default {
     return {
       largeScreen: false,
       ws: null,
-      url: '',
-      lastUrl: '',
+      url: "",
+      lastUrl: "",
       // 视频控制相关 start
       commandConfig: {
         //视频控制开关配置
         direction: [
           //方向
-          { type: 'top-left', name: '左上', command: 25 },
-          { type: 'top', name: '上', command: 21 },
-          { type: 'top-right', name: '右上', command: 26 },
-          { type: 'back', name: '左', command: 23 },
-          { type: 'rank', name: '中', command: 0 },
-          { type: 'right', name: '右', command: 24 },
-          { type: 'bottom-left', name: '左下', command: 27 },
-          { type: 'bottom', name: '下', command: 22 },
-          { type: 'bottom-right', name: '右下', command: 28 },
+          { type: "top-left", name: "左上", command: 25 },
+          { type: "top", name: "上", command: 21 },
+          { type: "top-right", name: "右上", command: 26 },
+          { type: "back", name: "左", command: 23 },
+          { type: "rank", name: "中", command: 0 },
+          { type: "right", name: "右", command: 24 },
+          { type: "bottom-left", name: "左下", command: 27 },
+          { type: "bottom", name: "下", command: 22 },
+          { type: "bottom-right", name: "右下", command: 28 },
         ],
         zoom: [
           //放大缩小
-          { type: 'zoom-in', name: '放大', command: 11 },
-          { type: 'zoom-out', name: '缩小', command: 12 },
+          { type: "zoom-in", name: "放大", command: 11 },
+          { type: "zoom-out", name: "缩小", command: 12 },
         ],
       },
       startTime: null,
       speed: 4,
       presetIndex: 1,
       // 视频控制 end
-      ndValue: '0',
-    }
+      ndValue: "0",
+    };
   },
   beforeDestroy() {
-    this.closeHandler()
+    this.closeHandler();
   },
   created() {
-    this.init(JSON.stringify({ id: this.cameraId }))
+    this.init(JSON.stringify({ id: this.cameraId }));
   },
   mounted() {
-    const ndTopic = 'DataCommunication/TagData/' + this.cameraData.tags?.ndTag
-    const _this = this
-    this.$store.dispatch('mqtt/subscribe', {
+    const ndTopic = "DataCommunication/TagData/" + this.cameraData.tags?.ndTag;
+    const _this = this;
+    this.$store.dispatch("mqtt/subscribe", {
       topic: [ndTopic],
       onMessage: (topic, message, packet) => {
         // console.log(topic);
-        const content = message.toString()
+        const content = message.toString();
         if (topic === ndTopic) {
           //console.log(new Date(), content);
-          const data = JSON.parse(content)
-          _this.ndValue = data.Value
+          const data = JSON.parse(content);
+          _this.ndValue = data.Value;
         }
       },
-    })
-    this.$once('hook:beforeDestroy', () => {
-      this.$store.dispatch('mqtt/unsubscribe', [ndTopic])
-    })
+    });
+    this.$once("hook:beforeDestroy", () => {
+      this.$store.dispatch("mqtt/unsubscribe", [ndTopic]);
+    });
   },
   methods: {
     // 放大缩小
     init(str) {
       // this.socketList[`ws_${this.cameraId}`];
-      this.ws = new WebSocket(VUE_APP_BASE_WS() + '/VideoOverWebSocket')
+      this.ws = new WebSocketClient("/websocket/video");
+      // this.ws = new WebSocket(VUE_APP_BASE_WS() + '/VideoOverWebSocket')
       // this.ws = new WebSocket('wss://' + window.location.host + BASE_URL + '/VideoOverWebSocket')
-      this.ws.onmessage = evt => {
-        if (typeof evt.data === 'string') {
+      this.ws.onopen = (evt) => {
+        if (str) {
+          this.ws.send(str);
+        }
+      };
+      this.ws.onmessage = (evt) => {
+        if (typeof evt.data === "string") {
           // console.log(JSON.parse(evt.data).Msg)
         } else {
-          let _this = this
           // const updateUrl = _.debounce(function () {
-          let currUrl = URL.createObjectURL(evt.data)
-          if (_this.lastUrl) URL.revokeObjectURL(_this.lastUrl)
-          _this.lastUrl = currUrl
-          _this.url = currUrl
+          let currUrl = URL.createObjectURL(evt.data);
+          if (this.lastUrl) URL.revokeObjectURL(this.lastUrl);
+          this.lastUrl = currUrl;
+          this.url = currUrl;
           // }, 500);
           // updateUrl();
         }
-      }
-      this.handleSend(str)
-      // console.log(window);
-    },
-    handleSend(str) {
-      if (this.ws.readyState === WebSocket.OPEN) {
-        this.ws.send(str)
-      } else if (this.ws.readyState == WebSocket.CONNECTING) {
-        // Wait for the open event, maybe do something with promises
-        // depending on your use case. I believe in you developer!
-        this.ws.addEventListener('open', () => this.handleSend(str))
-      } else {
-        // etc.
-      }
+      };
     },
     updateEvent(data, state) {
-      const param = JSON.stringify(data)
+      const param = JSON.stringify(data);
       if (this.ws && this.ws.readyState == 1) {
-        this.handleSend(param)
+        this.ws.send(param);
       } else {
-        this.init(param)
+        this.init(param);
       }
       if (state) {
         setTimeout(() => {
-          this.url = ''
-          if (this.lastUrl) URL.revokeObjectURL(this.lastUrl)
-          this.lastUrl = ''
-        }, 500)
+          this.url = "";
+          if (this.lastUrl) URL.revokeObjectURL(this.lastUrl);
+          this.lastUrl = "";
+        }, 500);
       }
     },
     closeHandler() {
-      console.log('close video websocket')
-      this.ws && this.ws.close()
+      console.log("close video websocket");
+      this.ws && this.ws.close();
       setTimeout(() => {
-        if (this.url) URL.revokeObjectURL(this.url)
-        this.url = ''
-        this.lastUrl = ''
+        if (this.url) URL.revokeObjectURL(this.url);
+        this.url = "";
+        this.lastUrl = "";
         this.ws &&
-          (this.ws.onclose = evt => {
+          (this.ws.onclose = (evt) => {
             // this.ws = null;
-            console.log('websocket已关闭')
-          })
-      }, 500)
+            console.log("websocket已关闭");
+          });
+      }, 500);
     },
   },
-}
+};
 </script>
 <style lang="less" scoped>
 .video_container {

+ 60 - 62
src/views/components/dialog/VoiceCallDialog/Dialer.vue

@@ -71,6 +71,7 @@
   
 <script>
 import { getSipList } from "@/API/sip";
+import { WebSocketClient } from "@/utils/WebSocketClient";
 import { mapGetters } from "vuex";
 
 export default {
@@ -85,6 +86,7 @@ export default {
       // 拨号盘上的数字和符号
       numbers: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "0", "#"],
       // 拨号链接
+      visible: false,
       clientId: "",
       dispalyDialog: false,
       activeName: "",
@@ -146,7 +148,7 @@ export default {
       }
     },
     beforeDialogClose() {
-      console.log("beforeDialogClose");
+      // console.log('beforeDialogClose')
       this.dispalyDialog = false;
     },
     click() {
@@ -166,61 +168,47 @@ export default {
       phone.ws.send(JSON.stringify({ cmd: 3 }));
       this.$refs.phone_trying.pause();
       this.$refs.phone_ringing.pause();
-      this.stopPeer();
     },
     async startPeer(phone) {
       if (this.pc) this.pc.close();
       this.pc = new RTCPeerConnection();
 
-      this.pc.ontrack = (evt) => {
-        console.log("ontrack");
-        this.$refs.callAudio.srcObject = evt.streams[0];
-      };
-
       this.localStream = await navigator.mediaDevices.getUserMedia({
         video: false,
         audio: true,
       });
       this.localStream.getTracks().forEach((track) => {
-        console.log("add local track " + track.kind + " to peer connection.");
-        console.log(track);
+        // console.log('add local track ' + track.kind + ' to peer connection.')
+        // console.log(track)
         this.pc.addTrack(track, this.localStream);
       });
 
-      this.pc.onicegatheringstatechange = () => {
-        console.log("onicegatheringstatechange: " + this.pc.iceGatheringState);
+      this.pc.ontrack = (evt) => {
+        // console.log('ontrack')
+        this.$refs.callAudio.srcObject = evt.streams[0];
       };
 
-      this.pc.oniceconnectionstatechange = () => {
-        console.log(
-          "oniceconnectionstatechange: " + this.pc.iceConnectionState
-        );
-        if (this.pc.iceConnectionState === "disconnected") {
-          this.handleHungup(phone);
-        }
-      };
+      // this.pc.onicegatheringstatechange = () => {
+      //   console.log('onicegatheringstatechange: ' + this.pc.iceGatheringState)
+      // }
 
-      this.pc.onconnectionstatechange = () => {
-        console.log("onconnectionstatechange: " + this.pc.connectionState);
-      };
+      // this.pc.oniceconnectionstatechange = () => {
+      //   console.log('oniceconnectionstatechange: ' + this.pc.iceConnectionState)
+      // }
 
-      this.pc.onsignalingstatechange = () => {
-        console.log("onsignalingstatechange: " + this.pc.signalingState);
-        // if (this.pc.signalingState == 'have-remote-offer') {
-        //   this.pc.createAnswer().then((answer) => {
-        //     this.pc.setLocalDescription(answer)
-        //     console.log('Sending answer SDP.')
-        //     console.log('SDP: ' + answer)
-        //     phone.ws.send(JSON.stringify({ cmd: 5, body: JSON.stringify(answer) }))
-        //   })
-        // }
-      };
+      // this.pc.onconnectionstatechange = () => {
+      //   console.log('onconnectionstatechange: ' + this.pc.connectionState)
+      // }
+
+      // this.pc.onsignalingstatechange = () => {
+      //   console.log('onsignalingstatechange: ' + this.pc.signalingState)
+      // }
 
       this.pc.onicecandidate = (event) => {
         if (event.candidate) {
-          console.log("new-ice-candidate:");
-          console.log(event.candidate.candidate);
-          console.log(event.candidate);
+          // console.log('new-ice-candidate:')
+          // console.log(event.candidate.candidate)
+          // console.log(event.candidate)
           phone.ws.send(
             JSON.stringify({ cmd: 4, body: JSON.stringify(event.candidate) })
           );
@@ -246,15 +234,16 @@ export default {
       }
     },
     async init() {
-      this.clientId = Math.random().toString(16).substring(2, 8);
+      this.clientId = Math.random().toString(16).substring(2, 8).slice();
       try {
         const res = await getSipList({ enable: true });
         if (res.code === 20000) {
           this.sips = res.data.content;
 
-          if (this.sips) {
+          if (this.sips && this.sips.length > 0) {
+            this.visible = true;
             this.phones = this.sips.map((a) => {
-              const ws = new WebSocket(VUE_APP_BASE_WS() + "/SipOverWebSocket");
+              const ws = new WebSocketClient("/websocket/sip");
               const obj = {
                 name: a.name,
                 no: "",
@@ -263,34 +252,44 @@ export default {
                 enableHungup: false,
                 ws,
               };
-              const _this = this;
+              ws.onopen = () => {
+                ws.send(
+                  JSON.stringify({ sipId: a.id, clientId: this.clientId })
+                );
+              };
               ws.onmessage = (evt) => {
                 const resp = JSON.parse(evt.data);
-                console.info(resp);
+                console.info("onmessage:", resp);
                 switch (resp.type) {
                   case "invite":
-                    this.phoneNumber = resp.no;
                     obj.no = resp.no;
                     obj.enableCall = false;
                     obj.enableAnswer = true;
                     obj.enableHungup = true;
-                    console.log(this.$refs.phone_ringing);
+                    this.activeName = obj.name;
+                    this.dispalyDialog = true;
                     this.$refs.phone_ringing.currentTime = 0;
                     this.$refs.phone_ringing.play();
                     break;
                   case "trying":
-                    this.$refs.phone_trying.currentTime = 0;
-                    this.$refs.phone_trying.play();
+                    // this.$refs.phone_trying.currentTime = 0
+                    // this.$refs.phone_trying.play()
                     break;
                   case "ringing":
-                    this.$refs.phone_ringing.currentTime = 0;
-                    this.$refs.phone_ringing.play();
+                    this.$refs.phone_trying.currentTime = 0;
+                    this.$refs.phone_trying.play();
                     break;
                   case "failed":
-                    this.$message.error(resp.msg);
+                    if (resp.msg == "Busy Here") this.$message.error("对方忙");
+                    else if (resp.msg != "Call cancelled by user.")
+                      this.$message.error(resp.msg);
                     this.stopPeer();
                     this.$refs.phone_trying.pause();
                     this.$refs.phone_ringing.pause();
+                    obj.no = "";
+                    obj.enableCall = true;
+                    obj.enableAnswer = false;
+                    obj.enableHungup = false;
                     break;
                   case "answered":
                     this.$refs.phone_trying.pause();
@@ -343,9 +342,15 @@ export default {
                       this.$message.error(resp.msg);
                       break;
                     }
-                    obj.enableCall = false;
-                    obj.enableAnswer = false;
-                    obj.enableHungup = true;
+                    if (resp.isSelf) {
+                      obj.enableCall = false;
+                      obj.enableAnswer = false;
+                      obj.enableHungup = true;
+                    } else {
+                      obj.enableCall = true;
+                      obj.enableAnswer = false;
+                      obj.enableHungup = false;
+                    }
                     break;
                   case "hungup":
                     this.stopPeer();
@@ -362,17 +367,15 @@ export default {
                     break;
                   case "getOffer":
                     const offer = JSON.parse(resp.body);
-                    console.log(
-                      "get offer: " + offer.type + " " + offer.sdp + "."
-                    );
+                    // console.log('get offer: ' + offer.type + ' ' + offer.sdp + '.')
                     this.startPeer(obj).then(() => {
                       this.pc.setRemoteDescription(
                         new RTCSessionDescription(offer)
                       );
                       this.pc.createAnswer().then((answer) => {
                         this.pc.setLocalDescription(answer);
-                        console.log("Sending answer SDP.");
-                        console.log("SDP: " + answer);
+                        // console.log('Sending answer SDP.')
+                        // console.log('SDP: ' + JSON.stringify(answer))
                         ws.send(
                           JSON.stringify({
                             cmd: 5,
@@ -383,16 +386,11 @@ export default {
                     });
                     break;
                   case "getIcecandidate":
-                    console.log("get icecandidate: ", resp.body);
+                    // console.log('get icecandidate: ', resp.body)
                     this.pc.addIceCandidate(JSON.parse(resp.body));
                     break;
                 }
               };
-              setTimeout(() => {
-                ws.send(
-                  JSON.stringify({ sipId: a.id, clientId: this.clientId })
-                );
-              }, 500);
 
               return obj;
             });

+ 73 - 61
src/views/videoMonitor/components/videoContent.vue

@@ -1,7 +1,13 @@
 <template>
   <div class="img-container">
     <!-- <img :src="url" v-if="url" /> -->
-    <canvas class="canvas_box" ref="canvas" v-if="url" width="960" height="540" />
+    <canvas
+      class="canvas_box"
+      ref="canvas"
+      v-if="url"
+      width="960"
+      height="540"
+    />
     <div class="image-slot" v-else>
       <i class="el-icon-video-camera"></i>
       <span>无信号</span>
@@ -10,7 +16,8 @@
   </div>
 </template>
 <script>
-import { debounce, throttle } from 'lodash-es'
+import { debounce, throttle } from "lodash-es";
+import { WebSocketClient } from "@/utils/WebSocketClient";
 export default {
   props: {
     cameraData: {
@@ -20,18 +27,18 @@ export default {
   },
   data() {
     return {
-      url: '',
+      url: "",
       ws: null,
       startTime: null,
       speed: 4,
       presetIndex: 1,
-    }
+    };
   },
   watch: {
     cameraData: {
       handler(val) {
         if (val.id) {
-          this.updateEvent({ id: val.id }, true)
+          this.updateEvent({ id: val.id }, true);
         }
       },
       deep: true,
@@ -39,99 +46,104 @@ export default {
     },
   },
   mounted() {
-    this.$EventBus.$off('updateSendParam')
-    this.$EventBus.$on('updateSendParam', (data, state) => {
+    this.$EventBus.$off("updateSendParam");
+    this.$EventBus.$on("updateSendParam", (data, state) => {
       if (data.id === this.cameraData.id) {
-        this.updateEvent(data, state)
+        this.updateEvent(data, state);
       }
-    })
+    });
   },
   beforeDestroy() {
-    this.closeHandler()
+    this.closeHandler();
   },
   methods: {
     init(str) {
       // this.socketList[`ws_${this.cameraData.id}`];
-      const host = VUE_APP_BASE_WS() + '/VideoOverWebSocket'
+      // const host = VUE_APP_BASE_WS() + '/VideoOverWebSocket'
+      // this.ws = new WebSocket(host)
       // const host = 'wss://' + window.location.host + BASE_URL + '/VideoOverWebSocket'
-      this.ws = new WebSocket(host)
-      this.ws.onmessage = evt => {
-        if (typeof evt.data === 'string') {
-          console.log(JSON.parse(evt.data).Msg)
+      this.ws = new WebSocketClient("/websocket/video");
+      this.ws.onopen = (evt) => {
+        if (str) {
+          this.ws.send(str);
+        }
+      };
+      this.ws.onmessage = (evt) => {
+        if (typeof evt.data === "string") {
+          console.log(JSON.parse(evt.data).Msg);
         } else {
-          this.url = URL.createObjectURL(evt.data)
+          this.url = URL.createObjectURL(evt.data);
           this.$nextTick(() => {
-            this.updateUrl()
-          })
+            this.updateUrl();
+          });
         }
-      }
-      this.handleSend(str)
-      // console.log(window);
+      };
     },
     updateUrl() {
-      const canvas = this.$refs.canvas
-      const ctx = canvas.getContext('2d')
-      const img = new Image()
+      const canvas = this.$refs.canvas;
+      const ctx = canvas.getContext("2d");
+      const img = new Image();
       // console.log('canvas', canvas.width, canvas.height)
 
-      img.src = this.url
+      img.src = this.url;
       img.onload = () => {
         // console.log('img', img.width, img.height)
-        const canvasRatio = canvas.width / canvas.height
-        const imgRatio = img.width / img.height
+        const canvasRatio = canvas.width / canvas.height;
+        const imgRatio = img.width / img.height;
         if (imgRatio > canvasRatio) {
           // 图片宽高比大于画布宽高比,以宽度为基准进行缩放
-          const newWidth = canvas.width
-          const newHeight = newWidth / canvasRatio
-          ctx.clearRect(0, 0, newWidth, newHeight)
-          ctx.drawImage(img, 0, (canvas.height - newHeight) / 2, newWidth, newHeight)
+          const newWidth = canvas.width;
+          const newHeight = newWidth / canvasRatio;
+          ctx.clearRect(0, 0, newWidth, newHeight);
+          ctx.drawImage(
+            img,
+            0,
+            (canvas.height - newHeight) / 2,
+            newWidth,
+            newHeight
+          );
         } else {
           // 图片宽高比小于等于画布宽高比,以高度为基准进行缩放
-          const newHeight = canvas.height
-          const newWidth = newHeight * canvasRatio
-          ctx.clearRect(0, 0, newWidth, newHeight)
-          ctx.drawImage(img, (canvas.width - newWidth) / 2, 0, newWidth, newHeight)
+          const newHeight = canvas.height;
+          const newWidth = newHeight * canvasRatio;
+          ctx.clearRect(0, 0, newWidth, newHeight);
+          ctx.drawImage(
+            img,
+            (canvas.width - newWidth) / 2,
+            0,
+            newWidth,
+            newHeight
+          );
         }
-      }
+      };
     },
     // 100),
-    handleSend(str) {
-      if (this.ws.readyState === WebSocket.OPEN) {
-        this.ws.send(str)
-      } else if (this.ws.readyState == WebSocket.CONNECTING) {
-        // Wait for the open event, maybe do something with promises
-        // depending on your use case. I believe in you developer!
-        this.ws.addEventListener('open', () => this.handleSend(str))
-      } else {
-        // etc.
-      }
-    },
     updateEvent(data, state) {
-      const param = JSON.stringify(data)
+      const param = JSON.stringify(data);
       if (this.ws && this.ws.readyState == 1) {
-        this.handleSend(param)
+        this.ws.send(param);
       } else {
-        this.init(param)
+        this.init(param);
       }
       if (state) {
         setTimeout(() => {
-          this.url = ''
-        }, 500)
+          this.url = "";
+        }, 500);
       }
     },
     closeHandler() {
-      this.ws && this.ws.close()
+      this.ws && this.ws.close();
       setTimeout(() => {
-        if (this.url) URL.revokeObjectURL(this.url)
-        this.url = ''
-        this.ws.onclose = evt => {
-          this.ws = null
-          console.log('websocket已关闭')
-        }
-      }, 500)
+        if (this.url) URL.revokeObjectURL(this.url);
+        this.url = "";
+        this.ws.onclose = (evt) => {
+          this.ws = null;
+          console.log("websocket已关闭");
+        };
+      }, 500);
     },
   },
-}
+};
 </script>
 
 <style lang="less" scoped>

+ 10 - 16
src/views/videoMonitorType/components/videoContent.vue

@@ -17,6 +17,7 @@
 </template>
 <script>
 import { debounce, throttle } from "lodash-es";
+import { WebSocketClient } from "@/utils/WebSocketClient";
 export default {
   props: {
     cameraData: {
@@ -58,9 +59,15 @@ export default {
   methods: {
     init(str) {
       // this.socketList[`ws_${this.cameraData.id}`];
-      const host = VUE_APP_BASE_WS() + "/VideoOverWebSocket";
+      // const host = VUE_APP_BASE_WS() + "/VideoOverWebSocket";
       // const host = 'wss://' + window.location.host + BASE_URL + '/VideoOverWebSocket'
-      this.ws = new WebSocket(host);
+      // this.ws = new WebSocket(host);
+      this.ws = new WebSocketClient("/websocket/video");
+      this.ws.onopen = (evt) => {
+        if (str) {
+          this.ws.send(str);
+        }
+      };
       this.ws.onmessage = (evt) => {
         if (typeof evt.data === "string") {
           console.log(JSON.parse(evt.data).Msg);
@@ -71,8 +78,6 @@ export default {
           });
         }
       };
-      this.handleSend(str);
-      // console.log(window);
     },
     updateUrl() {
       const canvas = this.$refs.canvas;
@@ -113,21 +118,10 @@ export default {
       };
     },
     // 100),
-    handleSend(str) {
-      if (this.ws.readyState === WebSocket.OPEN) {
-        this.ws.send(str);
-      } else if (this.ws.readyState == WebSocket.CONNECTING) {
-        // Wait for the open event, maybe do something with promises
-        // depending on your use case. I believe in you developer!
-        this.ws.addEventListener("open", () => this.handleSend(str));
-      } else {
-        // etc.
-      }
-    },
     updateEvent(data, state) {
       const param = JSON.stringify(data);
       if (this.ws && this.ws.readyState == 1) {
-        this.handleSend(param);
+        this.ws.send(param);
       } else {
         this.init(param);
       }