@tmagic/editor 1.0.0-beta.9 → 1.0.0-rc.3
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.
- package/README.md +1 -0
- package/coverage/clover.xml +1556 -0
- package/coverage/coverage-final.json +36 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +206 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov-report/src/Editor.vue.html +814 -0
- package/coverage/lcov-report/src/components/ContentMenu.vue.html +406 -0
- package/coverage/lcov-report/src/components/Icon.vue.html +172 -0
- package/coverage/lcov-report/src/components/ScrollViewer.vue.html +283 -0
- package/coverage/lcov-report/src/components/ToolButton.vue.html +772 -0
- package/coverage/lcov-report/src/components/index.html +161 -0
- package/coverage/lcov-report/src/index.html +131 -0
- package/coverage/lcov-report/src/layouts/AddPageBox.vue.html +214 -0
- package/coverage/lcov-report/src/layouts/Framework.vue.html +343 -0
- package/coverage/lcov-report/src/layouts/NavMenu.vue.html +208 -0
- package/coverage/lcov-report/src/layouts/PropsPanel.vue.html +319 -0
- package/coverage/lcov-report/src/layouts/Resizer.vue.html +268 -0
- package/coverage/lcov-report/src/layouts/index.html +176 -0
- package/coverage/lcov-report/src/layouts/sidebar/ComponentListPanel.vue.html +358 -0
- package/coverage/lcov-report/src/layouts/sidebar/LayerMenu.vue.html +421 -0
- package/coverage/lcov-report/src/layouts/sidebar/LayerPanel.vue.html +775 -0
- package/coverage/lcov-report/src/layouts/sidebar/Sidebar.vue.html +226 -0
- package/coverage/lcov-report/src/layouts/sidebar/TabPane.vue.html +289 -0
- package/coverage/lcov-report/src/layouts/sidebar/index.html +176 -0
- package/coverage/lcov-report/src/layouts/workspace/PageBar.vue.html +718 -0
- package/coverage/lcov-report/src/layouts/workspace/Stage.vue.html +766 -0
- package/coverage/lcov-report/src/layouts/workspace/ViewerMenu.vue.html +532 -0
- package/coverage/lcov-report/src/layouts/workspace/Workspace.vue.html +604 -0
- package/coverage/lcov-report/src/layouts/workspace/index.html +161 -0
- package/coverage/lcov-report/src/services/BaseService.ts.html +643 -0
- package/coverage/lcov-report/src/services/componentList.ts.html +229 -0
- package/coverage/lcov-report/src/services/editor.ts.html +2077 -0
- package/coverage/lcov-report/src/services/events.ts.html +331 -0
- package/coverage/lcov-report/src/services/history.ts.html +478 -0
- package/coverage/lcov-report/src/services/index.html +206 -0
- package/coverage/lcov-report/src/services/props.ts.html +415 -0
- package/coverage/lcov-report/src/services/ui.ts.html +580 -0
- package/coverage/lcov-report/src/type.ts.html +850 -0
- package/coverage/lcov-report/src/utils/compose.ts.html +184 -0
- package/coverage/lcov-report/src/utils/config.ts.html +172 -0
- package/coverage/lcov-report/src/utils/editor.ts.html +838 -0
- package/coverage/lcov-report/src/utils/index.html +221 -0
- package/coverage/lcov-report/src/utils/index.ts.html +151 -0
- package/coverage/lcov-report/src/utils/logger.ts.html +226 -0
- package/coverage/lcov-report/src/utils/props.ts.html +796 -0
- package/coverage/lcov-report/src/utils/scroll-viewer.ts.html +733 -0
- package/coverage/lcov-report/src/utils/undo-redo.ts.html +313 -0
- package/coverage/lcov.info +3747 -0
- package/dist/style.css +68 -36
- package/dist/tmagic-editor.es.js +1431 -2048
- package/dist/tmagic-editor.es.js.map +1 -1
- package/dist/tmagic-editor.umd.js +1431 -2048
- package/dist/tmagic-editor.umd.js.map +1 -1
- package/dist/types/src/Editor.vue.d.ts +15 -1
- package/dist/types/src/components/ToolButton.vue.d.ts +12 -1
- package/dist/types/src/fields/UISelect.vue.d.ts +4 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/layouts/CodeEditor.vue.d.ts +13 -4
- package/dist/types/src/layouts/Framework.vue.d.ts +15 -3
- package/dist/types/src/layouts/PropsPanel.vue.d.ts +2 -1
- package/dist/types/src/layouts/sidebar/ComponentListPanel.vue.d.ts +2 -1
- package/dist/types/src/layouts/sidebar/Sidebar.vue.d.ts +2 -3
- package/dist/types/src/layouts/sidebar/TabPane.vue.d.ts +14 -0
- package/dist/types/src/layouts/workspace/PageBar.vue.d.ts +10 -3
- package/dist/types/src/layouts/workspace/Workspace.vue.d.ts +9 -8
- package/dist/types/src/services/BaseService.d.ts +4 -1
- package/dist/types/src/services/editor.d.ts +5 -2
- package/dist/types/src/services/history.d.ts +1 -0
- package/dist/types/src/services/props.d.ts +2 -2
- package/dist/types/src/services/ui.d.ts +2 -0
- package/dist/types/src/type.d.ts +15 -9
- package/dist/types/src/utils/editor.d.ts +4 -3
- package/dist/types/src/utils/polyfills.d.ts +0 -0
- package/package.json +44 -12
- package/src/Editor.vue +10 -1
- package/src/components/ContentMenu.vue +107 -0
- package/src/components/ScrollViewer.vue +1 -1
- package/src/components/ToolButton.vue +69 -30
- package/src/fields/UISelect.vue +14 -6
- package/src/index.ts +2 -0
- package/src/layouts/CodeEditor.vue +20 -13
- package/src/layouts/Framework.vue +20 -7
- package/src/layouts/PropsPanel.vue +5 -4
- package/src/layouts/sidebar/ComponentListPanel.vue +25 -3
- package/src/layouts/sidebar/LayerMenu.vue +91 -96
- package/src/layouts/sidebar/LayerPanel.vue +31 -54
- package/src/layouts/sidebar/Sidebar.vue +13 -52
- package/src/layouts/sidebar/TabPane.vue +68 -0
- package/src/layouts/workspace/PageBar.vue +136 -13
- package/src/layouts/workspace/Stage.vue +53 -75
- package/src/layouts/workspace/ViewerMenu.vue +104 -67
- package/src/layouts/workspace/Workspace.vue +111 -22
- package/src/services/BaseService.ts +70 -22
- package/src/services/editor.ts +148 -39
- package/src/services/history.ts +7 -0
- package/src/services/props.ts +3 -3
- package/src/services/ui.ts +35 -5
- package/src/theme/common/var.scss +1 -0
- package/src/theme/component-list-panel.scss +2 -0
- package/src/theme/content-menu.scss +33 -26
- package/src/theme/layer-panel.scss +2 -1
- package/src/theme/nav-menu.scss +6 -2
- package/src/theme/page-bar.scss +35 -3
- package/src/theme/props-panel.scss +19 -17
- package/src/type.ts +16 -9
- package/src/utils/editor.ts +31 -6
- package/src/utils/polyfills.ts +8 -0
- package/src/utils/props.ts +20 -1
- package/dist/types/src/layouts/sidebar/LayerMenu.vue.d.ts +0 -31
- package/dist/types/src/layouts/sidebar/LayerPanel.vue.d.ts +0 -979
- package/dist/types/src/layouts/workspace/Stage.vue.d.ts +0 -193
- package/dist/types/src/layouts/workspace/ViewerMenu.vue.d.ts +0 -18
- package/tsconfig.json +0 -9
- package/vite.config.ts +0 -30
|
@@ -1,15 +1,12 @@
|
|
|
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"
|
|
6
|
+
:auto-scroll-into-view="autoScrollIntoView"
|
|
6
7
|
:render="render"
|
|
7
8
|
:moveable-options="moveableOptions"
|
|
8
9
|
:can-select="canSelect"
|
|
9
|
-
@select="selectHandler"
|
|
10
|
-
@highlight="highlightHandler"
|
|
11
|
-
@update="updateNodeHandler"
|
|
12
|
-
@sort="sortNodeHandler"
|
|
13
10
|
></magic-stage>
|
|
14
11
|
|
|
15
12
|
<slot name="workspace-content"></slot>
|
|
@@ -22,11 +19,13 @@
|
|
|
22
19
|
</template>
|
|
23
20
|
|
|
24
21
|
<script lang="ts">
|
|
25
|
-
import { computed, defineComponent, inject, PropType } from 'vue';
|
|
22
|
+
import { computed, defineComponent, inject, onMounted, onUnmounted, PropType, ref } from 'vue';
|
|
23
|
+
import KeyController from 'keycon';
|
|
26
24
|
|
|
27
|
-
import type {
|
|
28
|
-
import type { MoveableOptions
|
|
25
|
+
import type { MNode, MPage } from '@tmagic/schema';
|
|
26
|
+
import type { MoveableOptions } from '@tmagic/stage';
|
|
29
27
|
import StageCore from '@tmagic/stage';
|
|
28
|
+
import { isPage } from '@tmagic/utils';
|
|
30
29
|
|
|
31
30
|
import type { Services } from '@editor/type';
|
|
32
31
|
|
|
@@ -43,6 +42,7 @@ export default defineComponent({
|
|
|
43
42
|
|
|
44
43
|
props: {
|
|
45
44
|
runtimeUrl: String,
|
|
45
|
+
autoScrollIntoView: Boolean,
|
|
46
46
|
|
|
47
47
|
render: {
|
|
48
48
|
type: Function as PropType<() => HTMLDivElement>,
|
|
@@ -59,25 +59,114 @@ export default defineComponent({
|
|
|
59
59
|
|
|
60
60
|
setup() {
|
|
61
61
|
const services = inject<Services>('services');
|
|
62
|
+
const workspace = ref<HTMLDivElement>();
|
|
63
|
+
const node = computed(() => services?.editorService.get<MNode>('node'));
|
|
64
|
+
let keycon: KeyController;
|
|
62
65
|
|
|
63
|
-
|
|
64
|
-
|
|
66
|
+
const mouseenterHandler = () => {
|
|
67
|
+
workspace.value?.focus();
|
|
68
|
+
};
|
|
65
69
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
const mouseleaveHandler = () => {
|
|
71
|
+
workspace.value?.blur();
|
|
72
|
+
};
|
|
69
73
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
onMounted(() => {
|
|
75
|
+
workspace.value?.addEventListener('mouseenter', mouseenterHandler);
|
|
76
|
+
workspace.value?.addEventListener('mouseleave', mouseleaveHandler);
|
|
77
|
+
|
|
78
|
+
keycon = new KeyController(workspace.value);
|
|
79
|
+
|
|
80
|
+
const isMac = /mac os x/.test(navigator.userAgent.toLowerCase());
|
|
81
|
+
|
|
82
|
+
const ctrl = isMac ? 'meta' : 'ctrl';
|
|
83
|
+
|
|
84
|
+
keycon
|
|
85
|
+
.keydown((e) => {
|
|
86
|
+
console.log(e);
|
|
87
|
+
e.inputEvent.preventDefault();
|
|
88
|
+
})
|
|
89
|
+
.keyup('delete', (e) => {
|
|
90
|
+
e.inputEvent.preventDefault();
|
|
91
|
+
if (!node.value || isPage(node.value)) return;
|
|
92
|
+
services?.editorService.remove(node.value);
|
|
93
|
+
})
|
|
94
|
+
.keyup('backspace', (e) => {
|
|
95
|
+
e.inputEvent.preventDefault();
|
|
96
|
+
if (!node.value || isPage(node.value)) return;
|
|
97
|
+
services?.editorService.remove(node.value);
|
|
98
|
+
})
|
|
99
|
+
.keydown([ctrl, 'c'], () => {
|
|
100
|
+
node.value && services?.editorService.copy(node.value);
|
|
101
|
+
})
|
|
102
|
+
.keydown([ctrl, 'v'], () => {
|
|
103
|
+
node.value && services?.editorService.paste();
|
|
104
|
+
})
|
|
105
|
+
.keydown([ctrl, 'x'], () => {
|
|
106
|
+
if (!node.value || isPage(node.value)) return;
|
|
107
|
+
services?.editorService.copy(node.value);
|
|
108
|
+
services?.editorService.remove(node.value);
|
|
109
|
+
})
|
|
110
|
+
.keydown([ctrl, 'z'], () => {
|
|
111
|
+
services?.editorService.undo();
|
|
112
|
+
})
|
|
113
|
+
.keydown([ctrl, 'shift', 'z'], () => {
|
|
114
|
+
services?.editorService.redo();
|
|
115
|
+
})
|
|
116
|
+
.keydown('up', () => {
|
|
117
|
+
services?.editorService.move(0, -1);
|
|
118
|
+
})
|
|
119
|
+
.keydown('down', () => {
|
|
120
|
+
services?.editorService.move(0, 1);
|
|
121
|
+
})
|
|
122
|
+
.keydown('left', () => {
|
|
123
|
+
services?.editorService.move(-1, 0);
|
|
124
|
+
})
|
|
125
|
+
.keydown('right', () => {
|
|
126
|
+
services?.editorService.move(1, 0);
|
|
127
|
+
})
|
|
128
|
+
.keydown([ctrl, 'up'], () => {
|
|
129
|
+
services?.editorService.move(0, -10);
|
|
130
|
+
})
|
|
131
|
+
.keydown([ctrl, 'down'], () => {
|
|
132
|
+
services?.editorService.move(0, 10);
|
|
133
|
+
})
|
|
134
|
+
.keydown([ctrl, 'left'], () => {
|
|
135
|
+
services?.editorService.move(-10, 0);
|
|
136
|
+
})
|
|
137
|
+
.keydown([ctrl, 'right'], () => {
|
|
138
|
+
services?.editorService.move(10, 0);
|
|
139
|
+
})
|
|
140
|
+
.keydown('tab', () => {
|
|
141
|
+
services?.editorService.selectNextNode();
|
|
142
|
+
})
|
|
143
|
+
.keydown([ctrl, 'tab'], () => {
|
|
144
|
+
services?.editorService.selectNextPage();
|
|
145
|
+
})
|
|
146
|
+
.keydown([ctrl, '='], () => {
|
|
147
|
+
services?.uiService.zoom(0.1);
|
|
148
|
+
})
|
|
149
|
+
.keydown([ctrl, 'numpadplus'], () => {
|
|
150
|
+
services?.uiService.zoom(0.1);
|
|
151
|
+
})
|
|
152
|
+
.keydown([ctrl, '-'], () => {
|
|
153
|
+
services?.uiService.zoom(-0.1);
|
|
154
|
+
})
|
|
155
|
+
.keydown([ctrl, 'numpad-'], () => {
|
|
156
|
+
services?.uiService.zoom(-0.1);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
onUnmounted(() => {
|
|
161
|
+
workspace.value?.removeEventListener('mouseenter', mouseenterHandler);
|
|
162
|
+
workspace.value?.removeEventListener('mouseleave', mouseleaveHandler);
|
|
163
|
+
keycon.destroy();
|
|
164
|
+
});
|
|
73
165
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
},
|
|
166
|
+
return {
|
|
167
|
+
workspace,
|
|
77
168
|
|
|
78
|
-
|
|
79
|
-
services?.editorService.highlight(el.id);
|
|
80
|
-
},
|
|
169
|
+
page: computed(() => services?.editorService.get<MPage>('page')),
|
|
81
170
|
};
|
|
82
171
|
},
|
|
83
172
|
});
|
|
@@ -24,6 +24,46 @@ const methodName = (prefix: string, name: string) => `${prefix}${name[0].toUpper
|
|
|
24
24
|
|
|
25
25
|
const isError = (error: any): boolean => Object.prototype.toString.call(error) === '[object Error]';
|
|
26
26
|
|
|
27
|
+
const doAction = async (
|
|
28
|
+
args: any[],
|
|
29
|
+
scope: any,
|
|
30
|
+
sourceMethod: any,
|
|
31
|
+
beforeMethodName: string,
|
|
32
|
+
afterMethodName: string,
|
|
33
|
+
fn: (args: any[], next?: Function | undefined) => Promise<void>,
|
|
34
|
+
) => {
|
|
35
|
+
try {
|
|
36
|
+
let beforeArgs = args;
|
|
37
|
+
|
|
38
|
+
for (const beforeMethod of scope.pluginOptionsList[beforeMethodName]) {
|
|
39
|
+
let beforeReturnValue = (await beforeMethod(...beforeArgs)) || [];
|
|
40
|
+
|
|
41
|
+
if (isError(beforeReturnValue)) throw beforeReturnValue;
|
|
42
|
+
|
|
43
|
+
if (!Array.isArray(beforeReturnValue)) {
|
|
44
|
+
beforeReturnValue = [beforeReturnValue];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
beforeArgs = beforeArgs.map((v: any, index: number) => {
|
|
48
|
+
if (typeof beforeReturnValue[index] === 'undefined') return v;
|
|
49
|
+
return beforeReturnValue[index];
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let returnValue: any = await fn(beforeArgs, sourceMethod.bind(scope));
|
|
54
|
+
|
|
55
|
+
for (const afterMethod of scope.pluginOptionsList[afterMethodName]) {
|
|
56
|
+
returnValue = await afterMethod(returnValue, ...beforeArgs);
|
|
57
|
+
|
|
58
|
+
if (isError(returnValue)) throw returnValue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return returnValue;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
27
67
|
/**
|
|
28
68
|
* 提供两种方式对Class进行扩展
|
|
29
69
|
* 方法1:
|
|
@@ -74,8 +114,10 @@ const isError = (error: any): boolean => Object.prototype.toString.call(error) =
|
|
|
74
114
|
export default class extends EventEmitter {
|
|
75
115
|
private pluginOptionsList: Record<string, Function[]> = {};
|
|
76
116
|
private middleware: Record<string, Function[]> = {};
|
|
117
|
+
private taskList: (() => Promise<void>)[] = [];
|
|
118
|
+
private doingTask = false;
|
|
77
119
|
|
|
78
|
-
constructor(methods: string[]) {
|
|
120
|
+
constructor(methods: string[] = [], serialMethods: string[] = []) {
|
|
79
121
|
super();
|
|
80
122
|
|
|
81
123
|
methods.forEach((propertyName: string) => {
|
|
@@ -93,32 +135,28 @@ export default class extends EventEmitter {
|
|
|
93
135
|
const fn = compose(this.middleware[propertyName]);
|
|
94
136
|
Object.defineProperty(scope, propertyName, {
|
|
95
137
|
value: async (...args: any[]) => {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
for (const beforeMethod of this.pluginOptionsList[beforeMethodName]) {
|
|
99
|
-
let beforeReturnValue = (await beforeMethod(...beforeArgs)) || [];
|
|
100
|
-
|
|
101
|
-
if (isError(beforeReturnValue)) throw beforeReturnValue;
|
|
102
|
-
|
|
103
|
-
if (!Array.isArray(beforeReturnValue)) {
|
|
104
|
-
beforeReturnValue = [beforeReturnValue];
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
beforeArgs = beforeArgs.map((v: any, index: number) => {
|
|
108
|
-
if (typeof beforeReturnValue[index] === 'undefined') return v;
|
|
109
|
-
return beforeReturnValue[index];
|
|
110
|
-
});
|
|
138
|
+
if (!serialMethods.includes(propertyName)) {
|
|
139
|
+
return doAction(args, scope, sourceMethod, beforeMethodName, afterMethodName, fn);
|
|
111
140
|
}
|
|
112
141
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
142
|
+
// 由于async await,所以会出现函数执行到await时让出线程,导致执行顺序出错,例如调用了select(1) -> update -> select(2),这个时候就有可能出现update了2;
|
|
143
|
+
// 这里保证函数调用严格按顺序执行;
|
|
144
|
+
const promise = new Promise<any>((resolve, reject) => {
|
|
145
|
+
this.taskList.push(async () => {
|
|
146
|
+
try {
|
|
147
|
+
const value = await doAction(args, scope, sourceMethod, beforeMethodName, afterMethodName, fn);
|
|
148
|
+
resolve(value);
|
|
149
|
+
} catch (e) {
|
|
150
|
+
reject(e);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
});
|
|
117
154
|
|
|
118
|
-
|
|
155
|
+
if (!this.doingTask) {
|
|
156
|
+
this.doTask();
|
|
119
157
|
}
|
|
120
158
|
|
|
121
|
-
return
|
|
159
|
+
return promise;
|
|
122
160
|
},
|
|
123
161
|
});
|
|
124
162
|
});
|
|
@@ -135,4 +173,14 @@ export default class extends EventEmitter {
|
|
|
135
173
|
if (typeof method === 'function') this.pluginOptionsList[methodName].push(method);
|
|
136
174
|
});
|
|
137
175
|
}
|
|
176
|
+
|
|
177
|
+
private async doTask() {
|
|
178
|
+
this.doingTask = true;
|
|
179
|
+
let task = this.taskList.shift();
|
|
180
|
+
while (task) {
|
|
181
|
+
await task();
|
|
182
|
+
task = this.taskList.shift();
|
|
183
|
+
}
|
|
184
|
+
this.doingTask = false;
|
|
185
|
+
}
|
|
138
186
|
}
|
package/src/services/editor.ts
CHANGED
|
@@ -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
|
|
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,24 +54,30 @@ 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() {
|
|
60
|
-
super(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
61
|
+
super(
|
|
62
|
+
[
|
|
63
|
+
'getLayout',
|
|
64
|
+
'select',
|
|
65
|
+
'add',
|
|
66
|
+
'remove',
|
|
67
|
+
'update',
|
|
68
|
+
'sort',
|
|
69
|
+
'copy',
|
|
70
|
+
'paste',
|
|
71
|
+
'alignCenter',
|
|
72
|
+
'moveLayer',
|
|
73
|
+
'move',
|
|
74
|
+
'undo',
|
|
75
|
+
'redo',
|
|
76
|
+
'highlight',
|
|
77
|
+
],
|
|
78
|
+
// 需要注意循环依赖问题,如果函数间有相互调用的话,不能设置为串行调用
|
|
79
|
+
['select', 'update', 'moveLayer'],
|
|
80
|
+
);
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
/**
|
|
@@ -85,6 +91,7 @@ class Editor extends BaseService {
|
|
|
85
91
|
log('store set ', name, ' ', value);
|
|
86
92
|
|
|
87
93
|
if (name === 'root') {
|
|
94
|
+
this.state.pageLength = (value as unknown as MApp)?.items?.length || 0;
|
|
88
95
|
this.emit('root-change', value);
|
|
89
96
|
}
|
|
90
97
|
}
|
|
@@ -104,7 +111,7 @@ class Editor extends BaseService {
|
|
|
104
111
|
* @returns {EditorNodeInfo}
|
|
105
112
|
*/
|
|
106
113
|
public getNodeInfo(id: Id): EditorNodeInfo {
|
|
107
|
-
const root = this.get<MApp | null>('root');
|
|
114
|
+
const root = toRaw(this.get<MApp | null>('root'));
|
|
108
115
|
if (!root) return {};
|
|
109
116
|
|
|
110
117
|
if (id === root.id) {
|
|
@@ -155,13 +162,15 @@ class Editor extends BaseService {
|
|
|
155
162
|
/**
|
|
156
163
|
* 只有容器拥有布局
|
|
157
164
|
*/
|
|
158
|
-
public async getLayout(
|
|
159
|
-
if (node
|
|
160
|
-
|
|
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;
|
|
161
170
|
}
|
|
162
171
|
|
|
163
172
|
// 如果该节点没有设置position,则认为是流式布局,例如获取root的布局时
|
|
164
|
-
if (!
|
|
173
|
+
if (!parent.style?.position) {
|
|
165
174
|
return Layout.RELATIVE;
|
|
166
175
|
}
|
|
167
176
|
|
|
@@ -184,9 +193,45 @@ class Editor extends BaseService {
|
|
|
184
193
|
} else {
|
|
185
194
|
historyService.empty();
|
|
186
195
|
}
|
|
196
|
+
|
|
197
|
+
this.emit('select', node);
|
|
198
|
+
|
|
187
199
|
return node!;
|
|
188
200
|
}
|
|
189
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
|
+
|
|
190
235
|
/**
|
|
191
236
|
* 高亮指定节点
|
|
192
237
|
* @param config 指定节点配置或者ID
|
|
@@ -194,8 +239,8 @@ class Editor extends BaseService {
|
|
|
194
239
|
*/
|
|
195
240
|
public highlight(config: MNode | Id): void {
|
|
196
241
|
const { node } = this.selectedConfigExceptionHandler(config);
|
|
197
|
-
const
|
|
198
|
-
if (
|
|
242
|
+
const currentHighlightNode = this.get('highlightNode');
|
|
243
|
+
if (currentHighlightNode === node) return;
|
|
199
244
|
this.set('highlightNode', node);
|
|
200
245
|
}
|
|
201
246
|
|
|
@@ -205,12 +250,14 @@ class Editor extends BaseService {
|
|
|
205
250
|
* @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
|
|
206
251
|
* @returns 添加后的节点
|
|
207
252
|
*/
|
|
208
|
-
public async add(
|
|
253
|
+
public async add(addNode: AddMNode, parent?: MContainer | null): Promise<MNode> {
|
|
254
|
+
const { type, ...config } = addNode;
|
|
209
255
|
const curNode = this.get<MContainer>('node');
|
|
210
256
|
|
|
211
257
|
let parentNode: MNode | undefined;
|
|
258
|
+
const isPage = type === NodeType.PAGE;
|
|
212
259
|
|
|
213
|
-
if (
|
|
260
|
+
if (isPage) {
|
|
214
261
|
parentNode = this.get<MApp>('root');
|
|
215
262
|
// 由于支持中间件扩展,在parent参数为undefined时,parent会变成next函数
|
|
216
263
|
} else if (parent && typeof parent !== 'function') {
|
|
@@ -223,8 +270,13 @@ class Editor extends BaseService {
|
|
|
223
270
|
|
|
224
271
|
if (!parentNode) throw new Error('未找到父元素');
|
|
225
272
|
|
|
226
|
-
const layout = await this.getLayout(parentNode);
|
|
227
|
-
const newNode = initPosition(
|
|
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
|
+
);
|
|
228
280
|
|
|
229
281
|
if ((parentNode?.type === NodeType.ROOT || curNode.type === NodeType.ROOT) && newNode.type !== NodeType.PAGE) {
|
|
230
282
|
throw new Error('app下不能添加组件');
|
|
@@ -232,15 +284,25 @@ class Editor extends BaseService {
|
|
|
232
284
|
|
|
233
285
|
parentNode?.items?.push(newNode);
|
|
234
286
|
|
|
235
|
-
|
|
287
|
+
const stage = this.get<StageCore | null>('stage');
|
|
288
|
+
|
|
289
|
+
await stage?.add({ config: cloneDeep(newNode), root: cloneDeep(this.get('root')) });
|
|
236
290
|
|
|
237
291
|
await this.select(newNode);
|
|
238
292
|
|
|
239
293
|
this.addModifiedNodeId(newNode.id);
|
|
240
|
-
if (
|
|
294
|
+
if (!isPage) {
|
|
241
295
|
this.pushHistoryState();
|
|
242
296
|
}
|
|
243
297
|
|
|
298
|
+
stage?.select(newNode.id);
|
|
299
|
+
|
|
300
|
+
if (isPage) {
|
|
301
|
+
this.state.pageLength += 1;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
this.emit('add', newNode);
|
|
305
|
+
|
|
244
306
|
return newNode;
|
|
245
307
|
}
|
|
246
308
|
|
|
@@ -265,17 +327,38 @@ class Editor extends BaseService {
|
|
|
265
327
|
if (typeof index !== 'number' || index === -1) throw new Error('找不要删除的节点');
|
|
266
328
|
|
|
267
329
|
parent.items?.splice(index, 1);
|
|
268
|
-
this.get<StageCore>('stage')
|
|
330
|
+
const stage = this.get<StageCore | null>('stage');
|
|
331
|
+
stage?.remove({ id: node.id, root: this.get('root') });
|
|
269
332
|
|
|
270
333
|
if (node.type === NodeType.PAGE) {
|
|
271
|
-
|
|
334
|
+
this.state.pageLength -= 1;
|
|
335
|
+
|
|
336
|
+
if (root.items[0]) {
|
|
337
|
+
await this.select(root.items[0]);
|
|
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;
|
|
351
|
+
}
|
|
272
352
|
} else {
|
|
273
353
|
await this.select(parent);
|
|
354
|
+
stage?.select(parent.id);
|
|
274
355
|
}
|
|
275
356
|
|
|
276
357
|
this.addModifiedNodeId(parent.id);
|
|
277
358
|
this.pushHistoryState();
|
|
278
359
|
|
|
360
|
+
this.emit('remove', node);
|
|
361
|
+
|
|
279
362
|
return node;
|
|
280
363
|
}
|
|
281
364
|
|
|
@@ -324,11 +407,11 @@ class Editor extends BaseService {
|
|
|
324
407
|
|
|
325
408
|
parentNodeItems[index] = newConfig;
|
|
326
409
|
|
|
327
|
-
if (newConfig.id === this.get('node').id) {
|
|
410
|
+
if (`${newConfig.id}` === `${this.get('node').id}`) {
|
|
328
411
|
this.set('node', newConfig);
|
|
329
412
|
}
|
|
330
413
|
|
|
331
|
-
this.get<StageCore>('stage')?.update({ config: cloneDeep(newConfig), root: this.get('root') });
|
|
414
|
+
this.get<StageCore | null>('stage')?.update({ config: cloneDeep(newConfig), root: this.get('root') });
|
|
332
415
|
|
|
333
416
|
if (newConfig.type === NodeType.PAGE) {
|
|
334
417
|
this.set('page', newConfig);
|
|
@@ -337,6 +420,8 @@ class Editor extends BaseService {
|
|
|
337
420
|
this.addModifiedNodeId(newConfig.id);
|
|
338
421
|
this.pushHistoryState();
|
|
339
422
|
|
|
423
|
+
this.emit('update', newConfig);
|
|
424
|
+
|
|
340
425
|
return newConfig;
|
|
341
426
|
}
|
|
342
427
|
|
|
@@ -359,7 +444,7 @@ class Editor extends BaseService {
|
|
|
359
444
|
await this.update(parent);
|
|
360
445
|
await this.select(node);
|
|
361
446
|
|
|
362
|
-
this.get<StageCore>('stage')?.update({ config: cloneDeep(node), root: this.get('root') });
|
|
447
|
+
this.get<StageCore | null>('stage')?.update({ config: cloneDeep(node), root: this.get('root') });
|
|
363
448
|
|
|
364
449
|
this.addModifiedNodeId(parent.id);
|
|
365
450
|
this.pushHistoryState();
|
|
@@ -414,7 +499,7 @@ class Editor extends BaseService {
|
|
|
414
499
|
public async alignCenter(config: MNode): Promise<MNode | void> {
|
|
415
500
|
const parent = this.get<MContainer>('parent');
|
|
416
501
|
const node = this.get<MNode>('node');
|
|
417
|
-
const layout = await this.getLayout(parent);
|
|
502
|
+
const layout = await this.getLayout(toRaw(parent), toRaw(node));
|
|
418
503
|
if (layout === Layout.RELATIVE) {
|
|
419
504
|
return;
|
|
420
505
|
}
|
|
@@ -424,7 +509,7 @@ class Editor extends BaseService {
|
|
|
424
509
|
}
|
|
425
510
|
|
|
426
511
|
await this.update(node);
|
|
427
|
-
this.get<StageCore>('stage')?.update({ config: cloneDeep(toRaw(node)), root: this.get('root') });
|
|
512
|
+
this.get<StageCore | null>('stage')?.update({ config: cloneDeep(toRaw(node)), root: this.get('root') });
|
|
428
513
|
this.addModifiedNodeId(config.id);
|
|
429
514
|
this.pushHistoryState();
|
|
430
515
|
|
|
@@ -449,7 +534,7 @@ class Editor extends BaseService {
|
|
|
449
534
|
brothers.splice(index + parseInt(`${offset}`, 10), 0, brothers.splice(index, 1)[0]);
|
|
450
535
|
}
|
|
451
536
|
|
|
452
|
-
this.get<StageCore>('stage')?.update({ config: cloneDeep(toRaw(parent)), root: this.get('root') });
|
|
537
|
+
this.get<StageCore | null>('stage')?.update({ config: cloneDeep(toRaw(parent)), root: this.get('root') });
|
|
453
538
|
}
|
|
454
539
|
|
|
455
540
|
/**
|
|
@@ -472,6 +557,26 @@ class Editor extends BaseService {
|
|
|
472
557
|
return value;
|
|
473
558
|
}
|
|
474
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
|
+
|
|
475
580
|
public destroy() {
|
|
476
581
|
this.removeAllListeners();
|
|
477
582
|
this.set('root', null);
|
|
@@ -508,7 +613,11 @@ class Editor extends BaseService {
|
|
|
508
613
|
this.isHistoryStateChange = true;
|
|
509
614
|
await this.update(value.data);
|
|
510
615
|
this.set('modifiedNodeIds', value.modifiedNodeIds);
|
|
511
|
-
setTimeout(() =>
|
|
616
|
+
setTimeout(async () => {
|
|
617
|
+
if (!value.nodeId) return;
|
|
618
|
+
await this.select(value.nodeId);
|
|
619
|
+
this.get<StageCore | null>('stage')?.select(value.nodeId);
|
|
620
|
+
}, 0);
|
|
512
621
|
}
|
|
513
622
|
|
|
514
623
|
private async toggleFixedPosition(dist: MNode, src: MNode, root: MApp) {
|
package/src/services/history.ts
CHANGED
|
@@ -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
|
|
package/src/services/props.ts
CHANGED
|
@@ -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
|
}
|