const form = document.querySelector("#segmentForm"); const methodSelect = document.querySelector("#method"); const sensitivity = document.querySelector("#sensitivity"); const sensitivityValue = document.querySelector("#sensitivityValue"); const fileInput = document.querySelector("#file"); const fileName = document.querySelector("#fileName"); const resultGrid = document.querySelector("#resultGrid"); const emptyState = document.querySelector("#emptyState"); const videoLink = document.querySelector("#videoLink"); const health = document.querySelector("#health"); const template = document.querySelector("#resultCardTemplate"); const methodLabels = new Map(); function setBusy(isBusy) { const button = form.querySelector("button"); button.disabled = isBusy; button.querySelector("span").textContent = isBusy ? "分割中" : "开始分割"; } async function loadHealth() { try { const response = await fetch("/api/health"); if (!response.ok) throw new Error("bad health"); const data = await response.json(); health.textContent = `${data.service} ${data.version}`; health.classList.add("ok"); } catch { health.textContent = "服务不可用"; health.classList.add("bad"); } } async function loadMethods() { const response = await fetch("/api/methods"); const data = await response.json(); methodSelect.innerHTML = ""; Object.entries(data.methods).forEach(([key, value]) => { methodLabels.set(key, value.label); const option = document.createElement("option"); option.value = key; option.textContent = value.label; if (key === "fusion") option.selected = true; methodSelect.appendChild(option); }); } function renderResults(data) { resultGrid.innerHTML = ""; emptyState.hidden = true; videoLink.hidden = !data.video_url; if (data.video_url) { videoLink.href = data.video_url; videoLink.setAttribute("download", ""); } data.frames.forEach((frame) => { const node = template.content.firstElementChild.cloneNode(true); node.querySelector(".method").textContent = methodLabels.get(frame.method) || frame.method; node.querySelector(".frame-index").textContent = `帧 ${frame.frame_index}`; node.querySelector(".overlay").src = frame.overlay_url; node.querySelector(".mask").src = frame.mask_url; node.querySelector(".coverage").textContent = `${(frame.metrics.coverage * 100).toFixed(3)}%`; node.querySelector(".skeleton").textContent = frame.metrics.skeleton_length; node.querySelector(".components").textContent = frame.metrics.components; resultGrid.appendChild(node); }); } sensitivity.addEventListener("input", () => { sensitivityValue.textContent = Number(sensitivity.value).toFixed(2); }); fileInput.addEventListener("change", () => { const file = fileInput.files[0]; fileName.textContent = file ? file.name : "支持 mp4、avi、png、jpg、tiff"; }); form.addEventListener("submit", async (event) => { event.preventDefault(); setBusy(true); emptyState.hidden = false; emptyState.textContent = "正在抽帧和分割,请稍候。"; resultGrid.innerHTML = ""; videoLink.hidden = true; try { const payload = new FormData(form); const response = await fetch("/api/segment", { method: "POST", body: payload, }); const data = await response.json(); if (!response.ok) { throw new Error(data.detail || "分割失败"); } renderResults(data); } catch (error) { emptyState.hidden = false; emptyState.textContent = error.message; } finally { setBusy(false); } }); loadHealth(); loadMethods();