@tmagic/editor 1.2.0-beta.2 → 1.2.0-beta.21
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 +41 -28
- package/dist/{tmagic-editor.mjs → tmagic-editor.js} +1693 -1272
- package/dist/tmagic-editor.js.map +1 -0
- package/dist/{tmagic-editor.umd.js → tmagic-editor.umd.cjs} +1700 -1274
- package/dist/tmagic-editor.umd.cjs.map +1 -0
- package/package.json +16 -14
- package/src/Editor.vue +54 -37
- package/src/components/CodeDraftEditor.vue +145 -0
- package/src/components/ContentMenu.vue +4 -4
- package/src/components/FunctionEditor.vue +145 -0
- package/src/components/Icon.vue +7 -5
- package/src/components/Layout.vue +149 -0
- package/src/components/ScrollBar.vue +1 -1
- package/src/components/ScrollViewer.vue +10 -1
- package/src/components/ToolButton.vue +30 -15
- package/src/fields/Code.vue +1 -1
- package/src/fields/CodeLink.vue +1 -1
- package/src/fields/CodeSelect.vue +89 -110
- package/src/fields/UISelect.vue +9 -8
- package/src/index.ts +1 -0
- package/src/layouts/AddPageBox.vue +3 -2
- package/src/layouts/CodeEditor.vue +2 -3
- package/src/layouts/Framework.vue +62 -40
- package/src/layouts/NavMenu.vue +1 -1
- package/src/layouts/PropsPanel.vue +11 -13
- package/src/layouts/Resizer.vue +1 -1
- package/src/layouts/sidebar/ComponentListPanel.vue +15 -13
- package/src/layouts/sidebar/LayerMenu.vue +9 -7
- package/src/layouts/sidebar/LayerPanel.vue +277 -177
- package/src/layouts/sidebar/Sidebar.vue +113 -27
- package/src/layouts/sidebar/code-block/CodeBlockEditor.vue +34 -85
- package/src/layouts/sidebar/code-block/CodeBlockList.vue +55 -89
- package/src/layouts/workspace/Breadcrumb.vue +32 -0
- package/src/layouts/workspace/PageBar.vue +12 -11
- package/src/layouts/workspace/PageBarScrollContainer.vue +1 -1
- package/src/layouts/workspace/Stage.vue +26 -9
- package/src/layouts/workspace/ViewerMenu.vue +11 -9
- package/src/layouts/workspace/Workspace.vue +10 -3
- package/src/services/BaseService.ts +6 -1
- package/src/services/codeBlock.ts +124 -125
- package/src/services/componentList.ts +6 -1
- package/src/services/editor.ts +43 -19
- package/src/services/events.ts +6 -1
- package/src/services/history.ts +7 -16
- package/src/services/props.ts +7 -2
- package/src/services/storage.ts +1 -0
- package/src/services/ui.ts +12 -0
- package/src/theme/breadcrumb.scss +6 -0
- package/src/theme/code-block.scss +30 -21
- package/src/theme/component-list-panel.scss +3 -7
- package/src/theme/layer-panel.scss +7 -2
- package/src/theme/theme.scss +1 -0
- package/src/type.ts +76 -50
- package/src/utils/editor.ts +9 -1
- package/src/utils/index.ts +1 -0
- package/src/utils/props.ts +2 -2
- package/src/utils/scroll-viewer.ts +18 -2
- package/src/utils/stage.ts +9 -2
- package/types/Editor.vue.d.ts +42 -29
- package/types/components/CodeDraftEditor.vue.d.ts +148 -0
- package/types/components/ContentMenu.vue.d.ts +1 -3
- package/types/components/FunctionEditor.vue.d.ts +120 -0
- package/types/components/Icon.vue.d.ts +1 -3
- package/types/{layouts → components}/Layout.vue.d.ts +12 -1
- package/types/components/ScrollBar.vue.d.ts +1 -3
- package/types/components/ScrollViewer.vue.d.ts +45 -1
- package/types/components/ToolButton.vue.d.ts +1 -3
- package/types/fields/Code.vue.d.ts +1 -3
- package/types/fields/CodeLink.vue.d.ts +1 -3
- package/types/fields/CodeSelect.vue.d.ts +10 -12
- package/types/fields/UISelect.vue.d.ts +1 -3
- package/types/index.d.ts +1 -0
- package/types/layouts/AddPageBox.vue.d.ts +1 -3
- package/types/layouts/CodeEditor.vue.d.ts +6 -15
- package/types/layouts/NavMenu.vue.d.ts +1 -3
- package/types/layouts/sidebar/LayerMenu.vue.d.ts +25 -9
- package/types/layouts/sidebar/LayerPanel.vue.d.ts +38 -886
- package/types/layouts/sidebar/Sidebar.vue.d.ts +10 -5
- package/types/layouts/sidebar/code-block/CodeBlockEditor.vue.d.ts +1 -1
- package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +6 -5
- package/types/layouts/{sidebar/TabPane.vue.d.ts → workspace/Breadcrumb.vue.d.ts} +5 -43
- package/types/layouts/workspace/Stage.vue.d.ts +25 -8
- package/types/layouts/workspace/ViewerMenu.vue.d.ts +6 -3
- package/types/layouts/workspace/Workspace.vue.d.ts +23 -5
- package/types/services/BaseService.d.ts +2 -1
- package/types/services/codeBlock.d.ts +57 -36
- package/types/services/componentList.d.ts +1 -0
- package/types/services/editor.d.ts +5 -6
- package/types/services/events.d.ts +1 -0
- package/types/services/history.d.ts +5 -15
- package/types/services/props.d.ts +3 -2
- package/types/services/ui.d.ts +2 -1
- package/types/type.d.ts +64 -48
- package/types/utils/editor.d.ts +1 -0
- package/types/utils/index.d.ts +1 -0
- package/types/utils/props.d.ts +1 -1
- package/types/utils/scroll-viewer.d.ts +5 -0
- package/dist/tmagic-editor.mjs.map +0 -1
- package/dist/tmagic-editor.umd.js.map +0 -1
- package/src/layouts/Layout.vue +0 -100
- package/src/layouts/sidebar/TabPane.vue +0 -116
|
@@ -1,61 +1,147 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<TMagicTabs
|
|
3
3
|
v-if="data.type === 'tabs' && data.items.length"
|
|
4
4
|
class="m-editor-sidebar"
|
|
5
5
|
v-model="activeTabName"
|
|
6
6
|
type="card"
|
|
7
7
|
tab-position="left"
|
|
8
8
|
>
|
|
9
|
-
<
|
|
10
|
-
<template #
|
|
11
|
-
<
|
|
9
|
+
<component :is="uiComponent.component" v-for="(config, index) in sideBarItems" :key="index" :name="config.text">
|
|
10
|
+
<template #label>
|
|
11
|
+
<div :key="config.text">
|
|
12
|
+
<MIcon v-if="config.icon" :icon="config.icon"></MIcon>
|
|
13
|
+
<div v-if="config.text" class="magic-editor-tab-panel-title">{{ config.text }}</div>
|
|
14
|
+
</div>
|
|
12
15
|
</template>
|
|
13
16
|
|
|
14
|
-
<
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
+
<component v-if="config" :is="config.component" v-bind="config.props || {}" v-on="config?.listeners || {}">
|
|
18
|
+
<template
|
|
19
|
+
#component-list-panel-header
|
|
20
|
+
v-if="config.$key === 'component-list' || config.slots?.componentListPanelHeader"
|
|
21
|
+
>
|
|
22
|
+
<slot v-if="config.$key === 'component-list'" name="component-list-panel-header"></slot>
|
|
23
|
+
<component v-else-if="config.slots?.componentListPanelHeader" :is="config.slots.componentListPanelHeader" />
|
|
24
|
+
</template>
|
|
17
25
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
26
|
+
<template
|
|
27
|
+
#component-list-item="{ component }"
|
|
28
|
+
v-if="config.$key === 'component-list' || config.slots?.componentListItem"
|
|
29
|
+
>
|
|
30
|
+
<slot v-if="config.$key === 'component-list'" name="component-list-item" :component="component"></slot>
|
|
31
|
+
<component
|
|
32
|
+
v-else-if="config.slots?.componentListItem"
|
|
33
|
+
:is="config.slots.componentListItem"
|
|
34
|
+
:component="component"
|
|
35
|
+
/>
|
|
36
|
+
</template>
|
|
21
37
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
38
|
+
<template #layer-panel-header v-if="config.$key === 'layer' || config.slots?.layerPanelHeader">
|
|
39
|
+
<slot v-if="config.$key === 'layer'" name="layer-panel-header"></slot>
|
|
40
|
+
<component v-else-if="config.slots?.layerPanelHeader" :is="config.slots.layerPanelHeader" />
|
|
41
|
+
</template>
|
|
25
42
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
43
|
+
<template #code-block-panel-header v-if="config.$key === 'code-block' || config.slots?.codeBlockPanelHeader">
|
|
44
|
+
<slot v-if="config.$key === 'code-block'" name="code-block-panel-header"></slot>
|
|
45
|
+
<component v-else-if="config.slots?.codeBlockPanelHeader" :is="config.slots.codeBlockPanelHeader" />
|
|
46
|
+
</template>
|
|
29
47
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
48
|
+
<template
|
|
49
|
+
#code-block-panel-tool="{ id, data }"
|
|
50
|
+
v-if="config.$key === 'code-block' || config.slots?.codeBlockPanelTool"
|
|
51
|
+
>
|
|
52
|
+
<slot v-if="config.$key === 'code-block'" name="code-block-panel-tool" :id="id" :data="data"></slot>
|
|
53
|
+
<component v-else-if="config.slots?.codeBlockPanelTool" :is="config.slots.codeBlockPanelTool" />
|
|
54
|
+
</template>
|
|
33
55
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
56
|
+
<template
|
|
57
|
+
#code-block-edit-panel-header="{ id }"
|
|
58
|
+
v-if="config.$key === 'code-block' || config.slots?.codeBlockEditPanelHeader"
|
|
59
|
+
>
|
|
60
|
+
<slot v-if="config.$key === 'code-block'" name="code-block-edit-panel-header" :id="id"></slot>
|
|
61
|
+
<component v-else-if="config.slots?.codeBlockEditPanelHeader" :is="config.slots.codeBlockEditPanelHeader" />
|
|
62
|
+
</template>
|
|
63
|
+
|
|
64
|
+
<template
|
|
65
|
+
#layer-node-content="{ data: nodeData, node }"
|
|
66
|
+
v-if="config.$key === 'layer' || config.slots?.layerNodeContent"
|
|
67
|
+
>
|
|
68
|
+
<slot v-if="config.$key === 'layer'" name="layer-node-content" :data="nodeData" :node="node"></slot>
|
|
69
|
+
<component
|
|
70
|
+
v-else-if="config.slots?.layerNodeContent"
|
|
71
|
+
:is="config.slots.layerNodeContent"
|
|
72
|
+
:data="nodeData"
|
|
73
|
+
:node="node"
|
|
74
|
+
/>
|
|
75
|
+
</template>
|
|
76
|
+
</component>
|
|
77
|
+
</component>
|
|
78
|
+
</TMagicTabs>
|
|
39
79
|
</template>
|
|
40
80
|
|
|
41
|
-
<script lang="ts" setup>
|
|
42
|
-
import { ref, watch } from 'vue';
|
|
81
|
+
<script lang="ts" setup name="MEditorSidebar">
|
|
82
|
+
import { computed, ref, watch } from 'vue';
|
|
83
|
+
import { Coin, EditPen, Files } from '@element-plus/icons-vue';
|
|
84
|
+
|
|
85
|
+
import { getConfig, TMagicTabs } from '@tmagic/design';
|
|
43
86
|
|
|
87
|
+
import MIcon from '../../components/Icon.vue';
|
|
88
|
+
import type { MenuButton, MenuComponent, SideComponent, SideItem } from '../../type';
|
|
44
89
|
import { SideBarData } from '../../type';
|
|
45
90
|
|
|
46
|
-
import
|
|
91
|
+
import CodeBlockList from './code-block/CodeBlockList.vue';
|
|
92
|
+
import ComponentListPanel from './ComponentListPanel.vue';
|
|
93
|
+
import LayerPanel from './LayerPanel.vue';
|
|
47
94
|
|
|
48
95
|
const props = withDefaults(
|
|
49
96
|
defineProps<{
|
|
50
97
|
data?: SideBarData;
|
|
98
|
+
layerContentMenu: (MenuButton | MenuComponent)[];
|
|
51
99
|
}>(),
|
|
52
100
|
{
|
|
53
101
|
data: () => ({ type: 'tabs', status: '组件', items: ['component-list', 'layer', 'code-block'] }),
|
|
54
102
|
},
|
|
55
103
|
);
|
|
56
104
|
|
|
105
|
+
const uiComponent = getConfig('components').tabPane;
|
|
106
|
+
|
|
57
107
|
const activeTabName = ref(props.data?.status);
|
|
58
108
|
|
|
109
|
+
const getItemConfig = (data: SideItem): SideComponent => {
|
|
110
|
+
const map: Record<string, SideComponent> = {
|
|
111
|
+
'component-list': {
|
|
112
|
+
$key: 'component-list',
|
|
113
|
+
type: 'component',
|
|
114
|
+
icon: Coin,
|
|
115
|
+
text: '组件',
|
|
116
|
+
component: ComponentListPanel,
|
|
117
|
+
slots: {},
|
|
118
|
+
},
|
|
119
|
+
layer: {
|
|
120
|
+
$key: 'layer',
|
|
121
|
+
type: 'component',
|
|
122
|
+
icon: Files,
|
|
123
|
+
text: '已选组件',
|
|
124
|
+
props: {
|
|
125
|
+
layerContentMenu: props.layerContentMenu,
|
|
126
|
+
},
|
|
127
|
+
component: LayerPanel,
|
|
128
|
+
slots: {},
|
|
129
|
+
},
|
|
130
|
+
'code-block': {
|
|
131
|
+
$key: 'code-block',
|
|
132
|
+
type: 'component',
|
|
133
|
+
icon: EditPen,
|
|
134
|
+
text: '代码编辑',
|
|
135
|
+
component: CodeBlockList,
|
|
136
|
+
slots: {},
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return typeof data === 'string' ? map[data] : data;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const sideBarItems = computed(() => props.data.items.map((item) => getItemConfig(item)));
|
|
144
|
+
|
|
59
145
|
watch(
|
|
60
146
|
() => props.data.status,
|
|
61
147
|
(status) => {
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
2
|
+
<TMagicDialog
|
|
3
|
+
:model-value="true"
|
|
4
4
|
class="code-editor-dialog"
|
|
5
5
|
:title="currentTitle"
|
|
6
6
|
:fullscreen="true"
|
|
7
|
-
:
|
|
7
|
+
:close-on-press-escape="false"
|
|
8
8
|
:append-to-body="true"
|
|
9
|
+
:show-close="false"
|
|
9
10
|
>
|
|
10
|
-
<
|
|
11
|
+
<Layout v-model:left="left" :min-left="45" class="code-editor-layout">
|
|
11
12
|
<!-- 左侧列表 -->
|
|
12
13
|
<template #left v-if="mode === CodeEditorMode.LIST">
|
|
13
|
-
<
|
|
14
|
+
<TMagicTree
|
|
14
15
|
v-if="!isEmpty(state.codeList)"
|
|
15
|
-
|
|
16
|
+
class="side-tree"
|
|
16
17
|
node-key="id"
|
|
17
18
|
empty-text="暂无代码块"
|
|
18
19
|
:data="state.codeList"
|
|
19
20
|
:highlight-current="true"
|
|
20
|
-
@node-click="selectHandler"
|
|
21
21
|
:current-node-key="state.codeList[0].id"
|
|
22
|
-
|
|
22
|
+
@node-click="selectHandler"
|
|
23
23
|
>
|
|
24
24
|
<template #default="{ data }">
|
|
25
25
|
<div :id="data.id" class="list-container">
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
</div>
|
|
29
29
|
</div>
|
|
30
30
|
</template>
|
|
31
|
-
</
|
|
31
|
+
</TMagicTree>
|
|
32
32
|
</template>
|
|
33
33
|
<!-- 右侧区域 -->
|
|
34
34
|
<template #center>
|
|
@@ -41,56 +41,37 @@
|
|
|
41
41
|
]"
|
|
42
42
|
>
|
|
43
43
|
<slot name="code-block-edit-panel-header" :id="id"></slot>
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
v-if="codeConfig"
|
|
54
|
-
ref="codeEditor"
|
|
55
|
-
class="m-editor-container"
|
|
56
|
-
:init-values="`${codeConfig.content}`"
|
|
57
|
-
@save="saveCode"
|
|
58
|
-
:options="{
|
|
59
|
-
tabSize: 2,
|
|
60
|
-
fontSize: 16,
|
|
61
|
-
formatOnPaste: true,
|
|
62
|
-
readOnly: !editable,
|
|
63
|
-
}"
|
|
64
|
-
></magic-code-editor>
|
|
65
|
-
<div class="m-editor-content-bottom" v-if="editable">
|
|
66
|
-
<el-button type="primary" class="button" @click="saveCode">保存</el-button>
|
|
67
|
-
<el-button type="primary" class="button" @click="saveAndClose">关闭</el-button>
|
|
68
|
-
</div>
|
|
69
|
-
<div class="m-editor-content-bottom" v-else>
|
|
70
|
-
<el-button type="primary" class="button" @click="saveAndClose">关闭</el-button>
|
|
71
|
-
</div>
|
|
72
|
-
</div>
|
|
73
|
-
</el-card>
|
|
44
|
+
<FunctionEditor
|
|
45
|
+
v-if="codeConfig"
|
|
46
|
+
:id="id"
|
|
47
|
+
:name="codeConfig.name"
|
|
48
|
+
:content="codeConfig.content"
|
|
49
|
+
:editable="!!editable"
|
|
50
|
+
:autoSaveDraft="mode === CodeEditorMode.EDITOR"
|
|
51
|
+
:codeOptions="codeOptions"
|
|
52
|
+
></FunctionEditor>
|
|
74
53
|
</div>
|
|
75
54
|
</template>
|
|
76
|
-
</
|
|
77
|
-
</
|
|
55
|
+
</Layout>
|
|
56
|
+
</TMagicDialog>
|
|
78
57
|
</template>
|
|
79
58
|
|
|
80
|
-
<script lang="ts" setup>
|
|
59
|
+
<script lang="ts" setup name="MEditorCodeBlockEditor">
|
|
81
60
|
import { computed, inject, reactive, ref, watchEffect } from 'vue';
|
|
82
|
-
import {
|
|
83
|
-
import { forIn, isEmpty } from 'lodash-es';
|
|
84
|
-
import type * as monaco from 'monaco-editor';
|
|
61
|
+
import { cloneDeep, forIn, isEmpty } from 'lodash-es';
|
|
85
62
|
|
|
86
|
-
import
|
|
63
|
+
import { TMagicDialog, TMagicTree } from '@tmagic/design';
|
|
64
|
+
import { CodeBlockContent } from '@tmagic/schema';
|
|
65
|
+
|
|
66
|
+
import FunctionEditor from '../../../components/FunctionEditor.vue';
|
|
67
|
+
import Layout from '../../../components/Layout.vue';
|
|
68
|
+
import type { CodeDslItem, ListState, Services } from '../../../type';
|
|
87
69
|
import { CodeEditorMode } from '../../../type';
|
|
88
|
-
import
|
|
89
|
-
import Layout from '../../Layout.vue';
|
|
70
|
+
import { serializeConfig } from '../../../utils/editor';
|
|
90
71
|
|
|
91
72
|
const services = inject<Services>('services');
|
|
73
|
+
const codeOptions = inject('codeOptions', {});
|
|
92
74
|
|
|
93
|
-
const codeEditor = ref<InstanceType<typeof MagicCodeEditor>>();
|
|
94
75
|
const left = ref(200);
|
|
95
76
|
const currentTitle = ref('');
|
|
96
77
|
// 编辑器当前需展示的代码块内容
|
|
@@ -100,10 +81,6 @@ const state = reactive<ListState>({
|
|
|
100
81
|
codeList: [],
|
|
101
82
|
});
|
|
102
83
|
|
|
103
|
-
// 是否展示代码编辑区
|
|
104
|
-
const isShowCodeBlockEditor = computed(
|
|
105
|
-
() => (codeConfig.value && services?.codeBlockService.getCodeEditorShowStatus()) || false,
|
|
106
|
-
);
|
|
107
84
|
const mode = computed(() => services?.codeBlockService.getMode());
|
|
108
85
|
const id = computed(() => services?.codeBlockService.getId() || '');
|
|
109
86
|
const editable = computed(() => services?.codeBlockService.getEditStatus());
|
|
@@ -111,7 +88,9 @@ const editable = computed(() => services?.codeBlockService.getEditStatus());
|
|
|
111
88
|
const selectedIds = computed(() => services?.codeBlockService.getCombineIds() || []);
|
|
112
89
|
|
|
113
90
|
watchEffect(async () => {
|
|
114
|
-
codeConfig.value = (await services?.codeBlockService.getCodeContentById(id.value)) || null;
|
|
91
|
+
codeConfig.value = cloneDeep(await services?.codeBlockService.getCodeContentById(id.value)) || null;
|
|
92
|
+
if (!codeConfig.value) return;
|
|
93
|
+
codeConfig.value.content = serializeConfig(codeConfig.value.content);
|
|
115
94
|
});
|
|
116
95
|
|
|
117
96
|
watchEffect(async () => {
|
|
@@ -127,37 +106,7 @@ watchEffect(async () => {
|
|
|
127
106
|
currentTitle.value = state.codeList[0]?.name || '';
|
|
128
107
|
});
|
|
129
108
|
|
|
130
|
-
|
|
131
|
-
const saveCode = async (): Promise<boolean> => {
|
|
132
|
-
if (!codeEditor.value || !codeConfig.value || !editable.value) return true;
|
|
133
|
-
|
|
134
|
-
try {
|
|
135
|
-
// 代码内容
|
|
136
|
-
const codeContent = (codeEditor.value.getEditor() as monaco.editor.IStandaloneCodeEditor)?.getValue();
|
|
137
|
-
/* eslint no-eval: "off" */
|
|
138
|
-
codeConfig.value.content = eval(codeContent);
|
|
139
|
-
} catch (e: any) {
|
|
140
|
-
ElMessage.error(e.stack);
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
// 存入dsl
|
|
144
|
-
await services?.codeBlockService.setCodeDslById(id.value, {
|
|
145
|
-
name: codeConfig.value.name,
|
|
146
|
-
content: codeConfig.value.content,
|
|
147
|
-
});
|
|
148
|
-
ElMessage.success('代码保存成功');
|
|
149
|
-
return true;
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
// 保存并关闭
|
|
153
|
-
const saveAndClose = async () => {
|
|
154
|
-
const saveRes = await saveCode();
|
|
155
|
-
if (saveRes) {
|
|
156
|
-
await services?.codeBlockService.setCodeEditorShowStatus(false);
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
const selectHandler = (data: CodeDslList) => {
|
|
109
|
+
const selectHandler = (data: CodeDslItem) => {
|
|
161
110
|
services?.codeBlockService.setId(data.id);
|
|
162
111
|
currentTitle.value = data.name;
|
|
163
112
|
};
|
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
<div class="m-editor-code-block-list">
|
|
3
3
|
<slot name="code-block-panel-header">
|
|
4
4
|
<div class="code-header-wrapper">
|
|
5
|
-
<
|
|
5
|
+
<TMagicInput
|
|
6
6
|
:class="[editable ? 'code-filter-input' : 'code-filter-input-no-btn']"
|
|
7
7
|
size="small"
|
|
8
8
|
placeholder="输入关键字进行过滤"
|
|
9
9
|
clearable
|
|
10
10
|
v-model="filterText"
|
|
11
11
|
@input="filterTextChangeHandler"
|
|
12
|
-
></
|
|
13
|
-
<
|
|
14
|
-
>新增</
|
|
12
|
+
></TMagicInput>
|
|
13
|
+
<TMagicButton class="create-code-button" type="primary" size="small" @click="createCodeBlock" v-if="editable"
|
|
14
|
+
>新增</TMagicButton
|
|
15
15
|
>
|
|
16
16
|
</div>
|
|
17
17
|
</slot>
|
|
18
18
|
|
|
19
19
|
<!-- 代码块列表 -->
|
|
20
|
-
<
|
|
20
|
+
<TMagicTree
|
|
21
21
|
v-if="!isEmpty(state.codeList)"
|
|
22
22
|
ref="tree"
|
|
23
23
|
node-key="id"
|
|
@@ -30,30 +30,30 @@
|
|
|
30
30
|
<template #default="{ data }">
|
|
31
31
|
<div :id="data.id" class="list-container">
|
|
32
32
|
<div class="list-item">
|
|
33
|
-
<
|
|
33
|
+
<span class="code-name">{{ data.name }}({{ data.id }})</span>
|
|
34
34
|
<!-- 右侧工具栏 -->
|
|
35
35
|
<div class="right-tool">
|
|
36
|
-
<
|
|
36
|
+
<TMagicTooltip effect="dark" :content="editable ? '编辑' : '查看'" placement="bottom">
|
|
37
37
|
<Icon :icon="editable ? Edit : View" class="edit-icon" @click.stop="editCode(`${data.id}`)"></Icon>
|
|
38
|
-
</
|
|
39
|
-
<
|
|
38
|
+
</TMagicTooltip>
|
|
39
|
+
<TMagicTooltip
|
|
40
40
|
effect="dark"
|
|
41
41
|
content="查看绑定关系"
|
|
42
42
|
placement="bottom"
|
|
43
|
-
v-if="
|
|
43
|
+
v-if="data.combineInfo && data.combineInfo.length > 0"
|
|
44
44
|
>
|
|
45
45
|
<Icon :icon="Link" class="edit-icon" @click.stop="toggleCombineRelation(data)"></Icon>
|
|
46
|
-
</
|
|
47
|
-
<
|
|
46
|
+
</TMagicTooltip>
|
|
47
|
+
<TMagicTooltip effect="dark" content="删除" placement="bottom" v-if="editable">
|
|
48
48
|
<Icon :icon="Close" class="edit-icon" @click.stop="deleteCode(`${data.id}`)"></Icon>
|
|
49
|
-
</
|
|
49
|
+
</TMagicTooltip>
|
|
50
50
|
<slot name="code-block-panel-tool" :id="data.id" :data="data.codeBlockContent"></slot>
|
|
51
51
|
</div>
|
|
52
52
|
</div>
|
|
53
53
|
<!-- 展示代码块下绑定的组件 -->
|
|
54
54
|
<div
|
|
55
55
|
class="code-comp-map-wrapper"
|
|
56
|
-
v-if="data.showRelation &&
|
|
56
|
+
v-if="data.showRelation && data.combineInfo && data.combineInfo.length > 0"
|
|
57
57
|
>
|
|
58
58
|
<svg class="arrow-left" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" data-v-029747aa="">
|
|
59
59
|
<path
|
|
@@ -61,31 +61,22 @@
|
|
|
61
61
|
d="M609.408 149.376 277.76 489.6a32 32 0 0 0 0 44.672l331.648 340.352a29.12 29.12 0 0 0 41.728 0 30.592 30.592 0 0 0 0-42.752L339.264 511.936l311.872-319.872a30.592 30.592 0 0 0 0-42.688 29.12 29.12 0 0 0-41.728 0z"
|
|
62
62
|
></path>
|
|
63
63
|
</svg>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
v-for="(comp, index) in state.bindComps[data.id]"
|
|
64
|
+
<TMagicButton
|
|
65
|
+
v-for="(comp, index) in data.combineInfo"
|
|
67
66
|
:key="index"
|
|
68
67
|
class="code-comp"
|
|
69
68
|
size="small"
|
|
70
69
|
:plain="true"
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
<el-button
|
|
74
|
-
v-for="(comp, index) in state.bindComps[data.id]"
|
|
75
|
-
:key="index"
|
|
76
|
-
class="code-comp"
|
|
77
|
-
size="small"
|
|
78
|
-
:plain="true"
|
|
79
|
-
@click.stop="selectComp(comp.id)"
|
|
80
|
-
>{{ comp.name }}</el-button
|
|
70
|
+
@click.stop="selectComp(comp.compId)"
|
|
71
|
+
>{{ comp.compName }}</TMagicButton
|
|
81
72
|
>
|
|
82
73
|
</div>
|
|
83
74
|
</div>
|
|
84
75
|
</template>
|
|
85
|
-
</
|
|
76
|
+
</TMagicTree>
|
|
86
77
|
|
|
87
78
|
<!-- 代码块编辑区 -->
|
|
88
|
-
<code-block-editor>
|
|
79
|
+
<code-block-editor v-if="isShowCodeBlockEditor">
|
|
89
80
|
<template #code-block-edit-panel-header="{ id }">
|
|
90
81
|
<slot name="code-block-edit-panel-header" :id="id"></slot>
|
|
91
82
|
</template>
|
|
@@ -93,69 +84,68 @@
|
|
|
93
84
|
</div>
|
|
94
85
|
</template>
|
|
95
86
|
|
|
96
|
-
<script lang="ts" setup>
|
|
87
|
+
<script lang="ts" setup name="MEditorCodeBlockList">
|
|
97
88
|
import { computed, inject, reactive, ref, watch } from 'vue';
|
|
98
89
|
import { Close, Edit, Link, View } from '@element-plus/icons-vue';
|
|
99
|
-
import {
|
|
100
|
-
import { forIn, isEmpty } from 'lodash-es';
|
|
90
|
+
import { cloneDeep, forIn, isEmpty } from 'lodash-es';
|
|
101
91
|
|
|
102
|
-
import {
|
|
92
|
+
import { TMagicButton, TMagicInput, tMagicMessage, TMagicTooltip, TMagicTree } from '@tmagic/design';
|
|
93
|
+
import { CodeBlockContent, Id } from '@tmagic/schema';
|
|
103
94
|
import StageCore from '@tmagic/stage';
|
|
104
95
|
|
|
105
96
|
import Icon from '../../../components/Icon.vue';
|
|
106
|
-
import type {
|
|
107
|
-
import { CodeDeleteErrorType,
|
|
97
|
+
import type { CodeRelation, Services } from '../../../type';
|
|
98
|
+
import { CodeDeleteErrorType, CodeDslItem, CodeEditorMode, ListState } from '../../../type';
|
|
108
99
|
|
|
109
100
|
import codeBlockEditor from './CodeBlockEditor.vue';
|
|
110
101
|
|
|
111
102
|
const props = defineProps<{
|
|
112
|
-
customError?: (id:
|
|
103
|
+
customError?: (id: Id, errorType: CodeDeleteErrorType) => any;
|
|
113
104
|
}>();
|
|
114
105
|
|
|
115
106
|
const services = inject<Services>('services');
|
|
116
107
|
|
|
117
108
|
// 代码块列表
|
|
118
|
-
const state = reactive<
|
|
109
|
+
const state = reactive<ListState>({
|
|
119
110
|
codeList: [],
|
|
120
|
-
bindComps: {},
|
|
121
111
|
});
|
|
122
112
|
|
|
123
113
|
const editable = computed(() => services?.codeBlockService.getEditStatus());
|
|
124
114
|
|
|
115
|
+
// 是否展示代码编辑区
|
|
116
|
+
const isShowCodeBlockEditor = computed(() => services?.codeBlockService.getCodeEditorShowStatus() || false);
|
|
117
|
+
// 获取绑定关系
|
|
118
|
+
const codeCombineInfo = ref<CodeRelation | null>(null);
|
|
119
|
+
|
|
125
120
|
// 根据代码块ID获取其绑定的组件信息
|
|
126
|
-
const getBindCompsByCodeId = (codeId:
|
|
127
|
-
if (
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const bindCompIds = Object.keys(compsField);
|
|
133
|
-
const bindCompsFiltered = bindCompIds.filter((compId) => !isEmpty(compsField[compId]));
|
|
134
|
-
const compsInfo = bindCompsFiltered.map((compId) => ({
|
|
135
|
-
id: compId,
|
|
136
|
-
name: getCompName(compId),
|
|
121
|
+
const getBindCompsByCodeId = (codeId: Id) => {
|
|
122
|
+
if (!codeCombineInfo.value || !codeCombineInfo.value[codeId]) return [];
|
|
123
|
+
const bindCompsId = Object.keys(codeCombineInfo.value[codeId]);
|
|
124
|
+
return bindCompsId.map((compId) => ({
|
|
125
|
+
compId,
|
|
126
|
+
compName: getCompName(compId),
|
|
137
127
|
}));
|
|
138
|
-
state.bindComps[codeId] = compsInfo;
|
|
139
128
|
};
|
|
140
129
|
|
|
141
130
|
// 初始化代码块列表
|
|
142
131
|
const initList = async () => {
|
|
143
|
-
const codeDsl = (await services?.codeBlockService.getCodeDsl()) || null;
|
|
144
|
-
|
|
132
|
+
const codeDsl = cloneDeep(await services?.codeBlockService.getCodeDsl()) || null;
|
|
133
|
+
codeCombineInfo.value = cloneDeep(services?.codeBlockService.getCombineInfo()) || null;
|
|
134
|
+
if (!codeDsl || !codeCombineInfo.value) return;
|
|
145
135
|
state.codeList = [];
|
|
146
|
-
forIn(codeDsl, (value: CodeBlockContent, codeId:
|
|
147
|
-
getBindCompsByCodeId(codeId, value);
|
|
136
|
+
forIn(codeDsl, (value: CodeBlockContent, codeId: Id) => {
|
|
148
137
|
state.codeList.push({
|
|
149
138
|
id: codeId,
|
|
150
139
|
name: value.name,
|
|
151
140
|
codeBlockContent: value,
|
|
152
141
|
showRelation: true,
|
|
142
|
+
combineInfo: getBindCompsByCodeId(codeId),
|
|
153
143
|
});
|
|
154
144
|
});
|
|
155
145
|
};
|
|
156
146
|
|
|
157
147
|
watch(
|
|
158
|
-
() => services?.codeBlockService.
|
|
148
|
+
[() => services?.codeBlockService.getCodeDslSync(), () => services?.codeBlockService.refreshCombineInfo()],
|
|
159
149
|
() => {
|
|
160
150
|
initList();
|
|
161
151
|
},
|
|
@@ -165,31 +155,17 @@ watch(
|
|
|
165
155
|
},
|
|
166
156
|
);
|
|
167
157
|
|
|
168
|
-
// 监听组件名称修改,更新到代码块列表
|
|
169
|
-
watch(
|
|
170
|
-
() => services?.editorService.get('node'),
|
|
171
|
-
(curNode) => {
|
|
172
|
-
if (!curNode?.id) return;
|
|
173
|
-
forIn(state.bindComps, (bindCompInfo) => {
|
|
174
|
-
bindCompInfo.forEach((comp) => {
|
|
175
|
-
if (comp.id === curNode.id) {
|
|
176
|
-
comp.name = curNode.name;
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
},
|
|
181
|
-
);
|
|
182
|
-
|
|
183
158
|
// 新增代码块
|
|
184
159
|
const createCodeBlock = async () => {
|
|
185
160
|
const { codeBlockService } = services || {};
|
|
186
161
|
if (!codeBlockService) {
|
|
187
|
-
|
|
162
|
+
tMagicMessage.error('新增代码块失败');
|
|
188
163
|
return;
|
|
189
164
|
}
|
|
190
165
|
const codeConfig: CodeBlockContent = {
|
|
191
166
|
name: '代码块',
|
|
192
|
-
content: `() => {\n // place your code here\n}`,
|
|
167
|
+
content: `({app, params}) => {\n // place your code here\n}`,
|
|
168
|
+
params: [],
|
|
193
169
|
};
|
|
194
170
|
await codeBlockService.setMode(CodeEditorMode.EDITOR);
|
|
195
171
|
const id = await codeBlockService.getUniqueId();
|
|
@@ -198,14 +174,15 @@ const createCodeBlock = async () => {
|
|
|
198
174
|
};
|
|
199
175
|
|
|
200
176
|
// 编辑代码块
|
|
201
|
-
const editCode = async (key:
|
|
177
|
+
const editCode = async (key: Id) => {
|
|
202
178
|
await services?.codeBlockService.setMode(CodeEditorMode.EDITOR);
|
|
203
179
|
services?.codeBlockService.setCodeEditorContent(true, key);
|
|
204
180
|
};
|
|
205
181
|
|
|
206
182
|
// 删除代码块
|
|
207
|
-
const deleteCode = (key:
|
|
208
|
-
const
|
|
183
|
+
const deleteCode = (key: Id) => {
|
|
184
|
+
const currentCode = state.codeList.find((codeItem: CodeDslItem) => codeItem.id === key);
|
|
185
|
+
const existBinds = !isEmpty(currentCode?.combineInfo);
|
|
209
186
|
const undeleteableList = services?.codeBlockService.getUndeletableList() || [];
|
|
210
187
|
if (!existBinds && !undeleteableList.includes(key)) {
|
|
211
188
|
// 无绑定关系,且不在不可删除列表中
|
|
@@ -214,7 +191,7 @@ const deleteCode = (key: string) => {
|
|
|
214
191
|
if (typeof props.customError === 'function') {
|
|
215
192
|
props.customError(key, existBinds ? CodeDeleteErrorType.BIND : CodeDeleteErrorType.UNDELETEABLE);
|
|
216
193
|
} else {
|
|
217
|
-
|
|
194
|
+
tMagicMessage.error('代码块删除失败');
|
|
218
195
|
}
|
|
219
196
|
}
|
|
220
197
|
};
|
|
@@ -222,7 +199,7 @@ const deleteCode = (key: string) => {
|
|
|
222
199
|
const filterText = ref('');
|
|
223
200
|
const tree = ref();
|
|
224
201
|
|
|
225
|
-
const filterNode = (value: string, data:
|
|
202
|
+
const filterNode = (value: string, data: CodeDslItem): boolean => {
|
|
226
203
|
if (!value) {
|
|
227
204
|
return true;
|
|
228
205
|
}
|
|
@@ -234,7 +211,7 @@ const filterTextChangeHandler = (val: string) => {
|
|
|
234
211
|
};
|
|
235
212
|
|
|
236
213
|
// 展示/隐藏组件绑定关系
|
|
237
|
-
const toggleCombineRelation = (data:
|
|
214
|
+
const toggleCombineRelation = (data: CodeDslItem) => {
|
|
238
215
|
const { id } = data;
|
|
239
216
|
const currentCode = state.codeList.find((item) => item.id === id);
|
|
240
217
|
if (!currentCode) return;
|
|
@@ -247,17 +224,6 @@ const getCompName = (compId: Id): string => {
|
|
|
247
224
|
return node?.name || String(compId);
|
|
248
225
|
};
|
|
249
226
|
|
|
250
|
-
// todo 功能暂时隐藏
|
|
251
|
-
// 解除绑定
|
|
252
|
-
// const unbind = async (compId: Id, codeId: string) => {
|
|
253
|
-
// const res = await services?.codeBlockService.unbind(compId, codeId, codeHooks);
|
|
254
|
-
// if (res) {
|
|
255
|
-
// ElMessage.success('绑定关系解除成功');
|
|
256
|
-
// } else {
|
|
257
|
-
// ElMessage.error('绑定关系解除失败');
|
|
258
|
-
// }
|
|
259
|
-
// };
|
|
260
|
-
|
|
261
227
|
// 选中组件
|
|
262
228
|
const selectComp = (compId: Id) => {
|
|
263
229
|
const stage = services?.editorService.get<StageCore | null>('stage');
|