@tmagic/editor 1.3.5 → 1.3.7

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 (142) hide show
  1. package/LICENSE +5432 -0
  2. package/dist/style.css +88 -62
  3. package/dist/tmagic-editor.js +2448 -2016
  4. package/dist/tmagic-editor.js.map +1 -1
  5. package/dist/tmagic-editor.umd.cjs +2453 -2019
  6. package/dist/tmagic-editor.umd.cjs.map +1 -1
  7. package/package.json +23 -23
  8. package/src/Editor.vue +15 -7
  9. package/src/components/CodeBlockEditor.vue +2 -2
  10. package/src/components/ContentMenu.vue +5 -5
  11. package/src/components/FloatingBox.vue +117 -0
  12. package/src/components/ScrollBar.vue +2 -2
  13. package/src/components/ScrollViewer.vue +2 -2
  14. package/src/components/SplitView.vue +2 -2
  15. package/src/components/Tree.vue +5 -0
  16. package/src/components/TreeNode.vue +10 -4
  17. package/src/editorProps.ts +20 -3
  18. package/src/fields/PageFragmentSelect.vue +55 -0
  19. package/src/hooks/use-filter.ts +55 -0
  20. package/src/hooks/use-getso.ts +2 -2
  21. package/src/hooks/use-node-status.ts +48 -0
  22. package/src/hooks/use-stage.ts +19 -1
  23. package/src/index.ts +3 -0
  24. package/src/initService.ts +43 -17
  25. package/src/layouts/AddPageBox.vue +16 -4
  26. package/src/layouts/CodeEditor.vue +2 -2
  27. package/src/layouts/Framework.vue +17 -3
  28. package/src/layouts/PropsPanel.vue +2 -2
  29. package/src/layouts/page-bar/AddButton.vue +48 -0
  30. package/src/layouts/page-bar/PageBar.vue +158 -0
  31. package/src/layouts/{workspace → page-bar}/PageBarScrollContainer.vue +61 -35
  32. package/src/layouts/page-bar/SwitchTypeButton.vue +45 -0
  33. package/src/layouts/sidebar/Sidebar.vue +9 -2
  34. package/src/layouts/sidebar/code-block/CodeBlockList.vue +55 -65
  35. package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +2 -1
  36. package/src/layouts/sidebar/data-source/DataSourceList.vue +63 -59
  37. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +1 -1
  38. package/src/layouts/sidebar/layer/LayerMenu.vue +39 -27
  39. package/src/layouts/sidebar/layer/LayerPanel.vue +31 -8
  40. package/src/layouts/sidebar/layer/use-click.ts +5 -3
  41. package/src/layouts/sidebar/layer/use-keybinding.ts +8 -8
  42. package/src/layouts/sidebar/layer/use-node-status.ts +10 -13
  43. package/src/layouts/workspace/Workspace.vue +6 -7
  44. package/src/layouts/workspace/viewer/NodeListMenu.vue +68 -129
  45. package/src/layouts/workspace/viewer/Stage.vue +14 -6
  46. package/src/layouts/workspace/viewer/ViewerMenu.vue +94 -86
  47. package/src/services/BaseService.ts +63 -26
  48. package/src/services/codeBlock.ts +7 -1
  49. package/src/services/dep.ts +12 -8
  50. package/src/services/editor.ts +131 -64
  51. package/src/services/history.ts +6 -2
  52. package/src/services/keybinding.ts +48 -13
  53. package/src/services/props.ts +70 -12
  54. package/src/services/storage.ts +23 -13
  55. package/src/services/ui.ts +4 -1
  56. package/src/theme/code-block.scss +0 -16
  57. package/src/theme/floating-box.scss +30 -0
  58. package/src/theme/layer-panel.scss +19 -0
  59. package/src/theme/page-bar.scss +18 -1
  60. package/src/theme/stage.scss +24 -0
  61. package/src/theme/theme.scss +1 -1
  62. package/src/theme/tree.scss +12 -1
  63. package/src/type.ts +28 -10
  64. package/src/utils/compose.ts +25 -6
  65. package/src/utils/content-menu.ts +6 -7
  66. package/src/utils/editor.ts +34 -18
  67. package/src/utils/operator.ts +5 -6
  68. package/types/components/CodeBlockEditor.vue.d.ts +6 -2
  69. package/types/components/CodeParams.vue.d.ts +3 -1
  70. package/types/components/ContentMenu.vue.d.ts +13 -5
  71. package/types/components/FloatingBox.vue.d.ts +78 -0
  72. package/types/components/Resizer.vue.d.ts +6 -1
  73. package/types/components/ScrollBar.vue.d.ts +3 -1
  74. package/types/components/ScrollViewer.vue.d.ts +3 -3
  75. package/types/components/SearchInput.vue.d.ts +3 -1
  76. package/types/components/SplitView.vue.d.ts +8 -4
  77. package/types/components/ToolButton.vue.d.ts +3 -3
  78. package/types/components/Tree.vue.d.ts +27 -8
  79. package/types/components/TreeNode.vue.d.ts +24 -7
  80. package/types/editorProps.d.ts +16 -1
  81. package/types/fields/Code.vue.d.ts +6 -2
  82. package/types/fields/CodeLink.vue.d.ts +5 -3
  83. package/types/fields/CodeSelect.vue.d.ts +3 -1
  84. package/types/fields/CodeSelectCol.vue.d.ts +3 -1
  85. package/types/fields/DataSourceFieldSelect.vue.d.ts +3 -1
  86. package/types/fields/DataSourceFields.vue.d.ts +5 -3
  87. package/types/fields/DataSourceInput.vue.d.ts +8 -4
  88. package/types/fields/DataSourceMethodSelect.vue.d.ts +3 -1
  89. package/types/fields/DataSourceMethods.vue.d.ts +5 -3
  90. package/types/fields/DataSourceMocks.vue.d.ts +5 -3
  91. package/types/fields/DataSourceSelect.vue.d.ts +5 -3
  92. package/types/fields/EventSelect.vue.d.ts +3 -1
  93. package/types/fields/KeyValue.vue.d.ts +2 -2
  94. package/types/fields/PageFragmentSelect.vue.d.ts +31 -0
  95. package/types/fields/UISelect.vue.d.ts +3 -1
  96. package/types/hooks/use-code-block-edit.d.ts +14 -5
  97. package/types/hooks/use-data-source-method.d.ts +14 -5
  98. package/types/hooks/use-filter.d.ts +7 -0
  99. package/types/hooks/use-node-status.d.ts +6 -0
  100. package/types/index.d.ts +1 -0
  101. package/types/layouts/AddPageBox.vue.d.ts +14 -1
  102. package/types/layouts/CodeEditor.vue.d.ts +4 -1
  103. package/types/layouts/Framework.vue.d.ts +14 -1
  104. package/types/layouts/PropsPanel.vue.d.ts +19 -13
  105. package/types/layouts/{workspace/viewer/NodeListMenuTitle.vue.d.ts → page-bar/AddButton.vue.d.ts} +3 -2
  106. package/types/layouts/page-bar/PageBar.vue.d.ts +28 -0
  107. package/types/layouts/page-bar/PageBarScrollContainer.vue.d.ts +24 -0
  108. package/types/layouts/page-bar/SwitchTypeButton.vue.d.ts +20 -0
  109. package/types/layouts/sidebar/ComponentListPanel.vue.d.ts +1 -1
  110. package/types/layouts/sidebar/Sidebar.vue.d.ts +7 -5
  111. package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +9 -3
  112. package/types/layouts/sidebar/code-block/CodeBlockListPanel.vue.d.ts +1 -1
  113. package/types/layouts/sidebar/data-source/DataSourceConfigPanel.vue.d.ts +3 -1
  114. package/types/layouts/sidebar/data-source/DataSourceList.vue.d.ts +8 -2
  115. package/types/layouts/sidebar/data-source/DataSourceListPanel.vue.d.ts +1 -1
  116. package/types/layouts/sidebar/layer/LayerMenu.vue.d.ts +26 -3
  117. package/types/layouts/sidebar/layer/LayerPanel.vue.d.ts +3 -1
  118. package/types/layouts/sidebar/layer/use-click.d.ts +37 -6
  119. package/types/layouts/sidebar/layer/use-keybinding.d.ts +1 -1
  120. package/types/layouts/sidebar/layer/use-node-status.d.ts +3 -4
  121. package/types/layouts/workspace/Workspace.vue.d.ts +3 -1
  122. package/types/layouts/workspace/viewer/NodeListMenu.vue.d.ts +1 -14
  123. package/types/layouts/workspace/viewer/Stage.vue.d.ts +2 -0
  124. package/types/layouts/workspace/viewer/ViewerMenu.vue.d.ts +9 -1
  125. package/types/services/BaseService.d.ts +4 -1
  126. package/types/services/dataSource.d.ts +1 -0
  127. package/types/services/dep.d.ts +6 -5
  128. package/types/services/editor.d.ts +12 -5
  129. package/types/services/history.d.ts +3 -2
  130. package/types/services/keybinding.d.ts +21 -1
  131. package/types/services/props.d.ts +25 -1
  132. package/types/services/storage.d.ts +7 -7
  133. package/types/type.d.ts +32 -17
  134. package/types/utils/compose.d.ts +1 -1
  135. package/types/utils/editor.d.ts +14 -8
  136. package/src/layouts/sidebar/layer/use-filter.ts +0 -59
  137. package/src/layouts/workspace/PageBar.vue +0 -87
  138. package/src/layouts/workspace/viewer/NodeListMenuTitle.vue +0 -68
  139. package/src/theme/dep-list.scss +0 -59
  140. package/types/layouts/sidebar/layer/use-filter.d.ts +0 -7
  141. package/types/layouts/workspace/PageBar.vue.d.ts +0 -15
  142. package/types/layouts/workspace/PageBarScrollContainer.vue.d.ts +0 -9
@@ -17,13 +17,14 @@
17
17
  */
18
18
 
19
19
  import { reactive, toRaw } from 'vue';
20
- import { cloneDeep, isObject, mergeWith, uniq } from 'lodash-es';
20
+ import { cloneDeep, get, isObject, mergeWith, uniq } from 'lodash-es';
21
21
 
22
- import type { Id, MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
22
+ import { DepTargetType } from '@tmagic/dep';
23
+ import type { Id, MApp, MComponent, MContainer, MNode, MPage, MPageFragment } from '@tmagic/schema';
23
24
  import { NodeType } from '@tmagic/schema';
24
- import StageCore from '@tmagic/stage';
25
- import { getNodePath, isNumber, isPage, isPop } from '@tmagic/utils';
25
+ import { getNodePath, isNumber, isPage, isPageFragment, isPop } from '@tmagic/utils';
26
26
 
27
+ import depService from '@editor/services/dep';
27
28
  import historyService from '@editor/services/history';
28
29
  import storageService, { Protocol } from '@editor/services/storage';
29
30
  import type { AddMNode, EditorNodeInfo, PastePosition, StepValue, StoreState, StoreStateKey } from '@editor/type';
@@ -35,8 +36,10 @@ import {
35
36
  fixNodePosition,
36
37
  getInitPositionStyle,
37
38
  getNodeIndex,
39
+ getPageFragmentList,
40
+ getPageList,
38
41
  isFixed,
39
- setChilrenLayout,
42
+ setChildrenLayout,
40
43
  setLayout,
41
44
  } from '@editor/utils/editor';
42
45
  import { beforePaste, getAddParent } from '@editor/utils/operator';
@@ -52,36 +55,39 @@ class Editor extends BaseService {
52
55
  node: null,
53
56
  nodes: [],
54
57
  stage: null,
58
+ stageLoading: true,
55
59
  highlightNode: null,
56
60
  modifiedNodeIds: new Map(),
57
61
  pageLength: 0,
62
+ pageFragmentLength: 0,
63
+ disabledMultiSelect: false,
58
64
  });
59
65
  private isHistoryStateChange = false;
60
66
 
61
67
  constructor() {
62
68
  super(
63
69
  [
64
- 'getLayout',
65
- 'select',
66
- 'doAdd',
67
- 'add',
68
- 'doRemove',
69
- 'remove',
70
- 'doUpdate',
71
- 'update',
72
- 'sort',
73
- 'copy',
74
- 'paste',
75
- 'doPaste',
76
- 'doAlignCenter',
77
- 'alignCenter',
78
- 'moveLayer',
79
- 'moveToContainer',
80
- 'move',
81
- 'undo',
82
- 'redo',
83
- 'highlight',
84
- 'dragTo',
70
+ { name: 'getLayout', isAsync: true },
71
+ { name: 'select', isAsync: true },
72
+ { name: 'doAdd', isAsync: true },
73
+ { name: 'add', isAsync: true },
74
+ { name: 'doRemove', isAsync: true },
75
+ { name: 'remove', isAsync: true },
76
+ { name: 'doUpdate', isAsync: true },
77
+ { name: 'update', isAsync: true },
78
+ { name: 'sort', isAsync: true },
79
+ { name: 'copy', isAsync: true },
80
+ { name: 'paste', isAsync: true },
81
+ { name: 'doPaste', isAsync: true },
82
+ { name: 'doAlignCenter', isAsync: true },
83
+ { name: 'alignCenter', isAsync: true },
84
+ { name: 'moveLayer', isAsync: true },
85
+ { name: 'moveToContainer', isAsync: true },
86
+ { name: 'move', isAsync: true },
87
+ { name: 'undo', isAsync: true },
88
+ { name: 'redo', isAsync: true },
89
+ { name: 'highlight', isAsync: true },
90
+ { name: 'dragTo', isAsync: true },
85
91
  ],
86
92
  // 需要注意循环依赖问题,如果函数间有相互调用的话,不能设置为串行调用
87
93
  ['select', 'update', 'moveLayer'],
@@ -90,7 +96,7 @@ class Editor extends BaseService {
90
96
 
91
97
  /**
92
98
  * 设置当前指点节点配置
93
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength'
99
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength
94
100
  * @param value MNode
95
101
  */
96
102
  public set<K extends StoreStateKey, T extends StoreState[K]>(name: K, value: T) {
@@ -107,10 +113,15 @@ class Editor extends BaseService {
107
113
  throw new Error('root 不能为数组');
108
114
  }
109
115
 
110
- if (value && isObject(value) && !(value instanceof StageCore) && !(value instanceof Map)) {
111
- this.state.pageLength = value.items?.length || 0;
116
+ if (value && isObject(value)) {
117
+ const app = value as MApp;
118
+ this.state.pageLength = getPageList(app).length || 0;
119
+ this.state.pageFragmentLength = getPageFragmentList(app).length || 0;
120
+ this.state.stageLoading = this.state.pageLength !== 0;
112
121
  } else {
113
122
  this.state.pageLength = 0;
123
+ this.state.pageFragmentLength = 0;
124
+ this.state.stageLoading = false;
114
125
  }
115
126
 
116
127
  this.emit('root-change', value, preValue);
@@ -119,7 +130,7 @@ class Editor extends BaseService {
119
130
 
120
131
  /**
121
132
  * 获取当前指点节点配置
122
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength'
133
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength'
123
134
  * @returns MNode
124
135
  */
125
136
  public get<K extends StoreStateKey>(name: K): StoreState[K] {
@@ -161,8 +172,8 @@ class Editor extends BaseService {
161
172
  info.parent = path[path.length - 2] as MContainer;
162
173
 
163
174
  path.forEach((item) => {
164
- if (item.type === NodeType.PAGE) {
165
- info.page = item as MPage;
175
+ if (isPage(item) || isPageFragment(item)) {
176
+ info.page = item as MPage | MPageFragment;
166
177
  return;
167
178
  }
168
179
  });
@@ -310,6 +321,17 @@ class Editor extends BaseService {
310
321
  this.set('nodes', nodes);
311
322
  }
312
323
 
324
+ public selectRoot() {
325
+ const root = this.get('root');
326
+ if (!root) return;
327
+
328
+ this.set('nodes', [root]);
329
+ this.set('parent', null);
330
+ this.set('page', null);
331
+ this.set('stage', null);
332
+ this.set('highlightNode', null);
333
+ }
334
+
313
335
  public async doAdd(node: MNode, parent: MContainer): Promise<MNode> {
314
336
  const root = this.get('root');
315
337
 
@@ -320,11 +342,11 @@ class Editor extends BaseService {
320
342
 
321
343
  if (!curNode) throw new Error('当前选中节点为空');
322
344
 
323
- if ((parent.type === NodeType.ROOT || curNode?.type === NodeType.ROOT) && node.type !== NodeType.PAGE) {
345
+ if ((parent.type === NodeType.ROOT || curNode?.type === NodeType.ROOT) && !(isPage(node) || isPageFragment(node))) {
324
346
  throw new Error('app下不能添加组件');
325
347
  }
326
348
 
327
- if (parent.id !== curNode.id && node.type !== NodeType.PAGE) {
349
+ if (parent.id !== curNode.id && !(isPage(node) || isPageFragment(node))) {
328
350
  const index = parent.items.indexOf(curNode);
329
351
  parent.items?.splice(index + 1, 0, node);
330
352
  } else {
@@ -378,7 +400,7 @@ class Editor extends BaseService {
378
400
  const newNodes = await Promise.all(
379
401
  addNodes.map((node) => {
380
402
  const root = this.get('root');
381
- if (isPage(node) && root) {
403
+ if ((isPage(node) || isPageFragment(node)) && root) {
382
404
  return this.doAdd(node, root);
383
405
  }
384
406
  const parentNode = parent && typeof parent !== 'function' ? parent : getAddParent(node);
@@ -397,13 +419,15 @@ class Editor extends BaseService {
397
419
 
398
420
  if (isPage(newNodes[0])) {
399
421
  this.state.pageLength += 1;
422
+ } else if (isPageFragment(newNodes[0])) {
423
+ this.state.pageFragmentLength += 1;
400
424
  } else {
401
425
  // 新增页面,这个时候页面还有渲染出来,此时select会出错,在runtime-ready的时候回去select
402
426
  stage?.select(newNodes[0].id);
403
427
  }
404
428
  }
405
429
 
406
- if (!isPage(newNodes[0])) {
430
+ if (!(isPage(newNodes[0]) || isPageFragment(newNodes[0]))) {
407
431
  this.pushHistoryState();
408
432
  }
409
433
 
@@ -416,7 +440,7 @@ class Editor extends BaseService {
416
440
  const root = this.get('root');
417
441
  if (!root) throw new Error('root不能为空');
418
442
 
419
- const { parent, node: curNode } = this.getNodeInfo(node.id);
443
+ const { parent, node: curNode } = this.getNodeInfo(node.id, false);
420
444
 
421
445
  if (!parent || !curNode) throw new Error('找不要删除的节点');
422
446
 
@@ -428,29 +452,38 @@ class Editor extends BaseService {
428
452
  const stage = this.get('stage');
429
453
  stage?.remove({ id: node.id, parentId: parent.id, root: cloneDeep(root) });
430
454
 
431
- if (node.type === NodeType.PAGE) {
432
- this.state.pageLength -= 1;
433
-
434
- if (root.items[0]) {
435
- await this.select(root.items[0]);
436
- stage?.select(root.items[0].id);
455
+ const selectDefault = async (pages: MNode[]) => {
456
+ if (pages[0]) {
457
+ await this.select(pages[0]);
458
+ stage?.select(pages[0].id);
437
459
  } else {
438
- this.set('nodes', [root]);
439
- this.set('parent', null);
440
- this.set('page', null);
441
- this.set('stage', null);
442
- this.set('highlightNode', null);
443
- this.resetModifiedNodeId();
444
- historyService.reset();
460
+ this.selectRoot();
445
461
 
446
- return;
462
+ historyService.resetPage();
447
463
  }
464
+ };
465
+
466
+ const rootItems = root.items || [];
467
+
468
+ if (isPage(node)) {
469
+ this.state.pageLength -= 1;
470
+
471
+ await selectDefault(getPageList(root));
472
+ } else if (isPageFragment(node)) {
473
+ this.state.pageFragmentLength -= 1;
474
+
475
+ await selectDefault(getPageFragmentList(root));
448
476
  } else {
449
477
  await this.select(parent);
450
478
  stage?.select(parent.id);
479
+
480
+ this.addModifiedNodeId(parent.id);
451
481
  }
452
482
 
453
- this.addModifiedNodeId(parent.id);
483
+ if (!rootItems.length) {
484
+ this.resetModifiedNodeId();
485
+ historyService.reset();
486
+ }
454
487
  }
455
488
 
456
489
  /**
@@ -462,7 +495,7 @@ class Editor extends BaseService {
462
495
 
463
496
  await Promise.all(nodes.map((node) => this.doRemove(node)));
464
497
 
465
- if (!isPage(nodes[0])) {
498
+ if (!(isPage(nodes[0]) || isPageFragment(nodes[0]))) {
466
499
  // 更新历史记录
467
500
  this.pushHistoryState();
468
501
  }
@@ -512,7 +545,7 @@ class Editor extends BaseService {
512
545
  const newLayout = await this.getLayout(newConfig);
513
546
  const layout = await this.getLayout(node);
514
547
  if (Array.isArray(newConfig.items) && newLayout !== layout) {
515
- newConfig = setChilrenLayout(newConfig as MContainer, newLayout);
548
+ newConfig = setChildrenLayout(newConfig as MContainer, newLayout);
516
549
  }
517
550
 
518
551
  parentNodeItems[index] = newConfig;
@@ -529,8 +562,8 @@ class Editor extends BaseService {
529
562
  root: cloneDeep(root),
530
563
  });
531
564
 
532
- if (newConfig.type === NodeType.PAGE) {
533
- this.set('page', newConfig as MPage);
565
+ if (isPage(newConfig) || isPageFragment(newConfig)) {
566
+ this.set('page', newConfig as MPage | MPageFragment);
534
567
  }
535
568
 
536
569
  this.addModifiedNodeId(newConfig.id);
@@ -593,12 +626,47 @@ class Editor extends BaseService {
593
626
  }
594
627
 
595
628
  /**
596
- * 将组将节点配置转化成string,然后存储到localStorage中
629
+ * 将组件节点配置存储到localStorage中
597
630
  * @param config 组件节点配置
598
- * @returns 组件节点配置
631
+ * @returns
632
+ */
633
+ public copy(config: MNode | MNode[]): void {
634
+ storageService.setItem(COPY_STORAGE_KEY, Array.isArray(config) ? config : [config], {
635
+ protocol: Protocol.OBJECT,
636
+ });
637
+ }
638
+
639
+ /**
640
+ * 复制时会带上组件关联的依赖
641
+ * @param config 组件节点配置
642
+ * @returns
599
643
  */
600
- public async copy(config: MNode | MNode[]): Promise<void> {
601
- await storageService.setItem(COPY_STORAGE_KEY, Array.isArray(config) ? config : [config], {
644
+ public copyWithRelated(config: MNode | MNode[]): void {
645
+ const copyNodes: MNode[] = Array.isArray(config) ? config : [config];
646
+ // 关联的组件也一并复制
647
+ depService.getTarget(DepTargetType.RELATED_COMP_WHEN_COPY, DepTargetType.RELATED_COMP_WHEN_COPY)?.removeDep();
648
+ depService.collect(copyNodes, true, DepTargetType.RELATED_COMP_WHEN_COPY);
649
+ const customTarget = depService.getTarget(
650
+ DepTargetType.RELATED_COMP_WHEN_COPY,
651
+ DepTargetType.RELATED_COMP_WHEN_COPY,
652
+ );
653
+ if (customTarget) {
654
+ Object.keys(customTarget.deps).forEach((nodeId: Id) => {
655
+ const node = this.getNodeById(nodeId);
656
+ if (!node) return;
657
+ customTarget!.deps[nodeId].keys.forEach((key) => {
658
+ const relateNodeId = get(node, key);
659
+ const isExist = copyNodes.find((node) => node.id === relateNodeId);
660
+ if (!isExist) {
661
+ const relateNode = this.getNodeById(relateNodeId);
662
+ if (relateNode) {
663
+ copyNodes.push(relateNode);
664
+ }
665
+ }
666
+ });
667
+ });
668
+ }
669
+ storageService.setItem(COPY_STORAGE_KEY, copyNodes, {
602
670
  protocol: Protocol.OBJECT,
603
671
  });
604
672
  }
@@ -609,8 +677,7 @@ class Editor extends BaseService {
609
677
  * @returns 添加后的组件节点配置
610
678
  */
611
679
  public async paste(position: PastePosition = {}): Promise<MNode | MNode[] | void> {
612
- const config: MNode[] = await storageService.getItem(COPY_STORAGE_KEY);
613
-
680
+ const config: MNode[] = storageService.getItem(COPY_STORAGE_KEY);
614
681
  if (!Array.isArray(config)) return;
615
682
 
616
683
  const node = this.get('node');
@@ -623,13 +690,13 @@ class Editor extends BaseService {
623
690
  parent = this.get('page');
624
691
  }
625
692
  }
626
-
627
693
  const pasteConfigs = await this.doPaste(config, position);
628
-
694
+ propsService.replaceRelateId(config, pasteConfigs);
629
695
  return this.add(pasteConfigs, parent);
630
696
  }
631
697
 
632
698
  public async doPaste(config: MNode[], position: PastePosition = {}): Promise<MNode[]> {
699
+ propsService.clearRelateId();
633
700
  const pasteConfigs = await beforePaste(position, cloneDeep(config));
634
701
  return pasteConfigs;
635
702
  }
@@ -18,7 +18,7 @@
18
18
 
19
19
  import { reactive } from 'vue';
20
20
 
21
- import type { MPage } from '@tmagic/schema';
21
+ import type { MPage, MPageFragment } from '@tmagic/schema';
22
22
 
23
23
  import type { HistoryState, StepValue } from '@editor/type';
24
24
  import { UndoRedo } from '@editor/utils/undo-redo';
@@ -41,12 +41,16 @@ class History extends BaseService {
41
41
 
42
42
  public reset() {
43
43
  this.state.pageSteps = {};
44
+ this.resetPage();
45
+ }
46
+
47
+ public resetPage() {
44
48
  this.state.pageId = undefined;
45
49
  this.state.canRedo = false;
46
50
  this.state.canUndo = false;
47
51
  }
48
52
 
49
- public changePage(page: MPage): void {
53
+ public changePage(page: MPage | MPageFragment): void {
50
54
  if (!page) return;
51
55
 
52
56
  this.state.pageId = page.id;
@@ -1,6 +1,6 @@
1
1
  import KeyController, { KeyControllerEvent } from 'keycon';
2
2
 
3
- import { isPage } from '@tmagic/utils';
3
+ import { isPage, isPageFragment } from '@tmagic/utils';
4
4
 
5
5
  import { KeyBindingCacheItem, KeyBindingCommand, KeyBindingItem } from '@editor/type';
6
6
 
@@ -19,7 +19,7 @@ class Keybinding extends BaseService {
19
19
  [KeyBindingCommand.DELETE_NODE]: () => {
20
20
  const nodes = editorService.get('nodes');
21
21
 
22
- if (!nodes || isPage(nodes[0])) return;
22
+ if (!nodes || isPage(nodes[0]) || isPageFragment(nodes[0])) return;
23
23
  editorService.remove(nodes);
24
24
  },
25
25
  [KeyBindingCommand.COPY_NODE]: () => {
@@ -29,7 +29,7 @@ class Keybinding extends BaseService {
29
29
  [KeyBindingCommand.CUT_NODE]: () => {
30
30
  const nodes = editorService.get('nodes');
31
31
 
32
- if (!nodes || isPage(nodes[0])) return;
32
+ if (!nodes || isPage(nodes[0]) || isPageFragment(nodes[0])) return;
33
33
  editorService.copy(nodes);
34
34
  editorService.remove(nodes);
35
35
  },
@@ -84,15 +84,29 @@ class Keybinding extends BaseService {
84
84
  },
85
85
  };
86
86
 
87
- public registeCommand(command: string, handler: (e: KeyboardEvent) => void | Promise<void>) {
87
+ public registerCommand(command: string, handler: (e: KeyboardEvent) => void | Promise<void>) {
88
88
  this.commands[command] = handler;
89
89
  }
90
90
 
91
- public unregisteCommand(command: string) {
91
+ /**
92
+ * @deprecated
93
+ */
94
+ public registeCommand(command: string, handler: (e: KeyboardEvent) => void | Promise<void>) {
95
+ this.registerCommand(command, handler);
96
+ }
97
+
98
+ public unregisterCommand(command: string) {
92
99
  delete this.commands[command];
93
100
  }
94
101
 
95
- public registeEl(name: string, el?: HTMLElement) {
102
+ /**
103
+ * @deprecated
104
+ */
105
+ public unregisteCommand(command: string) {
106
+ this.unregisterCommand(command);
107
+ }
108
+
109
+ public registerEl(name: string, el?: HTMLElement) {
96
110
  if (name !== 'global' && !el) {
97
111
  throw new Error('只有name为global可以不传el');
98
112
  }
@@ -102,20 +116,34 @@ class Keybinding extends BaseService {
102
116
  this.bind(name);
103
117
  }
104
118
 
105
- public unregisteEl(name: string) {
119
+ /**
120
+ * @deprecated
121
+ */
122
+ public registeEl(name: string, el?: HTMLElement) {
123
+ this.registerEl(name, el);
124
+ }
125
+
126
+ public unregisterEl(name: string) {
106
127
  this.controllers.get(name)?.destroy();
107
128
  this.controllers.delete(name);
108
129
  this.bindingList.forEach((item) => {
109
- item.binded = false;
130
+ item.bound = false;
110
131
  });
111
132
  }
112
133
 
113
- public registe(maps: KeyBindingItem[]) {
134
+ /**
135
+ * @deprecated
136
+ */
137
+ public unregisteEl(name: string) {
138
+ this.unregisterEl(name);
139
+ }
140
+
141
+ public register(maps: KeyBindingItem[]) {
114
142
  for (const keybindingItem of maps) {
115
143
  const { command, keybinding, when } = keybindingItem;
116
144
 
117
145
  for (const [type = '', eventType = 'keydown'] of when) {
118
- const cacheItem: KeyBindingCacheItem = { type, command, keybinding, eventType, binded: false };
146
+ const cacheItem: KeyBindingCacheItem = { type, command, keybinding, eventType, bound: false };
119
147
 
120
148
  this.bindingList.push(cacheItem);
121
149
  }
@@ -124,6 +152,13 @@ class Keybinding extends BaseService {
124
152
  this.bind();
125
153
  }
126
154
 
155
+ /**
156
+ * @deprecated
157
+ */
158
+ public registe(map: KeyBindingItem[]) {
159
+ this.register(map);
160
+ }
161
+
127
162
  public reset() {
128
163
  this.controllers.forEach((keycon) => {
129
164
  keycon.destroy();
@@ -138,13 +173,13 @@ class Keybinding extends BaseService {
138
173
 
139
174
  private bind(name?: string) {
140
175
  for (const item of this.bindingList) {
141
- const { type, eventType, command, keybinding, binded } = item;
176
+ const { type, eventType, command, keybinding, bound } = item;
142
177
 
143
178
  if (name && name !== type) {
144
179
  continue;
145
180
  }
146
181
 
147
- if (binded) {
182
+ if (bound) {
148
183
  continue;
149
184
  }
150
185
 
@@ -166,7 +201,7 @@ class Keybinding extends BaseService {
166
201
  }
167
202
  });
168
203
 
169
- item.binded = true;
204
+ item.bound = true;
170
205
  }
171
206
  }
172
207
 
@@ -17,12 +17,15 @@
17
17
  */
18
18
 
19
19
  import { reactive } from 'vue';
20
- import { cloneDeep, mergeWith } from 'lodash-es';
20
+ import { cloneDeep, get, mergeWith, set } from 'lodash-es';
21
21
 
22
+ import { DepTargetType } from '@tmagic/dep';
22
23
  import type { FormConfig } from '@tmagic/form';
23
- import type { MComponent, MNode } from '@tmagic/schema';
24
+ import type { Id, MComponent, MNode } from '@tmagic/schema';
24
25
  import { guid, toLine } from '@tmagic/utils';
25
26
 
27
+ import depService from '@editor/services/dep';
28
+ import editorService from '@editor/services/editor';
26
29
  import type { PropsState } from '@editor/type';
27
30
  import { fillConfig } from '@editor/utils/props';
28
31
 
@@ -32,18 +35,19 @@ class Props extends BaseService {
32
35
  private state = reactive<PropsState>({
33
36
  propsConfigMap: {},
34
37
  propsValueMap: {},
38
+ relateIdMap: {},
35
39
  });
36
40
 
37
41
  constructor() {
38
42
  super([
39
- 'setPropsConfig',
40
- 'getPropsConfig',
41
- 'setPropsValue',
42
- 'getPropsValue',
43
- 'createId',
44
- 'setNewItemId',
45
- 'fillConfig',
46
- 'getDefaultPropsValue',
43
+ { name: 'setPropsConfig', isAsync: true },
44
+ { name: 'getPropsConfig', isAsync: true },
45
+ { name: 'setPropsValue', isAsync: true },
46
+ { name: 'getPropsValue', isAsync: true },
47
+ { name: 'createId', isAsync: false },
48
+ { name: 'setNewItemId', isAsync: true },
49
+ { name: 'fillConfig', isAsync: true },
50
+ { name: 'getDefaultPropsValue', isAsync: true },
47
51
  ]);
48
52
  }
49
53
 
@@ -135,11 +139,17 @@ class Props extends BaseService {
135
139
 
136
140
  /**
137
141
  * 将组件与组件的子元素配置中的id都设置成一个新的ID
142
+ * 如果没有相同ID并且force为false则保持不变
138
143
  * @param {Object} config 组件配置
144
+ * @param {Boolean} force 是否强制设置新的ID
139
145
  */
140
146
  /* eslint no-param-reassign: ["error", { "props": false }] */
141
- public async setNewItemId(config: MNode) {
142
- config.id = await this.createId(config.type || 'component');
147
+ public async setNewItemId(config: MNode, force = true) {
148
+ if (force || editorService.getNodeById(config.id)) {
149
+ const newId = await this.createId(config.type || 'component');
150
+ this.setRelateId(config.id, newId);
151
+ config.id = newId;
152
+ }
143
153
 
144
154
  if (config.items && Array.isArray(config.items)) {
145
155
  for (const item of config.items) {
@@ -176,11 +186,59 @@ class Props extends BaseService {
176
186
  this.state.propsValueMap = {};
177
187
  }
178
188
 
189
+ /**
190
+ * 替换关联ID
191
+ * @param originConfigs 原组件配置
192
+ * @param targetConfigs 待替换的组件配置
193
+ */
194
+ public replaceRelateId(originConfigs: MNode[], targetConfigs: MNode[]) {
195
+ const relateIdMap = this.getRelateIdMap();
196
+ if (Object.keys(relateIdMap).length === 0) return;
197
+ const target = depService.getTarget(DepTargetType.RELATED_COMP_WHEN_COPY, DepTargetType.RELATED_COMP_WHEN_COPY);
198
+ if (!target) return;
199
+ originConfigs.forEach((config: MNode) => {
200
+ const newId = relateIdMap[config.id];
201
+ const targetConfig = targetConfigs.find((targetConfig) => targetConfig.id === newId);
202
+ if (!targetConfig) return;
203
+ target.deps[config.id]?.keys?.forEach((fullKey) => {
204
+ const relateOriginId = get(config, fullKey);
205
+ const relateTargetId = relateIdMap[relateOriginId];
206
+ if (!relateTargetId) return;
207
+ set(targetConfig, fullKey, relateTargetId);
208
+ });
209
+ });
210
+ }
211
+
212
+ /**
213
+ * 清除setNewItemId前后映射关系
214
+ */
215
+ public clearRelateId() {
216
+ this.state.relateIdMap = {};
217
+ }
218
+
179
219
  public destroy() {
180
220
  this.resetState();
181
221
  this.removeAllListeners();
182
222
  this.removeAllPlugins();
183
223
  }
224
+
225
+ /**
226
+ * 获取setNewItemId前后映射关系
227
+ * @param oldId 原组件ID
228
+ * @returns 新旧ID映射
229
+ */
230
+ private getRelateIdMap() {
231
+ return this.state.relateIdMap;
232
+ }
233
+
234
+ /**
235
+ * 记录setNewItemId前后映射关系
236
+ * @param oldId 原组件ID
237
+ * @param newId 分配的新ID
238
+ */
239
+ private setRelateId(oldId: Id, newId: Id) {
240
+ this.state.relateIdMap[oldId] = newId;
241
+ }
184
242
  }
185
243
 
186
244
  export type PropsService = Props;