157 lines
4.8 KiB
JavaScript
157 lines
4.8 KiB
JavaScript
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();
|
|
});
|
|
|
|
function pollTaskDetailStatus(taskId) {
|
|
if (!taskId) return;
|
|
const intervalMs = 3000;
|
|
const poll = async () => {
|
|
try {
|
|
const resp = await fetch(`/api/tasks/${taskId}`, { cache: "no-store" });
|
|
if (!resp.ok) return;
|
|
const task = await resp.json();
|
|
if (task.status !== "running") {
|
|
window.location.reload();
|
|
return;
|
|
}
|
|
} catch (_error) {
|
|
return;
|
|
}
|
|
window.setTimeout(poll, intervalMs);
|
|
};
|
|
window.setTimeout(poll, intervalMs);
|
|
}
|
|
|
|
function pollTaskListStatus() {
|
|
const tableBody = document.querySelector("[data-task-list-auto-poll='true']");
|
|
if (!tableBody) return;
|
|
|
|
const intervalMs = 5000;
|
|
const poll = async () => {
|
|
try {
|
|
const resp = await fetch("/api/tasks", { cache: "no-store" });
|
|
if (!resp.ok) return;
|
|
const tasks = await resp.json();
|
|
const hasRunningTask = tasks.some((task) => task.status === "running");
|
|
window.location.reload();
|
|
if (!hasRunningTask) return;
|
|
} catch (_error) {
|
|
return;
|
|
}
|
|
window.setTimeout(poll, intervalMs);
|
|
};
|
|
window.setTimeout(poll, intervalMs);
|
|
}
|
|
|
|
async function downloadExport(url, defaultFilename, event) {
|
|
if (event) event.preventDefault();
|
|
const button = event ? event.currentTarget : null;
|
|
const originalText = button ? button.textContent : "";
|
|
if (button) {
|
|
button.disabled = true;
|
|
button.textContent = "下载中...";
|
|
}
|
|
try {
|
|
const resp = await fetch(url, { cache: "no-store" });
|
|
if (!resp.ok) {
|
|
let message = "导出失败,请稍后重试。";
|
|
try {
|
|
const data = await resp.json();
|
|
if (data.detail) message = data.detail;
|
|
} catch (_error) {
|
|
message = resp.status === 503 ? "数据库暂时不可用,请稍后重试。" : message;
|
|
}
|
|
alert(message);
|
|
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);
|
|
} catch (_error) {
|
|
alert("网络异常,导出未完成。");
|
|
} finally {
|
|
if (button) {
|
|
button.disabled = false;
|
|
button.textContent = originalText;
|
|
}
|
|
}
|
|
}
|