💥 feat(视频监控): 添加视频设备列表页、更换滚动为mescroll-body

This commit is contained in:
风花一世月 2024-07-03 23:04:07 +08:00
parent a33d755034
commit ad4b621978
5 changed files with 640 additions and 0 deletions

View File

@ -5,6 +5,8 @@ export default {
iotsApi:require("./iots/index.js").default,
// 组态模块
configurationApi:require("./configuration/index.js").default,
// 视频监控模块
videoApi:require("./video/index.js").default,
// 消息模块
newApi:require("./new.js").default,

View File

@ -0,0 +1,34 @@
import request from "../../request.js"
export default {
// 获取监控设备列表
getVideoList(data){
return new Promise((resolve, reject) => {
request.TokenRequest({
url: '/video/admin/video/list',
method: 'GET',
},data)
.then((res) =>{
resolve(res);
}).catch(err =>{
reject(err);
})
})
},
// 获取监控通道列表
getVideoChannelList(data){
return new Promise((resolve, reject) => {
request.TokenRequest({
url: '/video/admin/video/channel/list',
method: 'GET',
},data)
.then((res) =>{
resolve(res);
}).catch(err =>{
reject(err);
})
})
},
}

View File

@ -285,6 +285,33 @@
}
}
]
},
//
{
"root": "pages/video/",
"pages": [
{
"path": "video-list",
"style": {
"navigationBarTitleText": "视频监控列表"
// "navigationStyle":"custom"
}
},
{
"path": "video-detail",
"style": {
// "navigationStyle": "default",
"navigationBarTitleText": "视频监控详情"
// "navigationBarBackgroundColor":"#1b85e9",
// "navigationBarTextStyle":"white",
// "pageOrientation":"landscape"
// // #ifdef APP-PLUS
// ,
// "navigationStyle":"custom"
// // #endif
}
}
]
}
],
"globalStyle": {
@ -363,6 +390,11 @@
"path": "pages/tabbar/my", //
"query": "" //onLoad
},
{
"name": "视频监控列表", //
"path": "pages/video/video-list", //
"query": "" //onLoad
},
{
"name": "消息", //
"path": "pages/tabbar/new", //

View File

@ -0,0 +1,440 @@
<template>
<view class="video-detail">
<nav-bar :is-back="true" title="视频监控"></nav-bar>
<view class="video-box">
<!-- #ifdef APP-PLUS -->
<!-- <video id="myVideo" :src="url" autoplay v-if="showVideo" show-mute-btn="true" vslide-gesture="true" :controls="btnToggle"> -->
<video id="myVideo" :src="dataObj.url" autoplay v-if="showVideo" controls="false">
<!-- <cover-view class="btn-toggle" v-if="btnToggle" @click="quitFullScreen">
退出全屏
</cover-view> -->
<!-- <view class="btn-toggle" v-if="btnToggle" @click="quitFullScreen">
退出全屏
</view> -->
</video>
<!-- #endif -->
<!-- #ifdef MP-WEIXIN -->
<!-- <live-player id="live-video" src="http://al.flv.huya.com/src/78941969-2559461593-10992803837303062528-2693342886-10057-A-0-1-imgplus.flv?wsSecret=f61867c0ada81aceb02d7306fdd059a2&wsTime=6037810b&fm=RFdxOEJjSjNoNkRKdDZUWV8kMF8kMV8kMl8kMw%3D%3D&ctyp=huya_tars&txyp=o%3Aq5%3B&fs=bgct&&sphdcdn=al_7-tx_3-js_3-ws_7-bd2-hw2&sphdDC=huya&sphd=264-265&ratio=500" v-if="showVideo" autoplay> -->
<live-player id="live-video" :src="dataObj.url" v-if="showVideo" autoplay @statechange="statechange"
@netstatus="netstatus">
<cover-view class="btn-toggle" v-if="btnToggle" @click="quitFullScreen">
退出全屏
</cover-view>
</live-player>
<!-- #endif -->
</view>
<view class="video-title">
{{dataObj.name}}
</view>
<!-- #ifdef MP-WEIXIN -->
<view class="slider-box">
<!-- <view class="title">
推流状态
</view> -->
<view class="net-status">
<text>速度:{{netStatus.netSpeed==undefined?'':netStatus.netSpeed}}</text>
<text>网络抖动:{{netStatus.netJitter==undefined?'':netStatus.netJitter}}</text>
<text>视频帧率:{{netStatus.videoFPS==undefined?'':netStatus.videoFPS}}</text>
<text>状态码:{{videoStatus}}</text>
</view>
</view>
<!-- #endif -->
<!-- <view class="slider-box">
<view class="title">
速度
</view>
<slider v-model="speed" step="10" @change="sliderChange" max="200" show-value />
</view> -->
<view class="title">
云台控制
</view>
<view class="btn-box">
<view class="btn">
<view class="btn-top-bottom" @touchstart="PTZcontrol(ptzType.up)"
@touchcancel="PTZcontrol(ptzType.stop)" @touchend="PTZcontrol(ptzType.stop)">
<view class="iconfont icon-fangxiang-shang"></view>
</view>
<view class="btn-center">
<view class="btn-left-right" @touchstart="PTZcontrol(ptzType.left)"
@touchcancel="PTZcontrol(ptzType.stop)" @touchend="PTZcontrol(ptzType.stop)">
<view class="iconfont icon-fangxiang-zuo"></view>
</view>
<view class="btn-center-center">
<!-- <view class="iconfont iconluyin" @touchstart="startRecording()" @touchcancel="endRecording()" @touchend="endRecording()"></view> -->
</view>
<view class="btn-left-right" @touchstart="PTZcontrol(ptzType.right)"
@touchcancel="PTZcontrol(ptzType.stop)" @touchend="PTZcontrol(ptzType.stop)">
<view class="iconfont icon-fangxiang-you"></view>
</view>
</view>
<view class="btn-top-bottom" @touchstart="PTZcontrol(ptzType.down)"
@touchcancel="PTZcontrol(ptzType.stop)" @touchend="PTZcontrol(ptzType.stop)">
<view class="iconfont icon-fangxiang-xia"></view>
</view>
</view>
</view>
<view class="display-box">
<view class="title">
显示
</view>
<button class="zoom-btn" type="default" @click="fullScreen()">全屏</button>
</view>
<u-toast ref="uToast" />
</view>
</template>
<script>
import {
getPTZCmd,
PTZ_TYPE
} from '@/static/common/js/video/ptz-cmd.js'
export default {
data() {
return {
title: '',
url: '',
speed: 100,
btnToggle: false,
dataObj: {
url: '',
name: ''
},
refreshdata: 11,
showVideo: true,
//
//
videoStatus: '',
netStatus: {
netSpeed: '',
netJitter: '',
videoFPS: '',
},
videoObj: {
url: '',
id: '',
channel: '',
},
ptzType: {}
};
},
onLoad(option) {
this.ptzType = PTZ_TYPE;
console.log("PTZ_TYPE", PTZ_TYPE)
console.log("option: ", option);
console.log("optionobj: ", JSON.parse(decodeURIComponent(option.obj)));
let obj = JSON.parse(decodeURIComponent(option.obj));
if (obj.siteM7sHost && obj.siteM7sPort && obj.devId && obj.devChannel) {
if (obj.https) {
this.videoObj = {
url: 'https://' + obj.siteM7sHost + ':' + obj.siteM7sPort,
id: obj.devId,
channel: obj.devChannel,
}
this.pullVideoList('https://' + obj.siteM7sHost + ':' + obj.siteM7sPort, obj.devId, obj.devChannel)
this.dataObj.url = 'https://' + obj.siteM7sHost + ':' + obj.siteM7sPort + '/hdl/' + obj.devId + '/' +
obj.devChannel + '.flv'
} else {
this.videoObj = {
url: 'http://' + obj.siteM7sHost + ':' + obj.siteM7sPort,
id: obj.devId,
channel: obj.devChannel,
}
this.pullVideoList('http://' + obj.siteM7sHost + ':' + obj.siteM7sPort, obj.devId, obj.devChannel)
this.dataObj.url = 'http://' + obj.siteM7sHost + ':' + obj.siteM7sPort + '/hdl/' + obj.devId + '/' +
obj.devChannel + '.flv'
}
} else {
this.$u.toast('摄像头参数错误!');
}
this.dataObj.name = obj.label;
console.log("当前flv地址:" + this.dataObj.url)
//
// uni.setInnerAudioOption({
// obeyMuteSwitch: false
// })
},
watch: {
refreshdata() {
this.$nextTick(() => {
this.showVideo = true
})
}
},
methods: {
statechange(e) {
// console.log('live-player code:', e)
this.videoStatus = e.detail.code;
if (e.detail.code == -2301) {
this.$u.toast('网络断连,且经多次重连抢救无效,更多重试请自行重启播放')
}
},
netstatus(e) {
// console.log('live-player netstatus:', e)
this.netStatus = e.detail.info;
},
pullVideoList(url, id, channel) {
// console.log("",url,id,channel)
let params = {
id: id,
channel: channel,
// startTime:1657165870,
// endTime:1657770669,
}
let DefaultOpts = {
url: url + '/api/gb28181/invite',
data: params,
method: "GET",
}
uni.request(DefaultOpts).then(
(res) => {
console.log("pullVideoList", res);
}
).catch(
(response) => {
console.log("pullVideoList错误", res);
}
)
},
//
fullScreen() {
// #ifdef APP-PLUS
// const subNvue=uni.getSubNVueById('popup'); //
// subNvue.show() //
this.videoContext = uni.createVideoContext('myVideo');
//
this.videoContext.requestFullScreen();
this.btnToggle = true;
// #endif
// #ifdef MP-WEIXIN
this.videoContext = uni.createLivePlayerContext('live-video');
this.videoContext.requestFullScreen({
direction: 90
});
this.btnToggle = true;
// #endif
},
// 退
quitFullScreen() {
// #ifdef APP-PLUS
this.videoContext = uni.createVideoContext('myVideo');
//
this.videoContext.exitFullScreen();
this.btnToggle = false;
// const subNvue=uni.getSubNVueById('popup');
// subNvue.hide() //
// #endif
// #ifdef MP-WEIXIN
this.videoContext = uni.createLivePlayerContext('live-video');
this.videoContext.exitFullScreen();
this.btnToggle = false;
// #endif
},
//
sliderChange(e) {
console.log(e);
this.speed = e.detail.value;
},
//
PTZcontrol(type) {
console.log("type", type);
let ptzcmd = getPTZCmd({
type: type
});
console.log("ptzcmd", ptzcmd)
this.btnToast(type);
let params = {
id: this.videoObj.id,
channel: this.videoObj.channel,
ptzcmd: ptzcmd
}
let DefaultOpts = {
url: this.videoObj.url + '/api/gb28181/control',
data: params,
method: "GET",
}
uni.request(DefaultOpts).then(
(res) => {
console.log("PTZcontrol", res);
}
).catch(
(response) => {
console.log("PTZcontrol错误", res);
}
)
},
btnToast(type) {
if (type == this.ptzType.up) {
this.$refs.uToast.show({
title: '向上移动!',
type: 'success',
})
} else if (type == this.ptzType.left) {
this.$refs.uToast.show({
title: '向左移动!',
type: 'success',
})
} else if (type == this.ptzType.right) {
this.$refs.uToast.show({
title: '向右移动!',
type: 'success',
})
} else if (type == this.ptzType.down) {
this.$refs.uToast.show({
title: '向下移动!',
type: 'success',
})
} else if (type == this.ptzType.stop) {
this.$refs.uToast.show({
title: '停止移动!',
type: 'error',
})
}
}
}
}
</script>
<style lang="less" scoped>
.video-detail {
background-color: #fff;
height: 100%;
height: 100%;
.video-box {
width: 750rpx;
height: 422rpx;
video,
live-player {
width: 100%;
height: 100%;
}
.btn-toggle {
position: fixed;
top: 60rpx;
right: 80rpx;
z-index: 9999;
padding: 10rpx;
background: rgba(0, 0, 0, 0.5);
color: #fff;
font-size: 28rpx;
line-height: 36rpx;
}
}
.video-title {
font-weight: bold;
font-size: 36rpx;
line-height: 100rpx;
text-indent: 30rpx;
}
.title {
font-size: 26rpx;
font-weight: bold;
margin: 0 30rpx 0 20rpx;
}
.btn-box {
display: flex;
justify-content: center;
margin: 30rpx;
.btn {
width: 336rpx;
height: 336rpx;
border-radius: 50%;
background-color: #f2f3f7;
display: flex;
flex-direction: column;
align-items: center;
color: #7d90ab;
font-size: 56rpx;
overflow: hidden;
.btn-top-bottom {
width: 141rpx;
height: 98rpx;
display: flex;
justify-content: center;
align-items: center;
}
.btn-center {
display: flex;
align-items: center;
.btn-left-right {
width: 98rpx;
height: 141rpx;
display: flex;
justify-content: center;
align-items: center;
}
.btn-center-center {
width: 141rpx;
height: 141rpx;
background-color: #dde1ea;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
}
}
.btn-top-bottom:active,
.btn-left-right:active {
background-color: #dde1ea;
color: #7d90ab;
}
.btn-center-center:active {
background-color: #7d90ab;
color: #dde1ea;
}
}
}
.slider-box {
display: flex;
align-items: center;
margin: 30rpx;
slider {
width: 536rpx;
}
}
.zoom-box,
.display-box {
margin: 30rpx;
display: flex;
align-items: center;
.zoom-btn {
margin: 0;
line-height: 2;
border-color: #8293ab;
color: #8293ab;
}
}
}
/deep/.u-position-center{
top: calc(var(--status-bar-height) + 470rpx + 45px) !important;
}
.net-status {
text {
margin-right: 10rpx;
display: block;
}
}
</style>

132
pages/video/video-list.vue Normal file
View File

@ -0,0 +1,132 @@
<template>
<view id="video-list">
<!-- <scroll-view scroll-y class="right-box"> -->
<mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" :up="upOption" @up="upCallback">
<view class="page-view">
<u-collapse :accordion="false">
<u-collapse-item v-for="(item, index) in dataList"
:key="item.id" :open="true">
<template slot="title">
<view class="box-title">
<text>{{item.name}}</text>&nbsp;&nbsp;
<text>{{item.children?`(${item.children.length})`:''}}</text>
</view>
</template>
<view class="collapse-item">
<view class="box-item" v-for="(val,i) in item.children" :key="val.id"
@click="goDetail(val)">
{{val.label}}
</view>
</view>
</u-collapse-item>
</u-collapse>
</view>
</mescroll-body>
<!-- </scroll-view> -->
</view>
</template>
<script>
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js"
let liveList = [{
name:'区域1',
children:[
{
name:'监控1',
url:'https://gbs.gkiiot.com:8082/hdl/34020000001110000000/34020000001320000047.flv'
},
{
name:'监控2',
url:'https://gbs.gkiiot.com:8082/hdl/34020000001110000000/34020000001320000047.flv'
}
]
},{
name:'区域2',
children:[
{
name:'监控3',
url:'https://gbs.gkiiot.com:8082/hdl/34020000001110000000/34020000001320000047.flv'
},
{
name:'监控4',
url:'https://gbs.gkiiot.com:8082/hdl/34020000001110000000/34020000001320000047.flv'
}
]
}]
export default{
mixins: [MescrollMixin],
data(){
return {
upOption:{
noMoreSize: 4,
textNoMore: '---- 已经到底啦 ----',
empty:{
tip: '~ 搜索无数据 ~' //
}
},
dataList:[],
}
},
onLoad() {
// this.getVideoList();
},
methods:{
upCallback(page){
let params = {
page: page.num,
pageSize: page.size,
}
this.$api.videoApi.getVideoList(params).then(res => {
console.log("getVideoList",res);
if(res.code === 0){
let curPageData = res.data && res.data.list;
let curPageLen = curPageData && curPageData.length || 0;
let totalSize = res.data.totalCount;
this.$nextTick(()=>{
// ;
this.mescroll.endBySize(curPageLen, totalSize);
})
//
if(page.num == 1) this.dataList = []; //
this.dataList = curPageLen > 0 && this.dataList.concat(curPageData); //
this.total = totalSize;
}else{
this.$u.toast(res.msg);
this.mescroll.endErr();
}
}, error => {
this.$u.toast('服务器开小差了呢,请您稍后再试')
})
},
goDetail(item){
console.log("跳转详情",item);
let obj = {
url:encodeURIComponent(item.url),
name:item.label
}
uni.navigateTo({
url:'./video-detail?obj='+encodeURIComponent(JSON.stringify(item))
})
}
}
}
</script>
<style lang="less">
.box-title{
padding: 10rpx 24rpx;
}
/deep/ .u-collapse-head{
border: 1px solid #f5f5f5;
}
/deep/ .u-collapse-content{
// padding: 20rpx;
}
.box-item{
padding: 20rpx;
font-size: 30rpx;
}
</style>