@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.
Files changed (99) hide show
  1. package/dist/{style.css → tmagic-editor.css} +228 -22
  2. package/dist/tmagic-editor.js +1670 -1012
  3. package/dist/tmagic-editor.umd.cjs +1705 -1048
  4. package/package.json +14 -12
  5. package/src/Editor.vue +24 -6
  6. package/src/components/CodeBlockEditor.vue +14 -8
  7. package/src/components/CodeParams.vue +5 -5
  8. package/src/components/ContentMenu.vue +18 -18
  9. package/src/components/FloatingBox.vue +3 -3
  10. package/src/components/Resizer.vue +4 -4
  11. package/src/components/ScrollBar.vue +3 -3
  12. package/src/components/ScrollViewer.vue +3 -3
  13. package/src/components/SplitView.vue +2 -2
  14. package/src/components/ToolButton.vue +2 -1
  15. package/src/editorProps.ts +6 -2
  16. package/src/fields/Code.vue +1 -2
  17. package/src/fields/CodeSelect.vue +13 -11
  18. package/src/fields/CodeSelectCol.vue +32 -5
  19. package/src/fields/CondOpSelect.vue +5 -2
  20. package/src/fields/DataSourceFields.vue +31 -12
  21. package/src/fields/DataSourceInput.vue +2 -2
  22. package/src/fields/DataSourceMethods.vue +72 -14
  23. package/src/fields/DataSourceMocks.vue +0 -1
  24. package/src/fields/DisplayConds.vue +8 -3
  25. package/src/fields/EventSelect.vue +28 -12
  26. package/src/fields/KeyValue.vue +17 -12
  27. package/src/fields/UISelect.vue +6 -3
  28. package/src/hooks/index.ts +0 -1
  29. package/src/hooks/use-code-block-edit.ts +3 -3
  30. package/src/hooks/use-data-source-edit.ts +3 -2
  31. package/src/hooks/use-filter.ts +1 -1
  32. package/src/hooks/use-getso.ts +7 -7
  33. package/src/hooks/use-node-status.ts +2 -2
  34. package/src/index.ts +2 -1
  35. package/src/initService.ts +177 -74
  36. package/src/layouts/CodeEditor.vue +2 -2
  37. package/src/layouts/Framework.vue +13 -12
  38. package/src/layouts/NavMenu.vue +2 -2
  39. package/src/layouts/page-bar/AddButton.vue +29 -9
  40. package/src/layouts/page-bar/PageBar.vue +79 -66
  41. package/src/layouts/page-bar/PageBarScrollContainer.vue +84 -98
  42. package/src/layouts/page-bar/PageList.vue +5 -3
  43. package/src/layouts/page-bar/Search.vue +67 -0
  44. package/src/layouts/props-panel/FormPanel.vue +123 -0
  45. package/src/layouts/props-panel/PropsPanel.vue +146 -0
  46. package/src/layouts/props-panel/use-style-panel.ts +29 -0
  47. package/src/layouts/sidebar/Sidebar.vue +7 -1
  48. package/src/layouts/sidebar/code-block/CodeBlockList.vue +18 -2
  49. package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +35 -4
  50. package/src/layouts/sidebar/code-block/useContentMenu.ts +83 -0
  51. package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +7 -5
  52. package/src/layouts/sidebar/data-source/DataSourceList.vue +13 -8
  53. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +40 -4
  54. package/src/layouts/sidebar/data-source/useContentMenu.ts +81 -0
  55. package/src/layouts/sidebar/layer/LayerMenu.vue +7 -13
  56. package/src/layouts/sidebar/layer/LayerPanel.vue +11 -4
  57. package/src/layouts/sidebar/layer/use-click.ts +2 -2
  58. package/src/layouts/sidebar/layer/use-keybinding.ts +2 -2
  59. package/src/layouts/sidebar/layer/use-node-status.ts +2 -3
  60. package/src/layouts/workspace/Workspace.vue +13 -3
  61. package/src/layouts/workspace/viewer/NodeListMenu.vue +3 -3
  62. package/src/layouts/workspace/viewer/Stage.vue +41 -11
  63. package/src/layouts/workspace/viewer/StageOverlay.vue +2 -2
  64. package/src/layouts/workspace/viewer/ViewerMenu.vue +4 -6
  65. package/src/services/dataSource.ts +12 -5
  66. package/src/services/dep.ts +61 -13
  67. package/src/services/editor.ts +46 -51
  68. package/src/services/storage.ts +2 -1
  69. package/src/services/ui.ts +1 -0
  70. package/src/theme/common/var.scss +12 -10
  71. package/src/theme/component-list-panel.scss +9 -7
  72. package/src/theme/content-menu.scss +7 -5
  73. package/src/theme/data-source.scss +3 -1
  74. package/src/theme/floating-box.scss +4 -2
  75. package/src/theme/framework.scss +7 -5
  76. package/src/theme/index.scss +4 -5
  77. package/src/theme/key-value.scss +2 -2
  78. package/src/theme/layer-panel.scss +3 -1
  79. package/src/theme/layout.scss +1 -1
  80. package/src/theme/nav-menu.scss +7 -5
  81. package/src/theme/page-bar.scss +51 -27
  82. package/src/theme/props-panel.scss +81 -1
  83. package/src/theme/resizer.scss +1 -1
  84. package/src/theme/search-input.scss +1 -0
  85. package/src/theme/sidebar.scss +4 -2
  86. package/src/theme/stage.scss +3 -1
  87. package/src/theme/theme.scss +28 -28
  88. package/src/theme/tree.scss +14 -12
  89. package/src/type.ts +10 -1
  90. package/src/utils/content-menu.ts +2 -2
  91. package/src/utils/data-source/formConfigs/http.ts +2 -0
  92. package/src/utils/data-source/index.ts +2 -2
  93. package/src/utils/editor.ts +78 -22
  94. package/src/utils/idle-task.ts +36 -4
  95. package/src/utils/props.ts +48 -6
  96. package/types/index.d.ts +2312 -2080
  97. package/src/hooks/use-data-source-method.ts +0 -100
  98. package/src/layouts/PropsPanel.vue +0 -131
  99. 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?: (menus: (MenuButton | MenuComponent)[], type: string) => (MenuButton | MenuComponent)[];
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
- tMagicMessage.error('代码块删除失败');
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, ref } from 'vue';
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 { CodeBlockListPanelSlots, CodeDeleteErrorType, EventBus, Services } from '@editor/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 = ref<InstanceType<typeof 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(['submit']);
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 { tMagicMessageBox, TMagicTooltip } from '@tmagic/design';
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 { DataSourceListSlots, EventBus, Services } from '@editor/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>