diff --git a/components-diy/diy-recommend-code.vue b/components-diy/diy-recommend-code.vue index 9ac0596..cd1a997 100644 --- a/components-diy/diy-recommend-code.vue +++ b/components-diy/diy-recommend-code.vue @@ -1289,66 +1289,88 @@ onShareClick() { 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 进入'); + console.log('[推荐码分享] generateSharePoster 进入,stylePreset=', sp); return new Promise((resolve, reject) => { try { - console.log('[推荐码分享] 准备 createCanvasContext'); const ctx = uni.createCanvasContext('sharePosterCanvas', this); - console.log('[推荐码分享] createCanvasContext 返回:', !!ctx); - if (!ctx) { - console.error('[推荐码分享] createCanvasContext 失败'); - return reject(new Error('createCanvasContext 失败')); - } - // 1. 白底 - ctx.setFillStyle('#FFFFFF'); - ctx.fillRect(0, 0, W, H); - // 2. 顶部标题 - ctx.setFontSize(18); - ctx.setFillStyle('#1A1A1A'); + 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); - // 3. 提示文字 + // 4. 副标题 ctx.setFontSize(12); - ctx.setFillStyle('#999999'); + ctx.setFillStyle(palette.subtitle); ctx.fillText('分享给好友,邀请入驻获得奖励', 24, 70); - // 4. 米黄背景区 - ctx.setFillStyle('#FFF8F0'); + // 5. 推荐码区域(读 codeColor/codeFontSize/borderRadius) const blockY = 90, blockH = 120, blockX = 24, blockW = W - 48; - this._roundRect(ctx, blockX, blockY, blockW, blockH, 12); + const codeBlockRadius = Math.min(palette.borderRadius, 16); + // 推荐码区背景 + ctx.setFillStyle(palette.codeBlockBg); + this._roundRect(ctx, blockX, blockY, blockW, blockH, codeBlockRadius); ctx.fill(); - // 5. 推荐码(居中) - ctx.setFontSize(36); - ctx.setFillStyle(v.codeColor || '#DC143C'); + // 推荐码区边框(浅橙) + 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'); - // setFontWeight 是新版 canvas 2d API,旧版不支持;用 font 字符串兜底 if (typeof ctx.setFontWeight === 'function') { ctx.setFontWeight('bold'); } else { - try { ctx.font = 'bold 36px sans-serif'; } catch(e) {} + try { ctx.font = 'bold ' + palette.codeFontSize + 'px sans-serif'; } catch(e) {} } ctx.fillText(code, W / 2, blockY + blockH / 2 + 12); - // 6. 二维码(如果有 url) + // 6. 二维码(如果有) const qr = this.qrcodeRenderUrl; - console.log('[推荐码分享] qrcodeRenderUrl=', qr); const drawQrcodeAndExport = (qrLocalPath) => { - console.log('[推荐码分享] drawQrcodeAndExport 进入, qrLocalPath=', 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, 12); + 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('#999999'); + 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, cb) 是异步绘制,必须在 cb 里导出 ctx.draw(false, () => { console.log('[推荐码分享] ctx.draw callback 触发,准备 _exportCanvas'); this._exportCanvas(W, H).then(resolve).catch(reject); @@ -1365,6 +1387,56 @@ onShareClick() { } }); }, + /** + * 海报背景:根据 diyData.bgColorStart/End 画渐变,否则用 bgColor 单色 + */ + _drawPosterBg(ctx, W, H, palette) { + try { + // 尝试线性渐变(可能旧 canvas 不支持,需 try/catch) + if (palette.bgStart && palette.bgEnd && typeof ctx.createLinearGradient === 'function') { + const grad = ctx.createLinearGradient(0, 0, W, H); + grad.addColorStop(0, palette.bgStart); + grad.addColorStop(1, palette.bgEnd); + ctx.setFillStyle(grad); + } else { + ctx.setFillStyle(palette.bg); + } + ctx.fillRect(0, 0, W, H); + } catch (e) { + ctx.setFillStyle(palette.bg); + ctx.fillRect(0, 0, W, H); + } + }, + /** + * 卡片绘制:根据 stylePreset 选不同视觉布局 + * (虽然现在 canvas 限制很多,但保留 hook 以便后续扩展) + */ + _drawPosterCard(ctx, W, H, palette, sp) { + // 海报本身就是"卡片",这里可以做不同预设的装饰差异 + // 当前:所有预设统一用单色/渐变背景 + 米黄推荐码区 + // 后续如需 minimal 加 🎁 装饰 emoji 等,在这里按 sp 区分 + try { + if (sp === 'minimal') { + // minimal 风格:右上角加 🎁 emoji 装饰(模仿组件) + ctx.setFontSize(40); + ctx.setFillStyle('rgba(255,107,107,0.25)'); + ctx.setTextAlign('right'); + ctx.fillText('🎁', W - 24, 60); + } else if (sp === 'card') { + // card 风格:右上角加 🎁 + ctx.setFontSize(36); + ctx.setFillStyle('rgba(218,165,32,0.35)'); + ctx.setTextAlign('right'); + ctx.fillText('🎁', W - 24, 60); + } else if (sp === 'horizontal') { + // horizontal 风格:右上角加 🎁 + ctx.setFontSize(36); + ctx.setFillStyle('rgba(184,160,138,0.35)'); + ctx.setTextAlign('right'); + ctx.fillText('🎁', W - 24, 60); + } + } catch (e) {} + }, /** * canvas 转图片(uni.canvasToTempFilePath Promise 化) */