ry_app/package_a/monitor/monitor.nvue

335 lines
8.9 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="monitor-container">
<text class="header">实时监控</text>
<!-- 这里使用我们封装好的原生插件组件 ry-ipc-video -->
<view class="video-wrapper">
<!-- #ifdef APP-PLUS -->
<ry-ipc-video
ref="ipcVideo"
class="ipc-video"
:uid="deviceUid"
:channel="currentChannel"
:streamType="currentStreamType"
:isNvr="isNvr"
@onStatus="onVideoStatusChange">
</ry-ipc-video>
<!-- #endif -->
<!-- 非App端提示 -->
<!-- #ifndef APP-PLUS -->
<view class="not-support-tips">
<text class="not-support-tips-text">当前环境不支持查看监控请使用App</text>
</view>
<!-- #endif -->
</view>
<view class="status-text-wrap" v-if="statusMsg">
<text class="status-text">状态:{{ statusMsg }}</text>
</view>
<!-- 设置区域 -->
<view class="settings-panel">
<view class="setting-row">
<text class="setting-label">设备类型:</text>
<view class="options-group">
<text class="option-btn" :class="!isNvr ? 'active-btn' : ''" @click="changeNvr(false)">单体(IPC)</text>
<text class="option-btn" :class="isNvr ? 'active-btn' : ''" @click="changeNvr(true)">主机(NVR)</text>
</view>
</view>
<view class="setting-row" v-if="isNvr">
<text class="setting-label">通道:</text>
<view class="options-group">
<text class="option-btn" :class="currentChannel === 0 ? 'active-btn' : ''" @click="changeChannel(0)">CH1</text>
<text class="option-btn" :class="currentChannel === 1 ? 'active-btn' : ''" @click="changeChannel(1)">CH2</text>
<text class="option-btn" :class="currentChannel === 2 ? 'active-btn' : ''" @click="changeChannel(2)">CH3</text>
<text class="option-btn" :class="currentChannel === 3 ? 'active-btn' : ''" @click="changeChannel(3)">CH4</text>
</view>
</view>
<view class="setting-row">
<text class="setting-label">清晰度:</text>
<view class="options-group">
<text class="option-btn" :class="currentStreamType === 1 ? 'active-btn' : ''" @click="changeStream(1)">高清</text>
<text class="option-btn" :class="currentStreamType === 2 ? 'active-btn' : ''" @click="changeStream(2)">标清</text>
</view>
</view>
</view>
<view class="controls">
<button class="btn btn-primary" @click="startPlay"><text class="btn-text">播放</text></button>
<button class="btn btn-warn" @click="stopPlay"><text class="btn-text">停止</text></button>
</view>
</view>
</template>
<script>
export default {
data() {
return {
deviceUid: 'HLTY036190SYNSK', // 默认使用你 Demo 里的测试 UID
isPlaying: false,
statusMsg: '等待操作',
currentChannel: 0, // 0对应CH1, 1对应CH2...
currentStreamType: 1, // 1为高清(主码流)2为标清(子码流)
isNvr: true // 是否为 NVR 设备
}
},
onLoad(options) {
if (options && options.uid) {
this.deviceUid = options.uid;
}
// 如果上个页面传了 isNvr 参数,这里可以接收
if (options && typeof options.isNvr !== 'undefined') {
// options.isNvr 通常传过来是字符串 'true' 或 'false'
this.isNvr = options.isNvr === 'true';
}
},
onUnload() {
// 页面卸载时停止播放,释放资源
this.stopPlay();
},
methods: {
changeNvr(isNvrFlag) {
if (this.isNvr === isNvrFlag) return;
this.isNvr = isNvrFlag;
// 如果切换到了单体 IPC通道强制归 0
if (!isNvrFlag) {
this.currentChannel = 0;
}
// 如果正在播放,切换设备类型后需要完全重新连接并播放
if (this.isPlaying) {
this.stopPlay();
setTimeout(() => {
this.startPlay();
}, 1000); // 切换设备类型可能需要稍长一点的时间断开重连
}
},
changeChannel(ch) {
if (this.currentChannel === ch) return;
this.currentChannel = ch;
// 如果正在播放,切换通道后重新播放
if (this.isPlaying) {
this.stopPlay();
setTimeout(() => {
this.startPlay();
}, 500);
}
},
changeStream(type) {
if (this.currentStreamType === type) return;
this.currentStreamType = type;
// 如果有单独切换清晰度的方法可以调用,如果没有就重启播放
if (this.isPlaying) {
if (this.$refs.ipcVideo && typeof this.$refs.ipcVideo.setStreamType === 'function') {
this.$refs.ipcVideo.setStreamType(type);
} else {
this.stopPlay();
setTimeout(() => {
this.startPlay();
}, 500);
}
}
},
startPlay() {
// #ifdef APP-PLUS
if (this.$refs.ipcVideo) {
this.statusMsg = '正在发起播放请求...';
console.log('组件对象:', this.$refs.ipcVideo);
// 在 nvue 中调用组件的方法,有时可能需要通过 evalJS但绝大多数情况可以直接调
if (typeof this.$refs.ipcVideo.start === 'function') {
try{
this.$refs.ipcVideo.start();
}
catch(err){
console.error("this.$refs.ipcVideo.start时发生前端异常:", err);
uni.showModal({
title: '前端回调解析异常',
content: err.toString(),
showCancel: false
});
}
} else {
console.error('start 方法不存在,可能插件注册失败或版本未生效');
uni.showToast({ title: '插件方法未找到', icon: 'none' });
}
} else {
uni.showToast({ title: '插件未加载成功', icon: 'none' });
}
// #endif
// #ifndef APP-PLUS
uni.showToast({ title: '仅App端支持', icon: 'none' });
// #endif
},
stopPlay() {
// #ifdef APP-PLUS
if (this.$refs.ipcVideo) {
if (typeof this.$refs.ipcVideo.stop === 'function') {
this.$refs.ipcVideo.stop();
}
this.statusMsg = '已发送停止指令';
}
// #endif
},
onVideoStatusChange(e) {
try {
console.log("视频状态改变:", e);
// 根据我们在 Java 封装里写的 fireEventStatus 传回的 msg 显示
if (e && e.detail) {
// 提取参数
const status = e.detail.status;
const msg = e.detail.msg;
const msgParam = e.detail.msgParam;
// 更新页面显示的文字状态
if (msg) {
this.statusMsg = msg;
}
// 捕获来自 Java 层主动抛出的原生崩溃错误
if (status === 'error' && msg && msg.indexOf('原生启动崩溃') !== -1) {
uni.showModal({
title: '原生插件崩溃',
content: msg,
showCancel: false
});
this.isPlaying = false;
return;
}
// 处理特定的连接错误状态
if (status === 'error' || msgParam === 7 || msgParam === 14) {
uni.showToast({
title: msg ? msg : '摄像机离线,请检查设备网络',
icon: 'none',
duration: 3000
});
// 可以在这里做一些重置操作
this.isPlaying = false;
} else if (status === 'playing') {
this.isPlaying = true;
}
} else {
// 如果 e.detail 是空对象或者 undefined提示异常
console.error("收到空的状态事件:", e);
uni.showToast({
title: '收到空状态,原生可能发生异常',
icon: 'none'
});
}
} catch (err) {
console.error("解析状态回调时发生前端异常:", err);
uni.showModal({
title: '前端回调解析异常',
content: err.toString(),
showCancel: false
});
}
}
}
}
</script>
<style>
/* nvue 样式必须是纯 CSS/flex不支持 scss 嵌套和某些普通 css 属性 */
.monitor-container {
flex: 1;
flex-direction: column;
background-color: #f5f5f5;
}
.header {
font-size: 32rpx;
font-weight: bold;
text-align: center;
padding: 20rpx;
background-color: #ffffff;
}
.video-wrapper {
width: 750rpx;
height: 450rpx;
background-color: #000000;
flex-direction: column;
justify-content: center;
align-items: center;
}
.ipc-video {
width: 750rpx;
height: 450rpx;
}
.not-support-tips {
align-items: center;
justify-content: center;
}
.not-support-tips-text {
color: #ffffff;
font-size: 28rpx;
}
.status-text-wrap {
align-items: center;
margin-top: 20rpx;
}
.status-text {
text-align: center;
font-size: 26rpx;
color: #666666;
}
.controls {
flex-direction: row;
justify-content: space-around;
padding-top: 40rpx;
padding-bottom: 40rpx;
}
.btn {
width: 300rpx;
height: 80rpx;
border-radius: 10rpx;
align-items: center;
justify-content: center;
}
.btn-primary {
background-color: #007aff;
}
.btn-warn {
background-color: #e64340;
}
.btn-text {
color: #ffffff;
font-size: 30rpx;
}
.settings-panel {
padding: 20rpx;
margin-top: 20rpx;
background-color: #ffffff;
}
.setting-row {
flex-direction: row;
align-items: center;
margin-bottom: 20rpx;
}
.setting-label {
font-size: 28rpx;
color: #333333;
width: 120rpx;
}
.options-group {
flex-direction: row;
flex: 1;
}
.option-btn {
padding: 10rpx 20rpx;
font-size: 26rpx;
color: #666666;
background-color: #f0f0f0;
border-radius: 8rpx;
margin-right: 20rpx;
}
.active-btn {
color: #ffffff;
background-color: #007aff;
}
</style>