@tmagic/editor 1.4.8 → 1.4.9

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.
Files changed (143) hide show
  1. package/package.json +11 -11
  2. package/src/Editor.vue +211 -0
  3. package/src/components/CodeBlockEditor.vue +268 -0
  4. package/src/components/CodeParams.vue +60 -0
  5. package/src/components/ContentMenu.vue +193 -0
  6. package/src/components/FloatingBox.vue +178 -0
  7. package/src/components/Icon.vue +27 -0
  8. package/src/components/Resizer.vue +23 -0
  9. package/src/components/ScrollBar.vue +151 -0
  10. package/src/components/ScrollViewer.vue +128 -0
  11. package/src/components/SearchInput.vue +37 -0
  12. package/src/components/SplitView.vue +186 -0
  13. package/src/components/ToolButton.vue +138 -0
  14. package/src/components/Tree.vue +71 -0
  15. package/src/components/TreeNode.vue +150 -0
  16. package/src/editorProps.ts +117 -0
  17. package/src/fields/Code.vue +50 -0
  18. package/src/fields/CodeLink.vue +80 -0
  19. package/src/fields/CodeSelect.vue +132 -0
  20. package/src/fields/CodeSelectCol.vue +139 -0
  21. package/src/fields/DataSourceFieldSelect.vue +149 -0
  22. package/src/fields/DataSourceFields.vue +323 -0
  23. package/src/fields/DataSourceInput.vue +339 -0
  24. package/src/fields/DataSourceMethodSelect.vue +153 -0
  25. package/src/fields/DataSourceMethods.vue +104 -0
  26. package/src/fields/DataSourceMocks.vue +255 -0
  27. package/src/fields/DataSourceSelect.vue +98 -0
  28. package/src/fields/EventSelect.vue +375 -0
  29. package/src/fields/KeyValue.vue +137 -0
  30. package/src/fields/PageFragmentSelect.vue +63 -0
  31. package/src/fields/UISelect.vue +135 -0
  32. package/src/hooks/index.ts +24 -0
  33. package/src/hooks/use-code-block-edit.ts +83 -0
  34. package/src/hooks/use-data-source-edit.ts +46 -0
  35. package/src/hooks/use-data-source-method.ts +100 -0
  36. package/src/hooks/use-editor-content-height.ts +26 -0
  37. package/src/hooks/use-filter.ts +55 -0
  38. package/src/hooks/use-float-box.ts +76 -0
  39. package/src/hooks/use-getso.ts +35 -0
  40. package/src/hooks/use-next-float-box-position.ts +29 -0
  41. package/src/hooks/use-node-status.ts +48 -0
  42. package/src/hooks/use-stage.ts +129 -0
  43. package/src/hooks/use-window-rect.ts +20 -0
  44. package/src/icons/AppManageIcon.vue +15 -0
  45. package/src/icons/CenterIcon.vue +13 -0
  46. package/src/icons/CodeIcon.vue +28 -0
  47. package/src/icons/FolderMinusIcon.vue +22 -0
  48. package/src/icons/PinIcon.vue +25 -0
  49. package/src/icons/PinnedIcon.vue +25 -0
  50. package/src/index.ts +118 -0
  51. package/src/initService.ts +448 -0
  52. package/src/layouts/AddPageBox.vue +55 -0
  53. package/src/layouts/CodeEditor.vue +226 -0
  54. package/src/layouts/Framework.vue +163 -0
  55. package/src/layouts/NavMenu.vue +200 -0
  56. package/src/layouts/PropsPanel.vue +131 -0
  57. package/src/layouts/page-bar/AddButton.vue +48 -0
  58. package/src/layouts/page-bar/PageBar.vue +159 -0
  59. package/src/layouts/page-bar/PageBarScrollContainer.vue +156 -0
  60. package/src/layouts/page-bar/SwitchTypeButton.vue +45 -0
  61. package/src/layouts/sidebar/ComponentListPanel.vue +134 -0
  62. package/src/layouts/sidebar/Sidebar.vue +278 -0
  63. package/src/layouts/sidebar/code-block/CodeBlockList.vue +160 -0
  64. package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +70 -0
  65. package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +84 -0
  66. package/src/layouts/sidebar/data-source/DataSourceList.vue +172 -0
  67. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +106 -0
  68. package/src/layouts/sidebar/layer/LayerMenu.vue +128 -0
  69. package/src/layouts/sidebar/layer/LayerNodeTool.vue +32 -0
  70. package/src/layouts/sidebar/layer/LayerPanel.vue +119 -0
  71. package/src/layouts/sidebar/layer/use-click.ts +113 -0
  72. package/src/layouts/sidebar/layer/use-drag.ts +167 -0
  73. package/src/layouts/sidebar/layer/use-keybinding.ts +54 -0
  74. package/src/layouts/sidebar/layer/use-node-status.ts +114 -0
  75. package/src/layouts/workspace/Breadcrumb.vue +35 -0
  76. package/src/layouts/workspace/Workspace.vue +46 -0
  77. package/src/layouts/workspace/viewer/NodeListMenu.vue +115 -0
  78. package/src/layouts/workspace/viewer/Stage.vue +246 -0
  79. package/src/layouts/workspace/viewer/StageOverlay.vue +68 -0
  80. package/src/layouts/workspace/viewer/ViewerMenu.vue +145 -0
  81. package/src/services/BaseService.ts +230 -0
  82. package/src/services/codeBlock.ts +327 -0
  83. package/src/services/componentList.ts +58 -0
  84. package/src/services/dataSource.ts +216 -0
  85. package/src/services/dep.ts +152 -0
  86. package/src/services/editor.ts +1135 -0
  87. package/src/services/events.ts +93 -0
  88. package/src/services/history.ts +126 -0
  89. package/src/services/keybinding.ts +220 -0
  90. package/src/services/props.ts +286 -0
  91. package/src/services/stageOverlay.ts +212 -0
  92. package/src/services/storage.ts +155 -0
  93. package/src/services/ui.ts +156 -0
  94. package/src/shims-vue.d.ts +6 -0
  95. package/src/theme/breadcrumb.scss +6 -0
  96. package/src/theme/code-block.scss +6 -0
  97. package/src/theme/code-editor.scss +38 -0
  98. package/src/theme/common/var.scss +14 -0
  99. package/src/theme/component-list-panel.scss +95 -0
  100. package/src/theme/content-menu.scss +76 -0
  101. package/src/theme/data-source-field.scss +12 -0
  102. package/src/theme/data-source-fields.scss +13 -0
  103. package/src/theme/data-source-input.scss +18 -0
  104. package/src/theme/data-source-methods.scss +13 -0
  105. package/src/theme/data-source.scss +28 -0
  106. package/src/theme/event.scss +37 -0
  107. package/src/theme/floating-box.scss +32 -0
  108. package/src/theme/framework.scss +69 -0
  109. package/src/theme/icon.scss +12 -0
  110. package/src/theme/index.scss +10 -0
  111. package/src/theme/key-value.scss +20 -0
  112. package/src/theme/layer-panel.scss +26 -0
  113. package/src/theme/layout.scss +9 -0
  114. package/src/theme/nav-menu.scss +78 -0
  115. package/src/theme/page-bar.scss +89 -0
  116. package/src/theme/page-fragment-select.scss +14 -0
  117. package/src/theme/props-panel.scss +55 -0
  118. package/src/theme/resizer.scss +66 -0
  119. package/src/theme/ruler.scss +38 -0
  120. package/src/theme/search-input.scss +14 -0
  121. package/src/theme/sidebar.scss +79 -0
  122. package/src/theme/stage.scss +76 -0
  123. package/src/theme/theme.scss +27 -0
  124. package/src/theme/tree.scss +89 -0
  125. package/src/theme/workspace.scss +9 -0
  126. package/src/type.ts +749 -0
  127. package/src/utils/compose.ts +52 -0
  128. package/src/utils/config.ts +29 -0
  129. package/src/utils/content-menu.ts +95 -0
  130. package/src/utils/data-source/formConfigs/base.ts +30 -0
  131. package/src/utils/data-source/formConfigs/http.ts +60 -0
  132. package/src/utils/data-source/index.ts +243 -0
  133. package/src/utils/editor.ts +313 -0
  134. package/src/utils/idle-task.ts +72 -0
  135. package/src/utils/index.ts +24 -0
  136. package/src/utils/keybinding-config.ts +125 -0
  137. package/src/utils/logger.ts +47 -0
  138. package/src/utils/monaco-editor.ts +8 -0
  139. package/src/utils/operator.ts +106 -0
  140. package/src/utils/props.ts +504 -0
  141. package/src/utils/scroll-viewer.ts +165 -0
  142. package/src/utils/tree.ts +15 -0
  143. package/src/utils/undo-redo.ts +76 -0
@@ -0,0 +1,278 @@
1
+ <template>
2
+ <div class="m-editor-sidebar" v-if="data.type === 'tabs' && data.items.length">
3
+ <div class="m-editor-sidebar-header">
4
+ <div
5
+ class="m-editor-sidebar-header-item"
6
+ v-for="(config, index) in sideBarItems"
7
+ v-show="!floatBoxStates[config.$key]?.status"
8
+ draggable="true"
9
+ :key="config.$key ?? index"
10
+ :class="{ 'is-active': activeTabName === config.text }"
11
+ :style="config.tabStyle || {}"
12
+ @click="activeTabName = config.text || config.$key || `${index}`"
13
+ @dragstart="dragstartHandler"
14
+ @dragend="dragendHandler(config.$key, $event)"
15
+ >
16
+ <MIcon v-if="config.icon" :icon="config.icon"></MIcon>
17
+ <div v-if="config.text" class="magic-editor-tab-panel-title">{{ config.text }}</div>
18
+ </div>
19
+ </div>
20
+ <div
21
+ class="m-editor-sidebar-content"
22
+ v-for="(config, index) in sideBarItems"
23
+ :key="config.$key ?? index"
24
+ v-show="[config.text, config.$key, `${index}`].includes(activeTabName)"
25
+ >
26
+ <component
27
+ v-if="config && !floatBoxStates[config.$key]?.status"
28
+ :is="config.component"
29
+ v-bind="config.props || {}"
30
+ v-on="config?.listeners || {}"
31
+ >
32
+ <template
33
+ #component-list-panel-header
34
+ v-if="config.$key === 'component-list' || config.slots?.componentListPanelHeader"
35
+ >
36
+ <slot v-if="config.$key === 'component-list'" name="component-list-panel-header"></slot>
37
+ <component v-else-if="config.slots?.componentListPanelHeader" :is="config.slots.componentListPanelHeader" />
38
+ </template>
39
+
40
+ <template
41
+ #component-list-item="{ component }"
42
+ v-if="config.$key === 'component-list' || config.slots?.componentListItem"
43
+ >
44
+ <slot v-if="config.$key === 'component-list'" name="component-list-item" :component="component"></slot>
45
+ <component
46
+ v-else-if="config.slots?.componentListItem"
47
+ :is="config.slots.componentListItem"
48
+ :component="component"
49
+ />
50
+ </template>
51
+
52
+ <template #layer-panel-header v-if="config.$key === 'layer' || config.slots?.layerPanelHeader">
53
+ <slot v-if="config.$key === 'layer'" name="layer-panel-header"></slot>
54
+ <component v-else-if="config.slots?.layerPanelHeader" :is="config.slots.layerPanelHeader" />
55
+ </template>
56
+
57
+ <template #code-block-panel-header v-if="config.$key === 'code-block' || config.slots?.codeBlockPanelHeader">
58
+ <slot v-if="config.$key === 'code-block'" name="code-block-panel-header"></slot>
59
+ <component v-else-if="config.slots?.codeBlockPanelHeader" :is="config.slots.codeBlockPanelHeader" />
60
+ </template>
61
+
62
+ <template
63
+ #code-block-panel-tool="{ id, data }"
64
+ v-if="config.$key === 'code-block' || config.slots?.codeBlockPanelTool"
65
+ >
66
+ <slot v-if="config.$key === 'code-block'" name="code-block-panel-tool" :id="id" :data="data"></slot>
67
+ <component v-else-if="config.slots?.codeBlockPanelTool" :is="config.slots.codeBlockPanelTool" />
68
+ </template>
69
+
70
+ <template #code-block-panel-search v-if="config.$key === 'code-block' || config.slots?.codeBlockPanelSearch">
71
+ <slot v-if="config.$key === 'code-block'" name="code-block-panel-search"></slot>
72
+ <component v-else-if="config.slots?.codeBlockPanelSearch" :is="config.slots.codeBlockPanelSearch" />
73
+ </template>
74
+
75
+ <template
76
+ #layer-node-content="{ data: nodeData }"
77
+ v-if="config.$key === 'layer' || config.slots?.layerNodeContent"
78
+ >
79
+ <slot v-if="config.$key === 'layer'" name="layer-node-content" :data="nodeData"></slot>
80
+ <component v-else-if="config.slots?.layerNodeContent" :is="config.slots.layerNodeContent" :data="nodeData" />
81
+ </template>
82
+
83
+ <template #layer-node-label="{ data: nodeData }" v-if="config.$key === 'layer' || config.slots?.layerNodeLabel">
84
+ <slot v-if="config.$key === 'layer'" name="layer-node-label" :data="nodeData"></slot>
85
+ <component v-else-if="config.slots?.layerNodeLabel" :is="config.slots.layerNodeTool" :data="nodeData" />
86
+ </template>
87
+
88
+ <template #layer-node-tool="{ data: nodeData }" v-if="config.$key === 'layer' || config.slots?.layerNodeTool">
89
+ <slot v-if="config.$key === 'layer'" name="layer-node-tool" :data="nodeData"></slot>
90
+ <component v-else-if="config.slots?.layerNodeTool" :is="config.slots.layerNodeTool" :data="nodeData" />
91
+ </template>
92
+
93
+ <template
94
+ #data-source-panel-tool="{ data }"
95
+ v-if="config.$key === 'data-source' || config.slots?.dataSourcePanelTool"
96
+ >
97
+ <slot v-if="config.$key === 'data-source'" name="data-source-panel-tool" :data="data"></slot>
98
+ <component v-else-if="config.slots?.DataSourcePanelTool" :is="config.slots.DataSourcePanelTool" />
99
+ </template>
100
+
101
+ <template #data-source-panel-search v-if="config.$key === 'data-source' || config.slots?.dataSourcePanelSearch">
102
+ <slot v-if="config.$key === 'data-source'" name="data-source-panel-search"></slot>
103
+ <component v-else-if="config.slots?.dataSourcePanelSearch" :is="config.slots.dataSourcePanelSearch" />
104
+ </template>
105
+ </component>
106
+ </div>
107
+ </div>
108
+
109
+ <Teleport to="body">
110
+ <template v-for="(config, index) in sideBarItems">
111
+ <FloatingBox
112
+ :key="config.$key ?? index"
113
+ v-if="floatBoxStates[config.$key]?.status"
114
+ v-model:visible="floatBoxStates[config.$key].status"
115
+ v-model:height="columnLeftHeight"
116
+ :width="columnLeftWidth"
117
+ :title="config.text"
118
+ :position="{
119
+ left: floatBoxStates[config.$key].left,
120
+ top: floatBoxStates[config.$key].top,
121
+ }"
122
+ >
123
+ <template #body>
124
+ <div class="m-editor-slide-list-box">
125
+ <component
126
+ v-if="config && floatBoxStates[config.$key].status"
127
+ :is="config.component"
128
+ v-bind="config.props || {}"
129
+ v-on="config?.listeners || {}"
130
+ />
131
+ </div>
132
+ </template>
133
+ </FloatingBox>
134
+ </template>
135
+ </Teleport>
136
+ </template>
137
+
138
+ <script setup lang="ts">
139
+ import { computed, inject, ref, watch } from 'vue';
140
+ import { Coin, EditPen, Goods, List } from '@element-plus/icons-vue';
141
+
142
+ import FloatingBox from '@editor/components/FloatingBox.vue';
143
+ import MIcon from '@editor/components/Icon.vue';
144
+ import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height';
145
+ import { useFloatBox } from '@editor/hooks/use-float-box';
146
+ import {
147
+ ColumnLayout,
148
+ type MenuButton,
149
+ type MenuComponent,
150
+ type Services,
151
+ type SideBarData,
152
+ type SidebarSlots,
153
+ type SideComponent,
154
+ type SideItem,
155
+ SideItemKey,
156
+ } from '@editor/type';
157
+
158
+ import CodeBlockListPanel from './code-block/CodeBlockListPanel.vue';
159
+ import DataSourceListPanel from './data-source/DataSourceListPanel.vue';
160
+ import LayerPanel from './layer/LayerPanel.vue';
161
+ import ComponentListPanel from './ComponentListPanel.vue';
162
+
163
+ defineSlots<SidebarSlots>();
164
+
165
+ defineOptions({
166
+ name: 'MEditorSidebar',
167
+ });
168
+
169
+ const props = withDefaults(
170
+ defineProps<{
171
+ data: SideBarData;
172
+ layerContentMenu: (MenuButton | MenuComponent)[];
173
+ customContentMenu?: (menus: (MenuButton | MenuComponent)[], type: string) => (MenuButton | MenuComponent)[];
174
+ }>(),
175
+ {
176
+ data: () => ({
177
+ type: 'tabs',
178
+ status: '组件',
179
+ items: [SideItemKey.COMPONENT_LIST, SideItemKey.LAYER, SideItemKey.CODE_BLOCK, SideItemKey.DATA_SOURCE],
180
+ }),
181
+ },
182
+ );
183
+
184
+ const services = inject<Services>('services');
185
+
186
+ const columnLeftWidth = computed(() => services?.uiService.get('columnWidth')[ColumnLayout.LEFT] || 0);
187
+ const { height: columnLeftHeight } = useEditorContentHeight();
188
+
189
+ const activeTabName = ref(props.data?.status);
190
+
191
+ const getItemConfig = (data: SideItem): SideComponent => {
192
+ const map: Record<string, SideComponent> = {
193
+ [SideItemKey.COMPONENT_LIST]: {
194
+ $key: SideItemKey.COMPONENT_LIST,
195
+ type: 'component',
196
+ icon: Goods,
197
+ text: '组件',
198
+ component: ComponentListPanel,
199
+ slots: {},
200
+ },
201
+ layer: {
202
+ $key: 'layer',
203
+ type: 'component',
204
+ icon: List,
205
+ text: '已选组件',
206
+ props: {
207
+ layerContentMenu: props.layerContentMenu,
208
+ customContentMenu: props.customContentMenu,
209
+ },
210
+ component: LayerPanel,
211
+ slots: {},
212
+ },
213
+ [SideItemKey.CODE_BLOCK]: {
214
+ $key: 'code-block',
215
+ type: 'component',
216
+ icon: EditPen,
217
+ text: '代码编辑',
218
+ component: CodeBlockListPanel,
219
+ slots: {},
220
+ },
221
+ [SideItemKey.DATA_SOURCE]: {
222
+ $key: SideItemKey.DATA_SOURCE,
223
+ type: 'component',
224
+ icon: Coin,
225
+ text: '数据源',
226
+ component: DataSourceListPanel,
227
+ slots: {},
228
+ },
229
+ };
230
+
231
+ return typeof data === 'string' ? map[data] : data;
232
+ };
233
+
234
+ const sideBarItems = computed(() => props.data.items.map((item) => getItemConfig(item)));
235
+
236
+ watch(
237
+ sideBarItems,
238
+ (items) => {
239
+ services?.uiService.set('sideBarItems', items);
240
+ },
241
+ {
242
+ immediate: true,
243
+ },
244
+ );
245
+
246
+ watch(
247
+ () => props.data.status,
248
+ (status) => {
249
+ activeTabName.value = status || '0';
250
+ },
251
+ );
252
+
253
+ const slideKeys = computed(() => sideBarItems.value.map((sideBarItem) => sideBarItem.$key));
254
+
255
+ const { dragstartHandler, dragendHandler, floatBoxStates, showingBoxKeys } = useFloatBox(slideKeys);
256
+
257
+ watch(
258
+ () => showingBoxKeys.value.length,
259
+ () => {
260
+ const isActiveTabShow = showingBoxKeys.value.some(
261
+ (key) => activeTabName.value === sideBarItems.value.find((v) => v.$key === key)?.text,
262
+ );
263
+ if (!isActiveTabShow && activeTabName.value) return;
264
+ const nextSlideBarItem = sideBarItems.value.find((sideBarItem) => !showingBoxKeys.value.includes(sideBarItem.$key));
265
+ if (!nextSlideBarItem) {
266
+ activeTabName.value = '';
267
+ services?.uiService.set('hideSlideBar', true);
268
+ return;
269
+ }
270
+ services?.uiService.set('hideSlideBar', false);
271
+ activeTabName.value = nextSlideBarItem?.text;
272
+ },
273
+ );
274
+
275
+ defineExpose({
276
+ activeTabName,
277
+ });
278
+ </script>
@@ -0,0 +1,160 @@
1
+ <template>
2
+ <Tree :data="codeList" :node-status-map="nodeStatusMap" @node-click="clickHandler">
3
+ <template #tree-node-label="{ data }">
4
+ <div
5
+ :class="{
6
+ code: data.type === 'code',
7
+ hook: data.type === 'key',
8
+ disabled: data.type === 'key' || data.type === 'code',
9
+ }"
10
+ >
11
+ {{ data.name }} {{ data.key ? `(${data.key})` : '' }}
12
+ </div>
13
+ </template>
14
+
15
+ <template #tree-node-tool="{ data }">
16
+ <TMagicTooltip v-if="data.type === 'code'" effect="dark" :content="editable ? '编辑' : '查看'" placement="bottom">
17
+ <Icon :icon="editable ? Edit : View" class="edit-icon" @click.stop="editCode(`${data.key}`)"></Icon>
18
+ </TMagicTooltip>
19
+ <TMagicTooltip v-if="data.type === 'code' && editable" effect="dark" content="删除" placement="bottom">
20
+ <Icon :icon="Close" class="edit-icon" @click.stop="deleteCode(`${data.key}`)"></Icon>
21
+ </TMagicTooltip>
22
+ <slot name="code-block-panel-tool" :id="data.key" :data="data"></slot>
23
+ </template>
24
+ </Tree>
25
+ </template>
26
+
27
+ <script lang="ts" setup>
28
+ import { computed, inject } from 'vue';
29
+ import { Close, Edit, View } from '@element-plus/icons-vue';
30
+
31
+ import { DepTargetType } from '@tmagic/dep';
32
+ import { tMagicMessage, tMagicMessageBox, TMagicTooltip } from '@tmagic/design';
33
+ import type { Id, MNode } from '@tmagic/schema';
34
+
35
+ import Icon from '@editor/components/Icon.vue';
36
+ import Tree from '@editor/components/Tree.vue';
37
+ import { useFilter } from '@editor/hooks/use-filter';
38
+ import { useNodeStatus } from '@editor/hooks/use-node-status';
39
+ import { type CodeBlockListSlots, CodeDeleteErrorType, type Services, type TreeNodeData } from '@editor/type';
40
+
41
+ defineSlots<CodeBlockListSlots>();
42
+
43
+ defineOptions({
44
+ name: 'MEditorCodeBlockList',
45
+ });
46
+
47
+ const props = defineProps<{
48
+ customError?: (id: Id, errorType: CodeDeleteErrorType) => any;
49
+ }>();
50
+
51
+ const emit = defineEmits<{
52
+ edit: [id: string];
53
+ remove: [id: string];
54
+ }>();
55
+
56
+ const services = inject<Services>('services');
57
+ const { codeBlockService, depService, editorService } = services || {};
58
+
59
+ // 代码块列表
60
+ const codeList = computed<TreeNodeData[]>(() =>
61
+ Object.entries(codeBlockService?.getCodeDsl() || {}).map(([codeId, code]) => {
62
+ const target = depService?.getTarget(codeId, DepTargetType.CODE_BLOCK);
63
+
64
+ // 按页面分类显示
65
+ const pageList: TreeNodeData[] =
66
+ editorService?.get('root')?.items.map((page) => ({
67
+ name: page.devconfig?.tabName || page.name,
68
+ type: 'node',
69
+ id: `${codeId}_${page.id}`,
70
+ key: page.id,
71
+ items: [],
72
+ })) || [];
73
+
74
+ // 组件节点
75
+ if (target) {
76
+ Object.entries(target.deps).forEach(([id, dep]) => {
77
+ const page = pageList.find((page) => page.key === dep.data?.pageId);
78
+ page?.items?.push({
79
+ name: dep.name,
80
+ type: 'node',
81
+ id: `${page.id}_${id}`,
82
+ key: id,
83
+ items: dep.keys.map((key) => {
84
+ const data: TreeNodeData = { name: `${key}`, id: `${target.id}_${id}_${key}`, type: 'key' };
85
+ return data;
86
+ }),
87
+ });
88
+ });
89
+ }
90
+
91
+ const data: TreeNodeData = {
92
+ id: codeId,
93
+ key: codeId,
94
+ name: code.name,
95
+ type: 'code',
96
+ codeBlockContent: codeBlockService?.getCodeContentById(codeId),
97
+ // 只有一个页面不显示页面分类
98
+ items: pageList.length > 1 ? pageList.filter((page) => page.items?.length) : pageList[0]?.items || [],
99
+ };
100
+
101
+ return data;
102
+ }),
103
+ );
104
+
105
+ const filterNode = (value: string, data: MNode): boolean => {
106
+ if (!value) {
107
+ return true;
108
+ }
109
+ return `${data.name}${data.id}`.toLocaleLowerCase().includes(value.toLocaleLowerCase());
110
+ };
111
+
112
+ const { nodeStatusMap } = useNodeStatus(codeList);
113
+ const { filterTextChangeHandler } = useFilter(codeList, nodeStatusMap, filterNode);
114
+
115
+ const editable = computed(() => codeBlockService?.getEditStatus());
116
+
117
+ // 选中组件
118
+ const selectComp = (compId: Id) => {
119
+ const stage = editorService?.get('stage');
120
+ editorService?.select(compId);
121
+ stage?.select(compId);
122
+ };
123
+
124
+ const clickHandler = (event: MouseEvent, data: any) => {
125
+ if (data.type === 'node') {
126
+ selectComp(data.key);
127
+ }
128
+ };
129
+
130
+ // 编辑代码块
131
+ const editCode = (id: string) => {
132
+ emit('edit', id);
133
+ };
134
+
135
+ const deleteCode = async (id: string) => {
136
+ const currentCode = codeList.value.find((codeItem) => codeItem.id === id);
137
+ const existBinds = Boolean(currentCode?.items?.length);
138
+ const undeleteableList = codeBlockService?.getUndeletableList() || [];
139
+ if (!existBinds && !undeleteableList.includes(id)) {
140
+ await tMagicMessageBox.confirm('确定删除该代码块吗?', '提示', {
141
+ confirmButtonText: '确定',
142
+ cancelButtonText: '取消',
143
+ type: 'warning',
144
+ });
145
+
146
+ // 无绑定关系,且不在不可删除列表中
147
+ emit('remove', id);
148
+ } else {
149
+ if (typeof props.customError === 'function') {
150
+ props.customError(id, existBinds ? CodeDeleteErrorType.BIND : CodeDeleteErrorType.UNDELETEABLE);
151
+ } else {
152
+ tMagicMessage.error('代码块删除失败');
153
+ }
154
+ }
155
+ };
156
+
157
+ defineExpose({
158
+ filter: filterTextChangeHandler,
159
+ });
160
+ </script>
@@ -0,0 +1,70 @@
1
+ <template>
2
+ <TMagicScrollbar class="m-editor-code-block-list m-editor-layer-panel">
3
+ <slot name="code-block-panel-header">
4
+ <div class="search-wrapper">
5
+ <SearchInput @search="filterTextChangeHandler"></SearchInput>
6
+ <TMagicButton v-if="editable" class="create-code-button" type="primary" size="small" @click="createCodeBlock"
7
+ >新增</TMagicButton
8
+ >
9
+ <slot name="code-block-panel-search"></slot>
10
+ </div>
11
+ </slot>
12
+
13
+ <!-- 代码块列表 -->
14
+ <CodeBlockList ref="codeBlockList" :custom-error="customError" @edit="editCode" @remove="deleteCode">
15
+ <template #code-block-panel-tool="{ id, data }">
16
+ <slot name="code-block-panel-tool" :id="id" :data="data"></slot>
17
+ </template>
18
+ </CodeBlockList>
19
+ </TMagicScrollbar>
20
+
21
+ <CodeBlockEditor
22
+ v-if="codeConfig"
23
+ ref="codeBlockEditor"
24
+ :disabled="!editable"
25
+ :content="codeConfig"
26
+ @submit="submitCodeBlockHandler"
27
+ ></CodeBlockEditor>
28
+ </template>
29
+
30
+ <script setup lang="ts">
31
+ import { computed, inject, ref } from 'vue';
32
+
33
+ import { TMagicButton, TMagicScrollbar } from '@tmagic/design';
34
+ import type { Id } from '@tmagic/schema';
35
+
36
+ import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
37
+ import SearchInput from '@editor/components/SearchInput.vue';
38
+ import { useCodeBlockEdit } from '@editor/hooks/use-code-block-edit';
39
+ import type { CodeBlockListPanelSlots, CodeDeleteErrorType, EventBus, Services } from '@editor/type';
40
+
41
+ import CodeBlockList from './CodeBlockList.vue';
42
+
43
+ defineSlots<CodeBlockListPanelSlots>();
44
+
45
+ defineOptions({
46
+ name: 'MEditorCodeBlockListPanel',
47
+ });
48
+
49
+ defineProps<{
50
+ customError?: (id: Id, errorType: CodeDeleteErrorType) => any;
51
+ }>();
52
+
53
+ const eventBus = inject<EventBus>('eventBus');
54
+ const { codeBlockService } = inject<Services>('services') || {};
55
+
56
+ const editable = computed(() => codeBlockService?.getEditStatus());
57
+
58
+ const { codeBlockEditor, codeConfig, editCode, deleteCode, createCodeBlock, submitCodeBlockHandler } =
59
+ useCodeBlockEdit(codeBlockService);
60
+
61
+ const codeBlockList = ref<InstanceType<typeof CodeBlockList>>();
62
+
63
+ const filterTextChangeHandler = (val: string) => {
64
+ codeBlockList.value?.filter(val);
65
+ };
66
+
67
+ eventBus?.on('edit-code', (id: string) => {
68
+ editCode(id);
69
+ });
70
+ </script>
@@ -0,0 +1,84 @@
1
+ <template>
2
+ <FloatingBox
3
+ v-model:visible="boxVisible"
4
+ v-model:width="width"
5
+ v-model:height="editorHeight"
6
+ :title="title"
7
+ :position="boxPosition"
8
+ >
9
+ <template #body>
10
+ <MFormBox
11
+ label-width="80px"
12
+ :title="title"
13
+ :config="dataSourceConfig"
14
+ :values="initValues"
15
+ :disabled="disabled"
16
+ style="height: 100%"
17
+ @submit="submitHandler"
18
+ @error="errorHandler"
19
+ ></MFormBox>
20
+ </template>
21
+ </FloatingBox>
22
+ </template>
23
+
24
+ <script setup lang="ts">
25
+ import { inject, Ref, ref, watchEffect } from 'vue';
26
+
27
+ import { tMagicMessage } from '@tmagic/design';
28
+ import { FormConfig, MFormBox } from '@tmagic/form';
29
+ import { DataSourceSchema } from '@tmagic/schema';
30
+
31
+ import FloatingBox from '@editor/components/FloatingBox.vue';
32
+ import { useEditorContentHeight } from '@editor/hooks';
33
+ import { useNextFloatBoxPosition } from '@editor/hooks/use-next-float-box-position';
34
+ import type { Services } from '@editor/type';
35
+
36
+ defineOptions({
37
+ name: 'MEditorDataSourceConfigPanel',
38
+ });
39
+
40
+ const props = defineProps<{
41
+ title?: string;
42
+ values: any;
43
+ disabled: boolean;
44
+ }>();
45
+
46
+ const boxVisible = defineModel<boolean>('visible', { default: false });
47
+ const width = defineModel<number>('width', { default: 670 });
48
+
49
+ const emit = defineEmits(['submit']);
50
+
51
+ const services = inject<Services>('services');
52
+
53
+ const initValues = ref<Partial<DataSourceSchema>>({});
54
+ const dataSourceConfig = ref<FormConfig>([]);
55
+
56
+ const { height: editorHeight } = useEditorContentHeight();
57
+
58
+ const parentFloating = inject<Ref<HTMLDivElement | null>>('parentFloating', ref(null));
59
+ const { boxPosition, calcBoxPosition } = useNextFloatBoxPosition(services?.uiService, parentFloating);
60
+
61
+ watchEffect(() => {
62
+ initValues.value = props.values;
63
+ dataSourceConfig.value = services?.dataSourceService.getFormConfig(initValues.value.type) || [];
64
+ });
65
+
66
+ const submitHandler = (values: any) => {
67
+ emit('submit', values);
68
+ };
69
+
70
+ const errorHandler = (error: any) => {
71
+ tMagicMessage.error(error.message);
72
+ };
73
+
74
+ defineExpose({
75
+ show() {
76
+ calcBoxPosition();
77
+ boxVisible.value = true;
78
+ },
79
+
80
+ hide() {
81
+ boxVisible.value = false;
82
+ },
83
+ });
84
+ </script>