/* views/common.js —— 所有页面共用的渲染片段:状态徽标、人员、徽章、空态、骨架、筛选条。 * 只返回 HTML 字符串,事件由 app.js 的事件委托统一处理(data-act / data-*)。 * 设计约定:数字直接穿着强调色(不再套胶囊),标签只用字重和灰度分层。 */ (function () { "use strict"; const T = window.KF_TAXONOMY; const U = window.KF_UTIL; const esc = U.esc; /* ---------- 小片段 ---------- */ /* 层:小圆点 + 英文小字,克制的色标 */ function layerBadge(layer) { const l = T.layerOf(layer); if (!l) return `layer 缺失`; return `${l.en}`; } /* 证据:纯文字 + 状态点 */ function evidenceBadge(row) { const key = row.evidenceLevel || "none"; return `${esc(row.evidenceLabel)}`; } function priorityBadge(p) { const cls = ["S", "A", "B"].includes(p) ? `prio-${p}` : "prio-"; return `${esc(p || "—")}`; } /* 人:不构成"卡片",只有首字与名字。之后接的文字会显示在名字后面(inline) */ function person(name, extra) { return ` ${esc(name || "—")}${extra ? `${esc(extra)}` : ""}`; } /* 阶段:S 编号 + 五格进度,当前阶段用强调色指路 */ function stageCell(stageObj, raw, opts) { const cur = stageObj ? stageObj.no : 0; const steps = T.STAGES.map((s) => { if (s.no < cur) return ``; if (s.no === cur) return ``; return ``; }).join(""); const stuck = opts && opts.stuck; return ` ${stageObj ? "S" + stageObj.no : esc(raw || "—")} ${steps} `; } function stateBadge(status, label) { return `${esc(label || status || "—")}`; } const icons = { search: ``, menu: ``, x: ``, arrow: ``, info: ``, refresh: ``, chevron: ``, doc: ``, link: ``, quote: ``, layers: ``, grid: ``, spark: ``, users: ``, steps: ``, box: ``, archive: ``, cards: ``, back: `` }; /* 四层各自的色点(用于图例、层级条),来自 tokens 里的 --c-* */ function layerDot(key) { return `${esc(T.layerOf(key) ? T.layerOf(key).en : key)}`; } /* ---------- 空态 / 骨架 ---------- */ function empty(opts) { const o = opts || {}; const acts = (o.actions || []).map((a) => a.href ? `${esc(a.label)}` : ``).join(""); return `

${esc(o.title || "这里还没有内容")}

${o.body || ""}

${acts ? `
${acts}
` : ""}
`; } /* 骨架:高度与真实内容对齐,加载前后不顶开 */ function skeleton(kind, n) { const rows = []; for (let i = 0; i < (n || 4); i++) { rows.push(`
`); } if (kind === "metrics") { return `
${Array.from({ length: 4 }).map(() => `
`).join("")}
`; } return `
${rows.join("")}
`; } /* ---------- 筛选条 ---------- * dims: [{key,label,multi,options:[{value,label,count}],active:[...]}] * 每个选项都显示"在其他条件约束下还剩几条",0 条置灰不可点(但仍可看到)。 * 所有维度同时生效,点击任一维度只切换该维度自身的值。 */ function filterBar(dims, c, resultCount, unit) { const parts = dims.map((d) => { const act = Array.isArray(d.active) ? d.active : []; const chips = d.options.map((o) => { const on = act.includes(o.value); /* 0 条的选项仍可点:结果会变空,空态里会说明是哪项卡住并给出清除按钮 */ const zero = o.count === 0 && !on; return ``; }).join(""); return `
${esc(d.label)}${chips}
`; }).join(""); const any = !window.KF_QUERY.isEmpty(c); return `
${parts}
符合当前条件 ${resultCount} ${esc(unit || "条")} ${any ? `` : ""}
`; } /* 已选条件回显(文字型) */ function activeLine(c, labels) { const pills = []; const push = (key, value, label) => pills.push(`${esc(label)}`); c.priority.forEach((v) => push("priority", v, `优先级 ${v}`)); c.stage.forEach((v) => push("stage", v, T.stageLabel(v))); c.category.forEach((v) => push("category", v, `品类 ${v}`)); c.evidence.forEach((v) => push("evidence", v, `证据 ${(T.EVIDENCE.find((e) => e.key === v) || {}).label || v}`)); c.layer.forEach((v) => push("layer", v, `层 ${v}`)); c.member.forEach((v) => push("member", v, `成员 ${v}`)); if (c.q) pills.push(`搜索「${esc(c.q)}」`); if (!pills.length) return ""; return `
${pills.join("")}${labels || ""}
`; } function crumb(items) { return items.map((it, i) => { const last = i === items.length - 1; const node = last || !it.href ? `${esc(it.label)}` : `${esc(it.label)}`; return (i ? `/` : "") + node; }).join(""); } window.KF_UI = { esc, layerBadge, layerDot, evidenceBadge, priorityBadge, person, stageCell, stateBadge, icons, empty, skeleton, filterBar, activeLine, crumb }; })();