513 lines
33 KiB
JavaScript
513 lines
33 KiB
JavaScript
/**
|
||
* 名片 DIY 组件 · 共享设计系统层
|
||
*
|
||
* 目标(UI Designer 设计系统先行原则):
|
||
* - 4 个组件共用同一套 token(颜色/字阶/间距/圆角/阴影/动效时长)
|
||
* - 通用 helpers(px2rpx/头像兜底色/显隐开关解析)集中一处, 避免组件间漂移
|
||
* - 严格遵循小程序规范: 禁 CSS 变量, 动态样式一律 $util.objToStyle, 单位 rpx
|
||
*
|
||
* 注: 本文件为纯 JS(无 wxss), 小程序/H5 通用。
|
||
*/
|
||
|
||
export const CardTokens = {
|
||
color: {
|
||
primary: '#2F6BFF', // 主题默认蓝(名片 wap 公开页同色系)
|
||
text: '#1F2329', // 主文字
|
||
textSub: '#8A8F99', // 次文字(公司/职位)
|
||
textLabel: '#B0B4BC', // 字段标签
|
||
line: '#EEF0F3', // 分割线
|
||
bg: '#FFFFFF', // 卡片底
|
||
skeleton: '#F2F3F5', // 骨架块
|
||
skeletonHi: '#E8EAEE', // 骨架高亮扫过
|
||
scrim: 'rgba(15, 23, 42, 0.45)', // 封面图遮罩(对比度保障)
|
||
},
|
||
radius: {
|
||
sm: 8,
|
||
md: 16,
|
||
lg: 24,
|
||
pill: 999,
|
||
},
|
||
/** 字阶(px, 输出时 ×2 转 rpx) */
|
||
fontSize: {
|
||
display: 40, // 入口主标题
|
||
title: 32, // 分组标题/名片姓名
|
||
name: 30,
|
||
meta: 24, // 公司/职位/字段值
|
||
caption: 22, // 辅助说明
|
||
},
|
||
/** 间距(px) */
|
||
space: {
|
||
xs: 4,
|
||
sm: 8,
|
||
md: 12,
|
||
lg: 16,
|
||
xl: 20,
|
||
xxl: 24,
|
||
},
|
||
/** 阴影 */
|
||
shadow: {
|
||
card: '0 4rpx 16rpx rgba(15, 23, 42, 0.06)',
|
||
raised: '0 8rpx 24rpx rgba(15, 23, 42, 0.10)',
|
||
},
|
||
/** 动效时长(ms) */
|
||
duration: {
|
||
fast: 150,
|
||
normal: 240,
|
||
slow: 320,
|
||
},
|
||
/** 最小触达尺寸(rpx, 小程序可点性下限 44px≈88rpx, 芯片取 72rpx 高度) */
|
||
touch: {
|
||
chipHeight: 72,
|
||
btnHeight: 80,
|
||
avatar: 104,
|
||
},
|
||
};
|
||
|
||
/** px → rpx(组件配置字段均为 px 口径) */
|
||
export function px2rpx(v) {
|
||
const n = Number(v);
|
||
return isNaN(n) ? 0 : Math.round(n * 2);
|
||
}
|
||
|
||
/**
|
||
* 显隐开关解析: 兼容 true/false、1/0、"1"/"0"、"true"/"false"
|
||
* 未下发(undefined)时返回 fallback(由调用方决定默认值)
|
||
*/
|
||
export function flagOn(v, fallback) {
|
||
if (v === undefined || v === null || v === '') return fallback;
|
||
if (v === true || v === 1 || v === '1' || v === 'true') return true;
|
||
if (v === false || v === 0 || v === '0' || v === 'false') return false;
|
||
return !!v;
|
||
}
|
||
|
||
/** 姓名首字兜底(头像无图时) */
|
||
export function initialOf(name) {
|
||
const s = String(name || '').trim();
|
||
return s ? s.charAt(0) : '名';
|
||
}
|
||
|
||
/** 由字符串确定性派生色相(同名同色, 列表视觉稳定) */
|
||
export function hueOf(str) {
|
||
const s = String(str || '');
|
||
let h = 0;
|
||
for (let i = 0; i < s.length; i++) {
|
||
h = (h * 31 + s.charCodeAt(i)) % 360;
|
||
}
|
||
return h;
|
||
}
|
||
|
||
/** 头像兜底底色: 主题色优先, 否则按名片确定性色相 */
|
||
export function avatarBg(card) {
|
||
const theme = card && card.theme_color;
|
||
if (theme) return theme;
|
||
const h = hueOf((card && (card.card_id || card.name)) || 'card');
|
||
return 'hsl(' + h + ', 62%, 55%)';
|
||
}
|
||
|
||
/** 主题渐变(头部饰条/主按钮): 有渐变末色用双色, 否则单色 */
|
||
export function themeGradient(card, angle) {
|
||
const theme = (card && card.theme_color) || CardTokens.color.primary;
|
||
const end = card && card.gradient_end;
|
||
const deg = (card && card.gradient_angle) || angle || 135;
|
||
return end
|
||
? 'linear-gradient(' + deg + 'deg, ' + theme + ' 0%, ' + end + ' 100%)'
|
||
: 'linear-gradient(' + deg + 'deg, ' + theme + ' 20%, ' + theme + ' 100%)';
|
||
}
|
||
|
||
/** 颜色压暗(ratio 0~1): 用于「渐变圆角」在名片仅单主题色时生成可见明暗渐变终止色。
|
||
* 纯函数, 非法输入原样返回(不影响既有主题色渲染) */
|
||
export function darkenColor(hex, ratio) {
|
||
const r = ratio < 0 ? 0 : (ratio > 1 ? 1 : ratio);
|
||
const h = String(hex || '').replace('#', '');
|
||
if (!/^[0-9a-fA-F]{6}$/.test(h)) return hex;
|
||
const n = parseInt(h, 16);
|
||
const cr = Math.max(0, Math.round(((n >> 16) & 0xff) * (1 - r)));
|
||
const cg = Math.max(0, Math.round(((n >> 8) & 0xff) * (1 - r)));
|
||
const cb = Math.max(0, Math.round((n & 0xff) * (1 - r)));
|
||
return '#' + ((1 << 24) + (cr << 16) + (cg << 8) + cb).toString(16).slice(1);
|
||
}
|
||
|
||
/** 安全数字(配置字段) */
|
||
export function num(v, dft) {
|
||
const n = Number(v);
|
||
return isNaN(n) ? dft : n;
|
||
}
|
||
|
||
/** 字段行: 过滤空值, 限制条数(其余折叠) */
|
||
export function visibleRows(rows, max) {
|
||
const list = Array.isArray(rows) ? rows.filter(r => r && String(r.value || '').trim() !== '') : [];
|
||
if (max && max > 0 && list.length > max) {
|
||
return { shown: list.slice(0, max), rest: list.slice(max), hasMore: true };
|
||
}
|
||
return { shown: list, rest: [], hasMore: false };
|
||
}
|
||
|
||
/**
|
||
* 资料行图标(参考图: 电话/微信/地址带头像, 邮箱/网站/自定义无字形→退化为文字标签)
|
||
* 口径与 pages_tool/card/detail.vue 的 fieldRows 映射一致(同 glyph 同规则)
|
||
*/
|
||
const ROW_ICONS = {
|
||
mobile: 'icon-dianhua',
|
||
phone: 'icon-dianhua',
|
||
wechat: 'icon-weixin',
|
||
address: 'icon-dizhi',
|
||
// 方案1(2026-10-22): 邮箱图标 —— 需同时在 iconfont 字库补充信封字形 `icon-youxiang`
|
||
// (见 common/css/iconfont.css), 字库更新前该行会渲染空白, 故上线前必须配套更新字库。
|
||
// field 键名多写几套兜底(后端可能用 email / mail / 中文「邮箱」), 命中其一即可
|
||
email: 'icon-youxiang',
|
||
mail: 'icon-youxiang',
|
||
'邮箱': 'icon-youxiang',
|
||
// 网站/链接行(2026-10-22): 字库已有链条字形 icon-fuzhilianjie(\e03f), 直接复用
|
||
website: 'icon-fuzhilianjie',
|
||
link: 'icon-fuzhilianjie',
|
||
url: 'icon-fuzhilianjie',
|
||
'网站': 'icon-fuzhilianjie',
|
||
};
|
||
export function rowIconFor(field) {
|
||
return ROW_ICONS[String(field || '')] || '';
|
||
}
|
||
|
||
/**
|
||
* 资料行 CSS 描边图标(2.17.1): iconfont 无邮箱/网站字形, 同宫格口径
|
||
* 用纯 view 画信封/线框地球(与 ACT_GRID_ICONS.css 同一套图形, 尺寸缩小)
|
||
*/
|
||
const ROW_CSS_ICONS = {
|
||
email: 'mail',
|
||
website: 'web',
|
||
};
|
||
export function rowCssIconFor(field) {
|
||
return ROW_CSS_ICONS[String(field || '')] || '';
|
||
}
|
||
|
||
/**
|
||
* 资料行点击动作: 由服务端下发的 href 派生(tel:/mailto:/url/空)
|
||
* tel: → 拨号 mailto: → 邮箱(H5 调起 / 其它端复制引导)
|
||
* http(s) → web(跳转网站, 经 web-view 承载页) 空 → 无动作(纯展示, 如地址/自定义)
|
||
* 返回 null 表示不可点; type 取值与父组件 doAct 的 tel/mail/web/copy 分支对齐
|
||
*/
|
||
export function rowActOf(row) {
|
||
const href = String((row && row.href) || '').trim();
|
||
const value = String((row && row.value) || '').trim();
|
||
if (!href || !value) return null;
|
||
if (/^tel:/i.test(href)) return { type: 'tel', value: value };
|
||
if (/^mailto:/i.test(href)) return { type: 'mail', value: value };
|
||
if (/^https?:\/\//i.test(href)) return { type: 'web', value: value };
|
||
return { type: 'copy', value: value };
|
||
}
|
||
|
||
/** 二维码位置(四档): rows 资料行下方(默认) / head 头部右侧 / corner 右上角浮层 / bottom 主按钮上方 */
|
||
export const QR_POSITIONS = ['rows', 'head', 'corner', 'bottom'];
|
||
/** 位置档位中文别名(面板下拉若直接存中文档名, 归一化时兼容) */
|
||
const QR_POSITION_ALIAS = {
|
||
'资料行下方': 'rows',
|
||
'头部右侧': 'head',
|
||
'右上角浮层': 'corner',
|
||
'主按钮上方': 'bottom',
|
||
};
|
||
export function normalizeQrPosition(v) {
|
||
const s = String(v || '').trim();
|
||
if (QR_POSITIONS.indexOf(s) >= 0) return s;
|
||
return QR_POSITION_ALIAS[s] || 'rows';
|
||
}
|
||
/** 各档位的二维码边长(rpx): 头部右侧紧凑, 浮层带衬底, 行下/底部大图便于扫 */
|
||
/** 二维码默认尺寸(rpx); 面板「二维码尺寸」留空/0 = 按位自动取此档。
|
||
* rows/bottom(流内大码)240, head(头部右侧)200 —— 以 2026-10-22 用户参考图(C 端运行效果)定稿;
|
||
* corner(右上浮层, 占 Logo 位)空间受限保持 120 */
|
||
export const QR_SIZES = { rows: 240, head: 200, corner: 120, bottom: 240 };
|
||
|
||
/**
|
||
* 二维码外观样式(四档): plain 原图 / card 白卡描边(默认) / theme 主题色底 /
|
||
* gradient 渐变描边(码芯白底衬底, 保可扫)
|
||
* 多彩体现在装饰层(主题色/渐变), 码本身保持黑白 —— 可扫性优先
|
||
*/
|
||
export const QR_STYLES = ['plain', 'card', 'theme', 'gradient'];
|
||
/** 外观档位中文别名(面板下拉若直接存中文档名, 归一化时兼容) */
|
||
const QR_STYLE_ALIAS = {
|
||
'原图': 'plain',
|
||
'白卡描边': 'card',
|
||
'主题色衬底': 'theme',
|
||
'渐变圆角': 'gradient',
|
||
};
|
||
export function normalizeQrStyle(v) {
|
||
const s = String(v || '').trim();
|
||
if (QR_STYLES.indexOf(s) >= 0) return s;
|
||
return QR_STYLE_ALIAS[s] || 'card';
|
||
}
|
||
|
||
/** 二维码边长: 自定义 > 0 时用自定义(px), 否则按档位自动(rpx) */
|
||
export function resolveQrSize(position, custom) {
|
||
const n = Number(custom);
|
||
if (!isNaN(n) && n > 0) return n * 2;
|
||
return QR_SIZES[position] || QR_SIZES.rows;
|
||
}
|
||
|
||
/** 微调位移: 任一轴非零才输出 transform(WXSS 支持 translate; 不支持 calc) */
|
||
export function qrTransform(offsetX, offsetY) {
|
||
const x = Number(offsetX) || 0;
|
||
const y = Number(offsetY) || 0;
|
||
if (x === 0 && y === 0) return '';
|
||
return 'translate(' + (x * 2) + 'rpx, ' + (y * 2) + 'rpx)';
|
||
}
|
||
|
||
/**
|
||
* 头部形态(2.17): plain 白卡头部(历史默认) / theme 主题渐变整块头部
|
||
* theme 档: 渐变铺头部 + 装饰圆 + 姓名副行白字(headNameColor/headSubColor 可覆盖)
|
||
*/
|
||
export const HEAD_STYLES = ['plain', 'theme'];
|
||
export function normalizeHeadStyle(v) {
|
||
const s = String(v || '').trim();
|
||
return HEAD_STYLES.indexOf(s) >= 0 ? s : 'plain';
|
||
}
|
||
|
||
/**
|
||
* 快捷操作布局(2.17): chips 文字胶囊(历史默认) / grid 图标宫格
|
||
* grid 档图标: 有字形用 iconfont, 无字形退化为动作首字(与头像首字同构)
|
||
* 注: 小程序端 iconfont 暂无邮件/网站字形(iconfont.css 无 youxiang/wangzhan),
|
||
* 扩展字形后补此表即可, 无需改组件
|
||
*/
|
||
export const ACTS_LAYOUTS = ['chips', 'grid'];
|
||
export function normalizeActsLayout(v) {
|
||
const s = String(v || '').trim();
|
||
return ACTS_LAYOUTS.indexOf(s) >= 0 ? s : 'chips';
|
||
}
|
||
|
||
/**
|
||
* 宫格图标(2.18.1): 与 wap 公开页 detail 快捷操作同源的 SVG 线稿
|
||
* (路径逐一取自 wap/view/index/detail.html quickGrid, 保证两端观感一致)
|
||
* mode=stroke: 描边线稿(颜色动态跟随配色档/每按钮定制)
|
||
* mode=fill: 实心填充(wx 微信绿 / save 通讯录橙 —— detail 同款固定色)
|
||
* 渲染: svgLineIconBg 生成 data-URI 作为 background-image(H5+小程序通用,
|
||
* WXSS 对 base64 data URI 的 background-image 原生支持)
|
||
*/
|
||
export const WAP_LINE_ICONS = {
|
||
tel: {
|
||
mode: 'stroke',
|
||
body: '<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z"/>',
|
||
},
|
||
wx: {
|
||
mode: 'fill',
|
||
color: '#07C160',
|
||
body: '<path d="M8.69 4C4.98 4 2 6.46 2 9.5c0 1.77 1.05 3.35 2.68 4.4l-.67 2.02 2.35-1.18c.75.2 1.53.31 2.33.31h.28a5.42 5.42 0 0 1-.22-1.55c0-3.02 2.94-5.46 6.56-5.46h.28C15.03 5.9 12.14 4 8.69 4zM6.28 8.05a.94.94 0 1 1 0-1.88.94.94 0 0 1 0 1.88zm4.82 0a.94.94 0 1 1 0-1.88.94.94 0 0 1 0 1.88zM22 13.5c0-2.6-2.6-4.7-5.81-4.7s-5.81 2.1-5.81 4.7 2.6 4.7 5.81 4.7c.68 0 1.33-.09 1.94-.26l1.97 1-.57-1.7C20.99 16.36 22 15.02 22 13.5zm-7.55-.94a.8.8 0 1 1 0-1.6.8.8 0 0 1 0 1.6zm3.48 0a.8.8 0 1 1 0-1.6.8.8 0 0 1 0 1.6z"/>',
|
||
},
|
||
mail: {
|
||
mode: 'stroke',
|
||
body: '<rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/>',
|
||
},
|
||
web: {
|
||
mode: 'stroke',
|
||
body: '<circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>',
|
||
},
|
||
save: {
|
||
mode: 'stroke',
|
||
color: '#FF9500',
|
||
body: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M19 8v6M22 11h-6"/>',
|
||
},
|
||
};
|
||
|
||
/** 纯 ASCII base64(SVG 路径/颜色均为 ASCII, 小程序无 btoa 自带实现) */
|
||
const B64T = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||
function b64ascii(s) {
|
||
let out = '';
|
||
for (let i = 0; i < s.length; i += 3) {
|
||
const a = s.charCodeAt(i);
|
||
const b = s.charCodeAt(i + 1);
|
||
const c = s.charCodeAt(i + 2);
|
||
out += B64T[a >> 2]
|
||
+ B64T[((a & 3) << 4) | ((isNaN(b) ? 0 : b) >> 4)]
|
||
+ (isNaN(b) ? '=' : B64T[((b & 15) << 2) | ((isNaN(c) ? 0 : c) >> 6)])
|
||
+ (isNaN(c) ? '=' : B64T[c & 63]);
|
||
}
|
||
return out;
|
||
}
|
||
|
||
/**
|
||
* 宫格 SVG 图标 → background-image 值(data URI, 颜色内嵌)
|
||
* color 仅对 stroke 模式生效; fill 模式用 WAP_LINE_ICONS 内置固定色
|
||
*/
|
||
export function svgLineIconBg(type, color) {
|
||
const ico = WAP_LINE_ICONS[String(type || '')];
|
||
if (!ico) return '';
|
||
const attrs = ico.mode === 'fill'
|
||
? 'fill="' + (ico.color || '#07C160') + '"'
|
||
: 'fill="none" stroke="' + (color || '#2F6BFF') + '" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"';
|
||
const svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" ' + attrs + '>' + ico.body + '</svg>';
|
||
return 'url("data:image/svg+xml;base64,' + b64ascii(svg) + '")';
|
||
}
|
||
|
||
/**
|
||
* 宫格配色(2.17): preset 多彩预设(参考图: 浅底+深字五色) / theme 主题色
|
||
* theme 档浅底由 hexToRgba(theme, 0.12) 动态派生 —— WXSS 无 color-mix, JS 端换算
|
||
*/
|
||
export const ACT_GRID_PRESETS = {
|
||
tel: { bg: '#E1F5EE', fg: '#0F6E56' },
|
||
wx: { bg: '#EAF3DE', fg: '#3B6D11' },
|
||
mail: { bg: '#E6F1FB', fg: '#185FA5' },
|
||
web: { bg: '#FAEEDA', fg: '#854F0B' },
|
||
save: { bg: '#F0E9FB', fg: '#5B3DA8' },
|
||
};
|
||
export const ACT_GRID_PRESET_FALLBACK = { bg: '#F2F3F5', fg: '#5F5E5A' };
|
||
|
||
export function normalizeActGridColorMode(v) {
|
||
const s = String(v || '').trim();
|
||
return s === 'theme' ? 'theme' : 'preset';
|
||
}
|
||
|
||
/**
|
||
* 快捷操作区位置(2.18): below 资料区下方(历史默认) / above 资料区上方
|
||
* 历史配置无此字段时回落 below —— 渲染零变化
|
||
*/
|
||
export const ACTS_POSITIONS = ['below', 'above'];
|
||
export function normalizeActsPosition(v) {
|
||
const s = String(v || '').trim();
|
||
return ACTS_POSITIONS.indexOf(s) >= 0 ? s : 'below';
|
||
}
|
||
|
||
/**
|
||
* 每按钮图标配置(2.18): actIcons = { type: { img, color, bg } }
|
||
* img 自定义图标图片(媒体库, 空=自动档: iconfont 字形 / CSS 描边图标)
|
||
* color 图标前景色(空=跟随 preset/theme 配色档)
|
||
* bg 图标浅底色(空=跟随 preset/theme 配色档)
|
||
* 仅 grid 宫格档消费; 任意字段留空即回落自动档(历史形态零变化)
|
||
*/
|
||
export function actIconCfgOf(actIcons, type) {
|
||
const cfg = actIcons && actIcons[String(type || '')];
|
||
if (!cfg || typeof cfg !== 'object') return {};
|
||
return cfg;
|
||
}
|
||
|
||
/** hex(#RRGGBB / #RGB) → rgba 字符串(宫格主题浅底/复制按钮浅底用); 非法入参返回 null */
|
||
export function hexToRgba(hex, alpha) {
|
||
const s = String(hex || '').trim().replace(/^#/, '');
|
||
let full = '';
|
||
if (/^[0-9a-fA-F]{6}$/.test(s)) {
|
||
full = s;
|
||
} else if (/^[0-9a-fA-F]{3}$/.test(s)) {
|
||
full = s[0] + s[0] + s[1] + s[1] + s[2] + s[2];
|
||
} else {
|
||
return null;
|
||
}
|
||
const n = parseInt(full, 16);
|
||
return 'rgba(' + ((n >> 16) & 255) + ',' + ((n >> 8) & 255) + ',' + (n & 255) + ',' + alpha + ')';
|
||
}
|
||
|
||
/* ------------------------------------------------------------------
|
||
* 2.19 快捷操作: 显示控制 + 文案改名 + 自定义动作 + 素材图标库
|
||
* ------------------------------------------------------------------ */
|
||
|
||
/** 默认动作类型序(图层面板行序与 C 端合并序一致) */
|
||
export const ACT_TYPES = ['tel', 'wx', 'mail', 'web', 'save'];
|
||
|
||
/** 自定义动作类型白名单(doAct 天然消费: tel/wx/mail/copy 分支 + else 兜底;
|
||
* 2.19.3 扩展: sms 短信/map 导航/link 页面链接(系统链接对话框)) */
|
||
export const ACT_ACTION_TYPES = ['tel', 'wx', 'mail', 'web', 'copy', 'sms', 'map', 'link'];
|
||
|
||
export const ACT_LABEL_DEFAULTS = {
|
||
tel: '打电话',
|
||
wx: '微信号',
|
||
mail: '发邮件',
|
||
web: '网站',
|
||
save: '存通讯录',
|
||
};
|
||
|
||
/** 默认动作次序(2.19.1): 历史渲染顺序 = 后端 quickActs 下发顺序 */
|
||
export const ACT_ORDER_DEFAULT = ['tel', 'wx', 'mail', 'web', 'save'];
|
||
|
||
/**
|
||
* 默认动作次序(2.19.1): actOrder = [type...] —— 只收白名单内类型并去重,
|
||
* 缺失类型按默认顺序尾补(保证 5 类型齐全), 非数组/全非法 = 历史顺序
|
||
*/
|
||
export function normalizeActOrder(order) {
|
||
const out = [];
|
||
(Array.isArray(order) ? order : []).forEach(k => {
|
||
const t = String(k || '');
|
||
if (ACT_TYPES.indexOf(t) >= 0 && out.indexOf(t) < 0) out.push(t);
|
||
});
|
||
ACT_ORDER_DEFAULT.forEach(t => {
|
||
if (out.indexOf(t) < 0) out.push(t);
|
||
});
|
||
return out;
|
||
}
|
||
|
||
/**
|
||
* 默认动作显隐(2.19): actVis = { type: 0|1 }, 缺键/非法 = 显示(1)
|
||
* 历史配置无此字段 → 全 1, 渲染零变化
|
||
*/
|
||
export function normalizeActVis(vis) {
|
||
const src = vis && typeof vis === 'object' ? vis : {};
|
||
const out = {};
|
||
ACT_TYPES.forEach(t => {
|
||
out[t] = String(src[t]) === '0' ? 0 : 1;
|
||
});
|
||
return out;
|
||
}
|
||
|
||
/**
|
||
* 默认动作删除(2.19.2): actDeleted = { type: 0|1 }, 1=已删除(C端不渲染, 面板行隐藏,
|
||
* 经「恢复」chips 找回)。与 actVis(眼睛临时隐藏)独立 —— 删除与隐藏 C 端均不渲染。
|
||
* 缺键/非法 = 未删除(0), 历史配置无此字段 → 零变化
|
||
*/
|
||
export function normalizeActDeleted(deleted) {
|
||
const src = deleted && typeof deleted === 'object' ? deleted : {};
|
||
const out = {};
|
||
ACT_TYPES.forEach(t => {
|
||
out[t] = String(src[t]) === '1' ? 1 : 0;
|
||
});
|
||
return out;
|
||
}
|
||
|
||
/**
|
||
* 默认动作文案(2.19): actLabels = { type: string }, 空/缺 = 内置默认
|
||
* 历史配置无此字段 → 全默认, 渲染零变化
|
||
*/
|
||
export function normalizeActLabels(labels) {
|
||
const src = labels && typeof labels === 'object' ? labels : {};
|
||
const out = {};
|
||
ACT_TYPES.forEach(t => {
|
||
const s = String(src[t] || '').trim();
|
||
out[t] = s || ACT_LABEL_DEFAULTS[t];
|
||
});
|
||
return out;
|
||
}
|
||
|
||
/**
|
||
* 自定义动作列表(2.19): customActs = [{ label, type, value, iconKey, img, color, bg, vis }]
|
||
* 白名单过滤 + 字段收敛(防御脏配置); 非数组/空 → [] (历史形态零变化)
|
||
*/
|
||
export function normalizeCustomActs(list) {
|
||
if (!Array.isArray(list)) return [];
|
||
return list.slice(0, 10).map((raw, i) => {
|
||
const it = raw && typeof raw === 'object' ? raw : {};
|
||
const type = ACT_ACTION_TYPES.indexOf(String(it.type || '')) >= 0 ? String(it.type) : 'copy';
|
||
return {
|
||
label: String(it.label || '').trim().slice(0, 6) || '自定义',
|
||
type: type,
|
||
value: String(it.value || '').trim().slice(0, 200),
|
||
iconKey: String(it.iconKey || '').trim(),
|
||
img: String(it.img || '').trim(),
|
||
color: String(it.color || '').trim(),
|
||
bg: String(it.bg || '').trim(),
|
||
vis: String(it.vis) === '0' ? 0 : 1,
|
||
custom: 1,
|
||
_i: i,
|
||
};
|
||
}).filter(it => it.label !== '自定义' || it.value || it.img || it.iconKey);
|
||
}
|
||
/**
|
||
* 2.19 素材图标库(C 端): 与后端 addon\card\service\CardIconLibrary 同源,
|
||
* 由 gen 脚本从 PHP 常量逐字节生成, 契约测试锁定同步。
|
||
* key 图标渲染优先级: img(媒体库) > iconKey(本库, 实时配色) > WAP_LINE_ICONS > 首字
|
||
*/
|
||
export const CARD_ICON_LIB = {"phone":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M6.62 10.79c1.44 2.83 3.76 5.15 6.59 6.59l2.2-2.2c.28-.28.67-.36 1.02-.25c1.12.37 2.32.57 3.57.57a1 1 0 0 1 1 1V20a1 1 0 0 1-1 1A17 17 0 0 1 3 4a1 1 0 0 1 1-1h3.5a1 1 0 0 1 1 1c0 1.25.2 2.45.57 3.57c.11.35.03.74-.25 1.02z\"/></svg>","sms":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M20 2a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H6l-4 4V4a2 2 0 0 1 2-2zM4 4v13.17L5.17 16H20V4zm2 3h12v2H6zm0 4h9v2H6z\"/></svg>","email":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2zm-2 0l-8 5l-8-5zm0 12H4V8l8 5l8-5z\"/></svg>","wechat":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M9.5 4C5.36 4 2 6.69 2 10c0 1.89 1.08 3.56 2.78 4.66L4 17l2.5-1.5c.89.31 1.87.5 2.91.5A5.2 5.2 0 0 1 9 14c0-3.31 3.13-6 7-6c.19 0 .38 0 .56.03C15.54 5.69 12.78 4 9.5 4m-3 2.5a1 1 0 0 1 1 1a1 1 0 0 1-1 1a1 1 0 0 1-1-1a1 1 0 0 1 1-1m5 0a1 1 0 0 1 1 1a1 1 0 0 1-1 1a1 1 0 0 1-1-1a1 1 0 0 1 1-1M16 9c-3.31 0-6 2.24-6 5s2.69 5 6 5c.67 0 1.31-.08 1.91-.25L20 20l-.62-1.87C20.95 17.22 22 15.71 22 14c0-2.76-2.69-5-6-5m-2 2.5a1 1 0 0 1 1 1a1 1 0 0 1-1 1a1 1 0 0 1-1-1a1 1 0 0 1 1-1m4 0a1 1 0 0 1 1 1a1 1 0 0 1-1 1a1 1 0 0 1-1-1a1 1 0 0 1 1-1\"/></svg>","whatsapp":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M12.04 2c-5.46 0-9.91 4.45-9.91 9.91c0 1.75.46 3.45 1.32 4.95L2.05 22l5.25-1.38c1.45.79 3.08 1.21 4.74 1.21c5.46 0 9.91-4.45 9.91-9.91c0-2.65-1.03-5.14-2.9-7.01A9.82 9.82 0 0 0 12.04 2m.01 1.67c2.2 0 4.26.86 5.82 2.42a8.23 8.23 0 0 1 2.41 5.83c0 4.54-3.7 8.23-8.24 8.23c-1.48 0-2.93-.39-4.19-1.15l-.3-.17l-3.12.82l.83-3.04l-.2-.32a8.2 8.2 0 0 1-1.26-4.38c.01-4.54 3.7-8.24 8.25-8.24M8.53 7.33c-.16 0-.43.06-.66.31c-.22.25-.87.86-.87 2.07c0 1.22.89 2.39 1 2.56c.14.17 1.76 2.67 4.25 3.73c.59.27 1.05.42 1.41.53c.59.19 1.13.16 1.56.1c.48-.07 1.46-.6 1.67-1.18s.21-1.07.15-1.18c-.07-.1-.23-.16-.48-.27c-.25-.14-1.47-.74-1.69-.82c-.23-.08-.37-.12-.56.12c-.16.25-.64.81-.78.97c-.15.17-.29.19-.53.07c-.26-.13-1.06-.39-2-1.23c-.74-.66-1.23-1.47-1.38-1.72c-.12-.24-.01-.39.11-.5c.11-.11.27-.29.37-.44c.13-.14.17-.25.25-.41c.08-.17.04-.31-.02-.43c-.06-.11-.56-1.35-.77-1.84c-.2-.48-.4-.42-.56-.43c-.14 0-.3-.01-.47-.01\"/></svg>","linkedin":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M19 3a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2zm-.5 15.5v-5.3a3.26 3.26 0 0 0-3.26-3.26c-.85 0-1.84.52-2.32 1.3v-1.11h-2.79v8.37h2.79v-4.93c0-.77.62-1.4 1.39-1.4a1.4 1.4 0 0 1 1.4 1.4v4.93zM6.88 8.56a1.68 1.68 0 0 0 1.68-1.68c0-.93-.75-1.69-1.68-1.69a1.69 1.69 0 0 0-1.69 1.69c0 .93.76 1.68 1.69 1.68m1.39 9.94v-8.37H5.5v8.37z\"/></svg>","weibo":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M9.82 13.87c1.07 0 1.95.87 1.95 1.95a1.95 1.95 0 0 1-1.95 1.95c-1.08 0-1.95-.88-1.95-1.95c0-1.08.87-1.95 1.95-1.95M14.5 3.34l.68-.03c3.76 0 6.82 3.06 6.82 6.82l-.05.82l-1.19-.37l.02-.45c0-3.09-2.51-5.6-5.6-5.6l-.35.01zm.82 2.89c2.06.07 3.73 1.77 3.76 3.83l-1.24-.38c-.19-1.12-1.06-2-2.17-2.18zM2 15.41c-.03-.61.07-2.77 2.95-5.44c3.4-3.16 4.87-2.92 4.87-2.92s3.18-.3 1.24 3.41h.07c.47-.5 1.49-1.25 3.56-1.46c2.08-.21 2.08 1.5 1.81 2.7c1.88.94 3.06 2.33 3.06 3.88c0 2.82-3.93 5.11-8.78 5.11h-.28c-3.5 0-6.5-1.27-7.79-3.1c-.46-.62-.71-1.3-.71-2.01zm7.82-3.49c-3.23 0-5.85 1.75-5.85 3.9s2.62 3.9 5.85 3.9s5.85-1.75 5.85-3.9s-2.62-3.9-5.85-3.9\"/></svg>","shortvideo":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M12 3v10.55A4 4 0 1 0 14 17V7h4V3m-8 16a2 2 0 1 1 2-2a2 2 0 0 1-2 2\"/></svg>","web":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M17.9 17.39c-.26-.8-1.01-1.39-1.9-1.39h-1v-3a1 1 0 0 0-1-1H8v-2h2a1 1 0 0 0 1-1V7h2a2 2 0 0 0 2-2v-.41a7.984 7.984 0 0 1 2.9 12.8M11 19.93c-3.95-.49-7-3.85-7-7.93c0-.62.08-1.22.21-1.79L9 15v1a2 2 0 0 0 2 2m1-16A10 10 0 0 0 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2\"/></svg>","location":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M12 11.5A2.5 2.5 0 0 1 9.5 9A2.5 2.5 0 0 1 12 6.5A2.5 2.5 0 0 1 14.5 9a2.5 2.5 0 0 1-2.5 2.5M12 2a7 7 0 0 0-7 7c0 5.25 7 13 7 13s7-7.75 7-13a7 7 0 0 0-7-7\"/></svg>","qrcode":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M3 11h2v2H3zm8-6h2v4h-2zm-2 6h4v4h-2v-2H9zm6 0h2v2h2v-2h2v2h-2v2h2v4h-2v2h-2v-2h-4v2h-2v-4h4v-2h2v-2h-2zm4 8v-4h-2v4zM15 3h6v6h-6zm2 2v2h2V5zM3 3h6v6H3zm2 2v2h2V5zM3 15h6v6H3zm2 2v2h2v-2z\"/></svg>","account":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M19 19H5V5h14m0-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2m-2.5 13.25c0-1.5-3-2.25-4.5-2.25s-4.5.75-4.5 2.25V17h9M12 12.25A2.25 2.25 0 0 0 14.25 10A2.25 2.25 0 0 0 12 7.75A2.25 2.25 0 0 0 9.75 10A2.25 2.25 0 0 0 12 12.25\"/></svg>","company":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M5 3v18h6v-3.5h2V21h6V3zm2 2h2v2H7zm4 0h2v2h-2zm4 0h2v2h-2zM7 9h2v2H7zm4 0h2v2h-2zm4 0h2v2h-2zm-8 4h2v2H7zm4 0h2v2h-2zm4 0h2v2h-2zm-8 4h2v2H7zm8 0h2v2h-2z\"/></svg>","job":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M20 6c.58 0 1.05.2 1.42.59c.38.41.58.86.58 1.41v11c0 .55-.2 1-.58 1.41c-.37.39-.84.59-1.42.59H4c-.58 0-1.05-.2-1.42-.59C2.2 20 2 19.55 2 19V8c0-.55.2-1 .58-1.41C2.95 6.2 3.42 6 4 6h4V4c0-.58.2-1.05.58-1.42C8.95 2.2 9.42 2 10 2h4c.58 0 1.05.2 1.42.58c.38.37.58.84.58 1.42v2zM4 8v11h16V8zm10-2V4h-4v2z\"/></svg>","handshake":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M21.71 8.71c1.25-1.25.68-2.71 0-3.42l-3-3c-1.26-1.25-2.71-.68-3.42 0L13.59 4H11C9.1 4 8 5 7.44 6.15L3 10.59v4l-.71.7c-1.25 1.26-.68 2.71 0 3.42l3 3c.54.54 1.12.74 1.67.74c.71 0 1.36-.35 1.75-.74l2.7-2.71H15c1.7 0 2.56-1.06 2.87-2.1c1.13-.3 1.75-1.16 2-2C21.42 14.5 22 13.03 22 12V9h-.59zM20 12c0 .45-.19 1-1 1h-1v1c0 .45-.19 1-1 1h-1v1c0 .45-.19 1-1 1h-4.41l-3.28 3.28c-.31.29-.49.12-.6.01l-2.99-2.98c-.29-.31-.12-.49-.01-.6L5 15.41v-4l2-2V11c0 1.21.8 3 3 3s3-1.79 3-3h7zm.29-4.71L18.59 9H11v2c0 .45-.19 1-1 1s-1-.55-1-1V8c0-.46.17-2 2-2h3.41l2.28-2.28c.31-.29.49-.12.6-.01l2.99 2.98c.29.31.12.49.01.6\"/></svg>","chart":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"m16 11.78l4.24-7.33l1.73 1l-5.23 9.05l-6.51-3.75L5.46 19H22v2H2V3h2v14.54L9.5 8z\"/></svg>","store":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"m18.36 9l.6 3H5.04l.6-3zM20 4H4v2h16zm0 3H4l-1 5v2h1v6h10v-6h4v6h2v-6h1v-2zM6 18v-4h6v4z\"/></svg>","calendar":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M19 19H5V8h14m0-5h-1V1h-2v2H8V1H6v2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2m-2.47 8.06L15.47 10l-4.88 4.88l-2.12-2.12l-1.06 1.06L10.59 17z\"/></svg>","document":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M6 2a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6zm0 2h7v5h5v11H6zm2 8v2h8v-2zm0 4v2h5v-2z\"/></svg>","shield":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M21 11c0 5.55-3.84 10.74-9 12c-5.16-1.26-9-6.45-9-12V5l9-4l9 4zm-9 10c3.75-1 7-5.46 7-9.78V6.3l-7-3.12L5 6.3v4.92C5 15.54 8.25 20 12 21m-2-4l-4-4l1.41-1.41L10 14.17l6.59-6.59L18 9\"/></svg>","image":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M19 19H5V5h14m0-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2m-5.04 9.29l-2.75 3.54l-1.96-2.36L6.5 17h11z\"/></svg>","video":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M15 8v8H5V8zm1-2H4a1 1 0 0 0-1 1v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-3.5l4 4v-11l-4 4V7a1 1 0 0 0-1-1\"/></svg>","play":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M12 20c-4.41 0-8-3.59-8-8s3.59-8 8-8s8 3.59 8 8s-3.59 8-8 8m0-18A10 10 0 0 0 2 12a10 10 0 0 0 10 10a10 10 0 0 0 10-10A10 10 0 0 0 12 2m-2 14.5l6-4.5l-6-4.5z\"/></svg>","camera":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M20 4h-3.17L15 2H9L7.17 4H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2m0 14H4V6h4.05l1.83-2h4.24l1.83 2H20zM12 7a5 5 0 0 0-5 5a5 5 0 0 0 5 5a5 5 0 0 0 5-5a5 5 0 0 0-5-5m0 8a3 3 0 0 1-3-3a3 3 0 0 1 3-3a3 3 0 0 1 3 3a3 3 0 0 1-3 3\"/></svg>","music":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M21 3v12.5a3.5 3.5 0 0 1-3.5 3.5a3.5 3.5 0 0 1-3.5-3.5a3.5 3.5 0 0 1 3.5-3.5c.54 0 1.05.12 1.5.34V6.47L9 8.6v8.9A3.5 3.5 0 0 1 5.5 21A3.5 3.5 0 0 1 2 17.5A3.5 3.5 0 0 1 5.5 14c.54 0 1.05.12 1.5.34V6z\"/></svg>","star":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"m12 15.39l-3.76 2.27l.99-4.28l-3.32-2.88l4.38-.37L12 6.09l1.71 4.04l4.38.37l-3.32 2.88l.99 4.28M22 9.24l-7.19-.61L12 2L9.19 8.63L2 9.24l5.45 4.73L5.82 21L12 17.27L18.18 21l-1.64-7.03z\"/></svg>","heart":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"m12.1 18.55l-.1.1l-.11-.1C7.14 14.24 4 11.39 4 8.5C4 6.5 5.5 5 7.5 5c1.54 0 3.04 1 3.57 2.36h1.86C13.46 6 14.96 5 16.5 5c2 0 3.5 1.5 3.5 3.5c0 2.89-3.14 5.74-7.9 10.05M16.5 3c-1.74 0-3.41.81-4.5 2.08C10.91 3.81 9.24 3 7.5 3C4.42 3 2 5.41 2 8.5c0 3.77 3.4 6.86 8.55 11.53L12 21.35l1.45-1.32C18.6 15.36 22 12.27 22 8.5C22 5.41 19.58 3 16.5 3\"/></svg>","gift":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M22 12v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2v-8a1 1 0 0 1-1-1V8a2 2 0 0 1 2-2h3.17A3 3 0 0 1 6 5a3 3 0 0 1 3-3c1 0 1.88.5 2.43 1.24v-.01L12 4l.57-.77v.01C13.12 2.5 14 2 15 2a3 3 0 0 1 3 3a3 3 0 0 1-.17 1H21a2 2 0 0 1 2 2v3a1 1 0 0 1-1 1M4 20h7v-8H4zm16 0v-8h-7v8zM9 4a1 1 0 0 0-1 1a1 1 0 0 0 1 1a1 1 0 0 0 1-1a1 1 0 0 0-1-1m6 0a1 1 0 0 0-1 1a1 1 0 0 0 1 1a1 1 0 0 0 1-1a1 1 0 0 0-1-1M3 8v2h8V8zm10 0v2h8V8z\"/></svg>","pay":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M20 8H4V6h16m0 12H4v-6h16m0-8H4c-1.11 0-2 .89-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2\"/></svg>","truck":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M18 18.5c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5s.67 1.5 1.5 1.5m1.5-9H17V12h4.46zM6 18.5c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5s.67 1.5 1.5 1.5M20 8l3 4v5h-2c0 1.66-1.34 3-3 3s-3-1.34-3-3H9c0 1.66-1.34 3-3 3s-3-1.34-3-3H1V6c0-1.11.89-2 2-2h14v4zM3 6v9h.76c.55-.61 1.35-1 2.24-1s1.69.39 2.24 1H15V6z\"/></svg>","book":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M21 4H3a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2M3 19V6h8v13zm18 0h-8V6h8zm-7-9.5h6V11h-6zm0 2.5h6v1.5h-6zm0 2.5h6V16h-6z\"/></svg>","share":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81c1.66 0 3-1.34 3-3s-1.34-3-3-3s-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66c0 1.61 1.31 2.91 2.92 2.91s2.92-1.3 2.92-2.91s-1.31-2.92-2.92-2.92M18 4c.55 0 1 .45 1 1s-.45 1-1 1s-1-.45-1-1s.45-1 1-1M6 13c-.55 0-1-.45-1-1s.45-1 1-1s1 .45 1 1s-.45 1-1 1m12 7c-.55 0-1-.45-1-1s.45-1 1-1s1 .45 1 1s-.45 1-1 1\"/></svg>","settings":"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\"><path fill=\"currentColor\" d=\"M12 8a4 4 0 0 1 4 4a4 4 0 0 1-4 4a4 4 0 0 1-4-4a4 4 0 0 1 4-4m0 2a2 2 0 0 0-2 2a2 2 0 0 0 2 2a2 2 0 0 0 2-2a2 2 0 0 0-2-2m-2 12c-.25 0-.46-.18-.5-.42l-.37-2.65c-.63-.25-1.17-.59-1.69-.99l-2.49 1.01c-.22.08-.49 0-.61-.22l-2-3.46a.493.493 0 0 1 .12-.64l2.11-1.66L4.5 12l.07-1l-2.11-1.63a.493.493 0 0 1-.12-.64l2-3.46c.12-.22.39-.31.61-.22l2.49 1c.52-.39 1.06-.73 1.69-.98l.37-2.65c.04-.24.25-.42.5-.42h4c.25 0 .46.18.5.42l.37 2.65c.63.25 1.17.59 1.69.98l2.49-1c.22-.09.49 0 .61.22l2 3.46c.13.22.07.49-.12.64L19.43 11l.07 1l-.07 1l2.11 1.63c.19.15.25.42.12.64l-2 3.46c-.12.22-.39.31-.61.22l-2.49-1c-.52.39-1.06.73-1.69.98l-.37 2.65c-.04.24-.25.42-.5.42zm1.25-18l-.37 2.61c-1.2.25-2.26.89-3.03 1.78L5.44 7.35l-.75 1.3L6.8 10.2a5.55 5.55 0 0 0 0 3.6l-2.12 1.56l.75 1.3l2.43-1.04c.77.88 1.82 1.52 3.01 1.76l.37 2.62h1.52l.37-2.61c1.19-.25 2.24-.89 3.01-1.77l2.43 1.04l.75-1.3l-2.12-1.55c.4-1.17.4-2.44 0-3.61l2.11-1.55l-.75-1.3l-2.41 1.04a5.42 5.42 0 0 0-3.03-1.77L12.75 4z\"/></svg>"};
|
||
|
||
/** 素材库图标 SVG 字符串(缺 key 返回空串) */
|
||
export function libIconSvg(key) {
|
||
return CARD_ICON_LIB[String(key || '')] || '';
|
||
}
|
||
|
||
/** 素材库图标 → data URI background-image 值(小程序无 btoa, 复用 b64ascii) */
|
||
export function libIconBg(key, color) {
|
||
const svg = libIconSvg(key);
|
||
if (!svg) return '';
|
||
return 'url("data:image/svg+xml;base64,' + b64ascii(svg.split('currentColor').join(color || '#2F6BFF')) + '")';
|
||
}
|