841 lines
29 KiB
HTML
841 lines
29 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>智能监控视频系统</title>
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
|
||
}
|
||
|
||
body {
|
||
background-color: #0d1117;
|
||
color: #e6edf3;
|
||
width: 1920px;
|
||
height: 1080px;
|
||
overflow: hidden;
|
||
display: flex;
|
||
}
|
||
|
||
/* 侧边栏样式 */
|
||
.sidebar {
|
||
width: 320px;
|
||
background-color: #161b22;
|
||
border-right: 1px solid #30363d;
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.header {
|
||
padding: 24px 20px;
|
||
border-bottom: 1px solid #30363d;
|
||
}
|
||
|
||
.logo {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.logo i {
|
||
font-size: 28px;
|
||
color: #58a6ff;
|
||
}
|
||
|
||
.logo h1 {
|
||
font-size: 22px;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.status-info {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-top: 16px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.online {
|
||
color: #3fb950;
|
||
}
|
||
|
||
.offline {
|
||
color: #f85149;
|
||
}
|
||
|
||
.camera-list {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding: 16px 0;
|
||
}
|
||
|
||
.camera-list::-webkit-scrollbar {
|
||
width: 6px;
|
||
}
|
||
|
||
.camera-list::-webkit-scrollbar-thumb {
|
||
background-color: #30363d;
|
||
border-radius: 3px;
|
||
}
|
||
|
||
.camera-item {
|
||
padding: 16px 20px;
|
||
border-bottom: 1px solid #21262d;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
}
|
||
|
||
.camera-item:hover {
|
||
background-color: #1c2128;
|
||
}
|
||
|
||
.camera-item.active {
|
||
background-color: #1c2128;
|
||
border-left: 3px solid #58a6ff;
|
||
}
|
||
|
||
.camera-icon {
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 8px;
|
||
background-color: #238636;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 18px;
|
||
}
|
||
|
||
.camera-info {
|
||
flex: 1;
|
||
}
|
||
|
||
.camera-name {
|
||
font-weight: 600;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.camera-location {
|
||
font-size: 13px;
|
||
color: #8b949e;
|
||
}
|
||
|
||
.camera-status {
|
||
width: 10px;
|
||
height: 10px;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
.camera-status.online {
|
||
background-color: #3fb950;
|
||
}
|
||
|
||
.camera-status.offline {
|
||
background-color: #f85149;
|
||
}
|
||
|
||
.camera-status.recording {
|
||
background-color: #f0883e;
|
||
animation: pulse 1.5s infinite;
|
||
}
|
||
|
||
@keyframes pulse {
|
||
0% { opacity: 1; }
|
||
50% { opacity: 0.5; }
|
||
100% { opacity: 1; }
|
||
}
|
||
|
||
/* 主内容区样式 */
|
||
.main-content {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding: 24px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.controls {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 24px;
|
||
padding-bottom: 20px;
|
||
border-bottom: 1px solid #30363d;
|
||
}
|
||
|
||
.layout-controls {
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
|
||
.layout-btn {
|
||
padding: 10px 20px;
|
||
background-color: #21262d;
|
||
border: 1px solid #30363d;
|
||
color: #c9d1d9;
|
||
border-radius: 6px;
|
||
cursor: pointer;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
transition: all 0.2s;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.layout-btn:hover {
|
||
background-color: #30363d;
|
||
border-color: #8b949e;
|
||
}
|
||
|
||
.layout-btn.active {
|
||
background-color: #1f6feb;
|
||
border-color: #1f6feb;
|
||
color: white;
|
||
}
|
||
|
||
.time-display {
|
||
font-size: 16px;
|
||
color: #8b949e;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
/* 视频网格样式 */
|
||
.video-grid {
|
||
flex: 1;
|
||
display: grid;
|
||
gap: 16px;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.video-grid.single {
|
||
grid-template-columns: 1fr;
|
||
grid-template-rows: 1fr;
|
||
}
|
||
|
||
.video-grid.four {
|
||
grid-template-columns: 1fr 1fr;
|
||
grid-template-rows: 1fr 1fr;
|
||
}
|
||
|
||
.video-grid.nine {
|
||
grid-template-columns: 1fr 1fr 1fr;
|
||
grid-template-rows: 1fr 1fr 1fr;
|
||
}
|
||
|
||
.video-player {
|
||
background-color: #000;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
position: relative;
|
||
border: 1px solid #30363d;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.video-header {
|
||
padding: 12px 16px;
|
||
background-color: rgba(0, 0, 0, 0.7);
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
border-bottom: 1px solid #30363d;
|
||
}
|
||
|
||
.video-title {
|
||
font-weight: 600;
|
||
font-size: 14px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.video-controls {
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
|
||
.control-btn {
|
||
background: none;
|
||
border: none;
|
||
color: #c9d1d9;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
padding: 4px;
|
||
border-radius: 4px;
|
||
transition: all 0.2s;
|
||
}
|
||
|
||
.control-btn:hover {
|
||
background-color: rgba(255, 255, 255, 0.1);
|
||
}
|
||
|
||
.video-container {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
overflow: hidden;
|
||
position: relative;
|
||
}
|
||
|
||
.video-feed {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.no-video {
|
||
color: #8b949e;
|
||
font-size: 16px;
|
||
text-align: center;
|
||
}
|
||
|
||
.playing-indicator {
|
||
position: absolute;
|
||
top: 10px;
|
||
right: 10px;
|
||
background-color: rgba(0, 0, 0, 0.7);
|
||
color: #f85149;
|
||
padding: 4px 8px;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
|
||
/* 时间更新函数 */
|
||
.time-update {
|
||
margin-top: 20px;
|
||
text-align: center;
|
||
color: #8b949e;
|
||
font-size: 14px;
|
||
}
|
||
|
||
/* 响应式调整 */
|
||
@media (max-width: 1920px) {
|
||
body {
|
||
width: 100%;
|
||
height: 100vh;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<!-- 左侧边栏 -->
|
||
<div class="sidebar">
|
||
<div class="header">
|
||
<div class="logo">
|
||
<i class="fas fa-shield-alt"></i>
|
||
<h1>智能监控系统</h1>
|
||
</div>
|
||
<p>监控中心 v2.1</p>
|
||
|
||
<div class="status-info">
|
||
<div class="online">
|
||
<i class="fas fa-circle"></i> 在线: <span id="online-count">8</span>
|
||
</div>
|
||
<div class="offline">
|
||
<i class="fas fa-circle"></i> 离线: <span id="offline-count">2</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="camera-list" id="camera-list">
|
||
<!-- 摄像头列表将通过JavaScript动态生成 -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 主内容区 -->
|
||
<div class="main-content">
|
||
<div class="controls">
|
||
<div class="layout-controls">
|
||
<button class="layout-btn active" data-layout="single">
|
||
<i class="fas fa-expand"></i> 单画面
|
||
</button>
|
||
<button class="layout-btn" data-layout="four">
|
||
<i class="fas fa-th-large"></i> 四画面
|
||
</button>
|
||
<button class="layout-btn" data-layout="nine">
|
||
<i class="fas fa-th"></i> 九画面
|
||
</button>
|
||
</div>
|
||
|
||
<div class="time-display">
|
||
<i class="far fa-clock"></i>
|
||
<span id="current-time">--:--:--</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="video-grid nine" id="video-grid">
|
||
<!-- 视频播放器将通过JavaScript动态生成 -->
|
||
</div>
|
||
|
||
<div class="time-update">
|
||
<p>最后更新: <span id="last-update">--:--:--</span> | 系统运行正常</p>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// 模拟摄像头数据
|
||
const cameras = [
|
||
{ id: 1, name: "主入口", location: "一楼大厅", status: "online", recording: true, feed: "https://assets.mixkit.co/videos/preview/mixkit-security-camera-on-the-corner-of-a-building-42939-large.mp4" },
|
||
{ id: 2, name: "停车场", location: "地下B1层", status: "online", recording: true, feed: "https://assets.mixkit.co/videos/preview/mixkit-parking-lot-entrance-from-a-high-angle-42942-large.mp4" },
|
||
{ id: 3, name: "电梯间", location: "1号电梯", status: "online", recording: false, feed: "https://assets.mixkit.co/videos/preview/mixkit-going-up-the-stairs-of-a-modern-building-42944-large.mp4" },
|
||
{ id: 4, name: "财务室", location: "三楼301", status: "online", recording: true, feed: "https://assets.mixkit.co/videos/preview/mixkit-woman-working-on-a-laptop-42945-large.mp4" },
|
||
{ id: 5, name: "仓库", location: "地下一层", status: "offline", recording: false, feed: "" },
|
||
{ id: 6, name: "侧门", location: "建筑东侧", status: "online", recording: false, feed: "https://assets.mixkit.co/videos/preview/mixkit-exit-sign-on-a-building-wall-42943-large.mp4" },
|
||
{ id: 7, name: "办公室", location: "二楼201", status: "online", recording: false, feed: "https://assets.mixkit.co/videos/preview/mixkit-open-plan-office-with-modern-design-42941-large.mp4" },
|
||
{ id: 8, name: "走廊", location: "三楼北侧", status: "online", recording: true, feed: "https://assets.mixkit.co/videos/preview/mixkit-hallway-in-a-modern-office-building-42940-large.mp4" },
|
||
{ id: 9, name: "后院", location: "建筑后方", status: "offline", recording: false, feed: "" },
|
||
{ id: 10, name: "接待处", location: "一楼前台", status: "online", recording: true, feed: "https://assets.mixkit.co/videos/preview/mixkit-empty-reception-of-a-modern-hotel-42938-large.mp4" }
|
||
];
|
||
|
||
// 当前选中的摄像头
|
||
let selectedCameraId = 1;
|
||
// 当前布局模式
|
||
let currentLayout = 'nine';
|
||
// 视频播放器数组
|
||
let videoPlayers = [];
|
||
|
||
// DOM 加载完成后初始化
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
initCameraList();
|
||
initVideoGrid();
|
||
initLayoutControls();
|
||
updateTime();
|
||
setInterval(updateTime, 1000);
|
||
|
||
// 模拟视频播放
|
||
simulateVideoPlayback();
|
||
});
|
||
|
||
// 初始化摄像头列表
|
||
function initCameraList() {
|
||
const cameraList = document.getElementById('camera-list');
|
||
|
||
cameras.forEach(camera => {
|
||
const cameraItem = document.createElement('div');
|
||
cameraItem.className = `camera-item ${camera.id === selectedCameraId ? 'active' : ''}`;
|
||
cameraItem.dataset.id = camera.id;
|
||
|
||
// 根据状态设置图标颜色
|
||
let iconColor = '#238636'; // 默认绿色
|
||
let iconClass = 'fas fa-video';
|
||
|
||
if (camera.status === 'offline') {
|
||
iconColor = '#6e7681';
|
||
iconClass = 'fas fa-video-slash';
|
||
}
|
||
|
||
cameraItem.innerHTML = `
|
||
<div class="camera-icon" style="background-color: ${iconColor};">
|
||
<i class="${iconClass}"></i>
|
||
</div>
|
||
<div class="camera-info">
|
||
<div class="camera-name">${camera.name}</div>
|
||
<div class="camera-location">${camera.location}</div>
|
||
</div>
|
||
<div class="camera-status ${camera.status} ${camera.recording ? 'recording' : ''}"></div>
|
||
`;
|
||
|
||
cameraItem.addEventListener('click', function() {
|
||
// 移除所有active类
|
||
document.querySelectorAll('.camera-item').forEach(item => {
|
||
item.classList.remove('active');
|
||
});
|
||
|
||
// 添加active类到当前项
|
||
this.classList.add('active');
|
||
|
||
// 更新选中的摄像头
|
||
selectedCameraId = parseInt(this.dataset.id);
|
||
|
||
// 在第一个可用的视频播放器中播放选中的摄像头
|
||
playCameraInFirstAvailable(selectedCameraId);
|
||
});
|
||
|
||
cameraList.appendChild(cameraItem);
|
||
});
|
||
|
||
// 更新在线/离线计数
|
||
updateCameraCount();
|
||
}
|
||
|
||
// 初始化视频网格
|
||
function initVideoGrid() {
|
||
const videoGrid = document.getElementById('video-grid');
|
||
|
||
// 创建9个视频播放器
|
||
for (let i = 0; i < 9; i++) {
|
||
const cameraIndex = i < cameras.length ? i : 0;
|
||
const camera = cameras[cameraIndex];
|
||
|
||
const videoPlayer = document.createElement('div');
|
||
videoPlayer.className = 'video-player';
|
||
videoPlayer.dataset.index = i;
|
||
|
||
videoPlayer.innerHTML = `
|
||
<div class="video-header">
|
||
<div class="video-title">
|
||
<i class="fas fa-video"></i>
|
||
<span class="title-text">${camera.name}</span>
|
||
</div>
|
||
<div class="video-controls">
|
||
<button class="control-btn" title="全屏">
|
||
<i class="fas fa-expand"></i>
|
||
</button>
|
||
<button class="control-btn" title="声音">
|
||
<i class="fas fa-volume-up"></i>
|
||
</button>
|
||
<button class="control-btn" title="设置">
|
||
<i class="fas fa-cog"></i>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="video-container">
|
||
${camera.status === 'online' ?
|
||
`<video class="video-feed" muted loop>
|
||
<source src="${camera.feed}" type="video/mp4">
|
||
您的浏览器不支持视频标签。
|
||
</video>` :
|
||
`<div class="no-video">
|
||
<i class="fas fa-wifi-slash"></i>
|
||
<p>摄像头离线</p>
|
||
</div>`
|
||
}
|
||
${camera.recording ?
|
||
`<div class="playing-indicator">
|
||
<i class="fas fa-circle"></i> 录制中
|
||
</div>` : ''
|
||
}
|
||
</div>
|
||
`;
|
||
|
||
videoGrid.appendChild(videoPlayer);
|
||
|
||
// 保存视频元素引用
|
||
if (camera.status === 'online') {
|
||
const videoElement = videoPlayer.querySelector('video');
|
||
if (videoElement) {
|
||
videoPlayers.push(videoElement);
|
||
}
|
||
}
|
||
|
||
// 添加点击事件,点击视频播放器时选中对应的摄像头
|
||
videoPlayer.addEventListener('click', function(e) {
|
||
if (!e.target.closest('.video-controls')) {
|
||
const cameraId = cameras[cameraIndex].id;
|
||
selectedCameraId = cameraId;
|
||
|
||
// 更新摄像头列表选中状态
|
||
document.querySelectorAll('.camera-item').forEach(item => {
|
||
item.classList.remove('active');
|
||
if (parseInt(item.dataset.id) === cameraId) {
|
||
item.classList.add('active');
|
||
}
|
||
});
|
||
}
|
||
});
|
||
|
||
// 全屏按钮事件
|
||
const fullscreenBtn = videoPlayer.querySelector('.control-btn');
|
||
fullscreenBtn.addEventListener('click', function() {
|
||
const videoContainer = videoPlayer.querySelector('.video-container');
|
||
if (videoContainer.requestFullscreen) {
|
||
videoContainer.requestFullscreen();
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
// 初始化布局控制
|
||
function initLayoutControls() {
|
||
const layoutBtns = document.querySelectorAll('.layout-btn');
|
||
|
||
layoutBtns.forEach(btn => {
|
||
btn.addEventListener('click', function() {
|
||
// 移除所有active类
|
||
layoutBtns.forEach(b => b.classList.remove('active'));
|
||
|
||
// 添加active类到当前按钮
|
||
this.classList.add('active');
|
||
|
||
// 更新布局
|
||
const layout = this.dataset.layout;
|
||
changeLayout(layout);
|
||
});
|
||
});
|
||
}
|
||
|
||
// 更改布局
|
||
function changeLayout(layout) {
|
||
const videoGrid = document.getElementById('video-grid');
|
||
currentLayout = layout;
|
||
|
||
// 更新网格类
|
||
videoGrid.className = 'video-grid ' + layout;
|
||
|
||
// 根据布局显示/隐藏视频播放器
|
||
const videoPlayers = document.querySelectorAll('.video-player');
|
||
|
||
if (layout === 'single') {
|
||
// 单画面:只显示第一个,其他隐藏
|
||
videoPlayers.forEach((player, index) => {
|
||
player.style.display = index === 0 ? 'flex' : 'none';
|
||
});
|
||
} else if (layout === 'four') {
|
||
// 四画面:显示前4个,其他隐藏
|
||
videoPlayers.forEach((player, index) => {
|
||
player.style.display = index < 4 ? 'flex' : 'none';
|
||
});
|
||
} else {
|
||
// 九画面:显示所有
|
||
videoPlayers.forEach(player => {
|
||
player.style.display = 'flex';
|
||
});
|
||
}
|
||
}
|
||
|
||
// 在第一个可用的视频播放器中播放选中的摄像头
|
||
function playCameraInFirstAvailable(cameraId) {
|
||
const camera = cameras.find(c => c.id === cameraId);
|
||
if (!camera || camera.status === 'offline') return;
|
||
|
||
// 根据布局找到第一个可用的视频播放器
|
||
let targetIndex = 0;
|
||
|
||
if (currentLayout === 'single') {
|
||
targetIndex = 0;
|
||
} else if (currentLayout === 'four') {
|
||
// 在四宫格中寻找第一个不是正在录制重要视频的播放器
|
||
for (let i = 0; i < 4; i++) {
|
||
const currentCamera = cameras[i];
|
||
if (!currentCamera.recording) {
|
||
targetIndex = i;
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
// 在九宫格中寻找第一个不是正在录制重要视频的播放器
|
||
for (let i = 0; i < 9; i++) {
|
||
const currentCamera = cameras[i];
|
||
if (!currentCamera.recording) {
|
||
targetIndex = i;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 更新目标视频播放器
|
||
const videoPlayer = document.querySelectorAll('.video-player')[targetIndex];
|
||
if (!videoPlayer) return;
|
||
|
||
// 更新标题
|
||
const titleText = videoPlayer.querySelector('.title-text');
|
||
titleText.textContent = camera.name;
|
||
|
||
// 更新视频源
|
||
const videoContainer = videoPlayer.querySelector('.video-container');
|
||
videoContainer.innerHTML = `
|
||
<video class="video-feed" muted autoplay loop>
|
||
<source src="${camera.feed}" type="video/mp4">
|
||
您的浏览器不支持视频标签。
|
||
</video>
|
||
${camera.recording ?
|
||
`<div class="playing-indicator">
|
||
<i class="fas fa-circle"></i> 录制中
|
||
</div>` : ''
|
||
}
|
||
`;
|
||
|
||
// 更新录制指示器
|
||
const recordingIndicator = videoPlayer.querySelector('.playing-indicator');
|
||
if (camera.recording && !recordingIndicator) {
|
||
const indicator = document.createElement('div');
|
||
indicator.className = 'playing-indicator';
|
||
indicator.innerHTML = '<i class="fas fa-circle"></i> 录制中';
|
||
videoContainer.appendChild(indicator);
|
||
}
|
||
|
||
// 播放视频
|
||
const videoElement = videoContainer.querySelector('video');
|
||
if (videoElement) {
|
||
videoElement.play().catch(e => console.log("自动播放被阻止:", e));
|
||
|
||
// 更新视频播放器数组
|
||
if (!videoPlayers.includes(videoElement)) {
|
||
videoPlayers.push(videoElement);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 更新在线/离线摄像头计数
|
||
function updateCameraCount() {
|
||
const onlineCount = cameras.filter(c => c.status === 'online').length;
|
||
const offlineCount = cameras.length - onlineCount;
|
||
|
||
document.getElementById('online-count').textContent = onlineCount;
|
||
document.getElementById('offline-count').textContent = offlineCount;
|
||
}
|
||
|
||
// 更新时间显示
|
||
function updateTime() {
|
||
const now = new Date();
|
||
const timeString = now.toLocaleTimeString('zh-CN');
|
||
const dateString = now.toLocaleDateString('zh-CN');
|
||
|
||
document.getElementById('current-time').textContent = timeString;
|
||
document.getElementById('last-update').textContent = `${dateString} ${timeString}`;
|
||
}
|
||
|
||
// 模拟视频播放
|
||
function simulateVideoPlayback() {
|
||
// 播放所有在线的视频
|
||
videoPlayers.forEach(video => {
|
||
video.play().catch(e => console.log("视频播放失败:", e));
|
||
});
|
||
|
||
// 随机切换一些摄像头的状态(模拟实时变化)
|
||
setInterval(() => {
|
||
// 随机选择一个摄像头切换在线状态(10%概率)
|
||
if (Math.random() < 0.1) {
|
||
const randomIndex = Math.floor(Math.random() * cameras.length);
|
||
const camera = cameras[randomIndex];
|
||
|
||
// 切换状态
|
||
if (camera.status === 'online') {
|
||
camera.status = 'offline';
|
||
} else {
|
||
camera.status = 'online';
|
||
}
|
||
|
||
// 更新UI
|
||
updateCameraUI(randomIndex);
|
||
updateCameraCount();
|
||
}
|
||
|
||
// 随机开始/停止录制(5%概率)
|
||
if (Math.random() < 0.05) {
|
||
const randomIndex = Math.floor(Math.random() * cameras.length);
|
||
const camera = cameras[randomIndex];
|
||
|
||
if (camera.status === 'online') {
|
||
camera.recording = !camera.recording;
|
||
updateRecordingUI(randomIndex);
|
||
}
|
||
}
|
||
}, 5000);
|
||
}
|
||
|
||
// 更新摄像头UI状态
|
||
function updateCameraUI(index) {
|
||
const camera = cameras[index];
|
||
const cameraItem = document.querySelector(`.camera-item[data-id="${camera.id}"]`);
|
||
|
||
if (!cameraItem) return;
|
||
|
||
// 更新状态指示灯
|
||
const statusIndicator = cameraItem.querySelector('.camera-status');
|
||
statusIndicator.className = 'camera-status ' + camera.status;
|
||
|
||
// 更新图标
|
||
const cameraIcon = cameraItem.querySelector('.camera-icon i');
|
||
if (camera.status === 'offline') {
|
||
cameraIcon.className = 'fas fa-video-slash';
|
||
cameraItem.querySelector('.camera-icon').style.backgroundColor = '#6e7681';
|
||
} else {
|
||
cameraIcon.className = 'fas fa-video';
|
||
cameraItem.querySelector('.camera-icon').style.backgroundColor = '#238636';
|
||
}
|
||
|
||
// 更新对应的视频播放器
|
||
const videoPlayer = document.querySelectorAll('.video-player')[index];
|
||
if (videoPlayer) {
|
||
const videoContainer = videoPlayer.querySelector('.video-container');
|
||
|
||
if (camera.status === 'online') {
|
||
videoContainer.innerHTML = `
|
||
<video class="video-feed" muted autoplay loop>
|
||
<source src="${camera.feed}" type="video/mp4">
|
||
您的浏览器不支持视频标签。
|
||
</video>
|
||
${camera.recording ?
|
||
`<div class="playing-indicator">
|
||
<i class="fas fa-circle"></i> 录制中
|
||
</div>` : ''
|
||
}
|
||
`;
|
||
|
||
// 播放视频
|
||
const videoElement = videoContainer.querySelector('video');
|
||
if (videoElement) {
|
||
videoElement.play().catch(e => console.log("视频播放失败:", e));
|
||
|
||
// 添加到视频播放器数组
|
||
if (!videoPlayers.includes(videoElement)) {
|
||
videoPlayers.push(videoElement);
|
||
}
|
||
}
|
||
} else {
|
||
videoContainer.innerHTML = `
|
||
<div class="no-video">
|
||
<i class="fas fa-wifi-slash"></i>
|
||
<p>摄像头离线</p>
|
||
</div>
|
||
`;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 更新录制UI状态
|
||
function updateRecordingUI(index) {
|
||
const camera = cameras[index];
|
||
const videoPlayer = document.querySelectorAll('.video-player')[index];
|
||
|
||
if (!videoPlayer) return;
|
||
|
||
// 更新录制指示器
|
||
const videoContainer = videoPlayer.querySelector('.video-container');
|
||
let recordingIndicator = videoContainer.querySelector('.playing-indicator');
|
||
|
||
if (camera.recording) {
|
||
if (!recordingIndicator) {
|
||
recordingIndicator = document.createElement('div');
|
||
recordingIndicator.className = 'playing-indicator';
|
||
recordingIndicator.innerHTML = '<i class="fas fa-circle"></i> 录制中';
|
||
videoContainer.appendChild(recordingIndicator);
|
||
}
|
||
|
||
// 更新摄像头列表中的状态指示灯
|
||
const cameraItem = document.querySelector(`.camera-item[data-id="${camera.id}"]`);
|
||
if (cameraItem) {
|
||
const statusIndicator = cameraItem.querySelector('.camera-status');
|
||
statusIndicator.classList.add('recording');
|
||
}
|
||
} else {
|
||
if (recordingIndicator) {
|
||
recordingIndicator.remove();
|
||
}
|
||
|
||
// 更新摄像头列表中的状态指示灯
|
||
const cameraItem = document.querySelector(`.camera-item[data-id="${camera.id}"]`);
|
||
if (cameraItem) {
|
||
const statusIndicator = cameraItem.querySelector('.camera-status');
|
||
statusIndicator.classList.remove('recording');
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
</body>
|
||
</html> |