/* 图标库:统一 24×24 线性 SVG,描边走 currentColor,重点色由外层 CSS 决定。
用法:Icon.svg("wallet") → 裸图标
Icon.badge("wallet") → 图标 + 重点色底板
Icon.status("in_progress") → 执行状态图标
Icon.stage("signed") → 定制阶段图标
Icon.pipe("entry") → 交付环节图标
静态标记里写 ,加载时自动替换成 SVG。 */
(function () {
"use strict";
var P = {
/* 导航 */
board: '',
"list-checks": '',
package: '',
target: '',
users: '',
pencil: '',
sliders: '',
/* 通用动作 */
plus: '',
x: '',
"chevron-left": '',
"chevron-right": '',
"chevron-down": '',
"arrow-right": '',
search: '',
download: '',
check: '',
"check-circle": '',
circle: '',
clock: '',
alert: '',
"alert-circle": '',
info: '',
ban: '',
pause: '',
refresh: '',
star: '',
filter: '',
sparkles: '',
trash: '',
archive: '',
flag: '',
/* 业务语义 */
calendar: '',
"calendar-check": '',
user: '',
layers: '',
activity: '',
pie: '',
"bar-chart": '',
wallet: '',
receipt: '',
"trending-up": '',
inbox: '',
"shield-check": '',
gauge: '',
hourglass: '',
note: '',
database: '',
bell: '',
code: '',
link: '',
};
var STATUS_ICON = {
not_started: "circle",
in_progress: "refresh",
done: "check-circle",
at_risk: "alert",
blocked: "ban",
paused: "pause",
};
var STAGE_ICON = {
lead: "target",
signed: "pencil",
developing: "code",
delivered: "package",
accepted: "shield-check",
revenue: "wallet",
paused: "pause",
closed: "archive",
};
var PIPE_ICON = { entry: "inbox", signed: "pencil", delivered: "package", accepted: "shield-check", revenue: "wallet" };
function svg(name, cls, size) {
var body = P[name];
if (!body) return "";
var attrs =
'class="ic' + (cls ? " " + cls : "") + '" viewBox="0 0 24 24" fill="none" stroke="currentColor"' +
' stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"';
if (size) attrs += ' width="' + size + '" height="' + size + '"';
return "";
}
/** 图标 + 重点色小底板:重点色来自外层 --c */
function badge(name, cls) {
return '' + svg(name) + "";
}
function status(code) { return svg(STATUS_ICON[code] || "circle"); }
function nameOfStatus(code) { return STATUS_ICON[code] || "circle"; }
function nameOfStage(code) { return STAGE_ICON[code] || "circle"; }
function statusBadge(code) { return badge(nameOfStatus(code)); }
function stage(code) { return svg(STAGE_ICON[code] || "circle"); }
function pipe(key) { return svg(PIPE_ICON[key] || "circle"); }
function hydrate(root) {
var nodes = (root || document).querySelectorAll("[data-icon]");
for (var i = 0; i < nodes.length; i++) {
var el = nodes[i];
el.innerHTML = svg(el.getAttribute("data-icon"), el.getAttribute("data-icon-class") || "");
el.removeAttribute("data-icon");
}
}
/* ---------- 按标题关键词自动配图标 ----------
标题类文案(卡片标题 / 区块标题 / 看板列头 / 左栏分组头)由渲染函数拼装,
逐个手写图标会散落几十处;这里按关键词统一命中一次,命中不到就不加图标。 */
var TITLE_ICON = [
["工作重点", "list-checks"], ["重点工作", "list-checks"], ["工作重点", "list-checks"],
["执行记录", "activity"], ["交付记录", "activity"], ["更新活跃度", "activity"], ["活跃度", "activity"],
["未更新", "alert-circle"], ["卡点", "alert-circle"], ["预警", "alert-circle"], ["风险", "alert"],
["状态分布", "gauge"], ["进度", "gauge"], ["趋势", "trending-up"], ["走势", "trending-up"],
["分类分布", "pie"], ["金额分布", "pie"], ["金额", "wallet"],
["财务", "wallet"], ["收入", "wallet"], ["计收", "wallet"], ["合同", "wallet"],
["开票", "receipt"], ["回款", "receipt"],
["负责人", "users"], ["人员", "users"], ["人力", "users"], ["花名册", "users"], ["在册", "users"],
["区域", "layers"], ["归属", "layers"], ["分类", "pie"],
["明细", "list-checks"], ["待办", "list-checks"], ["本周新增", "inbox"], ["有变化", "refresh"],
["在管项目", "layers"], ["定制项目", "package"], ["交付环节", "layers"], ["交付漏斗", "filter"],
["交付", "package"], ["验收", "shield-check"], ["入库", "inbox"], ["签单", "pencil"],
["阶段流转", "refresh"], ["阶段", "layers"], ["线索", "target"], ["开发", "code"],
["重点项目", "star"], ["重点", "star"], ["周期反馈", "calendar-check"], ["周期管理", "calendar"],
["周期", "calendar"], ["更新录入", "pencil"], ["最近更新", "clock"], ["整理结果", "sparkles"],
["配置", "sliders"], ["数据库", "database"], ["统计概览", "bar-chart"], ["概览", "bar-chart"],
["基础信息", "info"], ["基本信息", "info"], ["说明", "info"], ["反馈", "note"], ["记录", "note"],
["归档", "archive"], ["项目", "layers"],
];
var EMPTY_ICON = [["加载", "refresh"], ["没有", "inbox"], ["暂无", "inbox"], ["未填写", "note"], ["找不到", "search"]];
function pickIcon(text, rules) {
for (var i = 0; i < rules.length; i++) if (text.indexOf(rules[i][0]) >= 0) return rules[i][1];
return "";
}
function leadingIcon(el, name, cls) {
if (!el || el.getAttribute("data-ico")) return;
el.setAttribute("data-ico", "1");
if (name) el.insertAdjacentHTML("afterbegin", svg(name, cls));
}
function text(el) {
return (el.textContent || "").replace(/\s+/g, "");
}
/** 给一处渲染结果补图标:静态标记里的 data-icon + 标题/空态/提示条的关键词图标 */
function auto(root) {
if (!root) return;
if (root.nodeType === 1) fill(root);
if (!root.querySelectorAll) return;
var nodes = root.querySelectorAll("[data-icon], .card-head h4, .sec-title, .kcol-title, .wk-group-head, .empty, .notice");
fillAll(nodes);
}
function fillAll(nodes) {
for (var i = 0; i < nodes.length; i++) fill(nodes[i]);
}
function fill(el) {
if (el.getAttribute("data-icon")) {
el.innerHTML = svg(el.getAttribute("data-icon"), el.getAttribute("data-icon-class") || "");
el.removeAttribute("data-icon");
return;
}
if (el.matches(".card-head h4, .sec-title, .kcol-title, .wk-group-head")) {
// 模板里已经自带前导图标的标题(如阶段看板的阶段图标)不再自动补第二个
if (el.querySelector(":scope > .ic")) { el.setAttribute("data-ico", "1"); return; }
leadingIcon(el, pickIcon(text(el), TITLE_ICON), "ti");
} else if (el.matches(".notice")) {
leadingIcon(el, el.classList.contains("err") ? "alert-circle" : el.classList.contains("warn") ? "alert-circle" : "info", "ti");
} else if (el.matches(".empty")) {
leadingIcon(el, pickIcon(text(el), EMPTY_ICON) || "inbox", "ic-lg");
}
}
function observe() {
if (!window.MutationObserver) return;
new MutationObserver(function (ms) {
for (var i = 0; i < ms.length; i++) {
var added = ms[i].addedNodes;
for (var j = 0; j < added.length; j++) auto(added[j]);
}
}).observe(document.body, { childList: true, subtree: true });
}
window.Icon = { svg: svg, badge: badge, status: status, stage: stage, pipe: pipe,
nameOfStatus: nameOfStatus, nameOfStage: nameOfStage, statusBadge: statusBadge,
hydrate: hydrate, auto: auto, names: P };
function boot() {
hydrate(document);
auto(document);
observe();
}
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", boot);
else boot();
})();