@zzalai/leafer-point-annotation 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.
@@ -0,0 +1,329 @@
1
+ # @zzalai/leafer-undo-redo 详细指南
2
+
3
+ ## 1. 库的概述
4
+
5
+ **@zzalai/leafer-undo-redo** 是一个基于命令模式的 LeaferJS 应用程序的健壮且灵活的撤销/重做系统。它为 LeaferJS 应用提供了专业级的撤销/重做功能,支持复杂的用户操作管理。
6
+
7
+ ## 2. 核心功能
8
+
9
+ ### 2.1 基于命令模式
10
+ - **清晰的架构**:使用命令模式设计,每个操作都是一个命令对象
11
+ - **可扩展性**:易于添加新的命令类型
12
+ - **类型安全**:完整的 TypeScript 类型定义
13
+
14
+ ### 2.2 撤销/重做堆栈
15
+ - **历史记录管理**:自动维护操作历史
16
+ - **堆栈限制**:可配置的历史记录长度限制(默认 50)
17
+ - **状态追踪**:提供 `canUndo()` 和 `canRedo()` 方法检查操作状态
18
+
19
+ ### 2.3 内置命令
20
+ - **AddElementCommand**:添加元素
21
+ - **MoveCommand**:移动元素
22
+ - **ResizeCommand**:调整元素大小
23
+ - **RemoveElementCommand**:删除元素
24
+ - **BatchCommand**:批量命令(将多个操作组合为一个)
25
+
26
+ ## 3. 核心 API
27
+
28
+ ### 3.1 ICommand 接口
29
+
30
+ ```typescript
31
+ interface ICommand {
32
+ execute(): void; // 执行命令
33
+ undo(): void; // 撤销命令
34
+ }
35
+ ```
36
+
37
+ ### 3.2 CommandManager 类
38
+
39
+ ```typescript
40
+ class CommandManager {
41
+ constructor(limit?: number); // 可选的历史记录限制
42
+
43
+ executeCommand(command: ICommand): void; // 执行命令并添加到历史记录
44
+ undo(): void; // 撤销上一个命令
45
+ redo(): void; // 重做上一个撤销的命令
46
+ canUndo(): boolean; // 检查是否可以撤销
47
+ canRedo(): boolean; // 检查是否可以重做
48
+ clear(): void; // 清除历史记录
49
+ }
50
+ ```
51
+
52
+ ### 3.3 内置命令
53
+
54
+ #### AddElementCommand
55
+ ```typescript
56
+ class AddElementCommand implements ICommand {
57
+ constructor(container: any, element: any);
58
+ execute(): void; // 添加元素到容器
59
+ undo(): void; // 从容器移除元素
60
+ }
61
+ ```
62
+
63
+ #### MoveCommand
64
+ ```typescript
65
+ class MoveCommand implements ICommand {
66
+ constructor(target: any, fromX: number, fromY: number, toX: number, toY: number);
67
+ execute(): void; // 移动到目标位置
68
+ undo(): void; // 恢复到原始位置
69
+ }
70
+ ```
71
+
72
+ #### ResizeCommand
73
+ ```typescript
74
+ class ResizeCommand implements ICommand {
75
+ constructor(target: any, fromWidth: number, fromHeight: number, toWidth: number, toHeight: number);
76
+ execute(): void; // 调整到目标大小
77
+ undo(): void; // 恢复到原始大小
78
+ }
79
+ ```
80
+
81
+ #### RemoveElementCommand
82
+ ```typescript
83
+ class RemoveElementCommand implements ICommand {
84
+ constructor(container: any, element: any);
85
+ execute(): void; // 从容器移除元素
86
+ undo(): void; // 将元素添加回容器
87
+ }
88
+ ```
89
+
90
+ #### BatchCommand
91
+ ```typescript
92
+ class BatchCommand implements ICommand {
93
+ constructor(commands: ICommand[]);
94
+ execute(): void; // 执行所有命令
95
+ undo(): void; // 撤销所有命令
96
+ }
97
+ ```
98
+
99
+ ## 4. 使用方法
100
+
101
+ ### 4.1 基本用法
102
+
103
+ ```typescript
104
+ import { CommandManager, AddElementCommand, MoveCommand } from '@zzalai/leafer-undo-redo';
105
+ import { App, Rect } from 'leafer-ui';
106
+
107
+ // 创建命令管理器
108
+ const commandManager = new CommandManager(100); // 历史记录限制为100
109
+
110
+ // 创建应用实例
111
+ const app = new App({...});
112
+
113
+ // 添加元素
114
+ const rect = new Rect({ x: 100, y: 100, width: 50, height: 50, fill: 'red' });
115
+ const addCommand = new AddElementCommand(app, rect);
116
+ commandManager.executeCommand(addCommand);
117
+
118
+ // 移动元素
119
+ const moveCommand = new MoveCommand(rect, 100, 100, 200, 200);
120
+ commandManager.executeCommand(moveCommand);
121
+
122
+ // 撤销/重做
123
+ commandManager.undo(); // 撤销移动
124
+ commandManager.undo(); // 撤销添加
125
+ commandManager.redo(); // 重做添加
126
+ commandManager.redo(); // 重做移动
127
+ ```
128
+
129
+ ### 4.2 批量命令
130
+
131
+ ```typescript
132
+ import { BatchCommand, AddElementCommand } from '@zzalai/leafer-undo-redo';
133
+
134
+ // 创建多个命令
135
+ const commands = [
136
+ new AddElementCommand(app, rect1),
137
+ new AddElementCommand(app, rect2),
138
+ new AddElementCommand(app, rect3)
139
+ ];
140
+
141
+ // 创建批量命令
142
+ const batchCommand = new BatchCommand(commands);
143
+ commandManager.executeCommand(batchCommand);
144
+
145
+ // 撤销整个批量操作
146
+ commandManager.undo(); // 会撤销所有三个添加操作
147
+ ```
148
+
149
+ ### 4.3 自定义命令
150
+
151
+ ```typescript
152
+ import { ICommand } from '@zzalai/leafer-undo-redo';
153
+
154
+ class ChangeColorCommand implements ICommand {
155
+ private target: any;
156
+ private fromColor: string;
157
+ private toColor: string;
158
+
159
+ constructor(target: any, fromColor: string, toColor: string) {
160
+ this.target = target;
161
+ this.fromColor = fromColor;
162
+ this.toColor = toColor;
163
+ }
164
+
165
+ execute(): void {
166
+ this.target.set({ fill: this.toColor });
167
+ }
168
+
169
+ undo(): void {
170
+ this.target.set({ fill: this.fromColor });
171
+ }
172
+ }
173
+
174
+ // 使用自定义命令
175
+ const colorCommand = new ChangeColorCommand(rect, 'red', 'blue');
176
+ commandManager.executeCommand(colorCommand);
177
+ ```
178
+
179
+ ## 5. 集成示例
180
+
181
+ ### 5.1 Vue 组件集成
182
+
183
+ ```vue
184
+ <template>
185
+ <div class="annotation-editor">
186
+ <div class="toolbar">
187
+ <button @click="undo" :disabled="!canUndo">撤销</button>
188
+ <button @click="redo" :disabled="!canRedo">重做</button>
189
+ </div>
190
+ <div ref="canvasRef" class="canvas-container"></div>
191
+ </div>
192
+ </template>
193
+
194
+ <script setup lang="ts">
195
+ import { ref, computed, onMounted } from 'vue';
196
+ import { App, Rect } from 'leafer-ui';
197
+ import { CommandManager, AddElementCommand } from '@zzalai/leafer-undo-redo';
198
+
199
+ const canvasRef = ref<HTMLElement>();
200
+ const commandManager = new CommandManager();
201
+ let app: App;
202
+
203
+ const canUndo = computed(() => commandManager.canUndo());
204
+ const canRedo = computed(() => commandManager.canRedo());
205
+
206
+ const undo = () => commandManager.undo();
207
+ const redo = () => commandManager.redo();
208
+
209
+ const addPoint = (x: number, y: number) => {
210
+ const point = new Rect({
211
+ x: x - 5,
212
+ y: y - 5,
213
+ width: 10,
214
+ height: 10,
215
+ fill: '#ff0000'
216
+ });
217
+
218
+ const command = new AddElementCommand(app, point);
219
+ commandManager.executeCommand(command);
220
+ };
221
+
222
+ onMounted(() => {
223
+ app = new App({
224
+ view: canvasRef.value!,
225
+ width: 800,
226
+ height: 600
227
+ });
228
+
229
+ // 点击添加点
230
+ app.on('pointerdown', (e: any) => {
231
+ addPoint(e.x, e.y);
232
+ });
233
+ });
234
+ </script>
235
+ ```
236
+
237
+ ### 5.2 事件处理集成
238
+
239
+ ```typescript
240
+ // 处理元素拖动
241
+ app.on('dragend', (e: any) => {
242
+ const target = e.target;
243
+ const moveCommand = new MoveCommand(
244
+ target,
245
+ e.startX,
246
+ e.startY,
247
+ target.x,
248
+ target.y
249
+ );
250
+ commandManager.executeCommand(moveCommand);
251
+ });
252
+
253
+ // 处理元素调整大小
254
+ app.on('resizeend', (e: any) => {
255
+ const target = e.target;
256
+ const resizeCommand = new ResizeCommand(
257
+ target,
258
+ e.startWidth,
259
+ e.startHeight,
260
+ target.width,
261
+ target.height
262
+ );
263
+ commandManager.executeCommand(resizeCommand);
264
+ });
265
+ ```
266
+
267
+ ## 6. 最佳实践
268
+
269
+ ### 6.1 性能优化
270
+ - **合理设置历史记录限制**:根据应用复杂度调整 `CommandManager` 的历史记录限制
271
+ - **使用批量命令**:对于复杂操作,使用 `BatchCommand` 减少历史记录条目
272
+ - **命令粒度**:合理设计命令粒度,避免过于细粒度的操作导致历史记录膨胀
273
+
274
+ ### 6.2 错误处理
275
+ - **命令执行安全**:确保命令的 `execute()` 和 `undo()` 方法能够安全执行
276
+ - **状态验证**:在执行命令前验证目标元素的状态
277
+ - **异常处理**:在命令执行过程中添加适当的异常处理
278
+
279
+ ### 6.3 测试建议
280
+ - **单元测试**:为自定义命令编写单元测试
281
+ - **集成测试**:测试命令管理器与应用的集成
282
+ - **边界测试**:测试历史记录限制、空操作等边界情况
283
+
284
+ ## 7. 常见问题
285
+
286
+ ### 7.1 命令执行失败
287
+ **问题**:命令执行后无法撤销
288
+ **解决方案**:确保命令的 `execute()` 和 `undo()` 方法能够正确恢复状态
289
+
290
+ ### 7.2 历史记录过大
291
+ **问题**:历史记录占用过多内存
292
+ **解决方案**:设置合理的历史记录限制,使用批量命令减少条目
293
+
294
+ ### 7.3 并发操作冲突
295
+ **问题**:多个操作同时执行导致状态不一致
296
+ **解决方案**:确保命令执行的原子性,避免并发修改
297
+
298
+ ## 8. 未来扩展
299
+
300
+ - **命令序列化**:支持命令历史的保存和加载
301
+ - **命令优先级**:为命令添加优先级机制
302
+ - **命令分组**:支持命令的逻辑分组
303
+ - **自定义历史记录存储**:允许自定义历史记录的存储方式
304
+
305
+ ## 9. 技术细节
306
+
307
+ ### 9.1 命令模式原理
308
+
309
+ 命令模式将请求封装为对象,使得可以用不同的请求参数化客户端,支持可撤销操作。在 LeaferJS 应用中,每个用户操作都被封装为一个命令对象,通过 CommandManager 管理执行和撤销。
310
+
311
+ ### 9.2 堆栈实现
312
+
313
+ - **撤销堆栈**:存储已执行的命令,按执行顺序排列
314
+ - **重做堆栈**:存储已撤销的命令,按撤销顺序排列
315
+ - **堆栈清理**:当执行新命令时,清空重做堆栈
316
+
317
+ ## 10. 版本历史
318
+
319
+ | 版本 | 日期 | 变更内容 |
320
+ |------|------|----------|
321
+ | 1.0.0 | 2026-01-01 | 初始版本 |
322
+ | 1.0.1 | 2026-02-15 | 修复批量命令执行顺序 |
323
+ | 1.0.2 | 2026-03-20 | 添加命令历史限制功能 |
324
+ | 1.0.3 | 2026-04-10 | 优化命令执行性能 |
325
+
326
+ ---
327
+
328
+ **文档更新时间**:2026-04-27
329
+ **作者**:AI Assistant