feat(diy-card): 增加可配置的外边距和头部样式属性

This commit is contained in:
2026-10-26 08:35:34 +08:00
parent 526b513345
commit d291f5a6c1
5 changed files with 93 additions and 53 deletions

View File

@@ -1,9 +1,9 @@
<template> <template>
<view data-component-name="diy-card-entry" class="diy-card-entry" :class="'entry-' + stylePreset" :style="wrapStyle" hover-class="entry-press" @tap="goLink"> <view data-component-name="diy-card-entry" class="diy-card-entry" :class="['entry-' + stylePreset, { 'entry-center': isCenter }]" :style="wrapStyle" hover-class="entry-press" @tap="goLink">
<!-- 图标装饰(可选) --> <!-- 图标装饰(可选) -->
<view v-if="showIcon" class="entry-icon" :style="iconWrapStyle"> <view v-if="showIcon" class="entry-icon" :style="iconWrapStyle">
<image v-if="iconType === 'image' && iconImage" class="icon-img" :src="$util.img(iconImage)" mode="aspectFit" /> <image v-if="iconType === 'image' && iconImage" class="icon-img" :src="$util.img(iconImage)" mode="aspectFit" />
<text v-else class="icon-glyph">{{ iconGlyph || '💼' }}</text> <text v-else class="icon-glyph" :style="iconGlyphStyle">{{ iconGlyph || '名' }}</text>
</view> </view>
<view class="entry-body"> <view class="entry-body">
@@ -68,7 +68,7 @@ export default {
}, },
wrapStyle() { wrapStyle() {
const v = this.v; const v = this.v;
const radius = px2rpx(num(v.borderRadius, CardTokens.radius.md)) + 'rpx'; const radius = px2rpx(num(v.borderRadius, 12)) + 'rpx';
const pad = px2rpx(num(v.padding, CardTokens.space.lg)) + 'rpx'; const pad = px2rpx(num(v.padding, CardTokens.space.lg)) + 'rpx';
if (this.isMinimal) { if (this.isMinimal) {
return this.$util.objToStyle({ return this.$util.objToStyle({
@@ -91,13 +91,18 @@ export default {
background: v.bgColorStart || CardTokens.color.primary, background: v.bgColorStart || CardTokens.color.primary,
borderRadius: radius, borderRadius: radius,
padding: pad, padding: pad,
borderLeft: '8rpx solid ' + (v.bgColorEnd || 'rgba(255,255,255,0.35)'), borderLeft: '8rpx solid ' + (v.bgColorEnd || '#1E40AF'),
minHeight: '120rpx', minHeight: '120rpx',
}); });
}, },
iconWrapStyle() { iconWrapStyle() {
return this.$util.objToStyle({ return this.$util.objToStyle({
background: this.isGradient || !this.isMinimal ? 'rgba(255,255,255,0.18)' : 'rgba(47,107,255,0.10)', background: this.isMinimal ? 'rgba(47,107,255,0.10)' : 'rgba(255,255,255,0.18)',
});
},
iconGlyphStyle() {
return this.$util.objToStyle({
color: this.v.textColor || (this.isMinimal ? CardTokens.color.text : '#FFFFFF'),
}); });
}, },
titleStyle() { titleStyle() {
@@ -110,7 +115,7 @@ export default {
return this.$util.objToStyle({ return this.$util.objToStyle({
color: this.v.textColor || (this.isMinimal ? CardTokens.color.textSub : '#FFFFFF'), color: this.v.textColor || (this.isMinimal ? CardTokens.color.textSub : '#FFFFFF'),
fontSize: px2rpx(num(this.v.subSize, 12)) + 'rpx', fontSize: px2rpx(num(this.v.subSize, 12)) + 'rpx',
opacity: this.isMinimal ? 1 : 0.85, opacity: this.isMinimal ? 0.65 : 0.85,
}); });
}, },
btnStyle() { btnStyle() {
@@ -142,7 +147,10 @@ export default {
return this.$util.objToStyle({ color: v.accentColor || '#FFEB3B', fontSize: fontSize }); return this.$util.objToStyle({ color: v.accentColor || '#FFEB3B', fontSize: fontSize });
} }
if (this.buttonStyle === 'link') { if (this.buttonStyle === 'link') {
return this.$util.objToStyle({ color: v.textColor || CardTokens.color.primary, fontSize: fontSize }); return this.$util.objToStyle({
color: this.isMinimal ? (v.accentColor || '#FFEB3B') : (v.textColor || '#FFFFFF'),
fontSize: fontSize,
});
} }
return this.$util.objToStyle({ color: '#333333', fontSize: fontSize }); return this.$util.objToStyle({ color: '#333333', fontSize: fontSize });
}, },
@@ -152,7 +160,7 @@ export default {
*/ */
btnArrowStyle() { btnArrowStyle() {
return this.$util.objToStyle({ return this.$util.objToStyle({
color: this.v.textColor || CardTokens.color.primary, color: this.isMinimal ? (this.v.accentColor || '#FFEB3B') : (this.v.textColor || '#FFFFFF'),
fontSize: px2rpx(num(this.v.btnSize, 12) + 2) + 'rpx', fontSize: px2rpx(num(this.v.btnSize, 12) + 2) + 'rpx',
}); });
}, },

View File

@@ -124,13 +124,13 @@ export default {
wallStyle() { wallStyle() {
return this.$util.objToStyle({ return this.$util.objToStyle({
background: this.v.bgColor || CardTokens.color.bg, background: this.v.bgColor || CardTokens.color.bg,
borderRadius: px2rpx(num(this.v.borderRadius, CardTokens.radius.md)) + 'rpx', borderRadius: px2rpx(num(this.v.borderRadius, 12)) + 'rpx',
padding: px2rpx(num(this.v.padding, CardTokens.space.md)) + 'rpx', padding: px2rpx(num(this.v.padding, CardTokens.space.md)) + 'rpx',
}); });
}, },
titleStyle() { titleStyle() {
return this.$util.objToStyle({ return this.$util.objToStyle({
color: this.v.titleColor || CardTokens.color.text, color: this.v.titleColor || '#333333',
fontSize: px2rpx(num(this.v.titleSize, 16)) + 'rpx', fontSize: px2rpx(num(this.v.titleSize, 16)) + 'rpx',
}); });
}, },
@@ -151,20 +151,20 @@ export default {
/** 网格/列表卡片信息行: 字号可配(默认 11px = 22rpx) */ /** 网格/列表卡片信息行: 字号可配(默认 11px = 22rpx) */
metaStyle() { metaStyle() {
return this.$util.objToStyle({ return this.$util.objToStyle({
color: this.v.infoColor || CardTokens.color.textSub, color: this.v.infoColor || '#888888',
fontSize: px2rpx(num(this.v.infoSize, 11)) + 'rpx', fontSize: px2rpx(num(this.v.infoSize, 11)) + 'rpx',
}); });
}, },
/** 横滑竖卡信息行: 比网格小一档(默认 10px = 20rpx, 与历史渲染一致) */ /** 横滑竖卡信息行: 比网格小一档(默认 10px = 20rpx, 与历史渲染一致) */
scrollMetaStyle() { scrollMetaStyle() {
return this.$util.objToStyle({ return this.$util.objToStyle({
color: this.v.infoColor || CardTokens.color.textSub, color: this.v.infoColor || '#888888',
fontSize: px2rpx(num(this.v.infoSize, 11) - 1) + 'rpx', fontSize: px2rpx(num(this.v.infoSize, 11) - 1) + 'rpx',
}); });
}, },
/** infoColor 纯色(箭头/空态用, 不带字号) */ /** infoColor 纯色(箭头/空态用, 不带字号) */
infoStyle() { infoStyle() {
return this.$util.objToStyle({ color: this.v.infoColor || CardTokens.color.textSub }); return this.$util.objToStyle({ color: this.v.infoColor || '#888888' });
}, },
countStyle() { countStyle() {
return this.$util.objToStyle({ return this.$util.objToStyle({
@@ -211,12 +211,11 @@ export default {
}, },
methods: { methods: {
/** /**
* 列表项最终样式: itemStyle + 逐项入场动画延迟 合并输出单对象。 * 列表项最终样式: itemStyle 字符串 + 逐项入场动画延迟合并输出单字符串。
* uni-app 运行时会静默丢弃 :style="[A, B]" 数组绑定(2026-10-24 mini.vue * 不能用 Object.assign 展开样式字符串(会拆成字符索引),也不能绑定样式数组。
* 操作区同根因修复的先例), 数组形态禁止回归
*/ */
itemFinalStyle(index) { itemFinalStyle(index) {
return Object.assign({}, this.itemStyle, { animationDelay: (index * 60) + 'ms' }); return ((this.itemStyle || '') + ' animation-delay: ' + (index * 60) + 'ms;').trim();
}, },
initialOf, initialOf,
avatarStyle(item) { avatarStyle(item) {

View File

@@ -26,7 +26,7 @@
<!-- 名片卡(精品名片 show 同款版式: 蓝底顶带 + 白卡压边; 角标压在左上) --> <!-- 名片卡(精品名片 show 同款版式: 蓝底顶带 + 白卡压边; 角标压在左上) -->
<view v-else class="mini-card-wrap"> <view v-else class="mini-card-wrap">
<view v-if="bandShow" class="mini-band" :style="bandStyle"></view> <view v-if="bandShow" class="mini-band" :style="bandStyle"></view>
<view class="mini-card fade-up" :style="wrapStyle" :class="{ 'qr-at-corner': qrPos === 'corner' }"> <view class="mini-card fade-up" :style="wrapStyle" :class="{ 'qr-at-corner': showQrCorner }">
<!-- 角标插槽(如 self 组件的「我的名片」) --> <!-- 角标插槽(如 self 组件的「我的名片」) -->
<view class="mini-badge-slot"> <view class="mini-badge-slot">
<slot name="badge"></slot> <slot name="badge"></slot>
@@ -267,18 +267,16 @@ export default {
}, },
wrapStyle() { wrapStyle() {
const c = this.c; const c = this.c;
const style = { return this.$util.objToStyle({
background: flagOn(c.useCover, false) && this.card && this.card.cover ? 'transparent' : (c.bgColor || CardTokens.color.bg), background: flagOn(c.useCover, false) && this.card && this.card.cover ? 'transparent' : (c.bgColor || CardTokens.color.bg),
borderRadius: px2rpx(num(c.borderRadius, CardTokens.radius.md)) + 'rpx', borderRadius: px2rpx(num(c.borderRadius, CardTokens.radius.md)) + 'rpx',
padding: px2rpx(num(c.padding, CardTokens.space.lg)) + 'rpx', padding: px2rpx(num(c.padding, CardTokens.space.lg)) + 'rpx',
marginTop: px2rpx(num(c.wrapMarginTop, 0)) + 'rpx',
marginRight: px2rpx(num(c.wrapMarginRight, 0)) + 'rpx',
marginBottom: px2rpx(num(c.wrapMarginBottom, 0)) + 'rpx',
marginLeft: px2rpx(num(c.wrapMarginLeft, 0)) + 'rpx',
boxShadow: flagOn(c.showShadow, true) ? CardTokens.shadow.card : 'none', boxShadow: flagOn(c.showShadow, true) ? CardTokens.shadow.card : 'none',
}; });
// 2.19.11: 外边距可配
const mt = num(c.wrapMarginTop, 0);
const mb = num(c.wrapMarginBottom, 0);
if (mt > 0) style.marginTop = px2rpx(mt) + 'rpx';
if (mb > 0) style.marginBottom = px2rpx(mb) + 'rpx';
return this.$util.objToStyle(style);
}, },
showCover() { showCover() {
return flagOn(this.c.useCover, false) && !!(this.card && this.card.cover); return flagOn(this.c.useCover, false) && !!(this.card && this.card.cover);
@@ -287,8 +285,8 @@ export default {
return flagOn(this.c.showGradient, false); return flagOn(this.c.showGradient, false);
}, },
showLogo() { showLogo() {
// corner 档二维码占右上同一位, Logo 自动避让(面板有提示) // 只有 corner 二维码实际显示时才避让;关闭二维码或无码源不隐藏 Logo。
return flagOn(this.c.showLogo, false) && this.qrPos !== 'corner'; return flagOn(this.c.showLogo, false) && !this.showQrCorner;
}, },
showActs() { showActs() {
return flagOn(this.c.showQuick, true); return flagOn(this.c.showQuick, true);
@@ -414,22 +412,33 @@ export default {
return normalizeHeadStyle(this.c.headStyle) === 'theme'; return normalizeHeadStyle(this.c.headStyle) === 'theme';
}, },
/** /**
* theme 档头部块: 渐变通铺卡边(负 margin 抵消 wrapStyle 的 padding, WXSS 不用 calc) * 头部容器样式(plain/theme 共用)。未开启自定义间距时完整保留历史布局:
* 内部左右 padding = 卡片原 padding, 保证头像与下方资料行左缘对齐 * theme 负外边距抵消卡片 padding,plain 不额外设置间距。
*/ */
headThemeStyle() { headBoxStyle() {
const pad = px2rpx(num(this.c.padding, CardTokens.space.lg)); const c = this.c;
const custom = flagOn(c.headSpacingCustom, false);
const style = { const style = {
margin: '-' + pad + 'rpx -' + pad + 'rpx 0', background: c.headBgMode === 'custom'
? (String(c.headBg || '').trim() || 'transparent')
: (this.headTheme ? themeGradient(this.card) : 'transparent'),
marginTop: px2rpx(custom ? num(c.headMarginTop, 0) : (this.headTheme ? -num(c.padding, CardTokens.space.lg) : 0)) + 'rpx',
marginRight: px2rpx(custom ? num(c.headMarginRight, 0) : (this.headTheme ? -num(c.padding, CardTokens.space.lg) : 0)) + 'rpx',
marginBottom: px2rpx(custom ? num(c.headMarginBottom, 0) : 0) + 'rpx',
marginLeft: px2rpx(custom ? num(c.headMarginLeft, 0) : (this.headTheme ? -num(c.padding, CardTokens.space.lg) : 0)) + 'rpx',
paddingTop: px2rpx(custom ? num(c.headPaddingTop, 16) : (this.headTheme ? 16 : 0)) + 'rpx',
paddingRight: px2rpx(custom ? num(c.headPaddingRight, 16) : (this.headTheme ? num(c.padding, CardTokens.space.lg) : 0)) + 'rpx',
paddingBottom: px2rpx(custom ? num(c.headPaddingBottom, 14) : (this.headTheme ? 14 : 0)) + 'rpx',
paddingLeft: px2rpx(custom ? num(c.headPaddingLeft, 16) : (this.headTheme ? num(c.padding, CardTokens.space.lg) : 0)) + 'rpx',
borderRadius: c.headRadiusMode === 'custom'
? px2rpx(num(c.headRadius, 8)) + 'rpx'
: (this.headTheme ? '0 0 16rpx 16rpx' : '0'),
}; };
// 2.19.11: headBg 自定义或主题渐变; headPadV/headPadH 可配
const bg = String(this.c.headBg || '').trim();
style.background = bg || themeGradient(this.card);
const padV = num(this.c.headPadV, 32);
const padH = num(this.c.headPadH, 0) || num(this.c.padding, CardTokens.space.lg);
style.padding = px2rpx(padV) + 'rpx ' + px2rpx(padH) + 'rpx';
return this.$util.objToStyle(style); return this.$util.objToStyle(style);
}, },
headThemeStyle() {
return this.headBoxStyle;
},
/** 资料区标题(2.17): 默认关(历史配置无此字段时不出标题) */ /** 资料区标题(2.17): 默认关(历史配置无此字段时不出标题) */
rowsTitleShow() { rowsTitleShow() {
return flagOn(this.c.showRowsTitle, false); return flagOn(this.c.showRowsTitle, false);
@@ -570,17 +579,14 @@ export default {
gradientStyle() { gradientStyle() {
return this.$util.objToStyle({ background: themeGradient(this.card) }); return this.$util.objToStyle({ background: themeGradient(this.card) });
}, },
/** 2.19.11: 顶带显隐(默认关) */ /** 顶部色带默认隐藏;背景可跟随名片设计或使用独立颜色。 */
bandShow() { bandShow() {
return flagOn(this.c.bandShow, false); return flagOn(this.c.bandShow, false);
}, },
bandStyle() { bandStyle() {
// 2.19.11: bandBg 自定义或名片主题渐变; bandHeight 控高度 const custom = this.c.bandBgMode === 'custom';
const bg = String(this.c.bandBg || '').trim();
const h = num(this.c.bandHeight, 8);
return this.$util.objToStyle({ return this.$util.objToStyle({
background: bg || themeGradient(this.card), background: custom ? (String(this.c.bandBg || '').trim() || 'transparent') : themeGradient(this.card),
height: px2rpx(h) + 'rpx',
}); });
}, },
shareBtnStyle() { shareBtnStyle() {
@@ -836,9 +842,10 @@ export default {
if (transform) parts.push('transform:' + transform + ';'); if (transform) parts.push('transform:' + transform + ';');
return parts.filter(Boolean).join(' ').trim(); return parts.filter(Boolean).join(' ').trim();
}, },
/** plain 头最终样式 */ /** plain 头最终样式:头部容器配置与 corner 二维码让位合并为单字符串。 */
plainHeadFinalStyle() { plainHeadFinalStyle() {
return this.cornerHeadStyle; const corner = this.$util.objToStyle(this.cornerHeadStyle || {});
return [this.headBoxStyle, corner].filter(Boolean).join(' ').trim();
}, },
detailBtnStyle() { detailBtnStyle() {
// 发送名片大按钮: 纯色主题实心(2026-10-22 参考图定稿, 不再用渐变); // 发送名片大按钮: 纯色主题实心(2026-10-22 参考图定稿, 不再用渐变);

View File

@@ -140,14 +140,27 @@ export default {
qrBlockBg: v.qrBlockBg, qrBlockBg: v.qrBlockBg,
qrBlockRadius: v.qrBlockRadius, qrBlockRadius: v.qrBlockRadius,
qrBlockPad: v.qrBlockPad, qrBlockPad: v.qrBlockPad,
// 顶部色带 / 卡片四向外边距 / 头部背景与盒模型
bandShow: v.bandShow, bandShow: v.bandShow,
bandBgMode: v.bandBgMode,
bandBg: v.bandBg, bandBg: v.bandBg,
bandHeight: v.bandHeight,
wrapMarginTop: v.wrapMarginTop, wrapMarginTop: v.wrapMarginTop,
wrapMarginRight: v.wrapMarginRight,
wrapMarginBottom: v.wrapMarginBottom, wrapMarginBottom: v.wrapMarginBottom,
wrapMarginLeft: v.wrapMarginLeft,
headBgMode: v.headBgMode,
headBg: v.headBg, headBg: v.headBg,
headPadV: v.headPadV, headRadiusMode: v.headRadiusMode,
headPadH: v.headPadH, headRadius: v.headRadius,
headSpacingCustom: v.headSpacingCustom,
headMarginTop: v.headMarginTop,
headMarginRight: v.headMarginRight,
headMarginBottom: v.headMarginBottom,
headMarginLeft: v.headMarginLeft,
headPaddingTop: v.headPaddingTop,
headPaddingRight: v.headPaddingRight,
headPaddingBottom: v.headPaddingBottom,
headPaddingLeft: v.headPaddingLeft,
}; };
}, },
boxStyle() { boxStyle() {

View File

@@ -105,14 +105,27 @@ export default {
qrBlockBg: v.qrBlockBg, qrBlockBg: v.qrBlockBg,
qrBlockRadius: v.qrBlockRadius, qrBlockRadius: v.qrBlockRadius,
qrBlockPad: v.qrBlockPad, qrBlockPad: v.qrBlockPad,
// 顶部色带 / 卡片四向外边距 / 头部背景与盒模型
bandShow: v.bandShow, bandShow: v.bandShow,
bandBgMode: v.bandBgMode,
bandBg: v.bandBg, bandBg: v.bandBg,
bandHeight: v.bandHeight,
wrapMarginTop: v.wrapMarginTop, wrapMarginTop: v.wrapMarginTop,
wrapMarginRight: v.wrapMarginRight,
wrapMarginBottom: v.wrapMarginBottom, wrapMarginBottom: v.wrapMarginBottom,
wrapMarginLeft: v.wrapMarginLeft,
headBgMode: v.headBgMode,
headBg: v.headBg, headBg: v.headBg,
headPadV: v.headPadV, headRadiusMode: v.headRadiusMode,
headPadH: v.headPadH, headRadius: v.headRadius,
headSpacingCustom: v.headSpacingCustom,
headMarginTop: v.headMarginTop,
headMarginRight: v.headMarginRight,
headMarginBottom: v.headMarginBottom,
headMarginLeft: v.headMarginLeft,
headPaddingTop: v.headPaddingTop,
headPaddingRight: v.headPaddingRight,
headPaddingBottom: v.headPaddingBottom,
headPaddingLeft: v.headPaddingLeft,
}; };
}, },
}, },