v1.0.1: 弹窗防误关确认/布局修复/定时任务首次执行时间/日间夜间双主题/总体统计区

This commit is contained in:
2026-08-11 13:02:34 +08:00
parent dede5003a4
commit 29176fc66b
5 changed files with 218 additions and 40 deletions
+69 -5
View File
@@ -11,6 +11,7 @@ const state = {
detail: { task: null, runId: null, logOffset: 0, timer: null },
logTimer: null,
};
let formDirty = false; // 新建/编辑表单是否有未保存修改
/* ---------------- 工具 ---------------- */
function toast(msg, isErr) {
@@ -48,6 +49,21 @@ async function loadTasks() {
} catch (e) {
toast("加载任务失败: " + e.message, true);
}
loadStats();
}
async function loadStats() {
try {
const s = await api("/api/stats");
$("stTasks").textContent = s.tasks;
$("stRunning").textContent = s.running;
$("stRuns").textContent = s.runs;
$("stOk").textContent = s.ok;
$("stFail").textContent = s.fail;
$("stImgs").textContent = s.images;
$("stDisk").textContent = s.disk_mb >= 1024
? (s.disk_mb / 1024).toFixed(1) + " GB" : s.disk_mb + " MB";
} catch (e) { /* 统计失败忽略 */ }
}
function taskStatusBadge(t) {
@@ -156,8 +172,17 @@ function switchMode(mode, lock) {
$("autoBox").classList.toggle("hidden", mode !== "auto");
}
function toLocalInputVal(iso) {
if (!iso) return "";
const d = new Date(String(iso).replace(" ", "T"));
if (isNaN(d.getTime())) return "";
const p = (n) => String(n).padStart(2, "0");
return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}T${p(d.getHours())}:${p(d.getMinutes())}`;
}
function openCreate() {
state.editTask = null;
formDirty = false;
$("modalTitle").textContent = "新建爬取任务";
$("taskForm").reset();
$("taskForm").elements["delay_min"].value = 2;
@@ -195,6 +220,7 @@ async function openEdit(tid) {
f.elements["interval_value"].value = t.schedule.interval_value ?? 24;
f.elements["interval_unit"].value = t.schedule.interval_unit || "hours";
f.elements["cron"].value = t.schedule.cron || "";
f.elements["first_run"].value = toLocalInputVal(t.schedule.next_run || "");
}
if (t.mode === "auto" && t.auto) {
f.elements["seed_url"].value = t.auto.seed_url || "";
@@ -209,6 +235,7 @@ async function openEdit(tid) {
$("formHint").textContent = t.running
? "⚠️ 任务运行中:参数修改将热更新(网址/规则改动下次运行生效)"
: "";
formDirty = false;
switchMode(t.mode, true);
showModal("taskModal");
}
@@ -259,6 +286,7 @@ async function submitForm(e) {
interval_unit: f.elements["interval_unit"].value,
interval_value: parseInt(f.elements["interval_value"].value) || 1,
cron: f.elements["cron"].value.trim(),
first_run: f.elements["first_run"].value || "",
};
}
try {
@@ -275,6 +303,7 @@ async function submitForm(e) {
});
toast(`任务「${t.name}」已创建`);
}
formDirty = false;
hideModal("taskModal");
loadTasks();
} catch (err) { toast(err.message, true); }
@@ -457,6 +486,29 @@ function previewFile(tid, path, title) {
function showModal(id) { $(id).classList.remove("hidden"); }
function hideModal(id) { $(id).classList.add("hidden"); }
/* 关闭任务弹窗: 有未保存修改时先确认 */
function safeCloseTaskModal() {
if (formDirty && !confirm("有未保存的修改,确定要放弃吗?")) return false;
formDirty = false;
hideModal("taskModal");
return true;
}
/* ---------------- 主题切换 ---------------- */
function applyTheme(theme) {
document.body.dataset.theme = theme;
$("btnTheme").textContent = theme === "light" ? "🌙" : "☀️";
try { localStorage.setItem("crawler_theme", theme); } catch (e) { /* ignore */ }
}
$("btnTheme").onclick = () => {
applyTheme(document.body.dataset.theme === "light" ? "dark" : "light");
};
try {
applyTheme(localStorage.getItem("crawler_theme") || "dark");
} catch (e) {
applyTheme("dark");
}
/* ---------------- 事件绑定 ---------------- */
$("btnNew").onclick = openCreate;
$("btnNew2").onclick = openCreate;
@@ -468,24 +520,36 @@ document.querySelectorAll("#modeTabs .tab").forEach((b) => {
$("taskForm").onsubmit = submitForm;
$("taskForm").elements["schedule_type"].onchange = syncScheduleUI;
document.querySelectorAll("[data-close]").forEach((b) => b.onclick = () => hideModal("taskModal"));
document.querySelectorAll("[data-close]").forEach((b) => b.onclick = safeCloseTaskModal);
document.querySelectorAll("[data-close-detail]").forEach((b) => b.onclick = () => { stopLogPoll(); hideModal("detailModal"); });
document.querySelectorAll("[data-close-preview]").forEach((b) => b.onclick = () => { $("previewFrame").src = "about:blank"; hideModal("previewModal"); });
/* 表单改动监听 -> 脏标记 */
$("taskForm").addEventListener("input", () => { formDirty = true; });
$("taskForm").addEventListener("change", () => { formDirty = true; });
document.querySelectorAll(".modal-overlay").forEach((ov) => {
ov.addEventListener("mousedown", (e) => {
if (e.target === ov) {
if (ov.id === "detailModal") stopLogPoll();
if (ov.id === "previewModal") $("previewFrame").src = "about:blank";
ov.classList.add("hidden");
if (ov.id === "taskModal") {
safeCloseTaskModal();
} else {
if (ov.id === "detailModal") stopLogPoll();
if (ov.id === "previewModal") $("previewFrame").src = "about:blank";
ov.classList.add("hidden");
}
}
});
});
document.addEventListener("keydown", (e) => {
if (e.key === "Escape") {
if (!$("taskModal").classList.contains("hidden")) {
safeCloseTaskModal();
return;
}
stopLogPoll();
$("previewFrame").src = "about:blank";
["taskModal", "detailModal", "previewModal"].forEach((id) => $(id).classList.add("hidden"));
["detailModal", "previewModal"].forEach((id) => $(id).classList.add("hidden"));
}
});