157 lines
5.6 KiB
JavaScript
157 lines
5.6 KiB
JavaScript
(() => {
|
|
const $ = (id) => document.getElementById(id);
|
|
const form = $("measure-test");
|
|
const { endpoint, totalHeightLabel } = form.dataset;
|
|
const fileInput = $("imageFile");
|
|
const submitButton = $("submitBtn");
|
|
const clearButton = $("clearBtn");
|
|
const copyButton = $("copyBtn");
|
|
const metrics = $("metricsBar");
|
|
const results = $("resultsArea");
|
|
const jsonContent = $("jsonContent");
|
|
const placeholder = $("imagePlaceholder");
|
|
const imageStack = $("imageStack");
|
|
const baseImage = $("baseImage");
|
|
const annotationImage = $("annotationImage");
|
|
let originalImageUrl;
|
|
|
|
function setStatus(text, type = "") {
|
|
const status = $("statusBar");
|
|
status.textContent = text;
|
|
status.className = type ? "status " + type : "status";
|
|
}
|
|
|
|
function resetResults() {
|
|
results.classList.add("hidden");
|
|
metrics.classList.add("hidden");
|
|
jsonContent.textContent = "";
|
|
imageStack.hidden = true;
|
|
placeholder.hidden = false;
|
|
placeholder.textContent = "提交后显示叠加效果";
|
|
baseImage.removeAttribute("src");
|
|
annotationImage.removeAttribute("src");
|
|
if (originalImageUrl) {
|
|
URL.revokeObjectURL(originalImageUrl);
|
|
originalImageUrl = undefined;
|
|
}
|
|
}
|
|
|
|
function updateOverlay() {
|
|
baseImage.hidden = !$("showBase").checked;
|
|
annotationImage.hidden = !$("showAnno").checked;
|
|
annotationImage.style.opacity = $("annoOpacity").value / 100;
|
|
}
|
|
|
|
function renderMetrics(data) {
|
|
const courts = data.four_courts || {};
|
|
const eyes = data.seven_eyes || {};
|
|
const items = [[totalHeightLabel, data.face_total_height_cm]];
|
|
if (form.hasAttribute("data-include-top-court")) items.push(["顶庭", courts.top_court_cm]);
|
|
items.push(
|
|
["上庭", courts.upper_court_cm],
|
|
["中庭", courts.middle_court_cm],
|
|
["下庭", courts.lower_court_cm],
|
|
["单眼宽度", eyes.eye_width_cm],
|
|
["脸宽", eyes.face_width_cm],
|
|
["两眼间距", eyes.inter_eye_distance_cm],
|
|
);
|
|
|
|
metrics.replaceChildren();
|
|
for (const [label, value] of items) {
|
|
const card = document.createElement("div");
|
|
card.className = "metric";
|
|
card.innerHTML = '<div class="label"></div><div class="value"><span></span> <span class="unit">cm</span></div>';
|
|
card.querySelector(".label").textContent = label;
|
|
card.querySelector(".value span").textContent = value ?? "—";
|
|
metrics.append(card);
|
|
}
|
|
metrics.classList.remove("hidden");
|
|
}
|
|
|
|
form.addEventListener("submit", async (event) => {
|
|
event.preventDefault();
|
|
const selected = fileInput.files[0];
|
|
if (!selected) {
|
|
setStatus("请先选择一张图片", "error");
|
|
return;
|
|
}
|
|
|
|
fileInput.disabled = submitButton.disabled = clearButton.disabled = true;
|
|
submitButton.textContent = "⏳ 请求中...";
|
|
resetResults();
|
|
setStatus("正在请求 " + endpoint, "info");
|
|
|
|
try {
|
|
const file = await window.downscaleImageFile(selected);
|
|
const body = new FormData();
|
|
body.append("image_file", file);
|
|
const start = performance.now();
|
|
const response = await window.hairClientFetch(endpoint, { method: "POST", body });
|
|
if (!response.headers.get("Content-Type")?.includes("application/json")) {
|
|
throw new Error("服务返回 HTTP " + response.status + ",响应格式异常");
|
|
}
|
|
const result = await response.json();
|
|
const elapsed = ((performance.now() - start) / 1000).toFixed(2);
|
|
jsonContent.textContent = JSON.stringify(result, null, 2);
|
|
results.classList.remove("hidden");
|
|
|
|
if (!response.ok || result.code !== 0) {
|
|
setStatus(`请求失败 (${elapsed}s) · HTTP ${response.status} · code ${result.code} · ${result.message}`, "error");
|
|
placeholder.textContent = "请求失败,无标注图";
|
|
return;
|
|
}
|
|
|
|
const data = result.data;
|
|
const annotationSrc = window.resolveImgSrc(data.annotated_image_url, data.annotated_image_base64, "image/png");
|
|
if (annotationSrc) {
|
|
originalImageUrl = URL.createObjectURL(file);
|
|
baseImage.src = originalImageUrl;
|
|
annotationImage.src = annotationSrc;
|
|
updateOverlay();
|
|
imageStack.hidden = false;
|
|
placeholder.hidden = true;
|
|
} else {
|
|
placeholder.textContent = "服务未返回标注图";
|
|
}
|
|
renderMetrics(data);
|
|
setStatus(`请求成功 (${elapsed}s) · request_id ${result.request_id}`, "success");
|
|
} catch (error) {
|
|
setStatus(error.message, "error");
|
|
} finally {
|
|
fileInput.disabled = submitButton.disabled = clearButton.disabled = false;
|
|
submitButton.textContent = "🚀 提交测试";
|
|
}
|
|
});
|
|
|
|
$("showBase").addEventListener("change", updateOverlay);
|
|
$("showAnno").addEventListener("change", updateOverlay);
|
|
$("annoOpacity").addEventListener("input", updateOverlay);
|
|
clearButton.addEventListener("click", () => {
|
|
resetResults();
|
|
fileInput.value = "";
|
|
setStatus("");
|
|
});
|
|
|
|
copyButton.addEventListener("click", async () => {
|
|
try {
|
|
await navigator.clipboard.writeText(jsonContent.textContent);
|
|
copyButton.textContent = "✅ 已复制";
|
|
setTimeout(() => { copyButton.textContent = "📋 复制"; }, 1500);
|
|
} catch {
|
|
setStatus("复制失败,请手动复制 JSON", "error");
|
|
}
|
|
});
|
|
|
|
fileInput.addEventListener("change", () => {
|
|
if (fileInput.files.length) setStatus("已选择 " + fileInput.files[0].name, "info");
|
|
});
|
|
fileInput.addEventListener("dragover", (event) => event.preventDefault());
|
|
fileInput.addEventListener("drop", (event) => {
|
|
event.preventDefault();
|
|
if (event.dataTransfer.files.length) {
|
|
fileInput.files = event.dataTransfer.files;
|
|
fileInput.dispatchEvent(new Event("change"));
|
|
}
|
|
});
|
|
})();
|