@jcyao/print-sdk 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +190 -20
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +196 -17
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +197 -16
- package/dist/index.js.map +1 -1
- package/dist/printEngine/renderers/PageNumberRenderer.d.ts +22 -0
- package/dist/printEngine/renderers/index.d.ts +1 -0
- package/dist/printEngine.d.ts +11 -0
- package/dist/sdk.d.ts +2 -2
- package/dist/types.d.ts +25 -0
- package/dist/utils/resourceLoader.d.ts +19 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
|
|
6
6
|
通用打印 SDK - 客户端打印解决方案
|
|
7
7
|
|
|
8
|
+
**当前版本**: v1.0.1
|
|
9
|
+
|
|
10
|
+
## 🆕 v1.0.1 新增功能
|
|
11
|
+
|
|
12
|
+
- ⭐ **页码功能**:支持6种位置、3种格式、自定义样式(pageConfig.pageNumber)
|
|
13
|
+
- ⭐ **批量打印预览**:支持多份文档一次性预览和打印
|
|
14
|
+
- ⭐ **PageNumberRenderer**:新增页码渲染器插件
|
|
15
|
+
|
|
8
16
|
## ✨ 特性
|
|
9
17
|
|
|
10
18
|
- 🎨 **可视化模板设计** - 拖拽式设计打印模板
|
|
@@ -24,13 +32,13 @@ npm install @jcyao/print-sdk
|
|
|
24
32
|
## 🚀 快速开始
|
|
25
33
|
|
|
26
34
|
```typescript
|
|
27
|
-
import {
|
|
35
|
+
import { createPrintSDK } from '@jcyao/print-sdk';
|
|
28
36
|
|
|
29
|
-
//
|
|
30
|
-
|
|
37
|
+
// 创建 SDK 实例(无需配置)
|
|
38
|
+
const sdk = createPrintSDK();
|
|
31
39
|
|
|
32
40
|
// 打印
|
|
33
|
-
print({
|
|
41
|
+
sdk.print({
|
|
34
42
|
template: {
|
|
35
43
|
pageConfig: {
|
|
36
44
|
size: 'A4',
|
|
@@ -71,14 +79,6 @@ print({
|
|
|
71
79
|
|
|
72
80
|
## 📖 API 文档
|
|
73
81
|
|
|
74
|
-
### `init()`
|
|
75
|
-
|
|
76
|
-
初始化打印 SDK,全局执行一次。
|
|
77
|
-
|
|
78
|
-
```typescript
|
|
79
|
-
init();
|
|
80
|
-
```
|
|
81
|
-
|
|
82
82
|
### `print(options: PrintOptions)`
|
|
83
83
|
|
|
84
84
|
执行打印操作。
|
|
@@ -92,14 +92,31 @@ interface PrintOptions {
|
|
|
92
92
|
}
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
-
### `
|
|
95
|
+
### `sdk.generateHTML(template, data)`
|
|
96
96
|
|
|
97
|
-
生成预览 HTML
|
|
97
|
+
生成预览 HTML(不执行打印)。
|
|
98
98
|
|
|
99
99
|
```typescript
|
|
100
|
-
const html =
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
const html = await sdk.generateHTML(myTemplate, myData);
|
|
101
|
+
console.log(html); // 完整的 HTML 字符串
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### `sdk.printMultiple(template, dataList, options)`
|
|
105
|
+
|
|
106
|
+
批量打印(同模板多数据)。
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
const dataList = [
|
|
110
|
+
{ orderNo: 'ORDER001', ... },
|
|
111
|
+
{ orderNo: 'ORDER002', ... },
|
|
112
|
+
{ orderNo: 'ORDER003', ... },
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
await sdk.printMultiple(myTemplate, dataList, {
|
|
116
|
+
preview: true, // 预览所有
|
|
117
|
+
onProgress: (progress) => {
|
|
118
|
+
console.log(`进度: ${progress.completed}/${progress.total}`);
|
|
119
|
+
}
|
|
103
120
|
});
|
|
104
121
|
```
|
|
105
122
|
|
|
@@ -111,8 +128,7 @@ const html = preview({
|
|
|
111
128
|
- **二维码组件** - 自动生成二维码
|
|
112
129
|
- **条形码组件** - 多种条形码格式
|
|
113
130
|
- **线条组件** - 实线/虚线装饰
|
|
114
|
-
- **矩形组件** -
|
|
115
|
-
- **页码组件** - 自动分页页码
|
|
131
|
+
- **矩形组件** - 边框装饰、背景色块
|
|
116
132
|
|
|
117
133
|
## 🔄 数据管道
|
|
118
134
|
|
|
@@ -184,19 +200,173 @@ const html = preview({
|
|
|
184
200
|
}
|
|
185
201
|
```
|
|
186
202
|
|
|
203
|
+
## 🔢 页码功能配置 ⭐ **v1.0.1 新增**
|
|
204
|
+
|
|
205
|
+
页码功能通过页面配置实现,而非作为组件添加:
|
|
206
|
+
|
|
207
|
+
### 基础配置
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
{
|
|
211
|
+
pageConfig: {
|
|
212
|
+
size: 'A4',
|
|
213
|
+
orientation: 'portrait',
|
|
214
|
+
marginMm: { top: 10, right: 10, bottom: 10, left: 10 },
|
|
215
|
+
|
|
216
|
+
// 页码配置
|
|
217
|
+
pageNumber: {
|
|
218
|
+
enabled: true,
|
|
219
|
+
position: 'bottom-center', // 6种位置
|
|
220
|
+
format: 'slash', // 3种格式
|
|
221
|
+
offsetX: 0, // 横向偏移 (mm)
|
|
222
|
+
offsetY: 0, // 纵向偏移 (mm)
|
|
223
|
+
prefix: '', // 前缀
|
|
224
|
+
suffix: '', // 后缀
|
|
225
|
+
style: {
|
|
226
|
+
fontSize: 12,
|
|
227
|
+
color: '#666',
|
|
228
|
+
fontWeight: 'normal'
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
components: [...]
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### 位置选项 (position)
|
|
237
|
+
|
|
238
|
+
- `top-left` - 左上角
|
|
239
|
+
- `top-center` - 上中
|
|
240
|
+
- `top-right` - 右上角
|
|
241
|
+
- `bottom-left` - 左下角
|
|
242
|
+
- `bottom-center` - 下中 (默认)
|
|
243
|
+
- `bottom-right` - 右下角
|
|
244
|
+
|
|
245
|
+
### 格式选项 (format)
|
|
246
|
+
|
|
247
|
+
1. **`simple`** - 简单格式
|
|
248
|
+
```
|
|
249
|
+
显示:1 2 3
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
2. **`slash`** - 斜线格式 (默认)
|
|
253
|
+
```
|
|
254
|
+
显示:1/3 2/3 3/3
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
3. **`text`** - 文字格式
|
|
258
|
+
```
|
|
259
|
+
显示:第1页 共3页 第2页 共3页
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### 完整示例
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
const template = {
|
|
266
|
+
pageConfig: {
|
|
267
|
+
size: 'A4',
|
|
268
|
+
orientation: 'portrait',
|
|
269
|
+
marginMm: { top: 10, right: 10, bottom: 10, left: 10 },
|
|
270
|
+
pageNumber: {
|
|
271
|
+
enabled: true,
|
|
272
|
+
position: 'bottom-right',
|
|
273
|
+
format: 'text',
|
|
274
|
+
offsetX: -5, // 向左偏移 5mm
|
|
275
|
+
offsetY: -3, // 向上偏移 3mm
|
|
276
|
+
prefix: '页码:',
|
|
277
|
+
suffix: '',
|
|
278
|
+
style: {
|
|
279
|
+
fontSize: 10,
|
|
280
|
+
color: '#999',
|
|
281
|
+
fontWeight: 'bold'
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
components: [...]
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
await sdk.print({ template, data });
|
|
289
|
+
// 打印输出:右下角显示 "页码:第1页 共3页"
|
|
290
|
+
```
|
|
291
|
+
|
|
187
292
|
## 🔧 类型定义
|
|
188
293
|
|
|
189
294
|
完整的 TypeScript 类型定义:
|
|
190
295
|
|
|
191
296
|
```typescript
|
|
192
297
|
import type {
|
|
193
|
-
|
|
298
|
+
PrintTemplate,
|
|
194
299
|
ComponentNode,
|
|
195
300
|
TableColumn,
|
|
196
301
|
PipeConfig
|
|
197
302
|
} from '@jcyao/print-sdk';
|
|
198
303
|
```
|
|
199
304
|
|
|
305
|
+
## 💻 使用示例
|
|
306
|
+
|
|
307
|
+
### 示例 1:基本打印
|
|
308
|
+
|
|
309
|
+
```typescript
|
|
310
|
+
import { createPrintSDK } from '@jcyao/print-sdk';
|
|
311
|
+
|
|
312
|
+
const sdk = createPrintSDK();
|
|
313
|
+
|
|
314
|
+
// 准备模板和数据
|
|
315
|
+
const template = {
|
|
316
|
+
pageConfig: {
|
|
317
|
+
size: 'A4',
|
|
318
|
+
orientation: 'portrait',
|
|
319
|
+
marginMm: { top: 10, right: 10, bottom: 10, left: 10 }
|
|
320
|
+
},
|
|
321
|
+
components: [
|
|
322
|
+
{
|
|
323
|
+
id: 'text-1',
|
|
324
|
+
type: 'text',
|
|
325
|
+
layout: { xMm: 20, yMm: 20, widthMm: 170, heightMm: 10 },
|
|
326
|
+
binding: { path: 'orderNo' },
|
|
327
|
+
props: { label: '订单号:' }
|
|
328
|
+
}
|
|
329
|
+
]
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
const data = { orderNo: 'SR202401', customerName: '张三' };
|
|
333
|
+
|
|
334
|
+
// 直接打印
|
|
335
|
+
await sdk.printDirect(template, data);
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### 示例 2:预览后打印
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
import { createPrintSDK } from '@jcyao/print-sdk';
|
|
342
|
+
|
|
343
|
+
const sdk = createPrintSDK();
|
|
344
|
+
|
|
345
|
+
// 预览后打印
|
|
346
|
+
await sdk.printWithPreview(template, data);
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### 示例 3:批量打印
|
|
350
|
+
|
|
351
|
+
```typescript
|
|
352
|
+
import { createPrintSDK } from '@jcyao/print-sdk';
|
|
353
|
+
|
|
354
|
+
const sdk = createPrintSDK();
|
|
355
|
+
|
|
356
|
+
const orders = [
|
|
357
|
+
{ orderNo: 'ORDER001', amount: 1000 },
|
|
358
|
+
{ orderNo: 'ORDER002', amount: 2000 },
|
|
359
|
+
{ orderNo: 'ORDER003', amount: 3000 },
|
|
360
|
+
];
|
|
361
|
+
|
|
362
|
+
await sdk.printMultiple(template, orders, {
|
|
363
|
+
preview: true,
|
|
364
|
+
onProgress: (progress) => {
|
|
365
|
+
console.log(`打印进度: ${progress.completed}/${progress.total}`);
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
```
|
|
369
|
+
|
|
200
370
|
## 📝 License
|
|
201
371
|
|
|
202
372
|
MIT © joke_yao
|
package/dist/index.d.ts
CHANGED
package/dist/index.esm.js
CHANGED
|
@@ -546,7 +546,7 @@ function buildPositionStyle(xMm, yMm, widthMm, heightMm, mmToPx = 3.78) {
|
|
|
546
546
|
styles.width = `${widthMm * mmToPx}px`;
|
|
547
547
|
}
|
|
548
548
|
if (heightMm !== undefined) {
|
|
549
|
-
styles.
|
|
549
|
+
styles.height = `${heightMm * mmToPx}px`;
|
|
550
550
|
}
|
|
551
551
|
return styles;
|
|
552
552
|
}
|
|
@@ -820,13 +820,13 @@ class ImageRenderer {
|
|
|
820
820
|
const imageSrc = value || (props === null || props === void 0 ? void 0 : props.src) || '';
|
|
821
821
|
// 容器定位样式
|
|
822
822
|
const positionStyles = buildPositionStyle(layout.xMm || 0, layout.yMm || 0, layout.widthMm || COMPONENT_DEFAULT_SIZE.IMAGE_WIDTH, layout.heightMm || COMPONENT_DEFAULT_SIZE.IMAGE_HEIGHT, context.mmToPx);
|
|
823
|
-
const containerStyles = Object.assign(Object.assign({}, positionStyles), {
|
|
823
|
+
const containerStyles = Object.assign(Object.assign({}, positionStyles), { overflow: 'hidden' });
|
|
824
824
|
const containerStyleStr = buildStyleString(containerStyles);
|
|
825
|
-
//
|
|
825
|
+
// 图片样式:严格按模板尺寸渲染,避免自适应导致排版错乱
|
|
826
826
|
const imageStyle = `
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
object-fit: ${(style === null || style === void 0 ? void 0 : style.objectFit) || '
|
|
827
|
+
width: 100%;
|
|
828
|
+
height: 100%;
|
|
829
|
+
object-fit: ${(style === null || style === void 0 ? void 0 : style.objectFit) || 'fill'};
|
|
830
830
|
`;
|
|
831
831
|
if (imageSrc) {
|
|
832
832
|
return `<div style="${containerStyleStr}"><img src="${imageSrc}" style="${imageStyle}" alt="" /></div>`;
|
|
@@ -1197,14 +1197,108 @@ class PrintEngine {
|
|
|
1197
1197
|
// 使用 MIN_ROW_HEIGHT * ROW_HEIGHT_FACTOR 作为行高
|
|
1198
1198
|
return TABLE_DEFAULT.MIN_ROW_HEIGHT * TABLE_DEFAULT.ROW_HEIGHT_FACTOR;
|
|
1199
1199
|
}
|
|
1200
|
+
/**
|
|
1201
|
+
* 渲染页码(根据页面配置)
|
|
1202
|
+
*/
|
|
1203
|
+
renderPageNumber(pageNumber, totalPages) {
|
|
1204
|
+
var _a, _b, _c, _d;
|
|
1205
|
+
const { page } = this.template;
|
|
1206
|
+
const pageNumberConfig = page.pageNumber;
|
|
1207
|
+
// 如果未启用页码或缺少页码信息,返回空
|
|
1208
|
+
if (!(pageNumberConfig === null || pageNumberConfig === void 0 ? void 0 : pageNumberConfig.enabled) || pageNumber === undefined || totalPages === undefined) {
|
|
1209
|
+
return '';
|
|
1210
|
+
}
|
|
1211
|
+
console.log(`[PrintEngine] 渲染页码: pageNumber=${pageNumber}, totalPages=${totalPages}`, pageNumberConfig);
|
|
1212
|
+
// 格式化页码文本
|
|
1213
|
+
const format = pageNumberConfig.format || 'slash';
|
|
1214
|
+
const prefix = pageNumberConfig.prefix || '';
|
|
1215
|
+
const suffix = pageNumberConfig.suffix || '';
|
|
1216
|
+
const separator = pageNumberConfig.separator || '/';
|
|
1217
|
+
let pageText = '';
|
|
1218
|
+
if (format === 'simple') {
|
|
1219
|
+
pageText = `${pageNumber}`;
|
|
1220
|
+
}
|
|
1221
|
+
else if (format === 'text') {
|
|
1222
|
+
pageText = `第${pageNumber}页 共${totalPages}页`;
|
|
1223
|
+
}
|
|
1224
|
+
else {
|
|
1225
|
+
pageText = `${pageNumber}${separator}${totalPages}`;
|
|
1226
|
+
}
|
|
1227
|
+
pageText = `${prefix}${pageText}${suffix}`;
|
|
1228
|
+
// 计算位置
|
|
1229
|
+
const { widthMm, heightMm } = this.getPageSize();
|
|
1230
|
+
const { position, offsetX = 0, offsetY = 0, style = {} } = pageNumberConfig;
|
|
1231
|
+
const fontSize = style.fontSize || 12;
|
|
1232
|
+
const color = style.color || '#666';
|
|
1233
|
+
const fontWeight = style.fontWeight || 'normal';
|
|
1234
|
+
// 根据 position 计算 x, y 坐标
|
|
1235
|
+
let xMm = 0;
|
|
1236
|
+
let yMm = 0;
|
|
1237
|
+
const pageNumberWidth = 20; // 页码宽度 mm
|
|
1238
|
+
const pageNumberHeight = 6; // 页码高度 mm
|
|
1239
|
+
const marginTop = ((_a = page.marginMm) === null || _a === void 0 ? void 0 : _a.top) || 0;
|
|
1240
|
+
const marginRight = ((_b = page.marginMm) === null || _b === void 0 ? void 0 : _b.right) || 0;
|
|
1241
|
+
const marginBottom = ((_c = page.marginMm) === null || _c === void 0 ? void 0 : _c.bottom) || 0;
|
|
1242
|
+
const marginLeft = ((_d = page.marginMm) === null || _d === void 0 ? void 0 : _d.left) || 0;
|
|
1243
|
+
switch (position) {
|
|
1244
|
+
case 'top-left':
|
|
1245
|
+
xMm = marginLeft;
|
|
1246
|
+
yMm = marginTop;
|
|
1247
|
+
break;
|
|
1248
|
+
case 'top-center':
|
|
1249
|
+
xMm = (widthMm - pageNumberWidth) / 2;
|
|
1250
|
+
yMm = marginTop;
|
|
1251
|
+
break;
|
|
1252
|
+
case 'top-right':
|
|
1253
|
+
xMm = widthMm - marginRight - pageNumberWidth;
|
|
1254
|
+
yMm = marginTop;
|
|
1255
|
+
break;
|
|
1256
|
+
case 'bottom-left':
|
|
1257
|
+
xMm = marginLeft;
|
|
1258
|
+
yMm = heightMm - marginBottom - pageNumberHeight;
|
|
1259
|
+
break;
|
|
1260
|
+
case 'bottom-center':
|
|
1261
|
+
xMm = (widthMm - pageNumberWidth) / 2;
|
|
1262
|
+
yMm = heightMm - marginBottom - pageNumberHeight;
|
|
1263
|
+
break;
|
|
1264
|
+
case 'bottom-right':
|
|
1265
|
+
default:
|
|
1266
|
+
xMm = widthMm - marginRight - pageNumberWidth;
|
|
1267
|
+
yMm = heightMm - marginBottom - pageNumberHeight;
|
|
1268
|
+
break;
|
|
1269
|
+
}
|
|
1270
|
+
// 应用偏移
|
|
1271
|
+
xMm += offsetX;
|
|
1272
|
+
yMm += offsetY;
|
|
1273
|
+
// 转换为 px
|
|
1274
|
+
const xPx = xMm * this.mmToPx;
|
|
1275
|
+
const yPx = yMm * this.mmToPx;
|
|
1276
|
+
const widthPx = pageNumberWidth * this.mmToPx;
|
|
1277
|
+
const heightPx = pageNumberHeight * this.mmToPx;
|
|
1278
|
+
// 生成 HTML
|
|
1279
|
+
const alignStyle = position.includes('left') ? 'left' : position.includes('right') ? 'right' : 'center';
|
|
1280
|
+
const justifyContent = alignStyle === 'left' ? 'flex-start' : alignStyle === 'right' ? 'flex-end' : 'center';
|
|
1281
|
+
return `<div style="position: absolute; left: ${xPx}px; top: ${yPx}px; width: ${widthPx}px; height: ${heightPx}px; font-size: ${fontSize}px; color: ${color}; font-weight: ${fontWeight}; display: flex; align-items: center; justify-content: ${justifyContent};">${this.escapeHtml(pageText)}</div>`;
|
|
1282
|
+
}
|
|
1283
|
+
/**
|
|
1284
|
+
* HTML 转义
|
|
1285
|
+
*/
|
|
1286
|
+
escapeHtml(text) {
|
|
1287
|
+
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''');
|
|
1288
|
+
}
|
|
1200
1289
|
/**
|
|
1201
1290
|
* 渲染单个页面(直接渲染,不做智能布局)
|
|
1291
|
+
* @param components 组件列表
|
|
1292
|
+
* @param pageNumber 当前页码(可选)
|
|
1293
|
+
* @param totalPages 总页数(可选)
|
|
1202
1294
|
*/
|
|
1203
|
-
renderSinglePage(components) {
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1295
|
+
renderSinglePage(components, pageNumber, totalPages) {
|
|
1296
|
+
console.log(`[PrintEngine] renderSinglePage: 页码=${pageNumber}, 总页数=${totalPages}, 组件数=${components.length}`);
|
|
1297
|
+
// 渲染所有组件
|
|
1298
|
+
const componentsHTML = components.map(comp => this.renderComponent(comp)).join('');
|
|
1299
|
+
// 如果页面配置启用了页码,在固定位置渲染页码
|
|
1300
|
+
const pageNumberHTML = this.renderPageNumber(pageNumber, totalPages);
|
|
1301
|
+
return componentsHTML + pageNumberHTML;
|
|
1208
1302
|
}
|
|
1209
1303
|
/**
|
|
1210
1304
|
* 虚拟分页:基于相对间距的流式布局
|
|
@@ -1437,10 +1531,12 @@ class PrintEngine {
|
|
|
1437
1531
|
}
|
|
1438
1532
|
// 标准页面模式:虚拟分页,生成多个独立的页面
|
|
1439
1533
|
const pages = this.calculatePages(components);
|
|
1534
|
+
const totalPages = pages.length;
|
|
1440
1535
|
// 渲染每个页面
|
|
1441
1536
|
const pagesHTML = pages.map((pageComponents, index) => {
|
|
1442
|
-
const
|
|
1443
|
-
|
|
1537
|
+
const pageNumber = index + 1;
|
|
1538
|
+
const pageContent = this.renderSinglePage(pageComponents, pageNumber, totalPages);
|
|
1539
|
+
return `<div class="print-page" data-page="${pageNumber}">${pageContent}</div>`;
|
|
1444
1540
|
}).join('');
|
|
1445
1541
|
const styles = generatePrintPageStyles({
|
|
1446
1542
|
pageWidthMm: widthMm,
|
|
@@ -1485,6 +1581,83 @@ function createPrintEngine(template, data) {
|
|
|
1485
1581
|
};
|
|
1486
1582
|
}
|
|
1487
1583
|
|
|
1584
|
+
/**
|
|
1585
|
+
* 资源加载工具
|
|
1586
|
+
* 用于等待图片、二维码、条形码等异步资源加载完成
|
|
1587
|
+
*/
|
|
1588
|
+
/**
|
|
1589
|
+
* 等待文档中所有图片加载完成
|
|
1590
|
+
* @param doc 文档对象(可以是 window.document 或 iframe.contentDocument)
|
|
1591
|
+
* @param timeout 超时时间(毫秒),默认 10000ms
|
|
1592
|
+
* @returns Promise<void>
|
|
1593
|
+
*/
|
|
1594
|
+
async function waitForImagesLoaded(doc, timeout = 10000) {
|
|
1595
|
+
const images = Array.from(doc.querySelectorAll('img'));
|
|
1596
|
+
if (images.length === 0) {
|
|
1597
|
+
// 没有图片,直接返回
|
|
1598
|
+
return Promise.resolve();
|
|
1599
|
+
}
|
|
1600
|
+
return new Promise((resolve, reject) => {
|
|
1601
|
+
let loadedCount = 0;
|
|
1602
|
+
let errorCount = 0;
|
|
1603
|
+
const totalImages = images.length;
|
|
1604
|
+
// 超时处理
|
|
1605
|
+
const timeoutId = setTimeout(() => {
|
|
1606
|
+
console.warn(`图片加载超时,已加载 ${loadedCount}/${totalImages},失败 ${errorCount} 张`);
|
|
1607
|
+
resolve(); // 超时也算完成,避免无限等待
|
|
1608
|
+
}, timeout);
|
|
1609
|
+
// 检查是否所有图片都已完成
|
|
1610
|
+
const checkComplete = () => {
|
|
1611
|
+
if (loadedCount + errorCount >= totalImages) {
|
|
1612
|
+
clearTimeout(timeoutId);
|
|
1613
|
+
if (errorCount > 0) {
|
|
1614
|
+
console.warn(`有 ${errorCount} 张图片加载失败`);
|
|
1615
|
+
}
|
|
1616
|
+
resolve();
|
|
1617
|
+
}
|
|
1618
|
+
};
|
|
1619
|
+
// 为每个图片添加加载监听
|
|
1620
|
+
images.forEach((img) => {
|
|
1621
|
+
// 如果图片已经加载完成(cached)
|
|
1622
|
+
if (img.complete) {
|
|
1623
|
+
if (img.naturalHeight !== 0) {
|
|
1624
|
+
loadedCount++;
|
|
1625
|
+
}
|
|
1626
|
+
else {
|
|
1627
|
+
errorCount++;
|
|
1628
|
+
}
|
|
1629
|
+
checkComplete();
|
|
1630
|
+
}
|
|
1631
|
+
else {
|
|
1632
|
+
// 监听 load 和 error 事件
|
|
1633
|
+
img.addEventListener('load', () => {
|
|
1634
|
+
loadedCount++;
|
|
1635
|
+
checkComplete();
|
|
1636
|
+
});
|
|
1637
|
+
img.addEventListener('error', (e) => {
|
|
1638
|
+
errorCount++;
|
|
1639
|
+
console.error('图片加载失败:', img.src, e);
|
|
1640
|
+
checkComplete();
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
});
|
|
1644
|
+
// 立即检查一次(可能所有图片都已缓存)
|
|
1645
|
+
checkComplete();
|
|
1646
|
+
});
|
|
1647
|
+
}
|
|
1648
|
+
/**
|
|
1649
|
+
* 等待所有打印资源加载完成
|
|
1650
|
+
* 包括:图片、二维码(已转base64)、条形码(已转base64)
|
|
1651
|
+
* 注意:二维码和条形码在渲染时已同步生成为 base64,所以主要等待外部图片
|
|
1652
|
+
* @param doc 文档对象
|
|
1653
|
+
* @param timeout 超时时间(毫秒),默认 10000ms
|
|
1654
|
+
*/
|
|
1655
|
+
async function waitForPrintResourcesReady(doc, timeout = 10000) {
|
|
1656
|
+
// 目前只需要等待图片加载
|
|
1657
|
+
// 二维码和条形码在 QRCodeRenderer 和 BarcodeRenderer 中已同步生成为 base64
|
|
1658
|
+
return waitForImagesLoaded(doc, timeout);
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1488
1661
|
/**
|
|
1489
1662
|
* 打印 SDK 核心类
|
|
1490
1663
|
* 提供完整的打印功能封装
|
|
@@ -1512,6 +1685,8 @@ class PrintSDK {
|
|
|
1512
1685
|
const html = engine.generatePrintHTML();
|
|
1513
1686
|
printWindow.document.write(html);
|
|
1514
1687
|
printWindow.document.close();
|
|
1688
|
+
// 等待所有图片加载完成后再打印
|
|
1689
|
+
await waitForImagesLoaded(printWindow.document);
|
|
1515
1690
|
printWindow.print();
|
|
1516
1691
|
}
|
|
1517
1692
|
else {
|
|
@@ -1528,7 +1703,9 @@ class PrintSDK {
|
|
|
1528
1703
|
}
|
|
1529
1704
|
iframeDoc.write(html);
|
|
1530
1705
|
iframeDoc.close();
|
|
1531
|
-
//
|
|
1706
|
+
// 等待所有图片加载完成后再打印
|
|
1707
|
+
// 注意:二维码和条形码已同步生成为base64,主要等待外部图片资源
|
|
1708
|
+
await waitForImagesLoaded(iframeDoc);
|
|
1532
1709
|
(_b = iframe.contentWindow) === null || _b === void 0 ? void 0 : _b.print();
|
|
1533
1710
|
// 打印完成后移除 iframe
|
|
1534
1711
|
setTimeout(() => {
|
|
@@ -1631,7 +1808,8 @@ class PrintSDK {
|
|
|
1631
1808
|
}
|
|
1632
1809
|
printWindow.document.write(fullHTML);
|
|
1633
1810
|
printWindow.document.close();
|
|
1634
|
-
//
|
|
1811
|
+
// 等待所有图片加载完成后再打印
|
|
1812
|
+
await waitForImagesLoaded(printWindow.document);
|
|
1635
1813
|
printWindow.print();
|
|
1636
1814
|
}
|
|
1637
1815
|
else {
|
|
@@ -1647,7 +1825,8 @@ class PrintSDK {
|
|
|
1647
1825
|
}
|
|
1648
1826
|
iframeDoc.write(fullHTML);
|
|
1649
1827
|
iframeDoc.close();
|
|
1650
|
-
//
|
|
1828
|
+
// 等待所有图片加载完成后再打印
|
|
1829
|
+
await waitForImagesLoaded(iframeDoc);
|
|
1651
1830
|
(_b = iframe.contentWindow) === null || _b === void 0 ? void 0 : _b.print();
|
|
1652
1831
|
// 打印完成后移除 iframe
|
|
1653
1832
|
setTimeout(() => {
|
|
@@ -1664,5 +1843,5 @@ function createPrintSDK() {
|
|
|
1664
1843
|
return new PrintSDK();
|
|
1665
1844
|
}
|
|
1666
1845
|
|
|
1667
|
-
export { BARCODE_CONFIG, BarcodeRenderer, COMPONENT_DEFAULT_SIZE, CurrencyPipe, DatePipe, DefaultPipe, ImageRenderer, LineRenderer, LowercasePipe, MM_TO_PX, MoneyPipe, PrintEngine, PrintSDK, QRCODE_CONFIG, QRCodeRenderer, RectRenderer, STYLE_DEFAULT, SlicePipe, TABLE_DEFAULT, TABLE_STYLE_DEFAULT, TableRenderer, TextRenderer, UppercasePipe, createPrintEngine, createPrintSDK, executePipe, generateBatchPrintStyles, generatePrintHTML, generatePrintPageStyles, getAllPipes, getExecutor, getPageSizeFromConfig, registerExecutor };
|
|
1846
|
+
export { BARCODE_CONFIG, BarcodeRenderer, COMPONENT_DEFAULT_SIZE, CurrencyPipe, DatePipe, DefaultPipe, ImageRenderer, LineRenderer, LowercasePipe, MM_TO_PX, MoneyPipe, PrintEngine, PrintSDK, QRCODE_CONFIG, QRCodeRenderer, RectRenderer, STYLE_DEFAULT, SlicePipe, TABLE_DEFAULT, TABLE_STYLE_DEFAULT, TableRenderer, TextRenderer, UppercasePipe, createPrintEngine, createPrintSDK, executePipe, generateBatchPrintStyles, generatePrintHTML, generatePrintPageStyles, getAllPipes, getExecutor, getPageSizeFromConfig, registerExecutor, waitForImagesLoaded, waitForPrintResourcesReady };
|
|
1668
1847
|
//# sourceMappingURL=index.esm.js.map
|