diff --git a/common/css/iconfont.css b/common/css/iconfont.css index 67d57ff..7ee6e62 100644 --- a/common/css/iconfont.css +++ b/common/css/iconfont.css @@ -460,4 +460,17 @@ } .icon-shuqing:before { content: "\e09b"; +} + +/* 邮箱信封图标(2026-10-22, 名片资料行方案1落地): + 字体包中暂无信封字形, 以内联 SVG 背景替代(content 置空避免渲染豆腐块)。 + 颜色取资料行文字标签默认色 #999999(与电话/地址图标观感一致); + 后续若 iconfont 字库补充了同名码点字形, 删除本条覆盖即可无缝切换 */ +.icon-youxiang:before { + content: ""; + display: inline-block; + width: 30rpx; + height: 30rpx; + vertical-align: middle; + background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23999999' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2' y='4' width='20' height='16' rx='2'/%3E%3Cpath d='m2 7 10 6 10-6'/%3E%3C/svg%3E") center/contain no-repeat; } \ No newline at end of file diff --git a/components-diy/diy-card-common.js b/components-diy/diy-card-common.js index 590d4ed..b5ba9cc 100644 --- a/components-diy/diy-card-common.js +++ b/components-diy/diy-card-common.js @@ -151,6 +151,12 @@ const ROW_ICONS = { 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', }; export function rowIconFor(field) { return ROW_ICONS[String(field || '')] || ''; diff --git a/components-diy/diy-card-show.vue b/components-diy/diy-card-show.vue index b1fda02..079d5bf 100644 --- a/components-diy/diy-card-show.vue +++ b/components-diy/diy-card-show.vue @@ -380,9 +380,10 @@ export default { }, }); }, - /** 资料行图标(字段无字形返回空串, 模板退化为文字标签) */ + /** 资料行图标: 面板「名片信息展示 → 资料行图标」(默认开; + * 关后全部退化为文字标签, 无字形的字段(邮箱/网站/自定义)也退化为文字标签) */ rowIcon(row) { - return rowIconFor(row && row.field); + return flagOn(this.c.showRowIcons, true) ? rowIconFor(row && row.field) : ''; }, /** 资料行是否可点: 服务端 href 派生得出动作 */ rowTapable(row) { diff --git a/tests/diy-card-ab-visual.test.js b/tests/diy-card-ab-visual.test.js index 2d11ff8..4c1d528 100644 --- a/tests/diy-card-ab-visual.test.js +++ b/tests/diy-card-ab-visual.test.js @@ -44,8 +44,10 @@ test('common: rowIconFor 字段→图标映射(无字形字段退化为空)', as assert.equal(m.rowIconFor('phone'), 'icon-dianhua'); assert.equal(m.rowIconFor('wechat'), 'icon-weixin'); assert.equal(m.rowIconFor('address'), 'icon-dizhi'); - // 无字形: 邮箱/网站/自定义/未知 → 空串(模板退化为文字标签) - assert.equal(m.rowIconFor('email'), ''); + // 邮箱: 方案1(2026-10-22)预留 icon-youxiang, 需字库补充信封字形后方能显示, 多键名兜底 + assert.equal(m.rowIconFor('email'), 'icon-youxiang'); + assert.equal(m.rowIconFor('mail'), 'icon-youxiang'); + // 仍无字形: 网站/自定义/未知 → 空串(模板退化为文字标签) assert.equal(m.rowIconFor('website'), ''); assert.equal(m.rowIconFor('custom'), ''); assert.equal(m.rowIconFor(''), ''); diff --git a/tests/diy-card-qr-style.test.js b/tests/diy-card-qr-style.test.js index 24cc748..21acffc 100644 --- a/tests/diy-card-qr-style.test.js +++ b/tests/diy-card-qr-style.test.js @@ -91,6 +91,31 @@ test('common: darkenColor 压暗生成可见渐变终止色', async () => { assert.equal(m.darkenColor('rgb(1,2,3)', 0.18), 'rgb(1,2,3)'); }); +test('common: rowIconFor 资料行图标字段映射(含方案1邮箱预留 icon-youxiang)', async () => { + const m = await import('../components-diy/diy-card-common.js'); + // 有字形的字段返回 iconfont 类 + assert.equal(m.rowIconFor('mobile'), 'icon-dianhua'); + assert.equal(m.rowIconFor('phone'), 'icon-dianhua'); + assert.equal(m.rowIconFor('wechat'), 'icon-weixin'); + assert.equal(m.rowIconFor('address'), 'icon-dizhi'); + // 邮箱: 方案1 预留 icon-youxiang(需字库补充信封字形后方能显示), 多键名兜底 + assert.equal(m.rowIconFor('email'), 'icon-youxiang'); + assert.equal(m.rowIconFor('mail'), 'icon-youxiang'); + assert.equal(m.rowIconFor('邮箱'), 'icon-youxiang'); + // 网站/自定义等仍无字形(退化文字标签) + assert.equal(m.rowIconFor('website'), ''); + assert.equal(m.rowIconFor('custom'), ''); + assert.equal(m.rowIconFor(''), ''); +}); + +test('common: iconfont.css 邮箱信封 SVG 覆盖(字库无字形期的降级实现)', () => { + const iconSrc = fs.readFileSync(path.resolve(__dirname, '../common/css/iconfont.css'), 'utf8'); + // 覆盖规则存在且 content 置空(避免渲染豆腐块), 尺寸/对齐与 row-icon 槽位匹配 + assert.match(iconSrc, /\.icon-youxiang:before \{[\s\S]*?content: "";[\s\S]*?width: 30rpx;[\s\S]*?height: 30rpx;/); + // 信封 SVG data-uri(描边风格, #999999 与资料行标签默认色一致) + assert.match(iconSrc, /\.icon-youxiang:before \{[\s\S]*?data:image\/svg\+xml,[\s\S]*?stroke='%23999999'[\s\S]*?%3E%3Crect x='2' y='4' width='20' height='16'[\s\S]*?center\/contain no-repeat/); +}); + test('common: resolveQrSize 自定义>档位自动, 单位 px→rpx(×2)', async () => { const m = await import('../components-diy/diy-card-common.js'); // 自定义尺寸(面板 px) × 2 = rpx @@ -264,6 +289,14 @@ test('show: 名片 Logo 开关(面板「名片设计资产 → 名片 Logo」, assert.match(showSrc, /\.card-logo \{[\s\S]*?width: 96rpx;[\s\S]*?margin-left: auto;/); }); +test('show: 资料行图标开关(面板「名片信息展示 → 资料行图标」, 默认开; 关后退化文字标签)', () => { + // 与 mini 基件 rowIcon 同口径: 开关 + 字段字形双重判定 + assert.match(showSrc, /rowIcon\(row\)\s*\{\s*return flagOn\(this\.c\.showRowIcons,\s*true\) \? rowIconFor\(row && row\.field\) : '';/); + // 模板: 有图标显图标, 无图标退化文字标签 + assert.match(showSrc, /<\/text>/); + assert.match(showSrc, /\{\{ row\.label \}\}<\/text>/); +}); + test('show: 发名片按钮开关(面板「底部按钮 → 发送名片按钮」, 默认显示; 文案消费 detailBtnText)', () => { assert.match(showSrc, /showSendBtn\(\)\s*\{\s*return flagOn\(this\.c\.showDetailBtn,\s*true\)/); assert.match(showSrc, /v-if="showSendBtn" class="show-send"/);