feat: 添加双屏控制功能,支持在第二块屏幕上全屏展示内容;更新相关 UI 和逻辑以适应新功能

This commit is contained in:
Pine
2026-08-19 13:13:54 +08:00
parent 1e63da95ec
commit 5ed396aea4
8 changed files with 123 additions and 16 deletions
+2 -4
View File
@@ -531,14 +531,12 @@ class VisionEventBody(BaseModel):
@router.post("/api/vision/event")
async def vision_event(body: VisionEventBody):
"""前端摄像头识别状态/触发上报;后端记录详细日志,triggered 时广播 alert 到全屏。"""
"""前端摄像头识别状态/触发上报;后端记录详细日志。
主动问候已关闭,故不再对 triggered 广播 alert(避免右上角弹「已主动问候」误导提示)。"""
log.info(
"vision event=%s faces=%d dwell_ms=%d detail=%s",
body.event, body.faces, body.dwell_ms, body.detail,
)
if body.event == "triggered":
hub.publish_command("alert", {"text": "有访客正对屏幕,语音助手已主动问候", "faces": body.faces})
log.info("vision triggered -> alert 已广播(faces=%d)", body.faces)
return {"ok": True}
+7
View File
@@ -1147,6 +1147,13 @@ button { font-family: "Poppins", "PingFang SC", sans-serif; cursor: pointer; tra
background: rgba(0, 189, 125, 0.14);
transform: translateY(-1px);
}
.screen-target.active {
background: #1D5DCE;
border-color: #1D5DCE;
color: #fff;
box-shadow: 0 4px 12px rgba(29, 93, 206, 0.3);
}
.screen-target.active:hover { background: #174fb0; }
.media-url {
display: flex;
align-items: center;
+15 -1
View File
@@ -10,6 +10,9 @@
var fullscreen = !!init.fullscreen;
var autostart = !!init.autostart;
/* 目标屏(双屏独立控制):both | main | secondary */
var screenTarget = 'both';
/* ---------- Toast ---------- */
var toastContainer = document.getElementById('toastContainer');
function toast(msg, type) {
@@ -107,10 +110,19 @@
});
document.addEventListener('click', function (e) {
var btn = e.target.closest ? e.target.closest('[data-nav], [data-mqtt], [data-control], [data-action], .copy-btn, [data-preview], #mqttAiSend, #mqttAlertSend') : null;
var btn = e.target.closest ? e.target.closest('[data-nav], [data-mqtt], [data-control], [data-action], .copy-btn, [data-preview], .screen-target, #mqttAiSend, #mqttAlertSend') : null;
if (!btn) return;
e.stopPropagation();
// 目标屏选择(双屏独立控制)
if (btn.classList.contains('screen-target')) {
screenTarget = btn.getAttribute('data-screen') || 'both';
var btns = document.querySelectorAll('.screen-target');
for (var i = 0; i < btns.length; i++) btns[i].classList.toggle('active', btns[i] === btn);
toast('目标屏:' + ({ both: '全部(镜像)', main: '仅主屏', secondary: '仅副屏' }[screenTarget] || screenTarget));
return;
}
// 预览
if (btn.hasAttribute('data-preview')) { openPreview(btn.closest('.media-card')); return; }
@@ -129,6 +141,8 @@
if (btn.hasAttribute('data-mqtt')) {
var cmd = null;
try { cmd = JSON.parse(btn.getAttribute('data-mqtt')); } catch (err) { toast('指令 JSON 错误', 'error'); return; }
// 双屏独立控制:除「双屏开关」外,注入目标屏(main/secondary/both)
if (cmd.action !== 'dual_screen') cmd.screen = screenTarget;
post('/api/display/command', cmd, function (d) {
toast(d && d.ok !== false ? '指令已发送:' + (cmd.action || '') : '发送失败(MQTT 未连接?)',
d && d.ok !== false ? 'ok' : 'error');
+23
View File
@@ -105,6 +105,19 @@
</div>
<!-- 页面切换 -->
<div class="card">
<div class="card-header">
<div class="card-icon"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="#1D5DCE" stroke-width="1.6"><rect x="3" y="3" width="8" height="8" rx="1.5"/><rect x="13" y="3" width="8" height="8" rx="1.5"/><rect x="3" y="13" width="8" height="8" rx="1.5"/><rect x="13" y="13" width="8" height="8" rx="1.5"/></svg></div>
<h3>目标屏(双屏独立控制)</h3>
</div>
<div class="page-switch-grid">
<button class="page-switch-btn screen-target" data-screen="both">全部(镜像)</button>
<button class="page-switch-btn screen-target" data-screen="main">仅主屏</button>
<button class="page-switch-btn screen-target" data-screen="secondary">仅副屏</button>
</div>
<div class="mqtt-hint">选择后,下方命令只发送到对应屏幕;「全部」两边同步。</div>
</div>
<div class="card">
<div class="card-header">
<div class="card-icon"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="#1D5DCE" stroke-width="1.6"><rect x="3" y="3" width="8" height="8" rx="1.5"/><rect x="13" y="3" width="8" height="8" rx="1.5"/><rect x="3" y="13" width="8" height="8" rx="1.5"/><rect x="13" y="13" width="8" height="8" rx="1.5"/></svg></div>
@@ -190,6 +203,16 @@
</div>
</div>
<!-- 双屏 -->
<div class="mqtt-group">
<div class="mqtt-group-title">双屏控制</div>
<div class="mqtt-row">
<button class="page-switch-btn" data-mqtt='{"action":"dual_screen","params":{"on":true}}'>启动双屏</button>
<button class="page-switch-btn" data-mqtt='{"action":"dual_screen","params":{"on":false}}'>关闭双屏</button>
</div>
<div class="mqtt-hint">在电脑另一块屏幕全屏展示(需展播机连接显示器)</div>
</div>
<!-- 媒体控制 -->
<div class="mqtt-group">
<div class="mqtt-group-title">媒体轮播</div>