style(fe): 身份卡按参考图收拾 — 去标题/正反面标签/疫苗,卡高按内容自适应

- 去掉顶部「宠物身份卡」大标题和「· 正面 ·/· 背面 ·」灰字,只留两张卡
- 正面卡高度按性格/介绍有无动态算,消掉中间大块空白
- 性格标签为空时整段不画
- 照护卡去掉「疫苗情况」行
- 导出只裁到实际内容底部,卡片下面不再拖空白

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-07-31 18:15:45 +08:00
parent 747e7c9d3a
commit d9a80d7f8a
+51 -49
View File
@@ -69,6 +69,7 @@ Page({
} catch (e) {
dpr = 2;
}
this._dpr = dpr;
node.width = W * dpr;
node.height = H * dpr;
const ctx = node.getContext('2d');
@@ -93,18 +94,14 @@ Page({
const cardW = W - pad * 2;
const fx = pad, cardW2 = cardW;
// 顶部标题
ctx.textAlign = 'center';
ctx.fillStyle = '#8A5A18';
ctx.font = '700 40px sans-serif';
ctx.fillText('🐾 宠物身份卡 🐾', W / 2, 66);
const tags = (c.personality || '').split(',').filter(Boolean);
// ══════════ 正面卡(暖黄)══════════
ctx.fillStyle = '#C7A36A';
ctx.font = '22px sans-serif';
ctx.fillText('· 正面 ·', W / 2, 116);
const fY = 140, fH = 700;
// 高度按内容算,避免留一大块空白
let fH = 420;
if (tags.length) fH += 90;
if (c.intro) fH += 90;
const fY = pad;
const gf = ctx.createLinearGradient(fx, fY, fx, fY + fH);
gf.addColorStop(0, '#FFF7E0');
gf.addColorStop(1, '#FFE7AE');
@@ -186,56 +183,53 @@ Page({
kvF('毛色', c.color, avY + 168);
kvF('年龄', c.age, avY + 206);
// 性格标签
let ty = avY + avD + 34;
ctx.textAlign = 'left';
ctx.fillStyle = '#8A5A18';
ctx.font = '600 24px sans-serif';
ctx.fillText('🐾 性格标签', fx + 34, ty);
const tags = (c.personality || '').split(',').filter(Boolean);
const tagColors = [['#FFE7A8', '#8A5A18'], ['#D6E9FF', '#2E6DA8'], ['#FFD9DE', '#C24A63']];
let tx = fx + 34;
ty += 42;
tags.forEach((t, i) => {
ctx.font = '23px sans-serif';
const tw = ctx.measureText(t).width + 32;
const col = tagColors[i % tagColors.length];
ctx.fillStyle = col[0];
this.roundRect(ctx, tx, ty - 28, tw, 40, 20);
ctx.fill();
ctx.fillStyle = col[1];
// 性格标签(没有就不画,避免留空行)
let ty = fY + 320;
if (tags.length) {
ctx.textAlign = 'left';
ctx.fillText(t, tx + 16, ty);
tx += tw + 14;
});
ctx.fillStyle = '#8A5A18';
ctx.font = '600 24px sans-serif';
ctx.fillText('🐾 性格标签', fx + 34, ty);
ty += 44;
const tagColors = [['#FFE7A8', '#8A5A18'], ['#D6E9FF', '#2E6DA8'], ['#FFD9DE', '#C24A63']];
let tx = fx + 34;
tags.forEach((t, i) => {
ctx.font = '23px sans-serif';
const tw = ctx.measureText(t).width + 32;
const col = tagColors[i % tagColors.length];
ctx.fillStyle = col[0];
this.roundRect(ctx, tx, ty - 28, tw, 40, 20);
ctx.fill();
ctx.fillStyle = col[1];
ctx.textAlign = 'left';
ctx.fillText(t, tx + 16, ty);
tx += tw + 14;
});
ty += 46;
}
// 介绍 quote
if (c.intro) {
const qy = ty + 30;
ctx.fillStyle = 'rgba(255,255,255,.6)';
this.roundRect(ctx, fx + 34, qy, cardW2 - 68, 60, 16);
this.roundRect(ctx, fx + 34, ty, cardW2 - 68, 60, 16);
ctx.fill();
ctx.fillStyle = '#7A5A2E';
ctx.font = '25px sans-serif';
ctx.textAlign = 'left';
ctx.fillText('“ ' + c.intro + ' ”', fx + 54, qy + 40);
ctx.fillText('“ ' + c.intro + ' ”', fx + 54, ty + 40);
}
// 底部:身份ID + 一起生活天数
// 底部:身份ID + 一起生活天数(贴卡片底边)
ctx.textAlign = 'left';
ctx.fillStyle = '#9C7A3E';
ctx.font = '22px sans-serif';
ctx.fillText('物ID ' + (c.id_no || ''), fx + 34, fY + fH - 60);
ctx.fillText('物ID ' + (c.id_no || ''), fx + 34, fY + fH - 56);
if (c.days > 0) {
ctx.fillText('和主人一起生活的第 ' + c.days + ' 天', fx + 34, fY + fH - 26);
ctx.fillText('和主人一起生活的第 ' + c.days + ' 天', fx + 34, fY + fH - 22);
}
// ══════════ 背面卡(白 + 蓝)══════════
const bY = fY + fH + 46, bH = 620;
ctx.textAlign = 'center';
ctx.fillStyle = '#C7A36A';
ctx.font = '22px sans-serif';
ctx.fillText('· 背面 ·', W / 2, bY - 16);
// ══════════ 照护卡(白 + 蓝)══════════
const bY = fY + fH + 40, bH = 566;
this.roundRect(ctx, fx, bY, cardW2, bH, 30);
ctx.fillStyle = '#FFFFFF';
ctx.fill();
@@ -267,12 +261,11 @@ Page({
ctx.fillText(value || '—', rowX + 170, yy);
};
let ry = bY + 140;
kvB('👤', '主人', c.owner, ry); ry += 46;
kvB('📞', '联系电话', c.phone || '未验证', ry); ry += 46;
kvB('💉', '疫苗情况', c.vaccine_state, ry); ry += 46;
kvB('📝', '备注', c.notes || '', ry); ry += 46;
kvB('🏛', '发证机构', c.org || '肉垫计划', ry); ry += 46;
kvB('📅', '有效期', c.validity || '永久有效', ry); ry += 40;
kvB('👤', '主人', c.owner, ry); ry += 48;
kvB('📞', '联系电话', c.phone || '未验证', ry); ry += 48;
kvB('📝', '备注', c.notes || '无', ry); ry += 48;
kvB('🏛', '发证机构', c.org || '肉垫计划', ry); ry += 48;
kvB('📅', '有效期', c.validity || '永久有效', ry); ry += 42;
// 走失联系框 + 小程序码
const lostY = ry + 16, lostH = bY + bH - lostY - 30;
@@ -302,9 +295,18 @@ Page({
ctx.fillText('待发布', qrX + qrD / 2, qrY + qrD / 2 + 24);
}
// 只导出到实际内容底部,避免卡片下面拖一条空白
const dpr = this._dpr || 2;
const contentH = bY + bH + pad;
wx.canvasToTempFilePath(
{
canvas: node,
x: 0,
y: 0,
width: W,
height: contentH,
destWidth: W * dpr,
destHeight: contentH * dpr,
fileType: 'png',
success: (r) => this.setData({ cardImg: r.tempFilePath }),
fail: () => wx.showToast({ title: '生成失败,稍后再试', icon: 'none' }),