|
|
|
@@ -224,26 +224,38 @@ Future<ui.Image> _renderLabelBitmap({
|
|
|
|
|
// 扫码溯源:居中于二维码正下方
|
|
|
|
|
_drawText(canvas, '扫码溯源', qrLeft, qrTop + qrS + 2, 14, qrS, center: true);
|
|
|
|
|
|
|
|
|
|
// 左侧文字(无字段名标签):按行等比分配高度铺满,字号自适应填充
|
|
|
|
|
// 左侧文字:固定字号 + 自适应行间距(与二维码上下对齐,行距均匀分配剩余空间)
|
|
|
|
|
const lx = 22.0;
|
|
|
|
|
const textMaxW = qrLeft - lx - 6; // 164
|
|
|
|
|
|
|
|
|
|
// 字号固定:只有商品名按宽度自适应,其余行均固定,杜绝自动放大
|
|
|
|
|
const double kShopSize = 17.0; // 酒行名,粗体
|
|
|
|
|
const double kSmallSize = 13.0; // 编号 / 度数 / 日期,统一小字
|
|
|
|
|
// 商品名:仅按宽度适配,上限 26px,下限 14px
|
|
|
|
|
final double nameSize = _fitFont(name, textMaxW, 1000, true, 26.0).clamp(14.0, 26.0);
|
|
|
|
|
|
|
|
|
|
final codeText = code.isNotEmpty ? '编号:$code' : '';
|
|
|
|
|
final rows = <(String, double, bool)>[
|
|
|
|
|
(shop, 0.9, true),
|
|
|
|
|
(name, 1.5, true),
|
|
|
|
|
if (codeText.isNotEmpty) (codeText, 1.0, false),
|
|
|
|
|
if (degSpec.isNotEmpty) (degSpec, 1.05, false),
|
|
|
|
|
if (date.isNotEmpty) (date, 1.0, false),
|
|
|
|
|
if (shop.isNotEmpty) (shop, kShopSize, true),
|
|
|
|
|
if (name.isNotEmpty) (name, nameSize, true),
|
|
|
|
|
if (codeText.isNotEmpty) (codeText, kSmallSize, false),
|
|
|
|
|
if (degSpec.isNotEmpty) (degSpec, kSmallSize, false),
|
|
|
|
|
if (date.isNotEmpty) (date, kSmallSize, false),
|
|
|
|
|
];
|
|
|
|
|
const topY = 4.0, botY = 156.0;
|
|
|
|
|
final totalWeight = rows.fold<double>(0, (a, r) => a + r.$2);
|
|
|
|
|
double yy = topY;
|
|
|
|
|
|
|
|
|
|
// 文字区域:整体略低于二维码顶部,底部延伸至扫码溯源文字下方
|
|
|
|
|
const double kTextTop = 16.0; // 比二维码顶部(8)低一些,整体下移
|
|
|
|
|
const double kTextBot = 150.0; // 延伸至底部,减少留白
|
|
|
|
|
const double kLineH = 1.35; // 行高倍数(字号 × 行高 = 该行占用高度)
|
|
|
|
|
final double totalTextH = rows.fold(0.0, (s, r) => s + r.$2 * kLineH);
|
|
|
|
|
final int n = rows.length;
|
|
|
|
|
final double rawGap = n > 0 ? (kTextBot - kTextTop - totalTextH) / (n + 1) : 4.0;
|
|
|
|
|
final double gap = rawGap < 1.5 ? 1.5 : rawGap;
|
|
|
|
|
|
|
|
|
|
double yy = kTextTop + gap;
|
|
|
|
|
for (final r in rows) {
|
|
|
|
|
final rowH = (botY - topY) * r.$2 / totalWeight;
|
|
|
|
|
final fs = _fitFont(r.$1, textMaxW, rowH * 0.84, r.$3, 30);
|
|
|
|
|
final ty = yy + (rowH - fs * 1.25) / 2;
|
|
|
|
|
_drawText(canvas, r.$1, lx, ty < yy ? yy : ty, fs, textMaxW, bold: r.$3);
|
|
|
|
|
yy += rowH;
|
|
|
|
|
_drawText(canvas, r.$1, lx, yy, r.$2, textMaxW, bold: r.$3);
|
|
|
|
|
yy += r.$2 * kLineH + gap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return recorder.endRecording().toImage(w, h);
|
|
|
|
@@ -327,7 +339,7 @@ Future<void> printProductLabelImpl({
|
|
|
|
|
final specVal = (spec ?? '').isNotEmpty ? spec! : '';
|
|
|
|
|
final seriesVal = (series ?? '').isNotEmpty ? series! : '';
|
|
|
|
|
// 度数(系列) + 规格 同一行
|
|
|
|
|
final degSpecVal = [seriesVal, specVal].where((s) => s.isNotEmpty).join(' ');
|
|
|
|
|
final degSpecVal = [seriesVal, specVal].where((s) => s.isNotEmpty).join(' ');
|
|
|
|
|
final dateVal = (productionDate ?? '').isNotEmpty
|
|
|
|
|
? (productionDate!.length > 10 ? productionDate.substring(0, 10) : productionDate)
|
|
|
|
|
: '—';
|
|
|
|
@@ -484,7 +496,7 @@ Future<void> printProductLabelImpl({
|
|
|
|
|
Future<Uint8List> renderLabelPreviewImpl(LabelData label) async {
|
|
|
|
|
final specVal = (label.spec ?? '').isNotEmpty ? label.spec! : '';
|
|
|
|
|
final seriesVal = (label.series ?? '').isNotEmpty ? label.series! : '';
|
|
|
|
|
final degSpecVal = [seriesVal, specVal].where((s) => s.isNotEmpty).join(' ');
|
|
|
|
|
final degSpecVal = [seriesVal, specVal].where((s) => s.isNotEmpty).join(' ');
|
|
|
|
|
final dateVal = (label.productionDate ?? '').isNotEmpty
|
|
|
|
|
? (label.productionDate!.length > 10
|
|
|
|
|
? label.productionDate!.substring(0, 10)
|
|
|
|
|