@jcyao/print-sdk 1.1.1 → 1.1.3

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/CHANGELOG.md CHANGED
@@ -2,6 +2,45 @@
2
2
 
3
3
  所有版本的变更记录都列在这里,遵循 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/) 规范。
4
4
 
5
+ ## [1.1.2] - 2026-05-07
6
+
7
+ ### 🐛 问题修复
8
+
9
+ | 问题 | 修复内容 |
10
+ |------|----------|
11
+ | 打印内容偏左 | `@media print` 中 `.print-page` 的 `margin` 未完全覆盖,导致 `auto` margin 在打印时产生偏移 |
12
+ | 批量打印无边距 | `generateBatchPrintStyles` 未设置 `.print-page` 的 `padding`,导致边距功能失效 |
13
+
14
+ ### ✨ 新增功能
15
+
16
+ - **多模板批量打印**:新增 `printMultiTemplate` 方法,支持一次打印操作中组合多个不同模板及各自对应的数据列表(一客一模板场景)
17
+
18
+ ### 🔧 修复详情
19
+
20
+ #### 1. 打印样式 `@media print` 增强
21
+
22
+ - `body` 样式增加 `!important` 标记,确保打印时完全重置 `margin`、`padding`、`background`
23
+ - `.print-page` 的 `margin-bottom: 0 !important` 改为 `margin: 0 !important`,完全覆盖所有方向的 `auto` margin
24
+ - `.print-page` 的 `box-shadow` 增加 `!important` 标记
25
+
26
+ **影响范围**:`generatePrintPageStyles`、`generateBatchPrintStyles`
27
+
28
+ #### 2. 批量打印边距修复
29
+
30
+ - `generateBatchPrintStyles` 的 `.print-page` 基础样式增加 `padding: ${marginTop}mm ${marginRight}mm ${marginBottom}mm ${marginLeft}mm`
31
+ - 确保批量打印时页面边距功能正常生效
32
+
33
+ ---
34
+
35
+ ## [1.1.1] - 2026-01-22
36
+
37
+ ### ✨ 新增功能
38
+
39
+ - **表格嵌套对象数据支持**:表格打印支持嵌套对象格式的数据源(如 `{ a: { b: 1 } }`),数据绑定时可通过路径(如 `a.b`)正确解析
40
+ - **服务架构简化**:移除独立 server 服务,改用 Vite mock 集成,降低开发和部署复杂度
41
+
42
+ ---
43
+
5
44
  ## [1.1.0] - 2026-04-15
6
45
 
7
46
  ### 🎯 重大改进
package/README.md CHANGED
@@ -5,7 +5,17 @@
5
5
 
6
6
  通用打印 SDK - 客户端打印解决方案
7
7
 
8
- **当前版本**: v1.1.0
8
+ **当前版本**: v1.1.2
9
+
10
+ ## 🆕 v1.1.2 问题修复
11
+
12
+ - 🐛 **打印内容偏移修复**:`@media print` 中 `.print-page` 的 `margin` 完全覆盖,解决打印内容偏左问题
13
+ - 🐛 **批量打印边距修复**:`generateBatchPrintStyles` 增加 `padding` 设置,批量打印边距功能正常生效
14
+
15
+ ## 🆕 v1.1.1 新增功能
16
+
17
+ - ✨ **表格嵌套对象数据支持**:表格打印支持嵌套对象格式的数据源
18
+ - 🔧 **服务架构简化**:移除独立 server 服务,改用 Vite mock 集成
9
19
 
10
20
  ## 🆕 v1.1.0 重大改进
11
21
 
@@ -128,6 +138,50 @@ await sdk.printMultiple(myTemplate, dataList, {
128
138
  });
129
139
  ```
130
140
 
141
+ ### `sdk.printMultiTemplate(groups, options)`
142
+
143
+ 多模板批量打印(多模板 + 各自对应的数据列表)。
144
+
145
+ ```typescript
146
+ await sdk.printMultiTemplate([
147
+ { template: templateA, dataList: [dataA1, dataA2] },
148
+ { template: templateB, dataList: [dataB1] },
149
+ ], {
150
+ preview: true,
151
+ onProgress: (progress) => {
152
+ console.log(
153
+ `组: ${progress.completedGroups}/${progress.totalGroups}, 数据: ${progress.completedDataItems}/${progress.totalDataItems}`
154
+ );
155
+ }
156
+ });
157
+ ```
158
+
159
+ **参数:**
160
+
161
+ ```typescript
162
+ interface PrintTemplateGroup {
163
+ template: PrintTemplate;
164
+ dataList: any[];
165
+ }
166
+
167
+ interface MultiTemplatePrintOptions {
168
+ preview?: boolean;
169
+ onProgress?: (progress: MultiTemplatePrintProgress) => void;
170
+ }
171
+
172
+ interface MultiTemplatePrintProgress {
173
+ totalGroups: number; // 模板组总数
174
+ completedGroups: number; // 已完成组数
175
+ totalDataItems: number; // 总数据条目
176
+ completedDataItems: number; // 已完成数据条目
177
+ failed: number; // 失败条目数
178
+ currentGroupIndex: number; // 当前处理组索引
179
+ currentDataIndex: number; // 当前处理数据索引
180
+ }
181
+ ```
182
+
183
+ > ⚠️ **已知限制**:所有模板必须使用相同的纸张尺寸和边距设置。混合纸张尺寸暂不支持。
184
+
131
185
  ## 🎨 支持的组件
132
186
 
133
187
  - **文本组件** - 显示文本内容,支持标签和数据绑定
@@ -28,6 +28,32 @@ export interface BatchPrintProgress {
28
28
  failed: number;
29
29
  currentIndex: number;
30
30
  }
31
+ /**
32
+ * 模板 + 数据组
33
+ */
34
+ export interface PrintTemplateGroup {
35
+ template: PrintTemplate;
36
+ dataList: any[];
37
+ }
38
+ /**
39
+ * 多模板打印选项
40
+ */
41
+ export interface MultiTemplatePrintOptions {
42
+ preview?: boolean;
43
+ onProgress?: (progress: MultiTemplatePrintProgress) => void;
44
+ }
45
+ /**
46
+ * 多模板打印进度
47
+ */
48
+ export interface MultiTemplatePrintProgress {
49
+ totalGroups: number;
50
+ completedGroups: number;
51
+ totalDataItems: number;
52
+ completedDataItems: number;
53
+ failed: number;
54
+ currentGroupIndex: number;
55
+ currentDataIndex: number;
56
+ }
31
57
  export declare class PrintSDK {
32
58
  /**
33
59
  * 打印
@@ -64,6 +90,15 @@ export declare class PrintSDK {
64
90
  * @param options 批量打印选项
65
91
  */
66
92
  printMultiple(template: PrintTemplate, dataList: any[], options?: BatchPrintOptions): Promise<void>;
93
+ /**
94
+ * 多模板批量打印
95
+ * 支持多个模板各自绑定数据列表,一次打印确认
96
+ *
97
+ * 注意:所有模板需使用相同纸张尺寸,混合尺寸暂不支持
98
+ * @param groups 模板+数据组列表
99
+ * @param options 打印选项
100
+ */
101
+ printMultiTemplate(groups: PrintTemplateGroup[], options?: MultiTemplatePrintOptions): Promise<void>;
67
102
  }
68
103
  /**
69
104
  * 创建 SDK 实例(无需配置)
package/dist/index.esm.js CHANGED
@@ -209,8 +209,9 @@ function generatePrintPageStyles(config) {
209
209
 
210
210
  @media print {
211
211
  body {
212
- background: white;
213
- padding: 0;
212
+ margin: 0 !important;
213
+ padding: 0 !important;
214
+ background: white !important;
214
215
  }
215
216
 
216
217
  @page {
@@ -219,9 +220,9 @@ function generatePrintPageStyles(config) {
219
220
  }
220
221
 
221
222
  .print-page {
223
+ margin: 0 !important;
224
+ box-shadow: none !important;
222
225
  page-break-after: always;
223
- margin-bottom: 0 !important;
224
- box-shadow: none; /* 移除阴影 */
225
226
  }
226
227
 
227
228
  .print-page:last-child {
@@ -248,7 +249,7 @@ function generatePrintPageStyles(config) {
248
249
  * 用于 PrintSDK 的 printMultiple
249
250
  */
250
251
  function generateBatchPrintStyles(config) {
251
- const { pageWidthMm, pageHeightMm, isContinuous = false, minHeightMm = 100, } = config;
252
+ const { pageWidthMm, pageHeightMm, marginTop = 0, marginRight = 0, marginBottom = 0, marginLeft = 0, isContinuous = false, minHeightMm = 100, } = config;
252
253
  return `
253
254
  * { margin: 0; padding: 0; box-sizing: border-box; }
254
255
  body { margin: 0; padding: 0; background: #f5f5f5; }
@@ -259,8 +260,8 @@ function generateBatchPrintStyles(config) {
259
260
  }
260
261
 
261
262
  @media print {
262
- body { margin: 0; padding: 0; background: white; }
263
- .print-page { margin: 0; page-break-after: always; box-shadow: none !important; }
263
+ body { margin: 0 !important; padding: 0 !important; background: white !important; }
264
+ .print-page { margin: 0 !important; page-break-after: always; box-shadow: none !important; }
264
265
  .print-page:last-child { page-break-after: auto; }
265
266
  }
266
267
 
@@ -271,7 +272,7 @@ function generateBatchPrintStyles(config) {
271
272
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
272
273
  }
273
274
  }
274
-
275
+
275
276
  .print-page {
276
277
  width: ${pageWidthMm}mm;
277
278
  height: ${isContinuous ? 'auto' : pageHeightMm + 'mm'};
@@ -279,6 +280,7 @@ function generateBatchPrintStyles(config) {
279
280
  background: white;
280
281
  position: relative;
281
282
  box-sizing: border-box;
283
+ padding: ${marginTop}mm ${marginRight}mm ${marginBottom}mm ${marginLeft}mm;
282
284
  }
283
285
 
284
286
  ${generateComponentStyles()}
@@ -1991,7 +1993,7 @@ class PrintSDK {
1991
1993
  * @param options 批量打印选项
1992
1994
  */
1993
1995
  async printMultiple(template, dataList, options = {}) {
1994
- var _a, _b;
1996
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
1995
1997
  const { preview = false, onProgress, } = options;
1996
1998
  const { page } = template;
1997
1999
  const progress = {
@@ -2034,6 +2036,10 @@ class PrintSDK {
2034
2036
  const styles = generateBatchPrintStyles({
2035
2037
  pageWidthMm,
2036
2038
  pageHeightMm,
2039
+ marginTop: (_b = (_a = page.marginMm) === null || _a === void 0 ? void 0 : _a.top) !== null && _b !== void 0 ? _b : 0,
2040
+ marginRight: (_d = (_c = page.marginMm) === null || _c === void 0 ? void 0 : _c.right) !== null && _d !== void 0 ? _d : 0,
2041
+ marginBottom: (_f = (_e = page.marginMm) === null || _e === void 0 ? void 0 : _e.bottom) !== null && _f !== void 0 ? _f : 0,
2042
+ marginLeft: (_h = (_g = page.marginMm) === null || _g === void 0 ? void 0 : _g.left) !== null && _h !== void 0 ? _h : 0,
2037
2043
  isContinuous: page.size === 'CONTINUOUS',
2038
2044
  minHeightMm: page.minHeightMm,
2039
2045
  });
@@ -2063,7 +2069,7 @@ class PrintSDK {
2063
2069
  iframe.style.top = '-9999px';
2064
2070
  iframe.style.left = '-9999px';
2065
2071
  document.body.appendChild(iframe);
2066
- const iframeDoc = (_a = iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.document;
2072
+ const iframeDoc = (_j = iframe.contentWindow) === null || _j === void 0 ? void 0 : _j.document;
2067
2073
  if (!iframeDoc) {
2068
2074
  throw new Error('Failed to access iframe document');
2069
2075
  }
@@ -2071,13 +2077,129 @@ class PrintSDK {
2071
2077
  iframeDoc.close();
2072
2078
  // 等待所有图片加载完成后再打印
2073
2079
  await waitForImagesLoaded(iframeDoc);
2074
- (_b = iframe.contentWindow) === null || _b === void 0 ? void 0 : _b.print();
2080
+ (_k = iframe.contentWindow) === null || _k === void 0 ? void 0 : _k.print();
2075
2081
  // 打印完成后移除 iframe
2076
2082
  setTimeout(() => {
2077
2083
  document.body.removeChild(iframe);
2078
2084
  }, 1000);
2079
2085
  }
2080
2086
  }
2087
+ /**
2088
+ * 多模板批量打印
2089
+ * 支持多个模板各自绑定数据列表,一次打印确认
2090
+ *
2091
+ * 注意:所有模板需使用相同纸张尺寸,混合尺寸暂不支持
2092
+ * @param groups 模板+数据组列表
2093
+ * @param options 打印选项
2094
+ */
2095
+ async printMultiTemplate(groups, options = {}) {
2096
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
2097
+ const { preview = false, onProgress } = options;
2098
+ if (!groups || groups.length === 0) {
2099
+ console.warn('[PrintSDK] printMultiTemplate: groups 为空,跳过打印');
2100
+ return;
2101
+ }
2102
+ const totalDataItems = groups.reduce((sum, g) => sum + g.dataList.length, 0);
2103
+ const progress = {
2104
+ totalGroups: groups.length,
2105
+ completedGroups: 0,
2106
+ totalDataItems,
2107
+ completedDataItems: 0,
2108
+ failed: 0,
2109
+ currentGroupIndex: 0,
2110
+ currentDataIndex: -1,
2111
+ };
2112
+ onProgress === null || onProgress === void 0 ? void 0 : onProgress(progress);
2113
+ const allPagesHTML = [];
2114
+ for (let groupIdx = 0; groupIdx < groups.length; groupIdx++) {
2115
+ const group = groups[groupIdx];
2116
+ progress.currentGroupIndex = groupIdx;
2117
+ progress.currentDataIndex = -1;
2118
+ onProgress === null || onProgress === void 0 ? void 0 : onProgress(progress);
2119
+ for (let dataIdx = 0; dataIdx < group.dataList.length; dataIdx++) {
2120
+ const data = group.dataList[dataIdx];
2121
+ progress.currentDataIndex = dataIdx;
2122
+ onProgress === null || onProgress === void 0 ? void 0 : onProgress(progress);
2123
+ try {
2124
+ const engine = createPrintEngine(group.template, data);
2125
+ const html = await engine.generatePrintHTML();
2126
+ const bodyContent = extractBodyContent(html);
2127
+ if (bodyContent) {
2128
+ allPagesHTML.push(bodyContent);
2129
+ }
2130
+ progress.completedDataItems++;
2131
+ onProgress === null || onProgress === void 0 ? void 0 : onProgress(progress);
2132
+ }
2133
+ catch (error) {
2134
+ progress.failed++;
2135
+ progress.completedDataItems++;
2136
+ onProgress === null || onProgress === void 0 ? void 0 : onProgress(progress);
2137
+ console.error(`[PrintSDK] 处理失败: groupIndex=${groupIdx}, dataIndex=${dataIdx}`, error);
2138
+ }
2139
+ }
2140
+ progress.completedGroups++;
2141
+ onProgress === null || onProgress === void 0 ? void 0 : onProgress(progress);
2142
+ }
2143
+ progress.currentGroupIndex = -1;
2144
+ progress.currentDataIndex = -1;
2145
+ onProgress === null || onProgress === void 0 ? void 0 : onProgress(progress);
2146
+ const { page } = groups[0].template;
2147
+ const { widthMm: pageWidthMm, heightMm: pageHeightMm } = getPageSizeFromConfig(page);
2148
+ const styles = generateBatchPrintStyles({
2149
+ pageWidthMm,
2150
+ pageHeightMm,
2151
+ marginTop: (_b = (_a = page.marginMm) === null || _a === void 0 ? void 0 : _a.top) !== null && _b !== void 0 ? _b : 0,
2152
+ marginRight: (_d = (_c = page.marginMm) === null || _c === void 0 ? void 0 : _c.right) !== null && _d !== void 0 ? _d : 0,
2153
+ marginBottom: (_f = (_e = page.marginMm) === null || _e === void 0 ? void 0 : _e.bottom) !== null && _f !== void 0 ? _f : 0,
2154
+ marginLeft: (_h = (_g = page.marginMm) === null || _g === void 0 ? void 0 : _g.left) !== null && _h !== void 0 ? _h : 0,
2155
+ isContinuous: page.size === 'CONTINUOUS',
2156
+ minHeightMm: page.minHeightMm,
2157
+ });
2158
+ const fullHTML = generatePrintHTML({
2159
+ title: '多模板批量打印',
2160
+ styles,
2161
+ bodyContent: allPagesHTML.join('\n'),
2162
+ });
2163
+ if (preview) {
2164
+ const printWindow = window.open('', '_blank');
2165
+ if (!printWindow) {
2166
+ throw new Error('Failed to open print window');
2167
+ }
2168
+ printWindow.document.write(fullHTML);
2169
+ printWindow.document.close();
2170
+ await waitForImagesLoaded(printWindow.document);
2171
+ printWindow.print();
2172
+ }
2173
+ else {
2174
+ const iframe = document.createElement('iframe');
2175
+ iframe.style.position = 'fixed';
2176
+ iframe.style.top = '-9999px';
2177
+ iframe.style.left = '-9999px';
2178
+ document.body.appendChild(iframe);
2179
+ const iframeDoc = (_j = iframe.contentWindow) === null || _j === void 0 ? void 0 : _j.document;
2180
+ if (!iframeDoc) {
2181
+ throw new Error('Failed to access iframe document');
2182
+ }
2183
+ iframeDoc.write(fullHTML);
2184
+ iframeDoc.close();
2185
+ await waitForImagesLoaded(iframeDoc);
2186
+ const cleanup = () => {
2187
+ if (iframe.parentNode) {
2188
+ document.body.removeChild(iframe);
2189
+ }
2190
+ };
2191
+ if (iframe.contentWindow) {
2192
+ iframe.contentWindow.addEventListener('afterprint', cleanup, { once: true });
2193
+ }
2194
+ (_k = iframe.contentWindow) === null || _k === void 0 ? void 0 : _k.print();
2195
+ setTimeout(() => {
2196
+ if (iframe.parentNode) {
2197
+ console.warn('[PrintSDK] afterprint 事件未触发,执行兜底清理');
2198
+ cleanup();
2199
+ }
2200
+ }, 5000);
2201
+ }
2202
+ }
2081
2203
  }
2082
2204
  /**
2083
2205
  * 创建 SDK 实例(无需配置)