211 lines
12 KiB
JavaScript
211 lines
12 KiB
JavaScript
/* 图标库:统一 24×24 线性 SVG,描边走 currentColor,重点色由外层 CSS 决定。
|
||
用法:Icon.svg("wallet") → 裸图标
|
||
Icon.badge("wallet") → 图标 + 重点色底板
|
||
Icon.status("in_progress") → 执行状态图标
|
||
Icon.stage("signed") → 定开阶段图标
|
||
Icon.pipe("entry") → 交付环节图标
|
||
静态标记里写 <i data-icon="wallet"></i>,加载时自动替换成 SVG。 */
|
||
(function () {
|
||
"use strict";
|
||
|
||
var P = {
|
||
/* 导航 */
|
||
board: '<rect x="3" y="3" width="7.5" height="9" rx="1.6"/><rect x="13.5" y="3" width="7.5" height="5" rx="1.6"/><rect x="13.5" y="12" width="7.5" height="9" rx="1.6"/><rect x="3" y="16" width="7.5" height="5" rx="1.6"/>',
|
||
"list-checks": '<path d="M3.4 6.6l1.7 1.7L8.2 5.2"/><path d="M3.4 12.6l1.7 1.7 3.1-3.1"/><path d="M3.4 18.6l1.7 1.7 3.1-3.1"/><path d="M11.6 7h9.2M11.6 13h9.2M11.6 19h9.2"/>',
|
||
package: '<path d="M12 3.4l8.1 4.4v8.4L12 20.6l-8.1-4.4V7.8L12 3.4z"/><path d="M4.2 8L12 12.3 19.8 8"/><path d="M12 12.3v8.3"/>',
|
||
target: '<circle cx="12" cy="12" r="8.4"/><circle cx="12" cy="12" r="4.2"/><circle cx="12" cy="12" r="1.1"/>',
|
||
users: '<circle cx="9.4" cy="8" r="3.4"/><path d="M3 20a6.4 6.4 0 0112.8 0"/><path d="M16.4 5.4a3.4 3.4 0 010 5.2"/><path d="M18.4 14.7A6.4 6.4 0 0121 20"/>',
|
||
pencil: '<path d="M4 20.2l.9-4L16.2 4.9a2.1 2.1 0 013 3L7.9 19.3l-3.9.9z"/><path d="M14.8 6.3l2.9 2.9"/>',
|
||
sliders: '<path d="M5 7h8.8M18.6 7H21M5 17h3.4M12.8 17h5.2"/><circle cx="16" cy="7" r="2.2"/><circle cx="10.6" cy="17" r="2.2"/>',
|
||
/* 通用动作 */
|
||
plus: '<path d="M12 5.2v13.6M5.2 12h13.6"/>',
|
||
x: '<path d="M6.2 6.2l11.6 11.6M17.8 6.2L6.2 17.8"/>',
|
||
"chevron-left": '<path d="M14.8 5.4L8 12l6.8 6.6"/>',
|
||
"chevron-right": '<path d="M9.2 5.4L16 12l-6.8 6.6"/>',
|
||
"chevron-down": '<path d="M6 9.2l6 6 6-6"/>',
|
||
"arrow-right": '<path d="M4.2 12h14.4M13 6.4l5.6 5.6L13 17.6"/>',
|
||
search: '<circle cx="11" cy="11" r="6.4"/><path d="M15.8 15.8l4.4 4.4"/>',
|
||
download: '<path d="M12 4v10.4M8 10.8l4 4 4-4"/><path d="M4.4 19.4h15.2"/>',
|
||
check: '<path d="M5 12.6l4.6 4.6L19 7"/>',
|
||
"check-circle": '<circle cx="12" cy="12" r="8.4"/><path d="M8.4 12.3l2.5 2.5 4.9-5"/>',
|
||
circle: '<circle cx="12" cy="12" r="8.4"/>',
|
||
clock: '<circle cx="12" cy="12" r="8.4"/><path d="M12 7.4V12l3.2 2"/>',
|
||
alert: '<path d="M12 4.2l8.6 15.1H3.4L12 4.2z"/><path d="M12 9.8v4.2M12 16.9h.01"/>',
|
||
"alert-circle": '<circle cx="12" cy="12" r="8.4"/><path d="M12 7.8v4.6M12 16.2h.01"/>',
|
||
info: '<circle cx="12" cy="12" r="8.4"/><path d="M12 11v5.2M12 7.9h.01"/>',
|
||
ban: '<circle cx="12" cy="12" r="8.4"/><path d="M6.1 6.1l11.8 11.8"/>',
|
||
pause: '<path d="M9.4 5.6v12.8M14.6 5.6v12.8"/>',
|
||
refresh: '<path d="M19.6 12a7.6 7.6 0 11-2.3-5.4"/><path d="M20 4.2v4.4h-4.4"/>',
|
||
star: '<path d="M12 3.9l2.55 5.2 5.75.83-4.15 4.05.98 5.72L12 17.02l-5.13 2.68.98-5.72L3.7 9.93l5.75-.83L12 3.9z"/>',
|
||
filter: '<path d="M4 5.8h16l-6.2 7.3v6l-3.6-2.1v-3.9L4 5.8z"/>',
|
||
sparkles: '<path d="M10.8 4l1.5 3.6 3.6 1.5-3.6 1.5-1.5 3.6-1.5-3.6L5.7 9.1l3.6-1.5L10.8 4z"/><path d="M17.6 14.6l.8 1.9 1.9.8-1.9.8-.8 1.9-.8-1.9-1.9-.8 1.9-.8.8-1.9z"/>',
|
||
trash: '<path d="M4.4 7h15.2"/><path d="M9.4 7V5.6A1.6 1.6 0 0111 4h2a1.6 1.6 0 011.6 1.6V7"/><path d="M6.6 7l.9 12.4A1.6 1.6 0 009.1 21h5.8a1.6 1.6 0 001.6-1.6L17.4 7"/>',
|
||
archive: '<rect x="3.2" y="4.2" width="17.6" height="4.4" rx="1.5"/><path d="M5.2 8.6v10.2a1.6 1.6 0 001.6 1.6h10.4a1.6 1.6 0 001.6-1.6V8.6"/><path d="M10 12.4h4"/>',
|
||
flag: '<path d="M6 20.6V4.2h11.6l-2.1 4.2 2.1 4.2H6"/>',
|
||
/* 业务语义 */
|
||
calendar: '<rect x="3.4" y="5.2" width="17.2" height="15.2" rx="2"/><path d="M3.4 10h17.2M8 3.4v3.4M16 3.4v3.4"/>',
|
||
"calendar-check": '<rect x="3.4" y="5.2" width="17.2" height="15.2" rx="2"/><path d="M3.4 10h17.2M8 3.4v3.4M16 3.4v3.4"/><path d="M8.8 14.4l2.1 2.1 4.3-4.3"/>',
|
||
user: '<circle cx="12" cy="8" r="3.5"/><path d="M5 20.2a7 7 0 0114 0"/>',
|
||
layers: '<path d="M12 3.4l8.4 4.4-8.4 4.4L3.6 7.8 12 3.4z"/><path d="M4 12.4l8 4.2 8-4.2"/><path d="M4 16.6l8 4.2 8-4.2"/>',
|
||
activity: '<path d="M3 12.4h3.8l2.4-6.2 4 12.2 2.4-6h5"/>',
|
||
pie: '<circle cx="12" cy="12" r="8.4"/><path d="M12 3.6V12h8.4"/>',
|
||
"bar-chart": '<path d="M4.6 20.2V11M12 20.2V4.4M19.4 20.2v-6.4M2.6 20.2h18.8"/>',
|
||
wallet: '<rect x="3.2" y="6" width="17.6" height="12.6" rx="2.4"/><path d="M3.2 10.4h17.6"/><circle cx="16.4" cy="14.6" r="1.2"/>',
|
||
receipt: '<path d="M6.2 3.6h11.6v17l-2.9-1.7-2.9 1.7-2.9-1.7-2.9 1.7v-17z"/><path d="M9.2 8.4h5.6M9.2 12.2h5.6"/>',
|
||
"trending-up": '<path d="M3.6 16.6l5.9-5.9 3.6 3.6 6.9-7.3"/><path d="M16.6 6.6h4.2v4.2"/>',
|
||
inbox: '<path d="M4 13.8h4l1.4 2.4h5.2l1.4-2.4h4"/><path d="M6.4 4.3h11.2l2.9 9.5v4.3a1.8 1.8 0 01-1.8 1.8H5.3a1.8 1.8 0 01-1.8-1.8v-4.3L6.4 4.3z"/>',
|
||
"shield-check": '<path d="M12 3.4l7 2.6v5.3c0 4.1-2.9 7.3-7 8.6-4.1-1.3-7-4.5-7-8.6V6l7-2.6z"/><path d="M8.8 11.9l2.2 2.2 4.2-4.2"/>',
|
||
gauge: '<path d="M4 17.6a8.4 8.4 0 1116 0"/><path d="M12 17.6l4-4.8"/><path d="M4 17.6h16"/>',
|
||
hourglass: '<path d="M7 3.6h10M7 20.4h10"/><path d="M7.6 3.6V7L12 11.4 16.4 7V3.6"/><path d="M7.6 20.4V17L12 12.6 16.4 17v3.4"/>',
|
||
note: '<path d="M5 4.4h14v10.4L14.4 19.4H5V4.4z"/><path d="M19 14.8h-4.6v4.6"/><path d="M8.4 9h7M8.4 12.4h4"/>',
|
||
database: '<ellipse cx="12" cy="6" rx="7.4" ry="2.9"/><path d="M4.6 6v11.9c0 1.6 3.3 2.9 7.4 2.9s7.4-1.3 7.4-2.9V6"/><path d="M19.4 12c0 1.6-3.3 2.9-7.4 2.9S4.6 13.6 4.6 12"/>',
|
||
bell: '<path d="M6.6 10.4a5.4 5.4 0 0110.8 0c0 4.2 1.4 5.6 1.4 5.6H5.2s1.4-1.4 1.4-5.6z"/><path d="M10 19a2.1 2.1 0 004 0"/>',
|
||
code: '<path d="M8.8 7.4L4.4 12l4.4 4.6"/><path d="M15.2 7.4L19.6 12l-4.4 4.6"/>',
|
||
link: '<path d="M10.2 13.8l3.6-3.6"/><path d="M8.6 11l-2 2a3.4 3.4 0 004.8 4.8l2-2"/><path d="M15.4 13l2-2a3.4 3.4 0 00-4.8-4.8l-2 2"/>',
|
||
};
|
||
|
||
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 "<svg " + attrs + ">" + body + "</svg>";
|
||
}
|
||
|
||
/** 图标 + 重点色小底板:重点色来自外层 --c */
|
||
function badge(name, cls) {
|
||
return '<span class="ic-badge' + (cls ? " " + cls : "") + '">' + svg(name) + "</span>";
|
||
}
|
||
|
||
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();
|
||
})();
|