x-print-designer 0.0.3 → 0.0.5

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.
Files changed (50) hide show
  1. package/README.md +382 -307
  2. package/dist/Axis-B3MEVZ27.js +18214 -0
  3. package/dist/JsBarcode-DR3EuD60.js +3866 -0
  4. package/dist/browser-DXN-y76H.js +2153 -0
  5. package/dist/charts-BEFiaBSj.js +18428 -0
  6. package/dist/components-DLGmq9Hs.js +16442 -0
  7. package/dist/core-BH5GCqh2.js +233 -0
  8. package/dist/createSeriesData-C8TQkDhm.js +434 -0
  9. package/dist/customGraphicKeyframeAnimation-Co1OX5Xt.js +9429 -0
  10. package/dist/dom-to-image-more.min-S-TpuEqL.js +540 -0
  11. package/dist/graphic-BP8Wuks8.js +11299 -0
  12. package/dist/html2canvas.esm-DWZSdXKi.js +7795 -0
  13. package/dist/index-CcUJ-UmV.js +336 -0
  14. package/dist/index.es-698hP6_U.js +10445 -0
  15. package/dist/jspdf.es.min-CSbVoGg3.js +11560 -0
  16. package/dist/jszip.min-BKMA8ptO.js +2354 -0
  17. package/dist/print-designer.css +1 -0
  18. package/dist/print-designer.es.js +4 -0
  19. package/dist/print-designer.umd.js +380 -0
  20. package/dist/purify.es-CoISWr_W.js +1082 -0
  21. package/dist/renderers-YjB_OS7R.js +2387 -0
  22. package/dist/typeof-DxTVrAEz.js +11 -0
  23. package/dist/web-component-ZD0SqIW-.js +48430 -0
  24. package/dist/web-component.d.ts +102 -0
  25. package/package.json +1 -1
  26. package/dist/assets/Axis-r61YvV2N.js +0 -8
  27. package/dist/assets/JsBarcode-BOwNyIpk.js +0 -1
  28. package/dist/assets/barcode-BoWGE6Vo.js +0 -8
  29. package/dist/assets/browser-CR_q2Gy7.js +0 -1
  30. package/dist/assets/canvg-ChoTXRpY.js +0 -18
  31. package/dist/assets/charts-lpQQN2Ks.js +0 -1
  32. package/dist/assets/components-D_JDMQgr.js +0 -22
  33. package/dist/assets/core-BrVw2LCo.js +0 -1
  34. package/dist/assets/createSeriesData-BS3ONHiF.js +0 -1
  35. package/dist/assets/customGraphicKeyframeAnimation-CXbdssPk.js +0 -3
  36. package/dist/assets/dom-to-image-more.min-CWakCYDx.js +0 -1
  37. package/dist/assets/favicon-o5K9zUr2.ico +0 -0
  38. package/dist/assets/graphic-DgPervEb.js +0 -29
  39. package/dist/assets/html2canvas.esm-QH1iLAAe.js +0 -22
  40. package/dist/assets/index-JEGgaCHA.css +0 -1
  41. package/dist/assets/index-OtDSuAAX.js +0 -154
  42. package/dist/assets/jszip.min-DLPWIKb1.js +0 -12
  43. package/dist/assets/logo-DXm7qK2I.png +0 -0
  44. package/dist/assets/monaco-DvnI3chM.js +0 -11
  45. package/dist/assets/pdf-D4995UXf.js +0 -170
  46. package/dist/assets/purify.es-BaNf_EpD.js +0 -2
  47. package/dist/assets/renderers-BUy3EJql.js +0 -3
  48. package/dist/assets/utils-C6aID195.js +0 -1
  49. package/dist/assets/vendor-ClC5F-pT.js +0 -39
  50. package/dist/index.html +0 -19
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # X-Print-Designer
2
2
 
3
- 基于 Vue 3 + TypeScript 开发的打印设计器,支持可视化拖拽设计打印模板,集成 ECharts 图表组件,提供模板模式与报告模式两种工作方式。
3
+ 基于 Vue 3 + TypeScript 开发的打印设计器,支持可视化拖拽设计打印模板,集成 ECharts 图表组件。
4
4
 
5
5
  ---
6
6
 
@@ -8,11 +8,15 @@
8
8
 
9
9
  - [核心概念:模板模式 vs 报告模式](#核心概念模板模式-vs-报告模式)
10
10
  - [快速开始](#快速开始)
11
+ - [Vue 组件使用说明](#vue-组件使用说明)
12
+ - [Web Component 使用说明](#web-component-使用说明)
11
13
  - [传入数据格式](#传入数据格式)
12
- - [支持的元素类型](#支持的元素类型)
13
- - [数据绑定方式](#数据绑定方式)
14
- - [测试数据](#测试数据)
14
+ - [保存回调 `onSave`](#保存回调-onsave)
15
+ - [自动保存配置](#自动保存配置)
16
+ - [模板数据结构(完整格式)](#模板数据结构完整格式)
17
+ - [元素数据绑定](#元素数据绑定)
15
18
  - [变量语法参考](#变量语法参考)
19
+ - [支持的元素类型](#支持的元素类型)
16
20
  - [技术栈](#技术栈)
17
21
 
18
22
  ---
@@ -21,103 +25,36 @@
21
25
 
22
26
  设计器支持两种工作模式,通过 `mode` 属性/方法进行切换。
23
27
 
24
- ### 模板模式(template)— 默认模式
25
-
26
- **适用场景:** 创建可复用的打印模板,模板与数据分离。先在设计器中画好布局,打印时再填入真实数据。
27
-
28
- **保存行为:**
29
- - 保存时**只保存页面布局信息**(元素位置、样式、画布大小、参考线等)
30
- - **不保存**测试数据(`testData`)
28
+ ### 模板模式(`template`)— 默认模式
31
29
 
32
- **典型工作流:**
33
- 1. 在设计器中拖拽元素,设计打印模板的布局
34
- 2. 使用变量语法(如 `{#orderNo}`)标记数据占位符
35
- 3. 保存模板,模板只包含布局结构
36
- 4. 业务系统调用打印/导出时,传入真实数据替换变量
30
+ **适用场景:** 创建可复用的打印模板,模板与数据分离。布局固定,数据在打印时动态注入。
37
31
 
38
- **保存的数据结构:**
32
+ - 保存时**只保存页面布局**(元素位置、样式、画布大小等)
33
+ - **不保存** `testData`(测试数据)
39
34
 
40
- ```json
41
- {
42
- "pages": [ /* 页面和元素列表 */ ],
43
- "canvasSize": { "width": 794, "height": 1123 },
44
- "guides": [],
45
- "zoom": 1,
46
- "showGrid": true,
47
- "headerHeight": 0,
48
- "footerHeight": 0,
49
- "watermark": { "enabled": false, "text": "", "angle": -30, "color": "#000000", "opacity": 0.1, "size": 24, "density": 160 },
50
- "unit": "mm",
51
- "pageSpacingX": 0,
52
- "pageSpacingY": 0,
53
- "canvasBackground": "#ffffff"
54
- }
55
- ```
35
+ ### 报告模式(`report`)
56
36
 
57
- ### 报告模式(report)
37
+ **适用场景:** 数据与布局绑定在一起的完整报告(如检测报告、统计报表)。
58
38
 
59
- **适用场景:** 创建包含数据的完整报告,布局和数据一起保存。适用于需要将模板和示例数据打包存储的场景。
39
+ - 保存时**同时保存页面布局 + 测试数据**(`testData`)
60
40
 
61
- **保存行为:**
62
- - 保存时**同时保存页面布局信息和测试数据**(`testData`)
63
- - `testData` 中包含当前设计器中填写/导入的测试数据
41
+ ### 对比
64
42
 
65
- **典型工作流:**
66
- 1. 在设计器中拖拽元素,设计报告布局
67
- 2. 通过 API 或界面导入测试数据
68
- 3. 保存报告,布局和数据一起持久化
69
- 4. 加载报告时,布局和测试数据一起恢复
43
+ | 特性 | `template` | `report` |
44
+ |------|-----------|----------|
45
+ | 保存布局 | ✅ | ✅ |
46
+ | 保存 testData | ❌ | ✅ |
47
+ | 典型用途 | 订单打印、标签 | 检测报告、报表 |
70
48
 
71
- **保存的数据结构(与模板模式的区别):**
72
-
73
- ```json
74
- {
75
- "pages": [ /* ... */ ],
76
- "canvasSize": { "width": 794, "height": 1123 },
77
- "watermark": { /* ... */ },
78
- "testData": {
79
- "orderNo": "A001",
80
- "customerName": "张三",
81
- "items": [
82
- { "name": "产品A", "price": 100, "quantity": 2 }
83
- ]
84
- }
85
- }
86
- ```
87
-
88
- ### 对比总结
89
-
90
- | 特性 | 模板模式 (template) | 报告模式 (report) |
91
- |------|-------------------|------------------|
92
- | 保存布局 | ✅ 是 | ✅ 是 |
93
- | 保存测试数据 (`testData`) | ❌ 否 | ✅ 是 |
94
- | 适用场景 | 通用打印模板 | 数据+布局一体的报告 |
95
- | 数据注入时机 | 打印/导出时传入 | 设计时即绑定 |
96
- | 典型用途 | 订单打印、标签打印 | 检测报告、统计报表 |
97
-
98
- ### 如何切换模式
99
-
100
- **Vue 组件方式:**
49
+ ### 切换模式
101
50
 
102
51
  ```vue
103
- <template>
104
- <!-- 模板模式(默认) -->
105
- <PrintDesigner mode="template" />
106
-
107
- <!-- 报告模式 -->
108
- <PrintDesigner mode="report" />
109
- </template>
52
+ <PrintDesigner mode="template" />
53
+ <PrintDesigner mode="report" />
110
54
  ```
111
55
 
112
- **Web Component 方式:**
113
-
114
56
  ```js
115
- const designer = document.querySelector('print-designer');
116
-
117
- // 切换为模板模式(默认)
118
57
  designer.setMode('template');
119
-
120
- // 切换为报告模式
121
58
  designer.setMode('report');
122
59
  ```
123
60
 
@@ -125,43 +62,100 @@ designer.setMode('report');
125
62
 
126
63
  ## 快速开始
127
64
 
128
- ### 安装
129
-
130
65
  ```bash
131
66
  npm install
132
67
  npm run dev
133
68
  ```
134
69
 
135
- ### Vue 组件方式
70
+ ### 最简接入
136
71
 
137
72
  ```vue
138
73
  <template>
139
74
  <PrintDesigner
140
75
  mode="template"
141
76
  :sample-data="sampleData"
77
+ :on-save="handleSave"
142
78
  />
143
79
  </template>
144
80
 
145
81
  <script setup>
146
82
  import { ref } from 'vue';
147
- import PrintDesigner from './components/PrintDesigner.vue';
83
+ import PrintDesigner from '@/components/PrintDesigner.vue';
148
84
 
149
85
  const sampleData = ref({
150
86
  id: 1,
151
87
  name: '测试报告',
88
+ templateId: 'tpl_001',
152
89
  variables: {
153
90
  title: '报告标题',
154
91
  customerName: '张三',
155
92
  items: [
156
- { name: '产品A', price: 100, quantity: 2 },
157
- { name: '产品B', price: 200, quantity: 3 }
93
+ { name: '产品A', price: 100, quantity: 2 }
158
94
  ]
159
95
  }
160
96
  });
97
+
98
+ const handleSave = (payload) => {
99
+ console.log('保存数据:', payload);
100
+ // { id: '...', name: '测试报告', data: {...}, isNew: false }
101
+ // 用户自行调用接口保存
102
+ };
161
103
  </script>
162
104
  ```
163
105
 
164
- ### Web Component 方式
106
+ ---
107
+
108
+ ## Vue 组件使用说明
109
+
110
+ ### 组件 Props 一览
111
+
112
+ | 属性 | 类型 | 默认值 | 说明 |
113
+ |------|------|--------|------|
114
+ | `mode` | `'template'` \| `'report'` | `'template'` | 工作模式 |
115
+ | `sample-data` | `object` | — | 传入的示例数据 |
116
+ | `on-save` | `(payload) => void \| Promise<void>` | — | 保存回调,详见下方 |
117
+ | `auto-save-enabled` | `boolean` | `false` | 是否开启定时自动保存 |
118
+ | `auto-save-interval-ms` | `number` | `120000` | 自动保存间隔(毫秒),默认 2 分钟 |
119
+
120
+ ### Props 详细说明
121
+
122
+ #### `mode`
123
+
124
+ 工作模式。`'template'` 保存时只存布局,`'report'` 保存时同时存布局和 testData。
125
+
126
+ ```vue
127
+ <PrintDesigner mode="report" />
128
+ ```
129
+
130
+ #### `sample-data`
131
+
132
+ 传入设计器的示例数据。**最关键的属性**。详见下方 [传入数据格式](#传入数据格式)。
133
+
134
+ ```vue
135
+ <PrintDesigner :sample-data="{ id: 1, name: '报告', variables: {...} }" />
136
+ ```
137
+
138
+ #### `on-save`
139
+
140
+ 保存回调函数。当用户点击保存按钮(或自动保存触发)时,设计器组装好数据后调用此回调,**不再自动调接口**,完全交由用户自行处理。
141
+
142
+ ```vue
143
+ <PrintDesigner :on-save="(payload) => fetch('/api/save', { method: 'POST', body: JSON.stringify(payload) })" />
144
+ ```
145
+
146
+ #### `auto-save-enabled`
147
+
148
+ 是否开启定时自动保存。开启后每隔 `auto-save-interval-ms` 毫秒自动触发 `on-save` 回调。详见 [自动保存配置](#自动保存配置)。
149
+
150
+ #### `auto-save-interval-ms`
151
+
152
+ 自动保存间隔时间(毫秒),默认 `120000`(2 分钟)。
153
+
154
+ ---
155
+
156
+ ## Web Component 使用说明
157
+
158
+ ### 标签方式
165
159
 
166
160
  ```html
167
161
  <print-designer id="designer"></print-designer>
@@ -169,131 +163,272 @@ const sampleData = ref({
169
163
  <script>
170
164
  const designer = document.getElementById('designer');
171
165
 
172
- // 设置模式
173
166
  designer.setMode('template');
174
-
175
- // 设置测试数据
176
- designer.setTestData({
177
- orderNo: 'ORD-001',
178
- customerName: '张三',
179
- items: [
180
- { name: '产品A', price: 100, quantity: 2 }
181
- ]
167
+ designer.setOnSave((payload) => {
168
+ fetch('/api/save', { method: 'POST', body: JSON.stringify(payload) });
182
169
  });
183
-
184
- // 设置模板列表
185
- designer.setTemplates([
186
- {
187
- id: 'tpl-1',
188
- name: 'A4 打印模板',
189
- data: {
190
- pages: [],
191
- canvasSize: { width: 794, height: 1123 },
192
- unit: 'mm'
193
- }
194
- }
195
- ], { currentTemplateId: 'tpl-1' });
170
+ designer.setAutoSave(true, 120000);
171
+ designer.setTestData({ orderNo: 'ORD-001', items: [...] });
196
172
  </script>
197
173
  ```
198
174
 
175
+ ### API 速览
176
+
177
+ | 方法 | 说明 |
178
+ |------|------|
179
+ | `setMode(mode)` | 设置模式:`'template'` / `'report'` |
180
+ | `setOnSave(handler)` | 设置保存回调 |
181
+ | `setAutoSave(enabled, intervalMs?)` | 开启/关闭自动保存 |
182
+ | `setTestData(data)` | 设置测试数据 |
183
+ | `setVariables(vars)` | 打印/导出时注入真实变量 |
184
+ | `setTemplates(list, opts)` | 设置模板列表 |
185
+ | `loadTemplate(id)` | 加载指定模板到画布 |
186
+ | `print(request)` | 执行打印 |
187
+ | `export(request)` | 导出 PDF/图片/HTML |
188
+
189
+ ### 打印时注入真实数据
190
+
191
+ ```js
192
+ const designer = document.querySelector('print-designer');
193
+
194
+ await designer.loadTemplate('tpl-order');
195
+
196
+ designer.setVariables({
197
+ orderNo: 'REAL-2026-001',
198
+ customerName: '李四',
199
+ totalAmount: 5999.00
200
+ });
201
+
202
+ await designer.print({ mode: 'browser', options: { silent: true } });
203
+ ```
204
+
199
205
  ---
200
206
 
201
207
  ## 传入数据格式
202
208
 
203
- ### 1. 通过 `sample-data` 属性传入(Vue 组件方式)
209
+ 数据以 **扁平键值对** 形式传入,所有字段在根级别,在模板中通过 `@变量名` 或 `{#变量名}` 直接引用。
204
210
 
205
- 使用 `PrintDesigner` 组件时,通过 `sample-data` 属性传入示例数据,用于设计器中的数据绑定预览:
211
+ ### `sample-data` 字段说明
206
212
 
207
- ```vue
208
- <template>
209
- <PrintDesigner :sample-data="sampleData" />
210
- </template>
213
+ | 字段 | 类型 | 必填 | 说明 |
214
+ |------|------|------|------|
215
+ | `id` | `number` / `string` | 是 | **业务数据的 ID**,如报告 ID、订单 ID |
216
+ | `templateId` | `string` | 否 | 加载的打印模板 ID,不传则用当前已选模板 |
217
+ | `name` | `string` | 否 | 数据名称,如报告标题、订单名称 |
218
+ | `variables` | `object` | 否 | **业务变量对象**,包含模板中绑定的所有字段 |
219
+ | 任意自定义字段 | `any` | 否 | 如 `createTime`、`selected`,也可在模板中引用 |
211
220
 
212
- <script setup>
213
- const sampleData = ref({
214
- id: 8,
215
- name: '测试报告',
216
- createTime: '2026-05-01',
217
- variables: {
218
- dockname: '机场名称',
219
- dronesn: '无人机序列号',
220
- totalcount: 100,
221
- flightRecords: [
222
- { time: '2026-04-02 09:30', taskname: '巡检任务A', distance: '12.5 km' },
223
- { time: '2026-04-02 14:00', taskname: '巡检任务B', distance: '8.3 km' }
221
+ > `id` 是业务数据的标识,`templateId` 是打印模板的标识,两者是不同概念。
222
+
223
+ ### `variables` 内部结构
224
+
225
+ `variables` 是一个扁平或嵌套的 JSON 对象,其中的所有字段都可在模板元素中通过变量语法绑定。
226
+
227
+ 支持的数据类型:
228
+ - **字符串** `string` — 文本元素绑定
229
+ - **数字** `number` — 文本、图表数据
230
+ - **布尔值** `boolean`
231
+ - **数组** `array` 表格数据源、图表数据源
232
+ - **嵌套对象** `object` 多级路径访问
233
+
234
+ ### 示例:完整数据格式
235
+
236
+ ```json
237
+ {
238
+ "id": 8,
239
+ "name": "宝安机场飞行任务记录",
240
+ "templateId": "tpl_report_drone",
241
+ "createTime": "2026-05-01",
242
+ "variables": {
243
+ "dockname": "宝安国际无人机机场",
244
+ "dronesn": "6EE5NC104E0PY6",
245
+ "dronename": "M300 RTK",
246
+ "reportname": "宝安机场飞行任务记录报告",
247
+ "tasktype": "航线巡检",
248
+ "totalcount": 4,
249
+ "totaldistance": "48.6 km",
250
+ "conclusion": "本轮飞行任务覆盖宝安区重点航线,数据采集完整。",
251
+ "flightRecords": [
252
+ {
253
+ "time": "2026-04-02 09:30",
254
+ "taskname": "宝安港区A线巡检",
255
+ "dockname": "宝安国际无人机机场",
256
+ "distance": "12.5 km",
257
+ "image": "https://picsum.photos/id/1/200/80"
258
+ },
259
+ {
260
+ "time": "2026-04-08 14:00",
261
+ "taskname": "西乡街道线路巡查",
262
+ "dockname": "罗湖无人机机场",
263
+ "distance": "10.2 km",
264
+ "image": "https://picsum.photos/id/10/200/80"
265
+ }
224
266
  ],
225
- chartData: [
226
- { category: '一月', value: 1200 },
227
- { category: '二月', value: 1500 },
228
- { category: '三月', value: 1800 }
267
+ "taskRecords": [
268
+ {
269
+ "time": "2026-04-02 09:30",
270
+ "taskname": "宝安港区A线巡检",
271
+ "dockname": "宝安国际无人机机场",
272
+ "warn": 2
273
+ },
274
+ {
275
+ "time": "2026-04-08 14:00",
276
+ "taskname": "西乡街道线路巡查",
277
+ "dockname": "罗湖无人机机场",
278
+ "warn": 1
279
+ }
280
+ ],
281
+ "chartData": [
282
+ { "category": "一月", "value": 1200 },
283
+ { "category": "二月", "value": 1500 },
284
+ { "category": "三月", "value": 1800 }
229
285
  ]
230
286
  }
231
- });
232
- </script>
287
+ }
233
288
  ```
234
289
 
235
- **`sampleData` 字段说明:**
290
+ ### 在模板中绑定变量
236
291
 
237
- | 字段 | 类型 | 必填 | 说明 |
238
- |-----|------|-----|------|
239
- | `id` | `number` / `string` | | 数据唯一标识 |
240
- | `name` | `string` | | 数据名称 |
241
- | `variables` | `object` | **是** | 变量对象,包含所有可供模板绑定的数据 |
292
+ | 变量 | 绑定写法 | 应用元素 |
293
+ |------|---------|---------|
294
+ | `variables.dockname` | `@dockname` `{#dockname}` | 文本 |
295
+ | `variables.reportname` | `@reportname` | 文本 |
296
+ | `variables.totaldistance` | `@totaldistance` | 文本 |
297
+ | `variables.createtime` | `@createtime` | 文本 |
298
+ | `variables.flightRecords` | `@flightRecords` | 表格、自定义表格 |
299
+ | `variables.taskRecords` | `@taskRecords` | 表格 |
300
+ | `variables.chartData` | `@chartData` | 图表 |
242
301
 
243
- `variables` 对象支持任意嵌套结构:
244
- - **简单值:** `string`、`number`、`boolean`
245
- - **数组:** 用于表格和图表组件的数据源
246
- - **嵌套对象:** 支持多级路径访问
302
+ ### 表格数据
247
303
 
248
- ### 2. 通过 `setTestData()` 方法传入(Web Component 方式)
304
+ 表格需要三个维度的变量:
249
305
 
250
- ```js
251
- const designer = document.querySelector('print-designer');
306
+ | 绑定字段 | 变量名 | 格式 | 说明 |
307
+ |---------|-------|------|------|
308
+ | `variable` | `@items` | `Array<Object>` | 表格行数据 |
309
+ | `columnsVariable` | `@tableCols` | `Array<{field, header, width}>` | 列定义 |
310
+ | `footerDataVariable` | `@tableFooter` | `Array<Object>` | 页脚汇总行 |
252
311
 
253
- designer.setTestData({
254
- orderNo: 'ORD-2026-001',
255
- customerName: '张三',
256
- items: [
257
- { name: '产品A', price: 100, quantity: 2 },
258
- { name: '产品B', price: 200, quantity: 3 }
312
+ ```json
313
+ {
314
+ "items": [
315
+ { "name": "产品A", "price": 100, "quantity": 2 },
316
+ { "name": "产品B", "price": 200, "quantity": 3 }
317
+ ],
318
+ "tableCols": [
319
+ { "field": "name", "header": "产品名称", "width": 120 },
320
+ { "field": "price", "header": "单价", "width": 80 },
321
+ { "field": "quantity", "header": "数量", "width": 80 }
259
322
  ],
260
- chartData: [
261
- { category: '一月', value: 1200 },
262
- { category: '二月', value: 1500 }
323
+ "tableFooter": [
324
+ { "name": { "value": "合计" }, "price": { "value": "300" } }
263
325
  ]
264
- });
326
+ }
265
327
  ```
266
328
 
267
- ### 3. 通过 `setVariables()` 方法传入(Web Component 方式)
329
+ ### 图表数据
268
330
 
269
- `setVariables` 用于在实际打印/导出时替换模板中的变量占位符:
331
+ ```json
332
+ {
333
+ "chartData": [
334
+ { "category": "一月", "value": 1200 },
335
+ { "category": "二月", "value": 1500 }
336
+ ]
337
+ }
338
+ ```
270
339
 
271
- ```js
272
- const designer = document.querySelector('print-designer');
340
+ 图表元素需配置:
341
+ - **chartDataVariable:** `@chartData`
342
+ - **chartXField:** `category`(X 轴字段名)
343
+ - **chartYField:** `value`(Y 轴字段名)
344
+ - **chartNameField:** `category`(饼图名称字段)
273
345
 
274
- // 加载模板
275
- await designer.loadTemplate('tpl-order');
346
+ ---
276
347
 
277
- // 设置真实数据
278
- designer.setVariables({
279
- orderNo: 'REAL-2026-001',
280
- customerName: '李四',
281
- totalAmount: 5999.00
282
- });
348
+ ## 保存回调 `onSave`
283
349
 
284
- // 静默打印
285
- await designer.print({ mode: 'browser', options: { silent: true } });
350
+ 传入 `onSave` 后,用户点击保存按钮(`Ctrl+S` 或工具栏按钮)时,设计器不再自动调接口,而是将组装好的数据通过回调交给用户自行处理。
351
+
352
+ ### 回调参数
353
+
354
+ ```ts
355
+ interface SavePayload {
356
+ id: string | null; // 模板 ID,null 表示新建
357
+ name: string; // 模板名称
358
+ data: Record<string, any>; // 完整模板数据(pages/canvasSize/watermark/testData...)
359
+ isNew: boolean; // 是否新建模板
360
+ }
361
+ ```
362
+
363
+ ### 使用示例
364
+
365
+ ```vue
366
+ <PrintDesigner
367
+ :on-save="handleSave"
368
+ />
369
+
370
+ <script setup>
371
+ const handleSave = async (payload) => {
372
+ // payload.data 包含 pages、canvasSize、watermark、testData 等所有画布数据
373
+ const response = await fetch('/api/save', {
374
+ method: 'POST',
375
+ headers: { 'Content-Type': 'application/json' },
376
+ body: JSON.stringify(payload)
377
+ });
378
+ if (!response.ok) throw new Error('保存失败');
379
+ };
380
+ </script>
381
+ ```
382
+
383
+ > 不传 `onSave` 时,设计器会走原有的默认保存逻辑(调用内置 CRUD 接口或存 localStorage),行为不变。
384
+
385
+ ---
386
+
387
+ ## 自动保存配置
388
+
389
+ 传入 `onSave` 后,可同时开启自动保存。开启后设计器会每隔固定时间调用 `onSave` 回调,右下角显示最近保存时间。
390
+
391
+ ### Props 配置
392
+
393
+ | 属性 | 类型 | 默认值 | 说明 |
394
+ |------|------|--------|------|
395
+ | `auto-save-enabled` | `boolean` | `false` | 是否开启 |
396
+ | `auto-save-interval-ms` | `number` | `120000` | 间隔(毫秒),默认 2 分钟 |
397
+
398
+ ### 示例
399
+
400
+ ```vue
401
+ <PrintDesigner
402
+ :on-save="handleSave"
403
+ :auto-save-enabled="true"
404
+ :auto-save-interval-ms="120000"
405
+ />
406
+ ```
407
+
408
+ ```js
409
+ designer.setOnSave(handleSave);
410
+ designer.setAutoSave(true, 120000); // 每 2 分钟自动保存
411
+ designer.setAutoSave(false); // 关闭
286
412
  ```
287
413
 
288
- ### 4. 模板数据结构(完整格式)
414
+ > 右下角会显示 "最近保存: HH:MM:SS",每次回调完成后自动更新时间。
415
+
416
+ ---
417
+
418
+ ## 模板数据结构(完整格式)
289
419
 
290
- 当你需要直接保存或加载完整的模板数据时,使用以下数据结构:
420
+ 当用户直接保存/加载完整模板时,使用以下数据结构:
291
421
 
292
422
  ```json
293
423
  {
294
424
  "id": "tpl_001",
295
425
  "name": "A4 订单模板",
296
426
  "updatedAt": 1715251200000,
427
+ "permissions": {
428
+ "editable": true,
429
+ "deletable": true,
430
+ "copyable": true
431
+ },
297
432
  "data": {
298
433
  "pages": [
299
434
  {
@@ -307,11 +442,7 @@ await designer.print({ mode: 'browser', options: { silent: true } });
307
442
  "width": 200,
308
443
  "height": 24,
309
444
  "content": "订单号: {#orderNo}",
310
- "style": {
311
- "fontSize": 14,
312
- "fontWeight": "bold",
313
- "color": "#111827"
314
- }
445
+ "style": { "fontSize": 14, "fontWeight": "bold", "color": "#111827" }
315
446
  },
316
447
  {
317
448
  "id": "el_table_1",
@@ -360,9 +491,7 @@ await designer.print({ mode: 'browser', options: { silent: true } });
360
491
  },
361
492
  "testData": {
362
493
  "orderNo": "A001",
363
- "items": [
364
- { "name": "产品A", "price": 100, "quantity": 2 }
365
- ]
494
+ "items": [{ "name": "产品A", "price": 100, "quantity": 2 }]
366
495
  }
367
496
  },
368
497
  "ext": {
@@ -380,154 +509,100 @@ await designer.print({ mode: 'browser', options: { silent: true } });
380
509
  }
381
510
  ```
382
511
 
383
- **顶层字段说明:**
512
+ ### 顶层字段
384
513
 
385
514
  | 字段 | 类型 | 必填 | 说明 |
386
515
  |-----|------|-----|------|
387
- | `id` | `string` | 是 | 模板唯一标识 |
516
+ | `id` | `string` | 是 | 模板唯一 ID |
388
517
  | `name` | `string` | 是 | 模板名称 |
389
518
  | `updatedAt` | `number` | 否 | 更新时间戳(毫秒) |
519
+ | `permissions` | `object` | 否 | `{ editable, deletable, copyable }` |
390
520
  | `data` | `object` | 是 | 模板设计数据 |
391
- | `data.pages` | `Page[]` | | 页面列表,每个页面包含元素数组 |
392
- | `data.canvasSize` | `Size` | 是 | 画布尺寸 `{ width, height }` |
393
- | `data.unit` | `string` | 否 | 单位:`mm` / `px` / `pt` / `in` / `cm` |
394
- | `data.watermark` | `WatermarkSettings` | 否 | 水印配置 |
395
- | `data.guides` | `Guide[]` | 否 | 参考线列表 |
396
- | `data.testData` | `object` | 否 | 测试数据(报告模式会保存) |
397
- | `ext` | `object` | 否 | 扩展字段 |
398
- | `ext.availableVariables` | `VariableTreeItem[]` | 否 | 模板绑定的变量树 |
399
- | `permissions` | `object` | 否 | 权限控制 `{ editable, deletable, copyable }` |
400
-
401
- ---
402
-
403
- ## 支持的元素类型
404
-
405
- | 元素类型 | 说明 | 数据支持 |
406
- |---------|------|---------|
407
- | 文本(Text) | 显示静态文本或变量内容 | 静态内容 / 变量引用 |
408
- | 长文本(LongText) | 自动扩展的多行文本 | 静态内容 / 变量引用 |
409
- | 图片(Image) | 显示图片 | 静态URL / 变量引用 |
410
- | 表格(Table) | 展示数据表格,支持自动分页 | 静态数据 / 数据变量 |
411
- | 自定义表格(CustomTable) | 自由合并单元格的表格 | 静态数据 / 数据变量 |
412
- | 条形码(Barcode) | 生成条形码 | 变量引用 |
413
- | 二维码(QRCode) | 生成二维码 | 变量引用 |
414
- | 图表(Chart) | ECharts 图表(柱状图/折线图/饼图/环形图/面积图) | 静态数据 / 数据变量 |
415
- | 线条(Line) | 绘制直线 | — |
416
- | 矩形(Rect) | 绘制矩形 | — |
417
- | 圆形(Circle) | 绘制圆形 | — |
418
- | 页码(PageNumber) | 显示页码信息 | — |
419
-
420
- ---
421
-
422
- ## 数据绑定方式
423
-
424
- ### 1. 静态数据
425
-
426
- 直接在元素属性面板中输入静态内容,适用于固定不变的文本、图片URL等。
427
-
428
- ### 2. 变量绑定
429
-
430
- 使用特殊语法引用外部传入的数据,数据在打印/导出时动态替换。
431
-
432
- ### 3. 数组数据(表格 & 图表)
433
-
434
- 表格和图表组件可以绑定数组类型的数据变量。
435
-
436
- **表格数据格式:**
437
-
438
- ```json
439
- {
440
- "items": [
441
- { "name": "产品A", "price": 100, "quantity": 2 },
442
- { "name": "产品B", "price": 200, "quantity": 3 }
443
- ],
444
- "tableCols": [
445
- { "field": "name", "header": "产品名称", "width": 120 },
446
- { "field": "price", "header": "单价", "width": 80 },
447
- { "field": "quantity", "header": "数量", "width": 80 }
448
- ],
449
- "tableFooter": [
450
- { "name": { "value": "合计" }, "price": { "value": "300" } }
451
- ]
452
- }
453
- ```
521
+ | `ext` | `object` | | 扩展数据(变量树等) |
454
522
 
455
- 在表格元素的属性面板中:
456
- - **数据变量(variable):** 绑定到 `@items`
457
- - **列定义变量(columnsVariable):** 绑定到 `@tableCols`
458
- - **页脚数据变量(footerDataVariable):** 绑定到 `@tableFooter`
523
+ ### `data` 内部字段
459
524
 
460
- **图表数据格式:**
461
-
462
- ```json
463
- {
464
- "chartData": [
465
- { "category": "一月", "value": 1200 },
466
- { "category": "二月", "value": 1500 },
467
- { "category": "三月", "value": 1800 }
468
- ]
525
+ | 字段 | 类型 | 必填 | 说明 |
526
+ |-----|------|-----|------|
527
+ | `pages` | `Page[]` | 是 | 页面列表,每页含 `elements` 数组 |
528
+ | `canvasSize` | `{ width, height }` | 是 | 画布尺寸(默认 A4: 794×1123) |
529
+ | `unit` | `'mm'`\|`'px'`\|`'pt'`\|`'in'`\|`'cm'` | 否 | 单位 |
530
+ | `watermark` | `WatermarkSettings` | 否 | 水印配置 |
531
+ | `guides` | `Guide[]` | 否 | 参考线 |
532
+ | `zoom` | `number` | 否 | 缩放比例 |
533
+ | `showGrid` | `boolean` | 否 | 显示网格 |
534
+ | `headerHeight` | `number` | 否 | 页眉高度 |
535
+ | `footerHeight` | `number` | 否 | 页脚高度 |
536
+ | `pageSpacingX` | `number` | 否 | 水平边距 |
537
+ | `pageSpacingY` | `number` | 否 | 垂直边距 |
538
+ | `canvasBackground` | `string` | 否 | 画布背景色 |
539
+ | `testData` | `object` | 否 | 测试数据(报告模式保存) |
540
+
541
+ ### 元素基本字段
542
+
543
+ ```ts
544
+ interface PrintElement {
545
+ id: string; // 元素唯一 ID
546
+ type: ElementType; // 元素类型: 'text'|'image'|'table'|'customTable'|'barcode'|'qrcode'|'line'|'rect'|'circle'|'chart'|'pageNumber'|'longtext'
547
+ x: number; // X 坐标
548
+ y: number; // Y 坐标
549
+ width: number; // 宽度
550
+ height: number; // 高度
551
+ content?: string; // 文本内容(支持变量占位符)
552
+ variable?: string; // 绑定变量名(如 '@items')
553
+ style: ElementStyle; // 样式对象
469
554
  }
470
555
  ```
471
556
 
472
- 图表元素支持的绑定字段:
473
- - **chartDataVariable:** 数据变量名
474
- - **chartXField:** X轴/分类字段名
475
- - **chartYField:** Y轴/数值字段名
476
- - **chartNameField:** 名称字段(饼图)
477
-
478
557
  ---
479
558
 
480
- ## 测试数据
481
-
482
- 在设计器中编辑时,可以手动编辑测试数据以便预览变量渲染效果。
483
-
484
- ### 设计器内编辑
559
+ ## 元素数据绑定
485
560
 
486
- 在模板列表中对模板右键 → 选择"测试数据" → 在弹出的 JSON 编辑器中编辑测试数据。
561
+ ### 变量语法
487
562
 
488
- ### 通过 API 设置
563
+ | 语法 | 示例 | 说明 |
564
+ |-----|------|------|
565
+ | `@变量名` | `@customerName` | 直接引用变量 |
566
+ | `{#变量路径}` | `{#order.items[0].name}` | 支持嵌套路径和数组索引 |
489
567
 
490
- ```js
491
- // Web Component 方式
492
- designer.setTestData({
493
- orderNo: 'TEST-001',
494
- customerName: '测试客户'
495
- });
568
+ ### 各元素可绑定字段
496
569
 
497
- // 获取当前测试数据
498
- const data = designer.getTestData();
499
- ```
570
+ | 元素 | 绑定的属性 | 示例 |
571
+ |------|----------|------|
572
+ | 文本 / 长文本 | `content` | `订单号: {#orderNo}` |
573
+ | 图片 | `variable` | `@imageUrl` |
574
+ | 表格 | `variable` + `columnsVariable` + `footerDataVariable` | `@items`, `@tableCols`, `@tableFooter` |
575
+ | 条形码 | `variable` | `@barcode` |
576
+ | 二维码 | `variable` | `@url` |
577
+ | 图表 | `chartDataVariable` + `chartXField` + `chartYField` | `@chartData`, `category`, `value` |
500
578
 
501
579
  ---
502
580
 
503
- ## 变量语法参考
504
-
505
- | 语法 | 示例 | 说明 |
506
- |-----|------|------|
507
- | `@变量名` | `@customerName` | 使用 `@` 符号引用顶层变量 |
508
- | `{#变量路径}` | `{#order.details[0].name}` | 使用 `{#}` 包裹,支持嵌套路径和数组索引 |
509
- | `{#变量名}` | `{#orderNo}` | 与 `@` 等价,推荐使用此格式 |
510
-
511
- 在元素属性中绑定变量:
581
+ ## 支持的元素类型
512
582
 
513
- | 元素类型 | 可绑定属性的字段 | 示例 |
514
- |---------|----------------|------|
515
- | 文本 | `content` | `订单号: {#orderNo}` |
516
- | 图片 | `variable` | `@logoUrl` |
517
- | 表格 | `variable`、`columnsVariable`、`footerDataVariable` | `@items` |
518
- | 条形码 | `variable` | `{#barcode}` |
519
- | 二维码 | `variable` | `{#url}` |
520
- | 图表 | `chartDataVariable` | `@chartData` |
583
+ | 类型 | 标识 | 说明 |
584
+ |------|------|------|
585
+ | 文本 | `text` | 静态文本或变量内容 |
586
+ | 长文本 | `longtext` | 多行自动扩展文本 |
587
+ | 图片 | `image` | 图片显示(URL 绑定) |
588
+ | 表格 | `table` | 数据表格,支持自动分页、页脚 |
589
+ | 自定义表格 | `customTable` | 自由合并单元格 |
590
+ | 条形码 | `barcode` | 一维条形码 |
591
+ | 二维码 | `qrcode` | 二维码 |
592
+ | 图表 | `chart` | ECharts(柱状图/折线图/饼图/环形图/面积图) |
593
+ | 线条 | `line` | 直线 |
594
+ | 矩形 | `rect` | 矩形 |
595
+ | 圆形 | `circle` | 圆形 |
596
+ | 页码 | `pageNumber` | 页码显示 |
521
597
 
522
598
  ---
523
599
 
524
600
  ## 技术栈
525
601
 
526
- - Vue 3
527
- - TypeScript
602
+ - Vue 3 / TypeScript
528
603
  - Pinia(状态管理)
529
604
  - Tailwind CSS
530
605
  - ECharts 5
531
- - Monaco Editor(代码编辑器)
606
+ - Monaco Editor
532
607
 
533
608
  ---