@tmagic/editor 1.1.0-beta.0 → 1.1.0-beta.11

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 (105) hide show
  1. package/dist/style.css +5 -1
  2. package/dist/{tmagic-editor.es.js → tmagic-editor.mjs} +1445 -1199
  3. package/dist/tmagic-editor.mjs.map +1 -0
  4. package/dist/tmagic-editor.umd.js +1483 -1236
  5. package/dist/tmagic-editor.umd.js.map +1 -1
  6. package/package.json +22 -20
  7. package/src/Editor.vue +23 -15
  8. package/src/components/ContentMenu.vue +87 -99
  9. package/src/components/Icon.vue +2 -2
  10. package/src/components/ScrollViewer.vue +2 -2
  11. package/src/components/ToolButton.vue +151 -168
  12. package/src/fields/UISelect.vue +51 -72
  13. package/src/index.ts +2 -1
  14. package/src/layouts/AddPageBox.vue +3 -3
  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 +3 -3
  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 +3 -3
  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 +141 -148
  28. package/src/layouts/workspace/Workspace.vue +11 -11
  29. package/src/services/BaseService.ts +5 -10
  30. package/src/services/componentList.ts +3 -3
  31. package/src/services/editor.ts +197 -147
  32. package/src/services/events.ts +1 -1
  33. package/src/services/history.ts +1 -1
  34. package/src/services/props.ts +63 -11
  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 -24
  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 +21 -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/{dist/types/src → types}/services/props.d.ts +26 -2
  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 -22
  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/shims-vue.d.ts +0 -6
  104. package/dist/types/src/vite-env.d.ts +0 -1
  105. package/src/vite-env.d.ts +0 -1
@@ -17,58 +17,61 @@
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
+ 'duAlignCenter',
72
75
  'alignCenter',
73
76
  'moveLayer',
74
77
  'moveToContainer',
@@ -84,14 +87,16 @@ class Editor extends BaseService {
84
87
 
85
88
  /**
86
89
  * 设置当前指点节点配置
87
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode'
90
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes'
88
91
  * @param value MNode
89
92
  * @returns MNode
90
93
  */
91
94
  public set<T = MNode>(name: keyof StoreState, value: T) {
92
95
  this.state[name] = value as any;
93
- log('store set ', name, ' ', value);
94
-
96
+ // set nodes时将node设置为nodes第一个元素
97
+ if (name === 'nodes') {
98
+ this.set('node', (value as unknown as MNode[])[0]);
99
+ }
95
100
  if (name === 'root') {
96
101
  this.state.pageLength = (value as unknown as MApp)?.items?.length || 0;
97
102
  this.emit('root-change', value);
@@ -100,7 +105,7 @@ class Editor extends BaseService {
100
105
 
101
106
  /**
102
107
  * 获取当前指点节点配置
103
- * @param name 'root' | 'page' | 'parent' | 'node'
108
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes'
104
109
  * @returns MNode
105
110
  */
106
111
  public get<T = MNode>(name: keyof StoreState): T {
@@ -192,7 +197,7 @@ class Editor extends BaseService {
192
197
  */
193
198
  public async select(config: MNode | Id): Promise<MNode> | never {
194
199
  const { node, page, parent } = this.selectedConfigExceptionHandler(config);
195
- this.set('node', node);
200
+ this.set('nodes', [node]);
196
201
  this.set('page', page || null);
197
202
  this.set('parent', parent || null);
198
203
 
@@ -267,85 +272,110 @@ class Editor extends BaseService {
267
272
  }
268
273
 
269
274
  /**
270
- * 向指点容器添加组件节点
271
- * @param addConfig 将要添加的组件节点配置
272
- * @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
273
- * @returns 添加后的节点
275
+ * 多选
276
+ * @param ids 指定节点ID
277
+ * @returns 加入多选的节点配置
274
278
  */
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('未找到父元素');
279
+ public multiSelect(ids: Id[]): void {
280
+ const nodes: MNode[] = [];
281
+ const idsUnique = uniq(ids);
282
+ idsUnique.forEach((id) => {
283
+ const { node } = this.getNodeInfo(id);
284
+ if (!node) return;
285
+ nodes.push(node);
286
+ });
287
+ this.set('nodes', nodes);
288
+ }
294
289
 
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'));
290
+ public async doAdd(node: MNode, parent: MContainer): Promise<MNode> {
291
+ const root = this.get<MApp>('root');
292
+ const curNode = this.get<MNode>('node');
293
+ const stage = this.get<StageCore | null>('stage');
298
294
 
299
- if ((parentNode?.type === NodeType.ROOT || curNode.type === NodeType.ROOT) && newNode.type !== NodeType.PAGE) {
295
+ if ((parent?.type === NodeType.ROOT || curNode.type === NodeType.ROOT) && node.type !== NodeType.PAGE) {
300
296
  throw new Error('app下不能添加组件');
301
297
  }
302
298
 
303
- parentNode?.items?.push(newNode);
299
+ // 新增节点添加到配置中
300
+ parent?.items?.push(node);
304
301
 
305
- const stage = this.get<StageCore | null>('stage');
306
- const root = this.get<MApp>('root');
302
+ const layout = await this.getLayout(toRaw(parent), node as MNode);
303
+ node.style = getInitPositionStyle(node.style, layout);
304
+
305
+ await stage?.add({
306
+ config: cloneDeep(node),
307
+ parent: cloneDeep(parent),
308
+ parentId: parent.id,
309
+ root: cloneDeep(root),
310
+ });
307
311
 
308
- await stage?.add({ config: cloneDeep(newNode), root: cloneDeep(root) });
312
+ node.style = fixNodePosition(node, parent, stage);
309
313
 
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
- }
314
+ await stage?.update({ config: cloneDeep(node), parentId: parent.id, root: cloneDeep(root) });
317
315
 
318
- await this.select(newNode);
316
+ this.addModifiedNodeId(node.id);
319
317
 
320
- this.addModifiedNodeId(newNode.id);
321
- if (!isPage) {
322
- this.pushHistoryState();
318
+ return node;
319
+ }
320
+
321
+ /**
322
+ * 向指点容器添加组件节点
323
+ * @param addConfig 将要添加的组件节点配置
324
+ * @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
325
+ * @returns 添加后的节点
326
+ */
327
+ public async add(addNode: AddMNode | MNode[], parent?: MContainer | null): Promise<MNode | MNode[]> {
328
+ const stage = this.get<StageCore | null>('stage');
329
+
330
+ // 新增多个组件只存在于粘贴多个组件,粘贴的是一个完整的config,所以不再需要getPropsValue
331
+ const addNodes = [];
332
+ if (!Array.isArray(addNode)) {
333
+ const { type, inputEvent, ...config } = addNode;
334
+
335
+ if (!type) throw new Error('组件类型不能为空');
336
+
337
+ addNodes.push({ ...toRaw(await propsService.getPropsValue(type, config)) });
338
+ } else {
339
+ addNodes.push(...addNode);
323
340
  }
324
341
 
325
- stage?.select(newNode.id);
342
+ const newNodes = await Promise.all(
343
+ addNodes.map((node) => {
344
+ const parentNode = parent && typeof parent !== 'function' ? parent : getAddParent(node);
345
+ if (!parentNode) throw new Error('未找到父元素');
346
+ return this.doAdd(node, parentNode);
347
+ }),
348
+ );
326
349
 
327
- if (isPage) {
328
- this.state.pageLength += 1;
350
+ if (newNodes.length > 1) {
351
+ const newNodeIds = newNodes.map((node) => node.id);
352
+ // 触发选中样式
353
+ stage?.multiSelect(newNodeIds);
354
+ await this.multiSelect(newNodeIds);
355
+ } else {
356
+ await this.select(newNodes[0]);
357
+
358
+ if (isPage(newNodes[0])) {
359
+ this.state.pageLength += 1;
360
+ } else {
361
+ // 新增页面,这个时候页面还有渲染出来,此时select会出错,在runtime-ready的时候回去select
362
+ stage?.select(newNodes[0].id);
363
+ }
329
364
  }
330
365
 
331
- this.emit('add', newNode);
366
+ this.pushHistoryState();
332
367
 
333
- return newNode;
334
- }
368
+ this.emit('add', newNodes);
335
369
 
336
- /**
337
- * 删除组件
338
- * @param {Object} node
339
- * @return {Object} 删除的组件配置
340
- */
341
- public async remove(node: MNode): Promise<MNode | void> {
342
- if (!node?.id) return;
370
+ return newNodes.length > 1 ? newNodes[0] : newNodes;
371
+ }
343
372
 
373
+ public async doRemove(node: MNode): Promise<void> {
344
374
  const root = this.get<MApp | null>('root');
345
375
 
346
376
  if (!root) throw new Error('没有root');
347
377
 
348
- const { parent, node: curNode } = this.getNodeInfo(node.id, false);
378
+ const { parent, node: curNode } = this.getNodeInfo(node.id);
349
379
 
350
380
  if (!parent || !curNode) throw new Error('找不要删除的节点');
351
381
 
@@ -355,7 +385,7 @@ class Editor extends BaseService {
355
385
 
356
386
  parent.items?.splice(index, 1);
357
387
  const stage = this.get<StageCore | null>('stage');
358
- stage?.remove({ id: node.id, root: cloneDeep(this.get('root')) });
388
+ stage?.remove({ id: node.id, parentId: parent.id, root: cloneDeep(root) });
359
389
 
360
390
  if (node.type === NodeType.PAGE) {
361
391
  this.state.pageLength -= 1;
@@ -372,9 +402,7 @@ class Editor extends BaseService {
372
402
  this.resetModifiedNodeId();
373
403
  historyService.reset();
374
404
 
375
- this.emit('remove', node);
376
-
377
- return node;
405
+ return;
378
406
  }
379
407
  } else {
380
408
  await this.select(parent);
@@ -382,19 +410,25 @@ class Editor extends BaseService {
382
410
  }
383
411
 
384
412
  this.addModifiedNodeId(parent.id);
385
- this.pushHistoryState();
386
-
387
- this.emit('remove', node);
388
-
389
- return node;
390
413
  }
391
414
 
392
415
  /**
393
- * 更新节点
394
- * @param config 新的节点配置,配置中需要有id信息
395
- * @returns 更新后的节点配置
416
+ * 删除组件
417
+ * @param {Object} node
418
+ * @return {Object} 删除的组件配置
396
419
  */
397
- public async update(config: MNode): Promise<MNode> {
420
+ public async remove(nodeOrNodeList: MNode | MNode[]): Promise<void> {
421
+ const nodes = Array.isArray(nodeOrNodeList) ? nodeOrNodeList : [nodeOrNodeList];
422
+
423
+ await Promise.all(nodes.map((node) => this.doRemove(node)));
424
+
425
+ // 更新历史记录
426
+ this.pushHistoryState();
427
+
428
+ this.emit('remove');
429
+ }
430
+
431
+ public async doUpdate(config: MNode) {
398
432
  if (!config?.id) throw new Error('没有配置或者配置缺少id值');
399
433
 
400
434
  const info = this.getNodeInfo(config.id, false);
@@ -434,22 +468,42 @@ class Editor extends BaseService {
434
468
 
435
469
  parentNodeItems[index] = newConfig;
436
470
 
437
- if (`${newConfig.id}` === `${this.get('node').id}`) {
438
- this.set('node', newConfig);
439
- }
471
+ // 将update后的配置更新到nodes中
472
+ const nodes = this.get('nodes');
473
+ const targetIndex = nodes.findIndex((nodeItem: MNode) => `${nodeItem.id}` === `${newConfig.id}`);
474
+ nodes.splice(targetIndex, 1, newConfig);
475
+ this.set('nodes', nodes);
440
476
 
441
- this.get<StageCore | null>('stage')?.update({ config: cloneDeep(newConfig), root: cloneDeep(this.get('root')) });
477
+ this.get<StageCore | null>('stage')?.update({
478
+ config: cloneDeep(newConfig),
479
+ parentId: parent.id,
480
+ root: cloneDeep(this.get('root')),
481
+ });
442
482
 
443
483
  if (newConfig.type === NodeType.PAGE) {
444
484
  this.set('page', newConfig);
445
485
  }
446
486
 
447
487
  this.addModifiedNodeId(newConfig.id);
488
+
489
+ return newConfig;
490
+ }
491
+
492
+ /**
493
+ * 更新节点
494
+ * @param config 新的节点配置,配置中需要有id信息
495
+ * @returns 更新后的节点配置
496
+ */
497
+ public async update(config: MNode | MNode[]): Promise<MNode | MNode[]> {
498
+ const nodes = Array.isArray(config) ? config : [config];
499
+
500
+ const newNodes = await Promise.all(nodes.map((node) => this.doUpdate(node)));
501
+
448
502
  this.pushHistoryState();
449
503
 
450
- this.emit('update', newConfig);
504
+ this.emit('update', newNodes);
451
505
 
452
- return newConfig;
506
+ return newNodes.length > 1 ? newNodes[0] : newNodes;
453
507
  }
454
508
 
455
509
  /**
@@ -471,7 +525,11 @@ class Editor extends BaseService {
471
525
  await this.update(parent);
472
526
  await this.select(node);
473
527
 
474
- this.get<StageCore | null>('stage')?.update({ config: cloneDeep(node), root: cloneDeep(this.get('root')) });
528
+ this.get<StageCore | null>('stage')?.update({
529
+ config: cloneDeep(node),
530
+ parentId: parent.id,
531
+ root: cloneDeep(this.get('root')),
532
+ });
475
533
 
476
534
  this.addModifiedNodeId(parent.id);
477
535
  this.pushHistoryState();
@@ -482,67 +540,46 @@ class Editor extends BaseService {
482
540
  * @param config 组件节点配置
483
541
  * @returns 组件节点配置
484
542
  */
485
- public async copy(config: MNode): Promise<void> {
486
- globalThis.localStorage.setItem(COPY_STORAGE_KEY, serialize(config));
543
+ public async copy(config: MNode | MNode[]): Promise<void> {
544
+ await storageService.setItem(COPY_STORAGE_KEY, Array.isArray(config) ? config : [config], {
545
+ protocol: Protocol.OBJECT,
546
+ });
487
547
  }
488
548
 
489
549
  /**
490
550
  * 从localStorage中获取节点,然后添加到当前容器中
491
- * @param position 如果设置,指定组件位置
551
+ * @param position 粘贴的坐标
492
552
  * @returns 添加后的组件节点配置
493
553
  */
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
- }
554
+ public async paste(position: PastePosition = {}): Promise<MNode | MNode[] | void> {
555
+ const config: MNode[] = await storageService.getItem(COPY_STORAGE_KEY);
501
556
 
502
- try {
503
- // eslint-disable-next-line no-eval
504
- eval(`config = ${configStr}`);
505
- } catch (e) {
506
- console.error(e);
507
- return;
508
- }
557
+ if (!Array.isArray(config)) return;
509
558
 
510
- await propsService.setNewItemId(config, this.get('root'));
511
- if (config.style) {
512
- config.style = {
513
- ...config.style,
514
- ...position,
515
- };
516
- }
517
-
518
- if (isPage(config)) {
519
- config.name = generatePageNameByApp(this.get('root'));
520
- }
559
+ const pasteConfigs = await beforePaste(position, config);
521
560
 
522
- return await this.add(config);
561
+ return this.add(pasteConfigs);
523
562
  }
524
563
 
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));
564
+ public async doAlignCenter(config: MNode): Promise<MNode> {
565
+ const parent = this.getParentById(config.id);
566
+
567
+ if (!parent) throw new Error('找不到父节点');
568
+
569
+ const node = cloneDeep(toRaw(config));
570
+ const layout = await this.getLayout(parent, node);
534
571
  if (layout === Layout.RELATIVE) {
535
- return;
572
+ return config;
536
573
  }
537
574
 
538
- if (!node.style) return;
575
+ if (!node.style) return config;
539
576
 
540
577
  const stage = this.get<StageCore>('stage');
541
578
  const doc = stage?.renderer.contentWindow?.document;
542
579
 
543
580
  if (doc) {
544
- const parentEl = doc.getElementById(`${parent.id}`);
545
581
  const el = doc.getElementById(`${node.id}`);
582
+ const parentEl = el?.offsetParent;
546
583
  if (parentEl && el) {
547
584
  node.style.left = (parentEl.clientWidth - el.clientWidth) / 2;
548
585
  }
@@ -550,15 +587,25 @@ class Editor extends BaseService {
550
587
  node.style.left = (parent.style.width - node.style.width) / 2;
551
588
  }
552
589
 
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();
590
+ return node;
591
+ }
592
+
593
+ /**
594
+ * 将指点节点设置居中
595
+ * @param config 组件节点配置
596
+ * @returns 当前组件节点配置
597
+ */
598
+ public async alignCenter(config: MNode | MNode[]): Promise<MNode | MNode[]> {
599
+ const nodes = Array.isArray(config) ? config : [config];
600
+ const stage = this.get<StageCore | null>('stage');
601
+
602
+ const newNodes = await Promise.all(nodes.map((node) => this.doAlignCenter(node)));
560
603
 
561
- return config;
604
+ const newNode = await this.update(newNodes);
605
+
606
+ await stage?.multiSelect(newNodes.map((node) => node.id));
607
+
608
+ return newNode;
562
609
  }
563
610
 
564
611
  /**
@@ -581,6 +628,7 @@ class Editor extends BaseService {
581
628
 
582
629
  this.get<StageCore | null>('stage')?.update({
583
630
  config: cloneDeep(toRaw(parent)),
631
+ parentId: parent.id,
584
632
  root: cloneDeep(this.get<MApp>('root')),
585
633
  });
586
634
  }
@@ -601,7 +649,7 @@ class Editor extends BaseService {
601
649
  const index = getNodeIndex(node, parent);
602
650
  parent.items?.splice(index, 1);
603
651
 
604
- await stage.remove({ id: node.id, root });
652
+ await stage.remove({ id: node.id, parentId: parent.id, root });
605
653
 
606
654
  const layout = await this.getLayout(target);
607
655
 
@@ -610,13 +658,13 @@ class Editor extends BaseService {
610
658
  return srcValue;
611
659
  }
612
660
  });
613
- newConfig.style = getInitPositionStyle(newConfig.style, layout, target, stage);
661
+ newConfig.style = getInitPositionStyle(newConfig.style, layout);
614
662
 
615
663
  target.items.push(newConfig);
616
664
 
617
665
  await stage.select(targetId);
618
666
 
619
- await stage.update({ config: cloneDeep(target), root });
667
+ await stage.update({ config: cloneDeep(target), parentId: parent.id, root });
620
668
 
621
669
  await this.select(newConfig);
622
670
  stage.select(newConfig.id);
@@ -653,7 +701,7 @@ class Editor extends BaseService {
653
701
  const node = toRaw(this.get('node'));
654
702
  if (!node || isPage(node)) return;
655
703
 
656
- const { style, id } = node;
704
+ const { style, id, type } = node;
657
705
  if (!style || style.position !== 'absolute') return;
658
706
 
659
707
  if (top && !isNumber(style.top)) return;
@@ -661,6 +709,7 @@ class Editor extends BaseService {
661
709
 
662
710
  this.update({
663
711
  id,
712
+ type,
664
713
  style: {
665
714
  ...style,
666
715
  left: Number(style.left) + left,
@@ -673,6 +722,7 @@ class Editor extends BaseService {
673
722
  this.removeAllListeners();
674
723
  this.set('root', null);
675
724
  this.set('node', null);
725
+ this.set('nodes', []);
676
726
  this.set('page', null);
677
727
  this.set('parent', null);
678
728
  }
@@ -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