@tmagic/editor 1.2.0-beta.9 → 1.2.1

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 (103) hide show
  1. package/dist/style.css +62 -34
  2. package/dist/{tmagic-editor.mjs → tmagic-editor.js} +1641 -1266
  3. package/dist/tmagic-editor.js.map +1 -0
  4. package/dist/{tmagic-editor.umd.js → tmagic-editor.umd.cjs} +1649 -1270
  5. package/dist/tmagic-editor.umd.cjs.map +1 -0
  6. package/package.json +16 -14
  7. package/src/Editor.vue +66 -40
  8. package/src/components/CodeDraftEditor.vue +137 -0
  9. package/src/components/ContentMenu.vue +1 -1
  10. package/src/components/FunctionEditor.vue +190 -0
  11. package/src/components/Icon.vue +1 -1
  12. package/src/{layouts → components}/Layout.vue +54 -15
  13. package/src/components/ScrollBar.vue +1 -1
  14. package/src/components/ScrollViewer.vue +10 -1
  15. package/src/components/ToolButton.vue +1 -1
  16. package/src/fields/Code.vue +1 -1
  17. package/src/fields/CodeLink.vue +1 -1
  18. package/src/fields/CodeSelect.vue +102 -99
  19. package/src/fields/UISelect.vue +3 -3
  20. package/src/index.ts +2 -1
  21. package/src/layouts/AddPageBox.vue +5 -2
  22. package/src/layouts/CodeEditor.vue +2 -3
  23. package/src/layouts/Framework.vue +12 -10
  24. package/src/layouts/NavMenu.vue +11 -8
  25. package/src/layouts/PropsPanel.vue +12 -9
  26. package/src/layouts/Resizer.vue +1 -1
  27. package/src/layouts/sidebar/ComponentListPanel.vue +2 -3
  28. package/src/layouts/sidebar/LayerMenu.vue +9 -7
  29. package/src/layouts/sidebar/LayerPanel.vue +130 -178
  30. package/src/layouts/sidebar/Sidebar.vue +110 -26
  31. package/src/layouts/sidebar/code-block/CodeBlockEditor.vue +33 -118
  32. package/src/layouts/sidebar/code-block/CodeBlockList.vue +93 -126
  33. package/src/layouts/workspace/Breadcrumb.vue +6 -7
  34. package/src/layouts/workspace/PageBar.vue +3 -3
  35. package/src/layouts/workspace/PageBarScrollContainer.vue +5 -3
  36. package/src/layouts/workspace/Stage.vue +36 -18
  37. package/src/layouts/workspace/ViewerMenu.vue +16 -15
  38. package/src/layouts/workspace/Workspace.vue +9 -6
  39. package/src/services/BaseService.ts +11 -2
  40. package/src/services/codeBlock.ts +136 -146
  41. package/src/services/componentList.ts +7 -2
  42. package/src/services/editor.ts +161 -85
  43. package/src/services/events.ts +7 -2
  44. package/src/services/history.ts +8 -17
  45. package/src/services/props.ts +8 -3
  46. package/src/services/storage.ts +1 -0
  47. package/src/services/ui.ts +20 -10
  48. package/src/theme/code-block.scss +46 -23
  49. package/src/theme/component-list-panel.scss +8 -5
  50. package/src/type.ts +84 -63
  51. package/src/utils/config.ts +1 -1
  52. package/src/utils/editor.ts +17 -3
  53. package/src/utils/index.ts +2 -1
  54. package/src/utils/logger.ts +1 -1
  55. package/src/utils/operator.ts +12 -12
  56. package/src/utils/props.ts +3 -3
  57. package/src/utils/scroll-viewer.ts +18 -2
  58. package/src/utils/stage.ts +10 -9
  59. package/src/utils/undo-redo.ts +1 -1
  60. package/types/Editor.vue.d.ts +48 -28
  61. package/types/components/CodeDraftEditor.vue.d.ts +55 -0
  62. package/types/components/ContentMenu.vue.d.ts +11 -87
  63. package/types/components/FunctionEditor.vue.d.ts +60 -0
  64. package/types/components/Icon.vue.d.ts +4 -50
  65. package/types/{layouts → components}/Layout.vue.d.ts +12 -1
  66. package/types/components/ScrollBar.vue.d.ts +7 -65
  67. package/types/components/ScrollViewer.vue.d.ts +45 -1
  68. package/types/components/ToolButton.vue.d.ts +12 -78
  69. package/types/fields/Code.vue.d.ts +11 -77
  70. package/types/fields/CodeLink.vue.d.ts +12 -82
  71. package/types/fields/CodeSelect.vue.d.ts +13 -77
  72. package/types/fields/UISelect.vue.d.ts +7 -65
  73. package/types/index.d.ts +1 -0
  74. package/types/layouts/AddPageBox.vue.d.ts +1 -43
  75. package/types/layouts/CodeEditor.vue.d.ts +22 -130
  76. package/types/layouts/NavMenu.vue.d.ts +9 -69
  77. package/types/layouts/sidebar/LayerMenu.vue.d.ts +16 -48
  78. package/types/layouts/sidebar/LayerPanel.vue.d.ts +24 -7
  79. package/types/layouts/sidebar/Sidebar.vue.d.ts +10 -5
  80. package/types/layouts/sidebar/code-block/CodeBlockEditor.vue.d.ts +24 -6
  81. package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +11 -5
  82. package/types/layouts/workspace/Breadcrumb.vue.d.ts +1 -43
  83. package/types/layouts/workspace/Stage.vue.d.ts +15 -43
  84. package/types/layouts/workspace/ViewerMenu.vue.d.ts +11 -66
  85. package/types/layouts/workspace/Workspace.vue.d.ts +25 -8
  86. package/types/services/BaseService.d.ts +2 -1
  87. package/types/services/codeBlock.d.ts +56 -47
  88. package/types/services/componentList.d.ts +1 -0
  89. package/types/services/editor.d.ts +10 -11
  90. package/types/services/events.d.ts +1 -0
  91. package/types/services/history.d.ts +5 -15
  92. package/types/services/props.d.ts +3 -2
  93. package/types/services/ui.d.ts +24 -24
  94. package/types/type.d.ts +70 -59
  95. package/types/utils/editor.d.ts +1 -0
  96. package/types/utils/index.d.ts +1 -0
  97. package/types/utils/operator.d.ts +1 -1
  98. package/types/utils/props.d.ts +1 -1
  99. package/types/utils/scroll-viewer.d.ts +5 -0
  100. package/dist/tmagic-editor.mjs.map +0 -1
  101. package/dist/tmagic-editor.umd.js.map +0 -1
  102. package/src/layouts/sidebar/TabPane.vue +0 -118
  103. package/types/layouts/sidebar/TabPane.vue.d.ts +0 -82
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * Tencent is pleased to support the open source community by making TMagicEditor available.
3
3
  *
4
- * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
4
+ * Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
5
5
  *
6
6
  * Licensed under the Apache License, Version 2.0 (the "License");
7
7
  * you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@
17
17
  */
18
18
 
19
19
  import { reactive, toRaw } from 'vue';
20
- import { cloneDeep, mergeWith, uniq } from 'lodash-es';
20
+ import { cloneDeep, isObject, mergeWith, uniq } from 'lodash-es';
21
21
 
22
22
  import type { CodeBlockDSL, Id, MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
23
23
  import { NodeType } from '@tmagic/schema';
@@ -25,9 +25,9 @@ import StageCore from '@tmagic/stage';
25
25
  import { getNodePath, isNumber, isPage, isPop } from '@tmagic/utils';
26
26
 
27
27
  import codeBlockService from '../services/codeBlock';
28
- import historyService, { StepValue } from '../services/history';
28
+ import historyService from '../services/history';
29
29
  import storageService, { Protocol } from '../services/storage';
30
- import type { AddMNode, EditorNodeInfo, PastePosition, StoreState } from '../type';
30
+ import type { AddMNode, EditorNodeInfo, PastePosition, StepValue, StoreState, StoreStateKey } from '../type';
31
31
  import { LayerOffset, Layout } from '../type';
32
32
  import {
33
33
  change2Fixed,
@@ -73,7 +73,7 @@ class Editor extends BaseService {
73
73
  'copy',
74
74
  'paste',
75
75
  'doPaste',
76
- 'duAlignCenter',
76
+ 'doAlignCenter',
77
77
  'alignCenter',
78
78
  'moveLayer',
79
79
  'moveToContainer',
@@ -91,29 +91,40 @@ class Editor extends BaseService {
91
91
 
92
92
  /**
93
93
  * 设置当前指点节点配置
94
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes'
94
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength'
95
95
  * @param value MNode
96
- * @returns MNode
97
96
  */
98
- public set<T = MNode>(name: keyof StoreState, value: T) {
99
- this.state[name] = value as any;
97
+ public set<K extends StoreStateKey, T extends StoreState[K]>(name: K, value: T) {
98
+ const preValue = this.state[name];
99
+ this.state[name] = value;
100
+
100
101
  // set nodes时将node设置为nodes第一个元素
101
- if (name === 'nodes') {
102
- this.set('node', (value as unknown as MNode[])[0]);
102
+ if (name === 'nodes' && Array.isArray(value)) {
103
+ this.set('node', value[0]);
103
104
  }
105
+
104
106
  if (name === 'root') {
105
- this.state.pageLength = (value as unknown as MApp)?.items?.length || 0;
106
- this.emit('root-change', value);
107
+ if (Array.isArray(value)) {
108
+ throw new Error('root 不能为数组');
109
+ }
110
+
111
+ if (isObject(value) && !(value instanceof StageCore) && !(value instanceof Map)) {
112
+ this.state.pageLength = value.items.length;
113
+ } else {
114
+ this.state.pageLength = 0;
115
+ }
116
+
117
+ this.emit('root-change', value, preValue);
107
118
  }
108
119
  }
109
120
 
110
121
  /**
111
122
  * 获取当前指点节点配置
112
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes'
123
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength'
113
124
  * @returns MNode
114
125
  */
115
- public get<T = MNode>(name: keyof StoreState): T {
116
- return (this.state as any)[name];
126
+ public get<K extends StoreStateKey>(name: K): StoreState[K] {
127
+ return this.state[name];
117
128
  }
118
129
 
119
130
  /**
@@ -123,22 +134,29 @@ class Editor extends BaseService {
123
134
  * @returns {EditorNodeInfo}
124
135
  */
125
136
  public getNodeInfo(id: Id, raw = true): EditorNodeInfo {
126
- let root = this.get<MApp | null>('root');
137
+ let root = this.get('root');
127
138
  if (raw) {
128
139
  root = toRaw(root);
129
140
  }
130
- if (!root) return {};
141
+
142
+ const info: EditorNodeInfo = {
143
+ node: null,
144
+ parent: null,
145
+ page: null,
146
+ };
147
+
148
+ if (!root) return info;
131
149
 
132
150
  if (id === root.id) {
133
- return { node: root };
151
+ info.node = root;
152
+ return info;
134
153
  }
135
154
 
136
155
  const path = getNodePath(id, root.items);
137
156
 
138
- if (!path.length) return {};
157
+ if (!path.length) return info;
139
158
 
140
159
  path.unshift(root);
141
- const info: EditorNodeInfo = {};
142
160
 
143
161
  info.node = path[path.length - 1] as MComponent;
144
162
  info.parent = path[path.length - 2] as MContainer;
@@ -159,7 +177,7 @@ class Editor extends BaseService {
159
177
  * @param {boolean} raw 是否使用toRaw
160
178
  * @returns 组件节点配置
161
179
  */
162
- public getNodeById(id: Id, raw = true): MNode | undefined {
180
+ public getNodeById(id: Id, raw = true): MNode | null {
163
181
  const { node } = this.getNodeInfo(id, raw);
164
182
  return node;
165
183
  }
@@ -170,8 +188,7 @@ class Editor extends BaseService {
170
188
  * @param {boolean} raw 是否使用toRaw
171
189
  * @returns 指点组件的父节点配置
172
190
  */
173
- public getParentById(id: Id, raw = true): MContainer | undefined {
174
- if (!this.get<MApp | null>('root')) return;
191
+ public getParentById(id: Id, raw = true): MContainer | null {
175
192
  const { parent } = this.getNodeInfo(id, raw);
176
193
  return parent;
177
194
  }
@@ -201,18 +218,18 @@ class Editor extends BaseService {
201
218
  */
202
219
  public async select(config: MNode | Id): Promise<MNode> | never {
203
220
  const { node, page, parent } = this.selectedConfigExceptionHandler(config);
204
- this.set('nodes', [node]);
205
- this.set('page', page || null);
206
- this.set('parent', parent || null);
221
+ this.set('nodes', node ? [node] : []);
222
+ this.set('page', page);
223
+ this.set('parent', parent);
207
224
 
208
225
  if (page) {
209
226
  historyService.changePage(toRaw(page));
210
227
  } else {
211
- historyService.empty();
228
+ historyService.resetState();
212
229
  }
213
230
 
214
231
  if (node?.id) {
215
- this.get<StageCore>('stage')
232
+ this.get('stage')
216
233
  ?.renderer.runtime?.getApp?.()
217
234
  ?.emit(
218
235
  'editor:select',
@@ -221,7 +238,7 @@ class Editor extends BaseService {
221
238
  page,
222
239
  parent,
223
240
  },
224
- getNodePath(node.id, this.get<MApp>('root').items),
241
+ getNodePath(node.id, this.get('root')?.items),
225
242
  );
226
243
  }
227
244
 
@@ -230,7 +247,7 @@ class Editor extends BaseService {
230
247
  return node!;
231
248
  }
232
249
 
233
- public async selectNextNode(): Promise<MNode> | never {
250
+ public async selectNextNode(): Promise<MNode | null> | never {
234
251
  const node = toRaw(this.get('node'));
235
252
 
236
253
  if (!node || isPage(node) || node.type === NodeType.ROOT) return node;
@@ -244,21 +261,24 @@ class Editor extends BaseService {
244
261
  const nextNode = parent.items[index + 1] || parent.items[0];
245
262
 
246
263
  await this.select(nextNode);
247
- this.get<StageCore>('stage')?.select(nextNode.id);
264
+ this.get('stage')?.select(nextNode.id);
248
265
 
249
266
  return nextNode;
250
267
  }
251
268
 
252
269
  public async selectNextPage(): Promise<MNode> | never {
253
- const root = toRaw(this.get<MApp>('root'));
270
+ const root = toRaw(this.get('root'));
254
271
  const page = toRaw(this.get('page'));
255
272
 
273
+ if (!page) throw new Error('page不能为空');
274
+ if (!root) throw new Error('root不能为空');
275
+
256
276
  const index = getNodeIndex(page, root);
257
277
 
258
278
  const nextPage = root.items[index + 1] || root.items[0];
259
279
 
260
280
  await this.select(nextPage);
261
- this.get<StageCore>('stage')?.select(nextPage.id);
281
+ this.get('stage')?.select(nextPage.id);
262
282
 
263
283
  return nextPage;
264
284
  }
@@ -292,20 +312,25 @@ class Editor extends BaseService {
292
312
  }
293
313
 
294
314
  public async doAdd(node: MNode, parent: MContainer): Promise<MNode> {
295
- const root = this.get<MApp>('root');
296
- const curNode = this.get<MNode>('node');
297
- const stage = this.get<StageCore | null>('stage');
315
+ const root = this.get('root');
316
+
317
+ if (!root) throw new Error('root为空');
318
+
319
+ const curNode = this.get('node');
320
+ const stage = this.get('stage');
298
321
 
299
- if ((parent?.type === NodeType.ROOT || curNode.type === NodeType.ROOT) && node.type !== NodeType.PAGE) {
322
+ if (!curNode) throw new Error('当前选中节点为空');
323
+
324
+ if ((parent.type === NodeType.ROOT || curNode?.type === NodeType.ROOT) && node.type !== NodeType.PAGE) {
300
325
  throw new Error('app下不能添加组件');
301
326
  }
302
327
 
303
328
  if (parent.id !== curNode.id && node.type !== NodeType.PAGE) {
304
329
  const index = parent.items.indexOf(curNode);
305
- parent?.items?.splice(index + 1, 0, node);
330
+ parent.items?.splice(index + 1, 0, node);
306
331
  } else {
307
332
  // 新增节点添加到配置中
308
- parent?.items?.push(node);
333
+ parent.items?.push(node);
309
334
  }
310
335
 
311
336
  const layout = await this.getLayout(toRaw(parent), node as MNode);
@@ -337,7 +362,7 @@ class Editor extends BaseService {
337
362
  * @returns 添加后的节点
338
363
  */
339
364
  public async add(addNode: AddMNode | MNode[], parent?: MContainer | null): Promise<MNode | MNode[]> {
340
- const stage = this.get<StageCore | null>('stage');
365
+ const stage = this.get('stage');
341
366
 
342
367
  // 新增多个组件只存在于粘贴多个组件,粘贴的是一个完整的config,所以不再需要getPropsValue
343
368
  const addNodes = [];
@@ -353,6 +378,10 @@ class Editor extends BaseService {
353
378
 
354
379
  const newNodes = await Promise.all(
355
380
  addNodes.map((node) => {
381
+ const root = this.get('root');
382
+ if (isPage(node) && root) {
383
+ return this.doAdd(node, root);
384
+ }
356
385
  const parentNode = parent && typeof parent !== 'function' ? parent : getAddParent(node);
357
386
  if (!parentNode) throw new Error('未找到父元素');
358
387
  return this.doAdd(node, parentNode);
@@ -375,7 +404,9 @@ class Editor extends BaseService {
375
404
  }
376
405
  }
377
406
 
378
- this.pushHistoryState();
407
+ if (!isPage(newNodes[0])) {
408
+ this.pushHistoryState();
409
+ }
379
410
 
380
411
  this.emit('add', newNodes);
381
412
 
@@ -383,9 +414,8 @@ class Editor extends BaseService {
383
414
  }
384
415
 
385
416
  public async doRemove(node: MNode): Promise<void> {
386
- const root = this.get<MApp | null>('root');
387
-
388
- if (!root) throw new Error('没有root');
417
+ const root = this.get('root');
418
+ if (!root) throw new Error('root不能为空');
389
419
 
390
420
  const { parent, node: curNode } = this.getNodeInfo(node.id);
391
421
 
@@ -396,7 +426,7 @@ class Editor extends BaseService {
396
426
  if (typeof index !== 'number' || index === -1) throw new Error('找不要删除的节点');
397
427
 
398
428
  parent.items?.splice(index, 1);
399
- const stage = this.get<StageCore | null>('stage');
429
+ const stage = this.get('stage');
400
430
  stage?.remove({ id: node.id, parentId: parent.id, root: cloneDeep(root) });
401
431
 
402
432
  if (node.type === NodeType.PAGE) {
@@ -430,20 +460,24 @@ class Editor extends BaseService {
430
460
  /**
431
461
  * 删除组件
432
462
  * @param {Object} node
433
- * @return {Object} 删除的组件配置
434
463
  */
435
464
  public async remove(nodeOrNodeList: MNode | MNode[]): Promise<void> {
436
465
  const nodes = Array.isArray(nodeOrNodeList) ? nodeOrNodeList : [nodeOrNodeList];
437
466
 
438
467
  await Promise.all(nodes.map((node) => this.doRemove(node)));
439
468
 
440
- // 更新历史记录
441
- this.pushHistoryState();
469
+ if (!isPage(nodes[0])) {
470
+ // 更新历史记录
471
+ this.pushHistoryState();
472
+ }
442
473
 
443
474
  this.emit('remove', nodes);
444
475
  }
445
476
 
446
477
  public async doUpdate(config: MNode) {
478
+ const root = this.get('root');
479
+ if (!root) throw new Error('root为空');
480
+
447
481
  if (!config?.id) throw new Error('没有配置或者配置缺少id值');
448
482
 
449
483
  const info = this.getNodeInfo(config.id, false);
@@ -452,9 +486,13 @@ class Editor extends BaseService {
452
486
 
453
487
  const node = cloneDeep(toRaw(info.node));
454
488
 
455
- let newConfig = await this.toggleFixedPosition(toRaw(config), node, this.get<MApp>('root'));
489
+ let newConfig = await this.toggleFixedPosition(toRaw(config), node, root);
456
490
 
457
491
  newConfig = mergeWith(cloneDeep(node), newConfig, (objValue, srcValue) => {
492
+ if (isObject(srcValue) && Array.isArray(objValue)) {
493
+ // 原来的配置是数组,新的配置是对象,则直接使用新的值
494
+ return srcValue;
495
+ }
458
496
  if (Array.isArray(srcValue)) {
459
497
  return srcValue;
460
498
  }
@@ -463,7 +501,7 @@ class Editor extends BaseService {
463
501
  if (!newConfig.type) throw new Error('配置缺少type值');
464
502
 
465
503
  if (newConfig.type === NodeType.ROOT) {
466
- this.set('root', newConfig);
504
+ this.set('root', newConfig as MApp);
467
505
  return newConfig;
468
506
  }
469
507
 
@@ -484,19 +522,19 @@ class Editor extends BaseService {
484
522
  parentNodeItems[index] = newConfig;
485
523
 
486
524
  // 将update后的配置更新到nodes中
487
- const nodes = this.get<MNode[]>('nodes') || [];
525
+ const nodes = this.get('nodes');
488
526
  const targetIndex = nodes.findIndex((nodeItem: MNode) => `${nodeItem.id}` === `${newConfig.id}`);
489
527
  nodes.splice(targetIndex, 1, newConfig);
490
528
  this.set('nodes', [...nodes]);
491
529
 
492
- this.get<StageCore | null>('stage')?.update({
530
+ this.get('stage')?.update({
493
531
  config: cloneDeep(newConfig),
494
532
  parentId: parent.id,
495
- root: cloneDeep(this.get('root')),
533
+ root: cloneDeep(root),
496
534
  });
497
535
 
498
536
  if (newConfig.type === NodeType.PAGE) {
499
- this.set('page', newConfig);
537
+ this.set('page', newConfig as MPage);
500
538
  }
501
539
 
502
540
  this.addModifiedNodeId(newConfig.id);
@@ -517,7 +555,7 @@ class Editor extends BaseService {
517
555
  this.pushHistoryState();
518
556
 
519
557
  this.emit('update', newNodes);
520
-
558
+ codeBlockService.refreshCombineInfo();
521
559
  return Array.isArray(config) ? newNodes : newNodes[0];
522
560
  }
523
561
 
@@ -528,8 +566,15 @@ class Editor extends BaseService {
528
566
  * @returns void
529
567
  */
530
568
  public async sort(id1: Id, id2: Id): Promise<void> {
531
- const node = this.get<MNode>('node');
532
- const parent = cloneDeep(toRaw(this.get<MContainer>('parent')));
569
+ const root = this.get('root');
570
+ if (!root) throw new Error('root为空');
571
+
572
+ const node = this.get('node');
573
+ if (!node) throw new Error('当前节点为空');
574
+
575
+ const parent = cloneDeep(toRaw(this.get('parent')));
576
+ if (!parent) throw new Error('父节点为空');
577
+
533
578
  const index2 = parent.items.findIndex((node: MNode) => `${node.id}` === `${id2}`);
534
579
  // 在 id1 的兄弟组件中若无 id2 则直接 return
535
580
  if (index2 < 0) return;
@@ -540,10 +585,10 @@ class Editor extends BaseService {
540
585
  await this.update(parent);
541
586
  await this.select(node);
542
587
 
543
- this.get<StageCore | null>('stage')?.update({
588
+ this.get('stage')?.update({
544
589
  config: cloneDeep(node),
545
590
  parentId: parent.id,
546
- root: cloneDeep(this.get('root')),
591
+ root: cloneDeep(root),
547
592
  });
548
593
 
549
594
  this.addModifiedNodeId(parent.id);
@@ -571,9 +616,20 @@ class Editor extends BaseService {
571
616
 
572
617
  if (!Array.isArray(config)) return;
573
618
 
619
+ const node = this.get('node');
620
+
621
+ let parent: MContainer | null = null;
622
+ // 粘贴的组件为当前选中组件的副本时,则添加到当前选中组件的父组件中
623
+ if (config.length === 1 && config[0].id === node?.id) {
624
+ parent = this.get('parent');
625
+ if (parent?.type === NodeType.ROOT) {
626
+ parent = this.get('page');
627
+ }
628
+ }
629
+
574
630
  const pasteConfigs = await this.doPaste(config, position);
575
631
 
576
- return this.add(pasteConfigs);
632
+ return this.add(pasteConfigs, parent);
577
633
  }
578
634
 
579
635
  public async doPaste(config: MNode[], position: PastePosition = {}): Promise<MNode[]> {
@@ -594,7 +650,7 @@ class Editor extends BaseService {
594
650
 
595
651
  if (!node.style) return config;
596
652
 
597
- const stage = this.get<StageCore>('stage');
653
+ const stage = this.get('stage');
598
654
  const doc = stage?.renderer.contentWindow?.document;
599
655
 
600
656
  if (doc) {
@@ -617,13 +673,17 @@ class Editor extends BaseService {
617
673
  */
618
674
  public async alignCenter(config: MNode | MNode[]): Promise<MNode | MNode[]> {
619
675
  const nodes = Array.isArray(config) ? config : [config];
620
- const stage = this.get<StageCore | null>('stage');
676
+ const stage = this.get('stage');
621
677
 
622
678
  const newNodes = await Promise.all(nodes.map((node) => this.doAlignCenter(node)));
623
679
 
624
680
  const newNode = await this.update(newNodes);
625
681
 
626
- await stage?.multiSelect(newNodes.map((node) => node.id));
682
+ if (newNodes.length > 1) {
683
+ await stage?.multiSelect(newNodes.map((node) => node.id));
684
+ } else {
685
+ await stage?.select(newNodes[0].id);
686
+ }
627
687
 
628
688
  return newNode;
629
689
  }
@@ -633,9 +693,16 @@ class Editor extends BaseService {
633
693
  * @param offset 偏移量
634
694
  */
635
695
  public async moveLayer(offset: number | LayerOffset): Promise<void> {
636
- const parent = this.get<MContainer>('parent');
637
- const node = this.get<MNode>('node');
638
- const brothers: MNode[] = parent?.items || [];
696
+ const root = this.get('root');
697
+ if (!root) throw new Error('root为空');
698
+
699
+ const parent = this.get('parent');
700
+ if (!parent) throw new Error('父节点为空');
701
+
702
+ const node = this.get('node');
703
+ if (!node) throw new Error('当前节点为空');
704
+
705
+ const brothers: MNode[] = parent.items || [];
639
706
  const index = brothers.findIndex((item) => `${item.id}` === `${node?.id}`);
640
707
 
641
708
  // 流式布局与绝对定位布局操作的相反的
@@ -653,10 +720,10 @@ class Editor extends BaseService {
653
720
  }
654
721
 
655
722
  const grandparent = this.getParentById(parent.id);
656
- this.get<StageCore | null>('stage')?.update({
723
+ this.get('stage')?.update({
657
724
  config: cloneDeep(toRaw(parent)),
658
725
  parentId: grandparent?.id,
659
- root: cloneDeep(this.get<MApp>('root')),
726
+ root: cloneDeep(root),
660
727
  });
661
728
 
662
729
  this.addModifiedNodeId(parent.id);
@@ -669,13 +736,12 @@ class Editor extends BaseService {
669
736
  * @param targetId 容器ID
670
737
  */
671
738
  public async moveToContainer(config: MNode, targetId: Id): Promise<MNode | undefined> {
739
+ const root = cloneDeep(this.get('root'));
672
740
  const { node, parent } = this.getNodeInfo(config.id, false);
673
741
  const target = this.getNodeById(targetId, false) as MContainer;
674
742
 
675
- const stage = this.get<StageCore | null>('stage');
676
-
677
- if (node && parent && stage) {
678
- const root = cloneDeep(this.get<MApp>('root'));
743
+ const stage = this.get('stage');
744
+ if (root && node && parent && stage) {
679
745
  const index = getNodeIndex(node, parent);
680
746
  parent.items?.splice(index, 1);
681
747
 
@@ -749,8 +815,7 @@ class Editor extends BaseService {
749
815
  });
750
816
  }
751
817
 
752
- public destroy() {
753
- this.removeAllListeners();
818
+ public resetState() {
754
819
  this.set('root', null);
755
820
  this.set('node', null);
756
821
  this.set('nodes', []);
@@ -759,11 +824,17 @@ class Editor extends BaseService {
759
824
  this.set('stage', null);
760
825
  this.set('highlightNode', null);
761
826
  this.set('modifiedNodeIds', new Map());
762
- this.set('pageLength', new Map());
827
+ this.set('pageLength', 0);
828
+ }
829
+
830
+ public destroy() {
831
+ this.removeAllListeners();
832
+ this.resetState();
833
+ this.removeAllPlugins();
763
834
  }
764
835
 
765
836
  public resetModifiedNodeId() {
766
- this.get<Map<Id, Id>>('modifiedNodeIds').clear();
837
+ this.get('modifiedNodeIds').clear();
767
838
  }
768
839
 
769
840
  /**
@@ -771,9 +842,13 @@ class Editor extends BaseService {
771
842
  * @returns {CodeBlockDSL | null}
772
843
  */
773
844
  public async getCodeDsl(): Promise<CodeBlockDSL | null> {
774
- const root = this.get<MApp | null>('root');
775
- if (!root) return null;
776
- return root.codeBlocks || null;
845
+ const root = this.get('root');
846
+ return root?.codeBlocks || null;
847
+ }
848
+
849
+ public getCodeDslSync(): CodeBlockDSL | null {
850
+ const root = this.get('root');
851
+ return root?.codeBlocks || null;
777
852
  }
778
853
 
779
854
  /**
@@ -788,16 +863,17 @@ class Editor extends BaseService {
788
863
 
789
864
  private addModifiedNodeId(id: Id) {
790
865
  if (!this.isHistoryStateChange) {
791
- this.get<Map<Id, Id>>('modifiedNodeIds').set(id, id);
866
+ this.get('modifiedNodeIds').set(id, id);
792
867
  }
793
868
  }
794
869
 
795
870
  private pushHistoryState() {
796
871
  const curNode = cloneDeep(toRaw(this.get('node')));
797
- if (!this.isHistoryStateChange) {
872
+ const page = this.get('page');
873
+ if (!this.isHistoryStateChange && curNode && page) {
798
874
  historyService.push({
799
- data: cloneDeep(toRaw(this.get('page'))),
800
- modifiedNodeIds: this.get<Map<Id, Id>>('modifiedNodeIds'),
875
+ data: cloneDeep(toRaw(page)),
876
+ modifiedNodeIds: this.get('modifiedNodeIds'),
801
877
  nodeId: curNode.id,
802
878
  });
803
879
  }
@@ -813,7 +889,7 @@ class Editor extends BaseService {
813
889
  setTimeout(async () => {
814
890
  if (!value.nodeId) return;
815
891
  await this.select(value.nodeId);
816
- this.get<StageCore | null>('stage')?.select(value.nodeId);
892
+ this.get('stage')?.select(value.nodeId);
817
893
  }, 0);
818
894
  }
819
895
 
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * Tencent is pleased to support the open source community by making TMagicEditor available.
3
3
  *
4
- * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
4
+ * Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
5
5
  *
6
6
  * Licensed under the Apache License, Version 2.0 (the "License");
7
7
  * you may not use this file except in compliance with the License.
@@ -76,10 +76,15 @@ class Events extends BaseService {
76
76
  return cloneDeep(methodMap[type] || DEFAULT_METHODS);
77
77
  }
78
78
 
79
- public destroy() {
79
+ public resetState() {
80
80
  eventMap = reactive({});
81
81
  methodMap = reactive({});
82
+ }
83
+
84
+ public destroy() {
85
+ this.resetState();
82
86
  this.removeAllListeners();
87
+ this.removeAllPlugins();
83
88
  }
84
89
  }
85
90
 
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * Tencent is pleased to support the open source community by making TMagicEditor available.
3
3
  *
4
- * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
4
+ * Copyright (C) 2023 THL A29 Limited, a Tencent company. All rights reserved.
5
5
  *
6
6
  * Licensed under the Apache License, Version 2.0 (the "License");
7
7
  * you may not use this file except in compliance with the License.
@@ -18,25 +18,13 @@
18
18
 
19
19
  import { reactive } from 'vue';
20
20
 
21
- import type { Id, MPage } from '@tmagic/schema';
21
+ import type { MPage } from '@tmagic/schema';
22
22
 
23
+ import { HistoryState, StepValue } from '../type';
23
24
  import { UndoRedo } from '../utils/undo-redo';
24
25
 
25
26
  import BaseService from './BaseService';
26
27
 
27
- export interface StepValue {
28
- data: MPage;
29
- modifiedNodeIds: Map<Id, Id>;
30
- nodeId: Id;
31
- }
32
-
33
- export interface HistoryState {
34
- pageId?: Id;
35
- pageSteps: Record<Id, UndoRedo<StepValue>>;
36
- canRedo: boolean;
37
- canUndo: boolean;
38
- }
39
-
40
28
  class History extends BaseService {
41
29
  public state = reactive<HistoryState>({
42
30
  pageSteps: {},
@@ -76,9 +64,11 @@ class History extends BaseService {
76
64
  }
77
65
 
78
66
  this.setCanUndoRedo();
67
+
68
+ this.emit('page-change', this.state.pageSteps[this.state.pageId]);
79
69
  }
80
70
 
81
- public empty(): void {
71
+ public resetState(): void {
82
72
  this.state.pageId = undefined;
83
73
  this.state.pageSteps = {};
84
74
  this.state.canRedo = false;
@@ -110,8 +100,9 @@ class History extends BaseService {
110
100
  }
111
101
 
112
102
  public destroy(): void {
113
- this.empty();
103
+ this.resetState();
114
104
  this.removeAllListeners();
105
+ this.removeAllPlugins();
115
106
  }
116
107
 
117
108
  private getUndoRedo() {