function readInt(id) { return parseInt(document.getElementById(id).value, 10) || 0; } function updateScaleHint() { const total = readInt("hotspot_limit") * readInt("item_limit_per_hotspot") * readInt("comment_limit_per_item"); const target = document.getElementById("scale-calc"); if (target) target.textContent = total.toLocaleString(); } function validateRange(input) { const value = readInt(input.id); const min = parseInt(input.min, 10); const max = parseInt(input.max, 10); const valid = value >= min && value <= max; input.classList.toggle("is-invalid", !valid); return valid; } async function submitTask(event) { event.preventDefault(); const btn = document.getElementById("submit-btn"); const errorBox = document.getElementById("form-error"); const inputs = Array.from(document.querySelectorAll(".scale-input")); if (!inputs.every(validateRange)) { errorBox.textContent = "参数超出范围,请检查配置。"; errorBox.classList.remove("d-none"); return; } btn.disabled = true; btn.textContent = "提交中..."; errorBox.classList.add("d-none"); const payload = { platform: document.getElementById("platform").value, hotspot_limit: readInt("hotspot_limit"), item_limit_per_hotspot: readInt("item_limit_per_hotspot"), comment_limit_per_item: readInt("comment_limit_per_item"), }; try { const resp = await fetch("/api/tasks", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }); if (resp.ok) { const data = await resp.json(); window.location.href = `/tasks/${data.task_id}`; return; } const data = await resp.json(); errorBox.textContent = resp.status === 422 ? "参数错误,请检查配置。" : (data.detail || "服务器错误,请稍后重试。"); errorBox.classList.remove("d-none"); } catch (_error) { errorBox.textContent = "网络异常,请检查连接后重试。"; errorBox.classList.remove("d-none"); } finally { btn.disabled = false; btn.textContent = "开始抓取"; } } document.addEventListener("DOMContentLoaded", () => { document.querySelectorAll(".scale-input").forEach((input) => { input.addEventListener("input", () => { validateRange(input); updateScaleHint(); }); }); updateScaleHint(); }); async function downloadExport(url, defaultFilename, event) { if (event) event.preventDefault(); const resp = await fetch(url); if (!resp.ok) { alert("导出失败,请稍后重试。"); return; } const blob = await resp.blob(); const disposition = resp.headers.get("Content-Disposition") || ""; const match = disposition.match(/filename\*=UTF-8''([^;]+)/); const filename = match ? decodeURIComponent(match[1]) : defaultFilename; const blobUrl = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = blobUrl; link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(blobUrl); }