Files
lucky_shop/components-diy/diy-card-show.vue
ZF sun bd691d2d30 feat(diy-card): Show 组件弃自绘版式恢复 diy-card-mini 基件(方案A 用户定稿)
决策依据: 后端 design.html 面板预览按 mini 基件渲染消费全部 53 字段,
方案A 下 C 端与编辑器预览逐字段一致(用户要求取与后端预览最接近的方案)。

- diy-card-show.vue: 恢复 2.19.3 末版 mini 基件实现(236 行, miniConfig
  全字段透传 含字号7/设计资产3/二维码6/宫格图层全套), 弃自绘版式(+888 行)
- 测试口径切换: qr-style 10 个/ab-visual 2 个/detail-jump 1 个「show: 自绘…」
  实现细节断言 → 改写为 miniConfig 全字段透传锁(防 merge 冲突解决再丢透传)
- 背景: 今晨两次 merge 保留自绘线丢弃透传致 27 字段 C 端失效(字段审核报告)

验证: 名片 7 套件全绿 + 全量仅剩 card-admin/card-api 存量失败(非本次) + 三回归 79/41/56
2026-10-24 11:53:34 +08:00

237 lines
6.7 KiB
Vue

<template>
<view data-component-name="diy-card-show" class="diy-card-show">
<diy-card-mini
:card="card"
:config="miniConfig"
:loading="loading"
empty-tip="商家还未选择名片"
@act="doAct"
@row-act="doAct"
@detail="goDetail"
></diy-card-mini>
</view>
</template>
<script>
// 数字名片 · 精选名片(装修组件)
// 视觉基件 diy-card-mini(封面/渐变/Logo/主题色/折叠/芯片/二维码), 本文件只管数据
export default {
name: 'diy-card-show',
props: {
value: { type: Object }
},
data() {
return {
card: null,
loading: false,
};
},
computed: {
miniConfig() {
const v = this.value || {};
return {
bgColor: v.bgColor,
nameColor: v.nameColor,
infoColor: v.infoColor,
labelColor: v.labelColor,
borderRadius: v.borderRadius,
padding: v.padding,
// 字号定制(px 口径, 缺省时基件按历史默认值渲染) —— 2.17 补齐:
// 此前 show 漏透传 7 个字号字段(面板配置不生效), 与 self 对齐
nameSize: v.nameSize,
cardTitleSize: v.cardTitleSize,
subSize: v.subSize,
labelSize: v.labelSize,
valueSize: v.valueSize,
actSize: v.actSize,
btnSize: v.btnSize,
showQuick: v.showQuick,
showDetailBtn: v.showDetailBtn,
detailBtnText: v.detailBtnText,
// 2.0 新增视觉开关
useCover: v.useCover,
showGradient: v.showGradient,
showLogo: v.showLogo,
showShadow: v.showShadow,
showRows: v.showRows,
maxRows: v.maxRows,
showQr: v.showQr,
qrTip: v.qrTip,
// 3.0 A+B 视觉升级
showRowIcons: v.showRowIcons,
rowTap: v.rowTap,
qrPosition: v.qrPosition,
// 3.1 头像开关 + 二维码样式/微调
showAvatar: v.showAvatar,
qrStyle: v.qrStyle,
qrSize: v.qrSize,
qrOffsetX: v.qrOffsetX,
qrOffsetY: v.qrOffsetY,
qrTipShow: v.qrTipShow,
// 2.17 双形态: 渐变头部 / 宫格快捷操作 / 资料区标题 / 复制按钮
headStyle: v.headStyle,
headNameColor: v.headNameColor,
headSubColor: v.headSubColor,
actsLayout: v.actsLayout,
actGridColorMode: v.actGridColorMode,
showRowsTitle: v.showRowsTitle,
rowsTitle: v.rowsTitle,
showCopyBtn: v.showCopyBtn,
// 2.18: 操作区位置/独立块样式/每按钮图标/自定义二维码图
actsPosition: v.actsPosition,
actsBlockBg: v.actsBlockBg,
actsBlockRadius: v.actsBlockRadius,
actsBlockPad: v.actsBlockPad,
actIcons: v.actIcons,
qrImage: v.qrImage,
// 2.19: 默认动作显隐/文案 + 自定义动作(基件 acts computed 合成)
actVis: v.actVis,
actDeleted: v.actDeleted,
actLabels: v.actLabels,
actOrder: v.actOrder,
customActs: v.customActs,
};
},
},
watch: {
'value.cardId'() {
this.fetchData();
},
},
mounted() {
this.fetchData();
},
methods: {
fetchData() {
const cardId = String((this.value || {}).cardId || '').trim();
if (cardId === '') {
this.card = null;
return;
}
this.loading = true;
this.$api.sendRequest({
url: '/card/api/diy/card',
data: { card_id: cardId },
success: res => {
this.loading = false;
this.card = (res.code == 0 && res.data && res.data.card) ? res.data.card : null;
},
fail: () => {
this.loading = false;
this.card = null;
},
});
},
doAct(act) {
if (!act) return;
// save(存通讯录): value 为空, 字段由本组件从 card 组装(白名单口径)
if (act.type === 'save') {
this.goSaveContact();
return;
}
// map(导航): value 留空回落名片地址(2.19.3)
if (act.type === 'map' && !act.value && this.card && this.card.address) {
act = Object.assign({}, act, { value: this.card.address });
}
if (!act.value) return;
if (act.type === 'tel') {
uni.makePhoneCall({ phoneNumber: act.value, fail: () => {} });
} else if (act.type === 'wx') {
uni.setClipboardData({
data: act.value,
success: () => uni.showToast({ title: '微信号已复制', icon: 'none' }),
});
} else if (act.type === 'mail') {
// #ifdef H5
window.location.href = 'mailto:' + act.value;
// #endif
// #ifndef H5
uni.setClipboardData({
data: act.value,
success: () => uni.showToast({ title: '邮箱已复制', icon: 'none' }),
});
// #endif
} else if (act.type === 'sms') {
// #ifdef H5
window.location.href = 'sms:' + act.value;
// #endif
// #ifndef H5
uni.setClipboardData({
data: act.value,
success: () => uni.showToast({ title: '号码已复制,请到短信粘贴', icon: 'none' }),
});
// #endif
} else if (act.type === 'map') {
uni.setClipboardData({
data: act.value,
success: () => uni.showToast({ title: '地址已复制,可粘贴到地图导航', icon: 'none' }),
});
} else if (act.type === 'link') {
var isHttp = act.value.indexOf('http://') === 0 || act.value.indexOf('https://') === 0;
if (isHttp) {
// #ifdef H5
window.location.href = act.value;
// #endif
// #ifndef H5
uni.setClipboardData({
data: act.value,
success: () => uni.showToast({ title: '链接已复制,请在浏览器打开', icon: 'none' }),
});
// #endif
} else {
this.$util.redirectTo(act.value, {});
}
} else {
uni.setClipboardData({
data: act.value,
success: () => uni.showToast({ title: '已复制', icon: 'none' }),
});
}
},
goDetail(card) {
if (!card || !card.card_id) return;
this.$util.redirectTo('/pages_tool/card/detail', { card_id: card.card_id });
},
/**
* 存通讯录(2.17.1): 字段全部从 card 组装 —— rows 已过 shouldShow
* 白名单(商家隐藏的字段不下发也不落通讯录, 隐私口径与后端一致),
* name/company/job_title 为公开头部字段直接取。
* 键名与 save-contact.vue onLoad 的 keys 一一对应。
*/
goSaveContact() {
const c = this.card || {};
const map = {};
(Array.isArray(c.rows) ? c.rows : []).forEach(r => {
if (r && r.field) map[r.field] = String(r.value || '');
});
// #ifdef H5
// H5 无通讯录 API: 复制资料文本兜底(小程序端走 save-contact 原生页)
const text = [c.name, map.mobile, map.email, c.company, c.job_title].filter(Boolean).join(' ');
uni.setClipboardData({
data: text,
success: () => uni.showToast({ title: '名片资料已复制', icon: 'none' }),
});
return;
// #endif
this.$util.redirectTo('/pages_tool/card/save-contact', {
name: c.name || '',
mobile: map.mobile || '',
company: c.company || '',
job_title: c.job_title || '',
email: map.email || '',
website: map.website || '',
address: map.address || '',
remark: map.remark || '',
wechat: map.wechat || '',
});
},
},
};
</script>
<style lang="scss" scoped>
.diy-card-show {
width: 100%;
}
</style>