- entry: 主/副标题与按钮字号内联消费(titleSize/subSize/btnSize, px→rpx ×2), 文字箭头拆 btnArrowStyle(比按钮大一档=28rpx) - list: 拆 gridNameStyle/scrollNameStyle/metaStyle/scrollMetaStyle, 横滑与 人数徽标按 -1px 派生保持历史渲染; infoStyle 保持纯色(箭头/空态共用) - mini(Show/Self 基件): name/cardTitle/sub/label/value/act/btn 七项字号内联, 拆 cardTitleStyle/rowLabelStyle/detailTextStyle; labelStyle 保持纯色给图标/qr tip - self: miniConfig 透传 7 个字号字段 - 新增 tests/diy-card-font-size.test.js: 三层口径契约(diy_view 默认值 ↔ design.html 面板/重置 ↔ vue 消费) + 派生关系 + 颜色 value 兜底 + 反例自测 验证: 契约 12/12 绿; diy-card 全套测试 0 失败; E2E 截图目检通过
291 lines
7.8 KiB
Vue
291 lines
7.8 KiB
Vue
<template>
|
||
<view data-component-name="diy-card-entry" class="diy-card-entry" :class="'entry-' + stylePreset" :style="wrapStyle" hover-class="entry-press" @tap="goLink">
|
||
<!-- 图标装饰(可选) -->
|
||
<view v-if="showIcon" class="entry-icon" :style="iconWrapStyle">
|
||
<image v-if="iconType === 'image' && iconImage" class="icon-img" :src="$util.img(iconImage)" mode="aspectFit" />
|
||
<text v-else class="icon-glyph">{{ iconGlyph || '💼' }}</text>
|
||
</view>
|
||
|
||
<view class="entry-body">
|
||
<text class="entry-title" :style="titleStyle">{{ value.title || '扫名片 · 谈合作' }}</text>
|
||
<text v-if="value.subtitle" class="entry-sub" :style="subStyle">{{ value.subtitle }}</text>
|
||
</view>
|
||
|
||
<!-- 按钮(可选; link 模式为文字箭头) -->
|
||
<view v-if="buttonStyle !== 'none' && value.btnText" :class="['entry-btn', 'btn-' + buttonStyle]" :style="btnStyle" hover-class="btn-press" hover-stay-time="60">
|
||
<text class="entry-btn-text" :style="btnTextStyle">{{ value.btnText }}</text>
|
||
<text v-if="buttonStyle === 'link'" class="btn-arrow" :style="btnArrowStyle">›</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
// 数字名片 · 名片入口横幅(装修组件)
|
||
// 预设: gradient(渐变) / solid(纯色+主题饰条) / minimal(透明轻量)
|
||
// 布局: left / center; 按钮: solid / ghost / link / none
|
||
import { CardTokens, px2rpx, flagOn, num } from './diy-card-common.js';
|
||
|
||
export default {
|
||
name: 'diy-card-entry',
|
||
props: {
|
||
value: { type: Object }
|
||
},
|
||
computed: {
|
||
v() {
|
||
return this.value || {};
|
||
},
|
||
stylePreset() {
|
||
const p = String(this.v.stylePreset || 'gradient');
|
||
return ['gradient', 'solid', 'minimal'].includes(p) ? p : 'gradient';
|
||
},
|
||
isCenter() {
|
||
return String(this.v.layout || 'left') === 'center';
|
||
},
|
||
showIcon() {
|
||
return flagOn(this.v.showIcon, false);
|
||
},
|
||
iconType() {
|
||
return String(this.v.iconType || 'icon') === 'image' ? 'image' : 'icon';
|
||
},
|
||
iconImage() {
|
||
return this.v.iconImage || '';
|
||
},
|
||
iconGlyph() {
|
||
return this.v.iconGlyph || '名';
|
||
},
|
||
buttonStyle() {
|
||
const b = String(this.v.buttonStyle || 'solid');
|
||
return ['solid', 'ghost', 'link', 'none'].includes(b) ? b : 'solid';
|
||
},
|
||
isGradient() {
|
||
return this.stylePreset === 'gradient';
|
||
},
|
||
isMinimal() {
|
||
return this.stylePreset === 'minimal';
|
||
},
|
||
themeColor() {
|
||
return this.v.bgColorStart || CardTokens.color.primary;
|
||
},
|
||
wrapStyle() {
|
||
const v = this.v;
|
||
const radius = px2rpx(num(v.borderRadius, CardTokens.radius.md)) + 'rpx';
|
||
const pad = px2rpx(num(v.padding, CardTokens.space.lg)) + 'rpx';
|
||
if (this.isMinimal) {
|
||
return this.$util.objToStyle({
|
||
background: 'transparent',
|
||
borderRadius: radius,
|
||
padding: pad,
|
||
boxShadow: 'none',
|
||
});
|
||
}
|
||
if (this.isGradient) {
|
||
return this.$util.objToStyle({
|
||
background: 'linear-gradient(135deg, ' + (v.bgColorStart || CardTokens.color.primary) + ' 0%, ' + (v.bgColorEnd || '#1E40AF') + ' 100%)',
|
||
borderRadius: radius,
|
||
padding: pad,
|
||
minHeight: '120rpx',
|
||
});
|
||
}
|
||
// solid: 主题色底 + 左侧亮饰条
|
||
return this.$util.objToStyle({
|
||
background: v.bgColorStart || CardTokens.color.primary,
|
||
borderRadius: radius,
|
||
padding: pad,
|
||
borderLeft: '8rpx solid ' + (v.bgColorEnd || 'rgba(255,255,255,0.35)'),
|
||
minHeight: '120rpx',
|
||
});
|
||
},
|
||
iconWrapStyle() {
|
||
return this.$util.objToStyle({
|
||
background: this.isGradient || !this.isMinimal ? 'rgba(255,255,255,0.18)' : 'rgba(47,107,255,0.10)',
|
||
});
|
||
},
|
||
titleStyle() {
|
||
return this.$util.objToStyle({
|
||
color: this.v.textColor || (this.isMinimal ? CardTokens.color.text : '#FFFFFF'),
|
||
fontSize: px2rpx(num(this.v.titleSize, 16)) + 'rpx',
|
||
});
|
||
},
|
||
subStyle() {
|
||
return this.$util.objToStyle({
|
||
color: this.v.textColor || (this.isMinimal ? CardTokens.color.textSub : '#FFFFFF'),
|
||
fontSize: px2rpx(num(this.v.subSize, 12)) + 'rpx',
|
||
opacity: this.isMinimal ? 1 : 0.85,
|
||
});
|
||
},
|
||
btnStyle() {
|
||
const v = this.v;
|
||
const radius = px2rpx(CardTokens.radius.pill) + 'rpx';
|
||
const pad = '0 ' + px2rpx(CardTokens.space.xl) + 'rpx';
|
||
if (this.buttonStyle === 'ghost') {
|
||
return this.$util.objToStyle({
|
||
border: '2rpx solid ' + (v.accentColor || '#FFEB3B'),
|
||
borderRadius: radius,
|
||
padding: pad,
|
||
height: px2rpx(CardTokens.touch.chipHeight) + 'rpx',
|
||
});
|
||
}
|
||
if (this.buttonStyle === 'link') {
|
||
return this.$util.objToStyle({ padding: '0 0 0 ' + px2rpx(CardTokens.space.sm) + 'rpx' });
|
||
}
|
||
return this.$util.objToStyle({
|
||
background: v.accentColor || '#FFEB3B',
|
||
borderRadius: radius,
|
||
padding: pad,
|
||
height: px2rpx(CardTokens.touch.chipHeight) + 'rpx',
|
||
});
|
||
},
|
||
btnTextStyle() {
|
||
const v = this.v;
|
||
const fontSize = px2rpx(num(this.v.btnSize, 12)) + 'rpx';
|
||
if (this.buttonStyle === 'ghost') {
|
||
return this.$util.objToStyle({ color: v.accentColor || '#FFEB3B', fontSize: fontSize });
|
||
}
|
||
if (this.buttonStyle === 'link') {
|
||
return this.$util.objToStyle({ color: v.textColor || CardTokens.color.primary, fontSize: fontSize });
|
||
}
|
||
return this.$util.objToStyle({ color: '#333333', fontSize: fontSize });
|
||
},
|
||
/**
|
||
* 文字箭头按钮的箭头: 仅 link 档渲染, 颜色与该档按钮文字一致;
|
||
* 字号比按钮文字略大一档(默认 14px = 28rpx, 与历史渲染一致)
|
||
*/
|
||
btnArrowStyle() {
|
||
return this.$util.objToStyle({
|
||
color: this.v.textColor || CardTokens.color.primary,
|
||
fontSize: px2rpx(num(this.v.btnSize, 12) + 2) + 'rpx',
|
||
});
|
||
},
|
||
},
|
||
methods: {
|
||
goLink() {
|
||
const v = this.v;
|
||
let url = '';
|
||
if (v.linkMode === 'contacts') {
|
||
url = '/pages_tool/card/contacts';
|
||
} else if (v.linkMode === 'custom') {
|
||
url = String(v.customUrl || '').trim();
|
||
if (url !== '' && url.indexOf('/') !== 0) {
|
||
uni.showToast({ title: '自定义路径需以 / 开头', icon: 'none' });
|
||
return;
|
||
}
|
||
} else if (v.linkMode === 'link') {
|
||
// 平台统一「选择链接」: link 是个对象({name,title,wap_url,page_key,page_params}),
|
||
// 走 $util.diyRedirectTo 由平台统一处理(内部页/外链/小程序/拨号 等)
|
||
const link = v.link && typeof v.link === 'object' ? v.link : null;
|
||
if (!link || !link.name) {
|
||
uni.showToast({ title: '请先选择链接', icon: 'none' });
|
||
return;
|
||
}
|
||
this.$util.diyRedirectTo(link);
|
||
return;
|
||
} else {
|
||
url = '/pages_tool/card/list';
|
||
}
|
||
if (url === '') {
|
||
uni.showToast({ title: '未配置跳转路径', icon: 'none' });
|
||
return;
|
||
}
|
||
this.$util.redirectTo(url);
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.diy-card-entry {
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
display: flex;
|
||
align-items: center;
|
||
overflow: hidden;
|
||
|
||
&.entry-center {
|
||
justify-content: center;
|
||
text-align: center;
|
||
|
||
.entry-body {
|
||
align-items: center;
|
||
}
|
||
}
|
||
|
||
.entry-icon {
|
||
flex-shrink: 0;
|
||
width: 96rpx;
|
||
height: 96rpx;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
overflow: hidden;
|
||
|
||
.icon-img {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: block;
|
||
}
|
||
|
||
.icon-glyph {
|
||
font-size: 48rpx;
|
||
}
|
||
}
|
||
|
||
.entry-body {
|
||
flex: 1;
|
||
min-width: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
.entry-title {
|
||
font-size: 32rpx;
|
||
font-weight: 700;
|
||
line-height: 1.4;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 1;
|
||
-webkit-box-orient: vertical;
|
||
}
|
||
|
||
.entry-sub {
|
||
font-size: 24rpx;
|
||
line-height: 1.5;
|
||
margin-top: 8rpx;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
}
|
||
}
|
||
|
||
.entry-btn {
|
||
flex-shrink: 0;
|
||
margin-left: 20rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
box-sizing: border-box;
|
||
|
||
.entry-btn-text {
|
||
font-size: 24rpx;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.btn-arrow {
|
||
font-size: 28rpx;
|
||
margin-left: 2rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.entry-press {
|
||
transform: scale(0.985);
|
||
opacity: 0.92;
|
||
}
|
||
|
||
.btn-press {
|
||
opacity: 0.72;
|
||
}
|
||
</style>
|