/* zikai 共享前端工具:toast、复制、字节/时间格式化、fetch 封装。 Basic Auth 同源时浏览器自动带缓存的凭据,无需额外处理;fetch 默认 same-origin。 */ (function (global) { "use strict"; // 布尔属性:用属性赋值而非 setAttribute(setAttribute("checked","false") 仍会勾选) const BOOL_PROPS = new Set([ "checked", "disabled", "readonly", "selected", "hidden", "multiple", "open", "autofocus", "required", "async", "defer", "controls", "autoplay", "loop", "muted", ]); function el(tag, attrs, ...children) { const node = document.createElement(tag); if (attrs) { for (const [k, v] of Object.entries(attrs)) { if (k === "class") node.className = v; else if (k === "dataset") Object.assign(node.dataset, v); else if (k.startsWith("on") && typeof v === "function") node.addEventListener(k.slice(2).toLowerCase(), v); else if (BOOL_PROPS.has(k)) node[k] = !!v; else if (v !== null && v !== undefined && v !== false) node.setAttribute(k, v); } } for (const c of children) { if (c == null || c === false) continue; node.appendChild(typeof c === "string" ? document.createTextNode(c) : c); } return node; } let toastTimer = null; function toast(msg, kind) { let node = document.querySelector(".toast"); if (!node) { node = el("div", { class: "toast" }); document.body.appendChild(node); } node.textContent = msg; node.className = "toast show" + (kind ? " " + kind : ""); clearTimeout(toastTimer); toastTimer = setTimeout(() => (node.className = "toast"), 2200); } async function copyText(text) { try { await navigator.clipboard.writeText(text); return true; } catch { // 降级:临时 textarea const ta = document.createElement("textarea"); ta.value = text; ta.style.position = "fixed"; ta.style.opacity = "0"; document.body.appendChild(ta); ta.select(); let ok = false; try { ok = document.execCommand("copy"); } catch {} document.body.removeChild(ta); return ok; } } function fmtBytes(n) { if (n == null) return "-"; const x = Number(n); if (!isFinite(x)) return "-"; for (const unit of ["B", "KiB", "MiB", "GiB", "TiB", "PiB"]) { if (Math.abs(x) < 1024 || unit === "PiB") return unit === "B" ? `${x} B` : `${x.toFixed(1)} ${unit}`; n = x / 1024; } return `${n.toFixed(1)} PiB`; } function fmtTime(s) { if (!s) return "-"; const d = new Date(s); if (isNaN(d.getTime())) return s; const p = (n) => String(n).padStart(2, "0"); return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`; } async function api(path, opts) { const res = await fetch(path, opts); if (res.status === 401) { // 触发浏览器 Basic Auth 弹窗(同源 reload 即可带上凭据) toast("需要登录"); throw new Error("UNAUTHORIZED"); } return res; } global.ZK = { el, toast, copyText, fmtBytes, fmtTime, api }; })(window);