0
点赞
收藏
分享

微信扫一扫

基于Vue3组件封装的技巧分享

weednoah 2024-09-25 阅读 27

​🌈个人主页:前端青山
🔥系列专栏:Vue篇
🔖人终将被年少不可得之物困其一生

目录

一、引言

二、环境搭建

三、代码解析

子组件部分

1. 导入模块和组件

2. 定义组件属性和事件

3. 初始化变量

4. 初始化视频插件

5. 控制分屏数量

6. 视频线路弹框按钮

7. 生命周期钩子

8. 初始化视频播放

9. 处理事件

10.调用插件播放

父组件调用

一、引言

随着视频监控技术的发展,许多应用场景需要集成实时视频监控功能。本文将详细介绍如何在 Vue 3 应用中集成海康 H5 监控视频播放功能,实现视频的实时播放、分屏显示以及全屏切换等功能。

二、环境搭建

为了确保代码能够正常运行,我们需要准备以下开发环境:

  • Vue 3:用于构建前端应用。

  • Element Plus:用于 UI 组件库。

  • 海康 H5 SDK:用于播放视频流。

三、代码解析

子组件部分

<template>
    <div class="play_windows" v-loading="loading" element-loading-background="rgba(122, 122, 122, 0.8)">
        <div class="tree-form">
            <el-tree
                ref="tree"
                :data="dataTree"
                :props="defaultProps"
                :highlight-current="true"
                @node-click="pitchOns"
            >
                <template #default="{ node, data }">
                    <span class="custom-tree-node">
                        {{ data.name }}
                    </span>
                </template>
            </el-tree>
        </div>
        <div class="video">
            <div class="dialog-slot-video-header-right">
                <el-button class="myButton" :class="{ 'active': videoIndex == 4 }" @click="videoTabClick(4)">1×1</el-button>
                <el-button class="myButton" :class="{ 'active': videoIndex == 1 }" @click="videoTabClick(1)">3×3</el-button>
                <el-button class="myButton" :class="{ 'active': videoIndex == 2 }" @click="videoTabClick(2)">4×4</el-button>
                <el-button class="myButton" :class="{ 'active': videoIndex == 3 }" @click="videoTabClick(3)">整体全屏</el-button>
            </div>
            <div id='corpvideo'></div>
        </div>
    </div>
</template>
  • 功能说明

    • play_windows 容器:包含两个主要部分,左侧为树形结构,右侧为视频播放区域。

    • el-tree 组件:展示监控设备树形结构,点击节点触发 pitchOns 方法。

    • 视频播放区域:通过按钮控制不同的分屏模式(1×1、3×3、4×4)及全屏模式。

    • id='corpvideo':指定视频播放容器的 ID,供 H5 SDK 使用。

1. 导入模块和组件
import { ref, onMounted, nextTick, defineProps, defineExpose, defineEmits, watch, onBeforeUnmount } from 'vue';
import { ElMessage } from 'element-plus';
import { videoallList } from '@/api/screenVideo/index';
import { getGetByCode } from '@/api/videoSurveillance/index';
  • 功能说明:导入了 Vue 的一些核心函数,如 refonMountednextTick 等,以及 Element Plus 的消息提示组件 ElMessage 和相关 API。

2. 定义组件属性和事件
const emit = defineEmits(["handleSpjkPOIClick"]);
const props = defineProps({
  playURL: String, // 视频 URL
  splitNum: Number, // 分屏播放,默认最大分屏 4*4
  dataTree: Object, // 树形数据
  defaultProps: Object
});
  • 功能说明:定义了组件的属性 props 和自定义事件 emit,用于接收父组件传递的数据和触发事件。

3. 初始化变量
let dataTree = ref<any>(props.dataTree);
let defaultProps = ref<any>(props.defaultProps);
let loading = ref<Boolean>(false);
let myPlugin = ref<any>(null);
let index = ref<any>(1); // 多屏播放显示第几个
let mode = ref<any>(1); // 0 为低级播放,1 为高级播放
const urlList = ref<any>([]);
let playURLs = ref<any>("");
const jsPlugin = (window as any).JSPlugin;
  • 功能说明:定义了一些响应式变量,用于存储数据和状态,如 loading 用于控制加载状态,myPlugin 用于存储视频插件实例。

4. 初始化视频插件
const myPlugins = () => {
  myPlugin.value = new jsPlugin({
    szId: 'corpvideo', // 需要英文字母开头,唯一性,必填
    szBasePath: '/h5player', // 必填,与 h5player.min.js 的引用目录一致
    bSupporDoubleClickFull: true, // 是否支持双击全屏,默认 true
    iMaxSplit: 4, // 分屏播放,默认最大分屏 4*4
    iCurrentSplit: splitNum.value,
    oStyle: {
      borderSelect: '#FFCC00',
    },
    openDebug: true,
  });
};
  • 功能说明:定义了一个方法 myPlugins,用于初始化视频插件实例,并配置相关参数。

5. 控制分屏数量
const tabPosition = ref<any>(1);
const playBackNum = (num: any) => {
  if (num == "3") {
    myPlugin.value.JS_FullScreenDisplay(true).then(
      () => { console.log(`wholeFullScreen success`) },
      (e: any) => { console.error(e) }
    );
    return;
  }
  if (num == splitNum.value) {
    return;
  }
  splitNum.value = num;
};
  • 功能说明:定义了一个方法 playBackNum,用于控制分屏数量,并支持全屏显示。

6. 视频线路弹框按钮
const splitNum = ref<any>(1);
const videoIndex = ref<number>(4); // 视频信息弹框默认值 index
const videoTabClick = (type: number) => {
  videoIndex.value = Number(type);
  if (type == 1) {
    splitNum.value = 3;
    const totalWindows = splitNum.value * splitNum.value;
    for (let i = 0; i < totalWindows; i++) {
      const url = urlList.value[i] || urlList.value[0];
      myPlugin.value.JS_Play(url, { playURL: url, mode: mode.value }, i).then(
        () => console.log(`Playing in window ${i}`),
        (e: any) => console.error('Error playing video', e)
      );
    }
  } else if (type == 2) {
    splitNum.value = 4;
    const totalWindows = splitNum.value * splitNum.value;
    for (let i = 0; i < totalWindows; i++) {
      const url = urlList.value[i] || urlList.value[0];
      myPlugin.value.JS_Play(url, { playURL: url, mode: mode.value }, i).then(
        () => console.log(`Playing in window ${i}`),
        (e: any) => console.error('Error playing video', e)
      );
    }
  } else if (type == 3) {
    myPlugin.value.JS_FullScreenDisplay(true).then(
      () => { console.log(`wholeFullScreen success`) },
      (e: any) => { console.error(e) }
    );
    return;
  } else if (type == 4) {
    splitNum.value = 1;
  }
  myPlugin.value.JS_ArrangeWindow(splitNum.value).then(
    () => { console.log(`arrangeWindow to ${splitNum.value}x${splitNum.value} success`) },
    (e: any) => { console.error(e) }
  );
  console.log(splitNum.value, '监控视频的值');
};
  • 功能说明:定义了一个方法 videoTabClick,用于控制视频线路弹框按钮,并根据不同的类型调整分屏数量和播放视频。

7. 生命周期钩子
onMounted(() => {
  nextTick(() => {
    myPlugins();
    // 事件回调绑定
    myPlugin.value.JS_SetWindowControlCallback({
      windowEventSelect: function (iWndIndex: any) { // 插件选中窗口回调
        console.log('windowSelect callback: ', iWndIndex);
      },
      pluginErrorHandler: function (iWndIndex: any, iErrorCode: any, oError: any) { // 插件错误回调
        console.log('pluginError callback: ', iWndIndex, iErrorCode, oError);
      },
      windowEventOver: function (iWndIndex: any) { // 鼠标移过回调
        // console.log(iWndIndex);
      },
      windowEventOut: function (iWndIndex: any) { // 鼠标移出回调
        // console.log(iWndIndex);
      },
      windowEventUp: function (iWndIndex: any) { // 鼠标mouseup事件回调
        // console.log(iWndIndex);
      },
      windowFullCreenChange: function (bFull: any) { // 全屏切换回调
        console.log('fullScreen callback: ', bFull);
      },
      firstFrameDisplay: function (iWndIndex: any, iWidth: any, iHeight: any) { // 首帧显示回调
        console.log('firstFrame loaded callback: ', iWndIndex, iWidth, iHeight);
      },
      performanceLack: function () { // 性能不足回调
        console.log('performanceLack callback: ');
      }
    });
  });
});
​
onBeforeUnmount(() => {
  console.log('切换了');
});
  • 功能说明:在组件挂载时初始化视频插件,并绑定事件回调。在组件卸载前执行清理操作。

8. 初始化视频播放
const initialize = (playURL: any, urls?: any[]) => {
  urlList.value = urls;
  playURLs.value = playURL;
  loading.value = true;
  index.value = myPlugin.value.currentWindowIndex;
  myPlugin.value.JS_Play(playURL, { playURL, mode: mode.value }, index.value).then(
    () => { loading.value = false }, // 成功操作
    (e: any) => { 
      loading.value = false;
      ElMessage.error('监控视频异常'); // 失败操作
    }
  );
};
  • 功能说明:定义了一个方法 initialize,用于初始化视频播放,并处理加载状态和错误提示。

9. 处理事件
const pitchOns = (e: any) => {
  if (!e || !e.self) {
    if (e.equipmentCoding) {
      handleAddChild(e);
    }
    return;
  }
  if (e.children) {
    emit("handleSpjkPOIClick", e.self.indexCode, '');
    return;
  } else {
    handleAddChild(e);
  }
};

const handleAddChild = (e: any) => {
  if (!e || !e.self) {
    if (e.equipmentCoding) {
      videoUrl(e.equipmentCoding);
    }
    return;
  }
  if (e.self.indexCode) {
    let params = {
      UnitIndexCode: e.self.indexCode,
    };
    videoallList(params).then((res: any) => {
      if (res.data.rows.length == 0) {
        emit("handleSpjkPOIClick", e.self.indexCode, '');
      } else {
        e.children = e.children || [];
        // 修改 res.data.rows 中所有数据对象的字段 equipmentName 变成 name
            res.data.rows = res.data.rows.map((child: any) => ({
              ...child,
              name: child.equipmentName, // 将 equipmentName 字段复制到 name 字段
              // 删除原 equipmentName 字段
            }));
            res.data.rows.forEach((child: any) => {
              e.children.push(child);
            });
            // 展开当前节点
            (e as any).expanded = true;
            }
          })
        }
}
10.调用插件播放
const videoUrl =(e:any)=>{
    let params = {
      equipmentCoding: e,
    };
    getGetByCode(params).then(res => {
      setTimeout(() => {
        initialize(res.data.url);
      }, 1);
    });
}
         // 使用 watch 监听 splitNum 的变化
   watch(splitNum, (newValue, oldValue) => {
  if (newValue !== oldValue) {
    myPlugins();
    myPlugin.value.JS_ArrangeWindow(splitNum.value).then(
          () => { console.log(`arrangeWindow to ${splitNum.value}x${splitNum.value} success`) },
          (e: any) => { console.error(e) }
        )
  }
});
//最后暴露方法
      defineExpose({
        initialize,
        myPlugins
      })

父组件调用

<template>
  <div>
    <ScreenMonitoring ref="screenmonitoring" :dataTree="dataTree" :defaultProps="defaultProps" @handleSpjkPOIClick="handleSpjkPOIClick" />
  </div>
</template>

<script setup>
import { ref, onMounted, nextTick } from 'vue';

const dataTree = ref<any>([]);
const dataTree1 = ref<any>([]);
let screenmonitoring = ref();
const defaultProps = {
  children: 'children',
  label: 'name',
};

onMounted(() => {
  nextTick(() => {
    let params = {};
    getTreeJson(params).then((res: any) => {
      let list = res.rows[0].children[9].children;

      list.forEach((item: any) => {
        extractNameAndRebuildTree(item);  // 对每个根节点执行递归提取
      });

      dataTree.value = list;  // 将处理后的列表赋值给 dataTree
    });
  });
});

const extractNameAndRebuildTree = (node: any) => {
  // 如果节点有 self,提取 name 并放在最外层
  if (node.self && node.self.name) {
    node.name = node.self.name;
  }
  // 如果节点有 children,递归处理每个子节点
  if (node.children && node.children.length > 0) {
    node.children.forEach((child: any) => {
      extractNameAndRebuildTree(child);
    });
  }
};
const handleSpjkPOIClick = (poiId: string, coord: string) => {
  let params = {
    UnitIndexCode: poiId,
  };
  getGetByCodes(params).then((res: any) => {
    setTimeout(() => {
      screenmonitoring.value.initialize(res.data.urls[0], res.data.urls);
    }, 1);
  });
};
</script>
  1. 初始化数据树

    • dataTreedataTree1 是两个响应式数组,用于存储处理后的数据。

    • screenmonitoring 是一个引用,用于指向 ScreenMonitoring 子组件的实例。

    • defaultProps 是传递给子组件的默认属性配置。

  2. 组件挂载时

    • nextTick():等待 DOM 更新完成后再执行后续操作。

    • getTreeJson(params):从服务器获取数据,并处理返回的数据。

    • extractNameAndRebuildTree(item):递归处理每个节点,提取 name 并放在最外层。

  3. 处理点击事件

    • handleSpjkPOIClick(poiId, coord):当点击某个 时,获取对应的视频 URL 并初始化视频播放。

    • getGetByCodes(params):从服务器获取视频 URL 数据。

    • setTimeout(() => { ... }, 1):延迟 1 毫秒后初始化视频播放。

通过这种方式,父组件能够有效地初始化数据树,并在点击 树状 时触发视频播放。

本文旨在详细介绍如何在 Vue 3 应用中集成海康 H5 监控视频播放功能,实现视频的实时播放、分屏显示以及全屏切换等功能。

举报

相关推荐

0 条评论