feat(diy-recommend-code): 海报颜色布局改为读取 diyData 动态适配

之前 generateSharePoster 颜色和布局都是 hardcode(米黄区、
黄橙渐变、FFC107 等)。后端改 stylePreset 后分享图不会跟随变化。

修改: 抽出 palette 对象,所有视觉参数都从 diyData.value 读:
- bgColor / bgColorStart / bgColorEnd / borderColor
- borderRadius / padding
- titleColor / titleFontSize
- codeColor / codeFontSize
- accentColor / textColor

新增方法:
- _drawPosterBg(ctx, W, H, palette)
  优先用 createLinearGradient 画 bgColorStart→bgColorEnd 渐变,
  旧 canvas 不支持则 fallback 到 bgColor 单色
- _drawPosterCard(ctx, W, H, palette, stylePreset)
  按 stylePreset 区分装饰:minimal/card/horizontal 右上角加 🎁 emoji
  gradient 不加装饰

后续如需更多差异,只在这两个方法里扩展即可,不影响主流程。
This commit is contained in:
2026-08-08 13:58:55 +08:00
parent 077fac40c2
commit 30b66ad7b3

View File

@@ -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 化)
*/