dedsudiyu 3 kuukautta sitten
vanhempi
commit
d52f599437

+ 5 - 2
src/components/voiceBroadcast/voiceBroadcast.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="voiceBroadcast">
+  <div class="voiceBroadcast" :class="showPermissionButton?'voiceBroadcastBack':''">
     <div class="null-box" @click="offButton()"></div>
     <p class="get-button"
       v-if="showPermissionButton"
@@ -376,6 +376,9 @@
   }
 </script>
 <style scoped lang="scss">
+  .voiceBroadcastBack{
+    background: rgba(0, 0, 0, 0.6) !important;
+  }
   .voiceBroadcast{
     height: 100%;
     width: 100%;
@@ -384,7 +387,7 @@
     display: flex;
     flex-direction: column;
     z-index: 10;
-    background: rgba(0, 0, 0, 0.4);
+    background: rgba(0, 0, 0, 0);
     .null-box {
       flex: 1;
     }

+ 1 - 1
src/views/alarmPage/index.vue

@@ -284,7 +284,7 @@
     <div class="bottom-max-big-box" v-if="warnType == 4 && !alarmOutType">
       <p class="center-text">预案已结束</p>
     </div>
-    <voiceBroadcast :trumpetList="trumpetList" v-if="voiceType"></voiceBroadcast>
+    <voiceBroadcast :trumpetList="trumpetList" v-show="voiceType"></voiceBroadcast>
     <fullH5PlayerVideo v-if="fullVideoType" :fullVideoProps="fullVideoProps"></fullH5PlayerVideo>
     <fullH5PlayerVideoTime v-if="fullVideoTypeBack" :fullVideoProps="fullVideoPropsBack"></fullH5PlayerVideoTime>
   </div>

+ 56 - 14
src/views/home.vue

@@ -1,6 +1,8 @@
 <!-- 首页 -->
 <template>
     <div class="home">
+      <videoBroadcastHk v-if="pageType == 'videoBroadcastHk'"></videoBroadcastHk>
+      <videoBroadcastHls v-if="pageType == 'videoBroadcastHls'"></videoBroadcastHls>
       <miniProgramPlayback v-if="pageType == 'miniProgramPlayback'"></miniProgramPlayback>
       <miniProgramVideo v-if="pageType == 'miniProgramVideo'"></miniProgramVideo>
       <miniProgramVideoMpegts v-if="pageType == 'miniProgramVideoMpegts'"></miniProgramVideoMpegts>
@@ -10,15 +12,23 @@
     </div>
 </template>
 <script>
+    import videoBroadcastHk from '@/views/videoBroadcast/videoBroadcastHk.vue';
+    import videoBroadcastHls from '@/views/videoBroadcast/videoBroadcastHls.vue';
     import miniProgramPlayback from '@/views/miniProgramPlayback/index.vue';
     import miniProgramVideo from '@/views/miniProgramVideo/index.vue';
     import miniProgramVideoMpegts from '@/views/miniProgramVideoMpegts/index.vue';
     import miniProgramVideoHls from '@/views/miniProgramVideoHls/index.vue';
     import chemicalsInstructionsVideo from '@/views/chemicalsInstructionsVideo/index.vue';
     import alarmPage from '@/views/alarmPage/index.vue';
+    import {
+      getConfigByType,
+    } from '@/api/index.js'
+    import { Encrypt,Decrypt} from '@/utils/secret'
     export default {
         name: 'home',
         components: {
+          videoBroadcastHk,
+          videoBroadcastHls,
           miniProgramPlayback,
           miniProgramVideo,
           miniProgramVideoMpegts,
@@ -39,19 +49,21 @@
           let text = decodeURIComponent(window.location.href);
           text = text.split("?")[1]
           if(text.indexOf('touken') != -1){
-            if(text.indexOf('mpegts') != -1){
-              this.$set(this,'pageType','miniProgramVideoHls');
-              // let userAgent = navigator.userAgent; //取得浏览器的userAgent字符串
-              // if (userAgent.indexOf("iPhone") > -1) {
-              //   console.log('1')
-              //   this.$set(this,'pageType','miniProgramVideoHls');
-              // }else if (userAgent.indexOf("Android") > -1 || userAgent.indexOf("Windows") > -1) {
-              //   console.log('2')
-              //   this.$set(this,'pageType','miniProgramVideoMpegts');
-              // }
-            }else{
-              this.$set(this,'pageType','miniProgramVideo');
-            }
+            Promise.all([
+              //获取开发配置
+              this.getConfigByType(),
+            ]).then((result) => {
+              //页面展示
+              if (text.indexOf('videoBroadcastHk') != -1){
+                this.$set(this,'pageType','videoBroadcastHk');
+              }else if(text.indexOf('videoBroadcastHls') != -1){
+                this.$set(this,'pageType','videoBroadcastHls');
+              }else if(text.indexOf('mpegts') != -1){
+                this.$set(this,'pageType','miniProgramVideoHls');
+              }else{
+                this.$set(this,'pageType','miniProgramVideo');
+              }
+            }).catch((error) => {})
           }else if(text.indexOf('alarmHK') != -1){
             localStorage.setItem('alarmUrl',window.location.href);
             this.$set(this,'pageType','alarm');
@@ -61,7 +73,37 @@
             this.$set(this,'pageType','chemicalsInstructionsVideo');
           }
         },
-        methods: {},
+        methods: {
+          //获取开发配置
+          getConfigByType() {
+            let text = decodeURIComponent(window.location.href);
+            let urlList = text.split("?")[1].split("&");
+            let urlData = {};
+            urlList.forEach((item) => {
+              urlData[item.split("=")[0]] = item.split("=")[1];
+            });
+            localStorage.setItem('touken',urlData.touken)
+            getConfigByType({category: 2, configType: 5}).then(response => {
+              let obj = JSON.parse(response.data.configValue)
+              //文件预览地址
+              localStorage.setItem('filePreviewUrl', 'https://' + obj.fileExtranetUrl)
+              //小程序视频地址
+              localStorage.setItem('cameraExtranetAgent', 'https://' + obj.cameraExtranetAgent)
+              //MQTT地址
+              localStorage.setItem('mqttUrl', Decrypt(obj.mqttExtranetUrl))
+              //MQTT地址-内网
+              localStorage.setItem('mqttIntranetUrl', Decrypt(obj.mqttIntranetUrl))
+              //MQTT账号
+              localStorage.setItem('mqttUser', Decrypt(obj.mqttExtranetUser))
+              //MQTT密码
+              localStorage.setItem('mqttPassword', Decrypt(obj.mqttExtranetPassword))
+              //文件浏览环境
+              localStorage.setItem('fileBrowseEnvironment', 'http://' + Decrypt(obj.fileBrowseEnvironment))
+              localStorage.setItem('fileBrowseEnvironmentExtranet', 'http://' + Decrypt(obj.fileBrowseEnvironmentExtranet))
+
+            })
+          },
+        },
     }
 </script>
 <style scoped lang="scss">

+ 34 - 0
src/views/videoBroadcast/videoBroadcastHk.vue

@@ -0,0 +1,34 @@
+<template>
+  <div class="videoBroadcastHk">
+    <div class="top-video-box">
+
+    </div>
+    <div class="bottom-broadcast-box">
+
+    </div>
+  </div>
+</template>
+<script>
+  export default {
+    name: 'videoBroadcastHk',
+    data () {
+      return {
+
+      }
+    },
+    created(){
+
+    },
+    mounted(){
+
+    },
+    methods:{
+
+    },
+  }
+</script>
+<style scoped lang="scss">
+  .videoBroadcastHk{
+
+  }
+</style>

+ 338 - 0
src/views/videoBroadcast/videoBroadcastHls.vue

@@ -0,0 +1,338 @@
+<template>
+  <div class="videoBroadcastHls">
+    <div class="top-video-box">
+      <div class="top-button-box">
+        <p :class="checkType == 1?'checkP':''" @click="checkButton(1)">实验室监控</p>
+        <p :class="checkType == 2?'checkP':''" @click="checkButton(2)">楼道监控</p>
+      </div>
+      <p class="null-text" v-if="nullType">{{nullType}}</p>
+      <div class="video-max-box" v-if="buttonType&&!fullVideoType">
+        <video class="video-box" id="video" controls autoplay muted
+               v-for="(item,index) in videoList" :key="index"
+               :width="item.width" :height="item.height"
+               :id="item.cameraIndexCode" :ref="item.cameraIndexCode"
+        ></video>
+      </div>
+    </div>
+    <div class="bottom-broadcast-box">
+      <voiceBroadcast :trumpetList="trumpetList" v-show="voiceType"></voiceBroadcast>
+      <p class="right-button-p" @click="voiceButton()">语音广播</p>
+    </div>
+  </div>
+</template>
+<script>
+  import Hls from 'hls.js'
+  import { iotCameraFindByCondition,iotAppSpeakerFindHorn } from "@/api/index";
+  import voiceBroadcast from '@/components/voiceBroadcast/voiceBroadcast.vue';
+  export default {
+    name: 'videoBroadcastHls',
+    components: {
+      voiceBroadcast
+    },
+    data () {
+      return {
+        checkType:1,
+        buttonType: false,
+        width: null,
+        height: null,
+        videoList: [],
+        nullType:false,
+        //报警视频数据
+        videoProps:null,
+        //全屏视频参数
+        fullVideoProps:{},
+        fullVideoType:false,
+        hlsPlayers: {}, // 存储每个视频的Hls实例
+        errors: {},      // 存储每个视频的错误状态
+        //喇叭数据
+        trumpetList:[],
+        voiceType:false,
+      }
+    },
+    created(){
+      const ratio = 0.5625;
+      const winWidth = window.innerWidth;
+      let width = parseInt(winWidth - 20);
+      let height = parseInt(this.accMul(width, ratio));
+      this.$set(this, 'width', width);
+      this.$set(this, 'height', height)
+    },
+    mounted(){
+      this.getUrl();
+    },
+    beforeDestroy() {
+      // 组件销毁前清理所有HLS实例
+      this.destroyAllHlsPlayers();
+    },
+    methods:{
+      voiceButton(){
+        this.$set(this,'voiceType',!this.voiceType);
+      },
+      checkButton(type){
+        if(this.checkType != type){
+          // 切换类型前先清理之前的HLS流
+          this.destroyAllHlsPlayers();
+          this.$set(this,'checkType',type);
+          this.$set(this, 'nullType', false);
+          this.$set(this,'videoList',[]);
+          this.$set(this, 'buttonType', false);
+          this.getUrl();
+        }
+      },
+      getUrl() {
+        let text = decodeURIComponent(window.location.href);
+        if(text.indexOf('touken') != -1){
+          this.subVideo(text);
+        }else{
+          this.$set(this,'nullType','参数异常,请联系管理员');
+        }
+      },
+      subVideo(text){
+        let self = this;
+        if(text.indexOf('touken') == -1){
+          this.$set(this,'nullType','touken参数异常,请联系管理员');
+          return
+        }
+        if(text.indexOf('source') == -1){
+          this.$set(this,'nullType','source参数异常,请联系管理员');
+          return
+        }
+        if(text.indexOf('type') == -1){
+          this.$set(this,'nullType','type参数异常,请联系管理员');
+          return
+        }
+        let urlList = text.split("?")[1].split("&");
+        let urlData = {};
+        urlList.forEach((item) => {
+          urlData[item.split("=")[0]] = item.split("=")[1];
+        });
+        localStorage.setItem('touken',urlData.touken)
+        // type 1.楼栋 2.楼层 3.楼道 4.实验室 5.楼道+实验室
+        let obj = {
+          page:'1',
+          pageSize:'20',
+          // protocol:window.location.href.indexOf('https') !== -1?'wss':'ws',
+          protocol:'wss',
+          streamType:1,
+          videoStreamType:'hls',
+        };
+        if(urlData.type == 1){
+          obj.buildId = urlData.buildId;
+        }else  if(urlData.type == 2){
+          obj.floorId = urlData.floorId;
+        }else  if(urlData.type == 3){
+          obj.passageway = urlData.floorId;
+        }else  if(urlData.type == 4 || urlData.type == 5){
+          if(this.checkType == 1){
+            obj.subIds = [urlData.subId];
+          }else if(this.checkType == 2){
+            obj.passageway = urlData.floorId;
+          }
+        }
+        if(urlData.source == '2'){
+          obj.source = 2;
+        }else if (urlData.source == '5'){
+          obj.source = 5;
+        }
+        if(urlData.type){
+          iotAppSpeakerFindHorn({subId:urlData.subId,floorId:urlData.floorId}).then(response => {
+            for (let i = 0; i < response.data.length; i++) {
+              response.data[i].type = false;
+            }
+            this.$set(this, 'trumpetList', response.data)
+            this.$set(this, 'voiceType', true)
+          })
+          iotCameraFindByCondition(obj).then(response => {
+            if (!response.data.records[0]){
+              this.$set(this,'nullType','视频异常,请联系管理员');
+            }else{
+              let list = [];
+              for(let i=0;i<response.data.records.length;i++){
+                let obj = {
+                  width: this.width, //(宽度:非必传-默认600)
+                  height: this.height, //(高度:非必传-默认338)
+                  cameraIndexCode: response.data.records[i].deviceNo,
+                };
+                if(response.data.records[i].streamUrl.indexOf('wss://') != -1){
+                  obj.url = 'https://'+response.data.records[i].streamUrl.split("://")[1];
+                }else{
+                  obj.url = response.data.records[i].streamUrl;
+                }
+                list.push(obj)
+              }
+              this.$set(this,'videoList',list)
+              self.$set(self, 'buttonType', true);
+              this.$nextTick(()=>{
+                setTimeout(function(){
+                  // 组件挂载后初始化所有视频播放器
+                  self.initVideoPlayers()
+                },1000);
+              })
+            }
+          });
+        }else{
+          this.$set(this,'nullType','参数异常,请联系管理员');
+        }
+      },
+      initVideoPlayers() {
+        let self = this;
+        // 先清理之前的HLS实例
+        self.destroyAllHlsPlayers();
+
+        for(let i=0;i<self.videoList.length;i++){
+          let url = self.videoList[i].url;
+          let videoId = self.videoList[i].cameraIndexCode;
+          let video = document.getElementById(videoId);
+
+          if (!video) continue;
+
+          // 先停止当前视频播放
+          video.pause();
+          video.src = '';
+          video.load();
+
+          if (Hls.isSupported()) {
+            let hls = new Hls({
+              enableWorker: true,
+              lowLatencyMode: true,
+              backBufferLength: 90
+            });
+
+            hls.loadSource(url);
+            hls.attachMedia(video);
+
+            hls.on(Hls.Events.MANIFEST_PARSED, function () {
+              console.log('m3u8 加载成功,可以播放');
+              video.play().catch(e => {
+                console.warn('自动播放被阻止:', e);
+              });
+            });
+
+            hls.on(Hls.Events.ERROR, function (e, data) {
+              console.error('hls.js 报错:', data);
+              if (data.fatal) {
+                switch(data.type) {
+                  case Hls.ErrorTypes.NETWORK_ERROR:
+                    console.log('网络错误,尝试重新加载');
+                    hls.startLoad();
+                    break;
+                  case Hls.ErrorTypes.MEDIA_ERROR:
+                    console.log('媒体错误,尝试恢复');
+                    hls.recoverMediaError();
+                    break;
+                  default:
+                    hls.destroy();
+                    delete self.hlsPlayers[videoId];
+                    break;
+                }
+              }
+            });
+
+            // 保存HLS实例
+            self.hlsPlayers[videoId] = hls;
+
+          } else if (video.canPlayType('application/vnd.apple.mpegurl')) {
+            // iOS 原生支持
+            video.src = url;
+            video.play().catch(e => {
+              console.warn('iOS自动播放被阻止:', e);
+            });
+          }
+        }
+      },
+      // 清理指定视频的HLS实例
+      destroyHlsPlayer(videoId) {
+        if (this.hlsPlayers[videoId]) {
+          const hls = this.hlsPlayers[videoId];
+          hls.destroy();
+          delete this.hlsPlayers[videoId];
+
+          // 清理video元素
+          const video = document.getElementById(videoId);
+          if (video) {
+            video.pause();
+            video.src = '';
+            video.load();
+          }
+          console.log(`已销毁视频 ${videoId} 的HLS实例`);
+        }
+      },
+      // 清理所有HLS实例
+      destroyAllHlsPlayers() {
+        Object.keys(this.hlsPlayers).forEach(videoId => {
+          this.destroyHlsPlayer(videoId);
+        });
+        this.hlsPlayers = {};
+        console.log('已清理所有HLS实例');
+      },
+      //乘法
+      accMul(arg1, arg2) {
+        var m = 0, s1 = arg1.toString(), s2 = arg2.toString();
+        try {
+          m += s1.split(".")[1].length
+        } catch (e) {
+        }
+        try {
+          m += s2.split(".")[1].length
+        } catch (e) {
+        }
+        return Number(s1.replace(".", "")) * Number(s2.replace(".", "")) / Math.pow(10, m)
+      },
+    },
+  }
+</script>
+<style scoped lang="scss">
+  .videoBroadcastHls{
+    flex:1;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+    .top-video-box{
+      flex:1;
+      overflow-y: scroll;
+      .top-button-box{
+        display: flex;
+        border-bottom:1px solid #999;
+        p{
+          flex:1;
+          text-align: center;
+          font-size:36px;
+          line-height:80px;
+          height:80px;
+          color:#999;
+        }
+        .checkP{
+          background-color: #0183FA;
+          color:#fff;
+        }
+      }
+      .null-text{
+        text-align: center;
+        font-size:16px;
+        line-height:80px;
+      }
+      .video-max-box {
+        padding-top:10px;
+        overflow-y: scroll;
+        overflow-x: hidden;
+      }
+      .video-box {
+        margin: 0 auto 10px;
+        display: inline-block
+      }
+    }
+    .bottom-broadcast-box{
+      height:100px;
+      .right-button-p{
+        color:#fff;
+        background-color: #0183fa;
+        flex:1;
+        height:80px;
+        line-height:82px;
+        border-radius:6px;
+        font-size:30px;
+        margin:10px 10px 10px 10px;
+      }
+    }
+  }
+</style>