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 +21 -0
- package/README.md +280 -0
- package/dist/print-designer.css +1 -0
- package/dist/print-designer.es.js +2964 -0
- package/dist/print-designer.umd.js +46 -0
- package/package.json +58 -0
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)}
|