chore:“发送名片”按钮功能正常
This commit is contained in:
@@ -114,6 +114,19 @@ export function themeGradient(card, angle) {
|
||||
: '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);
|
||||
@@ -160,9 +173,17 @@ export function rowActOf(row) {
|
||||
|
||||
/** 二维码位置(四档): 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();
|
||||
return QR_POSITIONS.indexOf(s) >= 0 ? s : 'rows';
|
||||
if (QR_POSITIONS.indexOf(s) >= 0) return s;
|
||||
return QR_POSITION_ALIAS[s] || 'rows';
|
||||
}
|
||||
/** 各档位的二维码边长(rpx): 头部右侧紧凑, 浮层带衬底, 行下/底部大图便于扫 */
|
||||
export const QR_SIZES = { rows: 180, head: 120, corner: 120, bottom: 180 };
|
||||
@@ -173,9 +194,17 @@ export const QR_SIZES = { rows: 180, head: 120, corner: 120, bottom: 180 };
|
||||
* 多彩体现在装饰层(主题色/渐变), 码本身保持黑白 —— 可扫性优先
|
||||
*/
|
||||
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();
|
||||
return QR_STYLES.indexOf(s) >= 0 ? s : 'card';
|
||||
if (QR_STYLES.indexOf(s) >= 0) return s;
|
||||
return QR_STYLE_ALIAS[s] || 'card';
|
||||
}
|
||||
|
||||
/** 二维码边长: 自定义 > 0 时用自定义(px), 否则按档位自动(rpx) */
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
// 数字名片 · 名片迷你卡基件(show/self 共用, 设计系统单一事实来源)
|
||||
// 消费名片自身设计资产: cover 封面 / logo / theme_color / 渐变末色与角度 / card_title
|
||||
import {
|
||||
CardTokens, px2rpx, flagOn, num, initialOf, avatarBg, themeGradient, visibleRows,
|
||||
CardTokens, px2rpx, flagOn, num, initialOf, avatarBg, themeGradient, darkenColor, visibleRows,
|
||||
rowIconFor, rowActOf, normalizeQrPosition, normalizeQrStyle, resolveQrSize, qrTransform, QR_SIZES,
|
||||
} from './diy-card-common.js';
|
||||
|
||||
@@ -229,7 +229,8 @@ export default {
|
||||
style.borderRadius = '12rpx';
|
||||
style.padding = '10rpx';
|
||||
} else if (this.qrStyle === 'gradient') {
|
||||
style.background = themeGradient(this.card);
|
||||
const end = (this.card && this.card.gradient_end) || darkenColor(theme, 0.18);
|
||||
style.background = 'linear-gradient(135deg, ' + theme + ' 0%, ' + end + ' 100%)';
|
||||
style.borderRadius = '16rpx';
|
||||
style.padding = '6rpx';
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<text v-if="jobLine" class="card-job" :style="subStyle">{{ jobLine }}</text>
|
||||
</view>
|
||||
<!-- 头部右侧档 -->
|
||||
<view v-if="showQrHead" class="card-qr" :style="qrShiftStyle" hover-class="btn-press" @tap.stop="openCardShare">
|
||||
<view v-if="showQrHead" class="card-qr" :style="[qrBlockStyle, qrShiftStyle]" hover-class="btn-press" @tap.stop="openCardShare">
|
||||
<image class="qr-img" :style="qrImgStyle" :src="$util.img(card.qr_url)" mode="aspectFit" />
|
||||
<text class="qr-tip" :style="subStyle">{{ qrTipText }}</text>
|
||||
</view>
|
||||
@@ -53,13 +53,13 @@
|
||||
</view>
|
||||
|
||||
<!-- 资料行下方档(默认): 大码居中, 资料行随之下移 -->
|
||||
<view v-if="showQrRows" class="qr-center" :style="qrShiftStyle" hover-class="btn-press" @tap.stop="openCardShare">
|
||||
<view v-if="showQrRows" class="qr-center" :style="[qrBlockStyle, qrShiftStyle]" hover-class="btn-press" @tap.stop="openCardShare">
|
||||
<image class="qr-img-c" :style="qrImgStyle" :src="$util.img(card.qr_url)" mode="aspectFit" />
|
||||
<text class="qr-tip-c" :style="subStyle">{{ qrTipText }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 右上角浮层档: 白底描边小卡浮在名片右上角, 点击弹分享抽屉 -->
|
||||
<view v-if="showQrCorner" class="card-qr-corner" :style="qrShiftStyle" hover-class="btn-press" @tap.stop="openCardShare">
|
||||
<view v-if="showQrCorner" class="card-qr-corner" :style="[qrBlockStyle, qrShiftStyle]" hover-class="btn-press" @tap.stop="openCardShare">
|
||||
<image class="qr-img-c" :style="qrImgStyle" :src="$util.img(card.qr_url)" mode="aspectFit" />
|
||||
<text class="qr-tip-corner" :style="subStyle">{{ qrTipText }}</text>
|
||||
</view>
|
||||
@@ -75,14 +75,14 @@
|
||||
</view>
|
||||
|
||||
<!-- 主按钮上方档: 大码居中卡于白卡与发名片按钮之间 -->
|
||||
<view v-if="showQrBottom" class="qr-center qr-center-bottom" :style="qrShiftStyle" hover-class="btn-press" @tap.stop="openCardShare">
|
||||
<view v-if="showQrBottom" class="qr-center qr-center-bottom" :style="[qrBlockStyle, qrShiftStyle]" hover-class="btn-press" @tap.stop="openCardShare">
|
||||
<image class="qr-img-c" :style="qrImgStyle" :src="$util.img(card.qr_url)" mode="aspectFit" />
|
||||
<text class="qr-tip-c" :style="subStyle">{{ qrTipText }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 发名片: 卡片外下方胶囊(详情页 send-bar 同款观感, 主题渐变色) -->
|
||||
<view class="show-send" :style="sendBtnStyle" hover-class="btn-press" @tap="onSend(card)">
|
||||
<text class="show-send-text">发名片</text>
|
||||
<!-- 发名片: 卡片外下方胶囊(详情页 send-bar 同款观感, 主题渐变色; 面板「发送名片按钮」可关) -->
|
||||
<view v-if="showSendBtn" class="show-send" :style="sendBtnStyle" hover-class="btn-press" @tap="onSend(card)">
|
||||
<text class="show-send-text">{{ sendBtnText }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -120,8 +120,8 @@
|
||||
// 自绘参考图版式 —— 主题色顶栏衬底 + 白卡压边 + 右上小二维码(预览名片) +
|
||||
// 图标资料行 + 卡外「发名片」胶囊。面板仅颜色/底色/圆角/阴影/行数继续生效。
|
||||
import {
|
||||
CardTokens, px2rpx, num, flagOn, initialOf, avatarBg, themeGradient, visibleRows, rowIconFor, rowActOf,
|
||||
resolveQrSize, normalizeQrPosition, qrTransform,
|
||||
CardTokens, px2rpx, num, flagOn, initialOf, avatarBg, themeGradient, darkenColor, visibleRows, rowIconFor, rowActOf,
|
||||
resolveQrSize, normalizeQrPosition, qrTransform, normalizeQrStyle,
|
||||
} from './diy-card-common.js';
|
||||
|
||||
export default {
|
||||
@@ -184,6 +184,35 @@ export default {
|
||||
const t = qrTransform(this.c.qrOffsetX, this.c.qrOffsetY);
|
||||
return t ? this.$util.objToStyle({ transform: t }) : '';
|
||||
},
|
||||
/** 二维码外观(四档): plain 原图 / card 白卡描边 / theme 主题色衬底 / gradient 渐变圆角。
|
||||
* 多彩只装饰外围衬底, 码本身保持黑白保可扫(与 mini 基件 qrBlockStyle 同口径) */
|
||||
qrStyle() {
|
||||
return normalizeQrStyle(this.c.qrStyle);
|
||||
},
|
||||
qrBlockStyle() {
|
||||
const style = {};
|
||||
const theme = (this.card && this.card.theme_color) || CardTokens.color.primary;
|
||||
if (this.qrStyle === 'card') {
|
||||
style.background = '#ffffff';
|
||||
style.border = '2rpx solid #EEF0F3';
|
||||
style.borderRadius = '12rpx';
|
||||
style.padding = '10rpx';
|
||||
} else if (this.qrStyle === 'theme') {
|
||||
style.background = theme;
|
||||
style.borderRadius = '12rpx';
|
||||
style.padding = '10rpx';
|
||||
} else if (this.qrStyle === 'gradient') {
|
||||
// 名片仅单主题色(无 gradient_end)时, themeGradient 返回纯色渐变(看不出渐变),
|
||||
// 故用压暗一档的终止色保证「渐变圆角」始终可见明暗过渡
|
||||
const theme = (this.card && this.card.theme_color) || CardTokens.color.primary;
|
||||
const end = (this.card && this.card.gradient_end) || darkenColor(theme, 0.18);
|
||||
style.background = 'linear-gradient(135deg, ' + theme + ' 0%, ' + end + ' 100%)';
|
||||
style.borderRadius = '16rpx';
|
||||
style.padding = '6rpx';
|
||||
}
|
||||
// plain 不进任何分支(无衬底, 即原图)
|
||||
return this.$util.objToStyle(style);
|
||||
},
|
||||
/** 二维码提示文字: 面板 qrTip, 未配置回落「扫码查看完整名片」 */
|
||||
qrTipText() {
|
||||
return this.c.qrTip || '扫码查看完整名片';
|
||||
@@ -260,6 +289,14 @@ export default {
|
||||
themeTextStyle() {
|
||||
return this.$util.objToStyle({ color: (this.card && this.card.theme_color) || CardTokens.color.primary });
|
||||
},
|
||||
/** 发名片按钮开关: 面板「底部按钮 → 发送名片按钮」(历史配置无此字段默认显示; 关后白卡下不留按钮) */
|
||||
showSendBtn() {
|
||||
return flagOn(this.c.showDetailBtn, true);
|
||||
},
|
||||
/** 发名片按钮文案: 面板「按钮文案」(默认「发送名片」, 与面板提示一致) */
|
||||
sendBtnText() {
|
||||
return this.c.detailBtnText || '发送名片';
|
||||
},
|
||||
/** 发名片按钮: 名片主题渐变色(无主题退化为默认主色), 详情页 send-bar 同款胶囊 */
|
||||
sendBtnStyle() {
|
||||
return this.$util.objToStyle({
|
||||
|
||||
@@ -62,6 +62,33 @@ test('common: normalizeQrStyle 合法直通, 未知/空回落 card(默认白卡)
|
||||
assert.equal(m.normalizeQrStyle('rainbow'), 'card');
|
||||
// 面板只下发小写档位; 大写属未知值, 回落 card(保守, 不炸)
|
||||
assert.equal(m.normalizeQrStyle('CARD'), 'card');
|
||||
// 中文档位名别名(面板下拉若直接存中文档名, 归一化兜住, 否则 C 端永远回落 card)
|
||||
assert.equal(m.normalizeQrStyle('原图'), 'plain');
|
||||
assert.equal(m.normalizeQrStyle('白卡描边'), 'card');
|
||||
assert.equal(m.normalizeQrStyle('主题色衬底'), 'theme');
|
||||
assert.equal(m.normalizeQrStyle('渐变圆角'), 'gradient');
|
||||
});
|
||||
|
||||
test('common: normalizeQrPosition 位置档中文别名兼容', async () => {
|
||||
const m = await import('../components-diy/diy-card-common.js');
|
||||
assert.equal(m.normalizeQrPosition('资料行下方'), 'rows');
|
||||
assert.equal(m.normalizeQrPosition('头部右侧'), 'head');
|
||||
assert.equal(m.normalizeQrPosition('右上角浮层'), 'corner');
|
||||
assert.equal(m.normalizeQrPosition('主按钮上方'), 'bottom');
|
||||
// 英文码直通 / 未知回落 rows
|
||||
assert.equal(m.normalizeQrPosition('bottom'), 'bottom');
|
||||
assert.equal(m.normalizeQrPosition('nope'), 'rows');
|
||||
});
|
||||
|
||||
test('common: darkenColor 压暗生成可见渐变终止色', async () => {
|
||||
const m = await import('../components-diy/diy-card-common.js');
|
||||
// 纯色压暗 0.18: #119dff → #0e81d1
|
||||
assert.equal(m.darkenColor('#119dff', 0.18), '#0e81d1');
|
||||
// 比例越大约暗
|
||||
assert.notEqual(m.darkenColor('#119dff', 0.4), m.darkenColor('#119dff', 0.18));
|
||||
// 非法输入原样返回(不影响既有渲染)
|
||||
assert.equal(m.darkenColor('#12', 0.18), '#12');
|
||||
assert.equal(m.darkenColor('rgb(1,2,3)', 0.18), 'rgb(1,2,3)');
|
||||
});
|
||||
|
||||
test('common: resolveQrSize 自定义>档位自动, 单位 px→rpx(×2)', async () => {
|
||||
@@ -121,8 +148,8 @@ test('mini: qrBlockStyle 四档外观分支(多彩只装饰外围, 码黑白保
|
||||
assert.match(miniSrc, /if \(this\.qrStyle === 'card'\) \{\s+style\.background = '#ffffff';\s+style\.border = '2rpx solid #EEF0F3';\s+style\.borderRadius = '12rpx';\s+style\.padding = '10rpx';/);
|
||||
// theme 主题色衬底
|
||||
assert.match(miniSrc, /else if \(this\.qrStyle === 'theme'\) \{\s+style\.background = theme;\s+style\.borderRadius = '12rpx';\s+style\.padding = '10rpx';/);
|
||||
// gradient 渐变 + 6rpx 紧凑内边距
|
||||
assert.match(miniSrc, /else if \(this\.qrStyle === 'gradient'\) \{\s+style\.background = themeGradient\(this\.card\);\s+style\.borderRadius = '16rpx';\s+style\.padding = '6rpx';/);
|
||||
// gradient 渐变 + 6rpx 紧凑内边距; 单主题色用 darkenColor 兜底终止色(可见明暗)
|
||||
assert.match(miniSrc, /else if \(this\.qrStyle === 'gradient'\) \{\s+const end = \(this\.card && this\.card\.gradient_end\) \|\| darkenColor\(theme, 0\.18\);\s+style\.background = 'linear-gradient\(135deg, ' \+ theme \+ ' 0%, ' \+ end \+ ' 100%\)';\s+style\.borderRadius = '16rpx';\s+style\.padding = '6rpx';/);
|
||||
// plain 不进任何分支(无衬底, 即原图) —— 三个 if 之外不再设 background
|
||||
assert.ok(!miniSrc.includes("qrStyle === 'plain' ? style.background"));
|
||||
});
|
||||
@@ -212,6 +239,29 @@ test('show: 二维码位置四档(与面板下拉/mini 基件同口径, 未配
|
||||
assert.match(showSrc, /qrTransform\(this\.c\.qrOffsetX,\s*this\.c\.qrOffsetY\)/);
|
||||
});
|
||||
|
||||
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"/);
|
||||
// 按钮文案: 面板「按钮文案」可配, 默认「发送名片」
|
||||
assert.match(showSrc, /sendBtnText\(\)\s*\{\s*return this\.c\.detailBtnText \|\| '发送名片';/);
|
||||
assert.match(showSrc, /<text class="show-send-text">\{\{ sendBtnText \}\}<\/text>/);
|
||||
// 反例形态: 不允许硬编码文案(绕过面板配置)
|
||||
assert.ok(!showSrc.includes('<text class="show-send-text">发名片</text>'));
|
||||
});
|
||||
|
||||
test('show: 二维码外观四档(plain 原图 / card 白卡描边 / theme 主题色 / gradient 渐变, 与 mini 同口径)', () => {
|
||||
assert.match(showSrc, /qrStyle\(\)\s*\{\s*return normalizeQrStyle\(this\.c\.qrStyle\)/);
|
||||
// 四个分支逐行收紧, 防跨分支 lazy 匹配造成假过(与 mini qrBlockStyle 同款值)
|
||||
assert.match(showSrc, /if \(this\.qrStyle === 'card'\) \{\s+style\.background = '#ffffff';\s+style\.border = '2rpx solid #EEF0F3';\s+style\.borderRadius = '12rpx';\s+style\.padding = '10rpx';/);
|
||||
assert.match(showSrc, /else if \(this\.qrStyle === 'theme'\) \{\s+style\.background = theme;\s+style\.borderRadius = '12rpx';\s+style\.padding = '10rpx';/);
|
||||
// gradient 分支: 单主题色时也要可见明暗渐变(用 darkenColor 兜底终止色), 不能退化成纯色
|
||||
assert.match(showSrc, /else if \(this\.qrStyle === 'gradient'\) \{\s+const theme = \(this\.card && this\.card\.theme_color\) \|\| CardTokens\.color\.primary;\s+const end = \(this\.card && this\.card\.gradient_end\) \|\| darkenColor\(theme, 0\.18\);\s+style\.background = 'linear-gradient\(135deg, ' \+ theme \+ ' 0%, ' \+ end \+ ' 100%\)';\s+style\.borderRadius = '16rpx';\s+style\.padding = '6rpx';/);
|
||||
// plain 不进任何分支(无衬底, 即原图)
|
||||
assert.ok(!showSrc.includes("qrStyle === 'plain' ? style.background"));
|
||||
// 四个位置档共用 qrBlockStyle
|
||||
assert.equal((showSrc.match(/:style="\[qrBlockStyle, qrShiftStyle\]"/g) || []).length, 4);
|
||||
});
|
||||
|
||||
test('show: 自绘版式自带小二维码(开关可关 + 点击进详情, 尺寸/提示文字面板可调)', () => {
|
||||
assert.match(showSrc, /class="card-qr"/);
|
||||
assert.match(showSrc, /\$util\.img\(card\.qr_url\)/);
|
||||
|
||||
Reference in New Issue
Block a user