首页点播布局功能初版
This commit is contained in:
parent
91bf81b2a1
commit
484e36b964
|
@ -1,7 +1,207 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="camera-container">
|
||||||
<h1>
|
<!-- 左侧摄像头列表 -->
|
||||||
首页
|
<div class="camera-list">
|
||||||
</h1>
|
<el-card v-for="camera in cameras" :key="camera.id" class="camera-item" @click="selectCamera(camera)">
|
||||||
</div>
|
<div class="camera-header">
|
||||||
</template>
|
<span>ID: {{ camera.id }}</span>
|
||||||
|
<span class="status" :class="{'online': camera.status === 'online', 'offline': camera.status !== 'online'}">
|
||||||
|
{{ camera.status === 'online' ? '在线' : '离线' }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="camera-content">
|
||||||
|
<img :src="camera.snapshot" alt="camera-preview" class="camera-thumbnail" />
|
||||||
|
<p class="camera-name">{{ camera.name }}</p>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧摄像头详情和播放控制 -->
|
||||||
|
<div class="camera-details" v-if="selectedCamera">
|
||||||
|
<el-card>
|
||||||
|
<p>{{ selectedCamera.name }}</p>
|
||||||
|
<canvas ref="canvasRef" class="camera-large"></canvas>
|
||||||
|
<p>摄像头状态:
|
||||||
|
<span :class="{'online': selectedCamera.status === 'online', 'offline': selectedCamera.status !== 'online'}">
|
||||||
|
{{ selectedCamera.status === 'online' ? '在线' : '离线' }}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
<!-- 播放控制按钮 -->
|
||||||
|
<div class="stream-control">
|
||||||
|
<el-button v-if="!playing" type="primary" @click="handleStartStream">开始点播</el-button>
|
||||||
|
<el-button v-if="playing" type="danger" @click="handleStopStream">停止点播</el-button>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted, onBeforeUnmount } from 'vue';
|
||||||
|
import { BoxApi } from '@/utils/boxApi.ts';
|
||||||
|
// import Jsmpeg from '../../public/static/jsmpeg-master/jsmpeg.min.js';
|
||||||
|
// console.log(">>>>>>>>>>>>>>>>>"+Jsmpeg);
|
||||||
|
|
||||||
|
const cameras = ref([]);
|
||||||
|
const selectedCamera = ref(null);
|
||||||
|
const playing = ref(false);
|
||||||
|
const streamPort = ref(null);
|
||||||
|
const canvasRef = ref(null);
|
||||||
|
const playerRef = ref(null);
|
||||||
|
|
||||||
|
const apiInstance = new BoxApi();
|
||||||
|
|
||||||
|
// 请求摄像头数据
|
||||||
|
const fetchCameras = async () => {
|
||||||
|
try {
|
||||||
|
const token = localStorage.getItem('alertToken');
|
||||||
|
const cameraData = await apiInstance.getMinCameras(token); // 调用 getMinCameras 方法
|
||||||
|
cameras.value = cameraData; // 保存摄像头列表数据
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching cameras:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 选择摄像头
|
||||||
|
const selectCamera = (camera) => {
|
||||||
|
selectedCamera.value = camera; // 将点击的摄像头设为选中的摄像头
|
||||||
|
playing.value = false; // 重置播放状态
|
||||||
|
streamPort.value = null; // 重置流端口
|
||||||
|
};
|
||||||
|
|
||||||
|
// 启动流播放
|
||||||
|
const handleStartStream = async () => {
|
||||||
|
if (!selectedCamera.value || !canvasRef.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = localStorage.getItem('alertToken');
|
||||||
|
try {
|
||||||
|
// 调用 API 获取端口
|
||||||
|
const response = await apiInstance.startCameraStream(token, selectedCamera.value.id);
|
||||||
|
streamPort.value = response.port;
|
||||||
|
playing.value = true;
|
||||||
|
|
||||||
|
// 创建播放器
|
||||||
|
const url = `ws://192.168.28.33:${streamPort.value}/`; // 动态流 URL
|
||||||
|
console.log('WebSocket URL>>>>>>>>>>>>>:', url);
|
||||||
|
|
||||||
|
// 使用全局的 window.JSMpeg 对象
|
||||||
|
if (playerRef.value) {
|
||||||
|
playerRef.value.destroy(); // 销毁旧播放器
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.JSMpeg) {
|
||||||
|
playerRef.value = new window.JSMpeg.Player(url, {
|
||||||
|
canvas: canvasRef.value, // 指定 canvas 渲染
|
||||||
|
autoplay: true,
|
||||||
|
videoBufferSize: 15 * 1024 * 1024, // 设置视频缓冲区大小
|
||||||
|
audioBufferSize: 5 * 1024 * 1024, // 设置音频缓冲区大小
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.error('JSMpeg is not available on window object.');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error starting stream:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 停止流播放
|
||||||
|
const handleStopStream = async () => {
|
||||||
|
if (!selectedCamera.value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = localStorage.getItem('alertToken');
|
||||||
|
try {
|
||||||
|
await apiInstance.stopCameraStream(token, selectedCamera.value.id);
|
||||||
|
playing.value = false;
|
||||||
|
|
||||||
|
// 销毁播放器
|
||||||
|
if (playerRef.value) {
|
||||||
|
playerRef.value.destroy();
|
||||||
|
playerRef.value = null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error stopping stream:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 组件卸载时清理播放器
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
if (playerRef.value) {
|
||||||
|
playerRef.value.destroy();
|
||||||
|
playerRef.value = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchCameras(); // 页面加载时请求摄像头数据
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style scoped>
|
||||||
|
.camera-container {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.camera-list {
|
||||||
|
width: 20%;
|
||||||
|
max-height: 90vh; /* 限制高度为一屏 */
|
||||||
|
overflow-y: auto; /* 超出时滚动 */
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.camera-item {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.camera-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.camera-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.camera-thumbnail {
|
||||||
|
width: 60px;
|
||||||
|
height: 40px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.camera-name {
|
||||||
|
flex: 1;
|
||||||
|
word-break: break-word; /* 文本超出时换行 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.camera-details {
|
||||||
|
width: 80%;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.camera-large {
|
||||||
|
width: 100%;
|
||||||
|
height: 68vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.online {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.offline {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stream-control {
|
||||||
|
margin-top: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue