various fix

This commit is contained in:
Kevin
2026-05-22 10:45:47 +08:00
parent 7600c5da2f
commit 87b6a7b804
8 changed files with 178 additions and 25 deletions

View File

@@ -12,6 +12,7 @@
let allLabels = [];
let selectedConsumables = new Set();
let lastVideoBatchDoctorDisplay = "";
let videoVisToken = 0;
const webcamSlotState = {};
const baseUrl = () => $("base-url").value.trim().replace(/\/+$/, "");
@@ -299,6 +300,7 @@
: mode === "live-simulated"
? "链路 2 · 模拟实时"
: "链路 3 · 离线精确";
if (mode !== "offline-batch") hideVideoBatchVisualization();
refreshRecordingModesStatus();
}
@@ -358,39 +360,119 @@
}
}
function hideVideoBatchVisualization() {
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function cancelVideoBatchVisualization() {
videoVisToken += 1;
const wrap = $("video-batch-vis");
const player = $("video-batch-vis-player");
const hint = $("video-batch-vis-hint");
if (wrap) wrap.classList.add("hidden");
if (player) {
player.onerror = null;
player.onloadeddata = null;
player.removeAttribute("src");
player.load();
}
if (hint) hint.textContent = "";
}
function showVideoBatchVisualization(sid, urlPath, doctorDisplay) {
function hideVideoBatchVisualization() {
cancelVideoBatchVisualization();
}
function revealVideoBatchVisualization() {
const wrap = $("video-batch-vis");
if (wrap) wrap.classList.remove("hidden");
}
function loadVideoWhenReady(player, src, token, timeoutMs = 15000) {
return new Promise((resolve) => {
if (token !== videoVisToken) {
resolve(false);
return;
}
let settled = false;
const finish = (ok) => {
if (settled) return;
settled = true;
clearTimeout(timer);
player.onerror = null;
player.onloadeddata = null;
if (!ok) {
player.removeAttribute("src");
player.load();
}
resolve(ok);
};
const timer = setTimeout(() => finish(false), timeoutMs);
player.onerror = () => finish(false);
player.onloadeddata = () => finish(true);
player.src = src;
player.load();
});
}
async function waitForVideoBatchVisualization(sid, urlPath, doctorDisplay) {
const wrap = $("video-batch-vis");
const player = $("video-batch-vis-player");
const hint = $("video-batch-vis-hint");
if (!wrap || !player) return;
const token = ++videoVisToken;
const path = urlPath || `/internal/demo/offline-batch/${sid}/visualization`;
const src = baseUrl() + path + "?t=" + Date.now();
wrap.classList.remove("hidden");
const docEl = $("video-batch-doctor-info");
const text = (doctorDisplay || lastVideoBatchDoctorDisplay || "").trim();
if (docEl) {
docEl.textContent = text ? "识别医生:" + text : "";
docEl.style.display = text ? "block" : "none";
const base = baseUrl() + path;
const pollIntervalMs = 3000;
const maxAttempts = 120;
wrap.classList.add("hidden");
showVideoBatchDoctorInfo(doctorDisplay || lastVideoBatchDoctorDisplay);
if (hint) hint.textContent = "";
for (let attempt = 0; attempt < maxAttempts; attempt++) {
if (token !== videoVisToken) return;
const src = base + "?t=" + Date.now();
const loaded = await loadVideoWhenReady(player, src, token);
if (token !== videoVisToken) return;
if (loaded) {
revealVideoBatchVisualization();
if (hint) hint.textContent = "标注视频已加载";
player.onerror = () => {
if (hint) {
hint.textContent = "视频加载失败,请稍后重试或新标签页打开链接。";
}
};
showBanner("标注视频已就绪", "ok");
return;
}
if (attempt === 0) {
showBanner("标注视频生成中,就绪后自动展示…", "info");
}
await sleep(pollIntervalMs);
}
player.onerror = () => {
if (hint) hint.textContent = "视频加载失败,请稍后重试或新标签页打开链接。";
};
player.onloadeddata = () => {
if (hint) hint.textContent = "标注视频已加载";
};
player.src = src;
player.load();
if (hint) hint.textContent = "正在加载…";
if (token === videoVisToken) {
showBanner("标注视频尚未就绪,请稍后点击「查询结果」重试", "warn");
}
}
function showVideoBatchDoctorInfo(displayText) {
const el = $("video-batch-doctor-info");
if (!el) return;
const text = (displayText || "").trim();
if (!text) {
el.textContent = "";
el.style.display = "none";
return;
}
el.textContent = "识别医生:" + text;
el.style.display = "block";
}
function showVideoBatchVisualization(sid, urlPath, doctorDisplay) {
void waitForVideoBatchVisualization(sid, urlPath, doctorDisplay);
}
function getDebugStreamCount() {
@@ -563,9 +645,12 @@
lastVideoBatchDoctorDisplay = body?.doctor_display || "";
if ($("offline-batch-include-vis")?.checked && body?.visualization_url) {
showVideoBatchVisualization(sid, body.visualization_url, lastVideoBatchDoctorDisplay);
} else hideVideoBatchVisualization();
} else {
hideVideoBatchVisualization();
showVideoBatchDoctorInfo(lastVideoBatchDoctorDisplay);
}
showBanner("离线处理完成,正在查询结果…", "ok");
await handleResult();
await handleResult({ skipVisualization: true });
return;
}
@@ -647,10 +732,14 @@
}
}
async function handleResult() {
async function handleResult(options = {}) {
const sid = ensureSurgeryId();
if (!sid) return;
if (getRunMode() === "offline-batch" && $("offline-batch-include-vis")?.checked) {
if (
!options.skipVisualization &&
getRunMode() === "offline-batch" &&
$("offline-batch-include-vis")?.checked
) {
showVideoBatchVisualization(sid, null);
} else if (getRunMode() !== "offline-batch") {
hideVideoBatchVisualization();
@@ -664,6 +753,9 @@
return;
}
const { details = [], summary = [] } = body;
if (getRunMode() === "offline-batch") {
showVideoBatchDoctorInfo(details[0]?.doctor_id || lastVideoBatchDoctorDisplay);
}
const renderTable = (title, rows, cols) => {
const h = document.createElement("h3");
h.textContent = title;
@@ -686,11 +778,13 @@
};
renderTable("消耗明细", details, [
{ key: "timestamp", label: "时间" },
{ key: "item_id", label: "耗材 ID" },
{ key: "item_name", label: "耗材" },
{ key: "qty", label: "数量" },
{ key: "doctor_id", label: "医生" },
{ key: "doctor_id", label: "医生(姓名+ID" },
]);
renderTable("汇总", summary, [
{ key: "item_id", label: "耗材 ID" },
{ key: "item_name", label: "耗材" },
{ key: "total_quantity", label: "合计" },
]);

View File

@@ -191,8 +191,8 @@
<section class="card">
<h2>结果</h2>
<p id="video-batch-doctor-info" class="labels-meta"></p>
<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>
</div>