@tmagic/editor 1.0.0-beta.8 → 1.0.0-rc.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.
- package/README.md +1 -0
- package/dist/style.css +94 -45
- package/dist/tmagic-editor.es.js +1622 -2069
- package/dist/tmagic-editor.es.js.map +1 -1
- package/dist/tmagic-editor.umd.js +1621 -2071
- package/dist/tmagic-editor.umd.js.map +1 -1
- package/dist/types/src/Editor.vue.d.ts +50 -2
- package/dist/types/src/fields/Code.vue.d.ts +4 -4
- package/dist/types/src/fields/CodeLink.vue.d.ts +1 -3
- 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 +4 -5
- 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/Workspace.vue.d.ts +5 -31
- package/dist/types/src/services/BaseService.d.ts +5 -2
- package/dist/types/src/services/editor.d.ts +16 -6
- package/dist/types/src/services/history.d.ts +2 -18
- package/dist/types/src/services/props.d.ts +8 -2
- package/dist/types/src/services/ui.d.ts +2 -0
- package/dist/types/src/type.d.ts +25 -9
- package/dist/types/src/utils/editor.d.ts +5 -10
- package/dist/types/src/utils/polyfills.d.ts +0 -0
- package/dist/types/src/utils/props.d.ts +8 -1
- package/package.json +35 -12
- package/src/Editor.vue +44 -9
- package/src/components/ContentMenu.vue +140 -0
- package/src/components/ScrollViewer.vue +1 -1
- package/src/components/ToolButton.vue +72 -31
- package/src/fields/UISelect.vue +14 -6
- package/src/index.ts +2 -0
- package/src/layouts/AddPageBox.vue +3 -1
- 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 +93 -95
- package/src/layouts/sidebar/LayerPanel.vue +80 -59
- package/src/layouts/sidebar/Sidebar.vue +13 -52
- package/src/layouts/sidebar/TabPane.vue +68 -0
- package/src/layouts/workspace/PageBar.vue +145 -17
- package/src/layouts/workspace/Stage.vue +65 -91
- package/src/layouts/workspace/ViewerMenu.vue +122 -68
- package/src/layouts/workspace/Workspace.vue +125 -42
- package/src/services/BaseService.ts +71 -23
- package/src/services/editor.ts +231 -69
- package/src/services/history.ts +7 -0
- package/src/services/props.ts +64 -9
- package/src/services/ui.ts +35 -5
- package/src/theme/common/var.scss +3 -2
- package/src/theme/component-list-panel.scss +2 -0
- package/src/theme/content-menu.scss +33 -26
- package/src/theme/layer-panel.scss +10 -0
- package/src/theme/nav-menu.scss +6 -2
- package/src/theme/page-bar.scss +42 -2
- package/src/theme/props-panel.scss +19 -17
- package/src/theme/workspace.scss +4 -0
- package/src/type.ts +27 -9
- package/src/utils/editor.ts +37 -56
- package/src/utils/polyfills.ts +8 -0
- package/src/utils/props.ts +50 -8
- package/LICENSE +0 -5432
- package/dist/types/src/components/ToolButton.vue.d.ts +0 -35
- package/dist/types/src/fields/UISelect.vue.d.ts +0 -32
- package/dist/types/src/layouts/sidebar/LayerMenu.vue.d.ts +0 -31
- package/dist/types/src/layouts/sidebar/LayerPanel.vue.d.ts +0 -973
- package/dist/types/src/layouts/workspace/PageBar.vue.d.ts +0 -11
- package/dist/types/src/layouts/workspace/Stage.vue.d.ts +0 -192
- package/dist/types/src/layouts/workspace/ViewerMenu.vue.d.ts +0 -18
- package/tsconfig.json +0 -9
- package/vite.config.ts +0 -30
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
<script lang="ts">
|
|
6
6
|
import { defineComponent, onMounted, onUnmounted, ref, watch } from 'vue';
|
|
7
|
+
import { throttle } from 'lodash-es';
|
|
7
8
|
import * as monaco from 'monaco-editor';
|
|
8
9
|
import serialize from 'serialize-javascript';
|
|
9
10
|
|
|
@@ -36,14 +37,19 @@ export default defineComponent({
|
|
|
36
37
|
},
|
|
37
38
|
|
|
38
39
|
type: {
|
|
39
|
-
type:
|
|
40
|
+
type: String,
|
|
40
41
|
default: () => '',
|
|
41
42
|
},
|
|
42
43
|
|
|
43
44
|
language: {
|
|
44
|
-
type:
|
|
45
|
+
type: String,
|
|
45
46
|
default: () => 'javascript',
|
|
46
47
|
},
|
|
48
|
+
|
|
49
|
+
options: {
|
|
50
|
+
type: Object,
|
|
51
|
+
default: () => ({}),
|
|
52
|
+
},
|
|
47
53
|
},
|
|
48
54
|
|
|
49
55
|
emits: ['initd', 'save'],
|
|
@@ -51,10 +57,18 @@ export default defineComponent({
|
|
|
51
57
|
setup(props, { emit }) {
|
|
52
58
|
let vsEditor: monaco.editor.IStandaloneCodeEditor | null = null;
|
|
53
59
|
let vsDiffEditor: monaco.editor.IStandaloneDiffEditor | null = null;
|
|
60
|
+
|
|
54
61
|
const values = ref('');
|
|
55
62
|
const loading = ref(false);
|
|
56
63
|
const codeEditor = ref<HTMLDivElement>();
|
|
57
64
|
|
|
65
|
+
const resizeObserver = new globalThis.ResizeObserver(
|
|
66
|
+
throttle((): void => {
|
|
67
|
+
vsEditor?.layout();
|
|
68
|
+
vsDiffEditor?.layout();
|
|
69
|
+
}, 300),
|
|
70
|
+
);
|
|
71
|
+
|
|
58
72
|
const setEditorValue = (v: string | any, m: string | any) => {
|
|
59
73
|
values.value = toString(v, props.language);
|
|
60
74
|
|
|
@@ -71,11 +85,6 @@ export default defineComponent({
|
|
|
71
85
|
return vsEditor?.setValue(values.value);
|
|
72
86
|
};
|
|
73
87
|
|
|
74
|
-
const resizeHandler = () => {
|
|
75
|
-
vsEditor?.layout();
|
|
76
|
-
vsDiffEditor?.layout();
|
|
77
|
-
};
|
|
78
|
-
|
|
79
88
|
const getEditorValue = () =>
|
|
80
89
|
props.type === 'diff' ? vsDiffEditor?.getModifiedEditor().getValue() : vsEditor?.getValue();
|
|
81
90
|
|
|
@@ -85,11 +94,8 @@ export default defineComponent({
|
|
|
85
94
|
const options = {
|
|
86
95
|
value: values.value,
|
|
87
96
|
language: props.language,
|
|
88
|
-
tabSize: 2,
|
|
89
97
|
theme: 'vs-dark',
|
|
90
|
-
|
|
91
|
-
fontSize: 15,
|
|
92
|
-
formatOnPaste: true,
|
|
98
|
+
...props.options,
|
|
93
99
|
};
|
|
94
100
|
|
|
95
101
|
if (props.type === 'diff') {
|
|
@@ -107,6 +113,7 @@ export default defineComponent({
|
|
|
107
113
|
codeEditor.value.addEventListener('keydown', (e) => {
|
|
108
114
|
if (e.keyCode === 83 && (navigator.platform.match('Mac') ? e.metaKey : e.ctrlKey)) {
|
|
109
115
|
e.preventDefault();
|
|
116
|
+
e.stopPropagation();
|
|
110
117
|
emit('save', getEditorValue());
|
|
111
118
|
}
|
|
112
119
|
});
|
|
@@ -117,7 +124,7 @@ export default defineComponent({
|
|
|
117
124
|
});
|
|
118
125
|
}
|
|
119
126
|
|
|
120
|
-
|
|
127
|
+
resizeObserver.observe(codeEditor.value);
|
|
121
128
|
};
|
|
122
129
|
|
|
123
130
|
watch(
|
|
@@ -140,7 +147,7 @@ export default defineComponent({
|
|
|
140
147
|
});
|
|
141
148
|
|
|
142
149
|
onUnmounted(() => {
|
|
143
|
-
|
|
150
|
+
resizeObserver.disconnect();
|
|
144
151
|
});
|
|
145
152
|
|
|
146
153
|
return {
|
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
<div class="m-editor">
|
|
3
3
|
<slot name="nav" class="m-editor-nav-menu"></slot>
|
|
4
4
|
|
|
5
|
-
<magic-code-editor
|
|
5
|
+
<magic-code-editor
|
|
6
|
+
v-if="showSrc"
|
|
7
|
+
class="m-editor-content"
|
|
8
|
+
:init-values="root"
|
|
9
|
+
:options="codeOptions"
|
|
10
|
+
@save="saveCode"
|
|
11
|
+
></magic-code-editor>
|
|
6
12
|
|
|
7
13
|
<div class="m-editor-content" v-else>
|
|
8
14
|
<div class="m-editor-framework-left" :style="`width: ${columnWidth?.left}px`">
|
|
@@ -48,21 +54,28 @@ export default defineComponent({
|
|
|
48
54
|
Resizer,
|
|
49
55
|
},
|
|
50
56
|
|
|
57
|
+
props: {
|
|
58
|
+
codeOptions: {
|
|
59
|
+
type: Object,
|
|
60
|
+
default: () => ({}),
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
|
|
51
64
|
setup() {
|
|
52
|
-
const
|
|
65
|
+
const { editorService, uiService } = inject<Services>('services') || {};
|
|
53
66
|
|
|
54
|
-
const root = computed(() =>
|
|
67
|
+
const root = computed(() => editorService?.get<MApp>('root'));
|
|
55
68
|
|
|
56
69
|
return {
|
|
57
70
|
root,
|
|
58
|
-
pageLength: computed(() =>
|
|
59
|
-
showSrc: computed(() =>
|
|
60
|
-
columnWidth: computed(() =>
|
|
71
|
+
pageLength: computed(() => editorService?.get<number>('pageLength') || 0),
|
|
72
|
+
showSrc: computed(() => uiService?.get<boolean>('showSrc')),
|
|
73
|
+
columnWidth: computed(() => uiService?.get<GetColumnWidth>('columnWidth')),
|
|
61
74
|
|
|
62
75
|
saveCode(value: string) {
|
|
63
76
|
try {
|
|
64
77
|
// eslint-disable-next-line no-eval
|
|
65
|
-
|
|
78
|
+
editorService?.set('root', eval(value));
|
|
66
79
|
} catch (e: any) {
|
|
67
80
|
console.error(e);
|
|
68
81
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<m-form
|
|
3
|
-
class="m-editor-props-panel"
|
|
4
|
-
popper-class="m-editor-props-panel-popper"
|
|
3
|
+
:class="`m-editor-props-panel ${propsPanelSize}`"
|
|
4
|
+
:popper-class="`m-editor-props-panel-popper ${propsPanelSize}`"
|
|
5
5
|
ref="configForm"
|
|
6
|
-
size="
|
|
6
|
+
:size="propsPanelSize"
|
|
7
7
|
:init-values="values"
|
|
8
8
|
:config="curFormConfig"
|
|
9
9
|
@change="submit"
|
|
@@ -39,9 +39,9 @@ export default defineComponent({
|
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
values.value = node.value;
|
|
43
42
|
const type = node.value.type || (node.value.items ? 'container' : 'text');
|
|
44
43
|
curFormConfig.value = (await services?.propsService.getPropsConfig(type)) || [];
|
|
44
|
+
values.value = node.value;
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
watchEffect(init);
|
|
@@ -55,6 +55,7 @@ export default defineComponent({
|
|
|
55
55
|
values,
|
|
56
56
|
configForm,
|
|
57
57
|
curFormConfig,
|
|
58
|
+
propsPanelSize: computed(() => services?.uiService.get('propsPanelSize') || 'small'),
|
|
58
59
|
|
|
59
60
|
async submit() {
|
|
60
61
|
try {
|
|
@@ -12,7 +12,14 @@
|
|
|
12
12
|
<template v-for="(group, index) in list">
|
|
13
13
|
<el-collapse-item v-if="group.items && group.items.length" :key="index" :name="index">
|
|
14
14
|
<template #title><i class="el-icon-s-grid"></i>{{ group.title }}</template>
|
|
15
|
-
<div
|
|
15
|
+
<div
|
|
16
|
+
class="component-item"
|
|
17
|
+
v-for="item in group.items"
|
|
18
|
+
draggable="true"
|
|
19
|
+
:key="item.type"
|
|
20
|
+
@click="appendComponent(item)"
|
|
21
|
+
@dragstart="dragstartHandler(item, $event)"
|
|
22
|
+
>
|
|
16
23
|
<m-icon :icon="item.icon"></m-icon>
|
|
17
24
|
|
|
18
25
|
<el-tooltip effect="dark" placement="bottom" :content="item.text">
|
|
@@ -27,6 +34,7 @@
|
|
|
27
34
|
|
|
28
35
|
<script lang="ts">
|
|
29
36
|
import { computed, defineComponent, inject, ref } from 'vue';
|
|
37
|
+
import serialize from 'serialize-javascript';
|
|
30
38
|
|
|
31
39
|
import MIcon from '@editor/components/Icon.vue';
|
|
32
40
|
import type { ComponentGroup, ComponentItem, Services } from '@editor/type';
|
|
@@ -56,13 +64,27 @@ export default defineComponent({
|
|
|
56
64
|
collapseValue,
|
|
57
65
|
list,
|
|
58
66
|
|
|
59
|
-
appendComponent({ text, type,
|
|
67
|
+
appendComponent({ text, type, data = {} }: ComponentItem): void {
|
|
60
68
|
services?.editorService.add({
|
|
61
69
|
name: text,
|
|
62
70
|
type,
|
|
63
|
-
...
|
|
71
|
+
...data,
|
|
64
72
|
});
|
|
65
73
|
},
|
|
74
|
+
|
|
75
|
+
dragstartHandler({ text, type, data = {} }: ComponentItem, e: DragEvent) {
|
|
76
|
+
if (e.dataTransfer) {
|
|
77
|
+
e.dataTransfer.effectAllowed = 'move';
|
|
78
|
+
e.dataTransfer.setData(
|
|
79
|
+
'data',
|
|
80
|
+
serialize({
|
|
81
|
+
name: text,
|
|
82
|
+
type,
|
|
83
|
+
...data,
|
|
84
|
+
}).replace(/"(\w+)":\s/g, '$1: '),
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
},
|
|
66
88
|
};
|
|
67
89
|
},
|
|
68
90
|
});
|
|
@@ -1,115 +1,113 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<div
|
|
4
|
-
v-if="node.items"
|
|
5
|
-
class="magic-editor-content-menu-item"
|
|
6
|
-
@mouseenter="setSubVisiable(true)"
|
|
7
|
-
@mouseleave="setSubVisiable(false)"
|
|
8
|
-
>
|
|
9
|
-
新增
|
|
10
|
-
</div>
|
|
11
|
-
<div v-if="node.type !== 'app'" class="magic-editor-content-menu-item" @click="() => copy(node)">复制</div>
|
|
12
|
-
<div
|
|
13
|
-
v-if="node.type !== 'app' && node.type !== 'page'"
|
|
14
|
-
class="magic-editor-content-menu-item"
|
|
15
|
-
@click="() => remove()"
|
|
16
|
-
>
|
|
17
|
-
删除
|
|
18
|
-
</div>
|
|
19
|
-
<div class="subMenu" v-show="subVisible" @mouseenter="setSubVisiable(true)" @mouseleave="setSubVisiable(false)">
|
|
20
|
-
<el-scrollbar>
|
|
21
|
-
<template v-if="node.type === 'tabs'">
|
|
22
|
-
<div
|
|
23
|
-
class="magic-editor-content-menu-item"
|
|
24
|
-
@click="
|
|
25
|
-
() =>
|
|
26
|
-
append({
|
|
27
|
-
type: 'tab-pane',
|
|
28
|
-
})
|
|
29
|
-
"
|
|
30
|
-
>
|
|
31
|
-
标签
|
|
32
|
-
</div>
|
|
33
|
-
</template>
|
|
34
|
-
|
|
35
|
-
<template v-else-if="node.type === 'app'">
|
|
36
|
-
<div
|
|
37
|
-
class="magic-editor-content-menu-item"
|
|
38
|
-
v-for="item in menu.app"
|
|
39
|
-
:key="item.type"
|
|
40
|
-
@click="() => append(item)"
|
|
41
|
-
>
|
|
42
|
-
{{ item.text }}
|
|
43
|
-
</div>
|
|
44
|
-
</template>
|
|
45
|
-
|
|
46
|
-
<template v-else-if="node.items">
|
|
47
|
-
<div v-for="list in menu.component" :key="list.title">
|
|
48
|
-
<template v-for="item in list.items">
|
|
49
|
-
<div class="magic-editor-content-menu-item" v-if="item" :key="item.type" @click="() => append(item)">
|
|
50
|
-
{{ item.text }}
|
|
51
|
-
</div>
|
|
52
|
-
</template>
|
|
53
|
-
<div class="separation"></div>
|
|
54
|
-
</div>
|
|
55
|
-
</template>
|
|
56
|
-
</el-scrollbar>
|
|
57
|
-
</div>
|
|
58
|
-
</div>
|
|
2
|
+
<content-menu :menu-data="menuData" ref="menu" style="overflow: initial"></content-menu>
|
|
59
3
|
</template>
|
|
60
4
|
|
|
61
5
|
<script lang="ts">
|
|
62
|
-
import { computed, defineComponent, inject,
|
|
6
|
+
import { computed, defineComponent, inject, markRaw, ref } from 'vue';
|
|
7
|
+
import { Delete, DocumentCopy, Files, Plus } from '@element-plus/icons';
|
|
63
8
|
|
|
64
|
-
import
|
|
9
|
+
import { NodeType } from '@tmagic/schema';
|
|
65
10
|
|
|
66
|
-
|
|
67
|
-
|
|
11
|
+
import ContentMenu from '@editor/components/ContentMenu.vue';
|
|
12
|
+
import type { ComponentGroup, MenuButton, MenuItem, Services } from '@editor/type';
|
|
68
13
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
type: Array as PropType<ComponentGroup[]>,
|
|
72
|
-
default: () => [],
|
|
73
|
-
},
|
|
74
|
-
},
|
|
14
|
+
export default defineComponent({
|
|
15
|
+
components: { ContentMenu },
|
|
75
16
|
|
|
76
|
-
setup(
|
|
17
|
+
setup() {
|
|
77
18
|
const services = inject<Services>('services');
|
|
78
|
-
const
|
|
19
|
+
const menu = ref<InstanceType<typeof ContentMenu>>();
|
|
79
20
|
const node = computed(() => services?.editorService.get('node'));
|
|
21
|
+
const isRoot = computed(() => node.value?.type === NodeType.ROOT);
|
|
22
|
+
const isPage = computed(() => node.value?.type === NodeType.PAGE);
|
|
23
|
+
const componentList = computed(() => services?.componentListService.getList() || []);
|
|
80
24
|
|
|
81
|
-
|
|
82
|
-
subVisible,
|
|
25
|
+
const layerContentMenu = inject<MenuItem[]>('layerContentMenu', []);
|
|
83
26
|
|
|
84
|
-
|
|
27
|
+
const createMenuItems = (group: ComponentGroup): MenuButton[] =>
|
|
28
|
+
group.items.map((component) => ({
|
|
29
|
+
text: component.text,
|
|
30
|
+
type: 'button',
|
|
31
|
+
icon: component.icon,
|
|
32
|
+
handler: () => {
|
|
33
|
+
services?.editorService.add({
|
|
34
|
+
name: component.text,
|
|
35
|
+
type: component.type,
|
|
36
|
+
...(component.data || {}),
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
}));
|
|
85
40
|
|
|
86
|
-
|
|
87
|
-
|
|
41
|
+
const getSubMenuData = computed<MenuButton[]>(() => {
|
|
42
|
+
if (node.value?.type === 'tabs') {
|
|
43
|
+
return [
|
|
88
44
|
{
|
|
89
|
-
|
|
90
|
-
|
|
45
|
+
text: '标签页',
|
|
46
|
+
type: 'button',
|
|
47
|
+
icon: Files,
|
|
48
|
+
handler: () => {
|
|
49
|
+
services?.editorService.add({
|
|
50
|
+
type: 'tab-pane',
|
|
51
|
+
});
|
|
52
|
+
},
|
|
91
53
|
},
|
|
92
|
-
]
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
if (node.value?.items) {
|
|
57
|
+
return (
|
|
58
|
+
componentList.value.reduce(
|
|
59
|
+
(subMenuData: MenuButton[], group: ComponentGroup, index) =>
|
|
60
|
+
subMenuData.concat(
|
|
61
|
+
createMenuItems(group),
|
|
62
|
+
index < componentList.value.length - 1
|
|
63
|
+
? [
|
|
64
|
+
{
|
|
65
|
+
type: 'divider',
|
|
66
|
+
direction: 'horizontal',
|
|
67
|
+
},
|
|
68
|
+
]
|
|
69
|
+
: [],
|
|
70
|
+
),
|
|
71
|
+
[],
|
|
72
|
+
) || []
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
return [];
|
|
76
|
+
});
|
|
106
77
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
78
|
+
return {
|
|
79
|
+
menu,
|
|
80
|
+
menuData: computed<MenuItem[]>(() => [
|
|
81
|
+
{
|
|
82
|
+
type: 'button',
|
|
83
|
+
text: '新增',
|
|
84
|
+
icon: markRaw(Plus),
|
|
85
|
+
display: () => node.value?.items,
|
|
86
|
+
items: getSubMenuData.value,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
type: 'button',
|
|
90
|
+
text: '复制',
|
|
91
|
+
icon: markRaw(DocumentCopy),
|
|
92
|
+
display: () => !isRoot.value,
|
|
93
|
+
handler: () => {
|
|
94
|
+
node.value && services?.editorService.copy(node.value);
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
type: 'button',
|
|
99
|
+
text: '删除',
|
|
100
|
+
icon: markRaw(Delete),
|
|
101
|
+
display: () => !isRoot.value && !isPage.value,
|
|
102
|
+
handler: () => {
|
|
103
|
+
node.value && services?.editorService.remove(node.value);
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
...layerContentMenu,
|
|
107
|
+
]),
|
|
110
108
|
|
|
111
|
-
|
|
112
|
-
|
|
109
|
+
show(e: MouseEvent) {
|
|
110
|
+
menu.value?.show(e);
|
|
113
111
|
},
|
|
114
112
|
};
|
|
115
113
|
},
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
ref="tree"
|
|
15
15
|
node-key="id"
|
|
16
16
|
draggable
|
|
17
|
+
:default-expanded-keys="expandedKeys"
|
|
17
18
|
:load="loadItems"
|
|
18
19
|
:data="values"
|
|
19
20
|
:expand-on-click-node="false"
|
|
@@ -29,37 +30,60 @@
|
|
|
29
30
|
empty-text="页面空荡荡的"
|
|
30
31
|
>
|
|
31
32
|
<template #default="{ node, data }">
|
|
32
|
-
<
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
<div
|
|
34
|
+
:id="data.id"
|
|
35
|
+
class="cus-tree-node"
|
|
36
|
+
@mousedown="toggleClickFlag"
|
|
37
|
+
@mouseup="toggleClickFlag"
|
|
38
|
+
@mouseenter="highlightHandler(data)"
|
|
39
|
+
:class="{ 'cus-tree-node-hover': canHighlight && data.id === highlightNode?.id }"
|
|
40
|
+
>
|
|
41
|
+
<slot name="layer-node-content" :node="node" :data="data">
|
|
42
|
+
<span>
|
|
43
|
+
{{ `${data.name} (${data.id})` }}
|
|
44
|
+
</span>
|
|
45
|
+
</slot>
|
|
46
|
+
</div>
|
|
37
47
|
</template>
|
|
38
48
|
</el-tree>
|
|
39
49
|
|
|
40
50
|
<teleport to="body">
|
|
41
|
-
<layer-menu
|
|
51
|
+
<layer-menu ref="menu"></layer-menu>
|
|
42
52
|
</teleport>
|
|
43
53
|
</el-scrollbar>
|
|
44
54
|
</template>
|
|
45
55
|
|
|
46
56
|
<script lang="ts">
|
|
47
|
-
import { computed, defineComponent, inject,
|
|
57
|
+
import { computed, defineComponent, inject, Ref, ref, watchEffect } from 'vue';
|
|
48
58
|
import type { ElTree } from 'element-plus';
|
|
59
|
+
import { throttle } from 'lodash-es';
|
|
49
60
|
|
|
50
61
|
import type { MNode, MPage } from '@tmagic/schema';
|
|
62
|
+
import { NodeType } from '@tmagic/schema';
|
|
63
|
+
import StageCore from '@tmagic/stage';
|
|
51
64
|
|
|
52
65
|
import type { EditorService } from '@editor/services/editor';
|
|
53
66
|
import type { Services } from '@editor/type';
|
|
54
67
|
|
|
55
68
|
import LayerMenu from './LayerMenu.vue';
|
|
56
69
|
|
|
57
|
-
const
|
|
70
|
+
const throttleTime = 150;
|
|
71
|
+
|
|
72
|
+
const select = async (data: MNode, editorService?: EditorService) => {
|
|
58
73
|
if (!data.id) {
|
|
59
74
|
throw new Error('没有id');
|
|
60
75
|
}
|
|
61
76
|
|
|
62
|
-
editorService?.select(data);
|
|
77
|
+
await editorService?.select(data);
|
|
78
|
+
editorService?.get<StageCore>('stage')?.select(data.id);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const highlight = (data: MNode, editorService?: EditorService) => {
|
|
82
|
+
if (!data?.id) {
|
|
83
|
+
throw new Error('没有id');
|
|
84
|
+
}
|
|
85
|
+
editorService?.highlight(data);
|
|
86
|
+
editorService?.get<StageCore>('stage')?.highlight(data.id);
|
|
63
87
|
};
|
|
64
88
|
|
|
65
89
|
const useDrop = (tree: Ref<InstanceType<typeof ElTree> | undefined>, editorService?: EditorService) => ({
|
|
@@ -69,8 +93,8 @@ const useDrop = (tree: Ref<InstanceType<typeof ElTree> | undefined>, editorServi
|
|
|
69
93
|
|
|
70
94
|
const { type: ingType } = ingData;
|
|
71
95
|
|
|
72
|
-
if (ingType !==
|
|
73
|
-
if (ingType ===
|
|
96
|
+
if (ingType !== NodeType.PAGE && data.type === NodeType.PAGE) return false;
|
|
97
|
+
if (ingType === NodeType.PAGE && data.type !== NodeType.PAGE) return false;
|
|
74
98
|
if (!data || !data.type) return false;
|
|
75
99
|
if (['prev', 'next'].includes(type)) return true;
|
|
76
100
|
if (data.items || data.type === 'container') return true;
|
|
@@ -87,19 +111,22 @@ const useDrop = (tree: Ref<InstanceType<typeof ElTree> | undefined>, editorServi
|
|
|
87
111
|
});
|
|
88
112
|
|
|
89
113
|
const useStatus = (tree: Ref<InstanceType<typeof ElTree> | undefined>, editorService?: EditorService) => {
|
|
114
|
+
const highlightNode = ref<MNode>();
|
|
115
|
+
const node = ref<MNode>();
|
|
90
116
|
const page = computed(() => editorService?.get('page'));
|
|
91
117
|
|
|
92
118
|
watchEffect(() => {
|
|
93
119
|
if (!tree.value) return;
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
node && tree.value.setCurrentKey(node.id, true);
|
|
120
|
+
node.value = editorService?.get('node');
|
|
121
|
+
node.value && tree.value.setCurrentKey(node.value.id, true);
|
|
97
122
|
|
|
98
123
|
const parent = editorService?.get('parent');
|
|
99
124
|
if (!parent?.id) return;
|
|
100
125
|
|
|
101
126
|
const treeNode = tree.value.getNode(parent.id);
|
|
102
127
|
treeNode?.updateChildren();
|
|
128
|
+
|
|
129
|
+
highlightNode.value = editorService?.get('highlightNode');
|
|
103
130
|
});
|
|
104
131
|
|
|
105
132
|
return {
|
|
@@ -114,6 +141,10 @@ const useStatus = (tree: Ref<InstanceType<typeof ElTree> | undefined>, editorSer
|
|
|
114
141
|
}
|
|
115
142
|
resolve([]);
|
|
116
143
|
},
|
|
144
|
+
|
|
145
|
+
highlightNode,
|
|
146
|
+
clickNode: node,
|
|
147
|
+
expandedKeys: computed(() => (node.value ? [node.value.id] : [])),
|
|
117
148
|
};
|
|
118
149
|
};
|
|
119
150
|
|
|
@@ -127,12 +158,10 @@ const useFilter = (tree: Ref<InstanceType<typeof ElTree> | undefined>) => ({
|
|
|
127
158
|
let name = '';
|
|
128
159
|
if (data.name) {
|
|
129
160
|
name = data.name;
|
|
130
|
-
} else if (data.type) {
|
|
131
|
-
name = data.type;
|
|
132
161
|
} else if (data.items) {
|
|
133
162
|
name = 'container';
|
|
134
163
|
}
|
|
135
|
-
return name.indexOf(value) !== -1;
|
|
164
|
+
return `${data.id}${name}${data.type}`.indexOf(value) !== -1;
|
|
136
165
|
},
|
|
137
166
|
|
|
138
167
|
filterTextChangeHandler(val: string) {
|
|
@@ -140,46 +169,6 @@ const useFilter = (tree: Ref<InstanceType<typeof ElTree> | undefined>) => ({
|
|
|
140
169
|
},
|
|
141
170
|
});
|
|
142
171
|
|
|
143
|
-
const useContentMenu = (editorService?: EditorService) => {
|
|
144
|
-
const menuStyle = ref({
|
|
145
|
-
position: 'absolute',
|
|
146
|
-
left: '0',
|
|
147
|
-
top: '0',
|
|
148
|
-
display: 'none',
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
onMounted(() => {
|
|
152
|
-
document.addEventListener(
|
|
153
|
-
'click',
|
|
154
|
-
() => {
|
|
155
|
-
menuStyle.value.display = 'none';
|
|
156
|
-
},
|
|
157
|
-
true,
|
|
158
|
-
);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
return {
|
|
162
|
-
menuStyle,
|
|
163
|
-
|
|
164
|
-
contextmenu(event: MouseEvent, data: MNode) {
|
|
165
|
-
const bodyHeight = globalThis.document.body.clientHeight;
|
|
166
|
-
|
|
167
|
-
const left = `${event.clientX + 20}px`;
|
|
168
|
-
let top = `${event.clientY - 10}px`;
|
|
169
|
-
|
|
170
|
-
if (event.clientY + 300 > bodyHeight) {
|
|
171
|
-
top = `${bodyHeight - 300}px`;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
menuStyle.value.left = left;
|
|
175
|
-
menuStyle.value.top = top;
|
|
176
|
-
menuStyle.value.display = '';
|
|
177
|
-
|
|
178
|
-
select(data, editorService);
|
|
179
|
-
},
|
|
180
|
-
};
|
|
181
|
-
};
|
|
182
|
-
|
|
183
172
|
export default defineComponent({
|
|
184
173
|
name: 'magic-editor-layer-panel',
|
|
185
174
|
|
|
@@ -188,21 +177,53 @@ export default defineComponent({
|
|
|
188
177
|
setup() {
|
|
189
178
|
const services = inject<Services>('services');
|
|
190
179
|
const tree = ref<InstanceType<typeof ElTree>>();
|
|
180
|
+
const menu = ref<InstanceType<typeof LayerMenu>>();
|
|
181
|
+
const clicked = ref(false);
|
|
191
182
|
const editorService = services?.editorService;
|
|
183
|
+
const highlightHandler = throttle((data: MNode) => {
|
|
184
|
+
highlight(data, editorService);
|
|
185
|
+
}, throttleTime);
|
|
186
|
+
|
|
187
|
+
const toggleClickFlag = () => {
|
|
188
|
+
clicked.value = !clicked.value;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const statusData = useStatus(tree, editorService);
|
|
192
|
+
const canHighlight = computed(
|
|
193
|
+
() => statusData.highlightNode.value?.id !== statusData.clickNode.value?.id && !clicked.value,
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
editorService?.on('remove', () => {
|
|
197
|
+
setTimeout(() => {
|
|
198
|
+
tree.value?.getNode(editorService.get('node').id)?.updateChildren();
|
|
199
|
+
}, 0);
|
|
200
|
+
});
|
|
201
|
+
|
|
192
202
|
return {
|
|
193
203
|
tree,
|
|
204
|
+
menu,
|
|
205
|
+
...statusData,
|
|
194
206
|
...useDrop(tree, editorService),
|
|
195
|
-
...useStatus(tree, editorService),
|
|
196
207
|
...useFilter(tree),
|
|
197
|
-
|
|
208
|
+
|
|
209
|
+
highlightHandler,
|
|
210
|
+
toggleClickFlag,
|
|
211
|
+
canHighlight,
|
|
198
212
|
|
|
199
213
|
clickHandler(data: MNode): void {
|
|
200
214
|
if (services?.uiService.get<boolean>('uiSelectMode')) {
|
|
201
215
|
document.dispatchEvent(new CustomEvent('ui-select', { detail: data }));
|
|
202
216
|
return;
|
|
203
217
|
}
|
|
218
|
+
tree.value?.setCurrentKey(data.id);
|
|
204
219
|
select(data, editorService);
|
|
205
220
|
},
|
|
221
|
+
|
|
222
|
+
async contextmenu(event: MouseEvent, data: MNode) {
|
|
223
|
+
event.preventDefault();
|
|
224
|
+
await select(data, editorService);
|
|
225
|
+
menu.value?.show(event);
|
|
226
|
+
},
|
|
206
227
|
};
|
|
207
228
|
},
|
|
208
229
|
});
|