Files
operating-room-monitor-server/clients/demo-client/index.html

217 lines
10 KiB
HTML
Raw Normal View History

2026-05-21 15:48:03 +08:00
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>手术监控 · 联调台</title>
<link rel="stylesheet" href="styles.css" />
2026-05-21 15:48:03 +08:00
</head>
<body>
<div class="app">
<header class="header">
<h1>手术监控 · 联调台</h1>
<p class="subtitle">三条链路:真摄像头实时、模拟实时、离线精确处理</p>
<div class="top-grid">
<div>
<label for="base-url">API 地址</label>
<input id="base-url" type="url" value="http://127.0.0.1:38080" />
</div>
<div>
<label for="surgery-id">手术号6 位)</label>
<input id="surgery-id" type="text" inputmode="numeric" maxlength="6" value="123456" />
2026-05-21 15:48:03 +08:00
</div>
<div>
<button type="button" class="secondary" id="btn-refresh-status">刷新状态</button>
2026-05-21 15:48:03 +08:00
</div>
</div>
2026-05-21 15:48:03 +08:00
<div class="status-bar">
<span class="pill" id="pill-health">检测中…</span>
<span class="pill" id="pill-demo-modes"></span>
<span class="pill" id="pill-mode">链路 1 · 真摄像头</span>
</div>
<div id="banner" class="banner"></div>
<div id="voice-callout" class="voice-callout hidden">
开录成功后,请启动
<a href="../voice-confirmation/">语音确认终端</a>(默认 :8080终端 ID 须与站点配置
<code>voice_or_room_bindings</code> 匹配。
</div>
</header>
<div class="layout-main">
<div class="main-col">
<section class="card">
<h2>选择运行模式</h2>
<div class="mode-cards">
<button type="button" class="mode-card active" data-mode="live-rtsp">
<div class="title">链路 1 · 真摄像头</div>
<div class="desc">真实 RTSP · 实时算法 · 语音播报 · 需结束手术</div>
</button>
<button type="button" class="mode-card" data-mode="live-simulated">
<div class="title">链路 2 · 模拟实时</div>
<div class="desc">上传视频假流 · 实时算法 · 语音播报 · 需结束手术</div>
</button>
<button type="button" class="mode-card" data-mode="offline-batch">
<div class="title">链路 3 · 离线精确</div>
<div class="desc">整段 MP4 批处理 · 无语音 · 无需结束 · 可选标注视频</div>
</button>
2026-05-21 15:48:03 +08:00
</div>
</section>
<section class="card">
<h2>耗材候选</h2>
<div class="chip-toolbar">
<input id="chip-search" class="chip-search" type="text" placeholder="搜索标签…" />
<button type="button" class="secondary" id="btn-chips-all">全选</button>
<button type="button" class="secondary" id="btn-chips-clear">清空</button>
</div>
<div id="chips" class="chips"></div>
<p id="labels-meta" class="labels-meta">加载中…</p>
<details class="advanced">
<summary>高级JSON 编辑(与上方标签同步)</summary>
<div class="advanced-body">
<textarea id="candidate-consumables-json" rows="5" spellcheck="false"></textarea>
2026-05-21 15:48:03 +08:00
</div>
</details>
</section>
<section class="card mode-panel active" data-mode="live-rtsp">
<h2>真摄像头配置</h2>
<label for="camera-ids">摄像头 ID逗号分隔</label>
<input id="camera-ids" type="text" value="or-cam-03" placeholder="or-cam-03, or-cam-02" />
<p class="labels-meta" style="margin-top:8px">使用站点 JSON 中的真实 RTSP 地址。</p>
</section>
<section class="card mode-panel" data-mode="live-simulated">
<h2>模拟实时 · 上传视频</h2>
<label for="debug-stream-count">模拟路数</label>
<select id="debug-stream-count" style="max-width:8rem;margin-bottom:12px">
<option value="1" selected>1 路</option>
<option value="2">2 路</option>
<option value="3">3 路</option>
<option value="4">4 路</option>
</select>
<p class="labels-meta">每路须选择视频文件,将合成假 RTSP 后开录。</p>
<div class="stream-grid">
<div class="stream-slot" id="sim-stream-1">
<h4>路 1</h4>
<div class="upload-zone" id="sim-zone-1">
<div class="icon"></div>
<div id="sim-fname-1">点击或拖放视频</div>
<input type="file" id="sim-vfile-1" hidden accept="video/*" />
2026-05-21 15:48:03 +08:00
</div>
<div class="stream-actions">
<button type="button" class="secondary" id="sim-pick-1">选择文件</button>
<button type="button" class="secondary" id="sim-webcam-1">摄像头</button>
2026-05-21 15:48:03 +08:00
</div>
<div class="stream-hint" id="sim-hint-1"></div>
2026-05-21 15:48:03 +08:00
</div>
<div class="stream-slot hidden" id="sim-stream-2">
<h4>路 2</h4>
<div class="upload-zone" id="sim-zone-2">
<div class="icon"></div>
<div id="sim-fname-2">点击或拖放视频</div>
<input type="file" id="sim-vfile-2" hidden accept="video/*" />
2026-05-21 15:48:03 +08:00
</div>
<div class="stream-actions">
<button type="button" class="secondary" id="sim-pick-2">选择文件</button>
<button type="button" class="secondary" id="sim-webcam-2">摄像头</button>
2026-05-21 15:48:03 +08:00
</div>
<div class="stream-hint" id="sim-hint-2"></div>
2026-05-21 15:48:03 +08:00
</div>
<div class="stream-slot hidden" id="sim-stream-3">
<h4>路 3</h4>
<div class="upload-zone" id="sim-zone-3">
<div class="icon"></div>
<div id="sim-fname-3">点击或拖放视频</div>
<input type="file" id="sim-vfile-3" hidden accept="video/*" />
</div>
<div class="stream-actions">
<button type="button" class="secondary" id="sim-pick-3">选择文件</button>
<button type="button" class="secondary" id="sim-webcam-3">摄像头</button>
</div>
<div class="stream-hint" id="sim-hint-3"></div>
2026-05-21 15:48:03 +08:00
</div>
<div class="stream-slot hidden" id="sim-stream-4">
<h4>路 4</h4>
<div class="upload-zone" id="sim-zone-4">
<div class="icon"></div>
<div id="sim-fname-4">点击或拖放视频</div>
<input type="file" id="sim-vfile-4" hidden accept="video/*" />
2026-05-21 15:48:03 +08:00
</div>
<div class="stream-actions">
<button type="button" class="secondary" id="sim-pick-4">选择文件</button>
<button type="button" class="secondary" id="sim-webcam-4">摄像头</button>
2026-05-21 15:48:03 +08:00
</div>
<div class="stream-hint" id="sim-hint-4"></div>
2026-05-21 15:48:03 +08:00
</div>
</div>
<details class="advanced">
<summary>高级:每路 RTSP 路径与 camera_id</summary>
<div class="advanced-body">
<div class="stream-grid">
<div><label>路 1 RTSP_PATH</label><input id="adv-rpath-1" type="text" value="demo1" /></div>
<div><label>路 1 camera_id</label><input id="adv-cam-1" type="text" value="or-cam-03" /></div>
<div><label>路 2 RTSP_PATH</label><input id="adv-rpath-2" type="text" value="demo2" /></div>
<div><label>路 2 camera_id</label><input id="adv-cam-2" type="text" value="or-cam-02" /></div>
<div><label>路 3 RTSP_PATH</label><input id="adv-rpath-3" type="text" value="demo3" /></div>
<div><label>路 3 camera_id</label><input id="adv-cam-3" type="text" value="or-cam-04" /></div>
<div><label>路 4 RTSP_PATH</label><input id="adv-rpath-4" type="text" value="demo4" /></div>
<div><label>路 4 camera_id</label><input id="adv-cam-4" type="text" value="or-cam-01" /></div>
2026-05-21 15:48:03 +08:00
</div>
</div>
</details>
</section>
<section class="card mode-panel" data-mode="offline-batch">
<h2>离线精确 · 上传 MP4</h2>
<p class="labels-meta">不启动实时会话,处理完成后直接查结果。</p>
<div class="upload-zone" id="offline-zone" style="margin-top:12px">
<div class="icon">MP4</div>
<div id="offline-fname">点击或拖放 MP4 到此处</div>
<input type="file" id="offline-vfile" hidden accept="video/mp4,video/*" />
2026-05-21 15:48:03 +08:00
</div>
<p class="stream-hint" id="offline-hint"></p>
<button type="button" class="secondary" id="offline-pick" style="margin-top:10px">选择文件</button>
<label class="checkbox-row">
<input type="checkbox" id="offline-batch-include-vis" />
<span>生成标注视频24 小时内可预览)</span>
</label>
</section>
<section class="card">
<h2>操作</h2>
<div class="steps">
<button type="button" class="primary lg" id="btn-start">开始手术</button>
<button type="button" class="warn" id="btn-end">结束手术</button>
<button type="button" class="secondary" id="btn-result">查询结果</button>
2026-05-21 15:48:03 +08:00
</div>
</section>
<section class="card">
<h2>结果</h2>
<div id="video-batch-vis" class="vis-block hidden">
<p id="video-batch-doctor-info" class="labels-meta"></p>
<video id="video-batch-vis-player" controls playsinline></video>
<p id="video-batch-vis-hint" class="stream-hint"></p>
2026-05-21 15:48:03 +08:00
</div>
<div id="result-render" class="result-area"></div>
</section>
2026-05-21 15:48:03 +08:00
</div>
<aside class="dev-log">
<details>
<summary>开发者日志</summary>
<div style="padding:8px 10px 0">
<button type="button" class="secondary" id="btn-clear-log" style="width:100%;margin-bottom:8px">清空日志</button>
</div>
<div id="log-scroll" class="log-scroll"></div>
</details>
</aside>
</div>
</div>
<script src="app.js"></script>
2026-05-21 15:48:03 +08:00
</body>
</html>