【vue】视频调阅

一、效果展示

在这里插入图片描述

二、参考链接

easyPlayer: https://github.com/tsingsee/EasyPlayer.js
grid:https://www.ruanyifeng.com/blog/2019/03/grid-layout-tutorial.html
vue-fullscreen:https://mirari.cc/2017/08/14/%E5%85%A8%E5%B1%8F%E5%88%87%E6%8D%A2%E7%BB%84%E4%BB%B6vue-fullscreen/

三、准备工作

如果easyPlayer不可用,可以使用livePlayer,
参考链接https://www.liveqing.com/docs/manuals/LivePlayer.html#%E5%B1%9E%E6%80%A7-property

  1. 安装 @easydarwin/easyplayer 和 vue-fullscreen

  2. 在 main.js 中全局注册 vue-fullscreen

    import fullscreen from 'vue-fullscreen'
    Vue.use(fullscreen)
    
  3. 下载 EasyPlayer-element.min.js 放在根目录的 public 文件夹下,在 index.html 中引入

四、 代码实现

1. src/views/monitor.vue

<template>
  <div class="container">
    <div class="layout-main-sidebar">
      <TreeList @clickInfo="onClickInfo" />
    </div>
    <div class="layout-main-container">
      <div class="monitor-screen-top">
        <div
          v-for="(item1, index1) in layoutPlayerList"
          :key="index1"
          :class="[
            'layout-screen-btn',
            item1.containerClass,
            { active: item1.id === screenTypeActive }
          ]"
          @click="handleScreenType(item1)"
        >
          <div
            v-for="(item2, index2) in item1.children"
            :key="index2"
            :class="item2.itemClass"
          ></div>
        </div>
        <i class="el-icon-full-screen" @click="toggle()"></i>
      </div>
      <fullscreen
        :fullscreen.sync="fullscreen"
        class="monitor-screen-container"
      >
        <div :class="['layout-screen-container', playList.containerClass]">
          <div
            v-for="(item, index) in playList.children"
            :key="index"
            :class="item.itemClass"
            @click="onActiveIndex(index + 1)"
          >
            <Player
              :active-index="activeIndex"
              :index="index + 1"
              :id="item.id"
              :title="item.title"
              :url="item.url"
              @close="onClose"
            />
          </div>
        </div>
      </fullscreen>
    </div>
  </div>
</template>

<script>
import TreeList from '@/components/TreeList.vue'
import Player from '@/components/Player.vue'
export default {
  name: 'Monitor',
  components: {
    Player,
    TreeList
  },
  data() {
    return {
      screenTypeActive: 3, // 当前显示模式
      activeNum: 6, // 显示数量
      fullscreen: false, // 是否全屏
      // 当前播放对象
      playList: {
        id: 3,
        type: 6,
        containerClass: 'layout-screen_3',
        children: [
          { id: '', title: '', url: '', itemClass: 'screen-item_6-1' },
          { id: '', title: '', url: '' },
          { id: '', title: '', url: '' },
          { id: '', title: '', url: '' },
          { id: '', title: '', url: '' },
          { id: '', title: '', url: '' }
        ]
      },
      // 预定义所有对象
      layoutPlayerList: [
        {
          id: 1,
          type: 1,
          containerClass: 'layout-screen_1',
          children: [{ id: '', title: '', url: '' }]
        },
        {
          id: 2,
          type: 4,
          containerClass: 'layout-screen_2',
          children: [
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' }
          ]
        },
        {
          id: 3,
          type: 6,
          containerClass: 'layout-screen_3',
          children: [
            { id: '', title: '', url: '', itemClass: 'screen-item_6-1' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' }
          ]
        },
        {
          id: 5,
          type: 9,
          containerClass: 'layout-screen_3',
          children: [
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' }
          ]
        },
        {
          id: 9,
          type: 16,
          containerClass: 'layout-screen_4',
          children: [
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' },
            { id: '', title: '', url: '' }
          ]
        }
      ],
      activeIndex: 0 // 当前激活播放器
    }
  },
  methods: {
    // 点击头部按钮,如果点击的按钮,是当前显示的模式 return
    // 否则获取当前点击的 id,根据 id 匹配到预定义的数据,赋值给 playList
    // 设置当前显示模式
    handleScreenType(item) {
      if (this.screenTypeActive === item.id) return
      this.screenTypeActive = item.id
      const data = this.layoutPlayerList.find(
        item => item.id === this.screenTypeActive
      )
      this.playList = data
      this.activeNum = item.type
    },
    // 是否全屏显示
    toggle() {
      this.fullscreen = !this.fullscreen
    },
    // TreeList 组件传递过来的方法,点击获取节点数据
    // activeIndex 为 0 表示,没有预选播放位置
    // 设置当前播放对象的 id,title,url 等需要配置的属性,传递给 Player 组件
    // 因为 activeIndex 从 0 开始 ,如果 activeIndex >= activeNum 表示已经到了最后一个
    // 否则 activeIndex ++ ,播放位置到下一个
    onClickInfo(val) {
      if (this.activeIndex === 0) this.activeIndex = 1
      this.playList.children[this.activeIndex - 1].id = this.activeIndex + ''
      this.playList.children[this.activeIndex - 1].title = val.name
      this.playList.children[this.activeIndex - 1].url = val.url
      if (this.activeIndex >= this.activeNum) {
        this.activeIndex = 1
      } else {
        this.activeIndex++
      }
    },
    // 设置 activeIndex 为当前播放的 index
    // 目的:高亮显示容器、控制播放位置
    onActiveIndex(index) {
      this.activeIndex = index
    },
    // Player 组件传递过来的方法,点击关闭视频
    onClose(val) {
      this.playList.children[val - 1].id = ''
      this.playList.children[val - 1].title = ''
      this.playList.children[val - 1].url = ''
    }
  }
}
</script>

<style lang="less" scoped>
.container {
  display: flex;
  height: 100%;
}
</style>

2. src/style/monitor.less

.layout-main-sidebar {
  width: 15%;
  background-color: #ccc;
}

.layout-main-container {
  width: 85%;

  .monitor-screen-top {
    background-color: chocolate;
    display: flex;
    .layout-screen-btn {
      display: grid;
      width: 20px;
      height: 20px;
      margin: 10px 5px;
      cursor: pointer;
      div {
        height: 100%;
        border: 1px solid #333;
        background-color: white;
      }
    }
    .active {
      div {
        background-color: deepskyblue;
      }
    }
    .el-icon-full-screen {
      color: #fff;
      font-size: 23px;
      display: flex;
      align-items: center;
      margin-left: 5px;
      cursor: pointer;
    }
  }
  .monitor-screen-container {
    height: calc(100% - 40px);
    .layout-screen-container {
      width: 100%;
      height: 100%;
      display: grid;
      // > div 父子级关系只能一级
      > div {
        height: 100%;
      }
      .player-container {
        position: relative; // 如果不设置成 relative,会显示一个全屏窗口
        height: 100%;
        border: 1px solid red;
        box-sizing: border-box;
        .player-title {
          position: absolute;
          top: 0;
          left: 0;
          z-index: 1;
          color: #fff;
          background-color: rgba(0, 0, 0, 0.5);
          width: 100%;
          display: flex;
          justify-content: space-between;
          padding: 5px 10px;
          box-sizing: border-box;
          .el-icon-close {
            cursor: pointer;
          }
        }
      }
      .active-index {
        box-shadow: 0 0 10px 1px deepskyblue;
        border: 1px solid deepskyblue;
        z-index: 9999;
      }
    }
  }
}

// .layout-screen_1 {
// }

.layout-screen_2 {
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
}

.layout-screen_3 {
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 1fr 1fr 1fr;
}

.layout-screen_4 {
  grid-template-columns: 1fr 1fr 1fr 1fr;
  grid-template-rows: 1fr 1fr 1fr 1fr;
}

.screen-item_6-1 {
  grid-row: 1 / 3;
  grid-column: 1 / 3;
}

3. src/components/Player.vue

<template>
  <!-- 根据 monitor 父组件传递过来的数据,判断容器是否需要高亮显示 -->
  <div :class="['player-container', { 'active-index': activeIndex === index }]">
    <div class="player-title" v-if="id !== ''">
      <span> {{ title }}</span>
      <i class="el-icon-close" @click="handleClose" />
    </div>
    <EasyPlayer
      :videoUrl="url"
      live
      :fluent="fluent"
      :autoplay="autoplay"
      :aspect="aspect"
      stretch
    ></EasyPlayer>
  </div>
</template>

<script>
import EasyPlayer from '@easydarwin/easyplayer'
export default {
  name: 'Player',
  components: { EasyPlayer },
  props: {
    activeIndex: {
      type: Number,
      default: 0
    },
    index: {
      type: Number,
      default: 0
    },
    id: {
      type: String,
      default: ''
    },
    title: {
      type: String,
      default: ''
    },
    url: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      aspect: '100%',
      fluent: true,
      autoplay: true
    }
  },
  methods: {
    handleClose() {
      this.$emit('close', this.index)
    }
  }
}
</script>

<style></style>

4. src/components/TreeList.vue

<template>
  <div>
    <el-tree
      :data="data"
      :props="defaultProps"
      @node-click="handleNodeClick"
    ></el-tree>
  </div>
</template>
<script>
export default {
  data() {
    return {
      data: [
        {
          label: 'test1',
          children: [
            {
              label: 'test1',
              name: 'test1',
              url: 'http://cctvalih5ca.v.myalicdn.com/live/cctv1_2/index.m3u8'
            }
          ]
        },
        {
          label: 'test2',
          children: [
            {
              label: 'test2',
              name: 'test2',
              url: 'http://220.161.87.62:8800/hls/1/index.m3u8'
            }
          ]
        }
      ],
      defaultProps: {
        children: 'children',
        label: 'label'
      }
    }
  },
  methods: {
    handleNodeClick(data) {
      this.$emit('clickInfo', data)
    }
  }
}
</script>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值