operp-print-designer 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 garychk
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,280 @@
1
+ # Print Designer 使用文档
2
+
3
+ ## 安装
4
+
5
+ ```bash
6
+ npm install @operp/print-designer
7
+ # 或者
8
+ yarn add @operp/print-designer
9
+ # 或者
10
+ pnpm add @operp/print-designer
11
+ ```
12
+
13
+ ## 基本使用
14
+
15
+ ### 1. 编辑模式
16
+
17
+ ```tsx
18
+ import PrintDesigner from '@operp/print-designer';
19
+
20
+ function Editor() {
21
+ return (
22
+ <PrintDesigner
23
+ initialTemplateData={initialData}
24
+ onSave={(data) => {
25
+ console.log('保存模板:', data);
26
+ }}
27
+ onPrint={() => {
28
+ console.log('打印');
29
+ }}
30
+ />
31
+ );
32
+ }
33
+ ```
34
+
35
+ ### 2. 预览模式
36
+
37
+ ```tsx
38
+ import PrintDesigner from '@operp/print-designer';
39
+
40
+ function Previewer() {
41
+ return (
42
+ <PrintDesigner
43
+ isPreview
44
+ initialTemplateData={templateData}
45
+ />
46
+ );
47
+ }
48
+ ```
49
+
50
+ ## 组件属性
51
+
52
+ | 属性 | 类型 | 必填 | 说明 |
53
+ |------|------|------|------|
54
+ | `isPreview` | `boolean` | 否 | 是否为预览模式(只读),默认为 `false` |
55
+ | `initialTemplateData` | `TemplateData` | 否 | 初始模板数据,用于加载已有的模板 |
56
+ | `onSave` | `(data: TemplateData) => void` | 否 | 保存回调函数,当用户点击保存按钮时触发 |
57
+ | `onPrint` | `() => void` | 否 | 打印回调函数,当用户点击打印按钮时触发 |
58
+
59
+ ## 类型定义
60
+
61
+ ### TemplateData
62
+
63
+ ```typescript
64
+ interface TemplateData {
65
+ version: string; // 模板版本号
66
+ templateName: string; // 模板名称
67
+ pageSettings: PageSettings; // 页面设置
68
+ components: Omit<CanvasComponent, 'id'>[]; // 组件列表(不包含 ID)
69
+ dataSources: Omit<DataSource, 'id'>[]; // 数据源列表(不包含 ID)
70
+ }
71
+ ```
72
+
73
+ ### DataSource
74
+
75
+ ```typescript
76
+ type DataSourceType = 'object' | 'array';
77
+
78
+ interface DataSource {
79
+ id: string; // 数据源 ID
80
+ name: string; // 数据源名称
81
+ type: DataSourceType; // 数据源类型:'object' 或 'array'
82
+ data: string; // JSON 字符串格式的数据
83
+ createdAt: number; // 创建时间戳
84
+ }
85
+ ```
86
+
87
+ ### CanvasComponent
88
+
89
+ ```typescript
90
+ type CanvasComponentType = 'text' | 'image' | 'table' | 'barcode' | 'line';
91
+
92
+ interface CanvasComponent {
93
+ id: string;
94
+ type: CanvasComponentType;
95
+ x: number; // X 坐标(网格单位)
96
+ y: number; // Y 坐标(网格单位)
97
+ w: number; // 宽度(网格单位)
98
+ h: number; // 高度(网格单位)
99
+ props: Record<string, unknown>; // 组件属性
100
+ tableColumns?: TableColumnConfig[]; // 表格列配置(仅表格组件)
101
+ }
102
+
103
+ interface TableColumnConfig {
104
+ title: string; // 列标题
105
+ fieldPath?: string; // 绑定的字段路径
106
+ width?: number; // 列宽
107
+ }
108
+ ```
109
+
110
+ ## 使用示例
111
+
112
+ ### 完整的工作流程
113
+
114
+ ```tsx
115
+ import { useState } from 'react';
116
+ import PrintDesigner, { TemplateData } from '@operp/print-designer';
117
+
118
+ function App() {
119
+ const [templateData, setTemplateData] = useState<TemplateData | undefined>();
120
+ const [isPreview, setIsPreview] = useState(false);
121
+
122
+ return (
123
+ <div style={{ width: '100vw', height: '100vh' }}>
124
+ <div style={{ position: 'absolute', top: 10, right: 10, zIndex: 100 }}>
125
+ <button onClick={() => setIsPreview(!isPreview)}>
126
+ {isPreview ? '编辑' : '预览'}
127
+ </button>
128
+ </div>
129
+
130
+ <PrintDesigner
131
+ isPreview={isPreview}
132
+ initialTemplateData={templateData}
133
+ onSave={(data) => {
134
+ setTemplateData(data);
135
+ alert('模板已保存');
136
+ }}
137
+ />
138
+ </div>
139
+ );
140
+ }
141
+ ```
142
+
143
+ ### 带数据绑定的示例
144
+
145
+ ```tsx
146
+ import PrintDesigner, { TemplateData } from '@operp/print-designer';
147
+
148
+ function Example() {
149
+ // 定义包含数据源的模板数据
150
+ const templateData: TemplateData = {
151
+ version: '1.0',
152
+ templateName: '商品标签',
153
+ pageSettings: {
154
+ paperSize: 'custom',
155
+ customWidth: 100,
156
+ customHeight: 60,
157
+ orientation: 'portrait',
158
+ marginTop: 5,
159
+ marginRight: 5,
160
+ marginBottom: 5,
161
+ marginLeft: 5,
162
+ showHeader: false,
163
+ headerContent: '',
164
+ showFooter: false,
165
+ footerContent: '',
166
+ showPageNumber: false,
167
+ pageNumberFormat: '',
168
+ },
169
+ components: [
170
+ {
171
+ type: 'text',
172
+ x: 0,
173
+ y: 0,
174
+ w: 15,
175
+ h: 3,
176
+ props: {
177
+ content: '商品名称',
178
+ contentBinding: {
179
+ dataSourceId: 'ds_1',
180
+ fieldPath: 'name',
181
+ staticValue: '商品名称',
182
+ },
183
+ },
184
+ },
185
+ ],
186
+ dataSources: [
187
+ {
188
+ name: '商品信息',
189
+ type: 'object',
190
+ data: '{"name": "iPhone 15", "price": 6999, "barcode": "1234567890123"}',
191
+ },
192
+ ],
193
+ };
194
+
195
+ return (
196
+ <PrintDesigner
197
+ isPreview
198
+ initialTemplateData={templateData}
199
+ />
200
+ );
201
+ }
202
+ ```
203
+
204
+ ### 表格数据绑定示例
205
+
206
+ ```tsx
207
+ import PrintDesigner, { TemplateData } from '@operp/print-designer';
208
+
209
+ function TableExample() {
210
+ const templateData: TemplateData = {
211
+ version: '1.0',
212
+ templateName: '订单清单',
213
+ pageSettings: {
214
+ paperSize: 'A4',
215
+ orientation: 'portrait',
216
+ marginTop: 10,
217
+ marginRight: 10,
218
+ marginBottom: 10,
219
+ marginLeft: 10,
220
+ showHeader: false,
221
+ headerContent: '',
222
+ showFooter: false,
223
+ footerContent: '',
224
+ showPageNumber: false,
225
+ pageNumberFormat: '',
226
+ },
227
+ components: [
228
+ {
229
+ type: 'table',
230
+ x: 0,
231
+ y: 0,
232
+ w: 100,
233
+ h: 30,
234
+ props: {
235
+ tableDataSourceId: 'ds_2',
236
+ },
237
+ tableColumns: [
238
+ { title: '订单号', fieldPath: 'orderId' },
239
+ { title: '商品', fieldPath: 'product' },
240
+ { title: '数量', fieldPath: 'quantity' },
241
+ { title: '单价', fieldPath: 'price' },
242
+ ],
243
+ },
244
+ ],
245
+ dataSources: [
246
+ {
247
+ name: '订单列表',
248
+ type: 'array',
249
+ data: JSON.stringify([
250
+ { orderId: 'A001', product: 'iPhone 15', quantity: 1, price: 6999 },
251
+ { orderId: 'A002', product: 'MacBook Pro', quantity: 1, price: 12999 },
252
+ { orderId: 'A003', product: 'AirPods Pro', quantity: 2, price: 1899 },
253
+ ]),
254
+ },
255
+ ],
256
+ };
257
+
258
+ return <PrintDesigner isPreview initialTemplateData={templateData} />;
259
+ }
260
+ ```
261
+
262
+ ## 样式导入
263
+
264
+ 如果需要导入全局样式(可选,因为组件内置了必要的样式):
265
+
266
+ ```tsx
267
+ import '@operp/print-designer/dist/index.css';
268
+ ```
269
+
270
+ ## 注意事项
271
+
272
+ 1. **peerDependencies**: 该库依赖 `react`、`react-dom`、`antd`、`@ant-design/icons`、`react-grid-layout`、`react-to-print` 和 `zustand`,这些依赖需要您在项目中单独安装。
273
+
274
+ 2. **TypeScript**: 该库使用 TypeScript 编写,自带类型定义。
275
+
276
+ 3. **数据绑定**: 数据源支持对象和数组两种类型:
277
+ - **对象类型**: 适用于文本、条码、图片等组件的数据绑定
278
+ - **数组类型**: 适用于表格组件的数据绑定
279
+
280
+ 4. **预览模式**: 当 `isPreview` 为 `true` 时,组件处于只读模式,用户不能编辑模板,只能预览和打印。
@@ -0,0 +1 @@
1
+ .react-grid-layout{position:relative;transition:height .2s ease}.react-grid-item{transition:all .2s ease;transition-property:left,top,width,height}.react-grid-item img{pointer-events:none;-webkit-user-select:none;user-select:none}.react-grid-item.cssTransforms{transition-property:transform,width,height}.react-grid-item.resizing{transition:none;z-index:1;will-change:width,height}.react-grid-item.react-draggable-dragging{transition:none;z-index:3;will-change:transform}.react-grid-item.dropping{visibility:hidden}.react-grid-item.react-grid-placeholder{background:red;opacity:.2;transition-duration:.1s;z-index:2;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}.react-grid-item.react-grid-placeholder.placeholder-resizing{transition:none}.react-grid-item>.react-resizable-handle{position:absolute;width:20px;height:20px}.react-grid-item>.react-resizable-handle:after{content:"";position:absolute;right:3px;bottom:3px;width:5px;height:5px;border-right:2px solid rgba(0,0,0,.4);border-bottom:2px solid rgba(0,0,0,.4)}.react-resizable-hide>.react-resizable-handle{display:none}.react-grid-item>.react-resizable-handle.react-resizable-handle-sw{bottom:0;left:0;cursor:sw-resize;transform:rotate(90deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-se{bottom:0;right:0;cursor:se-resize}.react-grid-item>.react-resizable-handle.react-resizable-handle-nw{top:0;left:0;cursor:nw-resize;transform:rotate(180deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-ne{top:0;right:0;cursor:ne-resize;transform:rotate(270deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-w,.react-grid-item>.react-resizable-handle.react-resizable-handle-e{top:50%;margin-top:-10px;cursor:ew-resize}.react-grid-item>.react-resizable-handle.react-resizable-handle-w{left:0;transform:rotate(135deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-e{right:0;transform:rotate(315deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-n,.react-grid-item>.react-resizable-handle.react-resizable-handle-s{left:50%;margin-left:-10px;cursor:ns-resize}.react-grid-item>.react-resizable-handle.react-resizable-handle-n{top:0;transform:rotate(225deg)}.react-grid-item>.react-resizable-handle.react-resizable-handle-s{bottom:0;transform:rotate(45deg)}.react-resizable{position:relative}.react-resizable-handle{position:absolute;width:20px;height:20px;background-repeat:no-repeat;background-origin:content-box;box-sizing:border-box;background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2IDYiIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmYwMCIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iNnB4Ij48ZyBvcGFjaXR5PSIwLjMwMiI+PHBhdGggZD0iTSA2IDYgTCAwIDYgTCAwIDQuMiBMIDQgNC4yIEwgNC4yIDQuMiBMIDQuMiAwIEwgNiAwIEwgNiA2IEwgNiA2IFoiIGZpbGw9IiMwMDAwMDAiLz48L2c+PC9zdmc+);background-position:bottom right;padding:0 3px 3px 0}.react-resizable-handle-sw{bottom:0;left:0;cursor:sw-resize;transform:rotate(90deg)}.react-resizable-handle-se{bottom:0;right:0;cursor:se-resize}.react-resizable-handle-nw{top:0;left:0;cursor:nw-resize;transform:rotate(180deg)}.react-resizable-handle-ne{top:0;right:0;cursor:ne-resize;transform:rotate(270deg)}.react-resizable-handle-w,.react-resizable-handle-e{top:50%;margin-top:-10px;cursor:ew-resize}.react-resizable-handle-w{left:0;transform:rotate(135deg)}.react-resizable-handle-e{right:0;transform:rotate(315deg)}.react-resizable-handle-n,.react-resizable-handle-s{left:50%;margin-left:-10px;cursor:ns-resize}.react-resizable-handle-n{top:0;transform:rotate(225deg)}.react-resizable-handle-s{bottom:0;transform:rotate(45deg)}