@tmagic/editor 1.1.0-beta.1 → 1.1.0-beta.12

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 (106) hide show
  1. package/dist/style.css +5 -1
  2. package/dist/{tmagic-editor.es.js → tmagic-editor.mjs} +1449 -1205
  3. package/dist/tmagic-editor.mjs.map +1 -0
  4. package/dist/tmagic-editor.umd.js +1463 -1219
  5. package/dist/tmagic-editor.umd.js.map +1 -1
  6. package/package.json +23 -21
  7. package/src/Editor.vue +24 -15
  8. package/src/components/ContentMenu.vue +87 -99
  9. package/src/components/Icon.vue +1 -1
  10. package/src/components/ScrollViewer.vue +2 -2
  11. package/src/components/ToolButton.vue +150 -167
  12. package/src/fields/UISelect.vue +51 -72
  13. package/src/index.ts +2 -1
  14. package/src/layouts/AddPageBox.vue +2 -2
  15. package/src/layouts/Framework.vue +1 -1
  16. package/src/layouts/NavMenu.vue +2 -2
  17. package/src/layouts/PropsPanel.vue +1 -1
  18. package/src/layouts/Resizer.vue +1 -1
  19. package/src/layouts/sidebar/ComponentListPanel.vue +6 -17
  20. package/src/layouts/sidebar/LayerMenu.vue +2 -2
  21. package/src/layouts/sidebar/LayerPanel.vue +52 -12
  22. package/src/layouts/sidebar/Sidebar.vue +1 -1
  23. package/src/layouts/sidebar/TabPane.vue +2 -2
  24. package/src/layouts/workspace/PageBar.vue +63 -192
  25. package/src/layouts/workspace/PageBarScrollContainer.vue +111 -0
  26. package/src/layouts/workspace/Stage.vue +198 -219
  27. package/src/layouts/workspace/ViewerMenu.vue +140 -147
  28. package/src/layouts/workspace/Workspace.vue +14 -12
  29. package/src/services/BaseService.ts +5 -10
  30. package/src/services/componentList.ts +3 -3
  31. package/src/services/editor.ts +208 -147
  32. package/src/services/events.ts +1 -1
  33. package/src/services/history.ts +1 -1
  34. package/src/services/props.ts +74 -49
  35. package/src/services/storage.ts +133 -0
  36. package/src/services/ui.ts +10 -4
  37. package/src/theme/component-list-panel.scss +5 -0
  38. package/src/theme/framework.scss +1 -1
  39. package/src/theme/stage.scss +2 -2
  40. package/src/type.ts +25 -7
  41. package/src/utils/config.ts +1 -1
  42. package/src/utils/editor.ts +20 -12
  43. package/src/utils/operator.ts +109 -0
  44. package/src/utils/props.ts +2 -27
  45. package/src/utils/scroll-viewer.ts +1 -1
  46. package/tsconfig.build.json +13 -0
  47. package/{dist/types/src → types}/Editor.vue.d.ts +17 -9
  48. package/types/components/ContentMenu.vue.d.ts +116 -0
  49. package/types/components/Icon.vue.d.ts +13 -0
  50. package/types/components/ScrollViewer.vue.d.ts +22 -0
  51. package/types/components/ToolButton.vue.d.ts +109 -0
  52. package/types/fields/Code.vue.d.ts +23 -0
  53. package/{dist/types/src → types}/fields/CodeLink.vue.d.ts +4 -6
  54. package/types/fields/UISelect.vue.d.ts +83 -0
  55. package/{dist/types/src → types}/index.d.ts +4 -1
  56. package/types/layouts/AddPageBox.vue.d.ts +4 -0
  57. package/{dist/types/src → types}/layouts/CodeEditor.vue.d.ts +11 -13
  58. package/types/layouts/Framework.vue.d.ts +22 -0
  59. package/types/layouts/NavMenu.vue.d.ts +25 -0
  60. package/types/layouts/PropsPanel.vue.d.ts +245 -0
  61. package/types/layouts/Resizer.vue.d.ts +12 -0
  62. package/types/layouts/sidebar/ComponentListPanel.vue.d.ts +14 -0
  63. package/types/layouts/sidebar/LayerMenu.vue.d.ts +100 -0
  64. package/types/layouts/sidebar/LayerPanel.vue.d.ts +1101 -0
  65. package/{dist/types/src → types}/layouts/sidebar/Sidebar.vue.d.ts +3 -5
  66. package/types/layouts/sidebar/TabPane.vue.d.ts +14 -0
  67. package/types/layouts/workspace/PageBar.vue.d.ts +54 -0
  68. package/types/layouts/workspace/PageBarScrollContainer.vue.d.ts +48 -0
  69. package/types/layouts/workspace/Stage.vue.d.ts +46 -0
  70. package/types/layouts/workspace/ViewerMenu.vue.d.ts +88 -0
  71. package/types/layouts/workspace/Workspace.vue.d.ts +6 -0
  72. package/{dist/types/src → types}/services/BaseService.d.ts +0 -0
  73. package/types/services/componentList.d.ts +14 -0
  74. package/{dist/types/src → types}/services/editor.d.ts +22 -14
  75. package/{dist/types/src → types}/services/events.d.ts +0 -0
  76. package/{dist/types/src → types}/services/history.d.ts +0 -0
  77. package/types/services/props.d.ts +97 -0
  78. package/types/services/storage.d.ts +56 -0
  79. package/{dist/types/src → types}/services/ui.d.ts +1 -1
  80. package/{dist/types/src → types}/type.d.ts +18 -1
  81. package/{dist/types/src → types}/utils/compose.d.ts +1 -1
  82. package/{dist/types/src → types}/utils/config.d.ts +0 -0
  83. package/{dist/types/src → types}/utils/editor.d.ts +6 -3
  84. package/{dist/types/src → types}/utils/index.d.ts +0 -0
  85. package/{dist/types/src → types}/utils/logger.d.ts +0 -0
  86. package/types/utils/operator.d.ts +21 -0
  87. package/{dist/types/src → types}/utils/polyfills.d.ts +0 -0
  88. package/{dist/types/src → types}/utils/props.d.ts +2 -23
  89. package/{dist/types/src → types}/utils/scroll-viewer.d.ts +0 -0
  90. package/{dist/types/src → types}/utils/undo-redo.d.ts +0 -0
  91. package/dist/tmagic-editor.es.js.map +0 -1
  92. package/dist/types/src/components/Icon.vue.d.ts +0 -14
  93. package/dist/types/src/components/ScrollViewer.vue.d.ts +0 -24
  94. package/dist/types/src/fields/Code.vue.d.ts +0 -25
  95. package/dist/types/src/layouts/AddPageBox.vue.d.ts +0 -5
  96. package/dist/types/src/layouts/Framework.vue.d.ts +0 -24
  97. package/dist/types/src/layouts/NavMenu.vue.d.ts +0 -27
  98. package/dist/types/src/layouts/PropsPanel.vue.d.ts +0 -13
  99. package/dist/types/src/layouts/Resizer.vue.d.ts +0 -14
  100. package/dist/types/src/layouts/sidebar/ComponentListPanel.vue.d.ts +0 -16
  101. package/dist/types/src/layouts/sidebar/TabPane.vue.d.ts +0 -16
  102. package/dist/types/src/layouts/workspace/Workspace.vue.d.ts +0 -8
  103. package/dist/types/src/services/props.d.ts +0 -42
  104. package/dist/types/src/shims-vue.d.ts +0 -6
  105. package/dist/types/src/vite-env.d.ts +0 -1
  106. package/src/vite-env.d.ts +0 -1
@@ -17,58 +17,62 @@
17
17
  */
18
18
 
19
19
  import { reactive, toRaw } from 'vue';
20
- import { cloneDeep, mergeWith } from 'lodash-es';
21
- import serialize from 'serialize-javascript';
20
+ import { cloneDeep, mergeWith, uniq } from 'lodash-es';
22
21
 
23
22
  import type { Id, MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
24
23
  import { NodeType } from '@tmagic/schema';
25
24
  import StageCore from '@tmagic/stage';
26
25
  import { getNodePath, isNumber, isPage, isPop } from '@tmagic/utils';
27
26
 
28
- import historyService, { StepValue } from '@editor/services/history';
29
- import propsService from '@editor/services/props';
30
- import type { AddMNode, EditorNodeInfo, StoreState } from '@editor/type';
31
- import { LayerOffset, Layout } from '@editor/type';
27
+ import historyService, { StepValue } from '../services/history';
28
+ import storageService, { Protocol } from '../services/storage';
29
+ import type { AddMNode, EditorNodeInfo, PastePosition, StoreState } from '../type';
30
+ import { LayerOffset, Layout } from '../type';
32
31
  import {
33
32
  change2Fixed,
34
33
  COPY_STORAGE_KEY,
35
34
  Fixed2Other,
36
- fixNodeLeft,
37
- generatePageNameByApp,
35
+ fixNodePosition,
38
36
  getInitPositionStyle,
39
37
  getNodeIndex,
40
38
  isFixed,
41
39
  setLayout,
42
- } from '@editor/utils/editor';
43
- import { log } from '@editor/utils/logger';
40
+ } from '../utils/editor';
41
+ import { beforePaste, getAddParent } from '../utils/operator';
44
42
 
45
43
  import BaseService from './BaseService';
44
+ import propsService from './props';
46
45
 
47
46
  class Editor extends BaseService {
48
- private isHistoryStateChange = false;
49
-
50
- private state = reactive<StoreState>({
47
+ public state: StoreState = reactive({
51
48
  root: null,
52
49
  page: null,
53
50
  parent: null,
54
51
  node: null,
52
+ nodes: [],
55
53
  stage: null,
56
54
  highlightNode: null,
57
55
  modifiedNodeIds: new Map(),
58
56
  pageLength: 0,
59
57
  });
58
+ private isHistoryStateChange = false;
60
59
 
61
60
  constructor() {
62
61
  super(
63
62
  [
64
63
  'getLayout',
65
64
  'select',
65
+ 'doAdd',
66
66
  'add',
67
+ 'doRemove',
67
68
  'remove',
69
+ 'doUpdate',
68
70
  'update',
69
71
  'sort',
70
72
  'copy',
71
73
  'paste',
74
+ 'doPaste',
75
+ 'duAlignCenter',
72
76
  'alignCenter',
73
77
  'moveLayer',
74
78
  'moveToContainer',
@@ -84,14 +88,16 @@ class Editor extends BaseService {
84
88
 
85
89
  /**
86
90
  * 设置当前指点节点配置
87
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode'
91
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes'
88
92
  * @param value MNode
89
93
  * @returns MNode
90
94
  */
91
95
  public set<T = MNode>(name: keyof StoreState, value: T) {
92
96
  this.state[name] = value as any;
93
- log('store set ', name, ' ', value);
94
-
97
+ // set nodes时将node设置为nodes第一个元素
98
+ if (name === 'nodes') {
99
+ this.set('node', (value as unknown as MNode[])[0]);
100
+ }
95
101
  if (name === 'root') {
96
102
  this.state.pageLength = (value as unknown as MApp)?.items?.length || 0;
97
103
  this.emit('root-change', value);
@@ -100,7 +106,7 @@ class Editor extends BaseService {
100
106
 
101
107
  /**
102
108
  * 获取当前指点节点配置
103
- * @param name 'root' | 'page' | 'parent' | 'node'
109
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes'
104
110
  * @returns MNode
105
111
  */
106
112
  public get<T = MNode>(name: keyof StoreState): T {
@@ -192,7 +198,7 @@ class Editor extends BaseService {
192
198
  */
193
199
  public async select(config: MNode | Id): Promise<MNode> | never {
194
200
  const { node, page, parent } = this.selectedConfigExceptionHandler(config);
195
- this.set('node', node);
201
+ this.set('nodes', [node]);
196
202
  this.set('page', page || null);
197
203
  this.set('parent', parent || null);
198
204
 
@@ -267,85 +273,110 @@ class Editor extends BaseService {
267
273
  }
268
274
 
269
275
  /**
270
- * 向指点容器添加组件节点
271
- * @param addConfig 将要添加的组件节点配置
272
- * @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
273
- * @returns 添加后的节点
276
+ * 多选
277
+ * @param ids 指定节点ID
278
+ * @returns 加入多选的节点配置
274
279
  */
275
- public async add(addNode: AddMNode, parent?: MContainer | null): Promise<MNode> {
276
- const { type, ...config } = addNode;
277
- const curNode = this.get<MContainer>('node');
278
-
279
- let parentNode: MContainer | undefined;
280
- const isPage = type === NodeType.PAGE;
281
-
282
- if (isPage) {
283
- parentNode = this.get<MApp>('root');
284
- // 由于支持中间件扩展,在parent参数为undefined时,parent会变成next函数
285
- } else if (parent && typeof parent !== 'function') {
286
- parentNode = parent;
287
- } else if (curNode.items) {
288
- parentNode = curNode;
289
- } else {
290
- parentNode = this.getParentById(curNode.id, false);
291
- }
292
-
293
- if (!parentNode) throw new Error('未找到父元素');
280
+ public multiSelect(ids: Id[]): void {
281
+ const nodes: MNode[] = [];
282
+ const idsUnique = uniq(ids);
283
+ idsUnique.forEach((id) => {
284
+ const { node } = this.getNodeInfo(id);
285
+ if (!node) return;
286
+ nodes.push(node);
287
+ });
288
+ this.set('nodes', nodes);
289
+ }
294
290
 
295
- const layout = await this.getLayout(toRaw(parentNode), addNode as MNode);
296
- const newNode = { ...toRaw(await propsService.getPropsValue(type, config)) };
297
- newNode.style = getInitPositionStyle(newNode.style, layout, parentNode, this.get<StageCore>('stage'));
291
+ public async doAdd(node: MNode, parent: MContainer): Promise<MNode> {
292
+ const root = this.get<MApp>('root');
293
+ const curNode = this.get<MNode>('node');
294
+ const stage = this.get<StageCore | null>('stage');
298
295
 
299
- if ((parentNode?.type === NodeType.ROOT || curNode.type === NodeType.ROOT) && newNode.type !== NodeType.PAGE) {
296
+ if ((parent?.type === NodeType.ROOT || curNode.type === NodeType.ROOT) && node.type !== NodeType.PAGE) {
300
297
  throw new Error('app下不能添加组件');
301
298
  }
302
299
 
303
- parentNode?.items?.push(newNode);
300
+ // 新增节点添加到配置中
301
+ parent?.items?.push(node);
304
302
 
305
- const stage = this.get<StageCore | null>('stage');
306
- const root = this.get<MApp>('root');
303
+ const layout = await this.getLayout(toRaw(parent), node as MNode);
304
+ node.style = getInitPositionStyle(node.style, layout);
307
305
 
308
- await stage?.add({ config: cloneDeep(newNode), root: cloneDeep(root) });
306
+ await stage?.add({
307
+ config: cloneDeep(node),
308
+ parent: cloneDeep(parent),
309
+ parentId: parent.id,
310
+ root: cloneDeep(root),
311
+ });
309
312
 
310
- if (layout === Layout.ABSOLUTE) {
311
- const fixedLeft = fixNodeLeft(newNode, parentNode, stage?.renderer.contentWindow?.document);
312
- if (typeof fixedLeft !== 'undefined') {
313
- newNode.style.left = fixedLeft;
314
- await stage?.update({ config: cloneDeep(newNode), root: cloneDeep(root) });
315
- }
316
- }
313
+ node.style = fixNodePosition(node, parent, stage);
317
314
 
318
- await this.select(newNode);
315
+ await stage?.update({ config: cloneDeep(node), parentId: parent.id, root: cloneDeep(root) });
319
316
 
320
- this.addModifiedNodeId(newNode.id);
321
- if (!isPage) {
322
- this.pushHistoryState();
317
+ this.addModifiedNodeId(node.id);
318
+
319
+ return node;
320
+ }
321
+
322
+ /**
323
+ * 向指点容器添加组件节点
324
+ * @param addConfig 将要添加的组件节点配置
325
+ * @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
326
+ * @returns 添加后的节点
327
+ */
328
+ public async add(addNode: AddMNode | MNode[], parent?: MContainer | null): Promise<MNode | MNode[]> {
329
+ const stage = this.get<StageCore | null>('stage');
330
+
331
+ // 新增多个组件只存在于粘贴多个组件,粘贴的是一个完整的config,所以不再需要getPropsValue
332
+ const addNodes = [];
333
+ if (!Array.isArray(addNode)) {
334
+ const { type, inputEvent, ...config } = addNode;
335
+
336
+ if (!type) throw new Error('组件类型不能为空');
337
+
338
+ addNodes.push({ ...toRaw(await propsService.getPropsValue(type, config)) });
339
+ } else {
340
+ addNodes.push(...addNode);
323
341
  }
324
342
 
325
- stage?.select(newNode.id);
343
+ const newNodes = await Promise.all(
344
+ addNodes.map((node) => {
345
+ const parentNode = parent && typeof parent !== 'function' ? parent : getAddParent(node);
346
+ if (!parentNode) throw new Error('未找到父元素');
347
+ return this.doAdd(node, parentNode);
348
+ }),
349
+ );
326
350
 
327
- if (isPage) {
328
- this.state.pageLength += 1;
351
+ if (newNodes.length > 1) {
352
+ const newNodeIds = newNodes.map((node) => node.id);
353
+ // 触发选中样式
354
+ stage?.multiSelect(newNodeIds);
355
+ await this.multiSelect(newNodeIds);
356
+ } else {
357
+ await this.select(newNodes[0]);
358
+
359
+ if (isPage(newNodes[0])) {
360
+ this.state.pageLength += 1;
361
+ } else {
362
+ // 新增页面,这个时候页面还有渲染出来,此时select会出错,在runtime-ready的时候回去select
363
+ stage?.select(newNodes[0].id);
364
+ }
329
365
  }
330
366
 
331
- this.emit('add', newNode);
367
+ this.pushHistoryState();
332
368
 
333
- return newNode;
334
- }
369
+ this.emit('add', newNodes);
335
370
 
336
- /**
337
- * 删除组件
338
- * @param {Object} node
339
- * @return {Object} 删除的组件配置
340
- */
341
- public async remove(node: MNode): Promise<MNode | void> {
342
- if (!node?.id) return;
371
+ return Array.isArray(addNode) ? newNodes : newNodes[0];
372
+ }
343
373
 
374
+ public async doRemove(node: MNode): Promise<void> {
344
375
  const root = this.get<MApp | null>('root');
345
376
 
346
377
  if (!root) throw new Error('没有root');
347
378
 
348
- const { parent, node: curNode } = this.getNodeInfo(node.id, false);
379
+ const { parent, node: curNode } = this.getNodeInfo(node.id);
349
380
 
350
381
  if (!parent || !curNode) throw new Error('找不要删除的节点');
351
382
 
@@ -355,7 +386,7 @@ class Editor extends BaseService {
355
386
 
356
387
  parent.items?.splice(index, 1);
357
388
  const stage = this.get<StageCore | null>('stage');
358
- stage?.remove({ id: node.id, root: cloneDeep(this.get('root')) });
389
+ stage?.remove({ id: node.id, parentId: parent.id, root: cloneDeep(root) });
359
390
 
360
391
  if (node.type === NodeType.PAGE) {
361
392
  this.state.pageLength -= 1;
@@ -372,9 +403,7 @@ class Editor extends BaseService {
372
403
  this.resetModifiedNodeId();
373
404
  historyService.reset();
374
405
 
375
- this.emit('remove', node);
376
-
377
- return node;
406
+ return;
378
407
  }
379
408
  } else {
380
409
  await this.select(parent);
@@ -382,19 +411,25 @@ class Editor extends BaseService {
382
411
  }
383
412
 
384
413
  this.addModifiedNodeId(parent.id);
385
- this.pushHistoryState();
386
-
387
- this.emit('remove', node);
388
-
389
- return node;
390
414
  }
391
415
 
392
416
  /**
393
- * 更新节点
394
- * @param config 新的节点配置,配置中需要有id信息
395
- * @returns 更新后的节点配置
417
+ * 删除组件
418
+ * @param {Object} node
419
+ * @return {Object} 删除的组件配置
396
420
  */
397
- public async update(config: MNode): Promise<MNode> {
421
+ public async remove(nodeOrNodeList: MNode | MNode[]): Promise<void> {
422
+ const nodes = Array.isArray(nodeOrNodeList) ? nodeOrNodeList : [nodeOrNodeList];
423
+
424
+ await Promise.all(nodes.map((node) => this.doRemove(node)));
425
+
426
+ // 更新历史记录
427
+ this.pushHistoryState();
428
+
429
+ this.emit('remove');
430
+ }
431
+
432
+ public async doUpdate(config: MNode) {
398
433
  if (!config?.id) throw new Error('没有配置或者配置缺少id值');
399
434
 
400
435
  const info = this.getNodeInfo(config.id, false);
@@ -434,22 +469,42 @@ class Editor extends BaseService {
434
469
 
435
470
  parentNodeItems[index] = newConfig;
436
471
 
437
- if (`${newConfig.id}` === `${this.get('node').id}`) {
438
- this.set('node', newConfig);
439
- }
472
+ // 将update后的配置更新到nodes中
473
+ const nodes = this.get('nodes');
474
+ const targetIndex = nodes.findIndex((nodeItem: MNode) => `${nodeItem.id}` === `${newConfig.id}`);
475
+ nodes.splice(targetIndex, 1, newConfig);
476
+ this.set('nodes', nodes);
440
477
 
441
- this.get<StageCore | null>('stage')?.update({ config: cloneDeep(newConfig), root: cloneDeep(this.get('root')) });
478
+ this.get<StageCore | null>('stage')?.update({
479
+ config: cloneDeep(newConfig),
480
+ parentId: parent.id,
481
+ root: cloneDeep(this.get('root')),
482
+ });
442
483
 
443
484
  if (newConfig.type === NodeType.PAGE) {
444
485
  this.set('page', newConfig);
445
486
  }
446
487
 
447
488
  this.addModifiedNodeId(newConfig.id);
489
+
490
+ return newConfig;
491
+ }
492
+
493
+ /**
494
+ * 更新节点
495
+ * @param config 新的节点配置,配置中需要有id信息
496
+ * @returns 更新后的节点配置
497
+ */
498
+ public async update(config: MNode | MNode[]): Promise<MNode | MNode[]> {
499
+ const nodes = Array.isArray(config) ? config : [config];
500
+
501
+ const newNodes = await Promise.all(nodes.map((node) => this.doUpdate(node)));
502
+
448
503
  this.pushHistoryState();
449
504
 
450
- this.emit('update', newConfig);
505
+ this.emit('update', newNodes);
451
506
 
452
- return newConfig;
507
+ return Array.isArray(config) ? newNodes : newNodes[0];
453
508
  }
454
509
 
455
510
  /**
@@ -471,7 +526,11 @@ class Editor extends BaseService {
471
526
  await this.update(parent);
472
527
  await this.select(node);
473
528
 
474
- this.get<StageCore | null>('stage')?.update({ config: cloneDeep(node), root: cloneDeep(this.get('root')) });
529
+ this.get<StageCore | null>('stage')?.update({
530
+ config: cloneDeep(node),
531
+ parentId: parent.id,
532
+ root: cloneDeep(this.get('root')),
533
+ });
475
534
 
476
535
  this.addModifiedNodeId(parent.id);
477
536
  this.pushHistoryState();
@@ -482,67 +541,51 @@ class Editor extends BaseService {
482
541
  * @param config 组件节点配置
483
542
  * @returns 组件节点配置
484
543
  */
485
- public async copy(config: MNode): Promise<void> {
486
- globalThis.localStorage.setItem(COPY_STORAGE_KEY, serialize(config));
544
+ public async copy(config: MNode | MNode[]): Promise<void> {
545
+ await storageService.setItem(COPY_STORAGE_KEY, Array.isArray(config) ? config : [config], {
546
+ protocol: Protocol.OBJECT,
547
+ });
487
548
  }
488
549
 
489
550
  /**
490
551
  * 从localStorage中获取节点,然后添加到当前容器中
491
- * @param position 如果设置,指定组件位置
552
+ * @param position 粘贴的坐标
492
553
  * @returns 添加后的组件节点配置
493
554
  */
494
- public async paste(position: { left?: number; top?: number } = {}): Promise<MNode | void> {
495
- const configStr = globalThis.localStorage.getItem(COPY_STORAGE_KEY);
496
- // eslint-disable-next-line prefer-const
497
- let config: any = {};
498
- if (!configStr) {
499
- return;
500
- }
555
+ public async paste(position: PastePosition = {}): Promise<MNode | MNode[] | void> {
556
+ const config: MNode[] = await storageService.getItem(COPY_STORAGE_KEY);
501
557
 
502
- try {
503
- // eslint-disable-next-line no-eval
504
- eval(`config = ${configStr}`);
505
- } catch (e) {
506
- console.error(e);
507
- return;
508
- }
558
+ if (!Array.isArray(config)) return;
509
559
 
510
- await propsService.setNewItemId(config, this.get('root'));
511
- if (config.style) {
512
- config.style = {
513
- ...config.style,
514
- ...position,
515
- };
516
- }
560
+ const pasteConfigs = await this.doPaste(config, position);
517
561
 
518
- if (isPage(config)) {
519
- config.name = generatePageNameByApp(this.get('root'));
520
- }
562
+ return this.add(pasteConfigs);
563
+ }
521
564
 
522
- return await this.add(config);
565
+ public async doPaste(config: MNode[], position: PastePosition = {}): Promise<MNode[]> {
566
+ const pasteConfigs = await beforePaste(position, cloneDeep(config));
567
+ return pasteConfigs;
523
568
  }
524
569
 
525
- /**
526
- * 将指点节点设置居中
527
- * @param config 组件节点配置
528
- * @returns 当前组件节点配置
529
- */
530
- public async alignCenter(config: MNode): Promise<MNode | void> {
531
- const parent = this.get<MContainer>('parent');
532
- const node = this.get<MNode>('node');
533
- const layout = await this.getLayout(toRaw(parent), toRaw(node));
570
+ public async doAlignCenter(config: MNode): Promise<MNode> {
571
+ const parent = this.getParentById(config.id);
572
+
573
+ if (!parent) throw new Error('找不到父节点');
574
+
575
+ const node = cloneDeep(toRaw(config));
576
+ const layout = await this.getLayout(parent, node);
534
577
  if (layout === Layout.RELATIVE) {
535
- return;
578
+ return config;
536
579
  }
537
580
 
538
- if (!node.style) return;
581
+ if (!node.style) return config;
539
582
 
540
583
  const stage = this.get<StageCore>('stage');
541
584
  const doc = stage?.renderer.contentWindow?.document;
542
585
 
543
586
  if (doc) {
544
- const parentEl = doc.getElementById(`${parent.id}`);
545
587
  const el = doc.getElementById(`${node.id}`);
588
+ const parentEl = el?.offsetParent;
546
589
  if (parentEl && el) {
547
590
  node.style.left = (parentEl.clientWidth - el.clientWidth) / 2;
548
591
  }
@@ -550,15 +593,25 @@ class Editor extends BaseService {
550
593
  node.style.left = (parent.style.width - node.style.width) / 2;
551
594
  }
552
595
 
553
- await this.update(node);
554
- this.get<StageCore | null>('stage')?.update({
555
- config: cloneDeep(toRaw(node)),
556
- root: cloneDeep(this.get<MApp>('root')),
557
- });
558
- this.addModifiedNodeId(config.id);
559
- this.pushHistoryState();
596
+ return node;
597
+ }
560
598
 
561
- return config;
599
+ /**
600
+ * 将指点节点设置居中
601
+ * @param config 组件节点配置
602
+ * @returns 当前组件节点配置
603
+ */
604
+ public async alignCenter(config: MNode | MNode[]): Promise<MNode | MNode[]> {
605
+ const nodes = Array.isArray(config) ? config : [config];
606
+ const stage = this.get<StageCore | null>('stage');
607
+
608
+ const newNodes = await Promise.all(nodes.map((node) => this.doAlignCenter(node)));
609
+
610
+ const newNode = await this.update(newNodes);
611
+
612
+ await stage?.multiSelect(newNodes.map((node) => node.id));
613
+
614
+ return newNode;
562
615
  }
563
616
 
564
617
  /**
@@ -567,7 +620,7 @@ class Editor extends BaseService {
567
620
  */
568
621
  public async moveLayer(offset: number | LayerOffset): Promise<void> {
569
622
  const parent = this.get<MContainer>('parent');
570
- const node = this.get('node');
623
+ const node = this.get<MNode>('node');
571
624
  const brothers: MNode[] = parent?.items || [];
572
625
  const index = brothers.findIndex((item) => `${item.id}` === `${node?.id}`);
573
626
 
@@ -579,10 +632,15 @@ class Editor extends BaseService {
579
632
  brothers.splice(index + parseInt(`${offset}`, 10), 0, brothers.splice(index, 1)[0]);
580
633
  }
581
634
 
635
+ const grandparent = this.getParentById(parent.id);
582
636
  this.get<StageCore | null>('stage')?.update({
583
637
  config: cloneDeep(toRaw(parent)),
638
+ parentId: grandparent?.id,
584
639
  root: cloneDeep(this.get<MApp>('root')),
585
640
  });
641
+
642
+ this.addModifiedNodeId(parent.id);
643
+ this.pushHistoryState();
586
644
  }
587
645
 
588
646
  /**
@@ -601,7 +659,7 @@ class Editor extends BaseService {
601
659
  const index = getNodeIndex(node, parent);
602
660
  parent.items?.splice(index, 1);
603
661
 
604
- await stage.remove({ id: node.id, root });
662
+ await stage.remove({ id: node.id, parentId: parent.id, root });
605
663
 
606
664
  const layout = await this.getLayout(target);
607
665
 
@@ -610,13 +668,14 @@ class Editor extends BaseService {
610
668
  return srcValue;
611
669
  }
612
670
  });
613
- newConfig.style = getInitPositionStyle(newConfig.style, layout, target, stage);
671
+ newConfig.style = getInitPositionStyle(newConfig.style, layout);
614
672
 
615
673
  target.items.push(newConfig);
616
674
 
617
675
  await stage.select(targetId);
618
676
 
619
- await stage.update({ config: cloneDeep(target), root });
677
+ const targetParent = this.getParentById(target.id);
678
+ await stage.update({ config: cloneDeep(target), parentId: targetParent?.id, root });
620
679
 
621
680
  await this.select(newConfig);
622
681
  stage.select(newConfig.id);
@@ -653,7 +712,7 @@ class Editor extends BaseService {
653
712
  const node = toRaw(this.get('node'));
654
713
  if (!node || isPage(node)) return;
655
714
 
656
- const { style, id } = node;
715
+ const { style, id, type } = node;
657
716
  if (!style || style.position !== 'absolute') return;
658
717
 
659
718
  if (top && !isNumber(style.top)) return;
@@ -661,6 +720,7 @@ class Editor extends BaseService {
661
720
 
662
721
  this.update({
663
722
  id,
723
+ type,
664
724
  style: {
665
725
  ...style,
666
726
  left: Number(style.left) + left,
@@ -673,6 +733,7 @@ class Editor extends BaseService {
673
733
  this.removeAllListeners();
674
734
  this.set('root', null);
675
735
  this.set('node', null);
736
+ this.set('nodes', []);
676
737
  this.set('page', null);
677
738
  this.set('parent', null);
678
739
  }
@@ -22,7 +22,7 @@ import { cloneDeep } from 'lodash-es';
22
22
  import { DEFAULT_EVENTS, DEFAULT_METHODS, EventOption } from '@tmagic/core';
23
23
  import { toLine } from '@tmagic/utils';
24
24
 
25
- import type { ComponentGroup } from '@editor/type';
25
+ import type { ComponentGroup } from '../type';
26
26
 
27
27
  import BaseService from './BaseService';
28
28
 
@@ -20,7 +20,7 @@ import { reactive } from 'vue';
20
20
 
21
21
  import type { Id, MPage } from '@tmagic/schema';
22
22
 
23
- import { UndoRedo } from '@editor/utils/undo-redo';
23
+ import { UndoRedo } from '../utils/undo-redo';
24
24
 
25
25
  import BaseService from './BaseService';
26
26