@tmagic/editor 1.0.0-beta.8 → 1.0.0-beta.9
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/dist/style.css +24 -10
- package/dist/tmagic-editor.es.js +158 -76
- package/dist/tmagic-editor.es.js.map +1 -1
- package/dist/tmagic-editor.umd.js +160 -79
- package/dist/tmagic-editor.umd.js.map +1 -1
- package/dist/types/src/layouts/sidebar/LayerPanel.vue.d.ts +8 -2
- package/dist/types/src/layouts/workspace/Stage.vue.d.ts +2 -1
- package/dist/types/src/layouts/workspace/Workspace.vue.d.ts +1 -0
- package/dist/types/src/services/editor.d.ts +11 -4
- package/dist/types/src/type.d.ts +1 -0
- package/dist/types/src/utils/editor.d.ts +1 -1
- package/package.json +5 -5
- package/src/components/ToolButton.vue +3 -1
- package/src/layouts/AddPageBox.vue +3 -1
- package/src/layouts/sidebar/LayerPanel.vue +55 -11
- package/src/layouts/workspace/PageBar.vue +9 -4
- package/src/layouts/workspace/Stage.vue +18 -2
- package/src/layouts/workspace/ViewerMenu.vue +5 -3
- package/src/layouts/workspace/Workspace.vue +5 -0
- package/src/services/editor.ts +53 -29
- package/src/theme/common/var.scss +2 -2
- package/src/theme/layer-panel.scss +9 -0
- package/src/theme/page-bar.scss +8 -0
- package/src/type.ts +1 -0
- package/src/utils/editor.ts +4 -3
|
@@ -2,10 +2,14 @@ import type { Translator } from 'element-plus';
|
|
|
2
2
|
import type { Nullable } from 'element-plus/es/utils';
|
|
3
3
|
import type { default } from 'element-plus/es/components/tree/src/model/node';
|
|
4
4
|
import type { TreeOptionProps, TreeKey, LoadFunction, FilterNodeMethodFunction, TreeNodeData, FakeNode, TreeNodeLoadedDefaultProps, TreeData } from 'element-plus/es/components/tree/src/tree.type';
|
|
5
|
+
import type { DebouncedFunc } from 'lodash';
|
|
5
6
|
import type { DefineComponent, ComputedRef, ComponentInternalInstance, ExtractPropTypes, PropType, Component, ComputedOptions, MethodOptions, VNodeProps, AllowedComponentProps, ComponentCustomProps, Slot, ComponentPublicInstance, ComponentOptionsBase, ComponentOptionsMixin, DebuggerEvent, nextTick, WatchOptions, WatchStopHandle, ShallowUnwrapRef, ComponentCustomProperties, EmitsOptions, Ref } from 'vue';
|
|
6
7
|
import type { MNode } from '@tmagic/schema';
|
|
7
8
|
declare const _default: DefineComponent<{}, {
|
|
8
9
|
clickHandler(data: MNode): void;
|
|
10
|
+
highlightHandler: DebouncedFunc<(data: MNode) => void>;
|
|
11
|
+
toogleClickFlag: () => void;
|
|
12
|
+
canHighlight: ComputedRef<boolean>;
|
|
9
13
|
menuStyle: Ref<{
|
|
10
14
|
position: string;
|
|
11
15
|
left: string;
|
|
@@ -16,10 +20,12 @@ declare const _default: DefineComponent<{}, {
|
|
|
16
20
|
filterText: Ref<string>;
|
|
17
21
|
filterNode: (value: string, data: MNode) => boolean;
|
|
18
22
|
filterTextChangeHandler(val: string): void;
|
|
19
|
-
values: ComputedRef<MNode[]>;
|
|
20
|
-
loadItems: (node: any, resolve: Function) => any;
|
|
21
23
|
allowDrop: (draggingNode: any, dropNode: any, type: string) => boolean;
|
|
22
24
|
handleDragEnd(): void;
|
|
25
|
+
values: ComputedRef<MNode[]>;
|
|
26
|
+
loadItems: (node: any, resolve: Function) => any;
|
|
27
|
+
highlightNode: Ref<MNode | undefined>;
|
|
28
|
+
clickNode: Ref<MNode | undefined>;
|
|
23
29
|
tree: Ref<({
|
|
24
30
|
$: ComponentInternalInstance;
|
|
25
31
|
$data: {};
|
|
@@ -166,7 +166,7 @@ declare const _default: DefineComponent<{
|
|
|
166
166
|
stageContainer: Ref<HTMLDivElement | undefined>;
|
|
167
167
|
stageRect: ComputedRef<StageRect | undefined>;
|
|
168
168
|
zoom: ComputedRef<number | undefined>;
|
|
169
|
-
}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, ("select" | "update" | "sort")[], "sort" | "select" | "update", VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly<ExtractPropTypes<{
|
|
169
|
+
}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, ("select" | "update" | "sort" | "highlight")[], "sort" | "select" | "update" | "highlight", VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly<ExtractPropTypes<{
|
|
170
170
|
render: {
|
|
171
171
|
type: PropType<() => HTMLDivElement>;
|
|
172
172
|
};
|
|
@@ -185,6 +185,7 @@ declare const _default: DefineComponent<{
|
|
|
185
185
|
onSelect?: ((...args: any[]) => any) | undefined;
|
|
186
186
|
onUpdate?: ((...args: any[]) => any) | undefined;
|
|
187
187
|
onSort?: ((...args: any[]) => any) | undefined;
|
|
188
|
+
onHighlight?: ((...args: any[]) => any) | undefined;
|
|
188
189
|
}, {
|
|
189
190
|
canSelect: (el: HTMLElement) => boolean | Promise<boolean>;
|
|
190
191
|
moveableOptions: MoveableOptions | ((core?: StageCore | undefined) => MoveableOptions);
|
|
@@ -18,6 +18,7 @@ declare const _default: DefineComponent<{
|
|
|
18
18
|
selectHandler(el: HTMLElement): void;
|
|
19
19
|
updateNodeHandler(node: MComponent | MContainer | MPage): void;
|
|
20
20
|
sortNodeHandler(ev: SortEventData): void;
|
|
21
|
+
highlightHandler(el: HTMLElement): void;
|
|
21
22
|
}, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record<string, any>, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly<ExtractPropTypes<{
|
|
22
23
|
runtimeUrl: StringConstructor;
|
|
23
24
|
render: {
|
|
@@ -9,7 +9,7 @@ declare class Editor extends BaseService {
|
|
|
9
9
|
constructor();
|
|
10
10
|
/**
|
|
11
11
|
* 设置当前指点节点配置
|
|
12
|
-
* @param name 'root' | 'page' | 'parent' | 'node'
|
|
12
|
+
* @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode'
|
|
13
13
|
* @param value MNode
|
|
14
14
|
* @returns MNode
|
|
15
15
|
*/
|
|
@@ -43,11 +43,17 @@ declare class Editor extends BaseService {
|
|
|
43
43
|
*/
|
|
44
44
|
getLayout(node: MNode): Promise<Layout>;
|
|
45
45
|
/**
|
|
46
|
-
*
|
|
47
|
-
* @param config
|
|
46
|
+
* 选中指定节点(将指定节点设置成当前选中状态)
|
|
47
|
+
* @param config 指定节点配置或者ID
|
|
48
48
|
* @returns 当前选中的节点配置
|
|
49
49
|
*/
|
|
50
|
-
select(config: MNode | Id): Promise<MNode
|
|
50
|
+
select(config: MNode | Id): Promise<MNode> | never;
|
|
51
|
+
/**
|
|
52
|
+
* 高亮指定节点
|
|
53
|
+
* @param config 指定节点配置或者ID
|
|
54
|
+
* @returns 当前高亮的节点配置
|
|
55
|
+
*/
|
|
56
|
+
highlight(config: MNode | Id): void;
|
|
51
57
|
/**
|
|
52
58
|
* 向指点容器添加组件节点
|
|
53
59
|
* @param addConfig 将要添加的组件节点配置
|
|
@@ -116,6 +122,7 @@ declare class Editor extends BaseService {
|
|
|
116
122
|
private pushHistoryState;
|
|
117
123
|
private changeHistoryState;
|
|
118
124
|
private toggleFixedPosition;
|
|
125
|
+
private selectedConfigExceptionHandler;
|
|
119
126
|
}
|
|
120
127
|
export declare type EditorService = Editor;
|
|
121
128
|
declare const _default: Editor;
|
package/dist/types/src/type.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MApp, MContainer, MNode, MPage } from '@tmagic/schema';
|
|
1
|
+
import type { MApp, MContainer, MNode, MPage } from '@tmagic/schema';
|
|
2
2
|
import { Layout } from '../type';
|
|
3
3
|
export declare const COPY_STORAGE_KEY = "$MagicEditorCopyData";
|
|
4
4
|
export declare const generateId: (type: string | number) => string;
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.0.0-beta.
|
|
2
|
+
"version": "1.0.0-beta.9",
|
|
3
3
|
"name": "@tmagic/editor",
|
|
4
4
|
"sideEffects": false,
|
|
5
5
|
"main": "dist/tmagic-editor.umd.js",
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@element-plus/icons": "0.0.11",
|
|
31
|
-
"@tmagic/core": "^1.0.0-beta.
|
|
31
|
+
"@tmagic/core": "^1.0.0-beta.9",
|
|
32
32
|
"@tmagic/form": "^1.0.0-beta.8",
|
|
33
|
-
"@tmagic/schema": "^1.0.0-beta.
|
|
34
|
-
"@tmagic/stage": "^1.0.0-beta.
|
|
33
|
+
"@tmagic/schema": "^1.0.0-beta.9",
|
|
34
|
+
"@tmagic/stage": "^1.0.0-beta.9",
|
|
35
35
|
"@tmagic/utils": "^1.0.0-beta.8",
|
|
36
36
|
"color": "^3.1.3",
|
|
37
37
|
"element-plus": "^2.1.7",
|
|
@@ -56,5 +56,5 @@
|
|
|
56
56
|
"vite-plugin-dts": "^0.9.6",
|
|
57
57
|
"vue-tsc": "^0.0.24"
|
|
58
58
|
},
|
|
59
|
-
"gitHead": "
|
|
59
|
+
"gitHead": "6314078c100ddcf513eb5581b38e2292f9b93eb9"
|
|
60
60
|
}
|
|
@@ -41,6 +41,8 @@
|
|
|
41
41
|
import { computed, defineComponent, inject, PropType } from 'vue';
|
|
42
42
|
import { ArrowDown, Back, Delete, Grid, Right, ScaleToOriginal, ZoomIn, ZoomOut } from '@element-plus/icons';
|
|
43
43
|
|
|
44
|
+
import { NodeType } from '@tmagic/schema';
|
|
45
|
+
|
|
44
46
|
import MIcon from '@editor/components/Icon.vue';
|
|
45
47
|
import type { MenuButton, MenuComponent, MenuItem, Services } from '@editor/type';
|
|
46
48
|
|
|
@@ -87,7 +89,7 @@ export default defineComponent({
|
|
|
87
89
|
type: 'button',
|
|
88
90
|
icon: Delete,
|
|
89
91
|
tooltip: '刪除',
|
|
90
|
-
disabled: () => services?.editorService.get('node')?.type ===
|
|
92
|
+
disabled: () => services?.editorService.get('node')?.type === NodeType.PAGE,
|
|
91
93
|
handler: () => services?.editorService.remove(services?.editorService.get('node')),
|
|
92
94
|
};
|
|
93
95
|
case 'undo':
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
import { defineComponent, inject, toRaw } from 'vue';
|
|
16
16
|
import { Plus } from '@element-plus/icons';
|
|
17
17
|
|
|
18
|
+
import { NodeType } from '@tmagic/schema';
|
|
19
|
+
|
|
18
20
|
import { Services } from '@editor/type';
|
|
19
21
|
import { generatePageNameByApp } from '@editor/utils';
|
|
20
22
|
|
|
@@ -31,7 +33,7 @@ export default defineComponent({
|
|
|
31
33
|
if (!editorService) return;
|
|
32
34
|
|
|
33
35
|
editorService.add({
|
|
34
|
-
type:
|
|
36
|
+
type: NodeType.PAGE,
|
|
35
37
|
name: generatePageNameByApp(toRaw(editorService.get('root'))),
|
|
36
38
|
});
|
|
37
39
|
},
|
|
@@ -29,11 +29,20 @@
|
|
|
29
29
|
empty-text="页面空荡荡的"
|
|
30
30
|
>
|
|
31
31
|
<template #default="{ node, data }">
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
<div
|
|
33
|
+
:id="data.id"
|
|
34
|
+
class="cus-tree-node"
|
|
35
|
+
@mousedown="toogleClickFlag"
|
|
36
|
+
@mouseup="toogleClickFlag"
|
|
37
|
+
@mouseenter="highlightHandler(data)"
|
|
38
|
+
:class="{ 'cus-tree-node-hover': canHighlight && data.id === highlightNode?.id }"
|
|
39
|
+
>
|
|
40
|
+
<slot name="layer-node-content" :node="node" :data="data">
|
|
41
|
+
<span>
|
|
42
|
+
{{ `${data.name} (${data.id})` }}
|
|
43
|
+
</span>
|
|
44
|
+
</slot>
|
|
45
|
+
</div>
|
|
37
46
|
</template>
|
|
38
47
|
</el-tree>
|
|
39
48
|
|
|
@@ -46,14 +55,18 @@
|
|
|
46
55
|
<script lang="ts">
|
|
47
56
|
import { computed, defineComponent, inject, onMounted, Ref, ref, watchEffect } from 'vue';
|
|
48
57
|
import type { ElTree } from 'element-plus';
|
|
58
|
+
import { throttle } from 'lodash-es';
|
|
49
59
|
|
|
50
60
|
import type { MNode, MPage } from '@tmagic/schema';
|
|
61
|
+
import { NodeType } from '@tmagic/schema';
|
|
51
62
|
|
|
52
63
|
import type { EditorService } from '@editor/services/editor';
|
|
53
64
|
import type { Services } from '@editor/type';
|
|
54
65
|
|
|
55
66
|
import LayerMenu from './LayerMenu.vue';
|
|
56
67
|
|
|
68
|
+
const throttleTime = 150;
|
|
69
|
+
|
|
57
70
|
const select = (data: MNode, editorService?: EditorService) => {
|
|
58
71
|
if (!data.id) {
|
|
59
72
|
throw new Error('没有id');
|
|
@@ -62,6 +75,13 @@ const select = (data: MNode, editorService?: EditorService) => {
|
|
|
62
75
|
editorService?.select(data);
|
|
63
76
|
};
|
|
64
77
|
|
|
78
|
+
const highlight = (data: MNode, editorService?: EditorService) => {
|
|
79
|
+
if (!data?.id) {
|
|
80
|
+
throw new Error('没有id');
|
|
81
|
+
}
|
|
82
|
+
editorService?.highlight(data);
|
|
83
|
+
};
|
|
84
|
+
|
|
65
85
|
const useDrop = (tree: Ref<InstanceType<typeof ElTree> | undefined>, editorService?: EditorService) => ({
|
|
66
86
|
allowDrop: (draggingNode: any, dropNode: any, type: string): boolean => {
|
|
67
87
|
const { data } = dropNode || {};
|
|
@@ -69,8 +89,8 @@ const useDrop = (tree: Ref<InstanceType<typeof ElTree> | undefined>, editorServi
|
|
|
69
89
|
|
|
70
90
|
const { type: ingType } = ingData;
|
|
71
91
|
|
|
72
|
-
if (ingType !==
|
|
73
|
-
if (ingType ===
|
|
92
|
+
if (ingType !== NodeType.PAGE && data.type === NodeType.PAGE) return false;
|
|
93
|
+
if (ingType === NodeType.PAGE && data.type !== NodeType.PAGE) return false;
|
|
74
94
|
if (!data || !data.type) return false;
|
|
75
95
|
if (['prev', 'next'].includes(type)) return true;
|
|
76
96
|
if (data.items || data.type === 'container') return true;
|
|
@@ -87,19 +107,22 @@ const useDrop = (tree: Ref<InstanceType<typeof ElTree> | undefined>, editorServi
|
|
|
87
107
|
});
|
|
88
108
|
|
|
89
109
|
const useStatus = (tree: Ref<InstanceType<typeof ElTree> | undefined>, editorService?: EditorService) => {
|
|
110
|
+
const highlightNode = ref<MNode>();
|
|
111
|
+
const node = ref<MNode>();
|
|
90
112
|
const page = computed(() => editorService?.get('page'));
|
|
91
113
|
|
|
92
114
|
watchEffect(() => {
|
|
93
115
|
if (!tree.value) return;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
node && tree.value.setCurrentKey(node.id, true);
|
|
116
|
+
node.value = editorService?.get('node');
|
|
117
|
+
node.value && tree.value.setCurrentKey(node.value.id, true);
|
|
97
118
|
|
|
98
119
|
const parent = editorService?.get('parent');
|
|
99
120
|
if (!parent?.id) return;
|
|
100
121
|
|
|
101
122
|
const treeNode = tree.value.getNode(parent.id);
|
|
102
123
|
treeNode?.updateChildren();
|
|
124
|
+
|
|
125
|
+
highlightNode.value = editorService?.get('highlightNode');
|
|
103
126
|
});
|
|
104
127
|
|
|
105
128
|
return {
|
|
@@ -114,6 +137,9 @@ const useStatus = (tree: Ref<InstanceType<typeof ElTree> | undefined>, editorSer
|
|
|
114
137
|
}
|
|
115
138
|
resolve([]);
|
|
116
139
|
},
|
|
140
|
+
|
|
141
|
+
highlightNode,
|
|
142
|
+
clickNode: node,
|
|
117
143
|
};
|
|
118
144
|
};
|
|
119
145
|
|
|
@@ -188,11 +214,25 @@ export default defineComponent({
|
|
|
188
214
|
setup() {
|
|
189
215
|
const services = inject<Services>('services');
|
|
190
216
|
const tree = ref<InstanceType<typeof ElTree>>();
|
|
217
|
+
const clicked = ref(false);
|
|
191
218
|
const editorService = services?.editorService;
|
|
219
|
+
const highlightHandler = throttle((data: MNode) => {
|
|
220
|
+
highlight(data, editorService);
|
|
221
|
+
}, throttleTime);
|
|
222
|
+
|
|
223
|
+
const toogleClickFlag = () => {
|
|
224
|
+
clicked.value = !clicked.value;
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const statusData = useStatus(tree, editorService);
|
|
228
|
+
const canHighlight = computed(
|
|
229
|
+
() => statusData.highlightNode.value?.id !== statusData.clickNode.value?.id && !clicked.value,
|
|
230
|
+
);
|
|
231
|
+
|
|
192
232
|
return {
|
|
193
233
|
tree,
|
|
234
|
+
...statusData,
|
|
194
235
|
...useDrop(tree, editorService),
|
|
195
|
-
...useStatus(tree, editorService),
|
|
196
236
|
...useFilter(tree),
|
|
197
237
|
...useContentMenu(editorService),
|
|
198
238
|
|
|
@@ -201,8 +241,12 @@ export default defineComponent({
|
|
|
201
241
|
document.dispatchEvent(new CustomEvent('ui-select', { detail: data }));
|
|
202
242
|
return;
|
|
203
243
|
}
|
|
244
|
+
tree.value?.setCurrentKey(data.id);
|
|
204
245
|
select(data, editorService);
|
|
205
246
|
},
|
|
247
|
+
highlightHandler,
|
|
248
|
+
toogleClickFlag,
|
|
249
|
+
canHighlight,
|
|
206
250
|
};
|
|
207
251
|
},
|
|
208
252
|
});
|
|
@@ -11,9 +11,13 @@
|
|
|
11
11
|
:class="{ active: page?.id === item.id }"
|
|
12
12
|
@click="switchPage(item)"
|
|
13
13
|
>
|
|
14
|
-
<
|
|
15
|
-
<
|
|
16
|
-
|
|
14
|
+
<div class="m-editor-page-bar-title">
|
|
15
|
+
<slot name="page-bar-title" :page="item">
|
|
16
|
+
<el-tooltip effect="dark" placement="top-start" :content="item.name">
|
|
17
|
+
<span>{{ item.name }}</span>
|
|
18
|
+
</el-tooltip>
|
|
19
|
+
</slot>
|
|
20
|
+
</div>
|
|
17
21
|
|
|
18
22
|
<el-popover placement="top" :width="160" trigger="hover">
|
|
19
23
|
<div>
|
|
@@ -38,6 +42,7 @@ import { computed, defineComponent, inject, toRaw } from 'vue';
|
|
|
38
42
|
import { CaretBottom, Plus } from '@element-plus/icons';
|
|
39
43
|
|
|
40
44
|
import type { MPage } from '@tmagic/schema';
|
|
45
|
+
import { NodeType } from '@tmagic/schema';
|
|
41
46
|
|
|
42
47
|
import type { Services } from '@editor/type';
|
|
43
48
|
import { generatePageNameByApp } from '@editor/utils/editor';
|
|
@@ -60,7 +65,7 @@ export default defineComponent({
|
|
|
60
65
|
addPage() {
|
|
61
66
|
if (!editorService) return;
|
|
62
67
|
const pageConfig = {
|
|
63
|
-
type:
|
|
68
|
+
type: NodeType.PAGE,
|
|
64
69
|
name: generatePageNameByApp(toRaw(editorService.get('root'))),
|
|
65
70
|
};
|
|
66
71
|
editorService.add(pageConfig);
|
|
@@ -110,7 +110,7 @@ export default defineComponent({
|
|
|
110
110
|
},
|
|
111
111
|
},
|
|
112
112
|
|
|
113
|
-
emits: ['select', 'update', 'sort'],
|
|
113
|
+
emits: ['select', 'update', 'sort', 'highlight'],
|
|
114
114
|
|
|
115
115
|
setup(props, { emit }) {
|
|
116
116
|
const services = inject<Services>('services');
|
|
@@ -124,6 +124,7 @@ export default defineComponent({
|
|
|
124
124
|
const page = computed(() => services?.editorService.get<MPage>('page'));
|
|
125
125
|
const zoom = computed(() => services?.uiService.get<number>('zoom'));
|
|
126
126
|
const node = computed(() => services?.editorService.get<MNode>('node'));
|
|
127
|
+
const highlightNode = computed(() => services?.editorService.get<MNode>('highlightNode'));
|
|
127
128
|
|
|
128
129
|
let stage: StageCore | null = null;
|
|
129
130
|
let runtime: Runtime | null = null;
|
|
@@ -155,7 +156,13 @@ export default defineComponent({
|
|
|
155
156
|
|
|
156
157
|
stage?.mount(stageContainer.value);
|
|
157
158
|
|
|
158
|
-
stage?.on('select', (el: HTMLElement) =>
|
|
159
|
+
stage?.on('select', (el: HTMLElement) => {
|
|
160
|
+
emit('select', el);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
stage?.on('highlight', (el: HTMLElement) => {
|
|
164
|
+
emit('highlight', el);
|
|
165
|
+
});
|
|
159
166
|
|
|
160
167
|
stage?.on('update', (ev: UpdateEventData) => {
|
|
161
168
|
emit('update', { id: ev.el.id, style: ev.style });
|
|
@@ -202,6 +209,15 @@ export default defineComponent({
|
|
|
202
209
|
},
|
|
203
210
|
);
|
|
204
211
|
|
|
212
|
+
watch(
|
|
213
|
+
() => highlightNode.value?.id,
|
|
214
|
+
(id) => {
|
|
215
|
+
nextTick(() => {
|
|
216
|
+
id && stage?.highlight(id);
|
|
217
|
+
});
|
|
218
|
+
},
|
|
219
|
+
);
|
|
220
|
+
|
|
205
221
|
const resizeObserver = new ResizeObserver((entries) => {
|
|
206
222
|
for (const { contentRect } of entries) {
|
|
207
223
|
services?.uiService.set('stageContainerRect', {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
<script lang="ts">
|
|
25
25
|
import { computed, defineComponent, inject, onMounted, ref, watch } from 'vue';
|
|
26
26
|
|
|
27
|
+
import { NodeType } from '@tmagic/schema';
|
|
27
28
|
import type StageCore from '@tmagic/stage';
|
|
28
29
|
|
|
29
30
|
import { LayerOffset, Layout, Services } from '@editor/type';
|
|
@@ -53,7 +54,8 @@ export default defineComponent({
|
|
|
53
54
|
if (!parent.value || !editorService) return (canCenter.value = false);
|
|
54
55
|
const layout = await editorService.getLayout(parent.value);
|
|
55
56
|
canCenter.value =
|
|
56
|
-
[Layout.ABSOLUTE, Layout.FIXED].includes(layout) &&
|
|
57
|
+
[Layout.ABSOLUTE, Layout.FIXED].includes(layout) &&
|
|
58
|
+
![NodeType.ROOT, NodeType.PAGE, 'pop'].includes(`${node.value?.type}`);
|
|
57
59
|
},
|
|
58
60
|
{ immediate: true },
|
|
59
61
|
);
|
|
@@ -62,8 +64,8 @@ export default defineComponent({
|
|
|
62
64
|
menu,
|
|
63
65
|
canPaste,
|
|
64
66
|
|
|
65
|
-
canDelete: computed(() => node.value?.type !==
|
|
66
|
-
canMoveZPos: computed(() => node.value?.type !==
|
|
67
|
+
canDelete: computed(() => node.value?.type !== NodeType.PAGE),
|
|
68
|
+
canMoveZPos: computed(() => node.value?.type !== NodeType.PAGE),
|
|
67
69
|
canCenter,
|
|
68
70
|
|
|
69
71
|
center() {
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
:moveable-options="moveableOptions"
|
|
8
8
|
:can-select="canSelect"
|
|
9
9
|
@select="selectHandler"
|
|
10
|
+
@highlight="highlightHandler"
|
|
10
11
|
@update="updateNodeHandler"
|
|
11
12
|
@sort="sortNodeHandler"
|
|
12
13
|
></magic-stage>
|
|
@@ -73,6 +74,10 @@ export default defineComponent({
|
|
|
73
74
|
sortNodeHandler(ev: SortEventData) {
|
|
74
75
|
services?.editorService.sort(ev.src, ev.dist);
|
|
75
76
|
},
|
|
77
|
+
|
|
78
|
+
highlightHandler(el: HTMLElement) {
|
|
79
|
+
services?.editorService.highlight(el.id);
|
|
80
|
+
},
|
|
76
81
|
};
|
|
77
82
|
},
|
|
78
83
|
});
|
package/src/services/editor.ts
CHANGED
|
@@ -21,6 +21,7 @@ import { cloneDeep, mergeWith } from 'lodash-es';
|
|
|
21
21
|
import serialize from 'serialize-javascript';
|
|
22
22
|
|
|
23
23
|
import type { Id, MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
|
|
24
|
+
import { NodeType } from '@tmagic/schema';
|
|
24
25
|
import type StageCore from '@tmagic/stage';
|
|
25
26
|
import { getNodePath, isPop } from '@tmagic/utils';
|
|
26
27
|
|
|
@@ -51,6 +52,7 @@ class Editor extends BaseService {
|
|
|
51
52
|
parent: null,
|
|
52
53
|
node: null,
|
|
53
54
|
stage: null,
|
|
55
|
+
highlightNode: null,
|
|
54
56
|
modifiedNodeIds: new Map(),
|
|
55
57
|
});
|
|
56
58
|
|
|
@@ -68,12 +70,13 @@ class Editor extends BaseService {
|
|
|
68
70
|
'moveLayer',
|
|
69
71
|
'undo',
|
|
70
72
|
'redo',
|
|
73
|
+
'highlight',
|
|
71
74
|
]);
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
/**
|
|
75
78
|
* 设置当前指点节点配置
|
|
76
|
-
* @param name 'root' | 'page' | 'parent' | 'node'
|
|
79
|
+
* @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode'
|
|
77
80
|
* @param value MNode
|
|
78
81
|
* @returns MNode
|
|
79
82
|
*/
|
|
@@ -119,7 +122,7 @@ class Editor extends BaseService {
|
|
|
119
122
|
info.parent = path[path.length - 2] as MContainer;
|
|
120
123
|
|
|
121
124
|
path.forEach((item) => {
|
|
122
|
-
if (item.type ===
|
|
125
|
+
if (item.type === NodeType.PAGE) {
|
|
123
126
|
info.page = item as MPage;
|
|
124
127
|
return;
|
|
125
128
|
}
|
|
@@ -166,28 +169,12 @@ class Editor extends BaseService {
|
|
|
166
169
|
}
|
|
167
170
|
|
|
168
171
|
/**
|
|
169
|
-
*
|
|
170
|
-
* @param config
|
|
172
|
+
* 选中指定节点(将指定节点设置成当前选中状态)
|
|
173
|
+
* @param config 指定节点配置或者ID
|
|
171
174
|
* @returns 当前选中的节点配置
|
|
172
175
|
*/
|
|
173
|
-
public async select(config: MNode | Id): Promise<MNode> {
|
|
174
|
-
|
|
175
|
-
if (typeof config === 'string' || typeof config === 'number') {
|
|
176
|
-
id = config;
|
|
177
|
-
} else {
|
|
178
|
-
id = config.id;
|
|
179
|
-
}
|
|
180
|
-
if (!id) {
|
|
181
|
-
throw new Error('没有ID,无法选中');
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const { node, parent, page } = this.getNodeInfo(id);
|
|
185
|
-
if (!node) throw new Error('获取不到组件信息');
|
|
186
|
-
|
|
187
|
-
if (node.id === this.state.root?.id) {
|
|
188
|
-
throw new Error('不能选根节点');
|
|
189
|
-
}
|
|
190
|
-
|
|
176
|
+
public async select(config: MNode | Id): Promise<MNode> | never {
|
|
177
|
+
const { node, page, parent } = this.selectedConfigExceptionHandler(config);
|
|
191
178
|
this.set('node', node);
|
|
192
179
|
this.set('page', page || null);
|
|
193
180
|
this.set('parent', parent || null);
|
|
@@ -197,8 +184,19 @@ class Editor extends BaseService {
|
|
|
197
184
|
} else {
|
|
198
185
|
historyService.empty();
|
|
199
186
|
}
|
|
187
|
+
return node!;
|
|
188
|
+
}
|
|
200
189
|
|
|
201
|
-
|
|
190
|
+
/**
|
|
191
|
+
* 高亮指定节点
|
|
192
|
+
* @param config 指定节点配置或者ID
|
|
193
|
+
* @returns 当前高亮的节点配置
|
|
194
|
+
*/
|
|
195
|
+
public highlight(config: MNode | Id): void {
|
|
196
|
+
const { node } = this.selectedConfigExceptionHandler(config);
|
|
197
|
+
const currentHighligtNode = this.get('highlightNode');
|
|
198
|
+
if (currentHighligtNode === node) return;
|
|
199
|
+
this.set('highlightNode', node);
|
|
202
200
|
}
|
|
203
201
|
|
|
204
202
|
/**
|
|
@@ -212,7 +210,7 @@ class Editor extends BaseService {
|
|
|
212
210
|
|
|
213
211
|
let parentNode: MNode | undefined;
|
|
214
212
|
|
|
215
|
-
if (type ===
|
|
213
|
+
if (type === NodeType.PAGE) {
|
|
216
214
|
parentNode = this.get<MApp>('root');
|
|
217
215
|
// 由于支持中间件扩展,在parent参数为undefined时,parent会变成next函数
|
|
218
216
|
} else if (parent && typeof parent !== 'function') {
|
|
@@ -228,7 +226,7 @@ class Editor extends BaseService {
|
|
|
228
226
|
const layout = await this.getLayout(parentNode);
|
|
229
227
|
const newNode = initPosition({ ...toRaw(await propsService.getPropsValue(type)), ...config }, layout);
|
|
230
228
|
|
|
231
|
-
if ((parentNode?.type ===
|
|
229
|
+
if ((parentNode?.type === NodeType.ROOT || curNode.type === NodeType.ROOT) && newNode.type !== NodeType.PAGE) {
|
|
232
230
|
throw new Error('app下不能添加组件');
|
|
233
231
|
}
|
|
234
232
|
|
|
@@ -239,7 +237,9 @@ class Editor extends BaseService {
|
|
|
239
237
|
await this.select(newNode);
|
|
240
238
|
|
|
241
239
|
this.addModifiedNodeId(newNode.id);
|
|
242
|
-
|
|
240
|
+
if (type !== NodeType.PAGE) {
|
|
241
|
+
this.pushHistoryState();
|
|
242
|
+
}
|
|
243
243
|
|
|
244
244
|
return newNode;
|
|
245
245
|
}
|
|
@@ -267,7 +267,7 @@ class Editor extends BaseService {
|
|
|
267
267
|
parent.items?.splice(index, 1);
|
|
268
268
|
this.get<StageCore>('stage')?.remove({ id: node.id, root: this.get('root') });
|
|
269
269
|
|
|
270
|
-
if (node.type ===
|
|
270
|
+
if (node.type === NodeType.PAGE) {
|
|
271
271
|
await this.select(root.items[0] || root);
|
|
272
272
|
} else {
|
|
273
273
|
await this.select(parent);
|
|
@@ -303,7 +303,7 @@ class Editor extends BaseService {
|
|
|
303
303
|
|
|
304
304
|
if (!newConfig.type) throw new Error('配置缺少type值');
|
|
305
305
|
|
|
306
|
-
if (newConfig.type ===
|
|
306
|
+
if (newConfig.type === NodeType.ROOT) {
|
|
307
307
|
this.set('root', newConfig);
|
|
308
308
|
return newConfig;
|
|
309
309
|
}
|
|
@@ -330,7 +330,7 @@ class Editor extends BaseService {
|
|
|
330
330
|
|
|
331
331
|
this.get<StageCore>('stage')?.update({ config: cloneDeep(newConfig), root: this.get('root') });
|
|
332
332
|
|
|
333
|
-
if (newConfig.type ===
|
|
333
|
+
if (newConfig.type === NodeType.PAGE) {
|
|
334
334
|
this.set('page', newConfig);
|
|
335
335
|
}
|
|
336
336
|
|
|
@@ -524,6 +524,30 @@ class Editor extends BaseService {
|
|
|
524
524
|
|
|
525
525
|
return newConfig;
|
|
526
526
|
}
|
|
527
|
+
|
|
528
|
+
private selectedConfigExceptionHandler(config: MNode | Id): EditorNodeInfo {
|
|
529
|
+
let id: Id;
|
|
530
|
+
if (typeof config === 'string' || typeof config === 'number') {
|
|
531
|
+
id = config;
|
|
532
|
+
} else {
|
|
533
|
+
id = config.id;
|
|
534
|
+
}
|
|
535
|
+
if (!id) {
|
|
536
|
+
throw new Error('没有ID,无法选中');
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const { node, parent, page } = this.getNodeInfo(id);
|
|
540
|
+
if (!node) throw new Error('获取不到组件信息');
|
|
541
|
+
|
|
542
|
+
if (node.id === this.state.root?.id) {
|
|
543
|
+
throw new Error('不能选根节点');
|
|
544
|
+
}
|
|
545
|
+
return {
|
|
546
|
+
node,
|
|
547
|
+
parent,
|
|
548
|
+
page,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
527
551
|
}
|
|
528
552
|
|
|
529
553
|
export type EditorService = Editor;
|
|
@@ -5,9 +5,9 @@ $--border-color: #d9dbdd;
|
|
|
5
5
|
|
|
6
6
|
$--nav-height: 35px;
|
|
7
7
|
$--nav-color: #070303;
|
|
8
|
-
$--nav--background-color: #
|
|
8
|
+
$--nav--background-color: #ffffff;
|
|
9
9
|
|
|
10
10
|
$--sidebar-heder-background-color: $--theme-color;
|
|
11
|
-
$--sidebar-content-background-color: #
|
|
11
|
+
$--sidebar-content-background-color: #ffffff;
|
|
12
12
|
|
|
13
13
|
$--page-bar-height: 32px;
|
|
@@ -50,6 +50,15 @@
|
|
|
50
50
|
color: #fff;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
.magic-editor-layer-panel .cus-tree-node {
|
|
54
|
+
width: 100%;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.magic-editor-layer-panel .cus-tree-node-hover {
|
|
58
|
+
background-color: #f3f5f9;
|
|
59
|
+
width: 100%;
|
|
60
|
+
}
|
|
61
|
+
|
|
53
62
|
.magic-editor-layer-panel .el-tree-node:focus > .el-tree-node__content {
|
|
54
63
|
background-color: $--sidebar-heder-background-color;
|
|
55
64
|
color: #fff;
|