Files
SupervisorAI/web_page/index.html

415 lines
12 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>指监智能场景识别系统</title>
<style>
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
background: transparent;
color: #e5e7eb;
overflow: hidden;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
display: flex;
flex-direction: column;
}
.app-container {
width: 100vw;
height: 100vh;
background: #111827;
overflow: hidden;
display: flex;
flex-direction: column;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.log-panel {
height: 50%;
background: #020617;
display: flex;
flex-direction: column;
flex-shrink: 0;
}
.log-header {
padding: 8px 12px;
background: #0f172a;
border-bottom: 1px solid #1f2937;
font-size: 14px;
color: #9ca3af;
font-weight: 500;
flex-shrink: 0;
}
.log-content {
flex: 1;
overflow-y: auto;
padding: 8px;
font-family: 'Courier New', monospace;
font-size: 12px;
min-height: 0;
}
.log-entry {
margin-bottom: 6px;
padding: 4px 6px;
word-wrap: break-word;
}
.log-info {
color: #60a5fa;
background: rgba(96, 165, 250, 0.1);
}
.log-success {
color: #34d399;
background: rgba(52, 211, 153, 0.1);
}
.log-warning {
color: #fbbf24;
background: rgba(251, 191, 36, 0.1);
}
.log-error {
color: #f87171;
background: rgba(248, 113, 113, 0.1);
}
main {
flex: 1;
display: flex;
min-height: 0;
position: relative;
z-index: 1;
overflow: hidden;
}
.left-panel {
width: 320px;
border-right: 1px solid #1f2937;
background: #020617;
display: flex;
flex-direction: column;
height: 100vh;
max-height: 100vh;
overflow: hidden;
}
.sidebar {
height: 50%;
display: flex;
flex-direction: column;
border-bottom: 1px solid #1f2937;
flex-shrink: 0;
}
.sidebar-header {
padding: 8px 12px;
border-bottom: 1px solid #1f2937;
font-size: 14px;
color: #9ca3af;
flex-shrink: 0;
}
.message-list {
flex: 1;
overflow-y: auto;
min-height: 0;
}
.message-item {
padding: 8px 12px;
border-bottom: 1px solid #111827;
cursor: pointer;
}
.message-item:hover {
background: #111827;
}
.message-item.active {
background: #1f2937;
}
.message-title {
font-size: 14px;
margin-bottom: 4px;
}
.message-meta {
font-size: 12px;
color: #9ca3af;
}
.content {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
background: #000;
position: relative;
min-width: 0;
}
#liveImage {
max-width: 100%;
max-height: 100%;
background: #000;
}
.status-bar {
position: absolute;
left: 12px;
bottom: 12px;
padding: 4px 8px;
background: rgba(15, 23, 42, 0.8);
font-size: 12px;
color: #e5e7eb;
}
.action-tag {
display: inline-block;
padding: 2px 6px;
font-size: 11px;
margin-right: 4px;
font-weight: 500;
}
.action-action {
background: rgba(239, 68, 68, 0.2);
color: #f87171;
border: 1px solid rgba(239, 68, 68, 0.3);
}
.action-face {
background: rgba(59, 130, 246, 0.2);
color: #60a5fa;
border: 1px solid rgba(59, 130, 246, 0.3);
}
.action-takeout {
background: rgba(139, 92, 246, 0.2);
color: #a78bfa;
border: 1px solid rgba(139, 92, 246, 0.3);
}
</style>
</head>
<body>
<div class="app-container">
<main>
<aside class="left-panel">
<div class="sidebar">
<div class="sidebar-header">异常消息</div>
<div id="messageList" class="message-list"></div>
</div>
<div class="log-panel">
<div class="log-header">系统日志</div>
<div id="logContent" class="log-content"></div>
</div>
</aside>
<section class="content">
<img id="liveImage" alt="live" style="display: none;" />
<div id="status" class="status-bar">连接中...</div>
<div id="waitingPlaceholder" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: #6b7280; font-size: 16px; text-align: center;">
<div>正在连接 WebSocket...</div>
</div>
</section>
</main>
</div>
<script>
const WS_PORT = 8765;
const WS_HOST = 'localhost';
const liveImage = document.getElementById('liveImage');
const statusBar = document.getElementById('status');
const messageListEl = document.getElementById('messageList');
const logContent = document.getElementById('logContent');
let alerts = [];
let ws = null;
let wsConnected = false;
let currentDetectedActions = [];
function setMode(newMode) {
// 模式切换功能已禁用
}
function renderMessages() {
messageListEl.innerHTML = '';
for (const msg of alerts.slice().reverse()) {
const div = document.createElement('div');
div.className = 'message-item';
const title = document.createElement('div');
title.className = 'message-title';
if (msg.msg_type === 'take_out') {
// 处理take_out类型的消息
title.textContent = `人员带出: `;
const tag = document.createElement('span');
tag.className = 'action-tag action-takeout';
tag.textContent = msg.person_name;
title.appendChild(tag);
const countTag = document.createElement('span');
countTag.className = 'action-tag action-face';
countTag.textContent = `${msg.historical_alert_count}`;
title.appendChild(countTag);
} else {
// 处理原有的异常检测消息
title.textContent = `检测到异常: `;
msg.result_type.forEach(action => {
const tag = document.createElement('span');
tag.className = 'action-tag';
if (action === 'face') {
tag.classList.add('action-face');
tag.textContent = '检测到黑名单';
} else {
tag.classList.add('action-action');
const actionMap = {
'Slap': '扇巴掌',
'LeavingPost': '离岗',
'Collision': '撞击',
'Push': '推搡',
'Lyingdown': '倒下',
'Hanging': '自缢'
};
tag.textContent = actionMap[action] || action;
}
title.appendChild(tag);
});
}
const meta = document.createElement('div');
meta.className = 'message-meta';
const date = new Date(msg.timestamp * 1000);
meta.textContent = date.toLocaleString();
div.appendChild(title);
div.appendChild(meta);
messageListEl.appendChild(div);
}
}
function addLog(message, type = 'info') {
const timestamp = new Date().toLocaleTimeString();
const logEntry = document.createElement('div');
logEntry.className = `log-entry log-${type}`;
logEntry.textContent = `[${timestamp}] ${message}`;
logContent.appendChild(logEntry);
logContent.scrollTop = logContent.scrollHeight;
}
function connectWebSocket() {
const wsUrl = `ws://${WS_HOST}:${WS_PORT}`;
statusBar.textContent = '连接中...';
addLog(`正在连接WebSocket: ${wsUrl}`, 'info');
ws = new WebSocket(wsUrl);
ws.onopen = () => {
wsConnected = true;
statusBar.textContent = '直播中';
addLog('WebSocket连接成功', 'success');
const placeholder = document.getElementById('waitingPlaceholder');
if (placeholder) {
placeholder.remove();
}
liveImage.style.display = 'block';
};
ws.onclose = () => {
wsConnected = false;
statusBar.textContent = '连接断开,重试中...';
addLog('WebSocket连接断开2秒后重试', 'warning');
setTimeout(() => {
connectWebSocket();
}, 2000);
};
ws.onerror = (err) => {
console.error('WebSocket error', err);
addLog('WebSocket连接错误', 'error');
};
ws.onmessage = (event) => {
try {
// console.log('收到WebSocket消息:', event.data);
const msg = JSON.parse(event.data);
// console.log('解析后的消息对象:', msg);
if (msg.msg_type === 'frame') {
// console.log('处理frame类型消息');
if (msg.image_base64) {
liveImage.src = `data:image/jpeg;base64,${msg.image_base64}`;
}
currentDetectedActions = msg.result_type || [];
// console.log('检测到的动作:', currentDetectedActions);
if (currentDetectedActions.length > 0) {
const actionText = currentDetectedActions.join(', ');
statusBar.textContent = `直播中 | 检测到: ${actionText}`;
} else {
statusBar.textContent = '直播中';
}
if (msg.result_type && msg.result_type.length > 0) {
const alertMsg = {
camera_id: msg.camera_id,
timestamp: msg.timestamp,
result_type: msg.result_type
};
// const now = msg.timestamp;
// const timeThreshold = now - 10;
// const existingAlert = alerts.find(alert =>
// JSON.stringify(alert.result_type) === JSON.stringify(alertMsg.result_type) &&
// alert.timestamp > timeThreshold
// );
// if (!existingAlert) {
alerts.push(alertMsg);
// console.log('添加新的异常告警:', alertMsg);
// console.log('当前alerts数组长度:', alerts.length);
renderMessages();
// console.log('渲染消息列表完成');
const alertTypes = alertMsg.result_type.join(', ');
addLog(`检测到异常: ${alertTypes}`, 'warning');
// }
}
} else if (msg.msg_type === 'take_out') {
// console.log('处理take_out类型消息');
// 处理take_out类型的消息
const takeOutMsg = {
msg_type: 'take_out',
person_name: msg.person_name,
historical_alert_count: msg.historical_alert_count || 0,
timestamp: Math.floor(Date.now() / 1000)
};
// console.log('创建take_out消息对象:', takeOutMsg);
alerts.push(takeOutMsg);
// console.log('当前alerts数组长度:', alerts.length);
renderMessages();
// console.log('渲染消息列表完成');
addLog(`人员带出: ${takeOutMsg.person_name} (第${takeOutMsg.historical_alert_count}次)`, 'warning');
} else {
// console.log('未知的消息类型:', msg.msg_type);
}
} catch (e) {
console.error('解析WebSocket消息错误:', e);
console.error('原始消息数据:', event.data);
}
};
}
// 初始化
addLog('AI Monitor 系统启动', 'info');
addLog('界面初始化完成', 'success');
setTimeout(() => {
connectWebSocket();
}, 500);
</script>
</body>
</html>