@tmagic/editor 1.0.0-beta.1 → 1.0.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +5432 -0
- package/dist/style.css +53 -24
- package/dist/tmagic-editor.es.js +832 -403
- package/dist/tmagic-editor.es.js.map +1 -1
- package/dist/tmagic-editor.umd.js +849 -403
- package/dist/tmagic-editor.umd.js.map +1 -1
- package/dist/types/src/Editor.vue.d.ts +5 -5
- package/dist/types/src/components/ScrollViewer.vue.d.ts +23 -0
- package/dist/types/src/layouts/CodeEditor.vue.d.ts +5 -4
- package/dist/types/src/layouts/NavMenu.vue.d.ts +2 -1
- package/dist/types/src/layouts/sidebar/LayerPanel.vue.d.ts +10 -3
- package/dist/types/src/layouts/workspace/PageBar.vue.d.ts +8 -3
- package/dist/types/src/layouts/workspace/Stage.vue.d.ts +77 -42
- package/dist/types/src/layouts/workspace/Workspace.vue.d.ts +2 -9
- package/dist/types/src/services/BaseService.d.ts +4 -1
- package/dist/types/src/services/editor.d.ts +11 -4
- package/dist/types/src/services/ui.d.ts +10 -3
- package/dist/types/src/type.d.ts +20 -6
- package/dist/types/src/utils/editor.d.ts +1 -2
- package/dist/types/src/utils/scroll-viewer.d.ts +35 -0
- package/package.json +15 -12
- package/src/Editor.vue +5 -5
- package/src/components/ScrollViewer.vue +66 -0
- package/src/components/ToolButton.vue +5 -3
- package/src/layouts/AddPageBox.vue +3 -1
- package/src/layouts/CodeEditor.vue +35 -59
- package/src/layouts/NavMenu.vue +7 -3
- package/src/layouts/PropsPanel.vue +6 -10
- package/src/layouts/sidebar/LayerMenu.vue +14 -39
- package/src/layouts/sidebar/LayerPanel.vue +63 -16
- package/src/layouts/workspace/PageBar.vue +113 -14
- package/src/layouts/workspace/Stage.vue +67 -61
- package/src/layouts/workspace/ViewerMenu.vue +5 -3
- package/src/layouts/workspace/Workspace.vue +3 -37
- package/src/services/BaseService.ts +70 -22
- package/src/services/editor.ts +98 -52
- package/src/services/ui.ts +42 -5
- package/src/theme/common/var.scss +2 -2
- package/src/theme/layer-panel.scss +9 -0
- package/src/theme/nav-menu.scss +3 -3
- package/src/theme/page-bar.scss +21 -2
- package/src/theme/props-panel.scss +8 -0
- package/src/theme/stage.scss +8 -7
- package/src/type.ts +23 -6
- package/src/utils/editor.ts +5 -23
- package/src/utils/scroll-viewer.ts +216 -0
- package/dist/types/src/layouts/sidebar/LayerMenu.vue.d.ts +0 -31
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="m-editor-scroll-viewer-container" ref="container">
|
|
3
|
+
<div ref="el" :style="style">
|
|
4
|
+
<slot></slot>
|
|
5
|
+
</div>
|
|
6
|
+
</div>
|
|
7
|
+
</template>
|
|
8
|
+
|
|
9
|
+
<script lang="ts">
|
|
10
|
+
import { computed, defineComponent, onMounted, onUnmounted, ref, watch } from 'vue';
|
|
11
|
+
|
|
12
|
+
import { ScrollViewer } from '@editor/utils/scroll-viewer';
|
|
13
|
+
|
|
14
|
+
export default defineComponent({
|
|
15
|
+
name: 'm-editor-scroll-viewer',
|
|
16
|
+
|
|
17
|
+
props: {
|
|
18
|
+
width: Number,
|
|
19
|
+
height: Number,
|
|
20
|
+
zoom: {
|
|
21
|
+
type: Number,
|
|
22
|
+
default: 1,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
|
|
26
|
+
setup(props) {
|
|
27
|
+
const container = ref<HTMLDivElement>();
|
|
28
|
+
const el = ref<HTMLDivElement>();
|
|
29
|
+
let scrollViewer: ScrollViewer;
|
|
30
|
+
|
|
31
|
+
onMounted(() => {
|
|
32
|
+
if (!container.value || !el.value) return;
|
|
33
|
+
scrollViewer = new ScrollViewer({
|
|
34
|
+
container: container.value,
|
|
35
|
+
target: el.value,
|
|
36
|
+
zoom: props.zoom,
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
onUnmounted(() => {
|
|
41
|
+
scrollViewer.destroy();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
watch(
|
|
45
|
+
() => props.zoom,
|
|
46
|
+
() => {
|
|
47
|
+
scrollViewer.setZoom(props.zoom);
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
container,
|
|
53
|
+
el,
|
|
54
|
+
|
|
55
|
+
style: computed(
|
|
56
|
+
() => `
|
|
57
|
+
width: ${props.width}px;
|
|
58
|
+
height: ${props.height}px;
|
|
59
|
+
position: absolute;
|
|
60
|
+
margin-top: 30px;
|
|
61
|
+
`,
|
|
62
|
+
),
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
</script>
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
<div v-else-if="item.type === 'text'" class="menu-item-text">{{ item.text }}</div>
|
|
5
5
|
|
|
6
6
|
<template v-else-if="item.type === 'zoom'">
|
|
7
|
-
<m-icon :icon="ZoomIn" @click="zoomInHandler"></m-icon>
|
|
7
|
+
<el-button size="small" type="text"><m-icon :icon="ZoomIn" @click="zoomInHandler"></m-icon></el-button>
|
|
8
8
|
<span class="menu-item-text" style="margin: 0 5px">{{ parseInt(`${zoom * 100}`, 10) }}%</span>
|
|
9
|
-
<m-icon :icon="ZoomOut" @click="zoomOutHandler"></m-icon>
|
|
9
|
+
<el-button size="small" type="text"><m-icon :icon="ZoomOut" @click="zoomOutHandler"></m-icon></el-button>
|
|
10
10
|
</template>
|
|
11
11
|
|
|
12
12
|
<el-tooltip
|
|
@@ -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
|
},
|
|
@@ -4,28 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
<script lang="ts">
|
|
6
6
|
import { defineComponent, onMounted, onUnmounted, ref, watch } from 'vue';
|
|
7
|
+
import * as monaco from 'monaco-editor';
|
|
7
8
|
import serialize from 'serialize-javascript';
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const initEditor = () => {
|
|
12
|
-
if ((globalThis as any).monaco) {
|
|
13
|
-
Promise.resolve((globalThis as any).monaco);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return asyncLoadJs(`https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.26.1/min/vs/loader.min.js`).then(() => {
|
|
17
|
-
(globalThis as any).require.config({
|
|
18
|
-
paths: { vs: `https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.26.1/min/vs` },
|
|
19
|
-
});
|
|
20
|
-
return new Promise((resolve) => {
|
|
21
|
-
(globalThis as any).require(['vs/editor/editor.main'], () => {
|
|
22
|
-
resolve((globalThis as any).monaco);
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const toString = (v: any, language: string): string => {
|
|
10
|
+
const toString = (v: string | any, language: string): string => {
|
|
29
11
|
let value = '';
|
|
30
12
|
if (typeof v !== 'string') {
|
|
31
13
|
value = serialize(v, {
|
|
@@ -64,60 +46,63 @@ export default defineComponent({
|
|
|
64
46
|
},
|
|
65
47
|
},
|
|
66
48
|
|
|
67
|
-
emits: ['
|
|
49
|
+
emits: ['initd', 'save'],
|
|
68
50
|
|
|
69
51
|
setup(props, { emit }) {
|
|
70
|
-
let vsEditor:
|
|
52
|
+
let vsEditor: monaco.editor.IStandaloneCodeEditor | null = null;
|
|
53
|
+
let vsDiffEditor: monaco.editor.IStandaloneDiffEditor | null = null;
|
|
71
54
|
const values = ref('');
|
|
72
55
|
const loading = ref(false);
|
|
73
56
|
const codeEditor = ref<HTMLDivElement>();
|
|
74
57
|
|
|
75
|
-
const setEditorValue = (v: any, m: any) => {
|
|
58
|
+
const setEditorValue = (v: string | any, m: string | any) => {
|
|
76
59
|
values.value = toString(v, props.language);
|
|
77
60
|
|
|
78
61
|
if (props.type === 'diff') {
|
|
79
|
-
const originalModel =
|
|
80
|
-
const modifiedModel =
|
|
81
|
-
toString(m, props.language),
|
|
82
|
-
'text/javascript',
|
|
83
|
-
);
|
|
62
|
+
const originalModel = monaco.editor.createModel(values.value, 'text/javascript');
|
|
63
|
+
const modifiedModel = monaco.editor.createModel(toString(m, props.language), 'text/javascript');
|
|
84
64
|
|
|
85
|
-
return
|
|
65
|
+
return vsDiffEditor?.setModel({
|
|
86
66
|
original: originalModel,
|
|
87
67
|
modified: modifiedModel,
|
|
88
68
|
});
|
|
89
69
|
}
|
|
90
70
|
|
|
91
|
-
return vsEditor
|
|
71
|
+
return vsEditor?.setValue(values.value);
|
|
92
72
|
};
|
|
93
73
|
|
|
94
74
|
const resizeHandler = () => {
|
|
95
75
|
vsEditor?.layout();
|
|
76
|
+
vsDiffEditor?.layout();
|
|
96
77
|
};
|
|
97
78
|
|
|
98
|
-
const getEditorValue = () =>
|
|
79
|
+
const getEditorValue = () =>
|
|
80
|
+
props.type === 'diff' ? vsDiffEditor?.getModifiedEditor().getValue() : vsEditor?.getValue();
|
|
99
81
|
|
|
100
82
|
const init = async () => {
|
|
101
83
|
if (!codeEditor.value) return;
|
|
102
84
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
85
|
+
const options = {
|
|
86
|
+
value: values.value,
|
|
87
|
+
language: props.language,
|
|
88
|
+
tabSize: 2,
|
|
89
|
+
theme: 'vs-dark',
|
|
90
|
+
fontFamily: 'dm, Menlo, Monaco, "Courier New", monospace',
|
|
91
|
+
fontSize: 15,
|
|
92
|
+
formatOnPaste: true,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
if (props.type === 'diff') {
|
|
96
|
+
vsDiffEditor = monaco.editor.createDiffEditor(codeEditor.value, options);
|
|
97
|
+
} else {
|
|
98
|
+
vsEditor = monaco.editor.create(codeEditor.value, options);
|
|
99
|
+
}
|
|
115
100
|
|
|
116
101
|
setEditorValue(props.initValues, props.modifiedValues);
|
|
117
102
|
|
|
118
103
|
loading.value = false;
|
|
119
104
|
|
|
120
|
-
emit('
|
|
105
|
+
emit('initd', vsEditor);
|
|
121
106
|
|
|
122
107
|
codeEditor.value.addEventListener('keydown', (e) => {
|
|
123
108
|
if (e.keyCode === 83 && (navigator.platform.match('Mac') ? e.metaKey : e.ctrlKey)) {
|
|
@@ -127,7 +112,7 @@ export default defineComponent({
|
|
|
127
112
|
});
|
|
128
113
|
|
|
129
114
|
if (props.type !== 'diff') {
|
|
130
|
-
vsEditor
|
|
115
|
+
vsEditor?.onDidBlurEditorWidget(() => {
|
|
131
116
|
emit('save', getEditorValue());
|
|
132
117
|
});
|
|
133
118
|
}
|
|
@@ -138,7 +123,7 @@ export default defineComponent({
|
|
|
138
123
|
watch(
|
|
139
124
|
() => props.initValues,
|
|
140
125
|
(v, preV) => {
|
|
141
|
-
if (
|
|
126
|
+
if (v !== preV) {
|
|
142
127
|
setEditorValue(props.initValues, props.modifiedValues);
|
|
143
128
|
}
|
|
144
129
|
},
|
|
@@ -151,17 +136,7 @@ export default defineComponent({
|
|
|
151
136
|
onMounted(async () => {
|
|
152
137
|
loading.value = true;
|
|
153
138
|
|
|
154
|
-
|
|
155
|
-
if (!(globalThis as any).monaco) {
|
|
156
|
-
const interval = setInterval(() => {
|
|
157
|
-
if ((globalThis as any).monaco) {
|
|
158
|
-
clearInterval(interval);
|
|
159
|
-
init();
|
|
160
|
-
}
|
|
161
|
-
}, 300);
|
|
162
|
-
} else {
|
|
163
|
-
init();
|
|
164
|
-
}
|
|
139
|
+
init();
|
|
165
140
|
});
|
|
166
141
|
|
|
167
142
|
onUnmounted(() => {
|
|
@@ -174,13 +149,14 @@ export default defineComponent({
|
|
|
174
149
|
codeEditor,
|
|
175
150
|
|
|
176
151
|
getEditor() {
|
|
177
|
-
return vsEditor;
|
|
152
|
+
return vsEditor || vsDiffEditor;
|
|
178
153
|
},
|
|
179
154
|
|
|
180
155
|
setEditorValue,
|
|
181
156
|
|
|
182
157
|
focus() {
|
|
183
|
-
vsEditor
|
|
158
|
+
vsEditor?.focus();
|
|
159
|
+
vsDiffEditor?.focus();
|
|
184
160
|
},
|
|
185
161
|
};
|
|
186
162
|
},
|
package/src/layouts/NavMenu.vue
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="m-editor-nav-menu" :style="{ height: `${height}px` }">
|
|
3
|
-
<div v-for="key in keys" :class="`menu-${key}`" :key="key">
|
|
3
|
+
<div v-for="key in keys" :class="`menu-${key}`" :key="key" :style="`width: ${columnWidth?.[key]}px`">
|
|
4
4
|
<tool-button :data="item" v-for="(item, index) in data[key]" :key="index"></tool-button>
|
|
5
5
|
</div>
|
|
6
6
|
</div>
|
|
7
7
|
</template>
|
|
8
8
|
|
|
9
9
|
<script lang="ts">
|
|
10
|
-
import { computed, defineComponent, PropType } from 'vue';
|
|
10
|
+
import { computed, defineComponent, inject, PropType } from 'vue';
|
|
11
11
|
|
|
12
12
|
import ToolButton from '@editor/components/ToolButton.vue';
|
|
13
|
-
import { MenuBarData } from '@editor/type';
|
|
13
|
+
import { GetColumnWidth, MenuBarData, Services } from '@editor/type';
|
|
14
14
|
|
|
15
15
|
export default defineComponent({
|
|
16
16
|
name: 'nav-menu',
|
|
@@ -29,8 +29,12 @@ export default defineComponent({
|
|
|
29
29
|
},
|
|
30
30
|
|
|
31
31
|
setup(props) {
|
|
32
|
+
const services = inject<Services>('services');
|
|
33
|
+
|
|
32
34
|
return {
|
|
33
35
|
keys: computed(() => Object.keys(props.data) as Array<keyof MenuBarData>),
|
|
36
|
+
|
|
37
|
+
columnWidth: computed(() => services?.uiService.get<GetColumnWidth>('columnWidth')),
|
|
34
38
|
};
|
|
35
39
|
},
|
|
36
40
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<m-form
|
|
3
3
|
class="m-editor-props-panel"
|
|
4
|
+
popper-class="m-editor-props-panel-popper"
|
|
4
5
|
ref="configForm"
|
|
5
6
|
size="small"
|
|
6
7
|
:init-values="values"
|
|
@@ -10,7 +11,7 @@
|
|
|
10
11
|
</template>
|
|
11
12
|
|
|
12
13
|
<script lang="ts">
|
|
13
|
-
import { defineComponent, getCurrentInstance, inject, onMounted, ref, watchEffect } from 'vue';
|
|
14
|
+
import { computed, defineComponent, getCurrentInstance, inject, onMounted, ref, watchEffect } from 'vue';
|
|
14
15
|
import { ElMessage } from 'element-plus';
|
|
15
16
|
|
|
16
17
|
import type { FormValue, MForm } from '@tmagic/form';
|
|
@@ -30,21 +31,16 @@ export default defineComponent({
|
|
|
30
31
|
// ts类型应该是FormConfig, 但是打包时会出错,所以暂时用any
|
|
31
32
|
const curFormConfig = ref<any>([]);
|
|
32
33
|
const services = inject<Services>('services');
|
|
34
|
+
const node = computed(() => services?.editorService.get<MNode | null>('node'));
|
|
33
35
|
|
|
34
36
|
const init = async () => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (!node) {
|
|
37
|
+
if (!node.value) {
|
|
38
38
|
curFormConfig.value = [];
|
|
39
39
|
return;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
values.value = node;
|
|
47
|
-
const type = node.type || (node.items ? 'container' : 'text');
|
|
42
|
+
values.value = node.value;
|
|
43
|
+
const type = node.value.type || (node.value.items ? 'container' : 'text');
|
|
48
44
|
curFormConfig.value = (await services?.propsService.getPropsConfig(type)) || [];
|
|
49
45
|
};
|
|
50
46
|
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
<div
|
|
4
4
|
v-if="node.items"
|
|
5
5
|
class="magic-editor-content-menu-item"
|
|
6
|
-
@mouseenter="
|
|
7
|
-
@mouseleave="
|
|
6
|
+
@mouseenter="setSubVisitable(true)"
|
|
7
|
+
@mouseleave="setSubVisitable(false)"
|
|
8
8
|
>
|
|
9
9
|
新增
|
|
10
10
|
</div>
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
>
|
|
17
17
|
删除
|
|
18
18
|
</div>
|
|
19
|
-
<div class="subMenu" v-show="subVisible" @mouseenter="
|
|
19
|
+
<div class="subMenu" v-show="subVisible" @mouseenter="setSubVisitable(true)" @mouseleave="setSubVisitable(false)">
|
|
20
20
|
<el-scrollbar>
|
|
21
21
|
<template v-if="node.type === 'tabs'">
|
|
22
22
|
<div
|
|
@@ -31,20 +31,8 @@
|
|
|
31
31
|
标签
|
|
32
32
|
</div>
|
|
33
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
34
|
<template v-else-if="node.items">
|
|
47
|
-
<div v-for="list in
|
|
35
|
+
<div v-for="list in componentGroupList" :key="list.title">
|
|
48
36
|
<template v-for="item in list.items">
|
|
49
37
|
<div class="magic-editor-content-menu-item" v-if="item" :key="item.type" @click="() => append(item)">
|
|
50
38
|
{{ item.text }}
|
|
@@ -59,21 +47,16 @@
|
|
|
59
47
|
</template>
|
|
60
48
|
|
|
61
49
|
<script lang="ts">
|
|
62
|
-
import { computed, defineComponent, inject,
|
|
50
|
+
import { computed, defineComponent, inject, ref } from 'vue';
|
|
51
|
+
|
|
52
|
+
import type { MNode } from '@tmagic/schema';
|
|
63
53
|
|
|
64
|
-
import type {
|
|
54
|
+
import type { AddMNode, Services } from '@editor/type';
|
|
65
55
|
|
|
66
56
|
export default defineComponent({
|
|
67
57
|
name: 'magic-editor-content-menu',
|
|
68
58
|
|
|
69
|
-
|
|
70
|
-
componentGroupList: {
|
|
71
|
-
type: Array as PropType<ComponentGroup[]>,
|
|
72
|
-
default: () => [],
|
|
73
|
-
},
|
|
74
|
-
},
|
|
75
|
-
|
|
76
|
-
setup(props) {
|
|
59
|
+
setup() {
|
|
77
60
|
const services = inject<Services>('services');
|
|
78
61
|
const subVisible = ref(false);
|
|
79
62
|
const node = computed(() => services?.editorService.get('node'));
|
|
@@ -83,17 +66,9 @@ export default defineComponent({
|
|
|
83
66
|
|
|
84
67
|
node,
|
|
85
68
|
|
|
86
|
-
|
|
87
|
-
app: [
|
|
88
|
-
{
|
|
89
|
-
type: 'page',
|
|
90
|
-
text: '页面',
|
|
91
|
-
},
|
|
92
|
-
],
|
|
93
|
-
component: props.componentGroupList,
|
|
94
|
-
})),
|
|
69
|
+
componentGroupList: computed(() => services?.componentListService.getList()),
|
|
95
70
|
|
|
96
|
-
append(config:
|
|
71
|
+
append(config: AddMNode) {
|
|
97
72
|
services?.editorService.add({
|
|
98
73
|
name: config.text,
|
|
99
74
|
type: config.type,
|
|
@@ -104,11 +79,11 @@ export default defineComponent({
|
|
|
104
79
|
node.value && services?.editorService.remove(node.value);
|
|
105
80
|
},
|
|
106
81
|
|
|
107
|
-
copy(node
|
|
108
|
-
services?.editorService.copy(node);
|
|
82
|
+
copy(node?: MNode) {
|
|
83
|
+
node && services?.editorService.copy(node);
|
|
109
84
|
},
|
|
110
85
|
|
|
111
|
-
|
|
86
|
+
setSubVisitable(v: boolean) {
|
|
112
87
|
subVisible.value = v;
|
|
113
88
|
},
|
|
114
89
|
};
|
|
@@ -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,11 +30,20 @@
|
|
|
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="toogleClickFlag"
|
|
37
|
+
@mouseup="toogleClickFlag"
|
|
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
|
|
|
@@ -46,20 +56,34 @@
|
|
|
46
56
|
<script lang="ts">
|
|
47
57
|
import { computed, defineComponent, inject, onMounted, 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) {
|
|
@@ -188,11 +217,25 @@ export default defineComponent({
|
|
|
188
217
|
setup() {
|
|
189
218
|
const services = inject<Services>('services');
|
|
190
219
|
const tree = ref<InstanceType<typeof ElTree>>();
|
|
220
|
+
const clicked = ref(false);
|
|
191
221
|
const editorService = services?.editorService;
|
|
222
|
+
const highlightHandler = throttle((data: MNode) => {
|
|
223
|
+
highlight(data, editorService);
|
|
224
|
+
}, throttleTime);
|
|
225
|
+
|
|
226
|
+
const toogleClickFlag = () => {
|
|
227
|
+
clicked.value = !clicked.value;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const statusData = useStatus(tree, editorService);
|
|
231
|
+
const canHighlight = computed(
|
|
232
|
+
() => statusData.highlightNode.value?.id !== statusData.clickNode.value?.id && !clicked.value,
|
|
233
|
+
);
|
|
234
|
+
|
|
192
235
|
return {
|
|
193
236
|
tree,
|
|
237
|
+
...statusData,
|
|
194
238
|
...useDrop(tree, editorService),
|
|
195
|
-
...useStatus(tree, editorService),
|
|
196
239
|
...useFilter(tree),
|
|
197
240
|
...useContentMenu(editorService),
|
|
198
241
|
|
|
@@ -201,8 +244,12 @@ export default defineComponent({
|
|
|
201
244
|
document.dispatchEvent(new CustomEvent('ui-select', { detail: data }));
|
|
202
245
|
return;
|
|
203
246
|
}
|
|
247
|
+
tree.value?.setCurrentKey(data.id);
|
|
204
248
|
select(data, editorService);
|
|
205
249
|
},
|
|
250
|
+
highlightHandler,
|
|
251
|
+
toogleClickFlag,
|
|
252
|
+
canHighlight,
|
|
206
253
|
};
|
|
207
254
|
},
|
|
208
255
|
});
|