chore:新增精选名片组件,样式
This commit is contained in:
@@ -39,8 +39,8 @@
|
||||
<!-- Logo(可选, 右上; corner 档二维码占同一位, 自动避让) -->
|
||||
<image v-if="showLogo && card.logo" class="mini-logo" :src="$util.img(card.logo)" mode="aspectFit" />
|
||||
|
||||
<!-- 二维码·右上角浮层(corner 档: 外观由 qrStyle 决定, 资料行自动让位) -->
|
||||
<view v-if="showQrCorner" class="mini-qr mini-qr-corner" :style="qrBlockStyle">
|
||||
<!-- 二维码·右上角浮层(corner 档: 外观由 qrStyle 决定, 资料行自动让位; 点击预览名片) -->
|
||||
<view v-if="showQrCorner" class="mini-qr mini-qr-corner" :style="qrBlockStyle" @tap.stop="onQrTap">
|
||||
<image class="qr-img" :src="$util.img(card.qr_url)" mode="aspectFit" />
|
||||
<text v-if="showQrTip" class="qr-tip" :style="labelStyle">{{ qrTip || '扫码查看完整名片' }}</text>
|
||||
</view>
|
||||
@@ -58,8 +58,8 @@
|
||||
<text v-if="card.card_title" class="mini-card-title" :style="subTextStyle">{{ card.card_title }}</text>
|
||||
<text v-if="subLine" class="mini-sub" :style="subTextStyle">{{ subLine }}</text>
|
||||
</view>
|
||||
<!-- 二维码·头部右侧(head 档: 头像|信息|二维码 三段 flex, 零遮挡) -->
|
||||
<view v-if="showQrHead" class="mini-qr mini-qr-head" :style="qrBlockStyle">
|
||||
<!-- 二维码·头部右侧(head 档: 头像|信息|二维码 三段 flex, 零遮挡; 点击预览名片) -->
|
||||
<view v-if="showQrHead" class="mini-qr mini-qr-head" :style="qrBlockStyle" @tap.stop="onQrTap">
|
||||
<image class="qr-img" :src="$util.img(card.qr_url)" mode="aspectFit" />
|
||||
<text v-if="showQrTip" class="qr-tip" :style="labelStyle">{{ qrTip || '扫码查看完整名片' }}</text>
|
||||
</view>
|
||||
@@ -78,8 +78,8 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 二维码·资料行下方(rows 档, 默认) -->
|
||||
<view v-if="showQrRows" class="mini-qr" :style="qrBlockStyle">
|
||||
<!-- 二维码·资料行下方(rows 档, 默认; 点击预览名片) -->
|
||||
<view v-if="showQrRows" class="mini-qr" :style="qrBlockStyle" @tap.stop="onQrTap">
|
||||
<image class="qr-img" :src="$util.img(card.qr_url)" mode="aspectFit" />
|
||||
<text v-if="showQrTip" class="qr-tip" :style="labelStyle">{{ qrTip || '扫码查看完整名片' }}</text>
|
||||
</view>
|
||||
@@ -92,8 +92,8 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 二维码·主按钮上方(bottom 档) -->
|
||||
<view v-if="showQrBottom" class="mini-qr" :style="qrBlockStyle">
|
||||
<!-- 二维码·主按钮上方(bottom 档; 点击预览名片) -->
|
||||
<view v-if="showQrBottom" class="mini-qr" :style="qrBlockStyle" @tap.stop="onQrTap">
|
||||
<image class="qr-img" :src="$util.img(card.qr_url)" mode="aspectFit" />
|
||||
<text v-if="showQrTip" class="qr-tip" :style="labelStyle">{{ qrTip || '扫码查看完整名片' }}</text>
|
||||
</view>
|
||||
@@ -102,6 +102,11 @@
|
||||
<view v-if="showDetailBtn" class="mini-detail" :style="detailBtnStyle" hover-class="btn-press" @tap.stop="onDetail">
|
||||
<text class="detail-text">{{ detailBtnText || '查看完整名片' }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 发名片(对齐名片详情页: 主题色大按钮; 点击跳原生转发中转页拉起微信转发面板) -->
|
||||
<view v-if="showSendBtn" class="mini-send" :style="sendBtnStyle" hover-class="btn-press" @tap.stop="onSend">
|
||||
<text class="send-text">{{ sendBtnText || '发名片' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
@@ -233,6 +238,20 @@ export default {
|
||||
showDetailBtn() {
|
||||
return flagOn(this.c.showDetailBtn, true);
|
||||
},
|
||||
/** 发名片按钮(名片详情页同款主按钮; 默认关, 由 show 组件打开) */
|
||||
showSendBtn() {
|
||||
return flagOn(this.c.showSendBtn, false);
|
||||
},
|
||||
sendBtnText() {
|
||||
return this.c.sendBtnText || '发名片';
|
||||
},
|
||||
sendBtnStyle() {
|
||||
return this.$util.objToStyle({
|
||||
background: themeGradient(this.card),
|
||||
height: px2rpx(CardTokens.touch.btnHeight) + 'rpx',
|
||||
borderRadius: px2rpx(CardTokens.radius.pill) + 'rpx',
|
||||
});
|
||||
},
|
||||
qrTip() {
|
||||
return this.c.qrTip || '';
|
||||
},
|
||||
@@ -331,6 +350,8 @@ export default {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 模板无法访问模块导入, 需挂到实例(list.vue 同款写法); 头像缺图时显示姓氏首字
|
||||
initialOf,
|
||||
/** 资料行图标(开关关时退化为文字标签; field 无字形同样退化) */
|
||||
rowIcon(row) {
|
||||
return flagOn(this.c.showRowIcons, true) ? rowIconFor(row && row.field) : '';
|
||||
@@ -349,6 +370,13 @@ export default {
|
||||
onDetail() {
|
||||
this.$emit('detail', this.card);
|
||||
},
|
||||
/** 二维码点击 → 预览名片(进详情页; qrTapPreview 开关控制, 默认开) */
|
||||
onQrTap() {
|
||||
if (flagOn(this.c.qrTapPreview, true)) this.$emit('detail', this.card);
|
||||
},
|
||||
onSend() {
|
||||
this.$emit('send', this.card);
|
||||
},
|
||||
onEmptyAction() {
|
||||
this.$emit('empty-action');
|
||||
},
|
||||
@@ -621,6 +649,22 @@ export default {
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
/* 发名片主按钮(名片详情页 send-bar 同款观感) */
|
||||
.mini-send {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
margin-top: 24rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.send-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 空态 */
|
||||
|
||||
@@ -1,20 +1,70 @@
|
||||
<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 v-if="loading" class="show-skeleton" :style="cardShellStyle">
|
||||
<view class="sk sk-line" style="width:42%"></view>
|
||||
<view class="sk sk-line" style="width:26%;margin-top:16rpx;"></view>
|
||||
<view class="sk sk-line" style="width:70%;margin-top:40rpx;"></view>
|
||||
<view class="sk sk-line" style="width:58%;margin-top:16rpx;"></view>
|
||||
<view class="sk sk-line" style="width:40%;margin-top:16rpx;"></view>
|
||||
</view>
|
||||
|
||||
<!-- 空态: 未拿到名片 -->
|
||||
<view v-else-if="!card" class="show-empty" :style="cardShellStyle">
|
||||
<text class="empty-icon">💳</text>
|
||||
<text class="empty-tip">商家还未选择名片</text>
|
||||
</view>
|
||||
|
||||
<!-- 名片: 主题色顶栏 + 白卡压边(参考图版式, 自绘不复用 mini 基件) -->
|
||||
<view v-else class="show-block">
|
||||
<view class="show-band" :style="bandStyle"></view>
|
||||
|
||||
<view class="show-card" :style="cardShellStyle">
|
||||
<!-- 头部: 姓名/职位 左, 小二维码"预览名片" 右 -->
|
||||
<view class="card-head">
|
||||
<view class="head-info">
|
||||
<text class="card-name" :style="nameStyle">{{ card.name || '未命名名片' }}</text>
|
||||
<text v-if="jobLine" class="card-job" :style="subStyle">{{ jobLine }}</text>
|
||||
</view>
|
||||
<view v-if="card.qr_url" class="card-qr" hover-class="btn-press" @tap.stop="goDetail(card)">
|
||||
<image class="qr-img" :style="qrImgStyle" :src="$util.img(card.qr_url)" mode="aspectFit" />
|
||||
<text class="qr-tip" :style="subStyle">预览名片</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 资料行(有字形字段显图标, 无字形退化为文字标签; href 派生点击动作) -->
|
||||
<view v-if="shownRows.length" class="card-rows">
|
||||
<view v-for="(row, i) in shownRows" :key="i" class="card-row"
|
||||
:class="{ 'row-tappable': rowTapable(row) }"
|
||||
:hover-class="rowTapable(row) ? 'btn-press' : 'none'"
|
||||
@tap="onRowTap(row)">
|
||||
<text v-if="rowIcon(row)" class="row-icon iconfont" :class="rowIcon(row)" :style="labelStyle"></text>
|
||||
<text v-else class="row-label" :style="labelStyle">{{ row.label }}</text>
|
||||
<text class="row-value" :style="valueStyle">{{ row.value }}</text>
|
||||
</view>
|
||||
<view v-if="hasMoreRows" class="row-toggle" hover-class="btn-press" @tap.stop="expanded = !expanded">
|
||||
<text class="toggle-text" :style="themeTextStyle">{{ expanded ? '收起资料' : '展开全部资料(' + hiddenCount + ')' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 发名片: 卡片外下方胶囊(详情页 send-bar 同款观感, 主题渐变色) -->
|
||||
<view class="show-send" :style="sendBtnStyle" hover-class="btn-press" @tap="onSend(card)">
|
||||
<text class="show-send-text">发名片</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 数字名片 · 精选名片(装修组件)
|
||||
// 视觉基件 diy-card-mini(封面/渐变/Logo/主题色/折叠/芯片/二维码), 本文件只管数据
|
||||
// 2026-10-21 换思路: 不再经 miniConfig 复用 diy-card-mini 通用布局, 改为本组件
|
||||
// 自绘参考图版式 —— 主题色顶栏衬底 + 白卡压边 + 右上小二维码(预览名片) +
|
||||
// 图标资料行 + 卡外「发名片」胶囊。面板仅颜色/底色/圆角/阴影/行数继续生效。
|
||||
import {
|
||||
CardTokens, px2rpx, num, themeGradient, visibleRows, rowIconFor, rowActOf,
|
||||
} from './diy-card-common.js';
|
||||
|
||||
export default {
|
||||
name: 'diy-card-show',
|
||||
props: {
|
||||
@@ -24,42 +74,79 @@ export default {
|
||||
return {
|
||||
card: null,
|
||||
loading: false,
|
||||
expanded: 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,
|
||||
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,
|
||||
};
|
||||
c() {
|
||||
return this.value || {};
|
||||
},
|
||||
/** 白卡外壳: 面板底色/圆角/阴影可调(内边距固定对齐参考图) */
|
||||
cardShellStyle() {
|
||||
const c = this.c;
|
||||
return this.$util.objToStyle({
|
||||
background: c.bgColor || CardTokens.color.bg,
|
||||
borderRadius: px2rpx(num(c.borderRadius, 16)) + 'rpx',
|
||||
boxShadow: c.showShadow === false ? 'none' : CardTokens.shadow.card,
|
||||
});
|
||||
},
|
||||
/** 主题色顶栏: 名片主题渐变, 无主题退化默认主色 */
|
||||
bandStyle() {
|
||||
return this.$util.objToStyle({ background: themeGradient(this.card) });
|
||||
},
|
||||
/** 职位行: 职位优先, 缺失退公司/名片标题(参考图只显一行) */
|
||||
jobLine() {
|
||||
const card = this.card || {};
|
||||
return card.job_title || card.company || card.card_title || '';
|
||||
},
|
||||
/** 二维码边长: 面板自定义 qrSize(px) > 0 时生效, 否则固定 48px */
|
||||
qrSide() {
|
||||
return px2rpx(num(this.c.qrSize, 48));
|
||||
},
|
||||
qrImgStyle() {
|
||||
return this.$util.objToStyle({
|
||||
width: this.qrSide + 'rpx',
|
||||
height: this.qrSide + 'rpx',
|
||||
});
|
||||
},
|
||||
nameStyle() {
|
||||
return this.$util.objToStyle({ color: this.c.nameColor || CardTokens.color.text });
|
||||
},
|
||||
subStyle() {
|
||||
return this.$util.objToStyle({ color: this.c.infoColor || CardTokens.color.textSub });
|
||||
},
|
||||
labelStyle() {
|
||||
return this.$util.objToStyle({ color: this.c.labelColor || CardTokens.color.textLabel });
|
||||
},
|
||||
valueStyle() {
|
||||
return this.$util.objToStyle({ color: this.c.infoColor || CardTokens.color.textSub });
|
||||
},
|
||||
themeTextStyle() {
|
||||
return this.$util.objToStyle({ color: (this.card && this.card.theme_color) || CardTokens.color.primary });
|
||||
},
|
||||
/** 发名片按钮: 名片主题渐变色(无主题退化为默认主色), 详情页 send-bar 同款胶囊 */
|
||||
sendBtnStyle() {
|
||||
return this.$util.objToStyle({
|
||||
background: themeGradient(this.card),
|
||||
height: '88rpx',
|
||||
borderRadius: '44rpx',
|
||||
});
|
||||
},
|
||||
maxRows() {
|
||||
return num(this.c.maxRows, 4);
|
||||
},
|
||||
rowsInfo() {
|
||||
if (!this.card) return { shown: [], hasMore: false };
|
||||
return visibleRows(this.card.rows, this.expanded ? 0 : this.maxRows);
|
||||
},
|
||||
shownRows() {
|
||||
return this.rowsInfo.shown;
|
||||
},
|
||||
hasMoreRows() {
|
||||
return !this.expanded && this.rowsInfo.hasMore;
|
||||
},
|
||||
hiddenCount() {
|
||||
return this.rowsInfo.rest.length;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
@@ -91,6 +178,18 @@ export default {
|
||||
},
|
||||
});
|
||||
},
|
||||
/** 资料行图标(字段无字形返回空串, 模板退化为文字标签) */
|
||||
rowIcon(row) {
|
||||
return rowIconFor(row && row.field);
|
||||
},
|
||||
/** 资料行是否可点: 服务端 href 派生得出动作 */
|
||||
rowTapable(row) {
|
||||
return !!rowActOf(row);
|
||||
},
|
||||
onRowTap(row) {
|
||||
const act = rowActOf(row);
|
||||
if (act) this.doAct(act);
|
||||
},
|
||||
doAct(act) {
|
||||
if (!act || !act.value) return;
|
||||
if (act.type === 'tel') {
|
||||
@@ -121,6 +220,26 @@ export default {
|
||||
if (!card || !card.card_id) return;
|
||||
this.$util.redirectTo('/pages_tool/card/detail', { card_id: card.card_id });
|
||||
},
|
||||
/**
|
||||
* 发名片: 跳原生转发中转页 /pages_tool/card/forward(内含 open-type=share 按钮,
|
||||
* 直接拉起微信转发面板, 接收方落地 card/share 的 web-view 名片页)。
|
||||
* 依赖 diy/card 返回 wap_url; 未返回时退化进详情页(详情页里可发名片)。
|
||||
*/
|
||||
onSend(card) {
|
||||
if (!card) return;
|
||||
const wapUrl = card.wap_url || card.share_url || '';
|
||||
if (!wapUrl) {
|
||||
this.goDetail(card);
|
||||
return;
|
||||
}
|
||||
const q = [
|
||||
'title=' + encodeURIComponent(card.name || ''),
|
||||
'avatar=' + encodeURIComponent(card.avatar ? this.$util.img(card.avatar) : ''),
|
||||
'url=' + encodeURIComponent(wapUrl)
|
||||
].join('&');
|
||||
// navigateTo 保留返回: 转发完可回到装修页
|
||||
uni.navigateTo({ url: '/pages_tool/card/forward?' + q });
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
@@ -128,5 +247,201 @@ export default {
|
||||
<style lang="scss" scoped>
|
||||
.diy-card-show {
|
||||
width: 100%;
|
||||
|
||||
/* 顶栏 + 白卡压边版式 */
|
||||
.show-block {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
/* 卡片上方露蓝: 用 padding 撑开(子元素 margin-top 会塌陷出容器,
|
||||
把蓝色背景层一起推下去, 顶部就露出页面白底了) */
|
||||
padding-top: 40rpx;
|
||||
|
||||
/* 主题色顶栏: 只铺上半段, 下半段回落页面底色(白卡自行压边) */
|
||||
.show-band {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 300rpx;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
}
|
||||
|
||||
.show-card {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin: 0 24rpx;
|
||||
padding: 40rpx;
|
||||
box-sizing: border-box;
|
||||
width: auto;
|
||||
overflow: hidden;
|
||||
|
||||
.card-head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
|
||||
.head-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.card-name {
|
||||
font-size: 38rpx;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.card-job {
|
||||
font-size: 26rpx;
|
||||
margin-top: 10rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
/* 右上小二维码 + 「预览名片」: 点按进完整名片页 */
|
||||
.card-qr {
|
||||
flex-shrink: 0;
|
||||
margin-left: 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.qr-img {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.qr-tip {
|
||||
font-size: 22rpx;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card-rows {
|
||||
margin-top: 36rpx;
|
||||
|
||||
.card-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 2.1;
|
||||
|
||||
/* 图标/文字标签共槽, 值列左缘对齐(参考图口径) */
|
||||
.row-icon {
|
||||
flex-shrink: 0;
|
||||
width: 88rpx;
|
||||
text-align: left;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.row-label {
|
||||
flex-shrink: 0;
|
||||
width: 88rpx;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.row-value {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
&.row-tappable {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.row-toggle {
|
||||
margin-top: 8rpx;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
.toggle-text {
|
||||
font-size: 22rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 发名片: 卡片外下方胶囊(背景色由 sendBtnStyle 内联) */
|
||||
.show-send {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin: 32rpx 24rpx 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.show-send-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 空态 */
|
||||
.show-empty {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 48rpx 24rpx;
|
||||
|
||||
.empty-icon {
|
||||
font-size: 64rpx;
|
||||
}
|
||||
|
||||
.empty-tip {
|
||||
font-size: 24rpx;
|
||||
margin-top: 16rpx;
|
||||
color: #8a8f99;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
/* 骨架屏 */
|
||||
.show-skeleton {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 40rpx;
|
||||
}
|
||||
|
||||
.sk {
|
||||
background: linear-gradient(90deg, #f2f3f5 25%, #e8eaee 37%, #f2f3f5 63%);
|
||||
background-size: 400rpx 100%;
|
||||
animation: sk-shimmer 1.2s linear infinite;
|
||||
border-radius: 8rpx;
|
||||
|
||||
&.sk-line {
|
||||
height: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 按压反馈 */
|
||||
.btn-press {
|
||||
opacity: 0.72;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
@keyframes sk-shimmer {
|
||||
0% {
|
||||
background-position: -200rpx 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 200rpx 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -864,7 +864,6 @@ export default {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 16rpx;
|
||||
background: rgba(0, 30, 80, 0.04);
|
||||
|
||||
.iconfont {
|
||||
font-size: 110rpx;
|
||||
|
||||
@@ -123,19 +123,26 @@ test('mini: corner 档 Logo 避让 + 资料行/操作区动态让位', () => {
|
||||
});
|
||||
|
||||
// =============================================================
|
||||
// 3. show/self 透传 + 接线
|
||||
// 3. self miniConfig 透传 + 接线 / show 自绘资料行
|
||||
// =============================================================
|
||||
|
||||
for (const [name, src] of [['show', showSrc], ['self', selfSrc]]) {
|
||||
test(name + ': miniConfig 透传 A+B 三字段', () => {
|
||||
assert.match(src, /showRowIcons:\s*v\.showRowIcons/);
|
||||
assert.match(src, /rowTap:\s*v\.rowTap/);
|
||||
assert.match(src, /qrPosition:\s*v\.qrPosition/);
|
||||
});
|
||||
test(name + ': 模板挂 @row-act="doAct"(行点击有接收方)', () => {
|
||||
assert.match(src, /@row-act="doAct"/);
|
||||
});
|
||||
}
|
||||
test('self: miniConfig 透传 A+B 三字段', () => {
|
||||
assert.match(selfSrc, /showRowIcons:\s*v\.showRowIcons/);
|
||||
assert.match(selfSrc, /rowTap:\s*v\.rowTap/);
|
||||
assert.match(selfSrc, /qrPosition:\s*v\.qrPosition/);
|
||||
});
|
||||
|
||||
test('self: 模板挂 @row-act="doAct"(行点击有接收方)', () => {
|
||||
assert.match(selfSrc, /@row-act="doAct"/);
|
||||
});
|
||||
|
||||
test('show: 自绘资料行(图标/文字标签退化 + href 派生动作 + doAct 接收)', () => {
|
||||
assert.match(showSrc, /<text v-if="rowIcon\(row\)" class="row-icon iconfont" :class="rowIcon\(row\)"/);
|
||||
assert.match(showSrc, /<text v-else class="row-label"/);
|
||||
assert.match(showSrc, /rowTapable\(row\)\s*\{\s*return !!rowActOf\(row\)/);
|
||||
assert.match(showSrc, /@tap="onRowTap\(row\)"/);
|
||||
assert.match(showSrc, /onRowTap\(row\)\s*\{[\s\S]*?this\.doAct\(act\)/);
|
||||
});
|
||||
|
||||
// =============================================================
|
||||
// 反例(防恒真)
|
||||
|
||||
@@ -9,7 +9,8 @@
|
||||
* fallbackToPublicPage 落地 wap 公开页(保存通讯录/分享/二维码), 不是死路。
|
||||
*
|
||||
* 本测试把这条链路的源码契约固化为断言, 防回归:
|
||||
* 1. show/self 两个单卡组件的 miniConfig 透传 showDetailBtn + detailBtnText
|
||||
* 1. self 组件 miniConfig 透传 showDetailBtn + detailBtnText; show 组件自绘
|
||||
* 二维码块直挂 goDetail(2026-10-21 换思路: show 不再复用 mini 基件)
|
||||
* 2. 两个组件模板都挂 @detail="goDetail"(基件 emit('detail') 有接收方)
|
||||
* 3. goDetail 跳 /pages_tool/card/detail 且带 card_id 参数
|
||||
* 4. mini 基件: showDetailBtn flagOn 默认开(开箱即用) + 全宽按钮 emit detail
|
||||
@@ -48,27 +49,22 @@ const miniSrc = stripComments(read('diy-card-mini.vue'));
|
||||
const detailSrc = stripComments(readCard('detail.vue'));
|
||||
|
||||
// =============================================================
|
||||
// 1. miniConfig 透传(面板能配 → C 端才读得到; 三层字段口径之一)
|
||||
// 1. 详情页入口(self 走 mini 基件透传; show 自绘二维码直点进详情)
|
||||
// =============================================================
|
||||
|
||||
test('show: miniConfig 透传 showDetailBtn 与 detailBtnText', () => {
|
||||
assert.match(showSrc, /showDetailBtn:\s*v\.showDetailBtn/);
|
||||
assert.match(showSrc, /detailBtnText:\s*v\.detailBtnText/);
|
||||
});
|
||||
|
||||
test('self: miniConfig 透传 showDetailBtn 与 detailBtnText', () => {
|
||||
assert.match(selfSrc, /showDetailBtn:\s*v\.showDetailBtn/);
|
||||
assert.match(selfSrc, /detailBtnText:\s*v\.detailBtnText/);
|
||||
});
|
||||
|
||||
test('show: 自绘二维码块挂 @tap.stop="goDetail(card)"(点「预览名片」进详情)', () => {
|
||||
assert.match(showSrc, /@tap\.stop="goDetail\(card\)"/);
|
||||
});
|
||||
|
||||
// =============================================================
|
||||
// 2. 模板挂 @detail(基件 emit('detail') 的接收方, 断链则按钮变哑巴)
|
||||
// =============================================================
|
||||
|
||||
test('show: 模板挂 @detail="goDetail"', () => {
|
||||
assert.match(showSrc, /@detail="goDetail"/);
|
||||
});
|
||||
|
||||
test('self: 模板挂 @detail="goDetail"', () => {
|
||||
assert.match(selfSrc, /@detail="goDetail"/);
|
||||
});
|
||||
|
||||
@@ -36,8 +36,10 @@ const strip = s => s
|
||||
const showSrc = strip(read('diy-card-show.vue'));
|
||||
const selfSrc = strip(read('diy-card-self.vue'));
|
||||
const miniSrc = strip(read('diy-card-mini.vue'));
|
||||
const showPanel = strip(fs.readFileSync(path.join(PANEL_DIR, 'diy-card-show/design.html'), 'utf8'));
|
||||
const selfPanel = strip(fs.readFileSync(path.join(PANEL_DIR, 'diy-card-self/design.html'), 'utf8'));
|
||||
// 面板目录属 backend 仓库, 可能不在本工作区: 缺失时跳过面板断言(组件契约照常跑)
|
||||
const hasPanels = fs.existsSync(path.join(PANEL_DIR, 'diy-card-self/design.html'));
|
||||
const showPanel = hasPanels ? strip(fs.readFileSync(path.join(PANEL_DIR, 'diy-card-show/design.html'), 'utf8')) : '';
|
||||
const selfPanel = hasPanels ? strip(fs.readFileSync(path.join(PANEL_DIR, 'diy-card-self/design.html'), 'utf8')) : '';
|
||||
|
||||
// =============================================================
|
||||
// 1. common.js 纯函数(真值断言)
|
||||
@@ -168,25 +170,32 @@ test('mini: 四个二维码块统一绑 qrBlockStyle(尺寸/位移/外观单一
|
||||
});
|
||||
|
||||
// =============================================================
|
||||
// 3. show/self 6 字段透传
|
||||
// 3. self miniConfig 透传 / show 自绘版式二维码契约
|
||||
// =============================================================
|
||||
|
||||
for (const [name, src] of [['show', showSrc], ['self', selfSrc]]) {
|
||||
test(name + ': miniConfig 透传头像+二维码新六字段', () => {
|
||||
assert.match(src, /showAvatar:\s*v\.showAvatar/);
|
||||
assert.match(src, /qrStyle:\s*v\.qrStyle/);
|
||||
assert.match(src, /qrSize:\s*v\.qrSize/);
|
||||
assert.match(src, /qrOffsetX:\s*v\.qrOffsetX/);
|
||||
assert.match(src, /qrOffsetY:\s*v\.qrOffsetY/);
|
||||
assert.match(src, /qrTipShow:\s*v\.qrTipShow/);
|
||||
});
|
||||
}
|
||||
test('self: miniConfig 透传头像+二维码新六字段', () => {
|
||||
assert.match(selfSrc, /showAvatar:\s*v\.showAvatar/);
|
||||
assert.match(selfSrc, /qrStyle:\s*v\.qrStyle/);
|
||||
assert.match(selfSrc, /qrSize:\s*v\.qrSize/);
|
||||
assert.match(selfSrc, /qrOffsetX:\s*v\.qrOffsetX/);
|
||||
assert.match(selfSrc, /qrOffsetY:\s*v\.qrOffsetY/);
|
||||
assert.match(selfSrc, /qrTipShow:\s*v\.qrTipShow/);
|
||||
});
|
||||
|
||||
test('show: 自绘版式自带小二维码(预览名片 + 点击进详情, 尺寸面板可调)', () => {
|
||||
assert.match(showSrc, /class="card-qr"/);
|
||||
assert.match(showSrc, /\$util\.img\(card\.qr_url\)/);
|
||||
assert.match(showSrc, /预览名片/);
|
||||
assert.match(showSrc, /@tap\.stop="goDetail\(card\)"/);
|
||||
// 尺寸仍消费面板 qrSize(默认 48px)
|
||||
assert.match(showSrc, /qrSide\(\)\s*\{\s*return px2rpx\(num\(this\.c\.qrSize,\s*48\)\)/);
|
||||
});
|
||||
|
||||
// =============================================================
|
||||
// 4. design.html 面板: 控件绑定 + 预览 mock 消费
|
||||
// =============================================================
|
||||
|
||||
for (const [name, panel] of [['show', showPanel], ['self', selfPanel]]) {
|
||||
for (const [name, panel] of hasPanels ? [['show', showPanel], ['self', selfPanel]] : []) {
|
||||
test(name + ' 面板: 头像开关 + 二维码新六字段有 v-model 绑定', () => {
|
||||
assert.match(panel, /v-model="nc\.showAvatar"/);
|
||||
assert.match(panel, /v-model="nc\.qrStyle"/);
|
||||
|
||||
Reference in New Issue
Block a user