/** * 图标生成脚本:微信小程序不支持内联 ,因此把初设图里的 SVG symbol * 编译成 WXSS 的 background-image(base64 data-uri),按「名称 + 色调」生成类名。 * * 用法:node scripts/build-icons.mjs * 产出:src/styles/icons.scss */ import fs from "node:fs"; import path from "node:path"; const ROOT = path.resolve(import.meta.dirname, ".."); const OUT = path.join(ROOT, "src/styles/icons.scss"); /** 颜色 token(与 design-token.scss 保持一致) */ const TONES = { ink: "#14181C", ink2: "#6B7280", ink3: "#9CA3AF", white: "#FFFFFF", brand: "#00B578", brandDeep: "#009A67", accent: "#FF8A3D", accentDeep: "#C9550C", danger: "#E5484D", }; /** stroke 型图标:24×24 线性图标,stroke-width 1.9 */ const STROKE_ICONS = { pin: '', arrow: '', chev: '', back: '', phone: '', copy: '', gift: '', home: '', ticket: '', user: '', check: '', close: '', doc: '', shield: '', info: '', headset: '', logout: '', trash: '', share: '', clock: '', map: '', food: '', bed: '', peak: '', people: '', }; /** fill 型图标(实心) */ const FILL_ICONS = { wx: '', }; /** 需要生成的「图标 × 色调」组合(只生成用得到的,控制体积) */ const MATRIX = { pin: ["ink3", "brand", "white"], arrow: ["white", "brandDeep", "ink"], chev: ["ink3", "white", "brand", "ink2", "accentDeep", "brandDeep"], back: ["ink", "white", "brand"], phone: ["white", "brandDeep"], copy: ["brandDeep", "brand"], gift: ["white", "brandDeep", "accent"], home: ["ink3", "brand"], ticket: ["ink3", "brand", "accent", "white", "ink2", "accentDeep"], user: ["ink3", "brand", "white", "ink2"], check: ["white", "brand"], close: ["ink3", "white", "danger"], doc: ["ink2", "brand"], shield: ["ink2", "white", "brand"], info: ["ink2", "brand", "ink3"], headset: ["ink2", "brand"], logout: ["ink2", "white"], trash: ["ink2", "danger"], share: ["white"], clock: ["brand", "ink3", "white"], map: ["brandDeep", "white"], food: ["brandDeep", "accent", "white", "brand"], bed: ["accent", "brand", "white", "brandDeep"], peak: ["accent", "brand", "white"], people: ["ink3", "brand", "white", "brandDeep"], wx: ["white"], }; const buildSvg = (name, body, color, type) => { const style = type === "fill" ? `fill='${color}' stroke='none'` : `fill='none' stroke='${color}' stroke-width='1.9' stroke-linecap='round' stroke-linejoin='round'`; return `${body}`; }; const toDataUri = (svg) => `data:image/svg+xml;base64,${Buffer.from(svg, "utf8").toString("base64")}`; const lines = [ "/* 由 scripts/build-icons.mjs 生成,请勿手改 */", "", ".ai{display:block;flex:none;background-repeat:no-repeat;background-position:center;background-size:100% 100%}", "", ]; let count = 0; for (const [name, tones] of Object.entries(MATRIX)) { const type = FILL_ICONS[name] ? "fill" : "stroke"; const body = FILL_ICONS[name] || STROKE_ICONS[name]; if (!body) throw new Error(`未定义的图标:${name}`); for (const tone of tones) { const color = TONES[tone]; if (!color) throw new Error(`未定义的色调:${tone}`); if (type === "stroke") { // stroke 型图标 stroke-width 随字号缩放会变细,统一用 1.9 } const uri = toDataUri(buildSvg(name, body, color, type)); lines.push(`.ai-${name}.t-${tone}{background-image:url("${uri}")}`); count += 1; } } lines.push(""); fs.writeFileSync(OUT, lines.join("\n"), "utf8"); const kb = (fs.statSync(OUT).size / 1024).toFixed(1); console.log(`icons.scss 已生成:${count} 条规则 / ${kb} KB`);