vkedit 2.8.6 → 3.2.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 (127) hide show
  1. package/README.en.md +385 -1193
  2. package/README.md +383 -1190
  3. package/dist/commands/distribute-elements-command.d.ts +13 -0
  4. package/dist/commands/index.d.ts +1 -0
  5. package/dist/components/ContextMenuDelete.vue.d.ts +1 -1
  6. package/dist/components/ui/{select/SelectTrigger.vue.d.ts → VkButton.vue.d.ts} +13 -7
  7. package/dist/components/ui/VkDropdown.vue.d.ts +22 -0
  8. package/dist/components/ui/VkIcon.vue.d.ts +8 -0
  9. package/dist/components/ui/{textarea/Textarea.vue.d.ts → VkInput.vue.d.ts} +6 -4
  10. package/dist/components/ui/VkInputMM.vue.d.ts +17 -0
  11. package/dist/components/ui/VkInputNumber.vue.d.ts +16 -0
  12. package/dist/components/ui/{input-mm/InputMM.vue.d.ts → VkInputNumberMM.vue.d.ts} +8 -6
  13. package/dist/components/ui/{number-field/NumberFieldContent.vue.d.ts → VkLabel.vue.d.ts} +3 -3
  14. package/dist/components/ui/{dropdown-menu/DropdownMenuRadioItem.vue.d.ts → VkSelect.vue.d.ts} +7 -7
  15. package/dist/components/ui/VkSwitch.vue.d.ts +14 -0
  16. package/dist/components/ui/{toggle/Toggle.vue.d.ts → VkToggle.vue.d.ts} +9 -12
  17. package/dist/components/ui/index.d.ts +11 -14
  18. package/dist/core/Editor.vue.d.ts +1 -7
  19. package/dist/{components/ui/button-group/ButtonGroupText.vue.d.ts → core/GraphicToolPanel.vue.d.ts} +14 -12
  20. package/dist/core/HelpGuide.vue.d.ts +2 -0
  21. package/dist/core/PropertyPanel.vue.d.ts +1 -0
  22. package/dist/core/{ScrollbarLayer.vue.d.ts → StatusBar.vue.d.ts} +1 -3
  23. package/dist/core/Toolbar.vue.d.ts +4 -6
  24. package/dist/core/editor-host.d.ts +12 -0
  25. package/dist/create-host.d.ts +1 -1
  26. package/dist/hooks/index.d.ts +0 -1
  27. package/dist/hooks/use-bg-layer.d.ts +6 -0
  28. package/dist/hooks/use-content-layer.d.ts +4 -0
  29. package/dist/hooks/use-ruler-layer.d.ts +6 -1
  30. package/dist/hooks/use-stage-event.d.ts +6 -1
  31. package/dist/hooks/use-stage.d.ts +11 -7
  32. package/dist/hooks/use-zoom.d.ts +20 -1
  33. package/dist/{index-CjPgwxVC.js → index-Bisex1w7.js} +25500 -35357
  34. package/dist/index.d.ts +0 -1
  35. package/dist/{index.es-DekiblxN.js → index.es-CEBX1sXY.js} +1 -1
  36. package/dist/lib/utils.d.ts +1 -2
  37. package/dist/plugins/barcode/Tool.vue.d.ts +2 -1
  38. package/dist/plugins/barcode/barcode.d.ts +4 -0
  39. package/dist/plugins/chart/Tool.vue.d.ts +2 -1
  40. package/dist/plugins/clipboard/clipboard.d.ts +33 -0
  41. package/dist/plugins/clipboard/index.d.ts +1 -0
  42. package/dist/plugins/index.d.ts +2 -0
  43. package/dist/plugins/keydown.d.ts +11 -1
  44. package/dist/plugins/line/Tool.vue.d.ts +2 -1
  45. package/dist/plugins/line/line.d.ts +1 -2
  46. package/dist/plugins/preview/PreviewButton.vue.d.ts +1 -1
  47. package/dist/plugins/qrcode/Tool.vue.d.ts +2 -1
  48. package/dist/plugins/rect/Tool.vue.d.ts +2 -1
  49. package/dist/plugins/rect/rect.d.ts +1 -2
  50. package/dist/plugins/selection.d.ts +5 -0
  51. package/dist/{components/ui/number-field/NumberFieldInput.vue.d.ts → plugins/snap/SnapToggle.vue.d.ts} +4 -3
  52. package/dist/plugins/snap/index.d.ts +2 -0
  53. package/dist/plugins/snap/snap.d.ts +24 -0
  54. package/dist/plugins/table/Tool.vue.d.ts +2 -1
  55. package/dist/plugins/table/table.d.ts +1 -2
  56. package/dist/plugins/text/TextContextMenu.vue.d.ts +1 -1
  57. package/dist/plugins/text/Tool.vue.d.ts +2 -1
  58. package/dist/plugins/text/text.d.ts +1 -0
  59. package/dist/plugins/toolbar-manager.d.ts +3 -1
  60. package/dist/types/base-graphic-element.d.ts +3 -0
  61. package/dist/types/base.d.ts +14 -0
  62. package/dist/types/event-data.d.ts +16 -0
  63. package/dist/types/event-map.d.ts +6 -3
  64. package/dist/utils/geometry.d.ts +38 -0
  65. package/dist/utils/index.d.ts +1 -0
  66. package/dist/vkedit.css +1 -1
  67. package/dist/vkedit.es.js +97 -107
  68. package/package.json +12 -21
  69. package/dist/components/ui/button/Button.vue.d.ts +0 -27
  70. package/dist/components/ui/button/index.d.ts +0 -7
  71. package/dist/components/ui/button-group/ButtonGroup.vue.d.ts +0 -23
  72. package/dist/components/ui/button-group/ButtonGroupSeparator.vue.d.ts +0 -9
  73. package/dist/components/ui/button-group/index.d.ts +0 -8
  74. package/dist/components/ui/dropdown-menu/DropdownMenu.vue.d.ts +0 -22
  75. package/dist/components/ui/dropdown-menu/DropdownMenuCheckboxItem.vue.d.ts +0 -28
  76. package/dist/components/ui/dropdown-menu/DropdownMenuContent.vue.d.ts +0 -36
  77. package/dist/components/ui/dropdown-menu/DropdownMenuGroup.vue.d.ts +0 -18
  78. package/dist/components/ui/dropdown-menu/DropdownMenuItem.vue.d.ts +0 -26
  79. package/dist/components/ui/dropdown-menu/DropdownMenuLabel.vue.d.ts +0 -23
  80. package/dist/components/ui/dropdown-menu/DropdownMenuRadioGroup.vue.d.ts +0 -22
  81. package/dist/components/ui/dropdown-menu/DropdownMenuSeparator.vue.d.ts +0 -7
  82. package/dist/components/ui/dropdown-menu/DropdownMenuShortcut.vue.d.ts +0 -21
  83. package/dist/components/ui/dropdown-menu/DropdownMenuSub.vue.d.ts +0 -22
  84. package/dist/components/ui/dropdown-menu/DropdownMenuSubContent.vue.d.ts +0 -38
  85. package/dist/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue.d.ts +0 -23
  86. package/dist/components/ui/dropdown-menu/DropdownMenuTrigger.vue.d.ts +0 -18
  87. package/dist/components/ui/dropdown-menu/index.d.ts +0 -15
  88. package/dist/components/ui/input/Input.vue.d.ts +0 -12
  89. package/dist/components/ui/input/index.d.ts +0 -1
  90. package/dist/components/ui/input-mm/InputNumberMM.vue.d.ts +0 -14
  91. package/dist/components/ui/input-mm/index.d.ts +0 -2
  92. package/dist/components/ui/input-number/VkInputNumber.vue.d.ts +0 -9
  93. package/dist/components/ui/input-number/index.d.ts +0 -1
  94. package/dist/components/ui/label/Label.vue.d.ts +0 -22
  95. package/dist/components/ui/label/index.d.ts +0 -1
  96. package/dist/components/ui/number-field/NumberField.vue.d.ts +0 -26
  97. package/dist/components/ui/number-field/NumberFieldDecrement.vue.d.ts +0 -22
  98. package/dist/components/ui/number-field/NumberFieldIncrement.vue.d.ts +0 -22
  99. package/dist/components/ui/number-field/index.d.ts +0 -5
  100. package/dist/components/ui/scroll-area/ScrollArea.vue.d.ts +0 -22
  101. package/dist/components/ui/scroll-area/ScrollBar.vue.d.ts +0 -9
  102. package/dist/components/ui/scroll-area/index.d.ts +0 -2
  103. package/dist/components/ui/select/Select.vue.d.ts +0 -25
  104. package/dist/components/ui/select/SelectContent.vue.d.ts +0 -32
  105. package/dist/components/ui/select/SelectGroup.vue.d.ts +0 -18
  106. package/dist/components/ui/select/SelectItem.vue.d.ts +0 -22
  107. package/dist/components/ui/select/SelectItemText.vue.d.ts +0 -18
  108. package/dist/components/ui/select/SelectLabel.vue.d.ts +0 -22
  109. package/dist/components/ui/select/SelectScrollDownButton.vue.d.ts +0 -22
  110. package/dist/components/ui/select/SelectScrollUpButton.vue.d.ts +0 -22
  111. package/dist/components/ui/select/SelectSeparator.vue.d.ts +0 -7
  112. package/dist/components/ui/select/SelectValue.vue.d.ts +0 -18
  113. package/dist/components/ui/select/index.d.ts +0 -11
  114. package/dist/components/ui/separator/Separator.vue.d.ts +0 -10
  115. package/dist/components/ui/separator/index.d.ts +0 -1
  116. package/dist/components/ui/slider/Slider.vue.d.ts +0 -13
  117. package/dist/components/ui/slider/index.d.ts +0 -1
  118. package/dist/components/ui/switch/Switch.vue.d.ts +0 -26
  119. package/dist/components/ui/switch/index.d.ts +0 -1
  120. package/dist/components/ui/textarea/index.d.ts +0 -1
  121. package/dist/components/ui/toggle/index.d.ts +0 -7
  122. package/dist/core/Toolbox.vue.d.ts +0 -23
  123. package/dist/hooks/use-scrollbar-layer.d.ts +0 -47
  124. package/dist/stores/index.d.ts +0 -1
  125. package/dist/stores/use-stage-store.d.ts +0 -40
  126. package/dist/styles/index.css +0 -144
  127. /package/dist/core/{Zoom.vue.d.ts → GuideLayer.vue.d.ts} +0 -0
package/README.md CHANGED
@@ -1,1190 +1,383 @@
1
- # vkedit
2
-
3
- <div align="center">
4
-
5
- [![NPM Version](https://img.shields.io/npm/v/vkedit?style=flat-square)](https://www.npmjs.com/package/vkedit)
6
- [![License](https://img.shields.io/npm/l/vkedit?style=flat-square)](LICENSE)
7
- [![Node.js](https://img.shields.io/node/v/vkedit?style=flat-square)](package.json)
8
-
9
- **Vue3 Konva Plug-in designer**
10
-
11
- 一个功能强大、可扩展的图形编辑器插件库,基于 Vue 3 和 Konva.js 构建。适用于标签模板设计、二维码设计、条码设计、票据设计、名片设计、证书设计等多种场景。
12
-
13
- **[🇺🇸 English Version](README.en.md)**
14
-
15
- </div>
16
-
17
- <div align="center">
18
-
19
- ![项目预览](./截图/image.png)
20
-
21
- </div>
22
-
23
- ---
24
-
25
- ## 📖 项目简介
26
-
27
- **vkedit** 是一个基于 Vue 3 和 Konva.js 的图形编辑器插件化设计库。它提供了一套完整的图形编辑功能,包括多种图形元素支持、插件系统架构、撤销/重做机制、导入/导出功能等。
28
-
29
- 该项目采用插件化架构设计,开发者可以根据需要灵活地启用或禁用各种功能模块,同时支持自定义插件和图形元素的扩展。
30
-
31
- vkedit 特别适用于**标签模板设计**、**二维码设计**、**条码设计**、**票据设计**、**名片设计**、**证书设计**等多种图形设计场景,为开发者提供强大的可视化设计能力。
32
-
33
- - **许可证**: MIT
34
- - **Node.js 要求**: ^20.19.0 || >=22.12.0
35
-
36
- ---
37
-
38
- ## 🚀 安装与使用
39
-
40
- ### 环境要求
41
-
42
- - **Node.js**: ^20.19.0 || >=22.12.0
43
- - **包管理器**: pnpm 10.19.0+
44
-
45
- ### 安装
46
-
47
- ```bash
48
- # 使用 npm
49
- npm install vkedit vue konva vue-konva pinia
50
-
51
- # 使用 pnpm
52
- pnpm add vkedit vue konva vue-konva pinia
53
-
54
- # 使用 yarn
55
- yarn add vkedit vue konva vue-konva pinia
56
- ```
57
-
58
- ### 入口文件示例(main.ts)
59
-
60
- 在项目入口文件 `main.ts` 中,需要正确配置 Vue 应用、Pinia 状态管理和 VueKonva:
61
-
62
- ```typescript
63
- import { createApp } from 'vue'
64
- import { createPinia } from 'pinia'
65
- import App from './App.vue'
66
- import VueKonva from 'vue-konva'
67
- import 'vkedit/dist/vkedit.css' // 导入 vkedit 样式
68
-
69
- const app = createApp(App)
70
-
71
- app.use(createPinia())
72
- app.use(VueKonva)
73
- app.mount('#app')
74
- ```
75
-
76
- ### 基础使用示例
77
-
78
- ```vue
79
- <template>
80
- <Vkedit
81
- :host="host"
82
- :show-toolbox="true"
83
- :show-property-panel="true"
84
- :show-toolbar="true"
85
- />
86
- </template>
87
-
88
- <script setup lang="ts">
89
- import { createEditorHost, Vkedit } from 'vkedit'
90
- import {
91
- RectPlugin,
92
- TextPlugin,
93
- TablePlugin,
94
- QrcodePlugin,
95
- BarcodePlugin,
96
- ChartPlugin,
97
- LinePlugin
98
- } from 'vkedit'
99
-
100
- // 创建编辑器宿主
101
- const host = createEditorHost({
102
- basePropertyPanel: false,
103
- baseCanvasPropertyPanel: true,
104
- exportPlugin: true,
105
- previewPlugin: true,
106
- importPlugin: true
107
- })
108
-
109
- // 安装图形插件
110
- host
111
- .installPlugin('rect-plugin', RectPlugin)
112
- .installPlugin('text-plugin', TextPlugin)
113
- .installPlugin('table-plugin', TablePlugin)
114
- .installPlugin('qr-plugin', QrcodePlugin)
115
- .installPlugin('barcode-plugin', BarcodePlugin)
116
- .installPlugin('chart-plugin', ChartPlugin)
117
- .installPlugin('line-plugin', LinePlugin)
118
-
119
- // 设置画布尺寸(A4 纸张)
120
- host.setStatus({
121
- dpm: 8, // 每毫米点数 (DPI / 25.4)
122
- width: 210 * 8, // A4 宽度 210mm
123
- height: 297 * 8, // A4 高度 297mm
124
- zoom: 0.4 // 缩放级别
125
- })
126
- </script>
127
- ```
128
-
129
- ### 可选配置说明
130
-
131
- `createEditorHost` 函数接受以下配置选项:
132
-
133
- | 选项 | 类型 | 默认值 | 说明 |
134
- | ------------------------- | ------- | ------ | ------------------------ |
135
- | `basePropertyPanel` | boolean | false | 是否启用基础元素属性面板 |
136
- | `baseCanvasPropertyPanel` | boolean | true | 是否启用画布属性面板 |
137
- | `exportPlugin` | boolean | true | 是否启用导出插件 |
138
- | `previewPlugin` | boolean | true | 是否启用预览插件 |
139
- | `importPlugin` | boolean | true | 是否启用导入插件 |
140
-
141
- ---
142
-
143
-
144
- ## 核心特性
145
-
146
- - **🔌 插件化架构**: 灵活的插件系统,可按需启用或禁用功能模块
147
- - **🎨 多图形元素支持**:
148
- - 矩形 (Rectangle)
149
- - 文本 (Text)
150
- - 线条 (Line)
151
- - 表格 (Table)
152
- - 二维码 (QR Code)
153
- - 条形码 (Barcode)
154
- - 图表 (Chart)
155
- - **📥📤 导入/导出功能**: 支持 JSON 格式的设计数据导入导出
156
- - **↩️↪️ 撤销/重做机制**: 基于命令模式的完整历史记录管理
157
- - **🏗️ 多图层画布系统**: 支持背景层、内容层、选择层、标尺层、滚动条层
158
- - **🔍 缩放与标尺**: 精确的画布缩放和尺标显示
159
- - **📐 对齐工具**: 支持元素的多种对齐和分布操作
160
- - **📋 上下文菜单**: 右键菜单支持快捷操作
161
- - **🎯 事件驱动**: 完善的事件系统支持插件间通信
162
- - **🏷️ 丰富的应用场景**: 标签模板设计、二维码设计、条码设计、票据设计、名片设计、证书设计等
163
-
164
- ---
165
-
166
- ## 🎯 应用场景
167
-
168
- vkedit 可广泛应用于以下场景:
169
-
170
- | 应用场景 | 适用功能 | 典型用途 |
171
- | ---------------- | -------------------------------- | ---------------------------------------- |
172
- | **标签模板设计** | 二维码、条形码、文本、矩形、表格 | 产品标签、物流标签、价格标签、库存标签 |
173
- | **二维码设计** | 二维码插件、文本、图形元素 | 营销二维码、支付二维码、信息二维码 |
174
- | **条码设计** | 条形码插件、文本、图形元素 | 商品条码、图书条码、库存条码、物流追踪码 |
175
- | **票据设计** | 表格、文本、矩形、线条 | 发票、收据、凭证、报表 |
176
- | **名片设计** | 文本、矩形、图像元素 | 个人名片、公司名片、VIP |
177
- | **证书设计** | 文本、矩形、表格、图像元素 | 毕业证书、荣誉证书、资格证书 |
178
- | **海报设计** | 多种图形元素组合 | 宣传海报、活动海报、产品海报 |
179
- | **表单设计** | 表格、文本、线条 | 调查表、报名表、申请表 |
180
-
181
- ### 典型使用案例
182
-
183
- #### 1. 标签模板设计
184
- ```typescript
185
- // 创建标签编辑器
186
- const host = createEditorHost({
187
- exportPlugin: true,
188
- previewPlugin: true
189
- })
190
-
191
- // 安装标签设计所需插件
192
- host
193
- .installPlugin('rect-plugin', RectPlugin) // 边框、背景
194
- .installPlugin('text-plugin', TextPlugin) // 文本信息
195
- .installPlugin('qr-plugin', QrcodePlugin) // 产品二维码
196
- .installPlugin('barcode-plugin', BarcodePlugin)// 商品条形码
197
- .installPlugin('table-plugin', TablePlugin) // 表格数据
198
-
199
- // 设置标签尺寸(标准标签 100mm x 60mm)
200
- host.setStatus({
201
- dpm: 8,
202
- width: 100 * 8,
203
- height: 60 * 8
204
- })
205
- ```
206
-
207
- #### 2. 二维码设计
208
- ```typescript
209
- // 创建二维码设计器
210
- const host = createEditorHost({
211
- exportPlugin: true
212
- })
213
-
214
- host
215
- .installPlugin('text-plugin', TextPlugin) // 说明文字
216
- .installPlugin('qr-plugin', QrcodePlugin) // 二维码元素
217
- .installPlugin('rect-plugin', RectPlugin) // 装饰边框
218
-
219
- // 设置画布尺寸
220
- host.setStatus({
221
- dpm: 8,
222
- width: 150 * 8,
223
- height: 150 * 8
224
- })
225
- ```
226
-
227
- #### 3. 条码设计
228
- ```typescript
229
- // 创建条码设计器
230
- const host = createEditorHost({
231
- exportPlugin: true,
232
- previewPlugin: true
233
- })
234
-
235
- host
236
- .installPlugin('barcode-plugin', BarcodePlugin)// 条形码
237
- .installPlugin('text-plugin', TextPlugin) // 商品信息
238
- .installPlugin('line-plugin', LinePlugin) // 分割线
239
-
240
- // 设置条码标签尺寸
241
- host.setStatus({
242
- dpm: 8,
243
- width: 80 * 8,
244
- height: 50 * 8
245
- })
246
- ```
247
-
248
- ---
249
-
250
- ## 🛠️ 技术栈
251
-
252
- ### 核心框架与库
253
-
254
- | 依赖 | 版本 | 说明 |
255
- | ---------------------------------------------------- | ------- | ---------------------- |
256
- | [Vue](https://vuejs.org/) | ^3.5.18 | 渐进式 JavaScript 框架 |
257
- | [Konva.js](https://konvajs.org/) | ^10.0.2 | 2D Canvas 库 |
258
- | [vue-konva](https://www.npmjs.com/package/vue-konva) | ^3.2.6 | Vue 3 绑定 |
259
- | [Pinia](https://pinia.vuejs.org/) | ^3.0.3 | Vue 状态管理 |
260
- | [TypeScript](https://www.typescriptlang.org/) | ~5.8.0 | 类型安全 |
261
- | [Vite](https://vitejs.dev/) | ^7.0.6 | 构建工具 |
262
-
263
- ### 运行时依赖
264
-
265
- - **@vueuse/core**: Vue 组合式工具库
266
- - **echarts**: 图表库
267
- - **exceljs**: Excel 文件处理
268
- - **jsbarcode**: 条形码生成
269
- - **jspdf**: PDF 导出
270
- - **lodash**: JavaScript 实用工具库
271
- - **qrcode**: 二维码生成
272
- - **uuid**: 唯一标识符生成
273
-
274
- ### 开发工具
275
-
276
- - ESLint & Prettier: 代码规范与格式化
277
- - Tailwind CSS v4: 原子化 CSS 框架
278
- - Reka UI: UI 组件库
279
-
280
- ---
281
-
282
- ## 🏗️ 架构设计
283
-
284
- ### 编辑器宿主机制
285
-
286
- `EditorHost` 是整个编辑器的核心宿主类,负责管理所有插件、状态和事件。
287
-
288
- ```mermaid
289
- classDiagram
290
- class EditorHost {
291
- -Map~string,IEditorPlugin~ plugins
292
- -Map~string,Function[]~ events
293
- -ICommand[] commandStack
294
- -number currentCommandIndex
295
- +IEditorState status
296
- +installPlugin() EditorHost
297
- +uninstallPlugin() EditorHost
298
- +getPlugin() IEditorPlugin
299
- +executeCommand() void
300
- +undo() void
301
- +redo() void
302
- +emit() void
303
- +on() void
304
- +off() void
305
- +toJSON() string
306
- +loadJSON() void
307
- }
308
- ```
309
-
310
- ### 插件系统架构
311
-
312
- 所有功能通过插件实现,插件可以注册到宿主并响应事件。
313
-
314
- ```mermaid
315
- graph TD
316
- A[EditorHost] --> B[ToolbarManagerPlugin]
317
- A --> C[GraphicToolManagerPlugin]
318
- A --> D[ElementManagerPlugin]
319
- A --> E[SelectionPlugin]
320
- A --> F[PropertyPanelManagerPlugin]
321
- A --> G[ExportPlugin]
322
- A --> H[ImportPlugin]
323
- A --> I[PreviewPlugin]
324
- A --> J[KeyDownPlugin]
325
- A --> K[AlignPlugin]
326
-
327
- A --> L[RectPlugin]
328
- A --> M[TextPlugin]
329
- A --> N[TablePlugin]
330
- A --> O[QrcodePlugin]
331
- A --> P[BarcodePlugin]
332
- A --> Q[ChartPlugin]
333
- A --> R[LinePlugin]
334
- ```
335
-
336
- ### 命令模式(撤销/重做)
337
-
338
- 所有可撤销操作都通过命令实现,支持命令历史管理。
339
-
340
- ```mermaid
341
- sequenceDiagram
342
- participant User
343
- participant EditorHost
344
- participant Command
345
- participant Stack
346
-
347
- User->>EditorHost: executeCommand(cmd)
348
- EditorHost->>Command: execute()
349
- Command-->>EditorHost: 执行完成
350
- EditorHost->>Stack: push(cmd)
351
-
352
- User->>EditorHost: undo()
353
- EditorHost->>Stack: get current cmd
354
- Stack-->>EditorHost: command
355
- EditorHost->>Command: undo()
356
-
357
- User->>EditorHost: redo()
358
- EditorHost->>Stack: get next cmd
359
- Stack-->>EditorHost: command
360
- EditorHost->>Command: redo()
361
- ```
362
-
363
- ### 事件驱动机制
364
-
365
- 编辑器通过事件系统实现插件间的松耦合通信。
366
-
367
- ```mermaid
368
- flowchart LR
369
- A[插件1] -- emit --> B[EventBus]
370
- C[插件2] -- on --> B
371
- D[插件3] -- on --> B
372
- B -- notify --> C
373
- B -- notify --> D
374
- ```
375
-
376
- ### 图层系统
377
-
378
- ```mermaid
379
- graph LR
380
- A[Stage] --> B[BgLayer 背景层]
381
- A --> C[ContentLayer 内容层]
382
- A --> D[SelectionLayer 选择层]
383
- A --> E[RulerLayer 标尺层]
384
- A --> F[ScrollbarLayer 滚动条层]
385
- ```
386
-
387
- ---
388
-
389
- ## 📁 项目结构
390
-
391
- ```
392
- vkedit/
393
- ├── src/
394
- │ ├── commands/ # 命令模式实现
395
- │ │ ├── base-command.ts
396
- │ │ ├── add-element-command.ts
397
- │ │ ├── remove-element-command.ts
398
- │ │ ├── transform-element-command.ts
399
- │ │ ├── update-property-command.ts
400
- │ │ ├── batch-command.ts
401
- │ │ ├── align-elements-command.ts
402
- │ │ └── ...
403
- │ ├── components/ # Vue 组件
404
- │ │ ├── ui/ # UI 统一组件库
405
- │ │ │ ├── button/
406
- │ │ │ ├── dropdown-menu/
407
- │ │ │ ├── input/
408
- │ │ │ ├── select/
409
- │ │ │ └── ...
410
- │ │ ├── BaseElementPropertyPanel.vue
411
- │ │ ├── CanvasPropertyPanel.vue
412
- │ │ └── ...
413
- │ ├── core/ # 核心编辑器组件
414
- │ │ ├── Editor.vue
415
- │ │ ├── editor-host.ts
416
- │ │ ├── StageView.vue
417
- │ │ ├── Toolbar.vue
418
- │ │ ├── PropertyPanel.vue
419
- │ │ ├── BgLayer.vue
420
- │ │ ├── ContentLayer.vue
421
- │ │ ├── SelectionLayer.vue
422
- │ │ ├── RulerLayer.vue
423
- │ │ ├── ScrollbarLayer.vue
424
- │ │ ├── ContextMenu.vue
425
- │ │ ├── Zoom.vue
426
- │ │ └── Toolbox.vue
427
- │ ├── hooks/ # Vue 组合式函数
428
- │ │ ├── use-host-state.ts
429
- │ │ ├── use-bg-layer.ts
430
- │ │ ├── use-content-layer.ts
431
- │ │ ├── use-selection-layer.ts
432
- │ │ ├── use-ruler-layer.ts
433
- │ │ ├── use-scrollbar-layer.ts
434
- │ │ ├── use-zoom.ts
435
- │ │ └── use-stage-event.ts
436
- │ ├── plugins/ # 插件系统
437
- │ │ ├── element-manager.ts
438
- │ │ ├── graphic-tool-manager.ts
439
- │ │ ├── graphic-manager.ts
440
- │ │ ├── selection.ts
441
- │ │ ├── toolbar-manager.ts
442
- │ │ ├── keydown.ts
443
- │ │ ├── align/
444
- │ │ │ ├── Align.vue
445
- │ │ │ └── align.ts
446
- │ │ ├── export/
447
- │ │ │ ├── Export.vue
448
- │ │ │ └── export.ts
449
- │ │ ├── import/
450
- │ │ │ ├── Import.vue
451
- │ │ │ └── import.ts
452
- │ │ ├── preview/
453
- │ │ │ ├── PreviewButton.vue
454
- │ │ │ └── preview.ts
455
- │ │ ├── rect/
456
- │ │ │ ├── RectPlugin.ts
457
- │ │ │ ├── Shape.vue
458
- │ │ │ ├── PropertyPanel.vue
459
- │ │ │ └── Tool.vue
460
- │ │ ├── text/
461
- │ │ ├── table/
462
- │ │ ├── qrcode/
463
- │ │ ├── barcode/
464
- │ │ ├── chart/
465
- │ │ ├── line/
466
- │ │ └── context-menu-manager/
467
- │ ├── stores/ # Pinia 状态管理
468
- │ ├── types/ # TypeScript 类型定义
469
- │ │ ├── base-graphic-element.ts
470
- │ │ ├── base-graphic-type.ts
471
- │ │ ├── base-plugin.ts
472
- │ │ ├── event-map.ts
473
- │ │ ├── event-data.ts
474
- │ │ └── ...
475
- │ ├── styles/ # 样式文件
476
- │ ├── create-host.ts # 宿主创建函数
477
- │ └── index.ts # 入口文件
478
- ├── playground/ # 示例项目
479
- │ ├── App.vue
480
- │ └── main.ts
481
- ├── package.json
482
- ├── vite.config.ts
483
- ├── tsconfig.json
484
- └── README.md
485
- ```
486
-
487
- ---
488
-
489
- ## 🔌 可用插件列表
490
-
491
- ### 核心插件
492
-
493
- | 插件名称 | 说明 |
494
- | ------------------------------ | -------------------------------- |
495
- | **ToolbarManagerPlugin** | 工具栏管理器,提供顶部工具栏功能 |
496
- | **GraphicToolManagerPlugin** | 图形工具管理器,管理图形绘制工具 |
497
- | **GraphicManagerPlugin** | 图形管理器,统一管理所有图形元素 |
498
- | **ElementManagerPlugin** | 元素管理器,管理元素的生命周期 |
499
- | **SelectionPlugin** | 选择插件,处理元素选择和多选操作 |
500
- | **PropertyPanelManagerPlugin** | 属性面板管理器,动态渲染属性面板 |
501
- | **KeyDownPlugin** | 键盘事件插件,处理快捷键 |
502
- | **AlignPlugin** | 对齐插件,提供元素对齐和分布功能 |
503
- | **ContextMenuManagerPlugin** | 上下文菜单管理器 |
504
-
505
- ### 功能插件
506
-
507
- | 插件名称 | 说明 |
508
- | ----------------- | ----------------------------------------------- |
509
- | **ExportPlugin** | 导出插件,支持导出为 JSON、PNG、JPG、PDF 等格式 |
510
- | **ImportPlugin** | 导入插件,支持从 JSON 文件导入设计数据 |
511
- | **PreviewPlugin** | 预览插件,提供设计预览功能 |
512
-
513
- ### 图形插件
514
-
515
- | 插件名称 | 图形类型 | 说明 |
516
- | ----------------- | -------- | ------------------------------------------- |
517
- | **RectPlugin** | 矩形 | 可拖拽、缩放、调整填充和边框的矩形元素 |
518
- | **TextPlugin** | 文本 | 支持字体、大小、颜色、对齐方式的文本元素 |
519
- | **LinePlugin** | 线条 | 支持起点、终点、颜色、宽度的线条元素 |
520
- | **TablePlugin** | 表格 | 支持行列、边框、文字对齐的表格元素 |
521
- | **QrcodePlugin** | 二维码 | 可生成可配置的二维码元素 |
522
- | **BarcodePlugin** | 条形码 | 支持多种格式(EAN-13、CODE-128 等)的条形码 |
523
- | **ChartPlugin** | 图表 | 基于 ECharts 的图表元素 |
524
-
525
- ---
526
-
527
- ## 📚 API 参考
528
-
529
- ### createEditorHost()
530
-
531
- 创建编辑器宿主实例并安装核心插件。
532
-
533
- ```typescript
534
- function createEditorHost(options: IOptions): EditorHost
535
- ```
536
-
537
- **参数:**
538
-
539
- ```typescript
540
- interface IOptions {
541
- basePropertyPanel?: boolean // 是否启用基础元素属性面板
542
- baseCanvasPropertyPanel?: boolean // 是否启用画布属性面板
543
- exportPlugin?: boolean // 是否启用导出插件
544
- previewPlugin?: boolean // 是否启用预览插件
545
- importPlugin?: boolean // 是否启用导入插件
546
- }
547
- ```
548
-
549
- **返回:** `EditorHost` 实例
550
-
551
- ---
552
-
553
- ### EditorHost 类方法
554
-
555
- #### 插件管理
556
-
557
- | 方法 | 说明 |
558
- | ---------------------------------- | ------------ |
559
- | `installPlugin(name, pluginClass)` | 安装插件 |
560
- | `uninstallPlugin(pluginName)` | 卸载插件 |
561
- | `getPlugin<T>(pluginName)` | 获取插件实例 |
562
-
563
- ```typescript
564
- // 安装插件
565
- host.installPlugin('rect-plugin', RectPlugin)
566
-
567
- // 卸载插件
568
- host.uninstallPlugin('rect-plugin')
569
-
570
- // 获取插件实例
571
- const rectPlugin = host.getPlugin<RectPlugin>('rect-plugin')
572
- ```
573
-
574
- #### 命令操作
575
-
576
- | 方法 | 说明 |
577
- | ------------------------- | -------- |
578
- | `executeCommand(command)` | 执行命令 |
579
- | `undo()` | 撤销 |
580
- | `redo()` | 重做 |
581
-
582
- ```typescript
583
- import { AddElementCommand } from 'vkedit'
584
-
585
- // 执行命令
586
- const command = new AddElementCommand(element, host)
587
- host.executeCommand(command)
588
-
589
- // 撤销
590
- host.undo()
591
-
592
- // 重做
593
- host.redo()
594
- ```
595
-
596
- #### 事件系统
597
-
598
- | 方法 | 说明 |
599
- | ---------------------- | -------- |
600
- | `emit(event, payload)` | 触发事件 |
601
- | `on(event, handler)` | 订阅事件 |
602
- | `off(event, handler)` | 取消订阅 |
603
-
604
- ```typescript
605
- // 订阅事件
606
- host.on('element:added', (payload) => {
607
- console.log('元素已添加:', payload)
608
- })
609
-
610
- // 触发事件
611
- host.emit('element:added', { element: myElement })
612
-
613
- // 取消订阅
614
- host.off('element:added')
615
- ```
616
-
617
- #### 状态管理
618
-
619
- | 属性 | 类型 | 说明 |
620
- | -------- | -------------- | ------------------ |
621
- | `status` | `IEditorState` | 编辑器状态(只读) |
622
-
623
- ```typescript
624
- interface IEditorState {
625
- zoom: number // 缩放级别
626
- currentTool: string // 当前工具
627
- snapToGrid: boolean // 是否吸附网格
628
- showGrid: boolean // 是否显示网格
629
- width: number // 画布宽度(像素)
630
- height: number // 画布高度(像素)
631
- wmm: number // 画布宽度(毫米)
632
- hmm: number // 画布高度(毫米)
633
- dpm: number // 每毫米点数
634
- }
635
- ```
636
-
637
- ```typescript
638
- // 更新状态
639
- host.setStatus({
640
- zoom: 1,
641
- width: 800,
642
- height: 600
643
- })
644
-
645
- // 读取状态
646
- console.log(host.status.zoom)
647
- ```
648
-
649
- #### 序列化
650
-
651
- | 方法 | 说明 |
652
- | ------------------- | ------------------ |
653
- | `toJSON()` | 导出为 JSON 字符串 |
654
- | `loadJSON(jsonStr)` | 从 JSON 字符串导入 |
655
-
656
- ```typescript
657
- // 导出
658
- const json = host.toJSON()
659
-
660
- // 导入
661
- host.loadJSON(json)
662
- ```
663
-
664
- ---
665
-
666
- ## 🎯 事件系统
667
-
668
- 编辑器提供丰富的事件类型,支持插件间的松耦合通信。
669
-
670
- ### 生命周期事件
671
-
672
- | 事件名 | 说明 |
673
- | ---------------- | -------------- |
674
- | `editor:ready` | 编辑器准备就绪 |
675
- | `editor:destroy` | 编辑器销毁 |
676
- | `editor:reset` | 编辑器重置 |
677
-
678
- ### 文件操作事件
679
-
680
- | 事件名 | 说明 |
681
- | ---------------------- | ---------------- |
682
- | `file:new` | 新建文件 |
683
- | `file:open` | 打开文件 |
684
- | `file:save` | 保存文件 |
685
- | `file:save-as` | 另存为 |
686
- | `file:export` | 导出文件 |
687
- | `file:import` | 导入文件 |
688
- | `file:loaded` | 文件已加载 |
689
- | `file:saved` | 文件已保存 |
690
- | `file:modified-change` | 文件修改状态变化 |
691
-
692
- ### 舞台交互事件
693
-
694
- | 事件名 | 说明 |
695
- | ------------------- | ---------- |
696
- | `stage:mousedown` | 鼠标按下 |
697
- | `stage:mousemove` | 鼠标移动 |
698
- | `stage:mouseup` | 鼠标松开 |
699
- | `stage:click` | 鼠标点击 |
700
- | `stage:dblclick` | 鼠标双击 |
701
- | `stage:contextmenu` | 上下文菜单 |
702
- | `stage:wheel` | 鼠标滚轮 |
703
- | `stage:dragstart` | 拖拽开始 |
704
- | `stage:dragend` | 拖拽结束 |
705
- | `stage:redraw` | 舞台重绘 |
706
-
707
- ### 键盘事件
708
-
709
- | 事件名 | 说明 |
710
- | ---------------------- | -------- |
711
- | `stage:keydown` | 键盘按下 |
712
- | `stage:keydown-delete` | 删除键 |
713
- | `stage:keydown-left` | 左箭头 |
714
- | `stage:keydown-right` | 右箭头 |
715
- | `stage:keydown-up` | 上箭头 |
716
- | `stage:keydown-down` | 下箭头 |
717
-
718
- ### 图形元素事件
719
-
720
- | 事件名 | 说明 |
721
- | --------------------------- | ------------ |
722
- | `element:registered` | 元素类型注册 |
723
- | `element:unregistered` | 元素类型注销 |
724
- | `element:added` | 元素添加 |
725
- | `element:removed` | 元素移除 |
726
- | `element:selected` | 元素选中 |
727
- | `element:deselected` | 元素取消选中 |
728
- | `element:transformed` | 元素变换 |
729
- | `element:updated` | 元素更新 |
730
- | `element:copied` | 元素复制 |
731
- | `element:pasted` | 元素粘贴 |
732
- | `element:cloned` | 元素克隆 |
733
- | `element:locked-change` | 锁定状态变化 |
734
- | `element:visibility-change` | 可见性变化 |
735
- | `element:zindex-change` | 层级变化 |
736
-
737
- ### 选择事件
738
-
739
- | 事件名 | 说明 |
740
- | ------------------------ | -------- |
741
- | `selection:changed` | 选择变化 |
742
- | `selection:cleared` | 清除选择 |
743
- | `selection:multi-change` | 多选变化 |
744
-
745
- ### 视图事件
746
-
747
- | 事件名 | 说明 |
748
- | ------------------------------ | ---------- |
749
- | `view:zoom-change` | 缩放变化 |
750
- | `view:pan` | 平移 |
751
- | `view:zoom-to` | 适应视图 |
752
- | `view:reset` | 重置视图 |
753
- | `view:grid-visibility-change` | 网格可见性 |
754
- | `view:snap-change` | 吸附变化 |
755
- | `view:ruler-visibility-change` | 标尺可见性 |
756
-
757
- ### 图层事件
758
-
759
- | 事件名 | 说明 |
760
- | ------------------------- | ------------ |
761
- | `layer:added` | 图层添加 |
762
- | `layer:removed` | 图层移除 |
763
- | `layer:order-changed` | 图层顺序变化 |
764
- | `layer:visibility-change` | 图层可见性 |
765
- | `layer:locked-change` | 图层锁定 |
766
- | `layer:active-change` | 图层激活 |
767
-
768
- ### 命令历史事件
769
-
770
- | 事件名 | 说明 |
771
- | ------------------ | -------- |
772
- | `command:executed` | 命令执行 |
773
- | `command:undone` | 命令撤销 |
774
- | `command:redone` | 命令重做 |
775
- | `history:changed` | 历史变化 |
776
- | `history:cleared` | 历史清除 |
777
-
778
- ### 插件系统事件
779
-
780
- | 事件名 | 说明 |
781
- | --------------------- | -------- |
782
- | `plugin:registered` | 插件注册 |
783
- | `plugin:unregistered` | 插件注销 |
784
- | `plugin:activated` | 插件激活 |
785
- | `plugin:deactivated` | 插件停用 |
786
- | `plugin:loaded` | 插件加载 |
787
- | `plugin:error` | 插件错误 |
788
-
789
- ### 对齐分布事件
790
-
791
- | 事件名 | 说明 |
792
- | --------------------- | ------------ |
793
- | `elements:align` | 元素对齐 |
794
- | `elements:distribute` | 元素分布 |
795
- | `elements:group` | 元素组合 |
796
- | `elements:ungroup` | 元素取消组合 |
797
- | `elements:layer` | 图层操作 |
798
-
799
- ### 状态管理事件
800
-
801
- | 事件名 | 说明 |
802
- | ---------------------- | -------- |
803
- | `host:status-changed` | 状态变化 |
804
- | `host:status-saved` | 状态保存 |
805
- | `host:status-restored` | 状态恢复 |
806
-
807
- ### 序列化事件
808
-
809
- | 事件名 | 说明 |
810
- | ------------------------- | -------------- |
811
- | `host:load-json:start` | 加载 JSON 开始 |
812
- | `host:load-json:complete` | 加载 JSON 完成 |
813
- | `host:load-json:error` | 加载 JSON 错误 |
814
- | `host:to-json:start` | 导出 JSON 开始 |
815
- | `host:to-json:complete` | 导出 JSON 完成 |
816
- | `host:to-json:error` | 导出 JSON 错误 |
817
-
818
- ### 事件使用示例
819
-
820
- ```typescript
821
- import type { ElementEventData, SelectionEventData } from 'vkedit'
822
-
823
- // 监听元素添加事件
824
- host.on('element:added', (payload: ElementEventData) => {
825
- console.log('新元素添加:', payload.element)
826
- })
827
-
828
- // 监听选择变化事件
829
- host.on('selection:changed', (payload: SelectionEventData) => {
830
- console.log('选中元素数量:', payload.selectedIds.length)
831
- })
832
-
833
- // 监听状态变化
834
- host.on('host:status-changed', (payload) => {
835
- console.log('状态已更新:', payload.status)
836
- })
837
-
838
- // 触发自定义事件
839
- host.emit('custom:my-event', { data: 'some data' })
840
- ```
841
-
842
- ### 扩展事件系统
843
-
844
- 开发者可以通过模块声明扩展事件映射:
845
-
846
- ```typescript
847
- declare module '@/types' {
848
- interface EventMap {
849
- 'my-plugin:some-event': (payload: MyEventData) => void
850
- }
851
- }
852
-
853
- interface MyEventData {
854
- id: string
855
- value: number
856
- }
857
-
858
- // 使用扩展事件
859
- host.emit('my-plugin:some-event', { id: '123', value: 42 })
860
-
861
- // 监听扩展事件
862
- host.on('my-plugin:some-event', (payload) => {
863
- console.log(payload.value)
864
- })
865
- ```
866
-
867
- ---
868
-
869
- ## 📝 命令系统
870
-
871
- vkedit 使用命令模式实现可撤销的操作。所有修改编辑器状态的操作都应通过命令执行。
872
-
873
- ### 可用命令类型
874
-
875
- | 命令类 | 说明 |
876
- | ------------------------- | ---------------------------- |
877
- | `AddElementCommand` | 添加元素 |
878
- | `RemoveElementCommand` | 移除元素 |
879
- | `TransformElementCommand` | 变换元素(位置、大小、旋转) |
880
- | `UpdatePropertyCommand` | 更新元素属性 |
881
- | `ClearSelectionCommand` | 清除选择 |
882
- | `BatchCommand` | 批量命令 |
883
- | `ChangeLayerOrderCommand` | 改变图层顺序 |
884
- | `AlignElementsCommand` | 对齐元素 |
885
-
886
- ### 命令执行
887
-
888
- ```typescript
889
- import { AddElementCommand, TransformElementCommand } from 'vkedit'
890
-
891
- // 创建并执行命令
892
- const command = new AddElementCommand(element, host)
893
- host.executeCommand(command)
894
- ```
895
-
896
- ### 撤销和重做
897
-
898
- ```typescript
899
- // 撤销上一步操作
900
- host.undo()
901
-
902
- // 重做已撤销的操作
903
- host.redo()
904
- ```
905
-
906
- ### 批量命令
907
-
908
- ```typescript
909
- import { BatchCommand } from 'vkedit'
910
-
911
- const commands = [
912
- new UpdatePropertyCommand(element1, 'x', 100),
913
- new UpdatePropertyCommand(element2, 'y', 200)
914
- ]
915
-
916
- const batchCommand = new BatchCommand(commands)
917
- host.executeCommand(batchCommand)
918
- ```
919
-
920
- ---
921
-
922
- ## 💻 开发指南
923
-
924
- ### 开发环境设置
925
-
926
- ```bash
927
- # 克隆仓库
928
- git clone https://github.com/pwg-code/vkedit.git
929
- cd vkedit
930
-
931
- # 安装依赖(推荐使用 pnpm)
932
- pnpm install
933
-
934
- # 启动开发服务器
935
- pnpm dev
936
-
937
- # 类型检查
938
- pnpm type-check
939
-
940
- # 代码检查和修复
941
- pnpm lint
942
-
943
- # 代码格式化
944
- pnpm format
945
-
946
- # 构建生产版本
947
- pnpm build
948
- ```
949
-
950
- ### 可用脚本命令
951
-
952
- | 命令 | 说明 |
953
- | ----------------- | ------------------------ |
954
- | `pnpm dev` | 启动开发服务器 |
955
- | `pnpm build` | 构建生产版本 |
956
- | `pnpm build-only` | 仅构建(不运行类型检查) |
957
- | `pnpm preview` | 预览生产构建 |
958
- | `pnpm type-check` | TypeScript 类型检查 |
959
- | `pnpm lint` | ESLint 检查并自动修复 |
960
- | `pnpm format` | Prettier 格式化代码 |
961
- | `pnpm build:css` | 构建 CSS 文件 |
962
-
963
- ### 开发自定义插件
964
-
965
- 自定义插件需要实现 [`IEditorPlugin`](src/types/base-plugin.ts:5) 接口:
966
-
967
- ```typescript
968
- import type { IEditorPlugin } from 'vkedit'
969
-
970
- export class MyCustomPlugin implements IEditorPlugin {
971
- constructor(private host: EditorHost) {}
972
-
973
- install(host: EditorHost): void {
974
- // 监听事件
975
- host.on('element:added', this.onElementAdded)
976
-
977
- // 注册工具
978
- host.emit('tool:registered', {
979
- name: 'my-tool',
980
- icon: 'my-icon',
981
- render: () => MyToolComponent
982
- })
983
- }
984
-
985
- uninstall(): void {
986
- // 清理资源
987
- this.host.off('element:added', this.onElementAdded)
988
- }
989
-
990
- private onElementAdded = (payload: any) => {
991
- console.log('元素已添加:', payload)
992
- }
993
- }
994
-
995
- // 使用自定义插件
996
- host.installPlugin('my-custom-plugin', MyCustomPlugin)
997
- ```
998
-
999
- ### 自定义图形元素
1000
-
1001
- 创建自定义图形元素需要继承 [`BaseGraphicElement`](src/types/base-graphic-element.ts:5):
1002
-
1003
- ```typescript
1004
- import { BaseGraphicElement } from 'vkedit'
1005
-
1006
- export class MyCustomElement extends BaseGraphicElement {
1007
- constructor(
1008
- id: string,
1009
- public width: number = 100,
1010
- public height: number = 100,
1011
- public fill: string = '#ff0000'
1012
- ) {
1013
- super(id, 'my-custom')
1014
- }
1015
-
1016
- serialize(): Record<string, any> {
1017
- return {
1018
- ...super.serialize(),
1019
- width: this.width,
1020
- height: this.height,
1021
- fill: this.fill
1022
- }
1023
- }
1024
-
1025
- deserialize(data: Record<string, any>): void {
1026
- super.deserialize(data)
1027
- this.width = data.width
1028
- this.height = data.height
1029
- this.fill = data.fill
1030
- }
1031
-
1032
- render() {
1033
- return {
1034
- tag: 'rect',
1035
- props: {
1036
- x: this.x,
1037
- y: this.y,
1038
- width: this.width,
1039
- height: this.height,
1040
- fill: this.fill
1041
- }
1042
- }
1043
- }
1044
- }
1045
- ```
1046
-
1047
- ---
1048
-
1049
- ## ❓ 常见问题(FAQ)
1050
-
1051
- ### Q: 如何设置画布尺寸为 A4 纸张?
1052
-
1053
- ```typescript
1054
- host.setStatus({
1055
- dpm: 8, // 每毫米点数(DPI / 25.4)
1056
- width: 210 * 8, // A4 宽度 210mm
1057
- height: 297 * 8 // A4 高度 297mm
1058
- })
1059
- ```
1060
-
1061
- ### Q: 如何导出设计数据?
1062
-
1063
- ```typescript
1064
- // 导出为 JSON
1065
- const json = host.toJSON()
1066
-
1067
- // 导出为图片(需要使用 ExportPlugin)
1068
- host.getPlugin('export-plugin').exportAsPNG()
1069
- ```
1070
-
1071
- ### Q: 如何监听元素选择变化?
1072
-
1073
- ```typescript
1074
- host.on('selection:changed', (payload) => {
1075
- const selectedIds = payload.selectedIds
1076
- console.log('当前选中元素:', selectedIds)
1077
- })
1078
- ```
1079
-
1080
- ### Q: 如何批量更新多个元素的属性?
1081
-
1082
- ```typescript
1083
- const batchCommand = new BatchCommand([
1084
- new UpdatePropertyCommand(element1, 'fill', '#ff0000'),
1085
- new UpdatePropertyCommand(element2, 'fill', '#00ff00'),
1086
- new UpdatePropertyCommand(element3, 'fill', '#0000ff')
1087
- ])
1088
- host.executeCommand(batchCommand)
1089
- ```
1090
-
1091
- ### Q: 如何禁用所有核心插件并自定义配置?
1092
-
1093
- ```typescript
1094
- const host = createEditorHost({
1095
- basePropertyPanel: false,
1096
- baseCanvasPropertyPanel: false,
1097
- exportPlugin: false,
1098
- previewPlugin: false,
1099
- importPlugin: false
1100
- })
1101
- ```
1102
-
1103
- ---
1104
-
1105
- ## 🤝 贡献指南
1106
-
1107
- 欢迎贡献代码!请遵循以下规范:
1108
-
1109
- ### 代码规范
1110
-
1111
- - 使用 TypeScript 编写代码
1112
- - 遵循 ESLint 规则(`pnpm lint` 自动检查)
1113
- - 使用 Prettier 格式化代码(`pnpm format`)
1114
- - 编写清晰的注释和文档
1115
-
1116
- ### 提交规范
1117
-
1118
- 使用语义化提交信息:
1119
-
1120
- ```
1121
- feat: 添加新功能
1122
- fix: 修复 bug
1123
- docs: 更新文档
1124
- style: 代码格式调整
1125
- refactor: 代码重构
1126
- perf: 性能优化
1127
- test: 添加测试
1128
- chore: 构建/工具变动
1129
- ```
1130
-
1131
- ### 开发流程
1132
-
1133
- 1. Fork 仓库
1134
- 2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
1135
- 3. 提交更改 (`git commit -m 'feat: add amazing feature'`)
1136
- 4. 推送到分支 (`git push origin feature/AmazingFeature`)
1137
- 5. 创建 Pull Request
1138
-
1139
- ---
1140
-
1141
- ## 📋 更新日志
1142
-
1143
- ### 2.8.5
1144
- - 当前稳定版本
1145
- - 完整的插件系统架构
1146
- - 支持多种图形元素
1147
- - 导入/导出功能
1148
- - 撤销/重做机制
1149
-
1150
- ### 版本历史
1151
- 详细版本历史请查看 [GitHub Releases](https://github.com/pwg-code/vkedit/releases)
1152
-
1153
- ---
1154
-
1155
- ## 📄 许可证
1156
-
1157
- 本项目采用 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件
1158
-
1159
- ---
1160
-
1161
- ## 🔗 相关链接
1162
-
1163
- - [Vue.js](https://vuejs.org/)
1164
- - [Konva.js](https://konvajs.org/)
1165
- - [vue-konva](https://www.npmjs.com/package/vue-konva)
1166
- - [Pinia](https://pinia.vuejs.org/)
1167
-
1168
- ---
1169
-
1170
- ## ☕ 支持作者
1171
-
1172
- 如果您觉得 vkedit 对您有帮助,欢迎支持作者喝杯咖啡!您的支持是项目持续发展的动力。
1173
-
1174
- <div align="center">
1175
-
1176
- **Made with ❤️ by vkedit contributors**
1177
-
1178
- </div>
1179
-
1180
- ---
1181
-
1182
- ## 📞 联系支持
1183
-
1184
- 如果您需要技术支持、功能定制或有任何问题,欢迎随时联系:
1185
-
1186
- - **QQ**: 16871824
1187
- - **邮箱**: 168715824@qq.com
1188
- - **服务**: 技术支持、功能定制、项目合作
1189
-
1190
- 期待与您交流,共同完善 vkedit!
1
+ # vkedit
2
+
3
+ [![NPM Version](https://img.shields.io/npm/v/vkedit?style=flat-square)](https://www.npmjs.com/package/vkedit)
4
+ [![License](https://img.shields.io/npm/l/vkedit?style=flat-square)](LICENSE)
5
+ [![Node.js](https://img.shields.io/node/v/vkedit?style=flat-square)](package.json)
6
+
7
+ 基于 Vue 3 与 Konva.js 的可视化画布设计器组件,采用插件化架构,适用于标签、二维码、条码、票据、名片、证书等模板设计与打印排版场景。
8
+
9
+ **[English](README.en.md)**
10
+
11
+ ---
12
+
13
+ ![项目预览](./截图/image.png)
14
+
15
+ ---
16
+
17
+ ## 特性
18
+
19
+ - **插件化架构** — 核心功能与图形元素均以插件形式注册,按需启用
20
+ - **7 种图形元素** — 矩形、文本、线条、表格、二维码、条形码、图表(ECharts)
21
+ - **撤销/重做** — 基于命令模式,所有修改操作可逆
22
+ - **导入/导出** — JSON 序列化、PNG/JPG 图片、PDF 文档
23
+ - **对齐与分布** — 左/右/上/下/居中对齐、水平/垂直均分
24
+ - **标尺与缩放** — 毫米级精度标尺、可配置 DPM(每毫米点数)
25
+ - **事件驱动** — 类型安全的事件系统,插件间松耦合通信
26
+ - **TypeScript** — 完整类型定义,泛型推断插件与元素类型
27
+
28
+ ---
29
+
30
+ ## 安装
31
+
32
+ ```bash
33
+ pnpm add vkedit vue konva vue-konva
34
+ ```
35
+
36
+ vkedit 将 `vue`、`konva`、`vue-konva` 作为 peerDependencies,需由宿主项目提供。
37
+
38
+ | Peer Dependency | 版本 |
39
+ | --------------- | ------- |
40
+ | vue | ^3.5.18 |
41
+ | konva | ^10.0.2 |
42
+ | vue-konva | ^3.2.6 |
43
+
44
+ Node.js 要求:`^20.19.0 || >=22.12.0`
45
+
46
+ ---
47
+
48
+ ## 快速上手
49
+
50
+ ### 1. 配置入口文件
51
+
52
+ `main.ts` 中注册 VueKonva 并引入样式:
53
+
54
+ ```typescript
55
+ import { createApp } from 'vue'
56
+ import App from './App.vue'
57
+ import VueKonva from 'vue-konva'
58
+ import 'vkedit/dist/vkedit.css'
59
+
60
+ const app = createApp(App)
61
+ app.use(VueKonva)
62
+ app.mount('#app')
63
+ ```
64
+
65
+ ### 2. 使用编辑器组件
66
+
67
+ ```vue
68
+ <template>
69
+ <Vkedit :host="host" />
70
+ </template>
71
+
72
+ <script setup lang="ts">
73
+ import { createEditorHost, Vkedit } from 'vkedit'
74
+ import {
75
+ RectPlugin,
76
+ TextPlugin,
77
+ TablePlugin,
78
+ QrcodePlugin,
79
+ BarcodePlugin,
80
+ ChartPlugin,
81
+ LinePlugin,
82
+ } from 'vkedit'
83
+
84
+ const host = createEditorHost()
85
+
86
+ // 安装图形插件
87
+ host
88
+ .installPlugin('rect-plugin', RectPlugin)
89
+ .installPlugin('text-plugin', TextPlugin)
90
+ .installPlugin('table-plugin', TablePlugin)
91
+ .installPlugin('qr-plugin', QrcodePlugin)
92
+ .installPlugin('barcode-plugin', BarcodePlugin)
93
+ .installPlugin('chart-plugin', ChartPlugin)
94
+ .installPlugin('line-plugin', LinePlugin)
95
+
96
+ // 设置画布尺寸(A4 纸张,DPM = 8)
97
+ host.setStatus({
98
+ dpm: 8,
99
+ width: 210 * 8,
100
+ height: 297 * 8,
101
+ zoom: 0.4,
102
+ })
103
+ </script>
104
+ ```
105
+
106
+ ### Vkedit 组件 Props
107
+
108
+ | Prop | 类型 | 默认值 | 说明 |
109
+ | ------------------- | ------------ | ------ | ---------------- |
110
+ | `host` | `EditorHost` | — | 编辑器宿主实例 |
111
+ | `showToolbox` | `boolean` | `true` | 显示左侧工具箱 |
112
+ | `showPropertyPanel` | `boolean` | `true` | 显示右侧属性面板 |
113
+ | `showToolbar` | `boolean` | `true` | 显示顶部工具栏 |
114
+
115
+ ---
116
+
117
+ ## 配置
118
+
119
+ ### createEditorHost 选项
120
+
121
+ `createEditorHost(options)` 创建宿主实例并自动安装核心插件。可选插件通过以下选项控制:
122
+
123
+ | 选项 | 类型 | 默认值 | 说明 |
124
+ | ------------------------- | --------- | ------- | ---------------- |
125
+ | `basePropertyPanel` | `boolean` | `false` | 基础元素属性面板 |
126
+ | `baseCanvasPropertyPanel` | `boolean` | `true` | 画布属性面板 |
127
+ | `exportPlugin` | `boolean` | `true` | 导出插件 |
128
+ | `previewPlugin` | `boolean` | `true` | 预览插件 |
129
+ | `importPlugin` | `boolean` | `true` | 导入插件 |
130
+
131
+ ### 画布状态
132
+
133
+ 通过 `host.setStatus()` 更新画布状态,支持部分更新:
134
+
135
+ ```typescript
136
+ interface IEditorState {
137
+ zoom: number // 缩放级别
138
+ currentTool: string // 当前工具
139
+ snapToGrid: boolean // 网格吸附
140
+ showGrid: boolean // 网格显示
141
+ width: number // 画布宽度(像素)
142
+ height: number // 画布高度(像素)
143
+ wmm: number // 画布宽度(毫米)
144
+ hmm: number // 画布高度(毫米)
145
+ dpm: number // 每毫米点数(DPI / 25.4)
146
+ }
147
+ ```
148
+
149
+ `width`/`height` `wmm`/`hmm` 通过 `dpm` 自动换算。设置 `dpm` 会以当前毫米尺寸重新计算像素宽高。
150
+
151
+ ---
152
+
153
+ ## 插件列表
154
+
155
+ ### 核心插件(createEditorHost 自动安装)
156
+
157
+ | 插件 | 说明 |
158
+ | ---------------------------- | ---------------- |
159
+ | `ToolbarManagerPlugin` | 顶部工具栏管理 |
160
+ | `GraphicToolManagerPlugin` | 图形工具管理 |
161
+ | `GraphicManagerPlugin` | 图形渲染管理 |
162
+ | `PropertyPanelManagerPlugin` | 属性面板动态渲染 |
163
+ | `ElementManagerPlugin` | 元素生命周期管理 |
164
+ | `SelectionPlugin` | 元素选择与多选 |
165
+ | `KeyDownPlugin` | 键盘快捷键 |
166
+ | `SnapPlugin` | 网格吸附 |
167
+ | `ClipboardPlugin` | 复制/剪切/粘贴 |
168
+ | `AlignPlugin` | 对齐与分布 |
169
+ | `ContextMenuManagerPlugin` | 右键菜单 |
170
+
171
+ ### 功能插件(通过选项启用)
172
+
173
+ | 插件 | 选项 | 说明 |
174
+ | --------------- | --------------- | --------------------------- |
175
+ | `ExportPlugin` | `exportPlugin` | 导出 JSON / PNG / JPG / PDF |
176
+ | `ImportPlugin` | `importPlugin` | 导入 JSON 文件 |
177
+ | `PreviewPlugin` | `previewPlugin` | 预览与打印预览 |
178
+
179
+ ### 图形插件(手动安装)
180
+
181
+ | 插件 | 元素类型 | 说明 |
182
+ | --------------- | --------- | ----------------------------------- |
183
+ | `RectPlugin` | `rect` | 矩形,支持填充、描边 |
184
+ | `TextPlugin` | `text` | 文本,支持字体、对齐、样式 |
185
+ | `LinePlugin` | `line` | 线条,支持颜色、宽度 |
186
+ | `TablePlugin` | `table` | 表格,支持行列、合并单元格、边框 |
187
+ | `QrcodePlugin` | `qr` | 二维码,支持前景/背景色 |
188
+ | `BarcodePlugin` | `barcode` | 条形码,支持 CODE128、EAN-13 等格式 |
189
+ | `ChartPlugin` | `chart` | 图表,基于 ECharts |
190
+
191
+ ---
192
+
193
+ ## 核心概念
194
+
195
+ ### EditorHost
196
+
197
+ `EditorHost` 是编辑器的核心宿主类,管理插件、状态、命令和历史记录。通过 `createEditorHost()` 创建实例后,所有操作都围绕 `host` 进行。
198
+
199
+ ### 插件系统
200
+
201
+ 插件通过 `installPlugin` / `uninstallPlugin` / `getPlugin` 管理。插件需继承 `BasePlugin` 并实现 `onInstall()` 钩子。`getPlugin` 支持泛型推断:
202
+
203
+ ```typescript
204
+ const rectPlugin = host.getPlugin('rect-plugin') // 自动推断为 RectPlugin
205
+ const exportPlugin = host.getPlugin('export-plugin')
206
+ ```
207
+
208
+ ### 命令模式
209
+
210
+ 所有可撤销的操作通过命令实现。`executeCommand()` 执行命令并压入历史栈,`undo()` / `redo()` 在栈中导航:
211
+
212
+ ```typescript
213
+ host.executeCommand(command) // 执行并入栈
214
+ host.undo() // 撤销
215
+ host.redo() // 重做
216
+ ```
217
+
218
+ ### 事件系统
219
+
220
+ 编辑器通过 `emit` / `on` / `off` 实现类型安全的事件通信:
221
+
222
+ ```typescript
223
+ // 监听元素添加
224
+ host.on('element:added', (payload) => {
225
+ console.log('元素已添加:', payload.element)
226
+ })
227
+
228
+ // 监听选择变化
229
+ host.on('selection:changed', (payload) => {
230
+ console.log('选中元素:', payload.selection)
231
+ })
232
+ ```
233
+
234
+ 完整事件列表及自定义事件扩展请参阅 API 文档。
235
+
236
+ ---
237
+
238
+ ## 常用操作
239
+
240
+ ### 导出 / 导入 JSON
241
+
242
+ ```typescript
243
+ // 导出为 JSON 字符串
244
+ const json = host.toJSON()
245
+
246
+ // 从 JSON 字符串加载
247
+ host.loadJSON(json)
248
+ ```
249
+
250
+ 通过 ExportPlugin / ImportPlugin 可触发文件下载和文件选择:
251
+
252
+ ```typescript
253
+ const exportPlugin = host.getPlugin('export-plugin')
254
+ exportPlugin.handleExportJSON() // 下载 .json 文件
255
+ exportPlugin.handleExportImage() // 下载 PNG 图片
256
+ exportPlugin.handleExportPdf() // 下载 PDF 文档
257
+
258
+ const importPlugin = host.getPlugin('import-plugin')
259
+ importPlugin.handleImportJSON() // 弹出文件选择器
260
+ ```
261
+
262
+ ### 撤销 / 重做
263
+
264
+ ```typescript
265
+ host.undo()
266
+ host.redo()
267
+ ```
268
+
269
+ ### 监听事件
270
+
271
+ ```typescript
272
+ host.on('selection:changed', (payload) => {
273
+ console.log('选中元素 ID:', payload.selection)
274
+ })
275
+ ```
276
+
277
+ ### 添加元素(通过命令)
278
+
279
+ ```typescript
280
+ import { AddElementCommand } from 'vkedit'
281
+
282
+ const elementManager = host.getPlugin('element-manager-plugin')
283
+ const element = elementManager.createElement('rect')
284
+ host.executeCommand(new AddElementCommand(host, element))
285
+ ```
286
+
287
+ ### 更新元素属性(通过命令)
288
+
289
+ ```typescript
290
+ import { UpdatePropertyCommand } from 'vkedit'
291
+
292
+ host.executeCommand(
293
+ new UpdatePropertyCommand(host, element, 'fill', 'red', 'blue')
294
+ )
295
+ ```
296
+
297
+ ### 批量操作
298
+
299
+ ```typescript
300
+ import { BatchCommand, UpdatePropertyCommand } from 'vkedit'
301
+
302
+ const batch = new BatchCommand(host, [
303
+ new UpdatePropertyCommand(host, el1, 'fill', 'red', 'blue'),
304
+ new UpdatePropertyCommand(host, el2, 'fill', 'red', 'green'),
305
+ ])
306
+ host.executeCommand(batch)
307
+ ```
308
+
309
+ ---
310
+
311
+ ## 适用场景
312
+
313
+ | 场景 | 适用插件 |
314
+ | ------------ | -------------------------------- |
315
+ | 标签模板设计 | 二维码、条形码、文本、矩形、表格 |
316
+ | 二维码设计 | 二维码、文本、矩形 |
317
+ | 条码设计 | 条形码、文本、线条 |
318
+ | 票据设计 | 表格、文本、矩形、线条 |
319
+ | 名片设计 | 文本、矩形、线条 |
320
+ | 证书设计 | 文本、矩形、表格 |
321
+ | 数据可视化 | 图表、文本、矩形 |
322
+
323
+ ---
324
+
325
+ ## 可用命令
326
+
327
+ | 命令 | 构造参数 | 说明 |
328
+ | --------------------------- | --------------------------------------------------- | -------------------------- |
329
+ | `AddElementCommand` | `(host, element)` | 添加元素 |
330
+ | `RemoveElementCommand` | `(host, element)` | 移除元素 |
331
+ | `TransformElementCommand` | `(host, element, oldState, newState)` | 变换元素(位置/大小/旋转) |
332
+ | `UpdatePropertyCommand` | `(host, element, propertyPath, oldValue, newValue)` | 更新属性 |
333
+ | `BatchCommand` | `(host, commands, description?)` | 批量命令 |
334
+ | `AlignElementsCommand` | `(host, alignment, elementIds)` | 对齐元素 |
335
+ | `DistributeElementsCommand` | `(host, direction, elementIds)` | 分布元素 |
336
+ | `ChangeLayerOrderCommand` | `(host, elementId, direction)` | 调整图层顺序 |
337
+ | `ClearSelectionCommand` | `(host)` | 清空选择 |
338
+
339
+ ---
340
+
341
+ ## 链接
342
+
343
+ - [npm](https://www.npmjs.com/package/vkedit)
344
+ - [GitHub Releases](https://github.com/pwg-code/vkedit/releases)
345
+ - [Vue.js](https://vuejs.org/)
346
+ - [Konva.js](https://konvajs.org/)
347
+ - [vue-konva](https://www.npmjs.com/package/vue-konva)
348
+
349
+ ---
350
+
351
+ ## License
352
+
353
+ [MIT](LICENSE)
354
+
355
+ ---
356
+
357
+ ## 联系方式
358
+
359
+ - **QQ**: 16871824
360
+ - **邮箱**: 168715824@qq.com
361
+
362
+ 提供技术支持、功能定制、项目合作。
363
+
364
+ ---
365
+
366
+ 如果 vkedit 对您有帮助,欢迎请作者喝杯咖啡。
367
+
368
+ <div align="center">
369
+
370
+ <table>
371
+ <tr>
372
+ <td align="center">
373
+ <img src="./截图/reward-alipay.png" width="200" alt="支付宝打赏" />
374
+ <br>支付宝
375
+ </td>
376
+ <td align="center">
377
+ <img src="./截图/reward-wechat.png" width="200" alt="微信打赏" />
378
+ <br>微信
379
+ </td>
380
+ </tr>
381
+ </table>
382
+
383
+ </div>