function toast(msg) { const el = document.getElementById("toast"); el.textContent = msg; el.classList.remove("hidden"); setTimeout(() => el.classList.add("hidden"), 2200); } document.querySelectorAll("[data-copy]").forEach((btn) => { btn.addEventListener("click", async () => { const text = btn.dataset.copy; try { await navigator.clipboard.writeText(text); toast("已复制到剪贴板"); } catch { toast("复制失败,请手动选择文本"); } }); }); const modal = document.getElementById("modal"); const addBtn = document.getElementById("addBtn"); const cancelBtn = document.getElementById("cancelBtn"); const confirmAddBtn = document.getElementById("confirmAddBtn"); const nodeName = document.getElementById("nodeName"); if (addBtn) { addBtn.addEventListener("click", () => { nodeName.value = ""; modal.classList.remove("hidden"); nodeName.focus(); }); } if (cancelBtn) { cancelBtn.addEventListener("click", () => modal.classList.add("hidden")); } if (confirmAddBtn) { confirmAddBtn.addEventListener("click", async () => { const name = nodeName.value.trim() || "新节点"; confirmAddBtn.disabled = true; try { const res = await fetch("/api/nodes", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name }), }); const data = await res.json(); if (!res.ok) throw new Error(data.error || "创建失败"); location.reload(); } catch (err) { toast(err.message); } finally { confirmAddBtn.disabled = false; } }); } document.querySelectorAll(".delete-btn").forEach((btn) => { btn.addEventListener("click", async () => { const id = btn.dataset.id; if (!confirm("确定删除该节点?删除后对应链接将失效。")) return; btn.disabled = true; try { const res = await fetch(`/api/nodes/${id}`, { method: "DELETE" }); const data = await res.json(); if (!res.ok) throw new Error(data.error || "删除失败"); location.reload(); } catch (err) { toast(err.message); btn.disabled = false; } }); });