/* Common helpers shared across pages (ES module). */ /** Debounce a function call. */ export function debounce(fn, delay) { let timer = null; return function (...args) { clearTimeout(timer); timer = setTimeout(() => fn.apply(this, args), delay); }; } /** Format an ISO date string to YYYY-MM-DD. */ export function formatDate(isoString) { if (!isoString) return ""; const d = new Date(isoString); if (isNaN(d.getTime())) return String(isoString).slice(0, 10); const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, "0"); const day = String(d.getDate()).padStart(2, "0"); return `${y}-${m}-${day}`; } /** Truncate text to maxLen chars, appending ellipsis. */ export function truncate(text, maxLen) { if (!text) return ""; const s = String(text); return s.length > maxLen ? s.slice(0, maxLen) + "…" : s; } /** Escape HTML special chars to prevent XSS when injecting via innerHTML. */ export function escapeHtml(text) { if (text === null || text === undefined) return ""; return String(text) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } /** Read the session CSRF token rendered into . */ export function getCsrf() { const meta = document.querySelector('meta[name="csrf-token"]'); return meta ? meta.content : ""; } /** Fetch JSON with basic error handling. */ export async function fetchJson(url) { const resp = await fetch(url); if (!resp.ok) { throw new Error(`请求失败: ${resp.status} ${resp.statusText}`); } return resp.json(); } /** POST JSON, injecting csrf_token into the body. Returns parsed JSON. */ export async function postJson(url, data) { const resp = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...data, csrf_token: getCsrf() }), }); const body = await resp.json().catch(() => ({})); if (!resp.ok) { throw new Error(body.error || `请求失败: ${resp.status}`); } return body; } /** Render a dismissible error alert into a container element. */ export function showError(container, msg) { if (!container) return; container.innerHTML = ''; }