fix(fe): 修社区图片撑破网格;发帖限 3 张;上传偶发失败自动重试 #5

Merged
Blizzard merged 33 commits from feat/dev into main 2026-07-31 10:43:15 +00:00
Showing only changes of commit 473c9f9200 - Show all commits
+49 -45
View File
@@ -213,72 +213,67 @@ Page({
// 正面印章(右上角) // 正面印章(右上角)
this.drawStamp(ctx, fx + cardW2 - 78, fY + 82); this.drawStamp(ctx, fx + cardW2 - 78, fY + 82);
// ══════════ 照护卡(白 + 蓝)══════════ // ══════════ 照护卡(淡紫,仿证件版式)══════════
const bY = fY + fH + 40, bH = 566; const bY = fY + fH + 40, bH = 630;
const gbc = ctx.createLinearGradient(fx, bY, fx, bY + bH);
gbc.addColorStop(0, '#E6E7FB');
gbc.addColorStop(1, '#F2F0FA');
this.roundRect(ctx, fx, bY, cardW2, bH, 30); this.roundRect(ctx, fx, bY, cardW2, bH, 30);
ctx.fillStyle = '#FFFFFF'; ctx.fillStyle = gbc;
ctx.fill(); ctx.fill();
// 蓝色标题(两行「X星地球移民 / 居民身份卡」 // 标题(两行左对齐):小字「X星地球移民」+ 大字「居民身份卡」
// 汪星人=狗、喵星人=猫,其它兜底宠星人
const star = c.species === '狗狗' ? '汪' : c.species === '猫猫' ? '喵' : '宠'; const star = c.species === '狗狗' ? '汪' : c.species === '猫猫' ? '喵' : '宠';
const hb = 92; ctx.textAlign = 'left';
const gb = ctx.createLinearGradient(fx, bY, fx + cardW2, bY); ctx.fillStyle = '#8A90AE';
gb.addColorStop(0, '#5AA0E8'); ctx.font = '600 27px sans-serif';
gb.addColorStop(1, '#7FB6F0'); this.spacedText(ctx, star + '星人地球移民', fx + 44, bY + 82, 8);
this.roundRect(ctx, fx + 24, bY + 24, cardW2 - 48, hb, 18); ctx.fillStyle = '#3E4A63';
ctx.fillStyle = gb; ctx.font = '800 66px sans-serif';
ctx.fill(); ctx.fillText('居民身份卡', fx + 44, bY + 168);
ctx.textAlign = 'center';
ctx.fillStyle = '#fff';
ctx.font = '700 30px sans-serif';
ctx.fillText(star + '星地球移民', W / 2, bY + 24 + 40);
ctx.font = '700 26px sans-serif';
ctx.fillText('居民身份卡', W / 2, bY + 24 + 76);
// 照护行(标签 + 值,不放图标 // 资料行(标签 + 值)
const rowX = fx + 44; const rowX = fx + 44;
const kvB = (label, value, yy) => { const kvB = (label, value, yy) => {
ctx.textAlign = 'left'; ctx.textAlign = 'left';
ctx.font = '24px sans-serif'; ctx.font = '25px sans-serif';
ctx.fillStyle = '#8D8277'; ctx.fillStyle = '#9A9FB5';
ctx.fillText(label, rowX, yy); ctx.fillText(label, rowX, yy);
ctx.font = '600 25px sans-serif'; ctx.font = '600 26px sans-serif';
ctx.fillStyle = '#2D2925'; ctx.fillStyle = '#3E4A63';
ctx.fillText(value || '—', rowX + 150, yy); ctx.fillText(value || '—', rowX + 160, yy);
}; };
let ry = bY + 140; let ry = bY + 244;
kvB('主人', c.owner, ry); ry += 48; kvB('主人', c.owner, ry); ry += 52;
kvB('联系电话', c.phone || '未验证', ry); ry += 48; kvB('联系电话', c.phone || '未验证', ry); ry += 52;
kvB('备注', c.notes || '无', ry); ry += 48; kvB('备注', c.notes || '无', ry); ry += 52;
kvB('发机构', c.org || '肉垫计划', ry); ry += 48; kvB('发机构', c.org || '肉垫计划', ry); ry += 52;
kvB('有效期', c.validity || '永久有效', ry); ry += 42; kvB('有效期', c.validity || '永久有效', ry);
// 走失联系框 + 小程序码 // 小程序码(右下角,直接落在淡紫底上)
const lostY = ry + 16, lostH = bY + bH - lostY - 30; const qrD = 128, qrX = fx + cardW2 - 44 - qrD, qrY = bY + bH - 40 - qrD;
this.roundRect(ctx, fx + 24, lostY, cardW2 - 48, lostH, 18);
ctx.fillStyle = '#EAF3FE';
ctx.fill();
ctx.textAlign = 'left';
ctx.fillStyle = '#5B7A9C';
ctx.font = '22px sans-serif';
ctx.fillText('微信扫一扫,查看' + (c.name || '') + '的身份信息', fx + 44, lostY + lostH / 2 + 8);
// 小程序码
const qrD = 118, qrX = fx + cardW2 - 24 - 20 - qrD, qrY = lostY + (lostH - qrD) / 2;
ctx.fillStyle = '#fff'; ctx.fillStyle = '#fff';
this.roundRect(ctx, qrX, qrY, qrD, qrD, 12); this.roundRect(ctx, qrX, qrY, qrD, qrD, 14);
ctx.fill(); ctx.fill();
if (qr) { if (qr) {
ctx.drawImage(qr, qrX + 8, qrY + 8, qrD - 16, qrD - 16); ctx.drawImage(qr, qrX + 9, qrY + 9, qrD - 18, qrD - 18);
} else { } else {
ctx.fillStyle = '#9CB4CC'; ctx.fillStyle = '#A7ABC4';
ctx.font = '18px sans-serif'; ctx.font = '18px sans-serif';
ctx.textAlign = 'center'; ctx.textAlign = 'center';
ctx.fillText('小程序码', qrX + qrD / 2, qrY + qrD / 2 - 2); ctx.fillText('小程序码', qrX + qrD / 2, qrY + qrD / 2 - 2);
ctx.fillText('待发布', qrX + qrD / 2, qrY + qrD / 2 + 24); ctx.fillText('待发布', qrX + qrD / 2, qrY + qrD / 2 + 24);
} }
// 扫码说明(左侧,和小程序码同高)
ctx.textAlign = 'left';
ctx.fillStyle = '#8A90AE';
ctx.font = '22px sans-serif';
ctx.fillText('微信扫一扫', fx + 44, qrY + qrD / 2 - 6);
ctx.fillText('查看' + (c.name || '') + '的身份信息', fx + 44, qrY + qrD / 2 + 28);
// 背面印章(右上角,压在蓝条右侧 // 背面印章(右上角)
this.drawStamp(ctx, fx + cardW2 - 78, bY + 70); this.drawStamp(ctx, fx + cardW2 - 84, bY + 92);
// 只导出到实际内容底部,避免卡片下面拖一条空白 // 只导出到实际内容底部,避免卡片下面拖一条空白
const dpr = this._dpr || 2; const dpr = this._dpr || 2;
@@ -310,6 +305,15 @@ Page({
ctx.closePath(); ctx.closePath();
}, },
// 逐字加字距地画一行(canvas 的 letterSpacing 各端支持不一,手动排最稳)
spacedText(ctx, text, x, y, gap) {
let cx = x;
for (const ch of text) {
ctx.fillText(ch, cx, y);
cx += ctx.measureText(ch).width + gap;
}
},
// 五角星路径(调用方负责 fill/stroke // 五角星路径(调用方负责 fill/stroke
star(ctx, cx, cy, rO, rI, n) { star(ctx, cx, cy, rO, rI, n) {
ctx.beginPath(); ctx.beginPath();