@tmagic/editor 1.0.0-beta.11 → 1.0.0-beta.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +1 -0
  2. package/coverage/clover.xml +1540 -0
  3. package/coverage/coverage-final.json +35 -0
  4. package/coverage/lcov-report/base.css +224 -0
  5. package/coverage/lcov-report/block-navigation.js +87 -0
  6. package/coverage/lcov-report/favicon.png +0 -0
  7. package/coverage/lcov-report/index.html +206 -0
  8. package/coverage/lcov-report/prettify.css +1 -0
  9. package/coverage/lcov-report/prettify.js +2 -0
  10. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  11. package/coverage/lcov-report/sorter.js +196 -0
  12. package/coverage/lcov-report/src/Editor.vue.html +787 -0
  13. package/coverage/lcov-report/src/components/ContentMenu.vue.html +406 -0
  14. package/coverage/lcov-report/src/components/Icon.vue.html +172 -0
  15. package/coverage/lcov-report/src/components/ScrollViewer.vue.html +283 -0
  16. package/coverage/lcov-report/src/components/ToolButton.vue.html +772 -0
  17. package/coverage/lcov-report/src/components/index.html +161 -0
  18. package/coverage/lcov-report/src/index.html +131 -0
  19. package/coverage/lcov-report/src/layouts/AddPageBox.vue.html +214 -0
  20. package/coverage/lcov-report/src/layouts/Framework.vue.html +304 -0
  21. package/coverage/lcov-report/src/layouts/NavMenu.vue.html +208 -0
  22. package/coverage/lcov-report/src/layouts/PropsPanel.vue.html +316 -0
  23. package/coverage/lcov-report/src/layouts/Resizer.vue.html +268 -0
  24. package/coverage/lcov-report/src/layouts/index.html +176 -0
  25. package/coverage/lcov-report/src/layouts/sidebar/ComponentListPanel.vue.html +358 -0
  26. package/coverage/lcov-report/src/layouts/sidebar/LayerMenu.vue.html +421 -0
  27. package/coverage/lcov-report/src/layouts/sidebar/LayerPanel.vue.html +775 -0
  28. package/coverage/lcov-report/src/layouts/sidebar/Sidebar.vue.html +343 -0
  29. package/coverage/lcov-report/src/layouts/sidebar/index.html +161 -0
  30. package/coverage/lcov-report/src/layouts/workspace/PageBar.vue.html +718 -0
  31. package/coverage/lcov-report/src/layouts/workspace/Stage.vue.html +757 -0
  32. package/coverage/lcov-report/src/layouts/workspace/ViewerMenu.vue.html +532 -0
  33. package/coverage/lcov-report/src/layouts/workspace/Workspace.vue.html +586 -0
  34. package/coverage/lcov-report/src/layouts/workspace/index.html +161 -0
  35. package/coverage/lcov-report/src/services/BaseService.ts.html +643 -0
  36. package/coverage/lcov-report/src/services/componentList.ts.html +229 -0
  37. package/coverage/lcov-report/src/services/editor.ts.html +2077 -0
  38. package/coverage/lcov-report/src/services/events.ts.html +331 -0
  39. package/coverage/lcov-report/src/services/history.ts.html +478 -0
  40. package/coverage/lcov-report/src/services/index.html +206 -0
  41. package/coverage/lcov-report/src/services/props.ts.html +415 -0
  42. package/coverage/lcov-report/src/services/ui.ts.html +514 -0
  43. package/coverage/lcov-report/src/type.ts.html +844 -0
  44. package/coverage/lcov-report/src/utils/compose.ts.html +184 -0
  45. package/coverage/lcov-report/src/utils/config.ts.html +172 -0
  46. package/coverage/lcov-report/src/utils/editor.ts.html +838 -0
  47. package/coverage/lcov-report/src/utils/index.html +221 -0
  48. package/coverage/lcov-report/src/utils/index.ts.html +151 -0
  49. package/coverage/lcov-report/src/utils/logger.ts.html +226 -0
  50. package/coverage/lcov-report/src/utils/props.ts.html +796 -0
  51. package/coverage/lcov-report/src/utils/scroll-viewer.ts.html +733 -0
  52. package/coverage/lcov-report/src/utils/undo-redo.ts.html +313 -0
  53. package/coverage/lcov.info +3676 -0
  54. package/dist/style.css +42 -20
  55. package/dist/tmagic-editor.es.js +972 -599
  56. package/dist/tmagic-editor.es.js.map +1 -1
  57. package/dist/tmagic-editor.umd.js +974 -601
  58. package/dist/tmagic-editor.umd.js.map +1 -1
  59. package/dist/types/src/Editor.vue.d.ts +1 -1
  60. package/dist/types/src/components/ToolButton.vue.d.ts +12 -1
  61. package/dist/types/src/index.d.ts +1 -0
  62. package/dist/types/src/layouts/sidebar/ComponentListPanel.vue.d.ts +2 -1
  63. package/dist/types/src/layouts/workspace/PageBar.vue.d.ts +2 -0
  64. package/dist/types/src/layouts/workspace/Workspace.vue.d.ts +2 -1
  65. package/dist/types/src/services/editor.d.ts +5 -2
  66. package/dist/types/src/services/history.d.ts +1 -0
  67. package/dist/types/src/services/props.d.ts +2 -2
  68. package/dist/types/src/services/ui.d.ts +1 -0
  69. package/dist/types/src/type.d.ts +9 -9
  70. package/dist/types/src/utils/editor.d.ts +4 -3
  71. package/dist/types/src/utils/polyfills.d.ts +0 -0
  72. package/package.json +29 -9
  73. package/src/components/ContentMenu.vue +107 -0
  74. package/src/components/ScrollViewer.vue +1 -1
  75. package/src/components/ToolButton.vue +69 -30
  76. package/src/index.ts +2 -0
  77. package/src/layouts/Framework.vue +6 -6
  78. package/src/layouts/PropsPanel.vue +1 -1
  79. package/src/layouts/sidebar/ComponentListPanel.vue +25 -3
  80. package/src/layouts/sidebar/LayerMenu.vue +91 -71
  81. package/src/layouts/sidebar/LayerPanel.vue +23 -49
  82. package/src/layouts/workspace/PageBar.vue +35 -6
  83. package/src/layouts/workspace/Stage.vue +41 -44
  84. package/src/layouts/workspace/ViewerMenu.vue +104 -67
  85. package/src/layouts/workspace/Workspace.vue +106 -3
  86. package/src/services/BaseService.ts +1 -1
  87. package/src/services/editor.ts +104 -12
  88. package/src/services/history.ts +7 -0
  89. package/src/services/props.ts +3 -3
  90. package/src/services/ui.ts +5 -0
  91. package/src/theme/common/var.scss +1 -0
  92. package/src/theme/component-list-panel.scss +2 -0
  93. package/src/theme/content-menu.scss +33 -26
  94. package/src/theme/layer-panel.scss +2 -1
  95. package/src/theme/page-bar.scss +22 -1
  96. package/src/type.ts +9 -9
  97. package/src/utils/editor.ts +31 -6
  98. package/{vite.config.ts → src/utils/polyfills.ts} +4 -12
  99. package/src/utils/props.ts +20 -1
  100. package/dist/types/src/layouts/sidebar/LayerPanel.vue.d.ts +0 -980
  101. package/dist/types/src/layouts/workspace/Stage.vue.d.ts +0 -188
  102. package/dist/types/src/layouts/workspace/ViewerMenu.vue.d.ts +0 -18
  103. package/tsconfig.json +0 -9
@@ -1,47 +1,31 @@
1
1
  <template>
2
- <div class="magic-editor-content-menu" ref="menu">
3
- <div>
4
- <div class="magic-editor-content-menu-item" @click="() => center()" v-if="canCenter">水平居中</div>
5
- <div class="magic-editor-content-menu-item" @click="() => copy()">复制</div>
6
- <div class="magic-editor-content-menu-item" @click="paste" v-if="canPaste">粘贴</div>
7
- <template v-if="canMoveZPos">
8
- <div class="separation"></div>
9
- <div class="magic-editor-content-menu-item" @click="topItem">上移一层</div>
10
- <div class="magic-editor-content-menu-item" @click="bottomItem">下移一层</div>
11
- <div class="magic-editor-content-menu-item" @click="top">置顶</div>
12
- <div class="magic-editor-content-menu-item" @click="bottom">置底</div>
13
- </template>
14
- <template v-if="canDelete">
15
- <div class="separation"></div>
16
- <div class="magic-editor-content-menu-item" @click="() => remove()">删除</div>
17
- </template>
18
- <div class="separation"></div>
19
- <div class="magic-editor-content-menu-item" @click="clearGuides">清空参考线</div>
20
- </div>
21
- </div>
2
+ <content-menu :menu-data="menuData" ref="menu"></content-menu>
22
3
  </template>
23
4
 
24
5
  <script lang="ts">
25
- import { computed, defineComponent, inject, onMounted, ref, watch } from 'vue';
6
+ import { computed, defineComponent, inject, markRaw, onMounted, reactive, ref, watch } from 'vue';
7
+ import { Bottom, Delete, DocumentCopy, Top } from '@element-plus/icons';
26
8
 
27
9
  import { NodeType } from '@tmagic/schema';
28
10
  import type StageCore from '@tmagic/stage';
29
11
 
30
- import { LayerOffset, Layout, Services } from '@editor/type';
12
+ import ContentMenu from '@editor/components/ContentMenu.vue';
13
+ import { LayerOffset, Layout, MenuItem, Services } from '@editor/type';
31
14
  import { COPY_STORAGE_KEY } from '@editor/utils/editor';
32
15
 
33
16
  export default defineComponent({
34
- name: 'magic-editor-ui-viewer-menu',
17
+ components: { ContentMenu },
35
18
 
36
19
  setup() {
37
20
  const services = inject<Services>('services');
38
21
  const editorService = services?.editorService;
39
- const menu = ref<HTMLDivElement>();
22
+ const menu = ref<InstanceType<typeof ContentMenu>>();
40
23
  const canPaste = ref(false);
41
24
  const canCenter = ref(false);
42
25
 
43
26
  const node = computed(() => editorService?.get('node'));
44
27
  const parent = computed(() => editorService?.get('parent'));
28
+ const isPage = computed(() => node.value?.type === NodeType.PAGE);
45
29
 
46
30
  onMounted(() => {
47
31
  const data = globalThis.localStorage.getItem(COPY_STORAGE_KEY);
@@ -62,49 +46,102 @@ export default defineComponent({
62
46
 
63
47
  return {
64
48
  menu,
65
- canPaste,
66
-
67
- canDelete: computed(() => node.value?.type !== NodeType.PAGE),
68
- canMoveZPos: computed(() => node.value?.type !== NodeType.PAGE),
69
- canCenter,
70
-
71
- center() {
72
- node.value && editorService?.alignCenter(node.value);
73
- },
74
-
75
- copy() {
76
- node.value && editorService?.copy(node.value);
77
- canPaste.value = true;
78
- },
79
-
80
- paste() {
81
- const top = menu.value?.offsetTop || 0;
82
- const left = menu.value?.offsetLeft || 0;
83
- editorService?.paste({ left, top });
84
- },
85
-
86
- remove() {
87
- node.value && editorService?.remove(node.value);
88
- },
89
-
90
- top() {
91
- editorService?.moveLayer(LayerOffset.TOP);
92
- },
93
-
94
- bottom() {
95
- editorService?.moveLayer(LayerOffset.BOTTOM);
96
- },
97
-
98
- topItem() {
99
- editorService?.moveLayer(1);
100
- },
101
-
102
- bottomItem() {
103
- editorService?.moveLayer(-1);
104
- },
105
-
106
- clearGuides() {
107
- editorService?.get<StageCore>('stage').clearGuides();
49
+ menuData: reactive<MenuItem[]>([
50
+ {
51
+ type: 'button',
52
+ text: '水平居中',
53
+ display: () => canCenter.value,
54
+ handler: () => {
55
+ node.value && editorService?.alignCenter(node.value);
56
+ },
57
+ },
58
+ {
59
+ type: 'button',
60
+ text: '复制',
61
+ icon: markRaw(DocumentCopy),
62
+ handler: () => {
63
+ node.value && editorService?.copy(node.value);
64
+ canPaste.value = true;
65
+ },
66
+ },
67
+ {
68
+ type: 'button',
69
+ text: '粘贴',
70
+ display: () => canPaste.value,
71
+ handler: () => {
72
+ const top = menu.value?.$el.offsetTop || 0;
73
+ const left = menu.value?.$el.offsetLeft || 0;
74
+ editorService?.paste({ left, top });
75
+ },
76
+ },
77
+ {
78
+ type: 'divider',
79
+ direction: 'horizontal',
80
+ display: () => !isPage.value,
81
+ },
82
+ {
83
+ type: 'button',
84
+ text: '上移一层',
85
+ icon: markRaw(Top),
86
+ display: () => !isPage.value,
87
+ handler: () => {
88
+ editorService?.moveLayer(1);
89
+ },
90
+ },
91
+ {
92
+ type: 'button',
93
+ text: '下移一层',
94
+ icon: markRaw(Bottom),
95
+ display: () => !isPage.value,
96
+ handler: () => {
97
+ editorService?.moveLayer(-1);
98
+ },
99
+ },
100
+ {
101
+ type: 'button',
102
+ text: '置顶',
103
+ display: () => !isPage.value,
104
+ handler: () => {
105
+ editorService?.moveLayer(LayerOffset.TOP);
106
+ },
107
+ },
108
+ {
109
+ type: 'button',
110
+ text: '置底',
111
+ display: () => !isPage.value,
112
+ handler: () => {
113
+ editorService?.moveLayer(LayerOffset.BOTTOM);
114
+ },
115
+ },
116
+ {
117
+ type: 'divider',
118
+ direction: 'horizontal',
119
+ display: () => !isPage.value,
120
+ },
121
+ {
122
+ type: 'button',
123
+ text: '删除',
124
+ icon: Delete,
125
+ display: () => !isPage.value,
126
+ handler: () => {
127
+ node.value && editorService?.remove(node.value);
128
+ },
129
+ },
130
+ {
131
+ type: 'divider',
132
+ direction: 'horizontal',
133
+ },
134
+ {
135
+ type: 'button',
136
+ text: '清空参考线',
137
+ handler: () => {
138
+ editorService?.get<StageCore>('stage').clearGuides();
139
+ },
140
+ },
141
+ ]),
142
+
143
+ show(e: MouseEvent) {
144
+ menu.value?.show(e);
108
145
  },
109
146
  };
110
147
  },
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="m-editor-workspace">
2
+ <div class="m-editor-workspace" tabindex="1" ref="workspace">
3
3
  <magic-stage
4
4
  :key="page?.id"
5
5
  :runtime-url="runtimeUrl"
@@ -18,11 +18,13 @@
18
18
  </template>
19
19
 
20
20
  <script lang="ts">
21
- import { computed, defineComponent, inject, PropType } from 'vue';
21
+ import { computed, defineComponent, inject, onMounted, onUnmounted, PropType, ref } from 'vue';
22
+ import KeyController from 'keycon';
22
23
 
23
- import type { MPage } from '@tmagic/schema';
24
+ import type { MNode, MPage } from '@tmagic/schema';
24
25
  import type { MoveableOptions } from '@tmagic/stage';
25
26
  import StageCore from '@tmagic/stage';
27
+ import { isPage } from '@tmagic/utils';
26
28
 
27
29
  import type { Services } from '@editor/type';
28
30
 
@@ -55,8 +57,109 @@ export default defineComponent({
55
57
 
56
58
  setup() {
57
59
  const services = inject<Services>('services');
60
+ const workspace = ref<HTMLDivElement>();
61
+ const node = computed(() => services?.editorService.get<MNode>('node'));
62
+ let keycon: KeyController;
63
+
64
+ const mouseenterHandler = () => {
65
+ workspace.value?.focus();
66
+ };
67
+
68
+ onMounted(() => {
69
+ workspace.value?.addEventListener('mouseenter', mouseenterHandler);
70
+
71
+ keycon = new KeyController(workspace.value);
72
+
73
+ const isMac = /mac os x/.test(navigator.userAgent.toLowerCase());
74
+
75
+ const ctrl = isMac ? 'meta' : 'ctrl';
76
+
77
+ keycon
78
+ .keyup('delete', (e) => {
79
+ e.inputEvent.preventDefault();
80
+ if (!node.value || isPage(node.value)) return;
81
+ services?.editorService.remove(node.value);
82
+ })
83
+ .keydown([ctrl, 'c'], (e) => {
84
+ e.inputEvent.preventDefault();
85
+ node.value && services?.editorService.copy(node.value);
86
+ })
87
+ .keydown([ctrl, 'v'], (e) => {
88
+ e.inputEvent.preventDefault();
89
+ node.value && services?.editorService.paste();
90
+ })
91
+ .keydown([ctrl, 'x'], (e) => {
92
+ e.inputEvent.preventDefault();
93
+ if (!node.value || isPage(node.value)) return;
94
+ services?.editorService.copy(node.value);
95
+ services?.editorService.remove(node.value);
96
+ })
97
+ .keydown([ctrl, 'z'], (e) => {
98
+ e.inputEvent.preventDefault();
99
+ services?.editorService.undo();
100
+ })
101
+ .keydown([ctrl, 'shift', 'z'], (e) => {
102
+ e.inputEvent.preventDefault();
103
+ services?.editorService.redo();
104
+ })
105
+ .keydown('up', (e) => {
106
+ e.inputEvent.preventDefault();
107
+ services?.editorService.move(0, -1);
108
+ })
109
+ .keydown('down', (e) => {
110
+ e.inputEvent.preventDefault();
111
+ services?.editorService.move(0, 1);
112
+ })
113
+ .keydown('left', (e) => {
114
+ e.inputEvent.preventDefault();
115
+ services?.editorService.move(-1, 0);
116
+ })
117
+ .keydown('right', (e) => {
118
+ e.inputEvent.preventDefault();
119
+ services?.editorService.move(1, 0);
120
+ })
121
+ .keydown([ctrl, 'up'], (e) => {
122
+ e.inputEvent.preventDefault();
123
+ services?.editorService.move(0, -10);
124
+ })
125
+ .keydown([ctrl, 'down'], (e) => {
126
+ e.inputEvent.preventDefault();
127
+ services?.editorService.move(0, 10);
128
+ })
129
+ .keydown([ctrl, 'left'], (e) => {
130
+ e.inputEvent.preventDefault();
131
+ services?.editorService.move(-10, 0);
132
+ })
133
+ .keydown([ctrl, 'right'], (e) => {
134
+ e.inputEvent.preventDefault();
135
+ services?.editorService.move(10, 0);
136
+ })
137
+ .keydown('tab', (e) => {
138
+ e.inputEvent.preventDefault();
139
+ services?.editorService.selectNextNode();
140
+ })
141
+ .keydown([ctrl, 'tab'], (e) => {
142
+ e.inputEvent.preventDefault();
143
+ services?.editorService.selectNextPage();
144
+ })
145
+ .keydown([ctrl, '='], (e) => {
146
+ e.inputEvent.preventDefault();
147
+ services?.uiService.zoom(0.1);
148
+ })
149
+ .keydown([ctrl, '-'], (e) => {
150
+ e.inputEvent.preventDefault();
151
+ services?.uiService.zoom(-0.1);
152
+ });
153
+ });
154
+
155
+ onUnmounted(() => {
156
+ workspace.value?.removeEventListener('mouseenter', mouseenterHandler);
157
+ keycon.destroy();
158
+ });
58
159
 
59
160
  return {
161
+ workspace,
162
+
60
163
  page: computed(() => services?.editorService.get<MPage>('page')),
61
164
  };
62
165
  },
@@ -53,7 +53,7 @@ const doAction = async (
53
53
  let returnValue: any = await fn(beforeArgs, sourceMethod.bind(scope));
54
54
 
55
55
  for (const afterMethod of scope.pluginOptionsList[afterMethodName]) {
56
- returnValue = await afterMethod(...beforeArgs, returnValue);
56
+ returnValue = await afterMethod(returnValue, ...beforeArgs);
57
57
 
58
58
  if (isError(returnValue)) throw returnValue;
59
59
  }
@@ -22,8 +22,8 @@ import serialize from 'serialize-javascript';
22
22
 
23
23
  import type { Id, MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
24
24
  import { NodeType } from '@tmagic/schema';
25
- import type StageCore from '@tmagic/stage';
26
- import { getNodePath, isPop } from '@tmagic/utils';
25
+ import StageCore from '@tmagic/stage';
26
+ import { getNodePath, isNumber, isPage, isPop } from '@tmagic/utils';
27
27
 
28
28
  import historyService, { StepValue } from '@editor/services/history';
29
29
  import propsService from '@editor/services/props';
@@ -54,6 +54,7 @@ class Editor extends BaseService {
54
54
  stage: null,
55
55
  highlightNode: null,
56
56
  modifiedNodeIds: new Map(),
57
+ pageLength: 0,
57
58
  });
58
59
 
59
60
  constructor() {
@@ -69,6 +70,7 @@ class Editor extends BaseService {
69
70
  'paste',
70
71
  'alignCenter',
71
72
  'moveLayer',
73
+ 'move',
72
74
  'undo',
73
75
  'redo',
74
76
  'highlight',
@@ -89,6 +91,7 @@ class Editor extends BaseService {
89
91
  log('store set ', name, ' ', value);
90
92
 
91
93
  if (name === 'root') {
94
+ this.state.pageLength = (value as unknown as MApp)?.items?.length || 0;
92
95
  this.emit('root-change', value);
93
96
  }
94
97
  }
@@ -108,7 +111,7 @@ class Editor extends BaseService {
108
111
  * @returns {EditorNodeInfo}
109
112
  */
110
113
  public getNodeInfo(id: Id): EditorNodeInfo {
111
- const root = this.get<MApp | null>('root');
114
+ const root = toRaw(this.get<MApp | null>('root'));
112
115
  if (!root) return {};
113
116
 
114
117
  if (id === root.id) {
@@ -159,13 +162,15 @@ class Editor extends BaseService {
159
162
  /**
160
163
  * 只有容器拥有布局
161
164
  */
162
- public async getLayout(node: MNode): Promise<Layout> {
163
- if (node.layout) {
164
- return node.layout;
165
+ public async getLayout(parent: MNode, node?: MNode): Promise<Layout> {
166
+ if (node && typeof node !== 'function' && isFixed(node)) return Layout.FIXED;
167
+
168
+ if (parent.layout) {
169
+ return parent.layout;
165
170
  }
166
171
 
167
172
  // 如果该节点没有设置position,则认为是流式布局,例如获取root的布局时
168
- if (!node.style?.position) {
173
+ if (!parent.style?.position) {
169
174
  return Layout.RELATIVE;
170
175
  }
171
176
 
@@ -188,9 +193,45 @@ class Editor extends BaseService {
188
193
  } else {
189
194
  historyService.empty();
190
195
  }
196
+
197
+ this.emit('select', node);
198
+
191
199
  return node!;
192
200
  }
193
201
 
202
+ public async selectNextNode(): Promise<MNode> | never {
203
+ const node = toRaw(this.get('node'));
204
+
205
+ if (!node || isPage(node) || node.type === NodeType.ROOT) return node;
206
+
207
+ const parent = toRaw(this.getParentById(node.id));
208
+
209
+ if (!parent) return node;
210
+
211
+ const index = getNodeIndex(node, parent);
212
+
213
+ const nextNode = parent.items[index + 1] || parent.items[0];
214
+
215
+ await this.select(nextNode);
216
+ this.get<StageCore>('stage')?.select(nextNode.id);
217
+
218
+ return nextNode;
219
+ }
220
+
221
+ public async selectNextPage(): Promise<MNode> | never {
222
+ const root = toRaw(this.get<MApp>('root'));
223
+ const page = toRaw(this.get('page'));
224
+
225
+ const index = getNodeIndex(page, root);
226
+
227
+ const nextPage = root.items[index + 1] || root.items[0];
228
+
229
+ await this.select(nextPage);
230
+ this.get<StageCore>('stage')?.select(nextPage.id);
231
+
232
+ return nextPage;
233
+ }
234
+
194
235
  /**
195
236
  * 高亮指定节点
196
237
  * @param config 指定节点配置或者ID
@@ -209,12 +250,14 @@ class Editor extends BaseService {
209
250
  * @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
210
251
  * @returns 添加后的节点
211
252
  */
212
- public async add({ type, ...config }: AddMNode, parent?: MContainer | null): Promise<MNode> {
253
+ public async add(addNode: AddMNode, parent?: MContainer | null): Promise<MNode> {
254
+ const { type, ...config } = addNode;
213
255
  const curNode = this.get<MContainer>('node');
214
256
 
215
257
  let parentNode: MNode | undefined;
258
+ const isPage = type === NodeType.PAGE;
216
259
 
217
- if (type === NodeType.PAGE) {
260
+ if (isPage) {
218
261
  parentNode = this.get<MApp>('root');
219
262
  // 由于支持中间件扩展,在parent参数为undefined时,parent会变成next函数
220
263
  } else if (parent && typeof parent !== 'function') {
@@ -227,8 +270,13 @@ class Editor extends BaseService {
227
270
 
228
271
  if (!parentNode) throw new Error('未找到父元素');
229
272
 
230
- const layout = await this.getLayout(parentNode);
231
- const newNode = initPosition({ ...toRaw(await propsService.getPropsValue(type)), ...config }, layout);
273
+ const layout = await this.getLayout(toRaw(parentNode), addNode as MNode);
274
+ const newNode = initPosition(
275
+ { ...toRaw(await propsService.getPropsValue(type, config)) },
276
+ layout,
277
+ parentNode,
278
+ this.get<StageCore>('stage'),
279
+ );
232
280
 
233
281
  if ((parentNode?.type === NodeType.ROOT || curNode.type === NodeType.ROOT) && newNode.type !== NodeType.PAGE) {
234
282
  throw new Error('app下不能添加组件');
@@ -249,6 +297,12 @@ class Editor extends BaseService {
249
297
 
250
298
  stage?.select(newNode.id);
251
299
 
300
+ if (isPage) {
301
+ this.state.pageLength += 1;
302
+ }
303
+
304
+ this.emit('add', newNode);
305
+
252
306
  return newNode;
253
307
  }
254
308
 
@@ -277,9 +331,23 @@ class Editor extends BaseService {
277
331
  stage?.remove({ id: node.id, root: this.get('root') });
278
332
 
279
333
  if (node.type === NodeType.PAGE) {
334
+ this.state.pageLength -= 1;
335
+
280
336
  if (root.items[0]) {
281
337
  await this.select(root.items[0]);
282
338
  stage?.select(root.items[0].id);
339
+ } else {
340
+ this.set('node', null);
341
+ this.set('parent', null);
342
+ this.set('page', null);
343
+ this.set('stage', null);
344
+ this.set('highlightNode', null);
345
+ this.resetModifiedNodeId();
346
+ historyService.reset();
347
+
348
+ this.emit('remove', node);
349
+
350
+ return node;
283
351
  }
284
352
  } else {
285
353
  await this.select(parent);
@@ -289,6 +357,8 @@ class Editor extends BaseService {
289
357
  this.addModifiedNodeId(parent.id);
290
358
  this.pushHistoryState();
291
359
 
360
+ this.emit('remove', node);
361
+
292
362
  return node;
293
363
  }
294
364
 
@@ -350,6 +420,8 @@ class Editor extends BaseService {
350
420
  this.addModifiedNodeId(newConfig.id);
351
421
  this.pushHistoryState();
352
422
 
423
+ this.emit('update', newConfig);
424
+
353
425
  return newConfig;
354
426
  }
355
427
 
@@ -427,7 +499,7 @@ class Editor extends BaseService {
427
499
  public async alignCenter(config: MNode): Promise<MNode | void> {
428
500
  const parent = this.get<MContainer>('parent');
429
501
  const node = this.get<MNode>('node');
430
- const layout = await this.getLayout(parent);
502
+ const layout = await this.getLayout(toRaw(parent), toRaw(node));
431
503
  if (layout === Layout.RELATIVE) {
432
504
  return;
433
505
  }
@@ -485,6 +557,26 @@ class Editor extends BaseService {
485
557
  return value;
486
558
  }
487
559
 
560
+ public async move(left: number, top: number) {
561
+ const node = toRaw(this.get('node'));
562
+ if (!node || isPage(node)) return;
563
+
564
+ const { style, id } = node;
565
+ if (!style || style.position !== 'absolute') return;
566
+
567
+ if (top && !isNumber(style.top)) return;
568
+ if (left && !isNumber(style.left)) return;
569
+
570
+ this.update({
571
+ id,
572
+ style: {
573
+ ...style,
574
+ left: Number(style.left) + left,
575
+ top: Number(style.top) + top,
576
+ },
577
+ });
578
+ }
579
+
488
580
  public destroy() {
489
581
  this.removeAllListeners();
490
582
  this.set('root', null);
@@ -51,6 +51,13 @@ class History extends BaseService {
51
51
  this.on('change', this.setCanUndoRedo);
52
52
  }
53
53
 
54
+ public reset() {
55
+ this.state.pageSteps = {};
56
+ this.state.pageId = undefined;
57
+ this.state.canRedo = false;
58
+ this.state.canUndo = false;
59
+ }
60
+
54
61
  public changePage(page: MPage): void {
55
62
  if (!page) return;
56
63
 
@@ -17,7 +17,7 @@
17
17
  */
18
18
 
19
19
  import { reactive } from 'vue';
20
- import { cloneDeep } from 'lodash-es';
20
+ import { cloneDeep, mergeWith } from 'lodash-es';
21
21
 
22
22
  import type { FormConfig } from '@tmagic/form';
23
23
  import type { MComponent, MNode } from '@tmagic/schema';
@@ -87,7 +87,7 @@ class Props extends BaseService {
87
87
  * @param type 组件类型
88
88
  * @returns 组件初始值
89
89
  */
90
- public async getPropsValue(type: string) {
90
+ public async getPropsValue(type: string, defaultValue = {}) {
91
91
  if (type === 'area') {
92
92
  const value = (await this.getPropsValue('button')) as MComponent;
93
93
  value.className = 'action-area';
@@ -100,7 +100,7 @@ class Props extends BaseService {
100
100
 
101
101
  return cloneDeep({
102
102
  ...getDefaultPropsValue(type),
103
- ...(this.state.propsValueMap[type] || {}),
103
+ ...mergeWith(this.state.propsValueMap[type] || {}, defaultValue),
104
104
  });
105
105
  }
106
106
  }
@@ -91,6 +91,11 @@ class Ui extends BaseService {
91
91
  return (state as any)[name];
92
92
  }
93
93
 
94
+ public zoom(zoom: number) {
95
+ this.set('zoom', (this.get<number>('zoom') * 100 + zoom * 100) / 100);
96
+ if (this.get<number>('zoom') < 0.1) this.set('zoom', 0.1);
97
+ }
98
+
94
99
  private setColumnWidth({ left, center, right }: SetColumnWidth) {
95
100
  const columnWidth = {
96
101
  ...toRaw(this.get<GetColumnWidth>('columnWidth')),
@@ -2,6 +2,7 @@ $--theme-color: #2882e0;
2
2
 
3
3
  $--font-color: #070303;
4
4
  $--border-color: #d9dbdd;
5
+ $--hover-color: #f3f5f9;
5
6
 
6
7
  $--nav-height: 35px;
7
8
  $--nav-color: #070303;
@@ -12,6 +12,7 @@
12
12
  top: 0;
13
13
  left: 0;
14
14
  box-sizing: border-box;
15
+ z-index: 1;
15
16
 
16
17
  .el-input__prefix {
17
18
  padding: 10px;
@@ -62,6 +63,7 @@
62
63
  color: $--font-color;
63
64
  flex-direction: column;
64
65
  width: 42px;
66
+ cursor: pointer;
65
67
 
66
68
  i {
67
69
  font-size: 20px;