摄像组件响应布局,缩小动态换行

This commit is contained in:
龚皓 2024-10-22 13:47:44 +08:00
parent c1bf58a99a
commit 8eaafbd577
1 changed files with 149 additions and 57 deletions

View File

@ -1,22 +1,30 @@
<template> <template>
<div> <div>
<el-card class="camera-card"> <el-card class="camera-card">
<el-row :gutter="20" class="camera-row"> <el-row class="camera-row">
<!-- 左侧块通道列表 --> <!-- 左侧块通道列表 -->
<el-col :span="8"> <el-col :sm="24" :md="8">
<el-card class="channel-card" shadow="hover"> <el-card class="channel-card" shadow="hover">
<div class="section-title">通道</div> <div class="section-title">通道</div>
<div class="scroll-container"> <div class="scroll-container">
<el-table :data="cameras" height="250"> <el-table :data="cameras" height="250">
<el-table-column prop="id" label="ID" width="100"></el-table-column> <el-table-column prop="id" label="ID" width="100" align="center"></el-table-column>
<el-table-column prop="name" label="名称"></el-table-column> <el-table-column prop="name" label="名称" align="center"></el-table-column>
<!-- 添加点播按钮的列 -->
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button type="primary" @click="handlePlay(scope.row)">
点播
</el-button>
</template>
</el-table-column>
</el-table> </el-table>
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
<!-- 中间块摄像头总数量 --> <!-- 中间块摄像头总数量 -->
<el-col :span="8"> <el-col :sm="24" :md="8">
<el-card class="count-card" shadow="hover"> <el-card class="count-card" shadow="hover">
<div class="section-title">摄像数量</div> <div class="section-title">摄像数量</div>
<div class="camera-count-chart"></div> <div class="camera-count-chart"></div>
@ -24,19 +32,37 @@
</el-col> </el-col>
<!-- 右侧块在线/离线/异常状态 --> <!-- 右侧块在线/离线/异常状态 -->
<el-col :span="8"> <el-col :sm="24" :md="8">
<el-card class="online-card" shadow="hover"> <el-card class="online-card" shadow="hover">
<div class="section-title">在线情况</div> <div class="section-title">在线情况</div>
<div class="status-summary-chart"></div> <div class="status-summary-chart"></div>
</el-card> </el-card>
</el-col> </el-col>
</el-row> </el-row>
<!-- 视频播放弹窗 -->
<el-dialog v-model="dialogVisible" width="50%" @close="handleStopStream" v-if="selectedCamera">
<p>
正在播放{{ selectedCamera.name }}
</p>
<div v-if="loading" class="loading-container">
<el-spinner size="large"></el-spinner>
</div>
<div v-show="!loading" class="video-container">
<canvas ref="canvasRef" class="video-canvas"></canvas>
</div>
<template #footer>
<el-button @click="dialogVisible = false">关闭</el-button>
</template>
</el-dialog>
</el-card> </el-card>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from 'vue'; import { ref, onMounted, onBeforeUnmount } from 'vue';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
import { BoxApi } from '@/utils/boxApi.ts'; import { BoxApi } from '@/utils/boxApi.ts';
@ -45,7 +71,11 @@ const cameraCount = ref(0);
const onlineCount = ref(0); const onlineCount = ref(0);
const offlineCount = ref(0); const offlineCount = ref(0);
const exceptionCount = ref(0); const exceptionCount = ref(0);
const selectedCamera = ref(null);
const dialogVisible = ref(false);
const loading = ref(true); //
const canvasRef = ref(null);
const playerRef = ref(null);
const apiInstance = new BoxApi(); const apiInstance = new BoxApi();
// //
@ -72,7 +102,7 @@ const fetchCameras = async () => {
cameras.value = allCameras; cameras.value = allCameras;
// 线线 // 线线
allCameras.forEach(camera => { allCameras.forEach((camera) => {
if (camera.status === 'online') { if (camera.status === 'online') {
onlineCount.value++; onlineCount.value++;
} else if (camera.status === 'offline') { } else if (camera.status === 'offline') {
@ -92,6 +122,59 @@ const fetchCameras = async () => {
} }
}; };
//
const handlePlay = (camera) => {
selectedCamera.value = camera;
dialogVisible.value = true;
loading.value = true;
//
setTimeout(() => {
loading.value = false;
handleStartStream(); //
}, 2000); // 2
};
//
const handleStartStream = async () => {
const token = localStorage.getItem('alertToken');
try {
const response = await apiInstance.startCameraStream(token, selectedCamera.value.id);
const streamPort = response.port;
//
const url = `ws://192.168.28.33:${streamPort}/`;
if (playerRef.value) {
playerRef.value.destroy(); //
}
if (window.JSMpeg) {
playerRef.value = new window.JSMpeg.Player(url, {
canvas: canvasRef.value,
autoplay: true,
videoBufferSize: 15 * 1024 * 1024,
audioBufferSize: 5 * 1024 * 1024,
});
}
} catch (error) {
console.error('Error starting stream:', error);
}
};
//
const handleStopStream = async () => {
const token = localStorage.getItem('alertToken');
try {
await apiInstance.stopCameraStream(token, selectedCamera.value.id);
if (playerRef.value) {
playerRef.value.destroy();
playerRef.value = null;
}
} catch (error) {
console.error('Error stopping stream:', error);
}
};
// //
const initCountChart = (count) => { const initCountChart = (count) => {
const chartDom = document.querySelector('.camera-count-chart'); const chartDom = document.querySelector('.camera-count-chart');
@ -109,15 +192,12 @@ const initCountChart = (count) => {
formatter: '{c}', formatter: '{c}',
fontSize: 24, fontSize: 24,
}, },
data: [ data: [{ value: count, name: '总数' }],
{ value: count, name: '总数' }
],
itemStyle: { itemStyle: {
color: 'rgba(80,160,225, 1)' color: 'rgba(80,160,225, 1)',
} },
},
} ],
]
}; };
myChart.setOption(option); myChart.setOption(option);
@ -135,24 +215,31 @@ const initStatusSummaryChart = (online, offline, exception) => {
radius: ['40%', '60%'], radius: ['40%', '60%'],
label: { label: {
show: true, show: true,
formatter: '{b}: {c} ({d}%)' formatter: '{b}: {c} ({d}%)',
}, },
data: [ data: [
{ value: online, name: '在线' }, { value: online, name: '在线' },
{ value: offline, name: '离线' }, { value: offline, name: '离线' },
{ value: exception, name: '异常' } { value: exception, name: '异常' },
], ],
itemStyle: { itemStyle: {
color: 'rgba(5,192,145, 1)' color: 'rgba(5,192,145, 1)',
// color: 'rgba(255,151,75, 1)' },
} },
} ],
]
}; };
myChart.setOption(option); myChart.setOption(option);
}; };
//
onBeforeUnmount(() => {
if (playerRef.value) {
playerRef.value.destroy();
playerRef.value = null;
}
});
onMounted(() => { onMounted(() => {
fetchCameras(); fetchCameras();
}); });
@ -160,22 +247,22 @@ onMounted(() => {
<style scoped> <style scoped>
.camera-card { .camera-card {
/* background-color: #abcef1; */
/* background-color: linear-gradient(to top, rgba(150, 206, 243, 0.2), rgba(214, 240, 250, 0.3)); */
color: #fff; color: #fff;
/* border-radius: 8px; */
margin: 0; margin: 0;
padding: 0; padding: 0;
/* padding-left: 5%; */
} }
.channel-card { .channel-card {
background: linear-gradient(to top, rgba(150, 206, 243, 1), rgba(125, 29, 235, 0.3)); background: linear-gradient(to top, rgba(150, 206, 243, 1), rgba(125, 29, 235, 0.3));
border-radius: 12px; border-radius: 12px;
} }
.count-card { .count-card {
background: linear-gradient(to bottom, rgba(246, 130, 85, 1), rgba(252, 186, 38, 0.3)); background: linear-gradient(to bottom, rgba(246, 130, 85, 1), rgba(252, 186, 38, 0.3));
border-radius: 12px; border-radius: 12px;
margin-left: 20px;
margin-right: 20px;
} }
.online-card { .online-card {
@ -183,45 +270,42 @@ onMounted(() => {
border-radius: 12px; border-radius: 12px;
} }
.camera-header {
font-size: 18px;
font-weight: bold;
border-bottom: 1px solid #3a4b5c;
}
.camera-row {
margin-top: 0px;
margin-bottom: 0px;
}
.section-title { .section-title {
font-size: 16px; font-size: 16px;
font-weight: bold; font-weight: bold;
margin-bottom: 10px; margin-bottom: 10px;
} }
.el-card {
padding: 0;
text-align: center;
background-color: azure
}
.scroll-container { .scroll-container {
height: 170px; height: 170px;
/* overflow-y: scroll; */ /* overflow-y: auto; */
scrollbar-width: none;
}
/* .scroll-container::-webkit-scrollbar { }
display: none;
} */
.status-summary-chart, .status-summary-chart,
.camera-count-chart { .camera-count-chart {
width: 100%; /* width: 100%; */
height: 170px; height: 170px;
} }
.loading-container {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
}
.video-container {
width: 100%;
height: 400px;
background-color: #000;
}
.video-canvas {
width: 100%;
height: 100%;
}
.el-table { .el-table {
border-radius: 8px; border-radius: 8px;
overflow: hidden; overflow: hidden;
@ -229,14 +313,22 @@ onMounted(() => {
} }
.el-table th { .el-table th {
background-color: #f5f7fa; /* 表头背景色 */ background-color: #f5f7fa;
color: #333; /* 表头字体颜色 */ color: #333;
font-weight: bold; font-weight: bold;
text-align: center; /* 居中对齐 */ text-align: center;
} }
::v-deep .el-card__body{ ::v-deep .el-card__body {
padding: 15px; padding: 15px;
} }
.camera-row {
max-height: 250px;
/* max-width: 1680px; */
overflow-x: auto;
/* gap: 1px; */
/* flex-wrap: nowrap; */
}
</style> </style>