unis_manager/web/icons.js

211 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/* 图标库:统一 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();
})();