@tmagic/editor 1.5.0-beta.9 → 1.5.1
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 → tmagic-editor.css} +228 -22
- package/dist/tmagic-editor.js +1670 -1012
- package/dist/tmagic-editor.umd.cjs +1705 -1048
- package/package.json +14 -12
- package/src/Editor.vue +24 -6
- package/src/components/CodeBlockEditor.vue +14 -8
- package/src/components/CodeParams.vue +5 -5
- package/src/components/ContentMenu.vue +18 -18
- package/src/components/FloatingBox.vue +3 -3
- package/src/components/Resizer.vue +4 -4
- package/src/components/ScrollBar.vue +3 -3
- package/src/components/ScrollViewer.vue +3 -3
- package/src/components/SplitView.vue +2 -2
- package/src/components/ToolButton.vue +2 -1
- package/src/editorProps.ts +6 -2
- package/src/fields/Code.vue +1 -2
- package/src/fields/CodeSelect.vue +13 -11
- package/src/fields/CodeSelectCol.vue +32 -5
- package/src/fields/CondOpSelect.vue +5 -2
- package/src/fields/DataSourceFields.vue +31 -12
- package/src/fields/DataSourceInput.vue +2 -2
- package/src/fields/DataSourceMethods.vue +72 -14
- package/src/fields/DataSourceMocks.vue +0 -1
- package/src/fields/DisplayConds.vue +8 -3
- package/src/fields/EventSelect.vue +28 -12
- package/src/fields/KeyValue.vue +17 -12
- package/src/fields/UISelect.vue +6 -3
- package/src/hooks/index.ts +0 -1
- package/src/hooks/use-code-block-edit.ts +3 -3
- package/src/hooks/use-data-source-edit.ts +3 -2
- package/src/hooks/use-filter.ts +1 -1
- package/src/hooks/use-getso.ts +7 -7
- package/src/hooks/use-node-status.ts +2 -2
- package/src/index.ts +2 -1
- package/src/initService.ts +177 -74
- package/src/layouts/CodeEditor.vue +2 -2
- package/src/layouts/Framework.vue +13 -12
- package/src/layouts/NavMenu.vue +2 -2
- package/src/layouts/page-bar/AddButton.vue +29 -9
- package/src/layouts/page-bar/PageBar.vue +79 -66
- package/src/layouts/page-bar/PageBarScrollContainer.vue +84 -98
- package/src/layouts/page-bar/PageList.vue +5 -3
- package/src/layouts/page-bar/Search.vue +67 -0
- package/src/layouts/props-panel/FormPanel.vue +123 -0
- package/src/layouts/props-panel/PropsPanel.vue +146 -0
- package/src/layouts/props-panel/use-style-panel.ts +29 -0
- package/src/layouts/sidebar/Sidebar.vue +7 -1
- package/src/layouts/sidebar/code-block/CodeBlockList.vue +18 -2
- package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +35 -4
- package/src/layouts/sidebar/code-block/useContentMenu.ts +83 -0
- package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +7 -5
- package/src/layouts/sidebar/data-source/DataSourceList.vue +13 -8
- package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +40 -4
- package/src/layouts/sidebar/data-source/useContentMenu.ts +81 -0
- package/src/layouts/sidebar/layer/LayerMenu.vue +7 -13
- package/src/layouts/sidebar/layer/LayerPanel.vue +11 -4
- package/src/layouts/sidebar/layer/use-click.ts +2 -2
- package/src/layouts/sidebar/layer/use-keybinding.ts +2 -2
- package/src/layouts/sidebar/layer/use-node-status.ts +2 -3
- package/src/layouts/workspace/Workspace.vue +13 -3
- package/src/layouts/workspace/viewer/NodeListMenu.vue +3 -3
- package/src/layouts/workspace/viewer/Stage.vue +41 -11
- package/src/layouts/workspace/viewer/StageOverlay.vue +2 -2
- package/src/layouts/workspace/viewer/ViewerMenu.vue +4 -6
- package/src/services/dataSource.ts +12 -5
- package/src/services/dep.ts +61 -13
- package/src/services/editor.ts +46 -51
- package/src/services/storage.ts +2 -1
- package/src/services/ui.ts +1 -0
- package/src/theme/common/var.scss +12 -10
- package/src/theme/component-list-panel.scss +9 -7
- package/src/theme/content-menu.scss +7 -5
- package/src/theme/data-source.scss +3 -1
- package/src/theme/floating-box.scss +4 -2
- package/src/theme/framework.scss +7 -5
- package/src/theme/index.scss +4 -5
- package/src/theme/key-value.scss +2 -2
- package/src/theme/layer-panel.scss +3 -1
- package/src/theme/layout.scss +1 -1
- package/src/theme/nav-menu.scss +7 -5
- package/src/theme/page-bar.scss +51 -27
- package/src/theme/props-panel.scss +81 -1
- package/src/theme/resizer.scss +1 -1
- package/src/theme/search-input.scss +1 -0
- package/src/theme/sidebar.scss +4 -2
- package/src/theme/stage.scss +3 -1
- package/src/theme/theme.scss +28 -28
- package/src/theme/tree.scss +14 -12
- package/src/type.ts +10 -1
- package/src/utils/content-menu.ts +2 -2
- package/src/utils/data-source/formConfigs/http.ts +2 -0
- package/src/utils/data-source/index.ts +2 -2
- package/src/utils/editor.ts +78 -22
- package/src/utils/idle-task.ts +36 -4
- package/src/utils/props.ts +48 -6
- package/types/index.d.ts +2312 -2080
- package/src/hooks/use-data-source-method.ts +0 -100
- package/src/layouts/PropsPanel.vue +0 -131
- package/src/layouts/page-bar/SwitchTypeButton.vue +0 -45
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="m-editor-props-panel" v-show="nodes.length === 1">
|
|
3
|
+
<slot name="props-panel-header"></slot>
|
|
4
|
+
<FormPanel
|
|
5
|
+
ref="propertyFormPanel"
|
|
6
|
+
class="m-editor-props-property-panel"
|
|
7
|
+
:class="{ 'show-style-panel': showStylePanel }"
|
|
8
|
+
:config="curFormConfig"
|
|
9
|
+
:values="values"
|
|
10
|
+
:disabledShowSrc="disabledShowSrc"
|
|
11
|
+
:extendState="extendState"
|
|
12
|
+
@submit="submit"
|
|
13
|
+
@submit-error="errorHandler"
|
|
14
|
+
@form-error="errorHandler"
|
|
15
|
+
@mounted="mountedHandler"
|
|
16
|
+
></FormPanel>
|
|
17
|
+
|
|
18
|
+
<FormPanel
|
|
19
|
+
v-if="showStylePanel"
|
|
20
|
+
class="m-editor-props-style-panel"
|
|
21
|
+
label-position="top"
|
|
22
|
+
code-value-key="style"
|
|
23
|
+
:config="styleFormConfig"
|
|
24
|
+
:values="values"
|
|
25
|
+
:disabledShowSrc="disabledShowSrc"
|
|
26
|
+
:extendState="extendState"
|
|
27
|
+
@submit="submit"
|
|
28
|
+
@submit-error="errorHandler"
|
|
29
|
+
@form-error="errorHandler"
|
|
30
|
+
>
|
|
31
|
+
<template #props-form-panel-header>
|
|
32
|
+
<div class="m-editor-props-style-panel-title">
|
|
33
|
+
<span>样式</span>
|
|
34
|
+
<div>
|
|
35
|
+
<TMagicButton link size="small" @click="closeStylePanelHandler"><MIcon :icon="Close"></MIcon></TMagicButton>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
</template>
|
|
39
|
+
</FormPanel>
|
|
40
|
+
|
|
41
|
+
<TMagicButton
|
|
42
|
+
v-if="!showStylePanel"
|
|
43
|
+
class="m-editor-props-panel-style-icon"
|
|
44
|
+
circle
|
|
45
|
+
:type="showStylePanel ? 'primary' : ''"
|
|
46
|
+
@click="showStylePanelHandler"
|
|
47
|
+
>
|
|
48
|
+
<MIcon :icon="Sugar"></MIcon>
|
|
49
|
+
</TMagicButton>
|
|
50
|
+
</div>
|
|
51
|
+
</template>
|
|
52
|
+
|
|
53
|
+
<script lang="ts" setup>
|
|
54
|
+
import { computed, inject, onBeforeUnmount, ref, useTemplateRef, watchEffect } from 'vue';
|
|
55
|
+
import { Close, Sugar } from '@element-plus/icons-vue';
|
|
56
|
+
|
|
57
|
+
import type { MNode } from '@tmagic/core';
|
|
58
|
+
import { TMagicButton } from '@tmagic/design';
|
|
59
|
+
import type { ContainerChangeEventData, FormState, FormValue } from '@tmagic/form';
|
|
60
|
+
|
|
61
|
+
import MIcon from '@editor/components/Icon.vue';
|
|
62
|
+
import type { PropsPanelSlots, Services } from '@editor/type';
|
|
63
|
+
import { styleTabConfig } from '@editor/utils';
|
|
64
|
+
|
|
65
|
+
import FormPanel from './FormPanel.vue';
|
|
66
|
+
import { useStylePanel } from './use-style-panel';
|
|
67
|
+
|
|
68
|
+
defineSlots<PropsPanelSlots>();
|
|
69
|
+
|
|
70
|
+
defineOptions({
|
|
71
|
+
name: 'MEditorPropsPanel',
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
defineProps<{
|
|
75
|
+
disabledShowSrc?: boolean;
|
|
76
|
+
extendState?: (state: FormState) => Record<string, any> | Promise<Record<string, any>>;
|
|
77
|
+
}>();
|
|
78
|
+
|
|
79
|
+
const emit = defineEmits<{
|
|
80
|
+
'submit-error': [e: any];
|
|
81
|
+
'form-error': [e: any];
|
|
82
|
+
mounted: [internalInstance: InstanceType<typeof FormPanel>];
|
|
83
|
+
}>();
|
|
84
|
+
|
|
85
|
+
const services = inject<Services>('services');
|
|
86
|
+
|
|
87
|
+
const values = ref<FormValue>({});
|
|
88
|
+
// ts类型应该是FormConfig, 但是打包时会出错,所以暂时用any
|
|
89
|
+
const curFormConfig = ref<any>([]);
|
|
90
|
+
const node = computed(() => services?.editorService.get('node'));
|
|
91
|
+
const nodes = computed(() => services?.editorService.get('nodes') || []);
|
|
92
|
+
|
|
93
|
+
const styleFormConfig = [
|
|
94
|
+
{
|
|
95
|
+
tabPosition: 'right',
|
|
96
|
+
items: styleTabConfig.items,
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
|
|
100
|
+
const init = async () => {
|
|
101
|
+
if (!node.value) {
|
|
102
|
+
curFormConfig.value = [];
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const type = node.value.type || (node.value.items ? 'container' : 'text');
|
|
107
|
+
curFormConfig.value = (await services?.propsService.getPropsConfig(type)) || [];
|
|
108
|
+
values.value = node.value;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
watchEffect(init);
|
|
112
|
+
services?.propsService.on('props-configs-change', init);
|
|
113
|
+
|
|
114
|
+
onBeforeUnmount(() => {
|
|
115
|
+
services?.propsService.off('props-configs-change', init);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const submit = async (v: MNode, eventData?: ContainerChangeEventData) => {
|
|
119
|
+
try {
|
|
120
|
+
if (!v.id) {
|
|
121
|
+
v.id = values.value.id;
|
|
122
|
+
}
|
|
123
|
+
services?.editorService.update(v, { changeRecords: eventData?.changeRecords });
|
|
124
|
+
} catch (e: any) {
|
|
125
|
+
emit('submit-error', e);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const errorHandler = (e: any) => {
|
|
130
|
+
emit('form-error', e);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const mountedHandler = (e: InstanceType<typeof FormPanel>) => {
|
|
134
|
+
emit('mounted', e);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const { showStylePanel, showStylePanelHandler, closeStylePanelHandler } = useStylePanel(services);
|
|
138
|
+
|
|
139
|
+
const propertyFormPanelRef = useTemplateRef<InstanceType<typeof FormPanel>>('propertyFormPanel');
|
|
140
|
+
defineExpose({
|
|
141
|
+
getFormState() {
|
|
142
|
+
return propertyFormPanelRef.value?.configForm?.formState;
|
|
143
|
+
},
|
|
144
|
+
submit,
|
|
145
|
+
});
|
|
146
|
+
</script>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { computed } from 'vue';
|
|
2
|
+
|
|
3
|
+
import { Protocol } from '@editor/services/storage';
|
|
4
|
+
import { Services } from '@editor/type';
|
|
5
|
+
|
|
6
|
+
export const useStylePanel = (services?: Services) => {
|
|
7
|
+
const showStylePanelStorageKey = 'props-panel-show-style-panel';
|
|
8
|
+
const showStylePanelStorageValue = services?.storageService.getItem(showStylePanelStorageKey, {
|
|
9
|
+
protocol: Protocol.BOOLEAN,
|
|
10
|
+
});
|
|
11
|
+
if (typeof showStylePanelStorageValue === 'boolean') {
|
|
12
|
+
services?.uiService.set('showStylePanel', showStylePanelStorageValue);
|
|
13
|
+
}
|
|
14
|
+
const showStylePanel = computed(() => services?.uiService.get('showStylePanel') ?? true);
|
|
15
|
+
const showStylePanelHandler = () => {
|
|
16
|
+
services?.uiService.set('showStylePanel', true);
|
|
17
|
+
services?.storageService.setItem(showStylePanelStorageKey, true, { protocol: Protocol.BOOLEAN });
|
|
18
|
+
};
|
|
19
|
+
const closeStylePanelHandler = () => {
|
|
20
|
+
services?.uiService.set('showStylePanel', false);
|
|
21
|
+
services?.storageService.setItem(showStylePanelStorageKey, false, { protocol: Protocol.BOOLEAN });
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
showStylePanel,
|
|
26
|
+
showStylePanelHandler,
|
|
27
|
+
closeStylePanelHandler,
|
|
28
|
+
};
|
|
29
|
+
};
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
</div>
|
|
20
20
|
<div
|
|
21
21
|
class="m-editor-sidebar-content"
|
|
22
|
+
:class="{ 'm-editor-dep-collecting': collecting }"
|
|
22
23
|
v-for="(config, index) in sideBarItems"
|
|
23
24
|
:key="config.$key ?? index"
|
|
24
25
|
v-show="[config.text, config.$key, `${index}`].includes(activeTabName)"
|
|
@@ -157,6 +158,7 @@ import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height'
|
|
|
157
158
|
import { useFloatBox } from '@editor/hooks/use-float-box';
|
|
158
159
|
import {
|
|
159
160
|
ColumnLayout,
|
|
161
|
+
CustomContentMenuFunction,
|
|
160
162
|
type MenuButton,
|
|
161
163
|
type MenuComponent,
|
|
162
164
|
type Services,
|
|
@@ -184,7 +186,7 @@ const props = withDefaults(
|
|
|
184
186
|
layerContentMenu: (MenuButton | MenuComponent)[];
|
|
185
187
|
indent?: number;
|
|
186
188
|
nextLevelIndentIncrement?: number;
|
|
187
|
-
customContentMenu
|
|
189
|
+
customContentMenu: CustomContentMenuFunction;
|
|
188
190
|
}>(),
|
|
189
191
|
{
|
|
190
192
|
data: () => ({
|
|
@@ -197,6 +199,8 @@ const props = withDefaults(
|
|
|
197
199
|
|
|
198
200
|
const services = inject<Services>('services');
|
|
199
201
|
|
|
202
|
+
const collecting = computed(() => services?.depService.get('collecting'));
|
|
203
|
+
|
|
200
204
|
const columnLeftWidth = computed(() => services?.uiService.get('columnWidth')[ColumnLayout.LEFT] || 0);
|
|
201
205
|
const { height: editorContentHeight } = useEditorContentHeight();
|
|
202
206
|
const columnLeftHeight = ref(0);
|
|
@@ -251,6 +255,7 @@ const getItemConfig = (data: SideItem): SideComponent => {
|
|
|
251
255
|
props: {
|
|
252
256
|
indent: props.indent,
|
|
253
257
|
nextLevelIndentIncrement: props.nextLevelIndentIncrement,
|
|
258
|
+
customContentMenu: props.customContentMenu,
|
|
254
259
|
},
|
|
255
260
|
slots: {},
|
|
256
261
|
},
|
|
@@ -263,6 +268,7 @@ const getItemConfig = (data: SideItem): SideComponent => {
|
|
|
263
268
|
props: {
|
|
264
269
|
indent: props.indent,
|
|
265
270
|
nextLevelIndentIncrement: props.nextLevelIndentIncrement,
|
|
271
|
+
customContentMenu: props.customContentMenu,
|
|
266
272
|
},
|
|
267
273
|
slots: {},
|
|
268
274
|
},
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
:indent="indent"
|
|
6
6
|
:next-level-indent-increment="nextLevelIndentIncrement"
|
|
7
7
|
@node-click="clickHandler"
|
|
8
|
+
@node-contextmenu="nodeContentMenuHandler"
|
|
8
9
|
>
|
|
9
10
|
<template #tree-node-label="{ data }">
|
|
10
11
|
<div
|
|
@@ -19,6 +20,9 @@
|
|
|
19
20
|
</template>
|
|
20
21
|
|
|
21
22
|
<template #tree-node-tool="{ data }">
|
|
23
|
+
<TMagicTag v-if="collecting && data.type === 'code'" type="info" size="small" style="margin-right: 5px"
|
|
24
|
+
>依赖收集中</TMagicTag
|
|
25
|
+
>
|
|
22
26
|
<TMagicTooltip v-if="data.type === 'code'" effect="dark" :content="editable ? '编辑' : '查看'" placement="bottom">
|
|
23
27
|
<Icon :icon="editable ? Edit : View" class="edit-icon" @click.stop="editCode(`${data.key}`)"></Icon>
|
|
24
28
|
</TMagicTooltip>
|
|
@@ -36,7 +40,7 @@ import { Close, Edit, View } from '@element-plus/icons-vue';
|
|
|
36
40
|
|
|
37
41
|
import type { Id, MNode } from '@tmagic/core';
|
|
38
42
|
import { DepTargetType } from '@tmagic/core';
|
|
39
|
-
import { tMagicMessage, tMagicMessageBox, TMagicTooltip } from '@tmagic/design';
|
|
43
|
+
import { tMagicMessage, tMagicMessageBox, TMagicTag, TMagicTooltip } from '@tmagic/design';
|
|
40
44
|
|
|
41
45
|
import Icon from '@editor/components/Icon.vue';
|
|
42
46
|
import Tree from '@editor/components/Tree.vue';
|
|
@@ -59,11 +63,14 @@ const props = defineProps<{
|
|
|
59
63
|
const emit = defineEmits<{
|
|
60
64
|
edit: [id: string];
|
|
61
65
|
remove: [id: string];
|
|
66
|
+
'node-contextmenu': [event: MouseEvent, data: TreeNodeData];
|
|
62
67
|
}>();
|
|
63
68
|
|
|
64
69
|
const services = inject<Services>('services');
|
|
65
70
|
const { codeBlockService, depService, editorService } = services || {};
|
|
66
71
|
|
|
72
|
+
const collecting = computed(() => depService?.get('collecting'));
|
|
73
|
+
|
|
67
74
|
// 代码块列表
|
|
68
75
|
const codeList = computed<TreeNodeData[]>(() =>
|
|
69
76
|
Object.entries(codeBlockService?.getCodeDsl() || {}).map(([codeId, code]) => {
|
|
@@ -157,12 +164,21 @@ const deleteCode = async (id: string) => {
|
|
|
157
164
|
if (typeof props.customError === 'function') {
|
|
158
165
|
props.customError(id, existBinds ? CodeDeleteErrorType.BIND : CodeDeleteErrorType.UNDELETEABLE);
|
|
159
166
|
} else {
|
|
160
|
-
|
|
167
|
+
if (existBinds) {
|
|
168
|
+
tMagicMessage.error('代码块存在绑定关系,不可删除');
|
|
169
|
+
} else {
|
|
170
|
+
tMagicMessage.error('代码块不可删除');
|
|
171
|
+
}
|
|
161
172
|
}
|
|
162
173
|
}
|
|
163
174
|
};
|
|
164
175
|
|
|
176
|
+
const nodeContentMenuHandler = (event: MouseEvent, data: TreeNodeData) => {
|
|
177
|
+
emit('node-contextmenu', event, data);
|
|
178
|
+
};
|
|
179
|
+
|
|
165
180
|
defineExpose({
|
|
166
181
|
filter: filterTextChangeHandler,
|
|
182
|
+
deleteCode,
|
|
167
183
|
});
|
|
168
184
|
</script>
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
:next-level-indent-increment="nextLevelIndentIncrement"
|
|
19
19
|
@edit="editCode"
|
|
20
20
|
@remove="deleteCode"
|
|
21
|
+
@node-contextmenu="nodeContentMenuHandler"
|
|
21
22
|
>
|
|
22
23
|
<template #code-block-panel-tool="{ id, data }">
|
|
23
24
|
<slot name="code-block-panel-tool" :id="id" :data="data"></slot>
|
|
@@ -32,20 +33,40 @@
|
|
|
32
33
|
:content="codeConfig"
|
|
33
34
|
@submit="submitCodeBlockHandler"
|
|
34
35
|
></CodeBlockEditor>
|
|
36
|
+
|
|
37
|
+
<Teleport to="body">
|
|
38
|
+
<ContentMenu
|
|
39
|
+
v-if="menuData.length"
|
|
40
|
+
:menu-data="menuData"
|
|
41
|
+
ref="menu"
|
|
42
|
+
style="overflow: initial"
|
|
43
|
+
@hide="contentMenuHideHandler"
|
|
44
|
+
></ContentMenu>
|
|
45
|
+
</Teleport>
|
|
35
46
|
</template>
|
|
36
47
|
|
|
37
48
|
<script setup lang="ts">
|
|
38
|
-
import { computed, inject,
|
|
49
|
+
import { computed, inject, useTemplateRef } from 'vue';
|
|
39
50
|
|
|
40
51
|
import type { Id } from '@tmagic/core';
|
|
41
52
|
import { TMagicButton, TMagicScrollbar } from '@tmagic/design';
|
|
42
53
|
|
|
43
54
|
import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
|
|
55
|
+
import ContentMenu from '@editor/components/ContentMenu.vue';
|
|
44
56
|
import SearchInput from '@editor/components/SearchInput.vue';
|
|
45
57
|
import { useCodeBlockEdit } from '@editor/hooks/use-code-block-edit';
|
|
46
|
-
import type {
|
|
58
|
+
import type {
|
|
59
|
+
CodeBlockListPanelSlots,
|
|
60
|
+
CodeDeleteErrorType,
|
|
61
|
+
CustomContentMenuFunction,
|
|
62
|
+
EventBus,
|
|
63
|
+
MenuButton,
|
|
64
|
+
MenuComponent,
|
|
65
|
+
Services,
|
|
66
|
+
} from '@editor/type';
|
|
47
67
|
|
|
48
68
|
import CodeBlockList from './CodeBlockList.vue';
|
|
69
|
+
import { useContentMenu } from './useContentMenu';
|
|
49
70
|
|
|
50
71
|
defineSlots<CodeBlockListPanelSlots>();
|
|
51
72
|
|
|
@@ -53,10 +74,11 @@ defineOptions({
|
|
|
53
74
|
name: 'MEditorCodeBlockListPanel',
|
|
54
75
|
});
|
|
55
76
|
|
|
56
|
-
defineProps<{
|
|
77
|
+
const props = defineProps<{
|
|
57
78
|
indent?: number;
|
|
58
79
|
nextLevelIndentIncrement?: number;
|
|
59
80
|
customError?: (id: Id, errorType: CodeDeleteErrorType) => any;
|
|
81
|
+
customContentMenu: CustomContentMenuFunction;
|
|
60
82
|
}>();
|
|
61
83
|
|
|
62
84
|
const eventBus = inject<EventBus>('eventBus');
|
|
@@ -67,7 +89,7 @@ const editable = computed(() => codeBlockService?.getEditStatus());
|
|
|
67
89
|
const { codeBlockEditor, codeConfig, editCode, deleteCode, createCodeBlock, submitCodeBlockHandler } =
|
|
68
90
|
useCodeBlockEdit(codeBlockService);
|
|
69
91
|
|
|
70
|
-
const codeBlockList =
|
|
92
|
+
const codeBlockList = useTemplateRef<InstanceType<typeof CodeBlockList>>('codeBlockList');
|
|
71
93
|
|
|
72
94
|
const filterTextChangeHandler = (val: string) => {
|
|
73
95
|
codeBlockList.value?.filter(val);
|
|
@@ -76,4 +98,13 @@ const filterTextChangeHandler = (val: string) => {
|
|
|
76
98
|
eventBus?.on('edit-code', (id: string) => {
|
|
77
99
|
editCode(id);
|
|
78
100
|
});
|
|
101
|
+
|
|
102
|
+
const {
|
|
103
|
+
nodeContentMenuHandler,
|
|
104
|
+
menuData: contentMenuData,
|
|
105
|
+
contentMenuHideHandler,
|
|
106
|
+
} = useContentMenu((id: string) => {
|
|
107
|
+
codeBlockList.value?.deleteCode(id);
|
|
108
|
+
});
|
|
109
|
+
const menuData = computed<(MenuButton | MenuComponent)[]>(() => props.customContentMenu(contentMenuData, 'code-block'));
|
|
79
110
|
</script>
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { inject, markRaw, useTemplateRef } from 'vue';
|
|
2
|
+
import { CopyDocument, Delete, Edit } from '@element-plus/icons-vue';
|
|
3
|
+
import { cloneDeep } from 'lodash-es';
|
|
4
|
+
|
|
5
|
+
import ContentMenu from '@editor/components/ContentMenu.vue';
|
|
6
|
+
import type { EventBus, MenuButton, MenuComponent, Services, TreeNodeData } from '@editor/type';
|
|
7
|
+
|
|
8
|
+
export const useContentMenu = (deleteCode: (id: string) => void) => {
|
|
9
|
+
const eventBus = inject<EventBus>('eventBus');
|
|
10
|
+
const menuRef = useTemplateRef<InstanceType<typeof ContentMenu>>('menu');
|
|
11
|
+
|
|
12
|
+
let selectId = '';
|
|
13
|
+
|
|
14
|
+
const menuData: (MenuButton | MenuComponent)[] = [
|
|
15
|
+
{
|
|
16
|
+
type: 'button',
|
|
17
|
+
text: '编辑',
|
|
18
|
+
icon: Edit,
|
|
19
|
+
display: (services) => services?.codeBlockService?.getEditStatus() ?? true,
|
|
20
|
+
handler: () => {
|
|
21
|
+
if (!selectId) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
eventBus?.emit('edit-code', selectId);
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type: 'button',
|
|
30
|
+
text: '复制并粘贴至当前',
|
|
31
|
+
icon: markRaw(CopyDocument),
|
|
32
|
+
handler: async ({ codeBlockService }: Services) => {
|
|
33
|
+
if (!selectId) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const codeBlock = codeBlockService.getCodeContentById(selectId);
|
|
38
|
+
if (!codeBlock) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const newCodeId = await codeBlockService.getUniqueId();
|
|
43
|
+
|
|
44
|
+
codeBlockService.setCodeDslById(newCodeId, cloneDeep(codeBlock));
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: 'button',
|
|
49
|
+
text: '删除',
|
|
50
|
+
icon: Delete,
|
|
51
|
+
handler: () => {
|
|
52
|
+
if (!selectId) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
deleteCode(selectId);
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
const nodeContentMenuHandler = (event: MouseEvent, data: TreeNodeData) => {
|
|
62
|
+
event.preventDefault();
|
|
63
|
+
|
|
64
|
+
if (data.type === 'code') {
|
|
65
|
+
menuRef.value?.show(event);
|
|
66
|
+
if (data.id) {
|
|
67
|
+
selectId = `${data.id}`;
|
|
68
|
+
} else {
|
|
69
|
+
selectId = '';
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const contentMenuHideHandler = () => {
|
|
75
|
+
selectId = '';
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
menuData,
|
|
80
|
+
nodeContentMenuHandler,
|
|
81
|
+
contentMenuHideHandler,
|
|
82
|
+
};
|
|
83
|
+
};
|
|
@@ -24,9 +24,9 @@
|
|
|
24
24
|
<script setup lang="ts">
|
|
25
25
|
import { inject, Ref, ref, watchEffect } from 'vue';
|
|
26
26
|
|
|
27
|
-
import { DataSourceSchema } from '@tmagic/core';
|
|
27
|
+
import type { DataSourceSchema } from '@tmagic/core';
|
|
28
28
|
import { tMagicMessage } from '@tmagic/design';
|
|
29
|
-
import { FormConfig, MFormBox } from '@tmagic/form';
|
|
29
|
+
import { type ContainerChangeEventData, type FormConfig, MFormBox } from '@tmagic/form';
|
|
30
30
|
|
|
31
31
|
import FloatingBox from '@editor/components/FloatingBox.vue';
|
|
32
32
|
import { useEditorContentHeight } from '@editor/hooks';
|
|
@@ -46,7 +46,9 @@ const props = defineProps<{
|
|
|
46
46
|
const boxVisible = defineModel<boolean>('visible', { default: false });
|
|
47
47
|
const width = defineModel<number>('width', { default: 670 });
|
|
48
48
|
|
|
49
|
-
const emit = defineEmits
|
|
49
|
+
const emit = defineEmits<{
|
|
50
|
+
submit: [v: any, eventData: ContainerChangeEventData];
|
|
51
|
+
}>();
|
|
50
52
|
|
|
51
53
|
const services = inject<Services>('services');
|
|
52
54
|
|
|
@@ -63,8 +65,8 @@ watchEffect(() => {
|
|
|
63
65
|
dataSourceConfig.value = services?.dataSourceService.getFormConfig(initValues.value.type) || [];
|
|
64
66
|
});
|
|
65
67
|
|
|
66
|
-
const submitHandler = (values: any) => {
|
|
67
|
-
emit('submit', values);
|
|
68
|
+
const submitHandler = (values: any, data: ContainerChangeEventData) => {
|
|
69
|
+
emit('submit', values, data);
|
|
68
70
|
};
|
|
69
71
|
|
|
70
72
|
const errorHandler = (error: any) => {
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
:indent="indent"
|
|
6
6
|
:next-level-indent-increment="nextLevelIndentIncrement"
|
|
7
7
|
@node-click="clickHandler"
|
|
8
|
+
@node-contextmenu="nodeContentMenuHandler"
|
|
8
9
|
>
|
|
9
10
|
<template #tree-node-label="{ data }">
|
|
10
11
|
<div
|
|
@@ -18,6 +19,9 @@
|
|
|
18
19
|
</div>
|
|
19
20
|
</template>
|
|
20
21
|
<template #tree-node-tool="{ data }">
|
|
22
|
+
<TMagicTag v-if="collecting && data.type === 'ds'" type="info" size="small" style="margin-right: 5px"
|
|
23
|
+
>依赖收集中</TMagicTag
|
|
24
|
+
>
|
|
21
25
|
<TMagicTooltip v-if="data.type === 'ds'" effect="dark" :content="editable ? '编辑' : '查看'" placement="bottom">
|
|
22
26
|
<Icon :icon="editable ? Edit : View" class="edit-icon" @click.stop="editHandler(`${data.key}`)"></Icon>
|
|
23
27
|
</TMagicTooltip>
|
|
@@ -34,13 +38,13 @@ import { computed, inject } from 'vue';
|
|
|
34
38
|
import { Close, Edit, View } from '@element-plus/icons-vue';
|
|
35
39
|
|
|
36
40
|
import { DepData, DepTargetType, Id, MNode } from '@tmagic/core';
|
|
37
|
-
import {
|
|
41
|
+
import { TMagicTag, TMagicTooltip } from '@tmagic/design';
|
|
38
42
|
|
|
39
43
|
import Icon from '@editor/components/Icon.vue';
|
|
40
44
|
import Tree from '@editor/components/Tree.vue';
|
|
41
45
|
import { useFilter } from '@editor/hooks/use-filter';
|
|
42
46
|
import { useNodeStatus } from '@editor/hooks/use-node-status';
|
|
43
|
-
import type { DataSourceListSlots, Services } from '@editor/type';
|
|
47
|
+
import type { DataSourceListSlots, Services, TreeNodeData } from '@editor/type';
|
|
44
48
|
|
|
45
49
|
defineSlots<DataSourceListSlots>();
|
|
46
50
|
|
|
@@ -56,10 +60,13 @@ defineProps<{
|
|
|
56
60
|
const emit = defineEmits<{
|
|
57
61
|
edit: [id: string];
|
|
58
62
|
remove: [id: string];
|
|
63
|
+
'node-contextmenu': [event: MouseEvent, data: TreeNodeData];
|
|
59
64
|
}>();
|
|
60
65
|
|
|
61
66
|
const { depService, editorService, dataSourceService } = inject<Services>('services') || {};
|
|
62
67
|
|
|
68
|
+
const collecting = computed(() => depService?.get('collecting'));
|
|
69
|
+
|
|
63
70
|
const editable = computed(() => dataSourceService?.get('editable') ?? true);
|
|
64
71
|
|
|
65
72
|
const dataSources = computed(() => dataSourceService?.get('dataSources') || []);
|
|
@@ -154,12 +161,6 @@ const editHandler = (id: string) => {
|
|
|
154
161
|
};
|
|
155
162
|
|
|
156
163
|
const removeHandler = async (id: string) => {
|
|
157
|
-
await tMagicMessageBox.confirm('确定删除?', '提示', {
|
|
158
|
-
confirmButtonText: '确定',
|
|
159
|
-
cancelButtonText: '取消',
|
|
160
|
-
type: 'warning',
|
|
161
|
-
});
|
|
162
|
-
|
|
163
164
|
emit('remove', id);
|
|
164
165
|
};
|
|
165
166
|
|
|
@@ -176,6 +177,10 @@ const clickHandler = (event: MouseEvent, data: any) => {
|
|
|
176
177
|
}
|
|
177
178
|
};
|
|
178
179
|
|
|
180
|
+
const nodeContentMenuHandler = (event: MouseEvent, data: TreeNodeData) => {
|
|
181
|
+
emit('node-contextmenu', event, data);
|
|
182
|
+
};
|
|
183
|
+
|
|
179
184
|
defineExpose({
|
|
180
185
|
filter: filterTextChangeHandler,
|
|
181
186
|
});
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
:next-level-indent-increment="nextLevelIndentIncrement"
|
|
36
36
|
@edit="editHandler"
|
|
37
37
|
@remove="removeHandler"
|
|
38
|
+
@node-contextmenu="nodeContentMenuHandler"
|
|
38
39
|
></DataSourceList>
|
|
39
40
|
</TMagicScrollbar>
|
|
40
41
|
|
|
@@ -45,21 +46,40 @@
|
|
|
45
46
|
:title="dialogTitle"
|
|
46
47
|
@submit="submitDataSourceHandler"
|
|
47
48
|
></DataSourceConfigPanel>
|
|
49
|
+
|
|
50
|
+
<Teleport to="body">
|
|
51
|
+
<ContentMenu
|
|
52
|
+
v-if="menuData.length"
|
|
53
|
+
:menu-data="menuData"
|
|
54
|
+
ref="menu"
|
|
55
|
+
style="overflow: initial"
|
|
56
|
+
@hide="contentMenuHideHandler"
|
|
57
|
+
></ContentMenu>
|
|
58
|
+
</Teleport>
|
|
48
59
|
</template>
|
|
49
60
|
|
|
50
61
|
<script setup lang="ts">
|
|
51
62
|
import { computed, inject, ref } from 'vue';
|
|
52
63
|
import { mergeWith } from 'lodash-es';
|
|
53
64
|
|
|
54
|
-
import { TMagicButton, TMagicPopover, TMagicScrollbar } from '@tmagic/design';
|
|
65
|
+
import { TMagicButton, tMagicMessageBox, TMagicPopover, TMagicScrollbar } from '@tmagic/design';
|
|
55
66
|
|
|
67
|
+
import ContentMenu from '@editor/components/ContentMenu.vue';
|
|
56
68
|
import SearchInput from '@editor/components/SearchInput.vue';
|
|
57
69
|
import ToolButton from '@editor/components/ToolButton.vue';
|
|
58
70
|
import { useDataSourceEdit } from '@editor/hooks/use-data-source-edit';
|
|
59
|
-
import type {
|
|
71
|
+
import type {
|
|
72
|
+
CustomContentMenuFunction,
|
|
73
|
+
DataSourceListSlots,
|
|
74
|
+
EventBus,
|
|
75
|
+
MenuButton,
|
|
76
|
+
MenuComponent,
|
|
77
|
+
Services,
|
|
78
|
+
} from '@editor/type';
|
|
60
79
|
|
|
61
80
|
import DataSourceConfigPanel from './DataSourceConfigPanel.vue';
|
|
62
81
|
import DataSourceList from './DataSourceList.vue';
|
|
82
|
+
import { useContentMenu } from './useContentMenu';
|
|
63
83
|
|
|
64
84
|
defineSlots<DataSourceListSlots>();
|
|
65
85
|
|
|
@@ -67,9 +87,10 @@ defineOptions({
|
|
|
67
87
|
name: 'MEditorDataSourceListPanel',
|
|
68
88
|
});
|
|
69
89
|
|
|
70
|
-
defineProps<{
|
|
90
|
+
const props = defineProps<{
|
|
71
91
|
indent?: number;
|
|
72
92
|
nextLevelIndentIncrement?: number;
|
|
93
|
+
customContentMenu: CustomContentMenuFunction;
|
|
73
94
|
}>();
|
|
74
95
|
|
|
75
96
|
const eventBus = inject<EventBus>('eventBus');
|
|
@@ -105,7 +126,13 @@ const addHandler = (type: string) => {
|
|
|
105
126
|
editDialog.value.show();
|
|
106
127
|
};
|
|
107
128
|
|
|
108
|
-
const removeHandler = (id: string) => {
|
|
129
|
+
const removeHandler = async (id: string) => {
|
|
130
|
+
await tMagicMessageBox.confirm('确定删除?', '提示', {
|
|
131
|
+
confirmButtonText: '确定',
|
|
132
|
+
cancelButtonText: '取消',
|
|
133
|
+
type: 'warning',
|
|
134
|
+
});
|
|
135
|
+
|
|
109
136
|
dataSourceService?.remove(id);
|
|
110
137
|
};
|
|
111
138
|
|
|
@@ -118,4 +145,13 @@ const filterTextChangeHandler = (val: string) => {
|
|
|
118
145
|
eventBus?.on('edit-data-source', (id: string) => {
|
|
119
146
|
editHandler(id);
|
|
120
147
|
});
|
|
148
|
+
|
|
149
|
+
eventBus?.on('remove-data-source', (id: string) => {
|
|
150
|
+
removeHandler(id);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const { nodeContentMenuHandler, menuData: contentMenuData, contentMenuHideHandler } = useContentMenu();
|
|
154
|
+
const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
|
|
155
|
+
props.customContentMenu(contentMenuData, 'data-source'),
|
|
156
|
+
);
|
|
121
157
|
</script>
|