fix: 修复打印中文乱码、XLS导入100条截断、Nginx图片路由

- 打印改为 Web 平台用 HTML+CSS 方案,避免 pdf 包中文字体 bug
- 新增 print_util.dart 条件导入层(web/stub),彻底隔离 platform channel
- XLS 导入不再依赖 DIMENSIONS 记录的 MaxRow,改为读到连续5空行停止
- Nginx /images/ 加 ^~ 前缀,防止 .jpg 正则 location 拦截

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-04-30 22:07:17 +08:00
parent 6fc6b52631
commit 50b139c97a
9 changed files with 131 additions and 35 deletions
+13
View File
@@ -0,0 +1,13 @@
import 'dart:typed_data';
import 'print_util_stub.dart'
if (dart.library.js_interop) 'print_util_web.dart';
/// 打印商品标签(二维码 + 名称 + 编号)。
/// Web:生成 HTML 弹出新窗口调用浏览器打印。
/// 非 Web:生成 PDF 用 printing 包调起原生打印。
Future<void> printProductLabel({
required Uint8List qrBytes,
required String name,
required String code,
String? spec,
}) => printProductLabelImpl(qrBytes: qrBytes, name: name, code: code, spec: spec);
@@ -0,0 +1,38 @@
import 'package:flutter/services.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
Future<void> printProductLabelImpl({
required Uint8List qrBytes,
required String name,
required String code,
String? spec,
}) async {
final fontData = await rootBundle.load('assets/fonts/NotoSansSC-Regular.ttf');
final font = pw.Font.ttf(fontData);
final doc = pw.Document();
final qrImage = pw.MemoryImage(qrBytes);
doc.addPage(pw.Page(
pageFormat: PdfPageFormat.a4,
build: (_) => pw.Center(
child: pw.Column(
mainAxisSize: pw.MainAxisSize.min,
children: [
pw.Image(qrImage, width: 200, height: 200),
pw.SizedBox(height: 12),
pw.Text(name,
style: pw.TextStyle(
font: font, fontSize: 18, fontWeight: pw.FontWeight.bold)),
pw.SizedBox(height: 4),
pw.Text('编号:$code',
style: pw.TextStyle(font: font, fontSize: 13)),
if ((spec ?? '').isNotEmpty)
pw.Text('规格:$spec',
style: pw.TextStyle(font: font, fontSize: 13)),
],
),
),
));
await Printing.layoutPdf(onLayout: (_) async => doc.save());
}
+46
View File
@@ -0,0 +1,46 @@
import 'dart:convert';
import 'dart:js_interop';
import 'dart:typed_data';
import 'package:web/web.dart' as web;
Future<void> printProductLabelImpl({
required Uint8List qrBytes,
required String name,
required String code,
String? spec,
}) async {
final base64Img = base64Encode(qrBytes);
final specHtml = (spec != null && spec.isNotEmpty)
? '<p style="margin:4px 0;font-size:13px;color:#555;">规格:$spec</p>'
: '';
final html = '''<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
*{box-sizing:border-box;margin:0;padding:0;}
body{display:flex;justify-content:center;align-items:center;min-height:100vh;
font-family:"PingFang SC","Microsoft YaHei",sans-serif;}
.label{text-align:center;padding:24px;}
img{width:200px;height:200px;display:block;margin:0 auto;}
h2{margin:12px 0 4px;font-size:18px;font-weight:bold;}
p{margin:4px 0;font-size:13px;color:#444;}
</style>
</head>
<body>
<div class="label">
<img src="data:image/png;base64,$base64Img"/>
<h2>$name</h2>
<p>编号:$code</p>
$specHtml
</div>
<script>window.onload=function(){window.print();};</script>
</body>
</html>''';
final win = web.window.open('', '_blank');
if (win != null) {
win.document.write(html.toJS);
win.document.close();
}
}