@tmagic/editor 1.3.0-alpha.8 → 1.3.0-beta.0
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 +82 -218
- package/dist/tmagic-editor.js +4985 -3999
- package/dist/tmagic-editor.js.map +1 -1
- package/dist/tmagic-editor.umd.cjs +5013 -4012
- package/dist/tmagic-editor.umd.cjs.map +1 -1
- package/package.json +15 -15
- package/src/Editor.vue +4 -5
- package/src/components/CodeBlockEditor.vue +257 -0
- package/src/components/CodeParams.vue +60 -0
- package/src/components/ContentMenu.vue +11 -3
- package/src/editorProps.ts +30 -5
- package/src/fields/Code.vue +23 -19
- package/src/fields/CodeLink.vue +6 -7
- package/src/fields/CodeSelect.vue +12 -9
- package/src/fields/CodeSelectCol.vue +70 -81
- package/src/fields/DataSourceFieldSelect.vue +47 -0
- package/src/fields/DataSourceFields.vue +28 -22
- package/src/fields/DataSourceInput.vue +21 -58
- package/src/fields/DataSourceMethodSelect.vue +136 -0
- package/src/fields/DataSourceMethods.vue +103 -0
- package/src/fields/DataSourceSelect.vue +68 -0
- package/src/fields/EventSelect.vue +53 -19
- package/src/fields/KeyValue.vue +9 -11
- package/src/fields/UISelect.vue +54 -14
- package/src/hooks/index.ts +21 -0
- package/src/hooks/use-code-block-edit.ts +84 -0
- package/src/hooks/use-data-source-method.ts +100 -0
- package/src/{utils/stage.ts → hooks/use-stage.ts} +4 -7
- package/src/icons/CenterIcon.vue +13 -0
- package/src/icons/CodeIcon.vue +0 -2
- package/src/icons/FolderMinusIcon.vue +22 -0
- package/src/index.ts +15 -0
- package/src/initService.ts +73 -16
- package/src/layouts/CodeEditor.vue +62 -10
- package/src/layouts/Framework.vue +7 -3
- package/src/layouts/PropsPanel.vue +8 -3
- package/src/layouts/sidebar/ComponentListPanel.vue +6 -9
- package/src/layouts/sidebar/LayerMenu.vue +20 -21
- package/src/layouts/sidebar/LayerNode.vue +40 -0
- package/src/layouts/sidebar/LayerPanel.vue +14 -5
- package/src/layouts/sidebar/Sidebar.vue +22 -36
- package/src/layouts/sidebar/code-block/CodeBlockList.vue +81 -119
- package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +64 -0
- package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +27 -47
- package/src/layouts/sidebar/data-source/DataSourceList.vue +145 -0
- package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +58 -69
- package/src/layouts/workspace/Stage.vue +13 -5
- package/src/layouts/workspace/ViewerMenu.vue +10 -35
- package/src/services/codeBlock.ts +18 -71
- package/src/services/dataSource.ts +16 -1
- package/src/services/dep.ts +23 -20
- package/src/services/editor.ts +46 -18
- package/src/services/props.ts +2 -2
- package/src/theme/code-block.scss +5 -117
- package/src/theme/code-editor.scss +27 -2
- package/src/theme/content-menu.scss +1 -1
- package/src/theme/data-source-methods.scss +13 -0
- package/src/theme/data-source.scss +11 -0
- package/src/theme/event.scss +15 -12
- package/src/theme/framework.scss +0 -3
- package/src/theme/layer-panel.scss +6 -0
- package/src/theme/layout.scss +4 -0
- package/src/theme/sidebar.scss +18 -60
- package/src/theme/theme.scss +1 -0
- package/src/type.ts +47 -7
- package/src/utils/content-menu.ts +92 -0
- package/src/utils/data-source/formConfigs/base.ts +28 -30
- package/src/utils/data-source/index.ts +65 -2
- package/src/utils/dep.ts +39 -7
- package/src/utils/index.ts +1 -1
- package/src/utils/props.ts +298 -189
- package/src/utils/undo-redo.ts +1 -1
- package/types/Editor.vue.d.ts +30 -6
- package/types/components/CodeBlockEditor.vue.d.ts +23 -0
- package/types/components/CodeParams.vue.d.ts +26 -0
- package/types/components/ContentMenu.vue.d.ts +2 -2
- package/types/components/ToolButton.vue.d.ts +3 -3
- package/types/editorProps.d.ts +19 -5
- package/types/fields/Code.vue.d.ts +24 -19
- package/types/fields/CodeLink.vue.d.ts +12 -21
- package/types/fields/CodeSelect.vue.d.ts +6 -17
- package/types/fields/CodeSelectCol.vue.d.ts +10 -14
- package/types/fields/DataSourceFieldSelect.vue.d.ts +29 -0
- package/types/fields/DataSourceFields.vue.d.ts +7 -18
- package/types/fields/DataSourceInput.vue.d.ts +10 -30
- package/types/fields/DataSourceMethodSelect.vue.d.ts +29 -0
- package/types/fields/DataSourceMethods.vue.d.ts +32 -0
- package/types/fields/DataSourceSelect.vue.d.ts +40 -0
- package/types/fields/EventSelect.vue.d.ts +2 -13
- package/types/fields/KeyValue.vue.d.ts +9 -24
- package/types/fields/UISelect.vue.d.ts +2 -11
- package/types/hooks/index.d.ts +3 -0
- package/types/{components/FunctionEditor.vue.d.ts → hooks/use-code-block-edit.d.ts} +31 -119
- package/types/hooks/use-data-source-method.d.ts +123 -0
- package/types/icons/CenterIcon.vue.d.ts +2 -0
- package/types/icons/FolderMinusIcon.vue.d.ts +2 -0
- package/types/index.d.ts +7 -0
- package/types/initService.d.ts +2 -2
- package/types/layouts/CodeEditor.vue.d.ts +10 -4
- package/types/layouts/PropsPanel.vue.d.ts +274 -2
- package/types/layouts/sidebar/LayerNode.vue.d.ts +16 -0
- package/types/layouts/sidebar/Sidebar.vue.d.ts +0 -3
- package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +4 -9
- package/types/layouts/sidebar/code-block/{CodeBlockEditor.vue.d.ts → CodeBlockListPanel.vue.d.ts} +9 -5
- package/types/layouts/sidebar/data-source/DataSourceConfigPanel.vue.d.ts +3 -1
- package/types/layouts/sidebar/data-source/DataSourceList.vue.d.ts +14 -0
- package/types/layouts/sidebar/data-source/DataSourceListPanel.vue.d.ts +1 -11
- package/types/services/codeBlock.d.ts +6 -37
- package/types/services/dataSource.d.ts +7 -1
- package/types/services/dep.d.ts +12 -21
- package/types/services/editor.d.ts +1 -1
- package/types/services/props.d.ts +10 -115
- package/types/type.d.ts +42 -7
- package/types/utils/content-menu.d.ts +7 -0
- package/types/utils/data-source/formConfigs/base.d.ts +1 -2
- package/types/utils/data-source/index.d.ts +5 -0
- package/types/utils/dep.d.ts +4 -2
- package/types/utils/index.d.ts +1 -1
- package/types/utils/props.d.ts +13 -102
- package/src/components/CodeDraftEditor.vue +0 -148
- package/src/components/FunctionEditor.vue +0 -197
- package/src/layouts/sidebar/code-block/CodeBlockEditor.vue +0 -100
- package/types/components/CodeDraftEditor.vue.d.ts +0 -61
- /package/types/{utils/stage.d.ts → hooks/use-stage.d.ts} +0 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<TMagicTree
|
|
3
|
+
ref="tree"
|
|
4
|
+
class="magic-editor-layer-tree"
|
|
5
|
+
node-key="id"
|
|
6
|
+
empty-text="暂无代码块"
|
|
7
|
+
default-expand-all
|
|
8
|
+
:expand-on-click-node="false"
|
|
9
|
+
:data="list"
|
|
10
|
+
:highlight-current="true"
|
|
11
|
+
@node-click="clickHandler"
|
|
12
|
+
>
|
|
13
|
+
<template #default="{ data }">
|
|
14
|
+
<div :id="data.id" class="list-container">
|
|
15
|
+
<div class="list-item">
|
|
16
|
+
<Icon v-if="data.type === 'code'" class="codeIcon" :icon="Coin"></Icon>
|
|
17
|
+
<Icon v-if="data.type === 'node'" class="compIcon" :icon="Aim"></Icon>
|
|
18
|
+
<span class="name" :class="{ code: data.type === 'ds', hook: data.type === 'key' }">{{
|
|
19
|
+
data.type === 'key' ? data.name : `${data.name}(${data.id})`
|
|
20
|
+
}}</span>
|
|
21
|
+
<!-- 右侧工具栏 -->
|
|
22
|
+
<div class="right-tool" v-if="data.type === 'ds'">
|
|
23
|
+
<TMagicTooltip effect="dark" :content="editable ? '编辑' : '查看'" placement="bottom">
|
|
24
|
+
<Icon :icon="editable ? Edit : View" class="edit-icon" @click.stop="editHandler(`${data.id}`)"></Icon>
|
|
25
|
+
</TMagicTooltip>
|
|
26
|
+
<TMagicTooltip v-if="editable" effect="dark" content="删除" placement="bottom">
|
|
27
|
+
<Icon :icon="Close" class="edit-icon" @click.stop="removeHandler(`${data.id}`)"></Icon>
|
|
28
|
+
</TMagicTooltip>
|
|
29
|
+
<slot name="data-source-panel-tool" :id="data.id" :data="data.codeBlockContent"></slot>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</template>
|
|
34
|
+
</TMagicTree>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script lang="ts" setup>
|
|
38
|
+
import { computed, inject, ref } from 'vue';
|
|
39
|
+
import { Aim, Close, Coin, Edit, View } from '@element-plus/icons-vue';
|
|
40
|
+
|
|
41
|
+
import { tMagicMessageBox, TMagicTooltip, TMagicTree } from '@tmagic/design';
|
|
42
|
+
import { Dep, Id } from '@tmagic/schema';
|
|
43
|
+
|
|
44
|
+
import Icon from '@editor/components/Icon.vue';
|
|
45
|
+
import { DepTargetType, Services } from '@editor/type';
|
|
46
|
+
|
|
47
|
+
defineOptions({
|
|
48
|
+
name: 'MEditorDataSourceList',
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const emit = defineEmits<{
|
|
52
|
+
edit: [id: string];
|
|
53
|
+
remove: [id: string];
|
|
54
|
+
}>();
|
|
55
|
+
|
|
56
|
+
const { depService, editorService, dataSourceService } = inject<Services>('services') || {};
|
|
57
|
+
|
|
58
|
+
const editable = computed(() => dataSourceService?.get('editable') ?? true);
|
|
59
|
+
|
|
60
|
+
const dataSources = computed(() => dataSourceService?.get('dataSources') || []);
|
|
61
|
+
|
|
62
|
+
const dsDep = computed(() => depService?.getTargets(DepTargetType.DATA_SOURCE) || {});
|
|
63
|
+
const dsMethodDep = computed(() => depService?.getTargets(DepTargetType.DATA_SOURCE_METHOD) || {});
|
|
64
|
+
const dsCondDep = computed(() => depService?.getTargets(DepTargetType.DATA_SOURCE_COND) || {});
|
|
65
|
+
|
|
66
|
+
const getKeyTreeConfig = (dep: Dep[string], type?: string) =>
|
|
67
|
+
dep.keys.map((key) => ({ name: key, id: key, type: 'key', isMethod: type === 'method', isCond: type === 'cond' }));
|
|
68
|
+
|
|
69
|
+
const getNodeTreeConfig = (id: string, dep: Dep[string], type?: string) => ({
|
|
70
|
+
name: dep.name,
|
|
71
|
+
type: 'node',
|
|
72
|
+
id,
|
|
73
|
+
children: getKeyTreeConfig(dep, type),
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const list = computed(() =>
|
|
77
|
+
dataSources.value.map((ds) => {
|
|
78
|
+
const dsDeps = dsDep.value[ds.id].deps;
|
|
79
|
+
const dsMethodDeps = dsMethodDep.value[ds.id].deps;
|
|
80
|
+
const dsCondDeps = dsCondDep.value[ds.id].deps;
|
|
81
|
+
|
|
82
|
+
const children: any[] = [];
|
|
83
|
+
|
|
84
|
+
const mergeChildren = (deps: Dep, type?: string) => {
|
|
85
|
+
Object.entries(deps).forEach(([id, dep]) => {
|
|
86
|
+
const nodeItem = children.find((item) => item.id === id);
|
|
87
|
+
if (nodeItem) {
|
|
88
|
+
nodeItem.children = nodeItem.children.concat(getKeyTreeConfig(dep, type));
|
|
89
|
+
} else {
|
|
90
|
+
children.push(getNodeTreeConfig(id, dep, type));
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
mergeChildren(dsDeps);
|
|
96
|
+
mergeChildren(dsMethodDeps, 'method');
|
|
97
|
+
mergeChildren(dsCondDeps, 'cond');
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
id: ds.id,
|
|
101
|
+
name: ds.title,
|
|
102
|
+
type: 'ds',
|
|
103
|
+
children,
|
|
104
|
+
};
|
|
105
|
+
}),
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
const editHandler = (id: string) => {
|
|
109
|
+
emit('edit', id);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const removeHandler = async (id: string) => {
|
|
113
|
+
await tMagicMessageBox.confirm('确定删除?', '提示', {
|
|
114
|
+
confirmButtonText: '确定',
|
|
115
|
+
cancelButtonText: '取消',
|
|
116
|
+
type: 'warning',
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
emit('remove', id);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// 选中组件
|
|
123
|
+
const selectComp = (compId: Id) => {
|
|
124
|
+
const stage = editorService?.get('stage');
|
|
125
|
+
editorService?.select(compId);
|
|
126
|
+
stage?.select(compId);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const clickHandler = (data: any, node: any) => {
|
|
130
|
+
if (data.type === 'node') {
|
|
131
|
+
selectComp(data.id);
|
|
132
|
+
} else if (data.type === 'key') {
|
|
133
|
+
selectComp(node.parent.data.id);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const tree = ref<InstanceType<typeof TMagicTree>>();
|
|
138
|
+
|
|
139
|
+
defineExpose({
|
|
140
|
+
filter(val: string) {
|
|
141
|
+
debugger;
|
|
142
|
+
tree.value?.filter(val);
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
</script>
|
|
@@ -2,121 +2,110 @@
|
|
|
2
2
|
<TMagicScrollbar class="data-source-list-panel m-editor-dep-list-panel">
|
|
3
3
|
<div class="search-wrapper">
|
|
4
4
|
<SearchInput @search="filterTextChangeHandler"></SearchInput>
|
|
5
|
-
<
|
|
5
|
+
<TMagicPopover v-if="editable" placement="right">
|
|
6
|
+
<template #reference>
|
|
7
|
+
<TMagicButton type="primary" size="small">新增</TMagicButton>
|
|
8
|
+
</template>
|
|
9
|
+
<div class="data-source-list-panel-add-menu">
|
|
10
|
+
<ToolButton
|
|
11
|
+
v-for="(item, index) in datasourceTypeList"
|
|
12
|
+
:data="{
|
|
13
|
+
type: 'button',
|
|
14
|
+
text: item.text,
|
|
15
|
+
handler: () => {
|
|
16
|
+
addHandler(item.type);
|
|
17
|
+
},
|
|
18
|
+
}"
|
|
19
|
+
:key="index"
|
|
20
|
+
></ToolButton>
|
|
21
|
+
</div>
|
|
22
|
+
</TMagicPopover>
|
|
6
23
|
</div>
|
|
7
24
|
|
|
8
25
|
<!-- 数据源列表 -->
|
|
9
|
-
<
|
|
10
|
-
ref="tree"
|
|
11
|
-
class="magic-editor-layer-tree"
|
|
12
|
-
node-key="id"
|
|
13
|
-
empty-text="暂无代码块"
|
|
14
|
-
default-expand-all
|
|
15
|
-
:expand-on-click-node="false"
|
|
16
|
-
:data="list"
|
|
17
|
-
:highlight-current="true"
|
|
18
|
-
>
|
|
19
|
-
<template #default="{ data }">
|
|
20
|
-
<div :id="data.id" class="list-container">
|
|
21
|
-
<div class="list-item">
|
|
22
|
-
<Icon v-if="data.type === 'code'" class="codeIcon" :icon="Coin"></Icon>
|
|
23
|
-
<Icon v-if="data.type === 'node'" class="compIcon" :icon="Aim"></Icon>
|
|
24
|
-
<span class="name" :class="{ code: data.type === 'code', hook: data.type === 'key' }"
|
|
25
|
-
>{{ data.name }}({{ data.id }})</span
|
|
26
|
-
>
|
|
27
|
-
<!-- 右侧工具栏 -->
|
|
28
|
-
<div class="right-tool" v-if="data.type === 'code'">
|
|
29
|
-
<TMagicTooltip effect="dark" content="编辑" placement="bottom">
|
|
30
|
-
<Icon class="edit-icon" :icon="Edit" @click.stop="editHandler(`${data.id}`)"></Icon>
|
|
31
|
-
</TMagicTooltip>
|
|
32
|
-
<TMagicTooltip effect="dark" content="删除" placement="bottom">
|
|
33
|
-
<Icon :icon="Close" class="edit-icon" @click.stop="removeHandler(`${data.id}`)"></Icon>
|
|
34
|
-
</TMagicTooltip>
|
|
35
|
-
<slot name="data-source-panel-tool" :id="data.id" :data="data.codeBlockContent"></slot>
|
|
36
|
-
</div>
|
|
37
|
-
</div>
|
|
38
|
-
</div>
|
|
39
|
-
</template>
|
|
40
|
-
</TMagicTree>
|
|
26
|
+
<DataSourceList @edit="editHandler" @remove="removeHandler"></DataSourceList>
|
|
41
27
|
|
|
42
28
|
<DataSourceConfigPanel
|
|
43
29
|
ref="editDialog"
|
|
30
|
+
:disabled="!editable"
|
|
44
31
|
:values="dataSourceValues"
|
|
45
|
-
:title="
|
|
32
|
+
:title="dialogTitle"
|
|
46
33
|
@submit="submitDataSourceHandler"
|
|
47
34
|
></DataSourceConfigPanel>
|
|
48
35
|
</TMagicScrollbar>
|
|
49
36
|
</template>
|
|
50
37
|
|
|
51
|
-
<script setup lang="ts"
|
|
38
|
+
<script setup lang="ts">
|
|
52
39
|
import { computed, inject, ref } from 'vue';
|
|
53
|
-
import {
|
|
40
|
+
import { mergeWith } from 'lodash-es';
|
|
54
41
|
|
|
55
|
-
import { TMagicButton,
|
|
56
|
-
import { DataSourceSchema } from '@tmagic/schema';
|
|
42
|
+
import { TMagicButton, TMagicPopover, TMagicScrollbar } from '@tmagic/design';
|
|
43
|
+
import type { DataSourceSchema } from '@tmagic/schema';
|
|
57
44
|
|
|
58
|
-
import Icon from '@editor/components/Icon.vue';
|
|
59
45
|
import SearchInput from '@editor/components/SearchInput.vue';
|
|
46
|
+
import ToolButton from '@editor/components/ToolButton.vue';
|
|
60
47
|
import type { Services } from '@editor/type';
|
|
61
48
|
|
|
62
49
|
import DataSourceConfigPanel from './DataSourceConfigPanel.vue';
|
|
50
|
+
import DataSourceList from './DataSourceList.vue';
|
|
63
51
|
|
|
64
52
|
defineOptions({
|
|
65
53
|
name: 'MEditorDataSourceListPanel',
|
|
66
54
|
});
|
|
67
55
|
|
|
68
|
-
const
|
|
69
|
-
const { dataSourceService, depService } = inject<Services>('services') || {};
|
|
70
|
-
|
|
71
|
-
const list = computed(() =>
|
|
72
|
-
Object.values(depService?.targets['data-source'] || {}).map((target) => ({
|
|
73
|
-
id: target.id,
|
|
74
|
-
name: target.name,
|
|
75
|
-
type: 'code',
|
|
76
|
-
children: Object.entries(target.deps).map(([id, dep]) => ({
|
|
77
|
-
name: dep.name,
|
|
78
|
-
type: 'node',
|
|
79
|
-
id,
|
|
80
|
-
children: dep.keys.map((key) => ({ name: key, id: key, type: 'key' })),
|
|
81
|
-
})),
|
|
82
|
-
})),
|
|
83
|
-
);
|
|
56
|
+
const { dataSourceService } = inject<Services>('services') || {};
|
|
84
57
|
|
|
85
58
|
const editDialog = ref<InstanceType<typeof DataSourceConfigPanel>>();
|
|
86
59
|
|
|
87
60
|
const dataSourceValues = ref<Record<string, any>>({});
|
|
88
61
|
|
|
89
|
-
const
|
|
62
|
+
const dialogTitle = ref('');
|
|
63
|
+
|
|
64
|
+
const editable = computed(() => dataSourceService?.get('editable') ?? true);
|
|
65
|
+
const datasourceTypeList = computed(() =>
|
|
66
|
+
[
|
|
67
|
+
{ text: '基础', type: 'base' },
|
|
68
|
+
{ text: 'HTTP', type: 'http' },
|
|
69
|
+
].concat(dataSourceService?.get('datasourceTypeList') ?? []),
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const addHandler = (type: string) => {
|
|
90
73
|
if (!editDialog.value) return;
|
|
91
74
|
|
|
92
|
-
|
|
75
|
+
const datasourceType = datasourceTypeList.value.find((item) => item.type === type);
|
|
76
|
+
|
|
77
|
+
dataSourceValues.value = mergeWith(
|
|
78
|
+
{ type, title: datasourceType?.text },
|
|
79
|
+
dataSourceService?.getFormValue(type) || {},
|
|
80
|
+
(objValue, srcValue) => {
|
|
81
|
+
if (Array.isArray(srcValue)) {
|
|
82
|
+
return srcValue;
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
dialogTitle.value = `新增${datasourceType?.text || ''}`;
|
|
93
88
|
|
|
94
89
|
editDialog.value.show();
|
|
95
90
|
};
|
|
96
91
|
|
|
97
92
|
const editHandler = (id: string) => {
|
|
98
|
-
if (!editDialog.value
|
|
93
|
+
if (!editDialog.value) return;
|
|
99
94
|
|
|
100
95
|
dataSourceValues.value = {
|
|
101
96
|
...dataSourceService?.getDataSourceById(id),
|
|
102
97
|
};
|
|
103
98
|
|
|
99
|
+
dialogTitle.value = `新增${dataSourceValues.value.title || ''}`;
|
|
100
|
+
|
|
104
101
|
editDialog.value.show();
|
|
105
102
|
};
|
|
106
103
|
|
|
107
|
-
const removeHandler =
|
|
108
|
-
await tMagicMessageBox.confirm('确定删除?', '提示', {
|
|
109
|
-
confirmButtonText: '确定',
|
|
110
|
-
cancelButtonText: '取消',
|
|
111
|
-
type: 'warning',
|
|
112
|
-
});
|
|
113
|
-
|
|
104
|
+
const removeHandler = (id: string) => {
|
|
114
105
|
dataSourceService?.remove(id);
|
|
115
106
|
};
|
|
116
107
|
|
|
117
108
|
const submitDataSourceHandler = (value: DataSourceSchema) => {
|
|
118
|
-
if (!services) return;
|
|
119
|
-
|
|
120
109
|
if (value.id) {
|
|
121
110
|
dataSourceService?.update(value);
|
|
122
111
|
} else {
|
|
@@ -126,9 +115,9 @@ const submitDataSourceHandler = (value: DataSourceSchema) => {
|
|
|
126
115
|
editDialog.value?.hide();
|
|
127
116
|
};
|
|
128
117
|
|
|
129
|
-
const
|
|
118
|
+
const dataSourceList = ref<InstanceType<typeof DataSourceList>>();
|
|
130
119
|
|
|
131
120
|
const filterTextChangeHandler = (val: string) => {
|
|
132
|
-
|
|
121
|
+
dataSourceList.value?.filter(val);
|
|
133
122
|
};
|
|
134
123
|
</script>
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
width: 60,
|
|
13
13
|
height: 50,
|
|
14
14
|
}"
|
|
15
|
+
@click="stageWrap?.container?.focus()"
|
|
15
16
|
>
|
|
16
17
|
<div
|
|
17
18
|
class="m-editor-stage-container"
|
|
@@ -35,9 +36,9 @@ import type { MContainer } from '@tmagic/schema';
|
|
|
35
36
|
import StageCore, { calcValueByFontsize, getOffset, Runtime } from '@tmagic/stage';
|
|
36
37
|
|
|
37
38
|
import ScrollViewer from '@editor/components/ScrollViewer.vue';
|
|
39
|
+
import { useStage } from '@editor/hooks/use-stage';
|
|
38
40
|
import { Layout, MenuButton, MenuComponent, Services, StageOptions } from '@editor/type';
|
|
39
41
|
import { getConfig } from '@editor/utils/config';
|
|
40
|
-
import { useStage } from '@editor/utils/stage';
|
|
41
42
|
|
|
42
43
|
import ViewerMenu from './ViewerMenu.vue';
|
|
43
44
|
|
|
@@ -146,9 +147,8 @@ const contextmenuHandler = (e: MouseEvent) => {
|
|
|
146
147
|
|
|
147
148
|
const dragoverHandler = (e: DragEvent) => {
|
|
148
149
|
e.preventDefault();
|
|
149
|
-
if (e.dataTransfer)
|
|
150
|
-
|
|
151
|
-
}
|
|
150
|
+
if (!e.dataTransfer) return;
|
|
151
|
+
e.dataTransfer.dropEffect = 'move';
|
|
152
152
|
};
|
|
153
153
|
|
|
154
154
|
const dropHandler = async (e: DragEvent) => {
|
|
@@ -164,7 +164,15 @@ const dropHandler = async (e: DragEvent) => {
|
|
|
164
164
|
|
|
165
165
|
if (e.dataTransfer && parent && stageContainer.value && stage) {
|
|
166
166
|
const parseDSL = getConfig('parseDSL');
|
|
167
|
-
|
|
167
|
+
|
|
168
|
+
const data = e.dataTransfer.getData('text/json');
|
|
169
|
+
|
|
170
|
+
if (!data) return;
|
|
171
|
+
|
|
172
|
+
const config = parseDSL(`(${data})`);
|
|
173
|
+
|
|
174
|
+
if (!config) return;
|
|
175
|
+
|
|
168
176
|
const layout = await services?.editorService.getLayout(parent);
|
|
169
177
|
|
|
170
178
|
const containerRect = stageContainer.value.getBoundingClientRect();
|
|
@@ -4,14 +4,16 @@
|
|
|
4
4
|
|
|
5
5
|
<script lang="ts" setup>
|
|
6
6
|
import { computed, inject, markRaw, ref, watch } from 'vue';
|
|
7
|
-
import { Bottom,
|
|
7
|
+
import { Bottom, Top } from '@element-plus/icons-vue';
|
|
8
8
|
|
|
9
9
|
import { NodeType } from '@tmagic/schema';
|
|
10
10
|
import { isPage } from '@tmagic/utils';
|
|
11
11
|
|
|
12
12
|
import ContentMenu from '@editor/components/ContentMenu.vue';
|
|
13
|
+
import CenterIcon from '@editor/icons/CenterIcon.vue';
|
|
13
14
|
import storageService from '@editor/services/storage';
|
|
14
15
|
import { LayerOffset, Layout, MenuButton, MenuComponent, Services } from '@editor/type';
|
|
16
|
+
import { useCopyMenu, useDeleteMenu, useMoveToMenu, usePasteMenu } from '@editor/utils/content-menu';
|
|
15
17
|
import { COPY_STORAGE_KEY } from '@editor/utils/editor';
|
|
16
18
|
|
|
17
19
|
defineOptions({
|
|
@@ -32,42 +34,20 @@ const canCenter = ref(false);
|
|
|
32
34
|
const node = computed(() => editorService?.get('node'));
|
|
33
35
|
const nodes = computed(() => editorService?.get('nodes'));
|
|
34
36
|
const parent = computed(() => editorService?.get('parent'));
|
|
35
|
-
const stage = computed(() => editorService?.get('stage'));
|
|
36
37
|
|
|
37
38
|
const menuData = computed<(MenuButton | MenuComponent)[]>(() => [
|
|
38
39
|
{
|
|
39
40
|
type: 'button',
|
|
40
41
|
text: '水平居中',
|
|
42
|
+
icon: markRaw(CenterIcon),
|
|
41
43
|
display: () => canCenter.value,
|
|
42
44
|
handler: () => {
|
|
43
45
|
if (!nodes.value) return;
|
|
44
46
|
editorService?.alignCenter(nodes.value);
|
|
45
47
|
},
|
|
46
48
|
},
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
text: '复制',
|
|
50
|
-
icon: markRaw(CopyDocument),
|
|
51
|
-
handler: () => {
|
|
52
|
-
nodes.value && editorService?.copy(nodes.value);
|
|
53
|
-
canPaste.value = true;
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
type: 'button',
|
|
58
|
-
text: '粘贴',
|
|
59
|
-
icon: markRaw(DocumentCopy),
|
|
60
|
-
display: () => canPaste.value,
|
|
61
|
-
handler: () => {
|
|
62
|
-
const rect = menu.value?.$el.getBoundingClientRect();
|
|
63
|
-
const parentRect = stage.value?.container?.getBoundingClientRect();
|
|
64
|
-
const initialLeft = (rect?.left || 0) - (parentRect?.left || 0);
|
|
65
|
-
const initialTop = (rect?.top || 0) - (parentRect?.top || 0);
|
|
66
|
-
|
|
67
|
-
if (!nodes.value || nodes.value.length === 0) return;
|
|
68
|
-
editorService?.paste({ left: initialLeft, top: initialTop });
|
|
69
|
-
},
|
|
70
|
-
},
|
|
49
|
+
useCopyMenu(),
|
|
50
|
+
usePasteMenu(menu),
|
|
71
51
|
{
|
|
72
52
|
type: 'divider',
|
|
73
53
|
direction: 'horizontal',
|
|
@@ -97,6 +77,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() => [
|
|
|
97
77
|
{
|
|
98
78
|
type: 'button',
|
|
99
79
|
text: '置顶',
|
|
80
|
+
icon: markRaw(Top),
|
|
100
81
|
display: () => !isPage(node.value) && !props.isMultiSelect,
|
|
101
82
|
handler: () => {
|
|
102
83
|
editorService?.moveLayer(LayerOffset.TOP);
|
|
@@ -105,25 +86,19 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() => [
|
|
|
105
86
|
{
|
|
106
87
|
type: 'button',
|
|
107
88
|
text: '置底',
|
|
89
|
+
icon: markRaw(Bottom),
|
|
108
90
|
display: () => !isPage(node.value) && !props.isMultiSelect,
|
|
109
91
|
handler: () => {
|
|
110
92
|
editorService?.moveLayer(LayerOffset.BOTTOM);
|
|
111
93
|
},
|
|
112
94
|
},
|
|
95
|
+
useMoveToMenu(services),
|
|
113
96
|
{
|
|
114
97
|
type: 'divider',
|
|
115
98
|
direction: 'horizontal',
|
|
116
99
|
display: () => !isPage(node.value) && !props.isMultiSelect,
|
|
117
100
|
},
|
|
118
|
-
|
|
119
|
-
type: 'button',
|
|
120
|
-
text: '删除',
|
|
121
|
-
icon: Delete,
|
|
122
|
-
display: () => !isPage(node.value),
|
|
123
|
-
handler: () => {
|
|
124
|
-
nodes.value && editorService?.remove(nodes.value);
|
|
125
|
-
},
|
|
126
|
-
},
|
|
101
|
+
useDeleteMenu(),
|
|
127
102
|
{
|
|
128
103
|
type: 'divider',
|
|
129
104
|
direction: 'horizontal',
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
import { reactive } from 'vue';
|
|
20
20
|
import { keys, pick } from 'lodash-es';
|
|
21
21
|
|
|
22
|
-
import {
|
|
22
|
+
import type { ColumnConfig } from '@tmagic/form';
|
|
23
|
+
import type { CodeBlockContent, CodeBlockDSL, Id } from '@tmagic/schema';
|
|
23
24
|
|
|
24
25
|
import type { CodeState } from '@editor/type';
|
|
25
26
|
import { CODE_DRAFT_STORAGE_KEY } from '@editor/type';
|
|
@@ -29,23 +30,15 @@ import BaseService from './BaseService';
|
|
|
29
30
|
|
|
30
31
|
class CodeBlock extends BaseService {
|
|
31
32
|
private state = reactive<CodeState>({
|
|
32
|
-
isShowCodeEditor: false,
|
|
33
33
|
codeDsl: null,
|
|
34
|
-
id: '',
|
|
35
34
|
editable: true,
|
|
36
35
|
combineIds: [],
|
|
37
36
|
undeletableList: [],
|
|
37
|
+
paramsColConfig: undefined,
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
constructor() {
|
|
41
|
-
super([
|
|
42
|
-
'setCodeDslById',
|
|
43
|
-
'setCodeEditorShowStatus',
|
|
44
|
-
'setEditStatus',
|
|
45
|
-
'setCombineIds',
|
|
46
|
-
'setUndeleteableList',
|
|
47
|
-
'deleteCodeDslByIds',
|
|
48
|
-
]);
|
|
41
|
+
super(['setCodeDslById', 'setEditStatus', 'setCombineIds', 'setUndeleteableList', 'deleteCodeDslByIds']);
|
|
49
42
|
}
|
|
50
43
|
|
|
51
44
|
/**
|
|
@@ -86,7 +79,7 @@ class CodeBlock extends BaseService {
|
|
|
86
79
|
* @param {CodeBlockContent} codeConfig 代码块内容配置信息
|
|
87
80
|
* @returns {void}
|
|
88
81
|
*/
|
|
89
|
-
public async setCodeDslById(id: Id, codeConfig: CodeBlockContent): Promise<void> {
|
|
82
|
+
public async setCodeDslById(id: Id, codeConfig: Partial<CodeBlockContent>): Promise<void> {
|
|
90
83
|
const codeDsl = this.getCodeDsl();
|
|
91
84
|
|
|
92
85
|
if (!codeDsl) {
|
|
@@ -97,7 +90,10 @@ class CodeBlock extends BaseService {
|
|
|
97
90
|
if (codeConfig.content) {
|
|
98
91
|
// 在保存的时候转换代码内容
|
|
99
92
|
const parseDSL = getConfig('parseDSL');
|
|
100
|
-
|
|
93
|
+
if (typeof codeConfig.content === 'string') {
|
|
94
|
+
codeConfig.content = parseDSL<(...args: any[]) => any>(codeConfig.content);
|
|
95
|
+
}
|
|
96
|
+
codeConfigProcessed.content = codeConfig.content;
|
|
101
97
|
}
|
|
102
98
|
|
|
103
99
|
const existContent = codeDsl[id] || {};
|
|
@@ -120,43 +116,6 @@ class CodeBlock extends BaseService {
|
|
|
120
116
|
return pick(codeDsl, ids) as CodeBlockDSL;
|
|
121
117
|
}
|
|
122
118
|
|
|
123
|
-
/**
|
|
124
|
-
* 设置代码编辑面板展示状态
|
|
125
|
-
* @param {boolean} status 是否展示代码编辑面板
|
|
126
|
-
* @returns {void}
|
|
127
|
-
*/
|
|
128
|
-
public async setCodeEditorShowStatus(status: boolean): Promise<void> {
|
|
129
|
-
this.state.isShowCodeEditor = status;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* 获取代码编辑面板展示状态
|
|
134
|
-
* @returns {boolean} 是否展示代码编辑面板
|
|
135
|
-
*/
|
|
136
|
-
public getCodeEditorShowStatus(): boolean {
|
|
137
|
-
return this.state.isShowCodeEditor;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* 设置代码编辑面板展示状态及展示内容
|
|
142
|
-
* @param {boolean} status 是否展示代码编辑面板
|
|
143
|
-
* @param {Id} id 代码块id
|
|
144
|
-
* @returns {void}
|
|
145
|
-
*/
|
|
146
|
-
public setCodeEditorContent(status: boolean, id: Id): void {
|
|
147
|
-
if (!id) return;
|
|
148
|
-
this.setId(id);
|
|
149
|
-
this.state.isShowCodeEditor = status;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* 获取当前选中的代码块内容
|
|
154
|
-
* @returns {CodeBlockContent | null}
|
|
155
|
-
*/
|
|
156
|
-
public getCurrentDsl(): CodeBlockContent | null {
|
|
157
|
-
return this.getCodeContentById(this.state.id);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
119
|
/**
|
|
161
120
|
* 获取编辑状态
|
|
162
121
|
* @returns {boolean} 是否可编辑
|
|
@@ -174,24 +133,6 @@ class CodeBlock extends BaseService {
|
|
|
174
133
|
this.state.editable = status;
|
|
175
134
|
}
|
|
176
135
|
|
|
177
|
-
/**
|
|
178
|
-
* 设置当前选中的代码块ID
|
|
179
|
-
* @param {Id} id 代码块id
|
|
180
|
-
* @returns {void}
|
|
181
|
-
*/
|
|
182
|
-
public setId(id: Id): void {
|
|
183
|
-
if (!id) return;
|
|
184
|
-
this.state.id = id;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* 获取当前选中的代码块ID
|
|
189
|
-
* @returns {Id} id 代码块id
|
|
190
|
-
*/
|
|
191
|
-
public getId(): Id {
|
|
192
|
-
return this.state.id;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
136
|
/**
|
|
196
137
|
* 设置当前选中组件已关联绑定的代码块id数组
|
|
197
138
|
* @param {string[]} ids 代码块id数组
|
|
@@ -263,11 +204,19 @@ class CodeBlock extends BaseService {
|
|
|
263
204
|
});
|
|
264
205
|
}
|
|
265
206
|
|
|
207
|
+
public setParamsColConfig(config: ColumnConfig): void {
|
|
208
|
+
this.state.paramsColConfig = config;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
public getParamsColConfig(): ColumnConfig | undefined {
|
|
212
|
+
return this.state.paramsColConfig;
|
|
213
|
+
}
|
|
214
|
+
|
|
266
215
|
/**
|
|
267
216
|
* 生成代码块唯一id
|
|
268
217
|
* @returns {Id} 代码块唯一id
|
|
269
218
|
*/
|
|
270
|
-
public async getUniqueId(): Promise<
|
|
219
|
+
public async getUniqueId(): Promise<string> {
|
|
271
220
|
const newId = `code_${Math.random().toString(10).substring(2).substring(0, 4)}`;
|
|
272
221
|
// 判断是否重复
|
|
273
222
|
const dsl = await this.getCodeDsl();
|
|
@@ -277,9 +226,7 @@ class CodeBlock extends BaseService {
|
|
|
277
226
|
}
|
|
278
227
|
|
|
279
228
|
public resetState() {
|
|
280
|
-
this.state.isShowCodeEditor = false;
|
|
281
229
|
this.state.codeDsl = null;
|
|
282
|
-
this.state.id = '';
|
|
283
230
|
this.state.editable = true;
|
|
284
231
|
this.state.combineIds = [];
|
|
285
232
|
this.state.undeletableList = [];
|
|
@@ -2,23 +2,30 @@ import { reactive } from 'vue';
|
|
|
2
2
|
import { cloneDeep } from 'lodash-es';
|
|
3
3
|
|
|
4
4
|
import type { FormConfig } from '@tmagic/form';
|
|
5
|
-
import { DataSourceSchema } from '@tmagic/schema';
|
|
5
|
+
import type { DataSourceSchema } from '@tmagic/schema';
|
|
6
6
|
import { guid } from '@tmagic/utils';
|
|
7
7
|
|
|
8
|
+
import type { DatasourceTypeOption } from '@editor/type';
|
|
8
9
|
import { getFormConfig } from '@editor/utils/data-source';
|
|
9
10
|
|
|
10
11
|
import BaseService from './BaseService';
|
|
11
12
|
|
|
12
13
|
interface State {
|
|
14
|
+
datasourceTypeList: DatasourceTypeOption[];
|
|
13
15
|
dataSources: DataSourceSchema[];
|
|
16
|
+
editable: boolean;
|
|
14
17
|
configs: Record<string, FormConfig>;
|
|
18
|
+
values: Record<string, Partial<DataSourceSchema>>;
|
|
15
19
|
}
|
|
16
20
|
|
|
17
21
|
type StateKey = keyof State;
|
|
18
22
|
class DataSource extends BaseService {
|
|
19
23
|
private state = reactive<State>({
|
|
24
|
+
datasourceTypeList: [],
|
|
20
25
|
dataSources: [],
|
|
26
|
+
editable: true,
|
|
21
27
|
configs: {},
|
|
28
|
+
values: {},
|
|
22
29
|
});
|
|
23
30
|
|
|
24
31
|
public set<K extends StateKey, T extends State[K]>(name: K, value: T) {
|
|
@@ -37,6 +44,14 @@ class DataSource extends BaseService {
|
|
|
37
44
|
this.get('configs')[type] = config;
|
|
38
45
|
}
|
|
39
46
|
|
|
47
|
+
public getFormValue(type = 'base') {
|
|
48
|
+
return this.get('values')[type];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public setFormValue(type: string, value: Partial<DataSourceSchema>) {
|
|
52
|
+
this.get('values')[type] = value;
|
|
53
|
+
}
|
|
54
|
+
|
|
40
55
|
public add(config: DataSourceSchema) {
|
|
41
56
|
const newConfig = {
|
|
42
57
|
...config,
|