先下载wvp-GB28181-pro项目
git clone https://gitee.com/pan648540858/wvp-GB28181-pro.git
多了解的话可以看以下该项目的gitee:https://gitee.com/pan648540858/wvp-GB28181-pro
后端有很多难点,具体要做的事情访问,连接:https://www.cnblogs.com/linjiaxin/p/17664391.html
将项目下载下来后,从项目中找到 jessibuca 文件夹,将其拷贝并复制到自己项目的 public 文件夹中,然后在index.html 文件中引入,看图 1、2、3
图1
图2
图3
当环境准备好后,就可以将封装好的 jessibuca 组件拷贝到自己的项目了
至于如何使用就看具体的需求了,如果按照 wvp-GB28181-pro 项目里的 live.vue 文件使用,展现出的效果如图2所示
图1
图2
但我做这个项目要求的样式不一样,如下图所示
代码
<template>
<div class="app-container fixed-full-size">
<div-backtop ref="pagescrollpart" style="padding:20px;flex:auto;width:100%">
<!-- <div style="position: fixed;width: 100vw;height: 100vh;background-color: red;top: 1px;left: 1px;z-index: 999999;"></div> -->
<div class="fu">
<div class="monitor">
<div class="title">沈阳高华液化石油气有限公司</div>
<div class="layout-btn">
<svg-icon icon-class="split1" :class="['icon-img',selectIcon == 0?'icon-active':'']" @click="iconChange(0)"/>
<svg-icon icon-class="split4" :class="['icon-img',selectIcon == 1?'icon-active':'']" @click="iconChange(1)"/>
<svg-icon icon-class="split9" :class="['icon-img',selectIcon == 2?'icon-active':'']" @click="iconChange(2)"/>
<svg-icon icon-class="fs" class="icon-img hs" @click="iconChange(3)"/>
<!-- <img class="icon-img" v-for="(item,index) in icons" :key="index" :src="selectIcon == index?item.active:item.noActive" @click="iconChange(index)"/> -->
</div>
<div :class="['monitor-layout',fullScreen?'full-screen':'monitor-content']" ref="screen">
<!-- <div style="width: 99%;height: 85vh;display: flex;flex-wrap: wrap;background-color: #000;"> -->
<div v-for="i in spilt" :key="i" class="play-box":style="liveStyle" @click="openAdd(i)">
<div v-if="!videoUrl[i-1]" class="play-title" >点击添加视频监控画面</div>
<div v-else class="play-item">
<player class="player" ref="player" :videoUrl="videoUrl[i-1]" fluent autoplay @screenshot="shot" @destroy="destroy"/>
<div class="btn-list">
<div class="icon-list">
<div class="node">
<img :src="qiye" />
<span>沈阳市高华液化石油气有限公司</span>
</div>
<div class="node">
<img :src="sxt" />
<span>库房工作区001</span>
</div>
</div>
<div class="function-list">
<svg-icon v-show="soundState[i-1] == 'play'" icon-class="sound" @click="soundFun(i,'stop')"/>
<svg-icon v-show="soundState[i-1] == 'stop'" icon-class="sound2" @click="soundFun(i,'play')"/>
<svg-icon icon-class="camera" @click="screenshot(i)"/>
<svg-icon v-show="playState[i-1] == 'play'" icon-class="split1" @click="playChange(i,'stop')"/>
<svg-icon v-show="playState[i-1] == 'stop'" icon-class="play" @click="playChange(i,'play')"/>
<svg-icon icon-class="clos" @click.stop="closeFun(i)"/>
<svg-icon icon-class="fs" @click="fullscreenSwich(i)"/>
</div>
</div>
</div>
</div>
<!-- </div> -->
<!-- <player ref="player" :videoUrl="videoUrl[i-1]" fluent autoplay @screenshot="shot"
@destroy="destroy"/> -->
</div>
</div>
<div class="warning-div">
<div class="real-time common">
<div class="row-one">
<div class="title real-time-title">实时警告</div>
<div class="jump">更多>></div>
</div>
<div class="for-item">
<div class="item-row-title">
<div class="title-element">
<div class="number number-real-time">
1
</div>
<div class="enterprise">
沈阳高华液化石油气有限公司
</div>
</div>
<div class="place">
<img />
<div>库房工作区</div>
</div>
</div>
<div class="item-row-info">
<div class=" frame frame-real-time">
<img />
</div>
<div class="default">
<div>
警告时间:2023-12-12 13:00:00
</div>
<div>
警告类型:二级警告
</div>
</div>
</div>
<div class="warning-content">
告警内容:有车辆停留在库房工作区
</div>
</div>
</div>
<div class="history common">
<div class="row-one">
<div class="title title-history">历史警告</div>
<div class="jump">更多>></div>
</div>
<div class="for-item">
<div class="item-row-title">
<div class="title-element">
<div class="number number-history">
1
</div>
<div class="enterprise">
沈阳高华液化石油气有限公司
</div>
</div>
<div class="place">
<img />
<div>库房工作区</div>
</div>
</div>
<div class="item-row-info">
<div class="frame frame-history">
<img />
</div>
<div class="default">
<div>
警告时间:2023-12-12 13:00:00
</div>
<div>
警告类型:二级警告
</div>
</div>
</div>
<div class="warning-content">
告警内容:有车辆停留在库房工作区
</div>
</div>
</div>
</div>
</div>
</div-backtop>
<el-dialog v-dialogDrag title="添加监控画面" :visible.sync="isShow" :close-on-click-modal="false" width="800px" class="padding-col" append-to-body>
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true">
<el-form-item label="视频通道" prop="videoChannel">
<el-input @keyup.enter.native="getList" v-model="queryParams.videoChannel" placeholder="请输入视频通道" clearable />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="getList">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" :data="tableDataList" >
<el-table-column type="index" width="55" />
<el-table-column label="视频通道" align="center" prop="videoChannel" />
<el-table-column label="操作" align="center" class-name="table-button" width="200">
<template slot-scope="scope" >
<el-button size="mini" icon="el-icon-edit" type="success" @click="selectFun(scope.row.url)"">选择</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="getList" />
<div slot="footer" class="dialog-footer">
<el-button @click="isShow=false">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import player from '.././components/jessibuca.vue'
const fs = require('@/assets/images/monitor/fs.png');
const qiye = require('@/assets/images/monitor/qiye.png');
const sxt = require('@/assets/images/monitor/sxt.png');
export default{
name:'realTimeMonitoring',
components: {
player
},
data(){
return {
//请求监控参数
queryParams:{
videoChannel:'',
pageNum: 1,
pageSize: 10,
},
//加载动画
loading:false,
//表格数据
tableDataList:[
{
videoChannel:'通道一',
url:"ws://192.168.2.142:80/rtp/34020000001320000002_34020000001320000002.live.flv",
},
{
videoChannel:'通道二',
url:"ws://192.168.2.142:80/rtp/34020000001320000001_34020000001320000001.live.flv",
},
{
videoChannel:'通道三',
url:"ws://192.168.2.142:80/rtp/34020000001320000003_34020000001320000003.live.flv",
},
{
videoChannel:'通道一',
url:"ws://192.168.2.142:80/rtp/34020000001320000002_34020000001320000002.live.flv",
},
{
videoChannel:'通道一',
url:"ws://192.168.2.142:80/rtp/34020000001320000002_34020000001320000002.live.flv",
},{
videoChannel:'通道一',
url:"ws://192.168.2.142:80/rtp/34020000001320000002_34020000001320000002.live.flv",
},{
videoChannel:'通道一',
url:"ws://192.168.2.142:80/rtp/34020000001320000002_34020000001320000002.live.flv",
},{
videoChannel:'通道一',
url:"ws://192.168.2.142:80/rtp/34020000001320000002_34020000001320000002.live.flv",
},{
videoChannel:'通道一',
url:"ws://192.168.2.142:80/rtp/34020000001320000002_34020000001320000002.live.flv",
},
,{
videoChannel:'通道一',
url:"ws://192.168.2.142:80/rtp/34020000001320000002_34020000001320000002.live.flv",
},
],
//分页条数
total:10,
//弹框显示
isShow:false,
//视频地址
videoUrl:[
''
// "ws://192.168.2.136:80/rtp/34020000001320000002_34020000001320000002.live.flv"
],
//被点击的视频索引
clickIndex:null,
// videoUrl:[],
//企业和摄像头图标
qiye:qiye,
sxt:sxt,
fs:fs,
//是否全屏展示
fullScreen:false,
// 被点击的图标
selectIcon:0,
//显示多少屏幕
spilt:1,
playerIdx: 0,//激活播放器
resizeObserver:null,
//记录播放状态的数组
playState:['play'],
soundState:['stop'],
}
},
mounted(){
//监听屏幕变化
this.resizeObserver = new ResizeObserver(entries => {
//判断是否全屏
if(
document.fullscreenElement ||
document.mozFullScreenElement ||
document.webkitFullscreenElement ||
document.msFullscreenElement
){
//全屏
// console.log('全屏了')
}else{
//全屏
this.fullScreen=false;
}
});
this.resizeObserver.observe(this.$el);
},
computed: {
//显示屏数量改变的时候改变监控视频大小
liveStyle() {
let style = {width: '100%', height: '100%'}
switch (this.spilt) {
case 4:
style = {width: '49.7%', height: '49.7%'}
break
case 9:
style = {width: '32.9%', height: '32.9%'}
break
}
this.$nextTick(() => {
for (let i = 0; i < this.spilt; i++) {
const player = this.$refs.player
player && player[i] && player[i].updatePlayerDomSize()
}
})
return style
}
},
methods:{
//请求列表
getList(){
this.loading=true
docList(this.queryParams)
.then(res=>{
if(res.code == 200){
this.tableDataList = res.rows;
this.total=res.total;
}
})
.catch(err=>{})
.finally(()=>{this.loading=false;})
},
//重置
resetQuery(){
this.value=''
this.queryParams={
videoChannel:'',
pageNum: 1,
pageSize: 10,
}
this.getList();
},
//选择监控视频
selectFun(url){
this.videoUrl[this.clickIndex]=url;
this.isShow=false;
},
//添加监控视频
openAdd(i){
console.log(i);
if(this.videoUrl[i-1] == ''||this.videoUrl[i-1] == undefined){
this.isShow=true;
this.clickIndex=i-1;
}
},
//布局图标点击
iconChange(index){
if(this.selectIcon == index)return
switch(index){
case 0:
this.spilt = 1;
this.selectIcon = index;
this.videoUrl=['']
this.playState=['play']
this.soundState=['stop']
break;
case 1:
this.spilt = 4;
this.selectIcon = index;
this.videoUrl=['','','','']
this.playState=['play','play','play','play']
this.soundState=['stop','stop','stop','stop']
break;
case 2:
this.spilt = 9;
this.selectIcon = index;
this.videoUrl=['','','','','','','','','']
this.playState=['play','play','play','play','play','play','play','play','play']
this.soundState=['stop','stop','stop','stop','stop','stop','stop','stop','stop']
break;
case 3:
//全屏时放大
var el = document.documentElement;
var rfs = el.requestFullScreen || el.webkitRequestFullScreen || el.mozRequestFullScreen || el.msRequestFullscreen;
if(typeof rfs != "undefined" && rfs) {
rfs.call(el);
};
this.fullScreen=true;
// return;
break;
}
},
//截图
screenshot(i){
this.$refs.player[i-1].screenshot()
},
//放大/缩放
fullscreenSwich(i){
this.$refs.player[i-1].fullscreenSwich()
},
//播放按钮点击事件
playChange(i,v){
let playState = JSON.parse(JSON.stringify(this.playState))
playState[i-1]=v
this.playState=JSON.parse(JSON.stringify(playState))
v=='play'?this.$refs.player[i-1].playBtnClick():this.$refs.player[i-1].destroy()
},
//声音
soundFun(i,v){
console.log('vvv',v)
let soundState = JSON.parse(JSON.stringify(this.soundState))
soundState[i-1]=v
this.soundState=JSON.parse(JSON.stringify(soundState))
v=='stop'?this.$refs.player[i-1].mute():this.$refs.player[i-1].cancelMute()
},
//关闭
closeFun(i){
let videoUrl = JSON.parse(JSON.stringify(this.videoUrl))
videoUrl[i-1]=''
this.videoUrl = JSON.parse(JSON.stringify(videoUrl))
},
destroy(idx) {
console.log(idx);
this.clear(idx.substring(idx.length - 1))
},
clear(idx) {
let dataStr = window.localStorage.getItem('playData') || '[]'
let data = JSON.parse(dataStr);
data[idx - 1] = null;
console.log(data);
window.localStorage.setItem('playData', JSON.stringify(data))
},
shot(e) {
// console.log(e)
// send({code:'image',data:e})
var base64ToBlob = function (code) {
let parts = code.split(';base64,');
let contentType = parts[0].split(':')[1];
let raw = window.atob(parts[1]);
let rawLength = raw.length;
let uInt8Array = new Uint8Array(rawLength);
for (let i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], {
type: contentType
});
};
let aLink = document.createElement('a');
let blob = base64ToBlob(e); //new Blob([content]);
let evt = document.createEvent("HTMLEvents");
evt.initEvent("click", true, true); //initEvent 不加后两个参数在FF下会报错 事件类型,是否冒泡,是否阻止浏览器的默认行为
aLink.download = '截图';
aLink.href = URL.createObjectURL(blob);
aLink.click();
},
},
watch: {
spilt(newValue) {
console.log("切换画幅;" + newValue)
let that = this
for (let i = 1; i <= newValue; i++) {
console.log(!that.$refs['player' + i])
if (!that.$refs['player' + i]) {
continue
}
this.$nextTick(() => {
if (that.$refs['player' + i] instanceof Array) {
that.$refs['player' + i][0].resize()
} else {
that.$refs['player' + i].resize()
}
})
}
// window.localStorage.setItem('split', newValue)
},
}
}
</script>
<style lang='scss' scoped>
.fu{
display: flex;
flex-wrap: nowrap;
flex-direction: row;
.monitor{
width: 72%;
display: flex;
flex-direction: column;
align-items: center;
.title{
color: #409EFF;
font-size: 23px;
}
.layout-btn{
margin-top:10px;
width: 100%;
height: 40px;
padding: 5px 10px;
display: flex;
justify-content: end;
background-color: #F2F2F2;
.icon-img{
cursor:pointer;
width: 30px;
height: 30px;
margin-left: 10px;
color:#6B6B6B;
}
.icon-active{
color:#409EFF;
}
.hs:hover {
color:#409EFF; /* 鼠标移入时的背景颜色 */
}
}
.monitor-content{
margin-top:10px;
width: 100%;
height: 500px;
}
.full-screen{
position: fixed;
width: 100vw;
height: 100vh;
background-color: #fff;
top: 0px;
left: 0px;
z-index: 2010;
}
.monitor-layout{
// margin-top:10px;
// width: 100%;
// min-height: 500px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
.play-title{
color: #FFFFFF;
font-size: 1rem;
// font-weight: bold;
}
.play-box {
cursor: pointer;
background-color: #858585;
border: 2px solid #505050;
display: flex;
align-items: center;
justify-content: center;
.play-item{
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
.player{
height: 90% !important;
}
.btn-list{
// flex: 1;
height:7vh;
width: 100%;
padding: 5px 10px;
background-color: #F2F2F2;
display: flex;
justify-content: space-between;
.icon-list{
display: flex;
flex-direction: column;
justify-content: space-between;
width: 41%;
height: 100%;
.node{
display: flex;
align-items: center;
// height: 50%;
img{
width: 12px;
height: 12px;
}
span{
margin-left: 5px;
font-size: 14px;
// color: #409EFF;
overflow: hidden;
white-space:nowrap;/*默认normal 自动换行*/
text-overflow:ellipsis; /*默认是clip 超出部分直接切除*/
}
}
}
.function-list{
flex: 1;
display: flex;
justify-content: end;
align-items: center;
.svg-icon:hover{
color: #409EFF;
}
.svg-icon{
width: 1.5rem;
height: 1.5rem;
margin-left: 10px;
cursor:pointer;
}
// img{
// width: 1.5rem;
// height: 1.5rem;
// margin-left: 10px;
// cursor:pointer;
// }
}
}
}
}
}
}
.warning-div{
flex: 1;
background-color: #F2F2F2;
padding: 10px;
margin-left: 5px;
.common{
border-radius: 10px;
padding: 10px;
margin-bottom: 10px;
background-color: #fff;
.row-one{
display: flex;
justify-content: space-between;
height: 18px;
width: 100%;
.title{
height: 18px;
line-height: 18px;
padding-left: 5px;
}
.real-time-title{
border-left: 5px solid red;
}
.title-history{
border-left: 5px solid #02A7F0;
}
.jump{
height: 18px;
line-height: 18px;
cursor:pointer;
}
}
.for-item{
// padding: 5px 0px;
margin-top: 5px;
border-bottom: 2px solid #D7D7D7;
.item-row-title{
display: flex;
justify-content: space-between;
margin-top: 10px;
.title-element{
display: flex;
align-items: center;
.number{
height: 14px;
width: 14px;
line-height: 14px;
font-size: 14px;
font-weight: 600;
text-align: center;
color: #fff;
}
.number-real-time{background-color: red;}
.number-history{background-color: #02A7F0;}
.enterprise{
font-size: 14px;
margin-left: 5px;
}
}
.place{
font-size: 14px;
color: #02A7F0;
display: flex;
align-items: center;
img{
width: 14px;
height: 14px;
margin-right: 5px;
}
}
}
.item-row-info{
margin-top: 10px;
padding: 5px;
display: flex;
font-size: 14px;
justify-content: space-between;
.frame{
width: 90px;
height: 60px;
img{
width: 100%;
height: 100%;
}
}
.frame-history{
border: 1px solid #02A7F0;
box-shadow: 1px 1px 5px 0 #02A7F0;
}
.frame-real-time{
border: 1px solid red;
box-shadow: 1px 1px 5px 0 #f60e0e;
}
.default{
padding: 5px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
}
.warning-content{
margin:10px 0px;
font-size: 14px;
}
}
}
.real-time{
}
.history{
}
}
}
</style>