/** * 工作台菜单图标解析:取菜单名称关键字 + 自动配色。 * * 规则: * 1. 先按 KEYWORD_RULES 的关键字匹配(specific 在前、generic 在后,顺序不能乱); * 2. 没命中时去掉通用后缀(申请/工单/管理等)取首字,保证后续新增菜单无需改前端就能有图标; * 3. 颜色:命中规则用规则色;兜底按名称哈希从色板取色,同一个菜单名永远同一个颜色。 */ // 已知菜单的关键字与颜色(顺序敏感:如「离职交接单」要先于「离职」、「补卡」要先于「打卡」) const KEYWORD_RULES = [ { keywords: ["工资", "薪资", "薪酬"], char: "薪", color: "#f59a23" }, { keywords: ["档案"], char: "档", color: "#9270ca" }, { keywords: ["交接"], char: "交", color: "#4b7902" }, { keywords: ["离职"], char: "离", color: "#ee7a6e" }, { keywords: ["调岗", "异动"], char: "岗", color: "#7262fd" }, { keywords: ["转正"], char: "正", color: "#269a99" }, { keywords: ["入职"], char: "入", color: "#07a44d" }, { keywords: ["请假"], char: "假", color: "#6e7bf2" }, { keywords: ["补卡"], char: "卡", color: "#4fa8ee" }, { keywords: ["打卡"], char: "打", color: "#157a2c" }, { keywords: ["排班"], char: "调", color: "#8f7bf0" }, { keywords: ["加班"], char: "班", color: "#4fc08d" }, { keywords: ["外勤", "外出"], char: "外", color: "#93a0b4" }, { keywords: ["出差"], char: "差", color: "#f2b63c" }, { keywords: ["考勤"], char: "勤", color: "#157a2c" }, { keywords: ["质检"], char: "质", color: "#269a99" }, { keywords: ["不合格"], char: "不", color: "#ee7a6e" }, { keywords: ["申请"], char: "申", color: "#157a2c" }, { keywords: ["审批", "待办"], char: "批", color: "#f59a23" }, ]; // 兜底取字时依次去掉的通用后缀(循环去,直到不再变化) const STRIP_SUFFIXES = ["申请", "办理", "管理", "记录", "审批", "工单", "清单", "列表", "单"]; // 兜底配色板:新菜单哈希取色,视觉上与主题绿协调 const PALETTE = [ "#157a2c", "#2699a9", "#9270ca", "#61cdbb", "#f2b63c", "#6e7bf2", "#ee7a6e", "#4b7902", ]; export function hexToRgba(hex, alpha) { const value = String(hex || "").replace("#", ""); if (value.length !== 6) return hex; const r = parseInt(value.slice(0, 2), 16); const g = parseInt(value.slice(2, 4), 16); const b = parseInt(value.slice(4, 6), 16); return `rgba(${r}, ${g}, ${b}, ${alpha})`; } export function stripMenuSuffixes(name) { let result = String(name || "").trim(); let changed = true; while (changed && result.length > 1) { changed = false; for (const suffix of STRIP_SUFFIXES) { if (result.length > suffix.length && result.endsWith(suffix)) { result = result.slice(0, -suffix.length); changed = true; } } } return result; } function hashName(name) { const text = String(name || ""); let hash = 0; for (let i = 0; i < text.length; i += 1) { hash = (hash * 31 + text.charCodeAt(i)) >>> 0; } return hash; } /** * @param {string} name 菜单名称(如「离职交接单」「保养工单」) * @returns {{ char: string, color: string, bg: string }} */ export function resolveMenuIcon(name) { const text = String(name || "").trim(); if (!text) return { char: "·", color: "#93a0b4", bg: hexToRgba("#93a0b4", 0.14) }; for (const rule of KEYWORD_RULES) { if (rule.keywords.some((k) => text.indexOf(k) >= 0)) { return { char: rule.char, color: rule.color, bg: hexToRgba(rule.color, 0.14) }; } } // 兜底:去通用后缀取首字(维修工单→维、保养工单→保、备品备件申请单→备) const stem = stripMenuSuffixes(text); const char = (stem || text).charAt(0); const color = PALETTE[hashName(text) % PALETTE.length]; return { char, color, bg: hexToRgba(color, 0.14) }; }