feat: 海报生成功能+推广优化
1. 阅读页: - 添加"生成海报"和"分享给好友"按钮 - 海报包含章节摘要+邀请码+小程序码占位 - 优化推广提示区域,添加推广中心入口 2. 分销中心: - 完善海报生成功能(推广海报) - 去掉"更多分享方式",改为"复制朋友圈文案" - 添加"分享给好友"小程序卡片按钮 3. 分享链接自动带分销ID
This commit is contained in:
@@ -47,7 +47,9 @@ Page({
|
||||
// 弹窗
|
||||
showShareModal: false,
|
||||
showLoginModal: false,
|
||||
showPosterModal: false,
|
||||
isPaying: false,
|
||||
isGeneratingPoster: false,
|
||||
|
||||
// 免费章节
|
||||
freeIds: ['preface', 'epilogue', '1.1', 'appendix-1', 'appendix-2', 'appendix-3']
|
||||
@@ -530,6 +532,160 @@ Page({
|
||||
wx.navigateTo({ url: '/pages/referral/referral' })
|
||||
},
|
||||
|
||||
// 生成海报
|
||||
async generatePoster() {
|
||||
wx.showLoading({ title: '生成中...' })
|
||||
this.setData({ showPosterModal: true, isGeneratingPoster: true })
|
||||
|
||||
try {
|
||||
const ctx = wx.createCanvasContext('posterCanvas', this)
|
||||
const { section, contentParagraphs } = this.data
|
||||
const userInfo = app.globalData.userInfo
|
||||
const referralCode = userInfo?.referralCode || 'SOUL'
|
||||
|
||||
// 海报尺寸 300x450
|
||||
const width = 300
|
||||
const height = 450
|
||||
|
||||
// 背景渐变
|
||||
const grd = ctx.createLinearGradient(0, 0, 0, height)
|
||||
grd.addColorStop(0, '#1a1a2e')
|
||||
grd.addColorStop(1, '#16213e')
|
||||
ctx.setFillStyle(grd)
|
||||
ctx.fillRect(0, 0, width, height)
|
||||
|
||||
// 顶部装饰条
|
||||
ctx.setFillStyle('#00CED1')
|
||||
ctx.fillRect(0, 0, width, 4)
|
||||
|
||||
// 标题区域
|
||||
ctx.setFillStyle('#ffffff')
|
||||
ctx.setFontSize(14)
|
||||
ctx.fillText('📚 Soul创业派对', 20, 35)
|
||||
|
||||
// 章节标题
|
||||
ctx.setFontSize(18)
|
||||
ctx.setFillStyle('#ffffff')
|
||||
const title = section?.title || '精彩内容'
|
||||
const titleLines = this.wrapText(ctx, title, width - 40, 18)
|
||||
let y = 70
|
||||
titleLines.forEach(line => {
|
||||
ctx.fillText(line, 20, y)
|
||||
y += 26
|
||||
})
|
||||
|
||||
// 分隔线
|
||||
ctx.setStrokeStyle('rgba(255,255,255,0.1)')
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(20, y + 10)
|
||||
ctx.lineTo(width - 20, y + 10)
|
||||
ctx.stroke()
|
||||
|
||||
// 内容摘要
|
||||
ctx.setFontSize(12)
|
||||
ctx.setFillStyle('rgba(255,255,255,0.8)')
|
||||
y += 30
|
||||
const summary = contentParagraphs.slice(0, 3).join(' ').slice(0, 150) + '...'
|
||||
const summaryLines = this.wrapText(ctx, summary, width - 40, 12)
|
||||
summaryLines.slice(0, 6).forEach(line => {
|
||||
ctx.fillText(line, 20, y)
|
||||
y += 20
|
||||
})
|
||||
|
||||
// 底部区域背景
|
||||
ctx.setFillStyle('rgba(0,206,209,0.1)')
|
||||
ctx.fillRect(0, height - 120, width, 120)
|
||||
|
||||
// 小程序码占位(实际需要获取小程序码图片)
|
||||
ctx.setFillStyle('#ffffff')
|
||||
ctx.beginPath()
|
||||
ctx.arc(width - 55, height - 60, 35, 0, Math.PI * 2)
|
||||
ctx.fill()
|
||||
ctx.setFillStyle('#00CED1')
|
||||
ctx.setFontSize(10)
|
||||
ctx.fillText('扫码阅读', width - 72, height - 58)
|
||||
|
||||
// 邀请信息
|
||||
ctx.setFillStyle('#ffffff')
|
||||
ctx.setFontSize(12)
|
||||
ctx.fillText('长按识别 · 阅读全文', 20, height - 70)
|
||||
ctx.setFillStyle('#FFD700')
|
||||
ctx.setFontSize(11)
|
||||
ctx.fillText(`邀请码: ${referralCode}`, 20, height - 50)
|
||||
ctx.setFillStyle('rgba(255,255,255,0.6)')
|
||||
ctx.setFontSize(10)
|
||||
ctx.fillText('好友购买你获90%收益', 20, height - 32)
|
||||
|
||||
ctx.draw(true, () => {
|
||||
wx.hideLoading()
|
||||
this.setData({ isGeneratingPoster: false })
|
||||
})
|
||||
} catch (e) {
|
||||
console.error('生成海报失败:', e)
|
||||
wx.hideLoading()
|
||||
wx.showToast({ title: '生成失败', icon: 'none' })
|
||||
this.setData({ showPosterModal: false, isGeneratingPoster: false })
|
||||
}
|
||||
},
|
||||
|
||||
// 文字换行处理
|
||||
wrapText(ctx, text, maxWidth, fontSize) {
|
||||
const lines = []
|
||||
let line = ''
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
const testLine = line + text[i]
|
||||
const metrics = ctx.measureText(testLine)
|
||||
if (metrics.width > maxWidth && line) {
|
||||
lines.push(line)
|
||||
line = text[i]
|
||||
} else {
|
||||
line = testLine
|
||||
}
|
||||
}
|
||||
if (line) lines.push(line)
|
||||
return lines
|
||||
},
|
||||
|
||||
// 关闭海报弹窗
|
||||
closePosterModal() {
|
||||
this.setData({ showPosterModal: false })
|
||||
},
|
||||
|
||||
// 保存海报到相册
|
||||
savePoster() {
|
||||
wx.canvasToTempFilePath({
|
||||
canvasId: 'posterCanvas',
|
||||
success: (res) => {
|
||||
wx.saveImageToPhotosAlbum({
|
||||
filePath: res.tempFilePath,
|
||||
success: () => {
|
||||
wx.showToast({ title: '已保存到相册', icon: 'success' })
|
||||
this.setData({ showPosterModal: false })
|
||||
},
|
||||
fail: (err) => {
|
||||
if (err.errMsg.includes('auth deny')) {
|
||||
wx.showModal({
|
||||
title: '提示',
|
||||
content: '需要相册权限才能保存海报',
|
||||
confirmText: '去设置',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
wx.openSetting()
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
wx.showToast({ title: '保存失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
fail: () => {
|
||||
wx.showToast({ title: '生成图片失败', icon: 'none' })
|
||||
}
|
||||
}, this)
|
||||
},
|
||||
|
||||
// 阻止冒泡
|
||||
stopPropagation() {}
|
||||
})
|
||||
|
||||
@@ -80,13 +80,37 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 推广提示 -->
|
||||
<view class="share-tip" bindtap="showShare">
|
||||
<view class="tip-content">
|
||||
<text class="tip-title">推荐好友,共同成长</text>
|
||||
<text class="tip-desc">邀请好友加入,享90%推广收益</text>
|
||||
<!-- 分享操作区 -->
|
||||
<view class="action-section">
|
||||
<view class="action-header">
|
||||
<text class="action-title">分享这篇内容</text>
|
||||
</view>
|
||||
<view class="action-buttons">
|
||||
<button class="action-btn btn-share" open-type="share">
|
||||
<text class="action-icon">💬</text>
|
||||
<text class="action-text">分享给好友</text>
|
||||
</button>
|
||||
<view class="action-btn btn-poster" bindtap="generatePoster">
|
||||
<text class="action-icon">🖼️</text>
|
||||
<text class="action-text">生成海报</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 推广提示 -->
|
||||
<view class="promo-section">
|
||||
<view class="promo-card" bindtap="goToReferral">
|
||||
<view class="promo-left">
|
||||
<view class="promo-icon">💰</view>
|
||||
<view class="promo-info">
|
||||
<text class="promo-title">推荐好友,共同成长</text>
|
||||
<text class="promo-desc">邀请好友购买,享90%推广收益</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="promo-right">
|
||||
<text class="promo-arrow">→</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="tip-btn">立即推广</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -171,34 +195,27 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 分享弹窗 -->
|
||||
<view class="modal-overlay" wx:if="{{showShareModal}}" bindtap="closeShareModal">
|
||||
<view class="modal-content share-modal" catchtap="stopPropagation">
|
||||
<!-- 海报生成弹窗 -->
|
||||
<view class="modal-overlay" wx:if="{{showPosterModal}}" bindtap="closePosterModal">
|
||||
<view class="modal-content poster-modal" catchtap="stopPropagation">
|
||||
<view class="modal-header">
|
||||
<text class="modal-title">分享文章</text>
|
||||
<view class="modal-close" bindtap="closeShareModal">✕</view>
|
||||
<text class="modal-title">生成海报</text>
|
||||
<view class="modal-close" bindtap="closePosterModal">✕</view>
|
||||
</view>
|
||||
|
||||
<view class="share-link-box">
|
||||
<text class="link-label">你的专属分享链接</text>
|
||||
<text class="link-url">https://soul.quwanzhi.com/read/{{sectionId}}</text>
|
||||
<text class="link-tip">邀请码: 好友购买你获得90%佣金</text>
|
||||
<!-- 海报预览 -->
|
||||
<view class="poster-preview">
|
||||
<canvas canvas-id="posterCanvas" class="poster-canvas" style="width: 300px; height: 450px;"></canvas>
|
||||
</view>
|
||||
|
||||
<view class="share-buttons">
|
||||
<view class="share-btn" bindtap="copyLink">
|
||||
<view class="share-btn-icon icon-copy">📋</view>
|
||||
<text class="share-btn-text">复制链接</text>
|
||||
</view>
|
||||
<button class="share-btn" open-type="share">
|
||||
<view class="share-btn-icon icon-wechat">微</view>
|
||||
<text class="share-btn-text">微信好友</text>
|
||||
</button>
|
||||
<view class="share-btn" bindtap="goToReferral">
|
||||
<view class="share-btn-icon icon-poster">🖼️</view>
|
||||
<text class="share-btn-text">生成海报</text>
|
||||
<view class="poster-actions">
|
||||
<view class="poster-btn btn-save" bindtap="savePoster">
|
||||
<text class="btn-icon">💾</text>
|
||||
<text>保存到相册</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<text class="poster-tip">长按海报可直接分享到微信</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
@@ -396,42 +396,123 @@
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
/* ===== 分享提示 ===== */
|
||||
.share-tip {
|
||||
/* ===== 分享操作区 ===== */
|
||||
.action-section {
|
||||
margin-top: 64rpx;
|
||||
padding: 32rpx;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-radius: 24rpx;
|
||||
border: 2rpx solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
.action-header {
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.action-title {
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 24rpx;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12rpx;
|
||||
padding: 28rpx 24rpx;
|
||||
border-radius: 20rpx;
|
||||
border: none;
|
||||
background: transparent;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.action-btn::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-share {
|
||||
background: rgba(7, 193, 96, 0.15);
|
||||
border: 2rpx solid rgba(7, 193, 96, 0.3);
|
||||
}
|
||||
|
||||
.btn-poster {
|
||||
background: rgba(255, 215, 0, 0.15);
|
||||
border: 2rpx solid rgba(255, 215, 0, 0.3);
|
||||
}
|
||||
|
||||
.action-icon {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.action-text {
|
||||
font-size: 28rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* ===== 推广提示区 ===== */
|
||||
.promo-section {
|
||||
margin-top: 32rpx;
|
||||
}
|
||||
|
||||
.promo-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 32rpx;
|
||||
background: linear-gradient(90deg, rgba(255, 215, 0, 0.1) 0%, transparent 100%);
|
||||
background: linear-gradient(135deg, rgba(255, 215, 0, 0.1) 0%, rgba(255, 165, 0, 0.05) 100%);
|
||||
border: 2rpx solid rgba(255, 215, 0, 0.2);
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
|
||||
.tip-content {
|
||||
.promo-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.promo-icon {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
background: rgba(255, 215, 0, 0.2);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.promo-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.tip-title {
|
||||
font-size: 28rpx;
|
||||
.promo-title {
|
||||
font-size: 30rpx;
|
||||
color: #ffffff;
|
||||
font-weight: 500;
|
||||
font-weight: 600;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tip-desc {
|
||||
.promo-desc {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
display: block;
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.tip-btn {
|
||||
padding: 16rpx 32rpx;
|
||||
background: #FFD700;
|
||||
color: #000000;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
border-radius: 16rpx;
|
||||
.promo-right {
|
||||
padding-left: 20rpx;
|
||||
}
|
||||
|
||||
.promo-arrow {
|
||||
font-size: 32rpx;
|
||||
color: #FFD700;
|
||||
}
|
||||
|
||||
/* ===== 弹窗 ===== */
|
||||
@@ -792,3 +873,56 @@
|
||||
font-size: 28rpx;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
/* ===== 海报弹窗 ===== */
|
||||
.poster-modal {
|
||||
padding-bottom: calc(64rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.poster-preview {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin: 32rpx 0;
|
||||
padding: 24rpx;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
|
||||
.poster-canvas {
|
||||
border-radius: 16rpx;
|
||||
box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.poster-actions {
|
||||
display: flex;
|
||||
gap: 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.poster-btn {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12rpx;
|
||||
padding: 28rpx;
|
||||
border-radius: 24rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.btn-save {
|
||||
background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.poster-tip {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user