摄像组件响应布局,缩小动态换行
This commit is contained in:
parent
c1bf58a99a
commit
8eaafbd577
|
@ -1,22 +1,30 @@
|
|||
<template>
|
||||
<div>
|
||||
<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">
|
||||
<div class="section-title">通道</div>
|
||||
<div class="scroll-container">
|
||||
<el-table :data="cameras" height="250">
|
||||
<el-table-column prop="id" label="ID" width="100"></el-table-column>
|
||||
<el-table-column prop="name" label="名称"></el-table-column>
|
||||
<el-table-column prop="id" label="ID" width="100" align="center"></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>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<!-- 中间块:摄像头总数量 -->
|
||||
<el-col :span="8">
|
||||
<el-col :sm="24" :md="8">
|
||||
<el-card class="count-card" shadow="hover">
|
||||
<div class="section-title">摄像数量</div>
|
||||
<div class="camera-count-chart"></div>
|
||||
|
@ -24,19 +32,37 @@
|
|||
</el-col>
|
||||
|
||||
<!-- 右侧块:在线/离线/异常状态 -->
|
||||
<el-col :span="8">
|
||||
<el-col :sm="24" :md="8">
|
||||
<el-card class="online-card" shadow="hover">
|
||||
<div class="section-title">在线情况</div>
|
||||
<div class="status-summary-chart"></div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</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>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import { BoxApi } from '@/utils/boxApi.ts';
|
||||
|
||||
|
@ -45,7 +71,11 @@ const cameraCount = ref(0);
|
|||
const onlineCount = ref(0);
|
||||
const offlineCount = 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();
|
||||
|
||||
// 分页获取摄像头数据,直到获取所有摄像头数据
|
||||
|
@ -72,7 +102,7 @@ const fetchCameras = async () => {
|
|||
cameras.value = allCameras;
|
||||
|
||||
// 统计在线、离线和异常的摄像头数量
|
||||
allCameras.forEach(camera => {
|
||||
allCameras.forEach((camera) => {
|
||||
if (camera.status === 'online') {
|
||||
onlineCount.value++;
|
||||
} 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 chartDom = document.querySelector('.camera-count-chart');
|
||||
|
@ -109,15 +192,12 @@ const initCountChart = (count) => {
|
|||
formatter: '{c}',
|
||||
fontSize: 24,
|
||||
},
|
||||
data: [
|
||||
{ value: count, name: '总数' }
|
||||
],
|
||||
data: [{ value: count, name: '总数' }],
|
||||
itemStyle: {
|
||||
color: 'rgba(80,160,225, 1)'
|
||||
}
|
||||
|
||||
}
|
||||
]
|
||||
color: 'rgba(80,160,225, 1)',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
myChart.setOption(option);
|
||||
|
@ -135,24 +215,31 @@ const initStatusSummaryChart = (online, offline, exception) => {
|
|||
radius: ['40%', '60%'],
|
||||
label: {
|
||||
show: true,
|
||||
formatter: '{b}: {c} ({d}%)'
|
||||
formatter: '{b}: {c} ({d}%)',
|
||||
},
|
||||
data: [
|
||||
{ value: online, name: '在线' },
|
||||
{ value: offline, name: '离线' },
|
||||
{ value: exception, name: '异常' }
|
||||
{ value: exception, name: '异常' },
|
||||
],
|
||||
itemStyle: {
|
||||
color: 'rgba(5,192,145, 1)'
|
||||
// color: 'rgba(255,151,75, 1)'
|
||||
}
|
||||
}
|
||||
]
|
||||
color: 'rgba(5,192,145, 1)',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
myChart.setOption(option);
|
||||
};
|
||||
|
||||
// 组件卸载时清理播放器
|
||||
onBeforeUnmount(() => {
|
||||
if (playerRef.value) {
|
||||
playerRef.value.destroy();
|
||||
playerRef.value = null;
|
||||
}
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
fetchCameras();
|
||||
});
|
||||
|
@ -160,22 +247,22 @@ onMounted(() => {
|
|||
|
||||
<style scoped>
|
||||
.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;
|
||||
/* border-radius: 8px; */
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
/* padding-left: 5%; */
|
||||
}
|
||||
|
||||
.channel-card {
|
||||
background: linear-gradient(to top, rgba(150, 206, 243, 1), rgba(125, 29, 235, 0.3));
|
||||
border-radius: 12px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.count-card {
|
||||
background: linear-gradient(to bottom, rgba(246, 130, 85, 1), rgba(252, 186, 38, 0.3));
|
||||
border-radius: 12px;
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.online-card {
|
||||
|
@ -183,45 +270,42 @@ onMounted(() => {
|
|||
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 {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.el-card {
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
background-color: azure
|
||||
}
|
||||
|
||||
.scroll-container {
|
||||
height: 170px;
|
||||
/* overflow-y: scroll; */
|
||||
scrollbar-width: none;
|
||||
}
|
||||
/* overflow-y: auto; */
|
||||
|
||||
/* .scroll-container::-webkit-scrollbar {
|
||||
display: none;
|
||||
} */
|
||||
}
|
||||
|
||||
.status-summary-chart,
|
||||
.camera-count-chart {
|
||||
width: 100%;
|
||||
/* width: 100%; */
|
||||
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 {
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
|
@ -229,14 +313,22 @@ onMounted(() => {
|
|||
}
|
||||
|
||||
.el-table th {
|
||||
background-color: #f5f7fa; /* 表头背景色 */
|
||||
color: #333; /* 表头字体颜色 */
|
||||
font-weight: bold;
|
||||
text-align: center; /* 居中对齐 */
|
||||
background-color: #f5f7fa;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
::v-deep .el-card__body{
|
||||
::v-deep .el-card__body {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.camera-row {
|
||||
max-height: 250px;
|
||||
/* max-width: 1680px; */
|
||||
overflow-x: auto;
|
||||
/* gap: 1px; */
|
||||
/* flex-wrap: nowrap; */
|
||||
}
|
||||
</style>
|
||||
|
Loading…
Reference in New Issue