Compare commits
32 Commits
557addb76b
...
temp2797
| Author | SHA1 | Date | |
|---|---|---|---|
| 3b81ecdf98 | |||
| c536fac4d2 | |||
| 475449f04e | |||
| e40b46a888 | |||
| a81386d336 | |||
| b69eeba74b | |||
| 82139e616d | |||
| ed6d9f563d | |||
| 3a8ad7d871 | |||
| ec960a29c4 | |||
| 8312de5d09 | |||
| 5833a244de | |||
| 92fe95d131 | |||
| 07718235c3 | |||
| 183181a15d | |||
| a4f2c81528 | |||
| 30b66ad7b3 | |||
| 077fac40c2 | |||
| 10d45b1c3b | |||
| 60dc054383 | |||
| ef8c7568ac | |||
| 551d6c103d | |||
| 4a92d9bec2 | |||
| ff4340c3a9 | |||
| 4303a93278 | |||
| 12e4493a17 | |||
| c94e214d05 | |||
| afede79047 | |||
| ab727eef1d | |||
| ee050f5df9 | |||
| 855fbb2fe5 | |||
| b706e67e82 |
15
.gitignore
vendored
15
.gitignore
vendored
@@ -61,6 +61,21 @@ stub-app-projects/mp-harmony/hvigor
|
|||||||
# Trae/IDE 临时文件
|
# Trae/IDE 临时文件
|
||||||
.hvigor/outputs
|
.hvigor/outputs
|
||||||
.trae_permission_check_*
|
.trae_permission_check_*
|
||||||
|
|
||||||
|
# 本地测试脚本与产物归档(不入 git, 留本地复用)
|
||||||
|
# 内容: Playwright 截图 / 测试脚本 / 对比图 / 临时提交草稿
|
||||||
|
# 复用方式: cd _test_archive && python xxx.py
|
||||||
|
_test_archive/
|
||||||
|
|
||||||
|
# 兜底:防止任何临时测试文件被遗漏在根目录
|
||||||
|
# 名称以 _test_/_compare_/_auth_ 开头, 或 check_button.py 等
|
||||||
|
_test_*.png
|
||||||
|
_test_*.html
|
||||||
|
_compare*.png
|
||||||
|
_compare*.py
|
||||||
|
_auth_*.json
|
||||||
|
_commit_msg.txt
|
||||||
|
/check_button.py
|
||||||
# 鸿蒙签名材料 (敏感, 绝不能提交到公共 git)
|
# 鸿蒙签名材料 (敏感, 绝不能提交到公共 git)
|
||||||
# 仓库有 origin (github.com/Zajd/jsysq-uniapp) 是公开仓库, 签名材料必须 gitignore
|
# 仓库有 origin (github.com/Zajd/jsysq-uniapp) 是公开仓库, 签名材料必须 gitignore
|
||||||
stub-app-projects/mp-harmony/**/cert-profile
|
stub-app-projects/mp-harmony/**/cert-profile
|
||||||
|
|||||||
@@ -156,6 +156,14 @@ const localDevConfig = ({
|
|||||||
uniacid: 248,
|
uniacid: 248,
|
||||||
domain: 'https://5g.aigc-quickapp.com/',
|
domain: 'https://5g.aigc-quickapp.com/',
|
||||||
},
|
},
|
||||||
|
'228': { // 商品分类,没有立即咨询文字
|
||||||
|
uniacid: 228,
|
||||||
|
domain: 'https://5g.aigc-quickapp.com/',
|
||||||
|
},
|
||||||
|
'2797': { //
|
||||||
|
uniacid: 2797,
|
||||||
|
domain: 'https://xcx.aigc-quickapp.com/',
|
||||||
|
},
|
||||||
// })['5g-237']; // 商品分类,没有立即咨询文字
|
// })['5g-237']; // 商品分类,没有立即咨询文字
|
||||||
// })['5g-97']; // 大宗商品贸易,支持自定义字体大小、颜色
|
// })['5g-97']; // 大宗商品贸易,支持自定义字体大小、颜色
|
||||||
// })['5g-50']; // 燃气安全产品机器人,购物车无反应
|
// })['5g-50']; // 燃气安全产品机器人,购物车无反应
|
||||||
@@ -169,7 +177,7 @@ const localDevConfig = ({
|
|||||||
// })['2724']; // 生物菌肥
|
// })['2724']; // 生物菌肥
|
||||||
// })['2811']; // 选择要使用的环境配置
|
// })['2811']; // 选择要使用的环境配置
|
||||||
// })['local-11']; // 选择要使用的环境配置
|
// })['local-11']; // 选择要使用的环境配置
|
||||||
})['2811']; // 本地
|
})['2797']; // 本地
|
||||||
// })['local-2']; // 本地
|
// })['local-2']; // 本地
|
||||||
// })['local-11']; // 本地
|
// })['local-11']; // 本地
|
||||||
// })['1-test']; // test
|
// })['1-test']; // test
|
||||||
|
|||||||
@@ -76,6 +76,9 @@
|
|||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="footer-wrap footer-wrap-template1">
|
||||||
|
<view class="detail-btn" @tap.stop="toDetail(item)">{{ $lang('components-diy.diy-category-item.consultNow') }}</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -110,6 +113,9 @@
|
|||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="footer-wrap footer-wrap-template1">
|
||||||
|
<view class="detail-btn" @tap.stop="toDetail(item)">{{ $lang('components-diy.diy-category-item.consultNow') }}</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -173,6 +179,9 @@
|
|||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
<view class="footer-wrap footer-wrap-template1">
|
||||||
|
<view class="detail-btn" @tap.stop="toDetail(item)">{{ $lang('components-diy.diy-category-item.consultNow') }}</view>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -1420,6 +1429,26 @@ export default {
|
|||||||
border-radius: 12rpx;
|
border-radius: 12rpx;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
margin-bottom: 20rpx;
|
margin-bottom: 20rpx;
|
||||||
|
|
||||||
|
.footer-wrap-template1 {
|
||||||
|
padding: 16rpx 4rpx 0rpx;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
.detail-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0 32rpx;
|
||||||
|
height: 55rpx;
|
||||||
|
background-color: #ff4545;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 25rpx;
|
||||||
|
border-radius: 26rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,6 +209,10 @@ export default {
|
|||||||
// 占位符:遗漏会被 ns-newform SCSS 默认值回退覆盖
|
// 占位符:遗漏会被 ns-newform SCSS 默认值回退覆盖
|
||||||
placeholderColor: pick('placeholderColor', 'placeholder_color'),
|
placeholderColor: pick('placeholderColor', 'placeholder_color'),
|
||||||
placeholderFontSize: pick('placeholderFontSize', 'placeholder_font_size'),
|
placeholderFontSize: pick('placeholderFontSize', 'placeholder_font_size'),
|
||||||
|
// textarea 显示行数 / 行高(透传给 ns-newform,与 ns-form 保持一致)
|
||||||
|
// 不传时 ns-newform 走自己的默认 4 行 / 44rpx
|
||||||
|
textareaRows: pick('textareaRows', 'textarea_rows'),
|
||||||
|
textareaLineHeight: pick('textareaLineHeight', 'textarea_line_height'),
|
||||||
// 输入框视觉增强(与 ns-newform 对齐)
|
// 输入框视觉增强(与 ns-newform 对齐)
|
||||||
inputBorderColor: pick('inputBorderColor', 'input_border_color'),
|
inputBorderColor: pick('inputBorderColor', 'input_border_color'),
|
||||||
inputBgColor: pick('inputBgColor', 'input_bg_color'),
|
inputBgColor: pick('inputBgColor', 'input_bg_color'),
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
<view v-if="showConsent" style="width:100%;box-sizing:border-box;margin-top:24rpx;background:#FFFFFF;border-radius:32rpx;padding:28rpx 28rpx;position:relative;overflow:hidden;box-shadow:0 4rpx 16rpx rgba(0,0,0,0.06);">
|
<view v-if="showConsent" style="width:100%;box-sizing:border-box;margin-top:24rpx;background:#FFFFFF;border-radius:32rpx;padding:28rpx 28rpx;position:relative;overflow:hidden;box-shadow:0 4rpx 16rpx rgba(0,0,0,0.06);">
|
||||||
<!-- 标题行 + 待获取标签 -->
|
<!-- 标题行 + 待获取标签 -->
|
||||||
<view style="display:flex;align-items:center;gap:12rpx;position:relative;z-index:1;">
|
<view style="display:flex;align-items:center;gap:12rpx;position:relative;z-index:1;">
|
||||||
<text :style="{ fontSize: px2rpx(value.titleFontSize || 17) + 'rpx', fontWeight: '800', color: value.titleColor || '#1A1A1A' }">{{ value.title || '我的推荐码' }}</text>
|
<text :style="$util.objToStyle({ fontSize: px2rpx(value.titleFontSize || 17) + 'px', fontWeight: '800', color: value.titleColor || '#1A1A1A' })">{{ value.title || '我的推荐码' }}</text>
|
||||||
<view style="display:inline-flex;align-items:center;background:rgba(255,152,0,0.12);border-radius:16rpx;padding:4rpx 14rpx;gap:4rpx;">
|
<view style="display:inline-flex;align-items:center;background:rgba(255,152,0,0.12);border-radius:16rpx;padding:4rpx 14rpx;gap:4rpx;">
|
||||||
<text style="font-size:18rpx;color:#FF8F00;">✓</text>
|
<text style="font-size:18rpx;color:#FF8F00;">✓</text>
|
||||||
<text style="font-size:20rpx;color:#FF8F00;font-weight:600;">待获取推荐码</text>
|
<text style="font-size:20rpx;color:#FF8F00;font-weight:600;">待获取推荐码</text>
|
||||||
@@ -87,10 +87,10 @@
|
|||||||
<!-- ============ 推荐码卡片:仅在非 consent 且非首次加载时渲染 ============ -->
|
<!-- ============ 推荐码卡片:仅在非 consent 且非首次加载时渲染 ============ -->
|
||||||
<block v-if="!showConsent && !(loadingCode && !code)">
|
<block v-if="!showConsent && !(loadingCode && !code)">
|
||||||
<!-- ============ 预设 1:Gradient 渐变风格(默认)全部内联样式 ============ -->
|
<!-- ============ 预设 1:Gradient 渐变风格(默认)全部内联样式 ============ -->
|
||||||
<view v-if="stylePreset === 'gradient'" :style="{ width: '100%', boxSizing: 'border-box', marginTop: '24rpx', background: '#FFFFFF', borderRadius: px2rpx(value.borderRadius || 8) + 'rpx', padding: px2rpx(value.padding || 16) + 'rpx ' + px2rpx(value.padding || 16) + 'rpx', position: 'relative', overflow: 'hidden', boxShadow: '0 4rpx 16rpx rgba(0,0,0,0.06)' }">
|
<view v-if="stylePreset === 'gradient'" :style="$util.objToStyle({ width: '100%', boxSizing: 'border-box', marginTop: '24rpx', background: '#FFFFFF', borderRadius: px2rpx(value.borderRadius || 8) + 'px', padding: px2rpx(value.padding || 16) + 'px ' + px2rpx(value.padding || 16) + 'px', position: 'relative', overflow: 'hidden', boxShadow: '0 4rpx 16rpx rgba(0,0,0,0.06)' })">
|
||||||
<!-- 标题行 + 已获得推荐码标签 -->
|
<!-- 标题行 + 已获得推荐码标签 -->
|
||||||
<view style="display:flex;align-items:center;gap:12rpx;position:relative;z-index:1;">
|
<view style="display:flex;align-items:center;gap:12rpx;position:relative;z-index:1;">
|
||||||
<text :style="{ fontSize: px2rpx(value.titleFontSize || 17) + 'rpx', fontWeight: '800', color: value.titleColor || '#1A1A1A' }">{{ value.title || '我的推荐码' }}</text>
|
<text :style="$util.objToStyle({ fontSize: px2rpx(value.titleFontSize || 17) + 'px', fontWeight: '800', color: value.titleColor || '#1A1A1A' })">{{ value.title || '我的推荐码' }}</text>
|
||||||
<view style="display:inline-flex;align-items:center;background:#52C41A;border-radius:16rpx;padding:4rpx 14rpx;gap:4rpx;">
|
<view style="display:inline-flex;align-items:center;background:#52C41A;border-radius:16rpx;padding:4rpx 14rpx;gap:4rpx;">
|
||||||
<text style="font-size:18rpx;color:#FFFFFF;">✓</text>
|
<text style="font-size:18rpx;color:#FFFFFF;">✓</text>
|
||||||
<text style="font-size:20rpx;color:#FFFFFF;font-weight:600;">已获得推荐码</text>
|
<text style="font-size:20rpx;color:#FFFFFF;font-weight:600;">已获得推荐码</text>
|
||||||
@@ -100,24 +100,28 @@
|
|||||||
<view style="margin-top:24rpx;background:#FFF8F0;border-radius:16rpx;padding:32rpx 20rpx 24rpx;position:relative;z-index:1;">
|
<view style="margin-top:24rpx;background:#FFF8F0;border-radius:16rpx;padding:32rpx 20rpx 24rpx;position:relative;z-index:1;">
|
||||||
<!-- 推荐码居中显示 -->
|
<!-- 推荐码居中显示 -->
|
||||||
<view style="text-align:center;">
|
<view style="text-align:center;">
|
||||||
<text :style="{ fontSize: px2rpx(Math.max(16, Math.min(48, (Number(value.codeFontSize) || 28) * 0.93))) + 'rpx', fontWeight: '900', color: value.codeColor || '#DC143C', letterSpacing: '2rpx', fontFamily: 'Courier New, monospace' }">{{ code || value.placeholderCode || 'ABCD1234' }}</text>
|
<text :style="$util.objToStyle({ fontSize: px2rpx(Math.max(16, Math.min(48, (Number(value.codeFontSize) || 28) * 0.93))) + 'px', fontWeight: '900', color: value.codeColor || '#DC143C', letterSpacing: '2rpx', fontFamily: 'Courier New, monospace' })">{{ code || value.placeholderCode || 'ABCD1234' }}</text>
|
||||||
</view>
|
</view>
|
||||||
<!-- 复制推荐码按钮:白色小按钮,红边框,在推荐码下方居中 -->
|
<!-- 复制推荐码按钮:白色小按钮,红边框,在推荐码下方居中 -->
|
||||||
<view v-if="value.showCopyButton !== false" style="margin-top:16rpx;display:flex;justify-content:center;">
|
<view v-if="value.showCopyButton !== false" style="margin-top:16rpx;text-align:center;">
|
||||||
<view @tap="copyRecommendCode" :style="{ width: 'fit-content', height: '60rpx', display: 'flex', justifyContent: 'center', alignItems: 'center', background: '#FFFFFF', border: '2rpx solid ' + (value.codeColor || '#DC143C'), borderRadius: '30rpx', padding: '0 32rpx', gap: '8rpx' }">
|
<view @tap="copyRecommendCode" :style="$util.objToStyle(copyBtnStyleObj({display:'inline-flex',alignItems:'center',borderRadius:'15px',padding:'0 16px',height:'30px',gap:'4px'}))">
|
||||||
<text style="font-size:22rpx;">📋</text>
|
<text style="font-size:22rpx;">📋</text>
|
||||||
<text :style="{ fontSize: '24rpx', color: value.codeColor || '#DC143C', fontWeight: '600' }">{{ nc.copyButtonText || '复制推荐码' }}</text>
|
<text :style="$util.objToStyle(copyBtnStyleObj({}, true))">{{ nc.copyButtonText || '复制推荐码' }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- 立即分享链接按钮:黄橙渐变,胶囊形圆角,紧凑布局
|
<!-- 立即分享链接按钮:黄橙渐变,胶囊形圆角,紧凑布局
|
||||||
固定走 linear-gradient,不读 diyData.buttonBgColor(避免被后端默认色覆盖)
|
固定走 linear-gradient,不读 diyData.buttonBgColor(避免被后端默认色覆盖)
|
||||||
H5 端用 view 触发(避免 button 默认样式),小程序/APP 端保留 button 以触发 open-type=share -->
|
H5 端用 view 触发(避免 button 默认样式),小程序/APP 端保留 button 以触发 open-type=share -->
|
||||||
<button v-if="value.showShareButton !== false && !isH5Platform" open-type="share" @tap="onShareClick" :style="{ marginTop: '16rpx', height: '80rpx', width: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center', background: 'linear-gradient(90deg,#FFC107 0%,#FF8F00 100%)', borderRadius: '40rpx', boxShadow: '0 4rpx 12rpx rgba(255,143,0,0.3)', position: 'relative', zIndex: '1', border: 'none', lineHeight: '1' }">
|
<button v-if="value.showShareButton !== false && !isH5Platform" open-type="share" @tap="onShareClick" style="position:relative;left:0;right:0;margin:16rpx 0 0 0;width:100%;text-align:left;background:transparent;border:0;padding:0;line-height:normal;display:block;box-sizing:border-box;">
|
||||||
<text :style="{ fontSize: '28rpx', fontWeight: '700', color: '#FFFFFF' }">📤 {{ value.buttonText || '立即分享' }}</text>
|
<view style="display:block;width:100%;height:80rpx;line-height:80rpx;background:linear-gradient(90deg,#FFC107 0%,#FF8F00 100%);border-radius:40rpx;box-shadow:0 4rpx 12rpx rgba(255,143,0,0.3);box-sizing:border-box;text-align:center;">
|
||||||
|
<text style="font-size:28rpx;font-weight:700;color:#FFFFFF;">📤 {{ value.buttonText || '立即分享' }}</text>
|
||||||
|
</view>
|
||||||
</button>
|
</button>
|
||||||
<view v-else-if="value.showShareButton !== false" @tap="onShareClick" :style="{ marginTop: '16rpx', height: '80rpx', width: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center', background: 'linear-gradient(90deg,#FFC107 0%,#FF8F00 100%)', borderRadius: '40rpx', boxShadow: '0 4rpx 12rpx rgba(255,143,0,0.3)', position: 'relative', zIndex: '1', lineHeight: '1', boxSizing: 'border-box' }">
|
<view v-else-if="value.showShareButton !== false" @tap="onShareClick" style="margin-top:16rpx;width:100%;">
|
||||||
<text :style="{ fontSize: '28rpx', fontWeight: '700', color: '#FFFFFF' }">📤 {{ nc.buttonText || '立即分享' }}</text>
|
<view style="display:block;height:80rpx;line-height:80rpx;background:linear-gradient(90deg,#FFC107 0%,#FF8F00 100%);border-radius:40rpx;box-shadow:0 4rpx 12rpx rgba(255,143,0,0.3);box-sizing:border-box;text-align:center;">
|
||||||
|
<text style="font-size:28rpx;font-weight:700;color:#FFFFFF;">📤 {{ nc.buttonText || '立即分享' }}</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- showLink -->
|
<!-- showLink -->
|
||||||
<view v-if="isFeatureOn(value.showLink)" style="margin-top:20rpx;text-align:center;position:relative;z-index:1;">
|
<view v-if="isFeatureOn(value.showLink)" style="margin-top:20rpx;text-align:center;position:relative;z-index:1;">
|
||||||
@@ -125,7 +129,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<!-- showQR -->
|
<!-- showQR -->
|
||||||
<view v-if="isFeatureOn(value.showQR)" style="margin-top:20rpx;display:flex;flex-direction:column;align-items:center;position:relative;z-index:1;">
|
<view v-if="isFeatureOn(value.showQR)" style="margin-top:20rpx;display:flex;flex-direction:column;align-items:center;position:relative;z-index:1;">
|
||||||
<view :style="{ width: px2rpx(nc.qrSize || 200) + 'rpx', height: px2rpx(nc.qrSize || 200) + 'rpx', background: nc.qrBoxBackground || 'transparent', border: nc.qrBoxBorderWidth && nc.qrBoxBorderColor ? (px2rpx(nc.qrBoxBorderWidth) + 'rpx solid ' + nc.qrBoxBorderColor) : 'none', borderRadius: px2rpx(nc.qrBoxBorderRadius || 0) + 'rpx', padding: px2rpx(nc.qrBoxPadding || 0) + 'rpx', boxSizing: 'border-box' }">
|
<view :style="$util.objToStyle({ width: px2rpx(nc.qrSize || 200) + 'px', height: px2rpx(nc.qrSize || 200) + 'px', background: nc.qrBoxBackground || 'transparent', border: nc.qrBoxBorderWidth && nc.qrBoxBorderColor ? (px2rpx(nc.qrBoxBorderWidth) + 'px solid ' + nc.qrBoxBorderColor) : 'none', borderRadius: px2rpx(nc.qrBoxBorderRadius || 0) + 'px', padding: px2rpx(nc.qrBoxPadding || 0) + 'px', boxSizing: 'border-box' })">
|
||||||
<image :src="qrcodeRenderUrl" mode="aspectFit" style="width:100%;height:100%;display:block;" />
|
<image :src="qrcodeRenderUrl" mode="aspectFit" style="width:100%;height:100%;display:block;" />
|
||||||
</view>
|
</view>
|
||||||
<text style="font-size:22rpx;color:#999;margin-top:12rpx;">{{ value.posterQrTip || '扫码进入并填写入驻信息' }}</text>
|
<text style="font-size:22rpx;color:#999;margin-top:12rpx;">{{ value.posterQrTip || '扫码进入并填写入驻信息' }}</text>
|
||||||
@@ -133,7 +137,7 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- ============ 预设 2:Minimal 极简风格(全部内联样式) ============ -->
|
<!-- ============ 预设 2:Minimal 极简风格(全部内联样式) ============ -->
|
||||||
<view v-if="stylePreset === 'minimal'" :style="{ width: '100%', boxSizing: 'border-box', marginTop: '24rpx', background: 'linear-gradient(90deg,#FFFFFF 0%,#FFF8F8 30%,#FFF5F5 60%,#FFF0F0 100%)', borderRadius: px2rpx(value.borderRadius || 8) + 'rpx', padding: px2rpx(value.padding || 16) + 'rpx ' + px2rpx(value.padding || 16) + 'rpx', position: 'relative', overflow: 'hidden', boxShadow: '0 4rpx 16rpx rgba(255,107,107,0.08)' }">
|
<view v-if="stylePreset === 'minimal'" :style="$util.objToStyle({ width: '100%', boxSizing: 'border-box', marginTop: '24rpx', background: 'linear-gradient(90deg,#FFFFFF 0%,#FFF8F8 30%,#FFF5F5 60%,#FFF0F0 100%)', borderRadius: px2rpx(value.borderRadius || 8) + 'px', padding: px2rpx(value.padding || 16) + 'px ' + px2rpx(value.padding || 16) + 'px', position: 'relative', overflow: 'hidden', boxShadow: '0 4rpx 16rpx rgba(255,107,107,0.08)' })">
|
||||||
<!-- 背景礼物装饰大图1(右上角,移至顶部) -->
|
<!-- 背景礼物装饰大图1(右上角,移至顶部) -->
|
||||||
<view style="position:absolute;top:-20rpx;right:-30rpx;z-index:0;">
|
<view style="position:absolute;top:-20rpx;right:-30rpx;z-index:0;">
|
||||||
<text style="font-size:200rpx;opacity:0.15;">🎁</text>
|
<text style="font-size:200rpx;opacity:0.15;">🎁</text>
|
||||||
@@ -154,7 +158,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<!-- 标题行 + 已获得标签 -->
|
<!-- 标题行 + 已获得标签 -->
|
||||||
<view style="display:flex;align-items:center;gap:12rpx;position:relative;z-index:1;">
|
<view style="display:flex;align-items:center;gap:12rpx;position:relative;z-index:1;">
|
||||||
<text :style="{ fontSize: px2rpx(value.titleFontSize || 17) + 'rpx', fontWeight: '800', color: value.titleColor || '#1A1A1A' }">{{ value.title || '我的推荐码' }}</text>
|
<text :style="$util.objToStyle({ fontSize: px2rpx(value.titleFontSize || 17) + 'px', fontWeight: '800', color: value.titleColor || '#1A1A1A' })">{{ value.title || '我的推荐码' }}</text>
|
||||||
<view style="display:inline-flex;align-items:center;background:rgba(255,107,107,0.15);border-radius:16rpx;padding:4rpx 14rpx;">
|
<view style="display:inline-flex;align-items:center;background:rgba(255,107,107,0.15);border-radius:16rpx;padding:4rpx 14rpx;">
|
||||||
<text style="font-size:20rpx;color:#FF6B6B;font-weight:600;">已获得</text>
|
<text style="font-size:20rpx;color:#FF6B6B;font-weight:600;">已获得</text>
|
||||||
</view>
|
</view>
|
||||||
@@ -166,24 +170,28 @@
|
|||||||
<!-- 推荐码红色虚线框:推荐码居中,圆角更圆,更紧凑 -->
|
<!-- 推荐码红色虚线框:推荐码居中,圆角更圆,更紧凑 -->
|
||||||
<view style="margin-top:20rpx;background:rgba(255,255,255,0.7);border:2rpx dashed #FF6B6B;border-radius:32rpx;padding:12rpx 16rpx;position:relative;z-index:1;">
|
<view style="margin-top:20rpx;background:rgba(255,255,255,0.7);border:2rpx dashed #FF6B6B;border-radius:32rpx;padding:12rpx 16rpx;position:relative;z-index:1;">
|
||||||
<view style="text-align:center;">
|
<view style="text-align:center;">
|
||||||
<text :style="{ fontSize: px2rpx(Math.max(16, Math.min(48, (Number(value.codeFontSize) || 28) * 0.86))) + 'rpx', fontWeight: '800', color: value.codeColor || '#FF4444', letterSpacing: '2rpx', fontFamily: 'Courier New, monospace' }">{{ code || value.placeholderCode || 'ABCD1234' }}</text>
|
<text :style="$util.objToStyle({ fontSize: px2rpx(Math.max(16, Math.min(48, (Number(value.codeFontSize) || 28) * 0.86))) + 'px', fontWeight: '800', color: value.codeColor || '#FF4444', letterSpacing: '2rpx', fontFamily: 'Courier New, monospace' })">{{ code || value.placeholderCode || 'ABCD1234' }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- 复制推荐码按钮:白色小按钮,在虚线框下方、立即分享链接上方 -->
|
<!-- 复制推荐码按钮:白色小按钮,在虚线框下方、立即分享链接上方 -->
|
||||||
<view v-if="value.showCopyButton !== false" style="margin-top:14rpx;display:flex;justify-content:center;position:relative;z-index:1;">
|
<view v-if="value.showCopyButton !== false" style="margin-top:14rpx;display:flex;justify-content:center;position:relative;z-index:1;">
|
||||||
<view @tap="copyRecommendCode" :style="{ width: 'fit-content', height: '60rpx', display: 'flex', justifyContent: 'center', alignItems: 'center', background: '#FFFFFF', border: '2rpx solid ' + (value.codeColor || '#FF6B6B'), borderRadius: '30rpx', padding: '0 32rpx', gap: '8rpx' }">
|
<view @tap="copyRecommendCode" :style="$util.objToStyle(copyBtnStyleObj({width:'fit-content',height:'30px',display:'flex',justifyContent:'center',alignItems:'center',borderRadius:'15px',padding:'0 16px',gap:'4px'}))">
|
||||||
<text style="font-size:22rpx;">📋</text>
|
<text style="font-size:22rpx;">📋</text>
|
||||||
<text :style="{ fontSize: '24rpx', color: value.codeColor || '#FF6B6B', fontWeight: '600' }">{{ nc.copyButtonText || '复制推荐码' }}</text>
|
<text :style="$util.objToStyle(copyBtnStyleObj({}, true))">{{ nc.copyButtonText || '复制推荐码' }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- 立即分享链接按钮:红橙渐变胶囊全宽铺满,触发微信原生分享
|
<!-- 立即分享链接按钮:红橙渐变胶囊全宽铺满,触发微信原生分享
|
||||||
固定走 linear-gradient,不读 diyData.buttonBgColor
|
固定走 linear-gradient,不读 diyData.buttonBgColor
|
||||||
H5 端用 view 触发(避免 button 默认样式),小程序/APP 端保留 button 触发 open-type=share -->
|
H5 端用 view 触发(避免 button 默认样式),小程序/APP 端保留 button 触发 open-type=share -->
|
||||||
<button v-if="value.showShareButton !== false && !isH5Platform" open-type="share" @tap="onShareClick" :style="{ marginTop: '16rpx', height: '76rpx', width: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center', background: 'linear-gradient(90deg,#FF6B6B 0%,#FF4444 100%)', borderRadius: '38rpx', boxShadow: '0 4rpx 12rpx rgba(255,68,68,0.3)', position: 'relative', zIndex: '1', border: 'none', lineHeight: '1' }">
|
<button v-if="value.showShareButton !== false && !isH5Platform" open-type="share" @tap="onShareClick" style="position:relative;left:0;right:0;margin:16rpx 0 0 0;width:100%;text-align:left;background:transparent;border:0;padding:0;line-height:normal;display:block;box-sizing:border-box;">
|
||||||
<text :style="{ fontSize: '27rpx', fontWeight: '700', color: '#FFFFFF' }">📤 {{ value.buttonText || '立即分享' }}</text>
|
<view style="display:block;width:100%;height:76rpx;line-height:76rpx;background:linear-gradient(90deg,#FF6B6B 0%,#FF4444 100%);border-radius:38rpx;box-shadow:0 4rpx 12rpx rgba(255,68,68,0.3);box-sizing:border-box;text-align:center;">
|
||||||
|
<text style="font-size:27rpx;font-weight:700;color:#FFFFFF;">📤 {{ value.buttonText || '立即分享' }}</text>
|
||||||
|
</view>
|
||||||
</button>
|
</button>
|
||||||
<view v-else-if="value.showShareButton !== false" @tap="onShareClick" :style="{ marginTop: '16rpx', height: '76rpx', width: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center', background: 'linear-gradient(90deg,#FF6B6B 0%,#FF4444 100%)', borderRadius: '38rpx', boxShadow: '0 4rpx 12rpx rgba(255,68,68,0.3)', position: 'relative', zIndex: '1', lineHeight: '1', boxSizing: 'border-box' }">
|
<view v-else-if="value.showShareButton !== false" @tap="onShareClick" style="margin-top:16rpx;width:100%;">
|
||||||
<text :style="{ fontSize: '27rpx', fontWeight: '700', color: '#FFFFFF' }">📤 {{ nc.buttonText || '立即分享' }}</text>
|
<view style="display:block;width:100%;height:76rpx;line-height:76rpx;background:linear-gradient(90deg,#FF6B6B 0%,#FF4444 100%);border-radius:38rpx;box-shadow:0 4rpx 12rpx rgba(255,68,68,0.3);box-sizing:border-box;text-align:center;">
|
||||||
|
<text style="font-size:27rpx;font-weight:700;color:#FFFFFF;">📤 {{ nc.buttonText || '立即分享' }}</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- showLink -->
|
<!-- showLink -->
|
||||||
<view v-if="isFeatureOn(value.showLink)" style="margin-top:16rpx;text-align:center;position:relative;z-index:1;">
|
<view v-if="isFeatureOn(value.showLink)" style="margin-top:16rpx;text-align:center;position:relative;z-index:1;">
|
||||||
@@ -191,7 +199,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<!-- showQR -->
|
<!-- showQR -->
|
||||||
<view v-if="isFeatureOn(value.showQR)" style="margin-top:16rpx;display:flex;flex-direction:column;align-items:center;position:relative;z-index:1;">
|
<view v-if="isFeatureOn(value.showQR)" style="margin-top:16rpx;display:flex;flex-direction:column;align-items:center;position:relative;z-index:1;">
|
||||||
<view :style="{ width: px2rpx(nc.qrSize || 200) + 'rpx', height: px2rpx(nc.qrSize || 200) + 'rpx', background: nc.qrBoxBackground || 'transparent', border: nc.qrBoxBorderWidth && nc.qrBoxBorderColor ? (px2rpx(nc.qrBoxBorderWidth) + 'rpx solid ' + nc.qrBoxBorderColor) : 'none', borderRadius: px2rpx(nc.qrBoxBorderRadius || 0) + 'rpx', padding: px2rpx(nc.qrBoxPadding || 0) + 'rpx', boxSizing: 'border-box' }">
|
<view :style="$util.objToStyle({ width: px2rpx(nc.qrSize || 200) + 'px', height: px2rpx(nc.qrSize || 200) + 'px', background: nc.qrBoxBackground || 'transparent', border: nc.qrBoxBorderWidth && nc.qrBoxBorderColor ? (px2rpx(nc.qrBoxBorderWidth) + 'px solid ' + nc.qrBoxBorderColor) : 'none', borderRadius: px2rpx(nc.qrBoxBorderRadius || 0) + 'px', padding: px2rpx(nc.qrBoxPadding || 0) + 'px', boxSizing: 'border-box' })">
|
||||||
<image :src="qrcodeRenderUrl" mode="aspectFit" style="width:100%;height:100%;display:block;" />
|
<image :src="qrcodeRenderUrl" mode="aspectFit" style="width:100%;height:100%;display:block;" />
|
||||||
</view>
|
</view>
|
||||||
<text style="font-size:22rpx;color:#999;margin-top:8rpx;">{{ value.posterQrTip || '扫码进入并填写入驻信息' }}</text>
|
<text style="font-size:22rpx;color:#999;margin-top:8rpx;">{{ value.posterQrTip || '扫码进入并填写入驻信息' }}</text>
|
||||||
@@ -199,14 +207,14 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- ============ 预设 3:Card 卡片风格(全部内联样式) ============ -->
|
<!-- ============ 预设 3:Card 卡片风格(全部内联样式) ============ -->
|
||||||
<view v-if="stylePreset === 'card'" :style="{ width: '100%', boxSizing: 'border-box', marginTop: '24rpx', background: 'linear-gradient(180deg,#FFF9F5 0%,#FFF5F0 100%)', borderRadius: px2rpx(value.borderRadius || 8) + 'rpx', padding: px2rpx(value.padding || 16) + 'rpx ' + px2rpx(value.padding || 16) + 'rpx', position: 'relative', overflow: 'hidden' }">
|
<view v-if="stylePreset === 'card'" :style="$util.objToStyle({ width: '100%', boxSizing: 'border-box', marginTop: '24rpx', background: 'linear-gradient(180deg,#FFF9F5 0%,#FFF5F0 100%)', borderRadius: px2rpx(value.borderRadius || 8) + 'px', padding: px2rpx(value.padding || 16) + 'px ' + px2rpx(value.padding || 16) + 'px', position: 'relative', overflow: 'hidden' })">
|
||||||
<!-- 右上角礼物装饰(开盖礼物盒) -->
|
<!-- 右上角礼物装饰(开盖礼物盒) -->
|
||||||
<view style="position:absolute;top:16rpx;right:20rpx;z-index:2;">
|
<view style="position:absolute;top:16rpx;right:20rpx;z-index:2;">
|
||||||
<text style="font-size:52rpx;">🎁</text>
|
<text style="font-size:52rpx;">🎁</text>
|
||||||
</view>
|
</view>
|
||||||
<!-- 标题行 + 已生效标签 -->
|
<!-- 标题行 + 已生效标签 -->
|
||||||
<view style="display:flex;align-items:center;gap:12rpx;position:relative;z-index:1;">
|
<view style="display:flex;align-items:center;gap:12rpx;position:relative;z-index:1;">
|
||||||
<text :style="{ fontSize: px2rpx(value.titleFontSize || 17) + 'rpx', fontWeight: '800', color: value.titleColor || '#1A1A1A' }">{{ value.title || '我的推荐码' }}</text>
|
<text :style="$util.objToStyle({ fontSize: px2rpx(value.titleFontSize || 17) + 'px', fontWeight: '800', color: value.titleColor || '#1A1A1A' })">{{ value.title || '我的推荐码' }}</text>
|
||||||
<view style="display:inline-flex;align-items:center;background:rgba(255,107,107,0.12);border-radius:16rpx;padding:4rpx 14rpx;">
|
<view style="display:inline-flex;align-items:center;background:rgba(255,107,107,0.12);border-radius:16rpx;padding:4rpx 14rpx;">
|
||||||
<text style="font-size:20rpx;color:#FF6B6B;font-weight:600;">✓ 已生效</text>
|
<text style="font-size:20rpx;color:#FF6B6B;font-weight:600;">✓ 已生效</text>
|
||||||
</view>
|
</view>
|
||||||
@@ -218,23 +226,27 @@
|
|||||||
<!-- 推荐码红色虚线框:居中,圆角,紧凑 -->
|
<!-- 推荐码红色虚线框:居中,圆角,紧凑 -->
|
||||||
<view style="margin-top:20rpx;display:flex;justify-content:center;position:relative;z-index:1;">
|
<view style="margin-top:20rpx;display:flex;justify-content:center;position:relative;z-index:1;">
|
||||||
<view style="background:rgba(255,255,255,0.7);border:2rpx dashed #FF6B6B;border-radius:32rpx;padding:12rpx 32rpx;text-align:center;">
|
<view style="background:rgba(255,255,255,0.7);border:2rpx dashed #FF6B6B;border-radius:32rpx;padding:12rpx 32rpx;text-align:center;">
|
||||||
<text :style="{ fontSize: px2rpx(Math.max(16, Math.min(48, (Number(value.codeFontSize) || 28) * 0.71))) + 'rpx', fontWeight: '800', color: value.codeColor || '#FF4444', letterSpacing: '2rpx', fontFamily: 'Courier New, monospace' }">{{ code || value.placeholderCode || 'ABCD1234' }}</text>
|
<text :style="$util.objToStyle({ fontSize: px2rpx(Math.max(16, Math.min(48, (Number(value.codeFontSize) || 28) * 0.71))) + 'px', fontWeight: '800', color: value.codeColor || '#FF4444', letterSpacing: '2rpx', fontFamily: 'Courier New, monospace' })">{{ code || value.placeholderCode || 'ABCD1234' }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- 复制推荐码按钮:白色小按钮,在虚线框下方、立即分享链接上方,居中 -->
|
<!-- 复制推荐码按钮:白色小按钮,在虚线框下方、立即分享链接上方,居中 -->
|
||||||
<view v-if="value.showCopyButton !== false" style="margin-top:14rpx;display:flex;justify-content:center;position:relative;z-index:1;">
|
<view v-if="value.showCopyButton !== false" style="margin-top:14rpx;display:flex;justify-content:center;position:relative;z-index:1;">
|
||||||
<view @tap="copyRecommendCode" :style="{ width: 'fit-content', height: '60rpx', display: 'flex', justifyContent: 'center', alignItems: 'center', background: '#FFFFFF', border: '2rpx solid ' + (value.codeColor || '#FF6B6B'), borderRadius: '30rpx', padding: '0 32rpx', gap: '8rpx' }">
|
<view @tap="copyRecommendCode" :style="$util.objToStyle(copyBtnStyleObj({width:'fit-content',height:'30px',display:'flex',justifyContent:'center',alignItems:'center',borderRadius:'15px',padding:'0 16px',gap:'4px'}))">
|
||||||
<text style="font-size:22rpx;">📋</text>
|
<text style="font-size:22rpx;">📋</text>
|
||||||
<text :style="{ fontSize: '24rpx', color: value.codeColor || '#FF6B6B', fontWeight: '600' }">{{ nc.copyButtonText || '复制推荐码' }}</text>
|
<text :style="$util.objToStyle(copyBtnStyleObj({}, true))">{{ nc.copyButtonText || '复制推荐码' }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- 立即分享链接按钮:Card 风格固定「白底红边框」设计,不读 buttonBgColor
|
<!-- 立即分享链接按钮:Card 风格固定「白底红边框」设计,不读 buttonBgColor
|
||||||
H5 端改用 view 触发(避免 <button> 默认样式影响),小程序/APP 端保留 button 以触发 open-type=share -->
|
H5 端改用 view 触发(避免 <button> 默认样式影响),小程序/APP 端保留 button 以触发 open-type=share -->
|
||||||
<button v-if="value.showShareButton !== false && !isH5Platform" open-type="share" @tap="onShareClick" :style="{ marginTop: '16rpx', height: '76rpx', width: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center', background: '#FFFFFF', border: '2rpx solid ' + (value.codeColor || '#FF6B6B'), borderRadius: '38rpx', position: 'relative', zIndex: '1', lineHeight: '1' }">
|
<button v-if="value.showShareButton !== false && !isH5Platform" open-type="share" @tap="onShareClick" style="position:relative;left:0;right:0;margin:16rpx 0 0 0;width:100%;text-align:left;background:transparent;border:0;padding:0;line-height:normal;display:block;box-sizing:border-box;">
|
||||||
<text :style="{ fontSize: '27rpx', fontWeight: '600', color: value.codeColor || '#FF6B6B' }">📤 {{ value.buttonText || '立即分享' }}</text>
|
<view style="display:block;width:100%;height:76rpx;line-height:76rpx;background:#FFFFFF;border:2rpx solid #FF6B6B;border-radius:38rpx;box-sizing:border-box;text-align:center;">
|
||||||
|
<text style="font-size:27rpx;font-weight:600;color:#FF6B6B;">📤 {{ value.buttonText || '立即分享' }}</text>
|
||||||
|
</view>
|
||||||
</button>
|
</button>
|
||||||
<view v-else-if="value.showShareButton !== false" @tap="onShareClick" :style="{ marginTop: '16rpx', height: '76rpx', width: '100%', display: 'flex', justifyContent: 'center', alignItems: 'center', background: '#FFFFFF', border: '2rpx solid ' + (nc.codeColor || '#FF6B6B'), borderRadius: '38rpx', position: 'relative', zIndex: '1', lineHeight: '1', boxSizing: 'border-box' }">
|
<view v-else-if="value.showShareButton !== false" @tap="onShareClick" style="margin-top:16rpx;width:100%;">
|
||||||
<text :style="{ fontSize: '27rpx', fontWeight: '600', color: nc.codeColor || '#FF6B6B' }">📤 {{ nc.buttonText || '立即分享' }}</text>
|
<view style="display:block;width:100%;height:76rpx;line-height:76rpx;background:#FFFFFF;border:2rpx solid #FF6B6B;border-radius:38rpx;box-sizing:border-box;text-align:center;">
|
||||||
|
<text style="font-size:27rpx;font-weight:600;color:#FF6B6B;">📤 {{ nc.buttonText || '立即分享' }}</text>
|
||||||
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- showLink -->
|
<!-- showLink -->
|
||||||
<view v-if="isFeatureOn(value.showLink)" style="margin-top:16rpx;text-align:center;position:relative;z-index:1;">
|
<view v-if="isFeatureOn(value.showLink)" style="margin-top:16rpx;text-align:center;position:relative;z-index:1;">
|
||||||
@@ -242,7 +254,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<!-- showQR -->
|
<!-- showQR -->
|
||||||
<view v-if="isFeatureOn(value.showQR)" style="margin-top:16rpx;display:flex;flex-direction:column;align-items:center;position:relative;z-index:1;">
|
<view v-if="isFeatureOn(value.showQR)" style="margin-top:16rpx;display:flex;flex-direction:column;align-items:center;position:relative;z-index:1;">
|
||||||
<view :style="{ width: px2rpx(nc.qrSize || 200) + 'rpx', height: px2rpx(nc.qrSize || 200) + 'rpx', background: nc.qrBoxBackground || 'transparent', border: nc.qrBoxBorderWidth && nc.qrBoxBorderColor ? (px2rpx(nc.qrBoxBorderWidth) + 'rpx solid ' + nc.qrBoxBorderColor) : 'none', borderRadius: px2rpx(nc.qrBoxBorderRadius || 0) + 'rpx', padding: px2rpx(nc.qrBoxPadding || 0) + 'rpx', boxSizing: 'border-box' }">
|
<view :style="$util.objToStyle({ width: px2rpx(nc.qrSize || 200) + 'px', height: px2rpx(nc.qrSize || 200) + 'px', background: nc.qrBoxBackground || 'transparent', border: nc.qrBoxBorderWidth && nc.qrBoxBorderColor ? (px2rpx(nc.qrBoxBorderWidth) + 'px solid ' + nc.qrBoxBorderColor) : 'none', borderRadius: px2rpx(nc.qrBoxBorderRadius || 0) + 'px', padding: px2rpx(nc.qrBoxPadding || 0) + 'px', boxSizing: 'border-box' })">
|
||||||
<image :src="qrcodeRenderUrl" mode="aspectFit" style="width:100%;height:100%;display:block;" />
|
<image :src="qrcodeRenderUrl" mode="aspectFit" style="width:100%;height:100%;display:block;" />
|
||||||
</view>
|
</view>
|
||||||
<text style="font-size:22rpx;color:#999;margin-top:8rpx;">{{ value.posterQrTip || '扫码进入并填写入驻信息' }}</text>
|
<text style="font-size:22rpx;color:#999;margin-top:8rpx;">{{ value.posterQrTip || '扫码进入并填写入驻信息' }}</text>
|
||||||
@@ -250,9 +262,9 @@
|
|||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- ============ 预设 4:Horizontal 横向风格(全部内联样式,匹配参考图) ============ -->
|
<!-- ============ 预设 4:Horizontal 横向风格(全部内联样式,匹配参考图) ============ -->
|
||||||
<view v-if="stylePreset === 'horizontal'" :style="{ width: '100%', boxSizing: 'border-box', marginTop: '24rpx', background: '#FFFFFF', borderRadius: px2rpx(value.borderRadius || 8) + 'rpx', padding: px2rpx(value.padding || 16) + 'rpx ' + px2rpx(value.padding || 16) + 'rpx', position: 'relative', overflow: 'hidden', boxShadow: '0 4rpx 16rpx rgba(0,0,0,0.06)' }">
|
<view v-if="stylePreset === 'horizontal'" :style="$util.objToStyle({ width: '100%', boxSizing: 'border-box', marginTop: '24rpx', background: '#FFFFFF', borderRadius: px2rpx(value.borderRadius || 8) + 'px', padding: px2rpx(value.padding || 16) + 'px ' + px2rpx(value.padding || 16) + 'px', position: 'relative', overflow: 'hidden', boxShadow: '0 4rpx 16rpx rgba(0,0,0,0.06)' })">
|
||||||
<!-- 卡片顶部:主标题 -->
|
<!-- 卡片顶部:主标题 -->
|
||||||
<text :style="{ fontSize: px2rpx(value.titleFontSize || 17) + 'rpx', fontWeight: '800', color: value.titleColor || '#1A1A1A', display: 'block' }">{{ value.title || '我的推荐码' }}</text>
|
<text :style="$util.objToStyle({ fontSize: px2rpx(value.titleFontSize || 17) + 'px', fontWeight: '800', color: value.titleColor || '#1A1A1A', display: 'block' })">{{ value.title || '我的推荐码' }}</text>
|
||||||
<!-- 内容主体框:浅橙细边框,极浅米白底色 -->
|
<!-- 内容主体框:浅橙细边框,极浅米白底色 -->
|
||||||
<view style="margin-top:20rpx;background:#FFFCF8;border:2rpx solid #FFE0B2;border-radius:20rpx;padding:24rpx 24rpx 20rpx;position:relative;overflow:hidden;">
|
<view style="margin-top:20rpx;background:#FFFCF8;border:2rpx solid #FFE0B2;border-radius:20rpx;padding:24rpx 24rpx 20rpx;position:relative;overflow:hidden;">
|
||||||
<!-- 2.1 状态标签(左上角) -->
|
<!-- 2.1 状态标签(左上角) -->
|
||||||
@@ -272,22 +284,24 @@
|
|||||||
</view>
|
</view>
|
||||||
<!-- 2.3 推荐码核心文本(左对齐) -->
|
<!-- 2.3 推荐码核心文本(左对齐) -->
|
||||||
<view style="margin-top:20rpx;position:relative;z-index:1;">
|
<view style="margin-top:20rpx;position:relative;z-index:1;">
|
||||||
<text :style="{ fontSize: px2rpx(Math.max(16, Math.min(48, (Number(value.codeFontSize) || 28) * 1.07))) + 'rpx', fontWeight: '900', color: value.codeColor || '#FF4444', letterSpacing: '2rpx', fontFamily: 'Courier New, monospace' }">{{ code || value.placeholderCode || 'ABCD1234' }}</text>
|
<text :style="$util.objToStyle({ fontSize: px2rpx(Math.max(16, Math.min(48, (Number(value.codeFontSize) || 28) * 1.07))) + 'px', fontWeight: '900', color: value.codeColor || '#FF4444', letterSpacing: '2rpx', fontFamily: 'Courier New, monospace' })">{{ code || value.placeholderCode || 'ABCD1234' }}</text>
|
||||||
</view>
|
</view>
|
||||||
<!-- 2.4 + 2.5 按钮行:左复制、右分享,上下错落 -->
|
<!-- 2.4 + 2.5 按钮行:左复制、右分享,按内容自适应宽度,两端对称对齐 -->
|
||||||
<view style="margin-top:16rpx;display:flex;align-items:flex-end;justify-content:space-between;gap:16rpx;position:relative;z-index:1;">
|
<view style="margin-top:16rpx;display:flex;align-items:center;justify-content:space-between;gap:16rpx;padding:0 8rpx;position:relative;z-index:1;">
|
||||||
<!-- 左下角:复制推荐码按钮(描边圆角胶囊) -->
|
<!-- 左下角:复制推荐码按钮(描边圆角胶囊,按内容自适应) -->
|
||||||
<view v-if="value.showCopyButton !== false" @tap="copyRecommendCode" :style="{ height: '64rpx', display: 'flex', justifyContent: 'center', alignItems: 'center', background: '#FFFFFF', border: '2rpx solid ' + (value.codeColor || '#FFB74D'), borderRadius: '32rpx', padding: '0 24rpx', gap: '6rpx' }">
|
<view v-if="value.showCopyButton !== false" @tap="copyRecommendCode" :style="$util.objToStyle(copyBtnStyleObj({height:'32px',display:'inline-flex',justifyContent:'center',alignItems:'center',borderRadius:'16px',padding:'0 12px',gap:'3px'}))">
|
||||||
<text style="font-size:22rpx;">📋</text>
|
<text style="font-size:22rpx;">📋</text>
|
||||||
<text :style="{ fontSize: '24rpx', color: value.codeColor || '#FF8F00', fontWeight: '600' }">{{ nc.copyButtonText || '复制推荐码' }}</text>
|
<text :style="$util.objToStyle(copyBtnStyleObj({}, true))">{{ nc.copyButtonText || '复制推荐码' }}</text>
|
||||||
</view>
|
</view>
|
||||||
<!-- 右下角:立即分享链接按钮(黄橙渐变胶囊,靠右,触发微信原生分享)
|
<!-- 右下角:立即分享链接按钮(黄橙渐变胶囊,按内容自适应,触发微信原生分享)
|
||||||
H5 端用 view 触发,小程序/APP 端保留 button 以触发 open-type=share -->
|
H5 端用 view 触发,小程序/APP 端保留 button 以触发 open-type=share -->
|
||||||
<button v-if="value.showShareButton !== false && !isH5Platform" open-type="share" @tap="onShareClick" :style="{ height: '72rpx', display: 'flex', justifyContent: 'center', alignItems: 'center', background: 'linear-gradient(90deg,#FFC107 0%,#FF8F00 100%)', borderRadius: '36rpx', padding: '0 40rpx', boxShadow: '0 4rpx 12rpx rgba(255,143,0,0.3)', marginBottom: '-4rpx', border: 'none', lineHeight: '1' }">
|
<button v-if="value.showShareButton !== false && !isH5Platform" open-type="share" @tap="onShareClick" style="position:relative;left:0;right:0;margin:0;padding:0;text-align:center;background:transparent;border:0;line-height:normal;display:inline-block;">
|
||||||
<text :style="{ fontSize: '27rpx', fontWeight: '700', color: '#FFFFFF' }">📤 {{ value.buttonText || '立即分享' }}</text>
|
<view style="display:inline-block;height:72rpx;line-height:72rpx;padding:0 32rpx;background:linear-gradient(90deg,#FFC107 0%,#FF8F00 100%);border-radius:36rpx;box-shadow:0 4rpx 12rpx rgba(255,143,0,0.3);box-sizing:border-box;">
|
||||||
|
<text style="font-size:27rpx;font-weight:700;color:#FFFFFF;">📤 {{ value.buttonText || '立即分享' }}</text>
|
||||||
|
</view>
|
||||||
</button>
|
</button>
|
||||||
<view v-else-if="value.showShareButton !== false" @tap="onShareClick" :style="{ height: '72rpx', display: 'flex', justifyContent: 'center', alignItems: 'center', background: 'linear-gradient(90deg,#FFC107 0%,#FF8F00 100%)', borderRadius: '36rpx', padding: '0 40rpx', boxShadow: '0 4rpx 12rpx rgba(255,143,0,0.3)', marginBottom: '-4rpx', lineHeight: '1', boxSizing: 'border-box' }">
|
<view v-else-if="value.showShareButton !== false" @tap="onShareClick" style="height:72rpx;display:inline-flex;justify-content:center;align-items:center;background:linear-gradient(90deg,#FFC107 0%,#FF8F00 100%);border-radius:36rpx;box-shadow:0 4rpx 12rpx rgba(255,143,0,0.3);padding:0 32rpx;line-height:1;box-sizing:border-box;">
|
||||||
<text :style="{ fontSize: '27rpx', fontWeight: '700', color: '#FFFFFF' }">📤 {{ nc.buttonText || '立即分享' }}</text>
|
<text style="font-size:27rpx;font-weight:700;color:#FFFFFF;">📤 {{ nc.buttonText || '立即分享' }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -303,7 +317,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<!-- showQR -->
|
<!-- showQR -->
|
||||||
<view v-if="isFeatureOn(value.showQR)" style="margin-top:16rpx;display:flex;flex-direction:column;align-items:center;">
|
<view v-if="isFeatureOn(value.showQR)" style="margin-top:16rpx;display:flex;flex-direction:column;align-items:center;">
|
||||||
<view :style="{ width: px2rpx(nc.qrSize || 200) + 'rpx', height: px2rpx(nc.qrSize || 200) + 'rpx', background: nc.qrBoxBackground || 'transparent', border: nc.qrBoxBorderWidth && nc.qrBoxBorderColor ? (px2rpx(nc.qrBoxBorderWidth) + 'rpx solid ' + nc.qrBoxBorderColor) : 'none', borderRadius: px2rpx(nc.qrBoxBorderRadius || 0) + 'rpx', padding: px2rpx(nc.qrBoxPadding || 0) + 'rpx', boxSizing: 'border-box' }">
|
<view :style="$util.objToStyle({ width: px2rpx(nc.qrSize || 200) + 'px', height: px2rpx(nc.qrSize || 200) + 'px', background: nc.qrBoxBackground || 'transparent', border: nc.qrBoxBorderWidth && nc.qrBoxBorderColor ? (px2rpx(nc.qrBoxBorderWidth) + 'px solid ' + nc.qrBoxBorderColor) : 'none', borderRadius: px2rpx(nc.qrBoxBorderRadius || 0) + 'px', padding: px2rpx(nc.qrBoxPadding || 0) + 'px', boxSizing: 'border-box' })">
|
||||||
<image :src="qrcodeRenderUrl" mode="aspectFit" style="width:100%;height:100%;display:block;" />
|
<image :src="qrcodeRenderUrl" mode="aspectFit" style="width:100%;height:100%;display:block;" />
|
||||||
</view>
|
</view>
|
||||||
<text style="font-size:22rpx;color:#999;margin-top:8rpx;">{{ value.posterQrTip || '扫码进入并填写入驻信息' }}</text>
|
<text style="font-size:22rpx;color:#999;margin-top:8rpx;">{{ value.posterQrTip || '扫码进入并填写入驻信息' }}</text>
|
||||||
@@ -313,6 +327,9 @@
|
|||||||
</view>
|
</view>
|
||||||
<!-- 登录组件占位(ns-login 全局 mixin 已注册) -->
|
<!-- 登录组件占位(ns-login 全局 mixin 已注册) -->
|
||||||
<ns-login ref="nsLoginBox" :popup="true" @getUserInfo="onLoginSuccess"></ns-login>
|
<ns-login ref="nsLoginBox" :popup="true" @getUserInfo="onLoginSuccess"></ns-login>
|
||||||
|
<!-- ============ 分享海报 canvas(隐藏,仅小程序分享时生成推荐码组件的截图) ============ -->
|
||||||
|
<canvas v-if="!isH5Platform" canvas-id="sharePosterCanvas" id="sharePosterCanvas"
|
||||||
|
style="position:fixed;left:-9999px;top:-9999px;width:375px;height:480px;z-index:-1;"></canvas>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -718,12 +735,37 @@ export default {
|
|||||||
onShareAppMessage() {
|
onShareAppMessage() {
|
||||||
const v = this.nc;
|
const v = this.nc;
|
||||||
const title = v.shareTitle || v.title || '邀您入驻低空经济产业平台';
|
const title = v.shareTitle || v.title || '邀您入驻低空经济产业平台';
|
||||||
|
// 小程序环境下,生成「仅推荐码组件」的截图海报作为分享图
|
||||||
|
// H5 平台不进入 canvas 路径,直接用 diyData.shareImageUrl
|
||||||
|
if (!this.isH5Platform) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
// 先生成海报;失败或非小程序时回退到默认 imageUrl
|
||||||
|
this.generateSharePoster()
|
||||||
|
.then((posterPath) => {
|
||||||
|
resolve({
|
||||||
|
title,
|
||||||
|
path: this.extractMiniProgramPath(),
|
||||||
|
imageUrl: posterPath || (v.shareImageUrl || '')
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
resolve({
|
||||||
|
title,
|
||||||
|
path: this.extractMiniProgramPath(),
|
||||||
|
imageUrl: v.shareImageUrl || ''
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
title,
|
title,
|
||||||
path: this.extractMiniProgramPath(),
|
path: this.extractMiniProgramPath(),
|
||||||
imageUrl: v.shareImageUrl || ''
|
imageUrl: v.shareImageUrl || ''
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 小程序分享到朋友圈(uniapp 生命周期钩子,必须在 export default 对象上,不能进 methods)
|
||||||
|
*/
|
||||||
onShareTimeline() {
|
onShareTimeline() {
|
||||||
const v = this.nc;
|
const v = this.nc;
|
||||||
// 从H5分享链接中提取完整查询参数,与分享给好友保持一致
|
// 从H5分享链接中提取完整查询参数,与分享给好友保持一致
|
||||||
@@ -759,8 +801,39 @@ export default {
|
|||||||
'font-weight:700;font-size:26rpx;'
|
'font-weight:700;font-size:26rpx;'
|
||||||
].join('');
|
].join('');
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 复制按钮样式对象(跟随后端 diyData.codeColor)
|
||||||
|
* 返回对象,用 $util.objToStyle 转字符串,
|
||||||
|
* 兼容 H5/小程序/APP/鸿蒙
|
||||||
|
* 注意:使用 px 而非 rpx 单位,避免鸿蒙元服务对动态 style 中 rpx
|
||||||
|
* 解析异常的问题。1rpx = 0.5px,直接换算成 px 数值。
|
||||||
|
* @param {Object} preset 预设差异(可选,如 {borderRadius:'16px',height:'32px'})
|
||||||
|
* @param {Boolean} textOnly 只返回文字样式(用于内嵌 text 节点)
|
||||||
|
*/
|
||||||
|
copyBtnStyleObj(preset, textOnly) {
|
||||||
|
const v = this.nc;
|
||||||
|
const codeColor = (v && v.codeColor) ? v.codeColor : '#DC143C';
|
||||||
|
if (textOnly) {
|
||||||
|
return Object.assign({
|
||||||
|
fontSize: '12px', // 原 24rpx → 12px
|
||||||
|
color: codeColor,
|
||||||
|
fontWeight: '600'
|
||||||
|
}, preset || {});
|
||||||
|
}
|
||||||
|
const base = {
|
||||||
|
background: '#FFFFFF',
|
||||||
|
border: `1px solid ${codeColor}`, // 原 2rpx → 1px
|
||||||
|
boxSizing: 'border-box'
|
||||||
|
};
|
||||||
|
return Object.assign(base, preset || {});
|
||||||
|
},
|
||||||
px2rpx(val) {
|
px2rpx(val) {
|
||||||
return Number(val || 0) * 2
|
// 后端 diyData 数值语义是 px(商家在设计器按 px 输入,后端 wap/design.html
|
||||||
|
// 也都用 px 输出)。前端之前 *2 转 rpx 是错的,商家设置 8px 实际渲染
|
||||||
|
// 成 16px 偏大。鸿蒙元服务对动态拼接的 rpx 也有解析问题,所以这里
|
||||||
|
// 直接返回原值,作为 px 使用。
|
||||||
|
// 调用方:动态拼接时用 + 'px'(不要用 + 'px')。
|
||||||
|
return Number(val || 0)
|
||||||
},
|
},
|
||||||
isFeatureOn(v) {
|
isFeatureOn(v) {
|
||||||
if (v === false || v === 0 || v === '0' || v === 'false' || v === null || v === undefined) return false;
|
if (v === false || v === 0 || v === '0' || v === 'false' || v === null || v === undefined) return false;
|
||||||
@@ -919,7 +992,9 @@ export default {
|
|||||||
if (consent) {
|
if (consent) {
|
||||||
uni.showToast && uni.showToast({ title: '获取成功', icon: 'success' });
|
uni.showToast && uni.showToast({ title: '获取成功', icon: 'success' });
|
||||||
}
|
}
|
||||||
// 拿到码后如果 showQR=true,顺便调 getMyQrcode(占位接口)
|
// 拿到码后立即预生成分享海报(不等二维码,先占位;二维码拿到后会再次更新)
|
||||||
|
try { vm._prepareSharePoster(); } catch(e){}
|
||||||
|
// 拿到码后如果 showQR=true,顺便调 getMyQrcode(占位接口)
|
||||||
try { vm.loadQrcodeIfNeeded(); } catch(e){}
|
try { vm.loadQrcodeIfNeeded(); } catch(e){}
|
||||||
},
|
},
|
||||||
fail: function(err){
|
fail: function(err){
|
||||||
@@ -1012,10 +1087,61 @@ export default {
|
|||||||
if (res && res.code === 0 && res.data && res.data.qrcode_url) {
|
if (res && res.code === 0 && res.data && res.data.qrcode_url) {
|
||||||
vm.qrcodeUrl = String(res.data.qrcode_url);
|
vm.qrcodeUrl = String(res.data.qrcode_url);
|
||||||
}
|
}
|
||||||
|
// 拿到二维码后立即预生成「仅推荐码组件」分享海报
|
||||||
|
// 这样用户点"立即分享"时,storage 里已有海报,页面级 onShareAppMessage
|
||||||
|
// 同步读取后返回 imageUrl,微信就不会用全屏截图
|
||||||
|
try { vm._prepareSharePoster(); } catch(e) {}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 预生成分享海报(异步,完成后写入 _recommendShareData storage)
|
||||||
|
* 小程序端:onShareAppMessage 读取这个 storage 拿到 imageUrl
|
||||||
|
* 非小程序端:跳过,onShareClick 不依赖它
|
||||||
|
*/
|
||||||
|
_prepareSharePoster() {
|
||||||
|
const vm = this;
|
||||||
|
console.log('[推荐码分享] _prepareSharePoster 被调用,isH5Platform=', this.isH5Platform, 'code=', this.code);
|
||||||
|
// 仅小程序需要这个(其他端 onShareClick 不依赖 storage)
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
if (!this.code) {
|
||||||
|
console.warn('[推荐码分享] 跳过预生成:推荐码为空');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 安全:isH5Platform 为 true 时不进入(H5 下 canvas 不渲染,createCanvasContext 会失败)
|
||||||
|
if (this.isH5Platform) {
|
||||||
|
console.log('[推荐码分享] H5 环境跳过海报预生成');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
console.log('[推荐码分享] 开始调 generateSharePoster...');
|
||||||
|
this.generateSharePoster().then(function(posterPath) {
|
||||||
|
console.log('[推荐码分享] generateSharePoster resolved, posterPath=', posterPath);
|
||||||
|
if (!posterPath) {
|
||||||
|
console.warn('[推荐码分享] 海报路径为空,跳过');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
uni.setStorageSync('_recommendSharePoster', posterPath);
|
||||||
|
const data = {
|
||||||
|
title: (vm.nc && (vm.nc.shareTitle || vm.nc.title)) || '邀您入驻低空经济产业平台',
|
||||||
|
path: vm.extractMiniProgramPath(),
|
||||||
|
imageUrl: posterPath
|
||||||
|
};
|
||||||
|
uni.setStorageSync('_recommendShareData', JSON.stringify(data));
|
||||||
|
console.log('[推荐码分享] 海报预生成完成,imageUrl=', posterPath);
|
||||||
|
} catch(e) {
|
||||||
|
console.error('[推荐码分享] 写 storage 失败:', e);
|
||||||
|
}
|
||||||
|
}).catch(function(err) {
|
||||||
|
console.warn('[推荐码分享] 海报预生成失败', err);
|
||||||
|
});
|
||||||
|
} catch(e) {
|
||||||
|
console.error('[推荐码分享] _prepareSharePoster try 块异常:', e);
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
|
||||||
onShareClick() {
|
onShareClick() {
|
||||||
console.log('[推荐码分享]点击分享按钮,完整推广链接:', this.formatShareLink,'当前推荐码:',this.code);
|
console.log('[推荐码分享]点击分享按钮,完整推广链接:', this.formatShareLink,'当前推荐码:',this.code);
|
||||||
@@ -1057,14 +1183,37 @@ onShareClick() {
|
|||||||
console.log('分享链接准备,原始配置', v);
|
console.log('分享链接准备,原始配置', v);
|
||||||
console.log('生成的分享完整路径:', this.extractMiniProgramPath());
|
console.log('生成的分享完整路径:', this.extractMiniProgramPath());
|
||||||
|
|
||||||
|
// 先用上次缓存的海报路径占位(避免用户快速二次分享时空白)
|
||||||
|
var cachedPoster = '';
|
||||||
|
try { cachedPoster = uni.getStorageSync('_recommendSharePoster') || ''; } catch(e) {}
|
||||||
try {
|
try {
|
||||||
var shareData = {
|
var shareData = {
|
||||||
title: v.shareTitle || v.title || '邀您入驻低空经济产业平台',
|
title: v.shareTitle || v.title || '邀您入驻低空经济产业平台',
|
||||||
path: this.extractMiniProgramPath(),
|
path: this.extractMiniProgramPath(),
|
||||||
imageUrl: v.shareImageUrl || ''
|
imageUrl: cachedPoster || v.shareImageUrl || ''
|
||||||
};
|
};
|
||||||
uni.setStorageSync('_recommendShareData', JSON.stringify(shareData));
|
uni.setStorageSync('_recommendShareData', JSON.stringify(shareData));
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
|
|
||||||
|
// 异步生成「仅推荐码组件」的海报图,生成后更新 storage
|
||||||
|
// 微信端:onShareAppMessage 触发时 _recommendShareData 中已是新海报
|
||||||
|
this.generateSharePoster().then(function(posterPath) {
|
||||||
|
if (!posterPath) return;
|
||||||
|
try {
|
||||||
|
// 缓存本次海报,下次分享时直接用
|
||||||
|
uni.setStorageSync('_recommendSharePoster', posterPath);
|
||||||
|
var data = {
|
||||||
|
title: v.shareTitle || v.title || '邀您入驻低空经济产业平台',
|
||||||
|
path: vm.extractMiniProgramPath(),
|
||||||
|
imageUrl: posterPath
|
||||||
|
};
|
||||||
|
uni.setStorageSync('_recommendShareData', JSON.stringify(data));
|
||||||
|
console.log('[推荐码分享]海报已生成并写入 storage', posterPath);
|
||||||
|
} catch(e) {}
|
||||||
|
}).catch(function(err) {
|
||||||
|
console.warn('[推荐码分享]海报生成失败,使用 fallback', err);
|
||||||
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
// #endif
|
// #endif
|
||||||
},
|
},
|
||||||
@@ -1160,6 +1309,232 @@ onShareClick() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成推荐码组件的分享海报(canvas 绘制 → 临时文件路径)
|
||||||
|
* 画布内容:白底 + 标题 + 推荐码大字 + 二维码(如有) + 简短提示
|
||||||
|
* 返回 Promise<tempFilePath>
|
||||||
|
*/
|
||||||
|
generateSharePoster() {
|
||||||
|
const v = this.nc;
|
||||||
|
const code = this.code || v.placeholderCode || 'ABCD1234';
|
||||||
|
const title = v.title || '我的推荐码';
|
||||||
|
const sp = this.stylePreset;
|
||||||
|
// ★ 所有视觉参数都从 diyData.value 读取,后端可改
|
||||||
|
const palette = {
|
||||||
|
bg: v.bgColor || '#FFFFFF',
|
||||||
|
bgStart: v.bgColorStart, // 渐变起始(可选)
|
||||||
|
bgEnd: v.bgColorEnd, // 渐变结束(可选)
|
||||||
|
border: v.borderColor || '#E5E7EB',
|
||||||
|
borderRadius: Number(v.borderRadius) || 12,
|
||||||
|
padding: Number(v.padding) || 16,
|
||||||
|
titleColor: v.titleColor || '#1A1A1A',
|
||||||
|
titleFontSize: Number(v.titleFontSize) || 18,
|
||||||
|
codeColor: v.codeColor || '#DC143C',
|
||||||
|
codeFontSize: Number(v.codeFontSize) || 36,
|
||||||
|
accent: v.accentColor || '#FFD700',
|
||||||
|
text: v.textColor || '#303133',
|
||||||
|
subtitle: '#999999',
|
||||||
|
codeBlockBg: '#FFF8F0', // 推荐码区背景(米黄)
|
||||||
|
codeBlockBorder: '#FFB74D'
|
||||||
|
};
|
||||||
|
const W = 375;
|
||||||
|
const H = 480;
|
||||||
|
console.log('[推荐码分享] generateSharePoster 进入,stylePreset=', sp);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
const ctx = uni.createCanvasContext('sharePosterCanvas', this);
|
||||||
|
if (!ctx) return reject(new Error('createCanvasContext 失败'));
|
||||||
|
console.log('[推荐码分享] createCanvasContext 返回');
|
||||||
|
// ====================== 绘制海报 ======================
|
||||||
|
// 1. 整张背景(根据后端 bgColorStart/End 渐变,否则用 bgColor 单色)
|
||||||
|
this._drawPosterBg(ctx, W, H, palette);
|
||||||
|
// 2. 卡片(根据 stylePreset 选择布局)
|
||||||
|
this._drawPosterCard(ctx, W, H, palette, sp);
|
||||||
|
// 3. 标题(读 titleColor/titleFontSize)
|
||||||
|
ctx.setFontSize(palette.titleFontSize);
|
||||||
|
ctx.setFillStyle(palette.titleColor);
|
||||||
|
ctx.setTextAlign('left');
|
||||||
|
ctx.fillText(title, 24, 44);
|
||||||
|
// 4. 副标题(根据后端配置,可选)
|
||||||
|
const showLink = this.isFeatureOn(v.showLink);
|
||||||
|
const showQR = this.isFeatureOn(v.showQR);
|
||||||
|
ctx.setFontSize(12);
|
||||||
|
ctx.setFillStyle(palette.subtitle);
|
||||||
|
ctx.fillText('分享给好友,邀请入驻获得奖励', 24, 70);
|
||||||
|
// 4.5 如果后端开启 showLink,绘制分享链接
|
||||||
|
let urlY = 88;
|
||||||
|
if (showLink) {
|
||||||
|
try {
|
||||||
|
ctx.setFontSize(11);
|
||||||
|
ctx.setFillStyle(palette.text || '#666');
|
||||||
|
ctx.setTextAlign('left');
|
||||||
|
// 缩短显示:最多 30 字符
|
||||||
|
let linkText = (v.shareLink || this.shareLink || '').toString();
|
||||||
|
if (linkText.length > 36) linkText = linkText.slice(0, 36) + '…';
|
||||||
|
ctx.fillText('链接:' + (linkText || '(未生成)'), 24, urlY);
|
||||||
|
urlY += 16; // 后续内容往下挪
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
// 5. 推荐码区域(读 codeColor/codeFontSize/borderRadius)
|
||||||
|
// blockY 根据 showLink 动态下移
|
||||||
|
const blockY = urlY, blockH = 110, blockX = 24, blockW = W - 48;
|
||||||
|
const codeBlockRadius = Math.min(palette.borderRadius, 16);
|
||||||
|
// 推荐码区背景
|
||||||
|
ctx.setFillStyle(palette.codeBlockBg);
|
||||||
|
this._roundRect(ctx, blockX, blockY, blockW, blockH, codeBlockRadius);
|
||||||
|
ctx.fill();
|
||||||
|
// 推荐码区边框(浅橙)
|
||||||
|
ctx.setStrokeStyle(palette.codeBlockBorder);
|
||||||
|
ctx.setLineWidth(1);
|
||||||
|
this._roundRect(ctx, blockX, blockY, blockW, blockH, codeBlockRadius);
|
||||||
|
ctx.stroke();
|
||||||
|
// 推荐码大字(居中)
|
||||||
|
ctx.setFontSize(palette.codeFontSize);
|
||||||
|
ctx.setFillStyle(palette.codeColor);
|
||||||
|
ctx.setTextAlign('center');
|
||||||
|
if (typeof ctx.setFontWeight === 'function') {
|
||||||
|
ctx.setFontWeight('bold');
|
||||||
|
} else {
|
||||||
|
try { ctx.font = 'bold ' + palette.codeFontSize + 'px sans-serif'; } catch(e) {}
|
||||||
|
}
|
||||||
|
ctx.fillText(code, W / 2, blockY + blockH / 2 + 12);
|
||||||
|
// 6. 二维码(如果有)
|
||||||
|
const qr = this.qrcodeRenderUrl;
|
||||||
|
const drawQrcodeAndExport = (qrLocalPath) => {
|
||||||
|
if (qrLocalPath) {
|
||||||
|
const qrSize = 180;
|
||||||
|
const qrX = (W - qrSize) / 2;
|
||||||
|
const qrY = blockY + blockH + 24;
|
||||||
|
// 白底框
|
||||||
|
ctx.setFillStyle('#FFFFFF');
|
||||||
|
this._roundRect(ctx, qrX - 6, qrY - 6, qrSize + 12, qrSize + 12, codeBlockRadius);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.drawImage(qrLocalPath, qrX, qrY, qrSize, qrSize);
|
||||||
|
// 底部提示
|
||||||
|
ctx.setFontSize(11);
|
||||||
|
ctx.setFillStyle(palette.subtitle);
|
||||||
|
ctx.setTextAlign('center');
|
||||||
|
ctx.fillText(v.posterQrTip || '扫码进入并填写入驻信息', W / 2, qrY + qrSize + 24);
|
||||||
|
}
|
||||||
|
console.log('[推荐码分享] 准备 ctx.draw(false, cb)');
|
||||||
|
ctx.draw(false, () => {
|
||||||
|
console.log('[推荐码分享] ctx.draw callback 触发,准备 _exportCanvas');
|
||||||
|
this._exportCanvas(W, H).then(resolve).catch(reject);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
if (qr) {
|
||||||
|
this._downloadImage(qr).then(drawQrcodeAndExport).catch(() => drawQrcodeAndExport(null));
|
||||||
|
} else {
|
||||||
|
drawQrcodeAndExport(null);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[推荐码分享] generateSharePoster try 块异常:', e);
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 海报背景:根据 diyData.bgColorStart/End 画渐变,否则用 bgColor 单色
|
||||||
|
*/
|
||||||
|
_drawPosterBg(ctx, W, H, palette) {
|
||||||
|
// 海报"画布外"背景:用 bgColor(浅色),不做整体渐变
|
||||||
|
try {
|
||||||
|
ctx.setFillStyle(palette.bg || '#FFFFFF');
|
||||||
|
ctx.fillRect(0, 0, W, H);
|
||||||
|
} catch (e) {
|
||||||
|
ctx.setFillStyle('#FFFFFF');
|
||||||
|
ctx.fillRect(0, 0, W, H);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 卡片绘制:画海报中部的小"卡片"(模仿组件的卡片视图)
|
||||||
|
* 根据 stylePreset 选不同视觉:
|
||||||
|
* - gradient: 卡片用 bgColorStart→bgColorEnd 渐变
|
||||||
|
* - minimal: 卡片用 bgColorStart→bgColorEnd 渐变(粉/米)
|
||||||
|
* - card: 卡片用 bgColorStart→bgColorEnd 渐变(米橙)
|
||||||
|
* - horizontal: 卡片用 bgColorStart→bgColorEnd 渐变
|
||||||
|
* 所有预设的卡片圆角读 borderRadius
|
||||||
|
*/
|
||||||
|
_drawPosterCard(ctx, W, H, palette, sp) {
|
||||||
|
// 不再画独立卡片,推荐码区本身就是"卡片"
|
||||||
|
// 这里只根据 stylePreset 在卡片右上角加 emoji 装饰
|
||||||
|
try {
|
||||||
|
ctx.setFontSize(36);
|
||||||
|
// 不同预设的装饰色 + emoji
|
||||||
|
const decos = {
|
||||||
|
gradient: { color: 'rgba(255,143,0,0.4)', emoji: '🎁' },
|
||||||
|
minimal: { color: 'rgba(255,107,107,0.35)', emoji: '🎁' },
|
||||||
|
card: { color: 'rgba(218,165,32,0.4)', emoji: '🎁' },
|
||||||
|
horizontal: { color: 'rgba(184,160,138,0.4)', emoji: '🎁' }
|
||||||
|
};
|
||||||
|
const deco = decos[sp] || decos.gradient;
|
||||||
|
ctx.setFillStyle(deco.color);
|
||||||
|
ctx.setTextAlign('right');
|
||||||
|
// 装饰位置:推荐码区右上角(blockX+W/2 区域,blockY 上方)
|
||||||
|
ctx.fillText(deco.emoji, W - 40, 78);
|
||||||
|
} catch (e) {}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* canvas 转图片(uni.canvasToTempFilePath Promise 化)
|
||||||
|
*/
|
||||||
|
_exportCanvas(W, H) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
console.log('[推荐码分享] _exportCanvas 开始执行');
|
||||||
|
uni.canvasToTempFilePath({
|
||||||
|
canvasId: 'sharePosterCanvas',
|
||||||
|
width: W,
|
||||||
|
height: H,
|
||||||
|
destWidth: W * 2,
|
||||||
|
destHeight: H * 2,
|
||||||
|
fileType: 'png',
|
||||||
|
quality: 1,
|
||||||
|
success: (r) => {
|
||||||
|
console.log('[推荐码分享] canvasToTempFilePath 成功:', r.tempFilePath);
|
||||||
|
resolve(r.tempFilePath);
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
console.error('[推荐码分享] canvasToTempFilePath 失败:', err);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
// 兜底超时:5s 后还没返回就 reject,避免永远卡住
|
||||||
|
setTimeout(() => {
|
||||||
|
console.warn('[推荐码分享] canvasToTempFilePath 超时');
|
||||||
|
reject(new Error('canvasToTempFilePath 超时'));
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 圆角矩形路径(canvas API)
|
||||||
|
*/
|
||||||
|
_roundRect(ctx, x, y, w, h, r) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x + r, y);
|
||||||
|
ctx.arcTo(x + w, y, x + w, y + h, r);
|
||||||
|
ctx.arcTo(x + w, y + h, x, y + h, r);
|
||||||
|
ctx.arcTo(x, y + h, x, y, r);
|
||||||
|
ctx.arcTo(x, y, x + w, y, r);
|
||||||
|
ctx.closePath();
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 下载图片到临时路径(Promise 化)
|
||||||
|
* 支持本地路径直接返回
|
||||||
|
*/
|
||||||
|
_downloadImage(url) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (!url) return reject(new Error('url 为空'));
|
||||||
|
if (!/^https?:\/\//i.test(url)) return resolve(url);
|
||||||
|
uni.downloadFile({
|
||||||
|
url,
|
||||||
|
success: (res) => {
|
||||||
|
if (res.statusCode === 200 && res.tempFilePath) resolve(res.tempFilePath);
|
||||||
|
else reject(new Error('下载失败:' + res.statusCode));
|
||||||
|
},
|
||||||
|
fail: reject
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -55,6 +55,7 @@
|
|||||||
<uni-popup ref="videoPopup" type="center" style="background:transparent;width:100%;height:100%;">
|
<uni-popup ref="videoPopup" type="center" style="background:transparent;width:100%;height:100%;">
|
||||||
<view class="video-container" :style="popupVideoStyle">
|
<view class="video-container" :style="popupVideoStyle">
|
||||||
<video
|
<video
|
||||||
|
:key="video_url"
|
||||||
ref="popupVideoRef"
|
ref="popupVideoRef"
|
||||||
class="adaptive-video"
|
class="adaptive-video"
|
||||||
:autoPauseIfNavigate="true"
|
:autoPauseIfNavigate="true"
|
||||||
@@ -65,6 +66,9 @@
|
|||||||
:src="video_url"
|
:src="video_url"
|
||||||
:controls="true"
|
:controls="true"
|
||||||
object-fit="contain"
|
object-fit="contain"
|
||||||
|
playsinline
|
||||||
|
webkit-playsinline
|
||||||
|
x5-playsinline
|
||||||
@loadedmetadata="onPopupVideoLoaded"
|
@loadedmetadata="onPopupVideoLoaded"
|
||||||
@error="onPopupVideoError"
|
@error="onPopupVideoError"
|
||||||
></video>
|
></video>
|
||||||
@@ -144,41 +148,21 @@ export default {
|
|||||||
|
|
||||||
// 弹窗视频容器样式
|
// 弹窗视频容器样式
|
||||||
popupVideoStyle() {
|
popupVideoStyle() {
|
||||||
let style = 'position:fixed;width:100%;left:0;';
|
// 容器横向铺满,黑色背景填满左右边界
|
||||||
const defaultRatio = 9 / 16;
|
// 视频用 object-fit:contain 保持原比例居中,左右黑边由容器黑色背景填充
|
||||||
|
let style = 'position:fixed;width:100%;height:50vh;left:50%;top:50%;transform:translate(-50%,-50%);border-radius:0;';
|
||||||
let ratio = defaultRatio;
|
|
||||||
if (this.popupVideoWidth && this.popupVideoHeight) {
|
|
||||||
ratio = this.popupVideoHeight / this.popupVideoWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isPortrait = ratio > 1;
|
|
||||||
|
|
||||||
if (isPortrait) {
|
|
||||||
style += 'top:50%;transform:translateY(-50%);';
|
|
||||||
} else {
|
|
||||||
style += 'top:20%;';
|
|
||||||
}
|
|
||||||
|
|
||||||
// #ifdef H5
|
|
||||||
style += 'padding-top: ' + (ratio * 100) + '%;';
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
style += 'padding-top: ' + (ratio * 100) + '%;';
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
// #ifdef MP-HARMONY
|
|
||||||
style += 'height: ' + (this.windowWidth * ratio) + 'px;';
|
|
||||||
// #endif
|
|
||||||
|
|
||||||
return style;
|
return style;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
// 显示视频播放弹窗
|
// 显示视频播放弹窗
|
||||||
showVideo(item) {
|
showVideo(item) {
|
||||||
this.video_url = item.videoUrl;
|
let url = item.videoUrl || '';
|
||||||
|
// 处理视频URL:完整链接直接使用,否则通过 $util.img() 转换
|
||||||
|
if (url && url.indexOf('http://') !== 0 && url.indexOf('https://') !== 0) {
|
||||||
|
url = this.$util.img(url);
|
||||||
|
}
|
||||||
|
this.video_url = url;
|
||||||
this.popupVideoWidth = 0;
|
this.popupVideoWidth = 0;
|
||||||
this.popupVideoHeight = 0;
|
this.popupVideoHeight = 0;
|
||||||
this.$refs.videoPopup.open();
|
this.$refs.videoPopup.open();
|
||||||
@@ -625,22 +609,6 @@ export default {
|
|||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* #ifdef MP-WEIXIN */
|
|
||||||
.adaptive-video {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
/* #endif */
|
|
||||||
|
|
||||||
/* #ifdef MP-HARMONY */
|
|
||||||
.adaptive-video {
|
|
||||||
position: static;
|
|
||||||
display: block;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
/* #endif */
|
|
||||||
|
|
||||||
.video-nav {
|
.video-nav {
|
||||||
padding: 16rpx;
|
padding: 16rpx;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|||||||
@@ -11,6 +11,9 @@
|
|||||||
:style="videoWarpCss"
|
:style="videoWarpCss"
|
||||||
controls
|
controls
|
||||||
preload="metadata"
|
preload="metadata"
|
||||||
|
playsinline
|
||||||
|
webkit-playsinline
|
||||||
|
x5-playsinline
|
||||||
@loadedmetadata="onVideoLoaded"
|
@loadedmetadata="onVideoLoaded"
|
||||||
@click="handlerClick(videoUrl)"
|
@click="handlerClick(videoUrl)"
|
||||||
></video>
|
></video>
|
||||||
@@ -106,11 +109,11 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// #ifdef H5
|
// #ifdef H5
|
||||||
style += 'padding-top: ' + (ratio * 100) + '%;';
|
style += 'height:0;padding-top: ' + (ratio * 100) + '%;';
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// #ifdef MP-WEIXIN
|
// #ifdef MP-WEIXIN
|
||||||
style += 'padding-top: ' + (ratio * 100) + '%;';
|
style += 'height:0;padding-top: ' + (ratio * 100) + '%;';
|
||||||
// #endif
|
// #endif
|
||||||
|
|
||||||
// #ifdef MP-HARMONY
|
// #ifdef MP-HARMONY
|
||||||
|
|||||||
@@ -26,38 +26,17 @@
|
|||||||
<text class="required" :style="{ color: requiredColor }">{{ item.value.required ? '*' : '' }}</text>
|
<text class="required" :style="{ color: requiredColor }">{{ item.value.required ? '*' : '' }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="box">
|
<view class="box">
|
||||||
<!-- #ifdef MP-HARMONY -->
|
<!-- 所有平台统一通过 formConfig.textareaRows / item.value.rows 控制显示行数:
|
||||||
<!-- 鸿蒙元服务渲染为原生 ArkUI TextArea,必须用 :rows 控制初始显示行数;CSS height 在原生组件上不生效 -->
|
- H5 / 微信:原生 textarea 识别 :rows 属性,可视高度 = rows × line-height(CSS 控制 line-height);
|
||||||
<textarea
|
- 鸿蒙:渲染为原生 ArkUI TextArea,CSS height 不生效,必须靠 :rows 控制。
|
||||||
:rows="item.value.rows || 2"
|
行数和行高全部由 formConfig 动态下发,不再硬编码 176rpx / row=2。 -->
|
||||||
:placeholder="item.value.placeholder"
|
|
||||||
placeholder-class="placeholder color-tip"
|
|
||||||
v-model="item.val"
|
|
||||||
:style="inputStyle"
|
|
||||||
></textarea>
|
|
||||||
<!-- #endif -->
|
|
||||||
<!-- #ifndef MP-HARMONY -->
|
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
|
||||||
<!-- 微信小程序原生 textarea:
|
|
||||||
不用 auto-height(会随内容无限扩展遮挡后续组件),改用 textareaStyle 中
|
|
||||||
固定 height 176rpx(约 4 行)+ line-height 44rpx,可视区域比原 88rpx 翻倍,
|
|
||||||
内容多时内部滚动条 thumb 比例改善一倍,且高度可控。 -->
|
|
||||||
<textarea
|
<textarea
|
||||||
|
:rows="item.value.rows || textareaRowsValue"
|
||||||
:placeholder="item.value.placeholder"
|
:placeholder="item.value.placeholder"
|
||||||
placeholder-class="placeholder color-tip"
|
placeholder-class="placeholder color-tip"
|
||||||
v-model="item.val"
|
v-model="item.val"
|
||||||
:style="textareaStyle"
|
:style="textareaStyle"
|
||||||
></textarea>
|
></textarea>
|
||||||
<!-- #endif -->
|
|
||||||
<!-- #ifndef MP-WEIXIN -->
|
|
||||||
<textarea
|
|
||||||
:placeholder="item.value.placeholder"
|
|
||||||
placeholder-class="placeholder color-tip"
|
|
||||||
v-model="item.val"
|
|
||||||
:style="inputStyle"
|
|
||||||
></textarea>
|
|
||||||
<!-- #endif -->
|
|
||||||
<!-- #endif -->
|
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -301,6 +280,11 @@
|
|||||||
checkedColor: '#ff4544',
|
checkedColor: '#ff4544',
|
||||||
placeholderColor: '#909399',
|
placeholderColor: '#909399',
|
||||||
placeholderFontSize: 28,
|
placeholderFontSize: 28,
|
||||||
|
// textarea 显示行数 / 行高(与 ns-newform 对齐)
|
||||||
|
// 改用 :rows + line-height 驱动高度:rows 给原生 <textarea>(H5/微信原生层/鸿蒙 ArkUI 都识别),
|
||||||
|
// line-height 用于在 textareaStyle 里算出对应的 px 高度;不再写死 176rpx。
|
||||||
|
textareaRows: 4,
|
||||||
|
textareaLineHeight: 44,
|
||||||
// 输入框视觉增强(与 ns-newform 对齐)
|
// 输入框视觉增强(与 ns-newform 对齐)
|
||||||
inputBorderColor: '#e5e7eb',
|
inputBorderColor: '#e5e7eb',
|
||||||
inputBgColor: '#f9fafb',
|
inputBgColor: '#f9fafb',
|
||||||
@@ -334,6 +318,8 @@
|
|||||||
checkedColor: pick('checkedColor', 'checked_color'),
|
checkedColor: pick('checkedColor', 'checked_color'),
|
||||||
placeholderColor: pick('placeholderColor', 'placeholder_color'),
|
placeholderColor: pick('placeholderColor', 'placeholder_color'),
|
||||||
placeholderFontSize: pick('placeholderFontSize', 'placeholder_font_size'),
|
placeholderFontSize: pick('placeholderFontSize', 'placeholder_font_size'),
|
||||||
|
textareaRows: pick('textareaRows', 'textarea_rows'),
|
||||||
|
textareaLineHeight: pick('textareaLineHeight', 'textarea_line_height'),
|
||||||
inputBorderColor: pick('inputBorderColor', 'input_border_color'),
|
inputBorderColor: pick('inputBorderColor', 'input_border_color'),
|
||||||
inputBgColor: pick('inputBgColor', 'input_bg_color'),
|
inputBgColor: pick('inputBgColor', 'input_bg_color'),
|
||||||
inputFocusBorderColor: pick('inputFocusBorderColor', 'input_focus_border_color'),
|
inputFocusBorderColor: pick('inputFocusBorderColor', 'input_focus_border_color'),
|
||||||
@@ -394,6 +380,8 @@
|
|||||||
'--checked-color': cfg.checkedColor,
|
'--checked-color': cfg.checkedColor,
|
||||||
'--placeholder-color': cfg.placeholderColor,
|
'--placeholder-color': cfg.placeholderColor,
|
||||||
'--placeholder-font-size': this.$util.rpx(num(cfg.placeholderFontSize, 28)),
|
'--placeholder-font-size': this.$util.rpx(num(cfg.placeholderFontSize, 28)),
|
||||||
|
'--textarea-rows': num(cfg.textareaRows, 4),
|
||||||
|
'--textarea-line-height': this.$util.rpx(num(cfg.textareaLineHeight, 44)),
|
||||||
// 输入框视觉增强(与 ns-newform 对齐)
|
// 输入框视觉增强(与 ns-newform 对齐)
|
||||||
'--input-border-color': cfg.inputBorderColor,
|
'--input-border-color': cfg.inputBorderColor,
|
||||||
'--input-bg-color': cfg.inputBgColor,
|
'--input-bg-color': cfg.inputBgColor,
|
||||||
@@ -434,21 +422,37 @@
|
|||||||
'line-height': this.$util.rpx(this.config.inputHeight)
|
'line-height': this.$util.rpx(this.config.inputHeight)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// textarea 专用 inline style(仅微信小程序端使用,见 template 中 #ifdef MP-WEIXIN 分支)
|
// textarea 显示行数(来自 formConfig.textareaRows,未配置时回退到默认 4)
|
||||||
|
textareaRowsValue() {
|
||||||
|
const n = parseInt(this.config.textareaRows, 10);
|
||||||
|
return Number.isFinite(n) && n > 0 ? n : 4;
|
||||||
|
},
|
||||||
|
// textarea 行高(来自 formConfig.textareaLineHeight,用于 :rows × line-height 算出固定可视高度)
|
||||||
|
textareaLineHeightValue() {
|
||||||
|
const n = parseInt(this.config.textareaLineHeight, 10);
|
||||||
|
return Number.isFinite(n) && n > 0 ? n : 44;
|
||||||
|
},
|
||||||
|
// textarea 专用 inline style(所有平台统一使用,见 template 中 textarea 节点)
|
||||||
// 设计要点:
|
// 设计要点:
|
||||||
// 1) 不复用 inputStyle 的 min-height / line-height:这两个值取自 inputHeight(默认 60rpx),
|
// 1) 不复用 inputStyle 的 min-height / line-height:那两个值取自 inputHeight(默认 60rpx),
|
||||||
// 对 textarea 太小,会让微信小程序原生 textarea 单行过高、可视区域过小,
|
// 对 textarea 太小,会让原生 textarea 单行过高、可视区域过小;
|
||||||
// 内容稍多即溢出导致原生滚动条 thumb 异常小;
|
// 2) 不使用 auto-height:原生 textarea 上 auto-height 会让组件随内容无限扩展,
|
||||||
// 2) 不使用 auto-height:在微信小程序原生 textarea 上,auto-height 会让 textarea 随内容
|
// 遮挡后续组件,导致"看不到下面的其他组件";
|
||||||
// 无限扩展,遮挡后续组件,导致"看不到下面的其他组件";
|
// 3) 高度 = rows × lineHeight(两个值都由 formConfig 动态下发,不再写死 176rpx):
|
||||||
// 3) 改用固定 height 176rpx(约 4 行,line-height 44rpx),相比原 CSS height: 88rpx(2 行)
|
// - H5 浏览器 / 微信原生层:原生 <textarea> 同时识别 :rows 和 CSS line-height,
|
||||||
// 可视区域翻倍,内容多时内部滚动条 thumb 比例改善一倍,且高度可控不会遮挡后续组件。
|
// 高度由浏览器自然算出,与 :rows 完全一致;
|
||||||
|
// - 鸿蒙 ArkUI:原生 TextArea 只识别 :rows,不识别 CSS height,所以我们在 JS 里
|
||||||
|
// 显式算 height = rows × lineHeight 反向写出,保证三端可视高度像素级一致。
|
||||||
textareaStyle() {
|
textareaStyle() {
|
||||||
const h = this.$util.rpx(176);
|
const lh = this.$util.rpx(this.textareaLineHeightValue);
|
||||||
|
const rows = this.textareaRowsValue;
|
||||||
|
// 加上 padding(12rpx 上下) 和 border(1rpx 上下) 的占用,避免与 :rows 算出的高度不一致时被截断
|
||||||
|
const paddings = this.$util.rpx(24) + this.$util.rpx(2);
|
||||||
|
const h = (this.$util.rpx(this.textareaLineHeightValue * (rows - 1)) + lh) + paddings;
|
||||||
return this.$util.objToStyle({
|
return this.$util.objToStyle({
|
||||||
'height': h,
|
'height': h,
|
||||||
'min-height': h,
|
'min-height': h,
|
||||||
'line-height': this.$util.rpx(44),
|
'line-height': lh,
|
||||||
'border-radius': this.$util.rpx(this.config.inputRadius),
|
'border-radius': this.$util.rpx(this.config.inputRadius),
|
||||||
'font-size': this.$util.rpx(this.config.labelFontSize),
|
'font-size': this.$util.rpx(this.config.labelFontSize),
|
||||||
'color': this.config.labelColor
|
'color': this.config.labelColor
|
||||||
@@ -959,6 +963,9 @@
|
|||||||
--placeholder-font-size: 28rpx;
|
--placeholder-font-size: 28rpx;
|
||||||
--checked-color: #ff4544;
|
--checked-color: #ff4544;
|
||||||
--placeholder-color: #909399;
|
--placeholder-color: #909399;
|
||||||
|
// textarea 行数 / 行高(运行时被 formWrapStyle 注入的 inline 覆盖;这里是兜底)
|
||||||
|
--textarea-rows: 4;
|
||||||
|
--textarea-line-height: 44rpx;
|
||||||
// 输入框视觉增强(与 ns-newform 对齐)
|
// 输入框视觉增强(与 ns-newform 对齐)
|
||||||
--input-border-color: #e5e7eb;
|
--input-border-color: #e5e7eb;
|
||||||
--input-bg-color: #f9fafb;
|
--input-bg-color: #f9fafb;
|
||||||
@@ -1094,11 +1101,12 @@
|
|||||||
font-size: var(--label-font-size, $font-size-base);
|
font-size: var(--label-font-size, $font-size-base);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: var(--label-color, #1f2937);
|
color: var(--label-color, #1f2937);
|
||||||
// 鸿蒙元服务渲染为原生 ArkUI TextArea,不识别 CSS height,由 :rows 控制行数;
|
// 行高由 formConfig.textareaLineHeight(兜底 44rpx)控制;
|
||||||
// 这里给所有端都设一个最小高度基线,避免空内容时塌缩
|
// 高度 = rows × line-height + padding + border,所有值都从 CSS 变量读取,
|
||||||
min-height: 88rpx;
|
// 真正做到了"rows 改 → 高度自动改"。
|
||||||
height: 88rpx;
|
line-height: var(--textarea-line-height, 44rpx);
|
||||||
line-height: 44rpx;
|
min-height: calc(var(--textarea-rows, 4) * var(--textarea-line-height, 44rpx) + 26rpx);
|
||||||
|
height: calc(var(--textarea-rows, 4) * var(--textarea-line-height, 44rpx) + 26rpx);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
border-radius: var(--input-radius, 0rpx);
|
border-radius: var(--input-radius, 0rpx);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|||||||
@@ -27,10 +27,16 @@
|
|||||||
<text class="required" :style="{ color: requiredColor }">{{ item.value.required ? '*' : '' }}</text>
|
<text class="required" :style="{ color: requiredColor }">{{ item.value.required ? '*' : '' }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view :class="['box', 'align-left']" :style="item._boxAlignStyle">
|
<view :class="['box', 'align-left']" :style="item._boxAlignStyle">
|
||||||
|
<!-- 所有平台统一通过 formConfig.textareaRows / item.value.rows 控制显示行数:
|
||||||
|
- H5 / 微信:原生 textarea 识别 :rows 属性;
|
||||||
|
- 鸿蒙:渲染为 ArkUI 原生 TextArea,CSS height 不生效,必须靠 :rows 控制。
|
||||||
|
rows 全部由 formConfig 动态下发,与 ns-form 行为保持一致。 -->
|
||||||
<textarea
|
<textarea
|
||||||
|
:rows="item.value.rows || textareaRowsValue"
|
||||||
:placeholder="item.value.placeholder"
|
:placeholder="item.value.placeholder"
|
||||||
placeholder-class="placeholder color-tip"
|
placeholder-class="placeholder color-tip"
|
||||||
v-model="item.val"
|
v-model="item.val"
|
||||||
|
:style="textareaStyle"
|
||||||
></textarea>
|
></textarea>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -377,6 +383,10 @@ export default {
|
|||||||
checkedColor: '#ff4544',
|
checkedColor: '#ff4544',
|
||||||
placeholderColor: '#ddd',
|
placeholderColor: '#ddd',
|
||||||
placeholderFontSize: 28,
|
placeholderFontSize: 28,
|
||||||
|
// textarea 显示行数 / 行高(与 ns-form 保持一致,由 formConfig 覆盖)
|
||||||
|
// 改用 :rows + line-height 驱动高度,rows 给原生 <textarea>,line-height 在 textareaStyle 算 px 高度
|
||||||
|
textareaRows: 4,
|
||||||
|
textareaLineHeight: 44,
|
||||||
// 输入框视觉增强(可由后端 form_config 覆盖)
|
// 输入框视觉增强(可由后端 form_config 覆盖)
|
||||||
inputBorderColor: '#e5e7eb',
|
inputBorderColor: '#e5e7eb',
|
||||||
inputBgColor: '#f9fafb',
|
inputBgColor: '#f9fafb',
|
||||||
@@ -452,6 +462,9 @@ export default {
|
|||||||
'--checked-color': cfg.checkedColor,
|
'--checked-color': cfg.checkedColor,
|
||||||
'--placeholder-color': cfg.placeholderColor,
|
'--placeholder-color': cfg.placeholderColor,
|
||||||
'--placeholder-font-size': this.$util.rpx(num(cfg.placeholderFontSize, 28)),
|
'--placeholder-font-size': this.$util.rpx(num(cfg.placeholderFontSize, 28)),
|
||||||
|
// textarea 行数 / 行高(CSS 兜底 + JS 算高度都用得到)
|
||||||
|
'--textarea-rows': num(cfg.textareaRows, 4),
|
||||||
|
'--textarea-line-height': this.$util.rpx(num(cfg.textareaLineHeight, 44)),
|
||||||
// 输入框视觉增强(边框 / 背景 / 聚焦 / label 字重)
|
// 输入框视觉增强(边框 / 背景 / 聚焦 / label 字重)
|
||||||
'--input-border-color': cfg.inputBorderColor,
|
'--input-border-color': cfg.inputBorderColor,
|
||||||
'--input-bg-color': cfg.inputBgColor,
|
'--input-bg-color': cfg.inputBgColor,
|
||||||
@@ -497,6 +510,30 @@ export default {
|
|||||||
'border-radius': this.$util.rpx(this.config.inputRadius)
|
'border-radius': this.$util.rpx(this.config.inputRadius)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// textarea 显示行数(来自 formConfig.textareaRows,未配置时回退到默认 4)
|
||||||
|
textareaRowsValue() {
|
||||||
|
const n = parseInt(this.config.textareaRows, 10);
|
||||||
|
return Number.isFinite(n) && n > 0 ? n : 4;
|
||||||
|
},
|
||||||
|
// textarea 行高(来自 formConfig.textareaLineHeight,用于 :rows × line-height 算出可视高度)
|
||||||
|
textareaLineHeightValue() {
|
||||||
|
const n = parseInt(this.config.textareaLineHeight, 10);
|
||||||
|
return Number.isFinite(n) && n > 0 ? n : 44;
|
||||||
|
},
|
||||||
|
// textarea 专用 inline style(所有平台统一使用,见 template 中 textarea 节点)
|
||||||
|
// 高度 = rows × lineHeight + padding + border,由 formConfig 动态控制,与 ns-form 同公式
|
||||||
|
textareaStyle() {
|
||||||
|
const lh = this.$util.rpx(this.textareaLineHeightValue);
|
||||||
|
const rows = this.textareaRowsValue;
|
||||||
|
const paddings = this.$util.rpx(24) + this.$util.rpx(2); // padding 12rpx 上下 + border 1rpx 上下
|
||||||
|
const h = (this.$util.rpx(this.textareaLineHeightValue * (rows - 1)) + lh) + paddings;
|
||||||
|
return this.$util.objToStyle({
|
||||||
|
'height': h,
|
||||||
|
'min-height': h,
|
||||||
|
'line-height': lh,
|
||||||
|
'border-radius': this.$util.rpx(this.config.inputRadius)
|
||||||
|
});
|
||||||
|
},
|
||||||
labelStyle() {
|
labelStyle() {
|
||||||
const style = {
|
const style = {
|
||||||
fontSize: this.$util.rpx(this.config.labelFontSize),
|
fontSize: this.$util.rpx(this.config.labelFontSize),
|
||||||
@@ -997,6 +1034,9 @@ export default {
|
|||||||
--submit-bg-color: #ff4544;
|
--submit-bg-color: #ff4544;
|
||||||
--placeholder-color: #ddd;
|
--placeholder-color: #ddd;
|
||||||
--placeholder-font-size: 28rpx;
|
--placeholder-font-size: 28rpx;
|
||||||
|
// textarea 行数 / 行高(运行时被 formWrapStyle 注入的 inline 覆盖;这里是兜底)
|
||||||
|
--textarea-rows: 4;
|
||||||
|
--textarea-line-height: 44rpx;
|
||||||
// 输入框视觉增强(边框 / 背景 / 聚焦 / label 字重)
|
// 输入框视觉增强(边框 / 背景 / 聚焦 / label 字重)
|
||||||
--input-border-color: #e5e7eb;
|
--input-border-color: #e5e7eb;
|
||||||
--input-bg-color: #f9fafb;
|
--input-bg-color: #f9fafb;
|
||||||
@@ -1234,8 +1274,10 @@ export default {
|
|||||||
::v-deep textarea {
|
::v-deep textarea {
|
||||||
font-size: var(--label-font-size, 28rpx);
|
font-size: var(--label-font-size, 28rpx);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 120rpx;
|
// 行高 / 高度全部由 formConfig 动态决定(CSS 兜底公式与 JS textareaStyle 一致)
|
||||||
line-height: 1.5;
|
line-height: var(--textarea-line-height, 44rpx);
|
||||||
|
min-height: calc(var(--textarea-rows, 4) * var(--textarea-line-height, 44rpx) + 26rpx);
|
||||||
|
height: calc(var(--textarea-rows, 4) * var(--textarea-line-height, 44rpx) + 26rpx);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
border: 1rpx solid var(--input-border-color, #e5e7eb);
|
border: 1rpx solid var(--input-border-color, #e5e7eb);
|
||||||
border-radius: var(--input-radius, 10rpx);
|
border-radius: var(--input-radius, 10rpx);
|
||||||
@@ -1244,7 +1286,6 @@ export default {
|
|||||||
color: var(--label-color, #1f2937);
|
color: var(--label-color, #1f2937);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
height: auto;
|
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
background: var(--input-focus-bg-color, #ffffff);
|
background: var(--input-focus-bg-color, #ffffff);
|
||||||
@@ -1260,8 +1301,10 @@ export default {
|
|||||||
textarea {
|
textarea {
|
||||||
font-size: var(--label-font-size, 28rpx);
|
font-size: var(--label-font-size, 28rpx);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 120rpx;
|
// 行高 / 高度全部由 formConfig 动态决定(与 ::v-deep textarea 同步)
|
||||||
line-height: 1.5;
|
line-height: var(--textarea-line-height, 44rpx);
|
||||||
|
min-height: calc(var(--textarea-rows, 4) * var(--textarea-line-height, 44rpx) + 26rpx);
|
||||||
|
height: calc(var(--textarea-rows, 4) * var(--textarea-line-height, 44rpx) + 26rpx);
|
||||||
text-align: left;
|
text-align: left;
|
||||||
border: 1rpx solid var(--input-border-color, #e5e7eb);
|
border: 1rpx solid var(--input-border-color, #e5e7eb);
|
||||||
border-radius: var(--input-radius, 10rpx);
|
border-radius: var(--input-radius, 10rpx);
|
||||||
@@ -1270,7 +1313,6 @@ export default {
|
|||||||
color: var(--label-color, #1f2937);
|
color: var(--label-color, #1f2937);
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
height: auto;
|
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
background: var(--input-focus-bg-color, #ffffff);
|
background: var(--input-focus-bg-color, #ffffff);
|
||||||
|
|||||||
@@ -9,9 +9,13 @@
|
|||||||
<view class="video-popup-body">
|
<view class="video-popup-body">
|
||||||
<video
|
<video
|
||||||
v-if="videoUrl"
|
v-if="videoUrl"
|
||||||
|
id="popupVideoPlayer"
|
||||||
:src="videoUrl"
|
:src="videoUrl"
|
||||||
controls
|
controls
|
||||||
autoplay
|
autoplay
|
||||||
|
playsinline
|
||||||
|
webkit-playsinline
|
||||||
|
x5-playsinline
|
||||||
class="video-player"
|
class="video-player"
|
||||||
></video>
|
></video>
|
||||||
<view v-else class="video-error">{{ $lang('components.ns-video-player-popup.videoUrlNotExist') }}</view>
|
<view v-else class="video-error">{{ $lang('components.ns-video-player-popup.videoUrlNotExist') }}</view>
|
||||||
@@ -22,6 +26,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { playVideoInContext } from '@/common/js/video-helper.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ns-video-player-popup',
|
name: 'ns-video-player-popup',
|
||||||
props: {
|
props: {
|
||||||
@@ -76,6 +82,10 @@ export default {
|
|||||||
* @param {Object} e - 弹窗状态变化事件
|
* @param {Object} e - 弹窗状态变化事件
|
||||||
*/
|
*/
|
||||||
onVideoPopupChange(e) {
|
onVideoPopupChange(e) {
|
||||||
|
// H5 端弹窗显示后触发播放:避免 autoplay 被浏览器拦截 / 弹窗动画未完成被 pause() 打断
|
||||||
|
if (e && e.show) {
|
||||||
|
playVideoInContext('popupVideoPlayer', this);
|
||||||
|
}
|
||||||
this.$emit('popup-change', e);
|
this.$emit('popup-change', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2154,7 +2154,7 @@
|
|||||||
"style": {
|
"style": {
|
||||||
// APP 端原本就是 custom;微信小程序 / 华为快应用 / 鸿蒙元服务
|
// APP 端原本就是 custom;微信小程序 / 华为快应用 / 鸿蒙元服务
|
||||||
// 的原生 navbar 不支持自定义左侧 Home 按钮,需要切到 custom 自绘
|
// 的原生 navbar 不支持自定义左侧 Home 按钮,需要切到 custom 自绘
|
||||||
// #ifdef APP-PLUS || MP-WEIXIN || QUICKAPP-WEBVIEW || HARMONY
|
// #ifdef APP-PLUS || MP
|
||||||
"navigationStyle": "custom",
|
"navigationStyle": "custom",
|
||||||
// #endif
|
// #endif
|
||||||
"navigationBarTitleText": "",
|
"navigationBarTitleText": "",
|
||||||
|
|||||||
@@ -59,7 +59,8 @@
|
|||||||
<!-- 商品视频 -->
|
<!-- 商品视频 -->
|
||||||
<view class="goods-video" :class="{ show: switchMedia == 'video' }">
|
<view class="goods-video" :class="{ show: switchMedia == 'video' }">
|
||||||
<video id="goodsVideo" :src="$util.img(goodsSkuDetail.video_url)"
|
<video id="goodsVideo" :src="$util.img(goodsSkuDetail.video_url)"
|
||||||
:poster="$util.img(goodsSkuDetail.sku_image, { size: 'big' })" objectFit="cover"></video>
|
:poster="$util.img(goodsSkuDetail.sku_image, { size: 'big' })" objectFit="cover"
|
||||||
|
playsinline webkit-playsinline x5-playsinline></video>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 切换视频、图片 -->
|
<!-- 切换视频、图片 -->
|
||||||
|
|||||||
Reference in New Issue
Block a user