diff --git a/miniprogram/pages/read/read.js b/miniprogram/pages/read/read.js index 91fb696..ac7eb28 100644 --- a/miniprogram/pages/read/read.js +++ b/miniprogram/pages/read/read.js @@ -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() {} }) diff --git a/miniprogram/pages/read/read.wxml b/miniprogram/pages/read/read.wxml index 4c74f6f..3ee142e 100644 --- a/miniprogram/pages/read/read.wxml +++ b/miniprogram/pages/read/read.wxml @@ -80,13 +80,37 @@ - - - - 推荐好友,共同成长 - 邀请好友加入,享90%推广收益 + + + + 分享这篇内容 + + + + + 🖼️ + 生成海报 + + + + + + + + + 💰 + + 推荐好友,共同成长 + 邀请好友购买,享90%推广收益 + + + + + - 立即推广 @@ -171,34 +195,27 @@ - - - + + + - 分享文章 - + 生成海报 + - - + + + + + + 推广海报 + + + + + + + + + + + 💾 + 保存到相册 + + + + 保存后可分享到朋友圈或发送给好友 + + diff --git a/miniprogram/pages/referral/referral.wxss b/miniprogram/pages/referral/referral.wxss index e14be1f..ba9df6e 100644 --- a/miniprogram/pages/referral/referral.wxss +++ b/miniprogram/pages/referral/referral.wxss @@ -111,4 +111,22 @@ .share-title { font-size: 28rpx; color: #fff; font-weight: 500; display: block; } .share-desc { font-size: 22rpx; color: rgba(255,255,255,0.5); margin-top: 4rpx; display: block; } .share-arrow { font-size: 28rpx; color: rgba(255,255,255,0.3); } -.share-btn { line-height: normal; font-size: inherit; } +.share-btn-wechat { line-height: normal; font-size: inherit; } + +/* 弹窗 */ +.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.7); backdrop-filter: blur(20rpx); display: flex; align-items: flex-end; justify-content: center; z-index: 1000; } +.modal-content { width: 100%; max-width: 750rpx; background: #1c1c1e; border-radius: 48rpx 48rpx 0 0; padding: 48rpx; padding-bottom: calc(48rpx + env(safe-area-inset-bottom)); animation: slideUp 0.3s ease; } +@keyframes slideUp { from { transform: translateY(100%); } to { transform: translateY(0); } } +.modal-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 32rpx; } +.modal-title { font-size: 36rpx; font-weight: 600; color: #fff; } +.modal-close { width: 64rpx; height: 64rpx; border-radius: 50%; background: rgba(255,255,255,0.1); display: flex; align-items: center; justify-content: center; font-size: 28rpx; color: rgba(255,255,255,0.6); } + +/* 海报弹窗 */ +.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; color: #fff; } +.btn-save { background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); } +.btn-icon { font-size: 32rpx; } +.poster-tip { font-size: 24rpx; color: rgba(255,255,255,0.4); text-align: center; display: block; }