@tmagic/editor 1.0.6 → 1.1.0-beta.10

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} +1522 -1177
  3. package/dist/tmagic-editor.mjs.map +1 -0
  4. package/dist/tmagic-editor.umd.js +1606 -1259
  5. package/dist/tmagic-editor.umd.js.map +1 -1
  6. package/package.json +22 -20
  7. package/src/Editor.vue +41 -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 +49 -70
  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 +43 -2
  20. package/src/layouts/sidebar/LayerMenu.vue +3 -3
  21. package/src/layouts/sidebar/LayerPanel.vue +13 -4
  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 +208 -188
  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 +247 -140
  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 +28 -7
  41. package/src/utils/config.ts +1 -1
  42. package/src/utils/editor.ts +53 -35
  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 +44 -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 +1099 -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 +27 -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 +21 -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 +11 -4
  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 -14
  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,59 +17,64 @@
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
- generatePageNameByApp,
35
+ fixNodePosition,
36
+ getInitPositionStyle,
37
37
  getNodeIndex,
38
- initPosition,
39
38
  isFixed,
40
39
  setLayout,
41
- } from '@editor/utils/editor';
42
- import { log } from '@editor/utils/logger';
40
+ } from '../utils/editor';
41
+ import { beforePaste, getAddParent } from '../utils/operator';
43
42
 
44
43
  import BaseService from './BaseService';
44
+ import propsService from './props';
45
45
 
46
46
  class Editor extends BaseService {
47
- private isHistoryStateChange = false;
48
-
49
- private state = reactive<StoreState>({
47
+ public state: StoreState = reactive({
50
48
  root: null,
51
49
  page: null,
52
50
  parent: null,
53
51
  node: null,
52
+ nodes: [],
54
53
  stage: null,
55
54
  highlightNode: null,
56
55
  modifiedNodeIds: new Map(),
57
56
  pageLength: 0,
58
57
  });
58
+ private isHistoryStateChange = false;
59
59
 
60
60
  constructor() {
61
61
  super(
62
62
  [
63
63
  'getLayout',
64
64
  'select',
65
+ 'doAdd',
65
66
  'add',
67
+ 'doRemove',
66
68
  'remove',
69
+ 'doUpdate',
67
70
  'update',
68
71
  'sort',
69
72
  'copy',
70
73
  'paste',
74
+ 'duAlignCenter',
71
75
  'alignCenter',
72
76
  'moveLayer',
77
+ 'moveToContainer',
73
78
  'move',
74
79
  'undo',
75
80
  'redo',
@@ -82,14 +87,16 @@ class Editor extends BaseService {
82
87
 
83
88
  /**
84
89
  * 设置当前指点节点配置
85
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode'
90
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes'
86
91
  * @param value MNode
87
92
  * @returns MNode
88
93
  */
89
94
  public set<T = MNode>(name: keyof StoreState, value: T) {
90
95
  this.state[name] = value as any;
91
- log('store set ', name, ' ', value);
92
-
96
+ // set nodes时将node设置为nodes第一个元素
97
+ if (name === 'nodes') {
98
+ this.set('node', (value as unknown as MNode[])[0]);
99
+ }
93
100
  if (name === 'root') {
94
101
  this.state.pageLength = (value as unknown as MApp)?.items?.length || 0;
95
102
  this.emit('root-change', value);
@@ -98,7 +105,7 @@ class Editor extends BaseService {
98
105
 
99
106
  /**
100
107
  * 获取当前指点节点配置
101
- * @param name 'root' | 'page' | 'parent' | 'node'
108
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes'
102
109
  * @returns MNode
103
110
  */
104
111
  public get<T = MNode>(name: keyof StoreState): T {
@@ -190,7 +197,7 @@ class Editor extends BaseService {
190
197
  */
191
198
  public async select(config: MNode | Id): Promise<MNode> | never {
192
199
  const { node, page, parent } = this.selectedConfigExceptionHandler(config);
193
- this.set('node', node);
200
+ this.set('nodes', [node]);
194
201
  this.set('page', page || null);
195
202
  this.set('parent', parent || null);
196
203
 
@@ -265,80 +272,110 @@ class Editor extends BaseService {
265
272
  }
266
273
 
267
274
  /**
268
- * 向指点容器添加组件节点
269
- * @param addConfig 将要添加的组件节点配置
270
- * @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
271
- * @returns 添加后的节点
275
+ * 多选
276
+ * @param ids 指定节点ID
277
+ * @returns 加入多选的节点配置
272
278
  */
273
- public async add(addNode: AddMNode, parent?: MContainer | null): Promise<MNode> {
274
- const { type, ...config } = addNode;
275
- const curNode = this.get<MContainer>('node');
276
-
277
- let parentNode: MNode | undefined;
278
- const isPage = type === NodeType.PAGE;
279
-
280
- if (isPage) {
281
- parentNode = this.get<MApp>('root');
282
- // 由于支持中间件扩展,在parent参数为undefined时,parent会变成next函数
283
- } else if (parent && typeof parent !== 'function') {
284
- parentNode = parent;
285
- } else if (curNode.items) {
286
- parentNode = curNode;
287
- } else {
288
- parentNode = this.getParentById(curNode.id, false);
289
- }
290
-
291
- 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
+ }
292
289
 
293
- const layout = await this.getLayout(toRaw(parentNode), addNode as MNode);
294
- const newNode = initPosition(
295
- { ...toRaw(await propsService.getPropsValue(type, config)) },
296
- layout,
297
- parentNode,
298
- this.get<StageCore>('stage'),
299
- );
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');
300
294
 
301
- 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) {
302
296
  throw new Error('app下不能添加组件');
303
297
  }
304
298
 
305
- parentNode?.items?.push(newNode);
299
+ // 新增节点添加到配置中
300
+ parent?.items?.push(node);
301
+
302
+ const layout = await this.getLayout(toRaw(parent), node as MNode);
303
+ node.style = getInitPositionStyle(node.style, layout);
306
304
 
305
+ await stage?.add({
306
+ config: cloneDeep(node),
307
+ parent: cloneDeep(parent),
308
+ parentId: parent.id,
309
+ root: cloneDeep(root),
310
+ });
311
+
312
+ node.style = fixNodePosition(node, parent, stage);
313
+
314
+ await stage?.update({ config: cloneDeep(node), parentId: parent.id, root: cloneDeep(root) });
315
+
316
+ this.addModifiedNodeId(node.id);
317
+
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[]> {
307
328
  const stage = this.get<StageCore | null>('stage');
308
329
 
309
- await stage?.add({ config: cloneDeep(newNode), root: cloneDeep(this.get('root')) });
330
+ // 新增多个组件只存在于粘贴多个组件,粘贴的是一个完整的config,所以不再需要getPropsValue
331
+ const addNodes = [];
332
+ if (!Array.isArray(addNode)) {
333
+ const { type, inputEvent, ...config } = addNode;
310
334
 
311
- await this.select(newNode);
335
+ if (!type) throw new Error('组件类型不能为空');
312
336
 
313
- this.addModifiedNodeId(newNode.id);
314
- if (!isPage) {
315
- this.pushHistoryState();
337
+ addNodes.push({ ...toRaw(await propsService.getPropsValue(type, config)) });
338
+ } else {
339
+ addNodes.push(...addNode);
316
340
  }
317
341
 
318
- 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
+ );
349
+
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]);
319
357
 
320
- if (isPage) {
321
- this.state.pageLength += 1;
358
+ if (isPage(newNodes[0])) {
359
+ this.state.pageLength += 1;
360
+ } else {
361
+ // 新增页面,这个时候页面还有渲染出来,此时select会出错,在runtime-ready的时候回去select
362
+ stage?.select(newNodes[0].id);
363
+ }
322
364
  }
323
365
 
324
- this.emit('add', newNode);
366
+ this.pushHistoryState();
325
367
 
326
- return newNode;
327
- }
368
+ this.emit('add', newNodes);
328
369
 
329
- /**
330
- * 删除组件
331
- * @param {Object} node
332
- * @return {Object} 删除的组件配置
333
- */
334
- public async remove(node: MNode): Promise<MNode | void> {
335
- if (!node?.id) return;
370
+ return newNodes.length > 1 ? newNodes[0] : newNodes;
371
+ }
336
372
 
373
+ public async doRemove(node: MNode): Promise<void> {
337
374
  const root = this.get<MApp | null>('root');
338
375
 
339
376
  if (!root) throw new Error('没有root');
340
377
 
341
- const { parent, node: curNode } = this.getNodeInfo(node.id, false);
378
+ const { parent, node: curNode } = this.getNodeInfo(node.id);
342
379
 
343
380
  if (!parent || !curNode) throw new Error('找不要删除的节点');
344
381
 
@@ -348,7 +385,7 @@ class Editor extends BaseService {
348
385
 
349
386
  parent.items?.splice(index, 1);
350
387
  const stage = this.get<StageCore | null>('stage');
351
- stage?.remove({ id: node.id, root: this.get('root') });
388
+ stage?.remove({ id: node.id, parentId: parent.id, root: cloneDeep(root) });
352
389
 
353
390
  if (node.type === NodeType.PAGE) {
354
391
  this.state.pageLength -= 1;
@@ -365,9 +402,7 @@ class Editor extends BaseService {
365
402
  this.resetModifiedNodeId();
366
403
  historyService.reset();
367
404
 
368
- this.emit('remove', node);
369
-
370
- return node;
405
+ return;
371
406
  }
372
407
  } else {
373
408
  await this.select(parent);
@@ -375,19 +410,25 @@ class Editor extends BaseService {
375
410
  }
376
411
 
377
412
  this.addModifiedNodeId(parent.id);
378
- this.pushHistoryState();
379
-
380
- this.emit('remove', node);
381
-
382
- return node;
383
413
  }
384
414
 
385
415
  /**
386
- * 更新节点
387
- * @param config 新的节点配置,配置中需要有id信息
388
- * @returns 更新后的节点配置
416
+ * 删除组件
417
+ * @param {Object} node
418
+ * @return {Object} 删除的组件配置
389
419
  */
390
- 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) {
391
432
  if (!config?.id) throw new Error('没有配置或者配置缺少id值');
392
433
 
393
434
  const info = this.getNodeInfo(config.id, false);
@@ -427,22 +468,42 @@ class Editor extends BaseService {
427
468
 
428
469
  parentNodeItems[index] = newConfig;
429
470
 
430
- if (`${newConfig.id}` === `${this.get('node').id}`) {
431
- this.set('node', newConfig);
432
- }
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);
433
476
 
434
- this.get<StageCore | null>('stage')?.update({ config: cloneDeep(newConfig), root: 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
+ });
435
482
 
436
483
  if (newConfig.type === NodeType.PAGE) {
437
484
  this.set('page', newConfig);
438
485
  }
439
486
 
440
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
+
441
502
  this.pushHistoryState();
442
503
 
443
- this.emit('update', newConfig);
504
+ this.emit('update', newNodes);
444
505
 
445
- return newConfig;
506
+ return newNodes.length > 1 ? newNodes[0] : newNodes;
446
507
  }
447
508
 
448
509
  /**
@@ -464,7 +525,11 @@ class Editor extends BaseService {
464
525
  await this.update(parent);
465
526
  await this.select(node);
466
527
 
467
- this.get<StageCore | null>('stage')?.update({ config: cloneDeep(node), root: 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
+ });
468
533
 
469
534
  this.addModifiedNodeId(parent.id);
470
535
  this.pushHistoryState();
@@ -475,67 +540,46 @@ class Editor extends BaseService {
475
540
  * @param config 组件节点配置
476
541
  * @returns 组件节点配置
477
542
  */
478
- public async copy(config: MNode): Promise<void> {
479
- 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
+ });
480
547
  }
481
548
 
482
549
  /**
483
550
  * 从localStorage中获取节点,然后添加到当前容器中
484
- * @param position 如果设置,指定组件位置
551
+ * @param position 粘贴的坐标
485
552
  * @returns 添加后的组件节点配置
486
553
  */
487
- public async paste(position: { left?: number; top?: number } = {}): Promise<MNode | void> {
488
- const configStr = globalThis.localStorage.getItem(COPY_STORAGE_KEY);
489
- // eslint-disable-next-line prefer-const
490
- let config: any = {};
491
- if (!configStr) {
492
- return;
493
- }
554
+ public async paste(position: PastePosition = {}): Promise<MNode | MNode[] | void> {
555
+ const config: MNode[] = await storageService.getItem(COPY_STORAGE_KEY);
494
556
 
495
- try {
496
- // eslint-disable-next-line no-eval
497
- eval(`config = ${configStr}`);
498
- } catch (e) {
499
- console.error(e);
500
- return;
501
- }
502
-
503
- await propsService.setNewItemId(config, this.get('root'));
504
- if (config.style) {
505
- config.style = {
506
- ...config.style,
507
- ...position,
508
- };
509
- }
557
+ if (!Array.isArray(config)) return;
510
558
 
511
- if (isPage(config)) {
512
- config.name = generatePageNameByApp(this.get('root'));
513
- }
559
+ const pasteConfigs = await beforePaste(position, config);
514
560
 
515
- return await this.add(config);
561
+ return this.add(pasteConfigs);
516
562
  }
517
563
 
518
- /**
519
- * 将指点节点设置居中
520
- * @param config 组件节点配置
521
- * @returns 当前组件节点配置
522
- */
523
- public async alignCenter(config: MNode): Promise<MNode | void> {
524
- const parent = this.get<MContainer>('parent');
525
- const node = this.get<MNode>('node');
526
- 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);
527
571
  if (layout === Layout.RELATIVE) {
528
- return;
572
+ return config;
529
573
  }
530
574
 
531
- if (!node.style) return;
575
+ if (!node.style) return config;
532
576
 
533
577
  const stage = this.get<StageCore>('stage');
534
578
  const doc = stage?.renderer.contentWindow?.document;
535
579
 
536
580
  if (doc) {
537
- const parentEl = doc.getElementById(`${parent.id}`);
538
581
  const el = doc.getElementById(`${node.id}`);
582
+ const parentEl = el?.offsetParent;
539
583
  if (parentEl && el) {
540
584
  node.style.left = (parentEl.clientWidth - el.clientWidth) / 2;
541
585
  }
@@ -543,12 +587,25 @@ class Editor extends BaseService {
543
587
  node.style.left = (parent.style.width - node.style.width) / 2;
544
588
  }
545
589
 
546
- await this.update(node);
547
- this.get<StageCore | null>('stage')?.update({ config: cloneDeep(toRaw(node)), root: this.get('root') });
548
- this.addModifiedNodeId(config.id);
549
- this.pushHistoryState();
590
+ return node;
591
+ }
550
592
 
551
- return config;
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)));
603
+
604
+ const newNode = await this.update(newNodes);
605
+
606
+ await stage?.multiSelect(newNodes.map((node) => node.id));
607
+
608
+ return newNode;
552
609
  }
553
610
 
554
611
  /**
@@ -569,7 +626,55 @@ class Editor extends BaseService {
569
626
  brothers.splice(index + parseInt(`${offset}`, 10), 0, brothers.splice(index, 1)[0]);
570
627
  }
571
628
 
572
- this.get<StageCore | null>('stage')?.update({ config: cloneDeep(toRaw(parent)), root: this.get('root') });
629
+ this.get<StageCore | null>('stage')?.update({
630
+ config: cloneDeep(toRaw(parent)),
631
+ parentId: parent.id,
632
+ root: cloneDeep(this.get<MApp>('root')),
633
+ });
634
+ }
635
+
636
+ /**
637
+ * 移动到指定容器中
638
+ * @param config 需要移动的节点
639
+ * @param targetId 容器ID
640
+ */
641
+ public async moveToContainer(config: MNode, targetId: Id): Promise<MNode | undefined> {
642
+ const { node, parent } = this.getNodeInfo(config.id, false);
643
+ const target = this.getNodeById(targetId, false) as MContainer;
644
+
645
+ const stage = this.get<StageCore | null>('stage');
646
+
647
+ if (node && parent && stage) {
648
+ const root = cloneDeep(this.get<MApp>('root'));
649
+ const index = getNodeIndex(node, parent);
650
+ parent.items?.splice(index, 1);
651
+
652
+ await stage.remove({ id: node.id, parentId: parent.id, root });
653
+
654
+ const layout = await this.getLayout(target);
655
+
656
+ const newConfig = mergeWith(cloneDeep(node), config, (objValue, srcValue) => {
657
+ if (Array.isArray(srcValue)) {
658
+ return srcValue;
659
+ }
660
+ });
661
+ newConfig.style = getInitPositionStyle(newConfig.style, layout);
662
+
663
+ target.items.push(newConfig);
664
+
665
+ await stage.select(targetId);
666
+
667
+ await stage.update({ config: cloneDeep(target), parentId: parent.id, root });
668
+
669
+ await this.select(newConfig);
670
+ stage.select(newConfig.id);
671
+
672
+ this.addModifiedNodeId(target.id);
673
+ this.addModifiedNodeId(parent.id);
674
+ this.pushHistoryState();
675
+
676
+ return newConfig;
677
+ }
573
678
  }
574
679
 
575
680
  /**
@@ -596,7 +701,7 @@ class Editor extends BaseService {
596
701
  const node = toRaw(this.get('node'));
597
702
  if (!node || isPage(node)) return;
598
703
 
599
- const { style, id } = node;
704
+ const { style, id, type } = node;
600
705
  if (!style || style.position !== 'absolute') return;
601
706
 
602
707
  if (top && !isNumber(style.top)) return;
@@ -604,6 +709,7 @@ class Editor extends BaseService {
604
709
 
605
710
  this.update({
606
711
  id,
712
+ type,
607
713
  style: {
608
714
  ...style,
609
715
  left: Number(style.left) + left,
@@ -616,6 +722,7 @@ class Editor extends BaseService {
616
722
  this.removeAllListeners();
617
723
  this.set('root', null);
618
724
  this.set('node', null);
725
+ this.set('nodes', []);
619
726
  this.set('page', null);
620
727
  this.set('parent', null);
621
728
  }
@@ -656,13 +763,13 @@ class Editor extends BaseService {
656
763
  }
657
764
 
658
765
  private async toggleFixedPosition(dist: MNode, src: MNode, root: MApp) {
659
- let newConfig = cloneDeep(dist);
766
+ const newConfig = cloneDeep(dist);
660
767
 
661
768
  if (!isPop(src) && newConfig.style?.position) {
662
769
  if (isFixed(newConfig) && !isFixed(src)) {
663
- newConfig = change2Fixed(newConfig, root);
770
+ newConfig.style = change2Fixed(newConfig, root);
664
771
  } else if (!isFixed(newConfig) && isFixed(src)) {
665
- newConfig = await Fixed2Other(newConfig, root, this.getLayout);
772
+ newConfig.style = await Fixed2Other(newConfig, root, this.getLayout);
666
773
  }
667
774
  }
668
775
 
@@ -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