@tmagic/editor 1.3.0-beta.2 → 1.3.0-beta.4
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 +52 -60
- package/dist/tmagic-editor.js +1634 -1065
- package/dist/tmagic-editor.js.map +1 -1
- package/dist/tmagic-editor.umd.cjs +1659 -1087
- package/dist/tmagic-editor.umd.cjs.map +1 -1
- package/package.json +10 -10
- package/src/Editor.vue +70 -65
- package/src/components/ContentMenu.vue +5 -1
- package/src/components/SearchInput.vue +5 -1
- package/src/editorProps.ts +51 -129
- package/src/fields/Code.vue +9 -3
- package/src/fields/DataSourceFieldSelect.vue +28 -16
- package/src/fields/DataSourceFields.vue +95 -7
- package/src/fields/DataSourceMethodSelect.vue +23 -14
- package/src/fields/DataSourceMocks.vue +237 -0
- package/src/fields/EventSelect.vue +18 -11
- package/src/index.ts +4 -1
- package/src/initService.ts +50 -20
- package/src/layouts/CodeEditor.vue +20 -4
- package/src/layouts/Framework.vue +3 -1
- package/src/layouts/PropsPanel.vue +3 -1
- package/src/layouts/sidebar/ComponentListPanel.vue +18 -8
- package/src/layouts/sidebar/Sidebar.vue +7 -10
- package/src/layouts/sidebar/code-block/CodeBlockList.vue +3 -1
- package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +3 -1
- package/src/layouts/sidebar/layer/LayerNode.vue +206 -0
- package/src/layouts/sidebar/layer/LayerNodeTool.vue +32 -0
- package/src/layouts/sidebar/layer/LayerPanel.vue +85 -0
- package/src/layouts/sidebar/layer/use-drag.ts +157 -0
- package/src/layouts/sidebar/layer/use-filter.ts +70 -0
- package/src/layouts/sidebar/layer/use-keybinding.ts +47 -0
- package/src/layouts/sidebar/layer/use-node-status.ts +103 -0
- package/src/layouts/workspace/Workspace.vue +3 -1
- package/src/layouts/workspace/viewer/NodeListMenu.vue +1 -1
- package/src/layouts/workspace/viewer/Stage.vue +19 -17
- package/src/services/dataSource.ts +10 -0
- package/src/services/editor.ts +45 -5
- package/src/theme/common/var.scss +2 -2
- package/src/theme/framework.scss +2 -0
- package/src/theme/layer-panel.scss +63 -63
- package/src/theme/props-panel.scss +8 -0
- package/src/theme/sidebar.scss +1 -5
- package/src/type.ts +60 -0
- package/src/utils/data-source/index.ts +11 -0
- package/src/utils/dep.ts +26 -11
- package/src/utils/editor.ts +14 -3
- package/src/utils/props.ts +3 -0
- package/types/editorProps.d.ts +61 -107
- package/types/fields/Code.vue.d.ts +10 -6
- package/types/fields/DataSourceMocks.vue.d.ts +32 -0
- package/types/index.d.ts +2 -1
- package/types/initService.d.ts +5 -4
- package/types/layouts/CodeEditor.vue.d.ts +5 -0
- package/types/layouts/Framework.vue.d.ts +2 -12
- package/types/layouts/PropsPanel.vue.d.ts +2 -3
- package/types/layouts/sidebar/ComponentListPanel.vue.d.ts +2 -7
- package/types/layouts/sidebar/Sidebar.vue.d.ts +2 -17
- package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +2 -7
- package/types/layouts/sidebar/code-block/CodeBlockListPanel.vue.d.ts +2 -9
- package/types/layouts/sidebar/{LayerMenu.vue.d.ts → layer/LayerMenu.vue.d.ts} +1 -1
- package/types/layouts/sidebar/layer/LayerNode.vue.d.ts +50 -0
- package/types/layouts/sidebar/{LayerNode.vue.d.ts → layer/LayerNodeTool.vue.d.ts} +1 -1
- package/types/layouts/sidebar/{LayerPanel.vue.d.ts → layer/LayerPanel.vue.d.ts} +2 -8
- package/types/layouts/sidebar/layer/use-drag.d.ts +14 -0
- package/types/layouts/sidebar/layer/use-filter.d.ts +8 -0
- package/types/layouts/sidebar/layer/use-keybinding.d.ts +4 -0
- package/types/layouts/sidebar/layer/use-node-status.d.ts +7 -0
- package/types/layouts/workspace/Workspace.vue.d.ts +2 -11
- package/types/layouts/workspace/viewer/Stage.vue.d.ts +1 -1
- package/types/services/dataSource.d.ts +3 -0
- package/types/services/editor.d.ts +1 -0
- package/types/type.d.ts +60 -0
- package/types/utils/dep.d.ts +1 -1
- package/types/utils/editor.d.ts +3 -2
- package/src/layouts/sidebar/LayerNode.vue +0 -40
- package/src/layouts/sidebar/LayerPanel.vue +0 -369
- package/types/Editor.vue.d.ts +0 -233
- /package/src/layouts/sidebar/{LayerMenu.vue → layer/LayerMenu.vue} +0 -0
package/src/initService.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { ExtractPropTypes } from 'vue';
|
|
2
1
|
import { onUnmounted, toRaw, watch } from 'vue';
|
|
3
2
|
import { cloneDeep } from 'lodash-es';
|
|
4
3
|
|
|
@@ -6,6 +5,7 @@ import type { EventOption } from '@tmagic/core';
|
|
|
6
5
|
import type { CodeBlockContent, DataSourceSchema, Id, MApp, MNode, MPage } from '@tmagic/schema';
|
|
7
6
|
import { getNodes } from '@tmagic/utils';
|
|
8
7
|
|
|
8
|
+
import PropsPanel from './layouts/PropsPanel.vue';
|
|
9
9
|
import type { Target } from './services/dep';
|
|
10
10
|
import {
|
|
11
11
|
createCodeBlockTarget,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
createDataSourceMethodTarget,
|
|
14
14
|
createDataSourceTarget,
|
|
15
15
|
} from './utils/dep';
|
|
16
|
-
import
|
|
16
|
+
import { EditorProps } from './editorProps';
|
|
17
17
|
import { DepTargetType, Services } from './type';
|
|
18
18
|
|
|
19
19
|
export declare type LooseRequired<T> = {
|
|
@@ -21,7 +21,7 @@ export declare type LooseRequired<T> = {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
export const initServiceState = (
|
|
24
|
-
props:
|
|
24
|
+
props: EditorProps,
|
|
25
25
|
{
|
|
26
26
|
editorService,
|
|
27
27
|
historyService,
|
|
@@ -38,7 +38,7 @@ export const initServiceState = (
|
|
|
38
38
|
watch(
|
|
39
39
|
() => props.modelValue,
|
|
40
40
|
(modelValue) => {
|
|
41
|
-
editorService.set('root', modelValue);
|
|
41
|
+
editorService.set('root', modelValue || null);
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
44
|
immediate: true,
|
|
@@ -47,7 +47,7 @@ export const initServiceState = (
|
|
|
47
47
|
|
|
48
48
|
watch(
|
|
49
49
|
() => props.componentGroupList,
|
|
50
|
-
(componentGroupList) => componentListService.setList(componentGroupList),
|
|
50
|
+
(componentGroupList) => componentGroupList && componentListService.setList(componentGroupList),
|
|
51
51
|
{
|
|
52
52
|
immediate: true,
|
|
53
53
|
},
|
|
@@ -55,7 +55,7 @@ export const initServiceState = (
|
|
|
55
55
|
|
|
56
56
|
watch(
|
|
57
57
|
() => props.datasourceList,
|
|
58
|
-
(datasourceList) => dataSourceService.set('datasourceTypeList', datasourceList),
|
|
58
|
+
(datasourceList) => datasourceList && dataSourceService.set('datasourceTypeList', datasourceList),
|
|
59
59
|
{
|
|
60
60
|
immediate: true,
|
|
61
61
|
},
|
|
@@ -63,7 +63,7 @@ export const initServiceState = (
|
|
|
63
63
|
|
|
64
64
|
watch(
|
|
65
65
|
() => props.propsConfigs,
|
|
66
|
-
(configs) => propsService.setPropsConfigs(configs),
|
|
66
|
+
(configs) => configs && propsService.setPropsConfigs(configs),
|
|
67
67
|
{
|
|
68
68
|
immediate: true,
|
|
69
69
|
},
|
|
@@ -71,7 +71,7 @@ export const initServiceState = (
|
|
|
71
71
|
|
|
72
72
|
watch(
|
|
73
73
|
() => props.propsValues,
|
|
74
|
-
(values) => propsService.setPropsValues(values),
|
|
74
|
+
(values) => values && propsService.setPropsValues(values),
|
|
75
75
|
{
|
|
76
76
|
immediate: true,
|
|
77
77
|
},
|
|
@@ -83,10 +83,11 @@ export const initServiceState = (
|
|
|
83
83
|
const eventsList: Record<string, EventOption[]> = {};
|
|
84
84
|
const methodsList: Record<string, EventOption[]> = {};
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
eventMethodList &&
|
|
87
|
+
Object.keys(eventMethodList).forEach((type: string) => {
|
|
88
|
+
eventsList[type] = eventMethodList[type].events;
|
|
89
|
+
methodsList[type] = eventMethodList[type].methods;
|
|
90
|
+
});
|
|
90
91
|
|
|
91
92
|
eventsService.setEvents(eventsList);
|
|
92
93
|
eventsService.setMethods(methodsList);
|
|
@@ -99,9 +100,10 @@ export const initServiceState = (
|
|
|
99
100
|
watch(
|
|
100
101
|
() => props.datasourceConfigs,
|
|
101
102
|
(configs) => {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
configs &&
|
|
104
|
+
Object.entries(configs).forEach(([key, value]) => {
|
|
105
|
+
dataSourceService.setFormConfig(key, value);
|
|
106
|
+
});
|
|
105
107
|
},
|
|
106
108
|
{
|
|
107
109
|
immediate: true,
|
|
@@ -111,8 +113,33 @@ export const initServiceState = (
|
|
|
111
113
|
watch(
|
|
112
114
|
() => props.datasourceValues,
|
|
113
115
|
(values) => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
+
values &&
|
|
117
|
+
Object.entries(values).forEach(([key, value]) => {
|
|
118
|
+
dataSourceService.setFormValue(key, value);
|
|
119
|
+
});
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
immediate: true,
|
|
123
|
+
},
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
watch(
|
|
127
|
+
() => props.datasourceEventMethodList,
|
|
128
|
+
(eventMethodList) => {
|
|
129
|
+
const eventsList: Record<string, EventOption[]> = {};
|
|
130
|
+
const methodsList: Record<string, EventOption[]> = {};
|
|
131
|
+
|
|
132
|
+
eventMethodList &&
|
|
133
|
+
Object.keys(eventMethodList).forEach((type: string) => {
|
|
134
|
+
eventsList[type] = eventMethodList[type].events;
|
|
135
|
+
methodsList[type] = eventMethodList[type].methods;
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
Object.entries(eventsList).forEach(([key, value]) => {
|
|
139
|
+
dataSourceService.setFormEvent(key, value);
|
|
140
|
+
});
|
|
141
|
+
Object.entries(methodsList).forEach(([key, value]) => {
|
|
142
|
+
dataSourceService.setFormMethod(key, value);
|
|
116
143
|
});
|
|
117
144
|
},
|
|
118
145
|
{
|
|
@@ -148,8 +175,9 @@ export const initServiceState = (
|
|
|
148
175
|
};
|
|
149
176
|
|
|
150
177
|
export const initServiceEvents = (
|
|
151
|
-
props:
|
|
152
|
-
emit: (event: 'props-panel-mounted'
|
|
178
|
+
props: EditorProps,
|
|
179
|
+
emit: ((event: 'props-panel-mounted', instance: InstanceType<typeof PropsPanel>) => void) &
|
|
180
|
+
((event: 'update:modelValue', value: MApp | null) => void),
|
|
153
181
|
{ editorService, codeBlockService, dataSourceService, depService }: Services,
|
|
154
182
|
) => {
|
|
155
183
|
const getApp = () => {
|
|
@@ -247,7 +275,7 @@ export const initServiceEvents = (
|
|
|
247
275
|
depService.addTarget(createDataSourceCondTarget(ds.id));
|
|
248
276
|
};
|
|
249
277
|
|
|
250
|
-
const rootChangeHandler = async (value: MApp, preValue?: MApp | null) => {
|
|
278
|
+
const rootChangeHandler = async (value: MApp | null, preValue?: MApp | null) => {
|
|
251
279
|
const nodeId = editorService.get('node')?.id || props.defaultSelected;
|
|
252
280
|
let node;
|
|
253
281
|
if (nodeId) {
|
|
@@ -268,6 +296,8 @@ export const initServiceEvents = (
|
|
|
268
296
|
emit('update:modelValue', value);
|
|
269
297
|
}
|
|
270
298
|
|
|
299
|
+
if (!value) return;
|
|
300
|
+
|
|
271
301
|
value.codeBlocks = value.codeBlocks || {};
|
|
272
302
|
value.dataSources = value.dataSources || [];
|
|
273
303
|
|
|
@@ -27,6 +27,8 @@ import serialize from 'serialize-javascript';
|
|
|
27
27
|
|
|
28
28
|
import { TMagicButton } from '@tmagic/design';
|
|
29
29
|
|
|
30
|
+
import { getConfig } from '@editor/utils/config';
|
|
31
|
+
|
|
30
32
|
defineOptions({
|
|
31
33
|
name: 'MEditorCodeEditor',
|
|
32
34
|
});
|
|
@@ -42,6 +44,7 @@ const props = withDefaults(
|
|
|
42
44
|
};
|
|
43
45
|
height?: string;
|
|
44
46
|
autoSave?: boolean;
|
|
47
|
+
parse?: boolean;
|
|
45
48
|
}>(),
|
|
46
49
|
{
|
|
47
50
|
autoSave: true,
|
|
@@ -49,6 +52,7 @@ const props = withDefaults(
|
|
|
49
52
|
options: () => ({
|
|
50
53
|
tabSize: 2,
|
|
51
54
|
}),
|
|
55
|
+
parse: false,
|
|
52
56
|
},
|
|
53
57
|
);
|
|
54
58
|
|
|
@@ -57,7 +61,7 @@ const emit = defineEmits(['initd', 'save']);
|
|
|
57
61
|
const toString = (v: string | any, language: string): string => {
|
|
58
62
|
let value = '';
|
|
59
63
|
if (typeof v !== 'string') {
|
|
60
|
-
if (
|
|
64
|
+
if (language === 'json') {
|
|
61
65
|
value = JSON.stringify(v, null, 2);
|
|
62
66
|
} else {
|
|
63
67
|
value = serialize(v, {
|
|
@@ -74,6 +78,18 @@ const toString = (v: string | any, language: string): string => {
|
|
|
74
78
|
return value;
|
|
75
79
|
};
|
|
76
80
|
|
|
81
|
+
const parse = (v: string | any, language: string): any => {
|
|
82
|
+
if (typeof v !== 'string') {
|
|
83
|
+
return v;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (language === 'json') {
|
|
87
|
+
return JSON.parse(v);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return getConfig('parseDSL')(v);
|
|
91
|
+
};
|
|
92
|
+
|
|
77
93
|
let vsEditor: monaco.editor.IStandaloneCodeEditor | null = null;
|
|
78
94
|
let vsDiffEditor: monaco.editor.IStandaloneDiffEditor | null = null;
|
|
79
95
|
|
|
@@ -89,7 +105,7 @@ const resizeObserver = new globalThis.ResizeObserver(
|
|
|
89
105
|
);
|
|
90
106
|
|
|
91
107
|
const setEditorValue = (v: string | any, m: string | any) => {
|
|
92
|
-
values.value = toString(v, props.language);
|
|
108
|
+
values.value = toString(v, props.language.toLocaleLowerCase());
|
|
93
109
|
|
|
94
110
|
if (props.type === 'diff') {
|
|
95
111
|
const originalModel = monaco.editor.createModel(values.value, 'text/javascript');
|
|
@@ -135,7 +151,7 @@ const init = async () => {
|
|
|
135
151
|
e.stopPropagation();
|
|
136
152
|
const newValue = getEditorValue();
|
|
137
153
|
values.value = newValue;
|
|
138
|
-
emit('save', newValue);
|
|
154
|
+
emit('save', props.parse ? parse(newValue, props.language) : newValue);
|
|
139
155
|
}
|
|
140
156
|
});
|
|
141
157
|
|
|
@@ -144,7 +160,7 @@ const init = async () => {
|
|
|
144
160
|
const newValue = getEditorValue();
|
|
145
161
|
if (values.value !== newValue) {
|
|
146
162
|
values.value = newValue;
|
|
147
|
-
emit('save', newValue);
|
|
163
|
+
emit('save', props.parse ? parse(newValue, props.language) : newValue);
|
|
148
164
|
}
|
|
149
165
|
});
|
|
150
166
|
}
|
|
@@ -51,12 +51,14 @@ import { computed, inject, ref, watch } from 'vue';
|
|
|
51
51
|
import { TMagicScrollbar } from '@tmagic/design';
|
|
52
52
|
|
|
53
53
|
import SplitView from '@editor/components/SplitView.vue';
|
|
54
|
-
import type { GetColumnWidth, Services } from '@editor/type';
|
|
54
|
+
import type { FrameworkSlots, GetColumnWidth, Services } from '@editor/type';
|
|
55
55
|
import { getConfig } from '@editor/utils/config';
|
|
56
56
|
|
|
57
57
|
import AddPageBox from './AddPageBox.vue';
|
|
58
58
|
import CodeEditor from './CodeEditor.vue';
|
|
59
59
|
|
|
60
|
+
defineSlots<FrameworkSlots>();
|
|
61
|
+
|
|
60
62
|
defineOptions({
|
|
61
63
|
name: 'MEditorFramework',
|
|
62
64
|
});
|
|
@@ -21,7 +21,9 @@ import { tMagicMessage } from '@tmagic/design';
|
|
|
21
21
|
import type { FormState, FormValue } from '@tmagic/form';
|
|
22
22
|
import { MForm } from '@tmagic/form';
|
|
23
23
|
|
|
24
|
-
import type { Services } from '@editor/type';
|
|
24
|
+
import type { PropsPanelSlots, Services } from '@editor/type';
|
|
25
|
+
|
|
26
|
+
defineSlots<PropsPanelSlots>();
|
|
25
27
|
|
|
26
28
|
defineOptions({
|
|
27
29
|
name: 'MEditorPropsPanel',
|
|
@@ -39,7 +39,16 @@ import { removeClassNameByClassName } from '@tmagic/utils';
|
|
|
39
39
|
|
|
40
40
|
import MIcon from '@editor/components/Icon.vue';
|
|
41
41
|
import SearchInput from '@editor/components/SearchInput.vue';
|
|
42
|
-
import
|
|
42
|
+
import {
|
|
43
|
+
type ComponentGroup,
|
|
44
|
+
type ComponentItem,
|
|
45
|
+
ComponentListPanelSlots,
|
|
46
|
+
DragType,
|
|
47
|
+
type Services,
|
|
48
|
+
type StageOptions,
|
|
49
|
+
} from '@editor/type';
|
|
50
|
+
|
|
51
|
+
defineSlots<ComponentListPanelSlots>();
|
|
43
52
|
|
|
44
53
|
defineOptions({
|
|
45
54
|
name: 'MEditorComponentListPanel',
|
|
@@ -80,16 +89,17 @@ const appendComponent = ({ text, type, data = {} }: ComponentItem): void => {
|
|
|
80
89
|
};
|
|
81
90
|
|
|
82
91
|
const dragstartHandler = ({ text, type, data = {} }: ComponentItem, e: DragEvent) => {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
e.dataTransfer?.setData(
|
|
93
|
+
'text/json',
|
|
94
|
+
serialize({
|
|
95
|
+
dragType: DragType.COMPONENT_LIST,
|
|
96
|
+
data: {
|
|
87
97
|
name: text,
|
|
88
98
|
type,
|
|
89
99
|
...data,
|
|
90
|
-
}
|
|
91
|
-
)
|
|
92
|
-
|
|
100
|
+
},
|
|
101
|
+
}),
|
|
102
|
+
);
|
|
93
103
|
};
|
|
94
104
|
|
|
95
105
|
const dragendHandler = () => {
|
|
@@ -58,16 +58,11 @@
|
|
|
58
58
|
</template>
|
|
59
59
|
|
|
60
60
|
<template
|
|
61
|
-
#layer-node-content="{ data: nodeData
|
|
61
|
+
#layer-node-content="{ data: nodeData }"
|
|
62
62
|
v-if="config.$key === 'layer' || config.slots?.layerNodeContent"
|
|
63
63
|
>
|
|
64
|
-
<slot v-if="config.$key === 'layer'" name="layer-node-content" :data="nodeData"
|
|
65
|
-
<component
|
|
66
|
-
v-else-if="config.slots?.layerNodeContent"
|
|
67
|
-
:is="config.slots.layerNodeContent"
|
|
68
|
-
:data="nodeData"
|
|
69
|
-
:node="node"
|
|
70
|
-
/>
|
|
64
|
+
<slot v-if="config.$key === 'layer'" name="layer-node-content" :data="nodeData"></slot>
|
|
65
|
+
<component v-else-if="config.slots?.layerNodeContent" :is="config.slots.layerNodeContent" :data="nodeData" />
|
|
71
66
|
</template>
|
|
72
67
|
</component>
|
|
73
68
|
</div>
|
|
@@ -79,13 +74,15 @@ import { computed, ref, watch } from 'vue';
|
|
|
79
74
|
import { Coin, EditPen, Goods, List } from '@element-plus/icons-vue';
|
|
80
75
|
|
|
81
76
|
import MIcon from '@editor/components/Icon.vue';
|
|
82
|
-
import type { MenuButton, MenuComponent, SideComponent, SideItem } from '@editor/type';
|
|
77
|
+
import type { MenuButton, MenuComponent, SidebarSlots, SideComponent, SideItem } from '@editor/type';
|
|
83
78
|
import { SideBarData } from '@editor/type';
|
|
84
79
|
|
|
85
80
|
import CodeBlockListPanel from './code-block/CodeBlockListPanel.vue';
|
|
86
81
|
import DataSourceListPanel from './data-source/DataSourceListPanel.vue';
|
|
82
|
+
import LayerPanel from './layer/LayerPanel.vue';
|
|
87
83
|
import ComponentListPanel from './ComponentListPanel.vue';
|
|
88
|
-
|
|
84
|
+
|
|
85
|
+
defineSlots<SidebarSlots>();
|
|
89
86
|
|
|
90
87
|
defineOptions({
|
|
91
88
|
name: 'MEditorSidebar',
|
|
@@ -50,7 +50,9 @@ import type { Id } from '@tmagic/schema';
|
|
|
50
50
|
import Icon from '@editor/components/Icon.vue';
|
|
51
51
|
import AppManageIcon from '@editor/icons/AppManageIcon.vue';
|
|
52
52
|
import CodeIcon from '@editor/icons/CodeIcon.vue';
|
|
53
|
-
import { CodeDeleteErrorType, CodeDslItem, DepTargetType, Services } from '@editor/type';
|
|
53
|
+
import { CodeBlockListSlots, CodeDeleteErrorType, CodeDslItem, DepTargetType, Services } from '@editor/type';
|
|
54
|
+
|
|
55
|
+
defineSlots<CodeBlockListSlots>();
|
|
54
56
|
|
|
55
57
|
defineOptions({
|
|
56
58
|
name: 'MEditorCodeBlockList',
|
|
@@ -37,10 +37,12 @@ import type { Id } from '@tmagic/schema';
|
|
|
37
37
|
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
|
|
38
38
|
import SearchInput from '@editor/components/SearchInput.vue';
|
|
39
39
|
import { useCodeBlockEdit } from '@editor/hooks/use-code-block-edit';
|
|
40
|
-
import type { CodeDeleteErrorType, Services } from '@editor/type';
|
|
40
|
+
import type { CodeBlockListPanelSlots, CodeDeleteErrorType, Services } from '@editor/type';
|
|
41
41
|
|
|
42
42
|
import CodeBlockList from './CodeBlockList.vue';
|
|
43
43
|
|
|
44
|
+
defineSlots<CodeBlockListPanelSlots>();
|
|
45
|
+
|
|
44
46
|
defineOptions({
|
|
45
47
|
name: 'MEditorCodeBlockListPanel',
|
|
46
48
|
});
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-show="visible"
|
|
4
|
+
class="magic-editor-layer-node"
|
|
5
|
+
ref="nodeEl"
|
|
6
|
+
:draggable="!isPage(data)"
|
|
7
|
+
:data-node-id="data.id"
|
|
8
|
+
:data-is-container="Array.isArray(data.items)"
|
|
9
|
+
@dragstart="handleDragStart"
|
|
10
|
+
@dragleave="handleDragLeave"
|
|
11
|
+
@dragend="handleDragEnd($event, data)"
|
|
12
|
+
>
|
|
13
|
+
<div
|
|
14
|
+
class="layer-node"
|
|
15
|
+
:class="{ selected, expanded }"
|
|
16
|
+
:style="`padding-left: ${indent}px`"
|
|
17
|
+
@contextmenu="contextmenuHandler"
|
|
18
|
+
@mouseenter="highlightHandler()"
|
|
19
|
+
>
|
|
20
|
+
<MIcon
|
|
21
|
+
class="expand-icon"
|
|
22
|
+
:style="hasChilren ? '' : 'color: transparent; cursor: default'"
|
|
23
|
+
:icon="expanded ? ArrowDown : ArrowRight"
|
|
24
|
+
@click="expandHandler"
|
|
25
|
+
></MIcon>
|
|
26
|
+
|
|
27
|
+
<div class="layer-node-content" @click="nodeClickHandler">
|
|
28
|
+
<slot name="layer-node-content" :data="data">
|
|
29
|
+
<div class="layer-node-label">{{ `${data.name} (${data.id})` }}</div>
|
|
30
|
+
<div class="layer-node-tool">
|
|
31
|
+
<LayerNodeTool :data="data"></LayerNodeTool>
|
|
32
|
+
</div>
|
|
33
|
+
</slot>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<div v-if="hasChilren && expanded" class="magic-editor-layer-node-children">
|
|
38
|
+
<LayerNode
|
|
39
|
+
v-for="item in data.items"
|
|
40
|
+
:data="item"
|
|
41
|
+
:parent="(data as MContainer)"
|
|
42
|
+
:key="item.id"
|
|
43
|
+
:indent="indent + 11"
|
|
44
|
+
:filter-text="filterText"
|
|
45
|
+
:is-ctrl-key-down="isCtrlKeyDown"
|
|
46
|
+
@node-contextmenu="nodeContentmenuHandler"
|
|
47
|
+
>
|
|
48
|
+
<template #layer-node-content="{ data: nodeData }">
|
|
49
|
+
<slot name="layer-node-content" :data="nodeData"> </slot>
|
|
50
|
+
</template>
|
|
51
|
+
</LayerNode>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
</template>
|
|
55
|
+
|
|
56
|
+
<script lang="ts" setup>
|
|
57
|
+
import { computed, type ComputedRef, inject, nextTick, ref } from 'vue';
|
|
58
|
+
import { ArrowDown, ArrowRight } from '@element-plus/icons-vue';
|
|
59
|
+
import { throttle } from 'lodash-es';
|
|
60
|
+
|
|
61
|
+
import type { Id, MContainer, MNode } from '@tmagic/schema';
|
|
62
|
+
import { isPage } from '@tmagic/utils';
|
|
63
|
+
|
|
64
|
+
import MIcon from '@editor/components/Icon.vue';
|
|
65
|
+
import type { LayerNodeSlots, LayerNodeStatus, Services } from '@editor/type';
|
|
66
|
+
|
|
67
|
+
import LayerNodeTool from './LayerNodeTool.vue';
|
|
68
|
+
import { useDrag } from './use-drag';
|
|
69
|
+
import { updateStatus } from './use-filter';
|
|
70
|
+
|
|
71
|
+
defineSlots<LayerNodeSlots>();
|
|
72
|
+
|
|
73
|
+
defineOptions({
|
|
74
|
+
name: 'MEditorLayerNode',
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const props = withDefaults(
|
|
78
|
+
defineProps<{
|
|
79
|
+
data: MNode;
|
|
80
|
+
parent?: MContainer;
|
|
81
|
+
indent?: number;
|
|
82
|
+
filterText?: string;
|
|
83
|
+
isCtrlKeyDown?: boolean;
|
|
84
|
+
}>(),
|
|
85
|
+
{
|
|
86
|
+
indent: 0,
|
|
87
|
+
filterText: '',
|
|
88
|
+
isCtrlKeyDown: false,
|
|
89
|
+
},
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
const emit = defineEmits<{
|
|
93
|
+
'node-contextmenu': [event: MouseEvent, node: MNode];
|
|
94
|
+
}>();
|
|
95
|
+
|
|
96
|
+
const services = inject<Services>('services');
|
|
97
|
+
const nodeStatusMap = inject<ComputedRef<Map<Id, LayerNodeStatus>>>('nodeStatusMap');
|
|
98
|
+
const editorService = services?.editorService;
|
|
99
|
+
const uiService = services?.uiService;
|
|
100
|
+
|
|
101
|
+
const nodeStatus = computed(
|
|
102
|
+
() =>
|
|
103
|
+
nodeStatusMap?.value?.get(props.data.id) || {
|
|
104
|
+
selected: false,
|
|
105
|
+
expand: false,
|
|
106
|
+
visible: false,
|
|
107
|
+
},
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const expanded = computed(() => nodeStatus.value.expand);
|
|
111
|
+
const selected = computed(() => nodeStatus.value.selected);
|
|
112
|
+
const visible = computed(() => nodeStatus.value.visible);
|
|
113
|
+
|
|
114
|
+
const hasChilren = computed(() => props.data.items?.length > 0);
|
|
115
|
+
|
|
116
|
+
const nodeEl = ref<HTMLDivElement>();
|
|
117
|
+
const { handleDragStart, handleDragEnd, handleDragLeave } = useDrag(services);
|
|
118
|
+
|
|
119
|
+
const expandHandler = () => {
|
|
120
|
+
if (!nodeStatusMap?.value) return;
|
|
121
|
+
|
|
122
|
+
updateStatus(nodeStatusMap.value, props.data.id, {
|
|
123
|
+
expand: !expanded.value,
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const nodeClickHandler = () => {
|
|
128
|
+
if (!nodeStatusMap?.value) return;
|
|
129
|
+
|
|
130
|
+
if (uiService?.get('uiSelectMode')) {
|
|
131
|
+
document.dispatchEvent(new CustomEvent('ui-select', { detail: props.data }));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (hasChilren.value && !props.isCtrlKeyDown) {
|
|
136
|
+
updateStatus(nodeStatusMap.value, props.data.id, {
|
|
137
|
+
expand: true,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
nextTick(() => {
|
|
142
|
+
select(props.data);
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const contextmenuHandler = (event: MouseEvent) => {
|
|
147
|
+
event.preventDefault();
|
|
148
|
+
|
|
149
|
+
nodeClickHandler();
|
|
150
|
+
|
|
151
|
+
emit('node-contextmenu', event, props.data);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const nodeContentmenuHandler = (event: MouseEvent, node: MNode) => {
|
|
155
|
+
emit('node-contextmenu', event, node);
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// 触发画布单选
|
|
159
|
+
const select = async (data: MNode) => {
|
|
160
|
+
if (!data.id) {
|
|
161
|
+
throw new Error('没有id');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (props.isCtrlKeyDown) {
|
|
165
|
+
multiSelect(data);
|
|
166
|
+
} else {
|
|
167
|
+
await editorService?.select(data);
|
|
168
|
+
editorService?.get('stage')?.select(data.id);
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const multiSelect = async (data: MNode) => {
|
|
173
|
+
const nodes = editorService?.get('nodes') || [];
|
|
174
|
+
|
|
175
|
+
const newNodes: Id[] = [];
|
|
176
|
+
let isCancel = false;
|
|
177
|
+
nodes.forEach((node) => {
|
|
178
|
+
if (node.id === data.id) {
|
|
179
|
+
isCancel = true;
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
newNodes.push(node.id);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// 只剩一个不能取消选中
|
|
187
|
+
if (!isCancel || newNodes.length === 0) {
|
|
188
|
+
newNodes.push(data.id);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
await editorService?.multiSelect(newNodes);
|
|
192
|
+
editorService?.get('stage')?.multiSelect(newNodes);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const throttleTime = 300;
|
|
196
|
+
// 鼠标在组件树移动触发高亮
|
|
197
|
+
const highlightHandler = throttle(() => {
|
|
198
|
+
highlight();
|
|
199
|
+
}, throttleTime);
|
|
200
|
+
|
|
201
|
+
// 触发画布高亮
|
|
202
|
+
const highlight = () => {
|
|
203
|
+
editorService?.highlight(props.data);
|
|
204
|
+
editorService?.get('stage')?.highlight(props.data.id);
|
|
205
|
+
};
|
|
206
|
+
</script>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<template v-if="data.type !== 'page'">
|
|
3
|
+
<MIcon v-if="data.visible === false" :icon="Hide" @click.stop="setNodeVisible(true)" title="点击显示"></MIcon>
|
|
4
|
+
<MIcon v-else :icon="View" @click.stop="setNodeVisible(false)" class="node-lock" title="点击隐藏"></MIcon>
|
|
5
|
+
</template>
|
|
6
|
+
</template>
|
|
7
|
+
|
|
8
|
+
<script setup lang="ts">
|
|
9
|
+
import { inject } from 'vue';
|
|
10
|
+
import { Hide, View } from '@element-plus/icons-vue';
|
|
11
|
+
|
|
12
|
+
import type { MNode } from '@tmagic/schema';
|
|
13
|
+
|
|
14
|
+
import MIcon from '@editor/components/Icon.vue';
|
|
15
|
+
import { Services } from '@editor/type';
|
|
16
|
+
|
|
17
|
+
const props = defineProps<{
|
|
18
|
+
data: MNode;
|
|
19
|
+
}>();
|
|
20
|
+
|
|
21
|
+
const services = inject<Services>('services');
|
|
22
|
+
const editorService = services?.editorService;
|
|
23
|
+
|
|
24
|
+
const setNodeVisible = (visible: boolean) => {
|
|
25
|
+
if (!editorService) return;
|
|
26
|
+
|
|
27
|
+
editorService.update({
|
|
28
|
+
id: props.data.id,
|
|
29
|
+
visible,
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
</script>
|