Files
zTools2/static/common.js
zikai 3284269399 fix: 复选框初始状态错误(el 对布尔属性用 setAttribute 导致 checked:false 仍勾选)
common.js 的 el() 对 checked/disabled/hidden 等布尔属性调用 setAttribute(k, v),
而 setAttribute("checked", "false") 仍会令 checkbox 呈勾选态(属性存在即为真)。
改为维护 BOOL_PROPS 集合,布尔属性用 node[k] = !!v 赋值;同时 v === false 时跳过 setAttribute。
修复后文件浏览页复选框初始未选中时正确显示为未勾选。
2026-07-21 14:41:10 +00:00

95 lines
3.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* zikai 共享前端工具toast、复制、字节/时间格式化、fetch 封装。
Basic Auth 同源时浏览器自动带缓存的凭据无需额外处理fetch 默认 same-origin。 */
(function (global) {
"use strict";
// 布尔属性:用属性赋值而非 setAttributesetAttribute("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);