安卓视频监控插件更新,监控列表更新

master
denghaohaoya 2026-04-10 23:41:40 +08:00
parent 69b227ec7f
commit 45e8a49330
9 changed files with 361 additions and 24 deletions

11
api/monitorList.js Normal file
View File

@ -0,0 +1,11 @@
import request from '@/utils/request'
import apiUrl from '@/utils/api'
// 根据用户ID查询门店列表
export function listMonitorData(id) {
return request({
baseUrl: apiUrl,
url: `/mall/device/getDevice/${id}`,
method: 'get'
})
}

View File

@ -1,6 +1,6 @@
{
"name" : "海驰24",
"appid" : "__UNI__F8B45C3",
"appid" : "__UNI__7302921",
"description" : "111",
"versionName" : "1.2.6",
"versionCode" : 106,

View File

@ -38,26 +38,33 @@
<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>
<text class="option-btn" v-if="!isNvr" :class="!isNvr ? 'active-btn' : ''" @click="changeNvr(false)">单体(IPC)</text>
<text class="option-btn" v-else :class="isNvr ? 'active-btn' : ''" @click="changeNvr(true)">主机(NVR)</text>
</view>
</view>
<view class="setting-row" v-if="isNvr">
<view class="setting-row" v-if="isNvr && totalChannel > 0">
<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>
<!-- nvue 的 flex 默认不支持换行flex-wrap通道多的话外面套一个 scroll-view 可以左右滑动 -->
<scroll-view class="channel-scroll" scroll-x="true" show-scrollbar="false">
<view class="options-group channel-group">
<text
class="option-btn"
v-for="(item, index) in totalChannel"
:key="index"
:class="currentChannel === index ? 'active-btn' : ''"
@click="changeChannel(index)">
CH{{ index + 1 }}
</text>
</view>
</scroll-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>
<!-- <text class="option-btn" :class="currentStreamType === 0 ? 'active-btn' : ''" @click="changeStream(0)">高清</text> -->
<text class="option-btn" :class="currentStreamType === 1 ? 'active-btn' : ''" @click="changeStream(1)">标清</text>
</view>
</view>
</view>
@ -77,12 +84,13 @@
export default {
data() {
return {
deviceUid: 'HLTY036190SYNSK', // 默认使用你 Demo 里的测试 UID
deviceUid: '', // 默认使用你 Demo 里的测试 UID
isPlaying: false,
isRecording: false, // 是否正在录像
statusMsg: '等待操作',
currentChannel: 0, // 0对应CH1, 1对应CH2...
currentStreamType: 1, // 1为高清(主码流)2为标清(子码流)
totalChannel: 1, // 设备的总通道数默认为1由上个页面传过来
currentStreamType: 1, // 0为高清(主码流)1为标清(子码流)
isNvr: true // 是否为 NVR 设备
}
},
@ -90,10 +98,18 @@
if (options && options.uid) {
this.deviceUid = options.uid;
}
// 接收总通道数
if (options && options.channel) {
this.totalChannel = parseInt(options.channel);
// 如果通道数不合理比如0或者NaN给个默认值
if (!this.totalChannel || this.totalChannel <= 0) {
this.totalChannel = 1;
}
}
// 如果上个页面传了 isNvr 参数,这里可以接收
if (options && typeof options.isNvr !== 'undefined') {
// options.isNvr 通常传过来是字符串 'true' 或 'false'
this.isNvr = options.isNvr === 'true';
this.isNvr = options.isNvr;
}
},
onUnload() {
@ -373,6 +389,14 @@
flex-direction: row;
flex: 1;
}
.channel-scroll {
flex: 1;
flex-direction: row;
}
.channel-group {
flex-direction: row;
padding-bottom: 10rpx; /* 预留一点底部空间,防止被遮挡 */
}
.option-btn {
padding: 10rpx 20rpx;
font-size: 26rpx;

View File

@ -0,0 +1,227 @@
<template>
<view class="container">
<!-- <view class="header">
<text class="title">监控设备列表</text>
</view> -->
<scroll-view class="list-container" scroll-y>
<!-- 设备列表循环 -->
<view class="device-card" v-for="(item, index) in deviceList" :key="index" @click="goToMonitorDetail(item)">
<view class="device-info">
<text class="device-name">{{ item.name }}</text>
<text class="device-uid">UID: {{ item.uid }}</text>
<!-- <view class="device-status">
<text class="status-dot" :class="item.online ? 'online' : 'offline'"></text>
<text class="status-text">{{ item.online ? '在线' : '离线' }}</text>
</view> -->
</view>
<view class="device-action">
<text class="action-text">{{ pageType === '0' ? '查看回放' : '查看监控' }}</text>
<uni-icons type="right" size="16" color="#999"></uni-icons>
</view>
</view>
<!-- 无数据提示 -->
<view class="empty-tips" v-if="deviceList.length === 0">
<text class="empty-text">暂无监控设备</text>
</view>
</scroll-view>
</view>
</template>
<script>
import { listMonitorData } from '@/api/monitorList'
import { getStoreId } from '@/utils/auth'
export default {
data() {
return {
//
deviceList: [],
pageType: '1' ,// 1 0
storeId:''
};
},
onLoad(options) {
this.storeId = getStoreId();
if (!this.storeId) {
uni.showToast({
title: '未获取到门店信息,请重新登录',
icon: 'none'
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
return;
}
if (options && options.type !== undefined) {
this.pageType = options.type;
}
//
uni.setNavigationBarTitle({
title: this.pageType === '0' ? '选择回放设备' : '监控设备列表'
});
//
this.fetchDeviceList();
},
methods: {
async fetchDeviceList() {
uni.showLoading({ title: '加载中...' });
try {
const res = await listMonitorData(this.storeId);
if (res.code === 200 && res.data) {
// deviceType 1 便
this.deviceList = res.data
.filter(item => item.deviceType === '1' || item.deviceType === 1)
.map(item => {
return {
name: item.deviceName,
uid: item.deviceCode,
isNvr: item.deviceModel == '1'?false:true, // 1(IPC) false 2(NVR) true
channel: item.channel !== undefined ? item.channel : 4, // 4
// online: item.status === '1' || item.status === 1 || item.online // 1 线
};
});
} else {
uni.showToast({ title: '获取数据失败', icon: 'none' });
}
} catch (error) {
console.error('获取监控列表失败', error);
uni.showToast({ title: '网络请求异常', icon: 'none' });
} finally {
uni.hideLoading();
}
},
goToMonitorDetail(device) {
// 线
// if (!device.online) {
// uni.showToast({ title: '线', icon: 'none' });
// return;
// }
if (this.pageType === '0') {
// uidisNvr channel
uni.navigateTo({
url: `/package_a/playback/playback?uid=${device.uid}&isNvr=${device.isNvr}&channel=${device.channel}`
});
} else {
// uidisNvr channel
uni.navigateTo({
url: `/package_a/monitor/monitor?uid=${device.uid}&isNvr=${device.isNvr}&channel=${device.channel}`
});
}
}
}
};
</script>
<style lang="scss" scoped>
.container {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f5f5f5;
}
.header {
padding: 30rpx 40rpx;
background-color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
border-bottom: 1px solid #eeeeee;
}
.title {
font-size: 32rpx;
font-weight: bold;
color: #333333;
}
.list-container {
flex: 1;
padding: 20rpx;
}
.device-card {
background-color: #ffffff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.device-info {
display: flex;
flex-direction: column;
}
.device-name {
font-size: 30rpx;
font-weight: bold;
color: #333333;
margin-bottom: 10rpx;
}
.device-uid {
font-size: 24rpx;
color: #666666;
margin-bottom: 10rpx;
}
.device-status {
display: flex;
flex-direction: row;
align-items: center;
}
.status-dot {
width: 12rpx;
height: 12rpx;
border-radius: 6rpx;
margin-right: 10rpx;
}
.status-dot.online {
background-color: #52c41a;
}
.status-dot.offline {
background-color: #999999;
}
.status-text {
font-size: 24rpx;
color: #666666;
}
.device-action {
display: flex;
flex-direction: row;
align-items: center;
}
.action-text {
font-size: 26rpx;
color: #007aff;
margin-right: 6rpx;
}
.empty-tips {
display: flex;
justify-content: center;
align-items: center;
padding-top: 100rpx;
}
.empty-text {
font-size: 28rpx;
color: #999999;
}
</style>

View File

@ -33,10 +33,26 @@
<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>
<text class="option-btn" v-if="!isNvr" :class="!isNvr ? 'active-btn' : ''" @click="changeNvr(false)">单体(IPC)</text>
<text class="option-btn" v-else :class="isNvr ? 'active-btn' : ''" @click="changeNvr(true)">主机(NVR)</text>
</view>
</view>
<view class="setting-row" v-if="isNvr && totalChannel > 0">
<text class="setting-label">通道:</text>
<!-- nvue 的 flex 默认不支持换行flex-wrap通道多的话外面套一个 scroll-view 可以左右滑动 -->
<scroll-view class="channel-scroll" scroll-x="true" show-scrollbar="false">
<view class="options-group channel-group">
<text
class="option-btn"
v-for="(item, index) in totalChannel"
:key="index"
:class="currentChannel === index ? 'active-btn' : ''"
@click="changeChannel(index)">
CH{{ index + 1 }}
</text>
</view>
</scroll-view>
</view>
<!-- 日期选择 -->
<view class="setting-row date-control-row">
@ -62,27 +78,34 @@
</template>
<script>
export default {
data() {
return {
deviceUid: 'HLTY036190SYNSK',
deviceUid: '',
currentChannel: 0,
isNvr: true,
playDate: '',
playTime: '',
statusMsg: '等待操作',
isPlaying: false
isPlaying: false,
totalChannel:1
}
},
onLoad(options) {
if (options && options.uid) {
this.deviceUid = 'HLTY036190SYNSK';
this.deviceUid = options.uid;
}
if (options && typeof options.isNvr !== 'undefined') {
this.isNvr = options.isNvr === 'true';
this.isNvr = options.isNvr;
}
// 接收总通道数
if (options && options.channel) {
this.currentChannel = parseInt(options.channel);
this.totalChannel = parseInt(options.channel);
// 如果通道数不合理比如0或者NaN给个默认值
if (!this.totalChannel || this.totalChannel <= 0) {
this.totalChannel = 1;
}
}
// 初始化默认时间为当前时间
@ -113,6 +136,18 @@
setTimeout(() => { this.startPlayback(); }, 1000);
}
},
changeChannel(ch) {
console.log(ch);
if (this.currentChannel === ch) return;
this.currentChannel = ch;
// 如果正在播放,切换通道后重新播放
if (this.isPlaying) {
this.stopPlayback();
setTimeout(() => {
this.startPlayback();
}, 500);
}
},
prevDay() {
// 解决部分环境解析 YYYY-MM-DD 报错,替换为 YYYY/MM/DD
let d = new Date(this.playDate.replace(/-/g, '/'));
@ -227,6 +262,40 @@
<style>
/* nvue 样式 */
.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;
}
.channel-scroll {
flex: 1;
flex-direction: row;
}
.channel-group {
flex-direction: row;
padding-bottom: 10rpx; /* 预留一点底部空间,防止被遮挡 */
}
.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;
}
.playback-container {
flex: 1;
flex-direction: column;

View File

@ -74,6 +74,12 @@
"style": {
"navigationBarTitleText": ""
}
},
{
"path": "monitorList/monitorList",
"style": {
"navigationBarTitleText": ""
}
}
]
}

View File

@ -372,7 +372,7 @@ export default {
// #ifndef MP
uni.navigateTo({
url: '/package_a/monitor/monitor'
url: '/package_a/monitorList/monitorList?type=1' // type=1
})
// #endif
},
@ -386,7 +386,7 @@ export default {
// #ifndef MP
uni.navigateTo({
url: '/package_a/playback/playback'
url: '/package_a/monitorList/monitorList?type=0' // type=0
})
// #endif
},