@tmagic/editor 1.5.0-beta.13 → 1.5.0-beta.15

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 (42) hide show
  1. package/dist/style.css +61 -6
  2. package/dist/tmagic-editor.js +521 -242
  3. package/dist/tmagic-editor.umd.cjs +522 -243
  4. package/package.json +12 -10
  5. package/src/components/CodeBlockEditor.vue +11 -5
  6. package/src/components/CodeParams.vue +3 -3
  7. package/src/fields/Code.vue +1 -2
  8. package/src/fields/CodeSelect.vue +13 -11
  9. package/src/fields/CodeSelectCol.vue +32 -5
  10. package/src/fields/CondOpSelect.vue +5 -2
  11. package/src/fields/DataSourceFields.vue +31 -12
  12. package/src/fields/DataSourceMethods.vue +72 -14
  13. package/src/fields/DisplayConds.vue +8 -3
  14. package/src/fields/EventSelect.vue +25 -8
  15. package/src/fields/KeyValue.vue +15 -10
  16. package/src/hooks/index.ts +0 -1
  17. package/src/hooks/use-code-block-edit.ts +1 -1
  18. package/src/hooks/use-data-source-edit.ts +3 -2
  19. package/src/initService.ts +103 -24
  20. package/src/layouts/Framework.vue +0 -3
  21. package/src/layouts/PropsPanel.vue +3 -3
  22. package/src/layouts/page-bar/PageBar.vue +46 -3
  23. package/src/layouts/page-bar/PageBarScrollContainer.vue +49 -24
  24. package/src/layouts/page-bar/PageList.vue +4 -2
  25. package/src/layouts/sidebar/Sidebar.vue +3 -0
  26. package/src/layouts/sidebar/code-block/CodeBlockList.vue +6 -1
  27. package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +7 -5
  28. package/src/layouts/sidebar/data-source/DataSourceList.vue +6 -1
  29. package/src/layouts/workspace/Workspace.vue +5 -2
  30. package/src/layouts/workspace/viewer/Stage.vue +12 -5
  31. package/src/services/dataSource.ts +12 -5
  32. package/src/services/dep.ts +49 -11
  33. package/src/services/editor.ts +26 -11
  34. package/src/theme/page-bar.scss +24 -9
  35. package/src/theme/search-input.scss +1 -0
  36. package/src/utils/data-source/formConfigs/http.ts +2 -0
  37. package/src/utils/data-source/index.ts +2 -2
  38. package/src/utils/editor.ts +94 -2
  39. package/src/utils/idle-task.ts +34 -3
  40. package/src/utils/props.ts +3 -3
  41. package/types/index.d.ts +803 -1169
  42. package/src/hooks/use-data-source-method.ts +0 -100
@@ -21,7 +21,7 @@ export const useCodeBlockEdit = (codeBlockService?: CodeBlockService) => {
21
21
 
22
22
  codeConfig.value = {
23
23
  name: '',
24
- content: `({app, params}) => {\n // place your code here\n}`,
24
+ content: `({app, params, flowState}) => {\n // place your code here\n}`,
25
25
  params: [],
26
26
  };
27
27
 
@@ -1,6 +1,7 @@
1
1
  import { computed, ref } from 'vue';
2
2
 
3
3
  import type { DataSourceSchema } from '@tmagic/core';
4
+ import type { ContainerChangeEventData } from '@tmagic/form';
4
5
 
5
6
  import DataSourceConfigPanel from '@editor/layouts/sidebar/data-source/DataSourceConfigPanel.vue';
6
7
  import type { DataSourceService } from '@editor/services/dataSource';
@@ -24,9 +25,9 @@ export const useDataSourceEdit = (dataSourceService?: DataSourceService) => {
24
25
  editDialog.value.show();
25
26
  };
26
27
 
27
- const submitDataSourceHandler = (value: DataSourceSchema) => {
28
+ const submitDataSourceHandler = (value: DataSourceSchema, eventData: ContainerChangeEventData) => {
28
29
  if (value.id) {
29
- dataSourceService?.update(value);
30
+ dataSourceService?.update(value, { changeRecords: eventData.changeRecords });
30
31
  } else {
31
32
  dataSourceService?.add(value);
32
33
  }
@@ -1,4 +1,4 @@
1
- import { onBeforeUnmount, reactive, toRaw, watch } from 'vue';
1
+ import { computed, onBeforeUnmount, reactive, toRaw, watch } from 'vue';
2
2
  import { cloneDeep } from 'lodash-es';
3
3
 
4
4
  import type {
@@ -17,11 +17,14 @@ import {
17
17
  createDataSourceMethodTarget,
18
18
  createDataSourceTarget,
19
19
  DepTargetType,
20
+ NODE_CONDS_KEY,
20
21
  Target,
21
22
  } from '@tmagic/core';
23
+ import { ChangeRecord } from '@tmagic/form';
22
24
  import { getNodes, isPage, traverseNode } from '@tmagic/utils';
23
25
 
24
26
  import PropsPanel from './layouts/PropsPanel.vue';
27
+ import { isIncludeDataSource, isValueIncludeDataSource } from './utils/editor';
25
28
  import { EditorProps } from './editorProps';
26
29
  import { Services } from './type';
27
30
 
@@ -219,6 +222,7 @@ export const initServiceEvents = (
219
222
  });
220
223
 
221
224
  if (Array.isArray(value.items)) {
225
+ depService.clearIdleTasks();
222
226
  collectIdle(value.items, true);
223
227
  } else {
224
228
  depService.clear();
@@ -247,10 +251,25 @@ export const initServiceEvents = (
247
251
  }
248
252
  };
249
253
 
250
- const getApp = () => {
251
- const stage = editorService.get('stage');
252
- return stage?.renderer?.runtime?.getApp?.();
253
- };
254
+ const stage = computed(() => editorService.get('stage'));
255
+
256
+ watch(stage, (stage) => {
257
+ if (!stage) {
258
+ return;
259
+ }
260
+
261
+ stage.on('rerender', () => {
262
+ const node = editorService.get('node');
263
+
264
+ if (!node) return;
265
+
266
+ collectIdle([node], true).then(() => {
267
+ afterUpdateNodes([node]);
268
+ });
269
+ });
270
+ });
271
+
272
+ const getApp = () => stage.value?.renderer?.runtime?.getApp?.();
254
273
 
255
274
  const updateDataSourceSchema = (nodes: MNode[], deep: boolean) => {
256
275
  const root = editorService.get('root');
@@ -266,9 +285,7 @@ export const initServiceEvents = (
266
285
  getApp()?.dataSourceManager?.updateSchema(root.dataSources);
267
286
  }
268
287
 
269
- const stage = editorService.get('stage');
270
-
271
- if (!root || !stage) return;
288
+ if (!root || !stage.value) return;
272
289
 
273
290
  const allNodes: MNode[] = [];
274
291
 
@@ -289,7 +306,7 @@ export const initServiceEvents = (
289
306
  Object.keys(dep).forEach((id) => {
290
307
  const node = allNodes.find((node) => node.id === id);
291
308
  node &&
292
- stage.update({
309
+ stage.value?.update({
293
310
  config: cloneDeep(node),
294
311
  parentId: editorService.getParentById(node.id)?.id,
295
312
  root: cloneDeep(root),
@@ -301,13 +318,8 @@ export const initServiceEvents = (
301
318
  const afterUpdateNodes = (nodes: MNode[]) => {
302
319
  const root = editorService.get('root');
303
320
  if (!root) return;
304
- const stage = editorService.get('stage');
305
- const app = getApp();
306
- if (app?.dsl) {
307
- app.dsl.dataSourceDeps = root.dataSourceDeps;
308
- }
309
321
  for (const node of nodes) {
310
- stage?.update({
322
+ stage.value?.update({
311
323
  config: cloneDeep(node),
312
324
  parentId: editorService.getParentById(node.id)?.id,
313
325
  root: cloneDeep(root),
@@ -348,8 +360,18 @@ export const initServiceEvents = (
348
360
  }
349
361
  };
350
362
 
363
+ const depCollectedHandler = () => {
364
+ const root = editorService.get('root');
365
+ if (!root) return;
366
+ const app = getApp();
367
+ if (app?.dsl) {
368
+ app.dsl.dataSourceDeps = root.dataSourceDeps;
369
+ }
370
+ };
371
+
351
372
  depService.on('add-target', targetAddHandler);
352
373
  depService.on('remove-target', targetRemoveHandler);
374
+ depService.on('collected', depCollectedHandler);
353
375
 
354
376
  const initDataSourceDepTarget = (ds: DataSourceSchema) => {
355
377
  depService.addTarget(createDataSourceTarget(ds, reactive({})));
@@ -380,10 +402,39 @@ export const initServiceEvents = (
380
402
  };
381
403
 
382
404
  // 节点更新,收集依赖
383
- const nodeUpdateHandler = (nodes: MNode[]) => {
384
- collectIdle(nodes, true).then(() => {
385
- afterUpdateNodes(nodes);
405
+ // 仅当修改到数据源相关的才收集
406
+ const nodeUpdateHandler = (data: { newNode: MNode; oldNode: MNode; changeRecords?: ChangeRecord[] }[]) => {
407
+ const needRecollectNodes: MNode[] = [];
408
+ const normalNodes: MNode[] = [];
409
+ data.forEach(({ newNode, oldNode, changeRecords }) => {
410
+ if (changeRecords?.length) {
411
+ for (const record of changeRecords) {
412
+ // NODE_CONDS_KEY为显示条件key
413
+ if (
414
+ !record.propPath ||
415
+ new RegExp(`${NODE_CONDS_KEY}.(\\d)+.cond`).test(record.propPath) ||
416
+ new RegExp(`${NODE_CONDS_KEY}.(\\d)+.cond.(\\d)+.value`).test(record.propPath) ||
417
+ record.propPath === NODE_CONDS_KEY ||
418
+ isValueIncludeDataSource(record.value)
419
+ ) {
420
+ needRecollectNodes.push(newNode);
421
+ break;
422
+ }
423
+ }
424
+ } else if (isIncludeDataSource(newNode, oldNode)) {
425
+ needRecollectNodes.push(newNode);
426
+ } else {
427
+ normalNodes.push(newNode);
428
+ }
386
429
  });
430
+
431
+ if (needRecollectNodes.length) {
432
+ collectIdle(needRecollectNodes, true).then(() => {
433
+ afterUpdateNodes(needRecollectNodes);
434
+ });
435
+ } else if (normalNodes.length) {
436
+ afterUpdateNodes(normalNodes);
437
+ }
387
438
  };
388
439
 
389
440
  // 节点删除,清除对齐的依赖收集
@@ -425,14 +476,41 @@ export const initServiceEvents = (
425
476
  getApp()?.dataSourceManager?.addDataSource(config);
426
477
  };
427
478
 
428
- const dataSourceUpdateHandler = (config: DataSourceSchema) => {
479
+ const dataSourceUpdateHandler = (config: DataSourceSchema, { changeRecords }: { changeRecords: ChangeRecord[] }) => {
480
+ let needRecollectDep = false;
481
+ for (const changeRecord of changeRecords) {
482
+ if (!changeRecord.propPath) {
483
+ continue;
484
+ }
485
+
486
+ needRecollectDep =
487
+ changeRecord.propPath === 'fields' ||
488
+ changeRecord.propPath === 'methods' ||
489
+ /fields.(\d)+.name/.test(changeRecord.propPath) ||
490
+ /fields.(\d)+$/.test(changeRecord.propPath) ||
491
+ /methods.(\d)+.name/.test(changeRecord.propPath) ||
492
+ /methods.(\d)+$/.test(changeRecord.propPath);
493
+
494
+ if (needRecollectDep) {
495
+ break;
496
+ }
497
+ }
498
+
429
499
  const root = editorService.get('root');
430
- removeDataSourceTarget(config.id);
431
- initDataSourceDepTarget(config);
500
+ if (needRecollectDep) {
501
+ if (Array.isArray(root?.items)) {
502
+ depService.clearIdleTasks();
432
503
 
433
- collectIdle(root?.items || [], true).then(() => {
434
- updateDataSourceSchema(root?.items || [], true);
435
- });
504
+ removeDataSourceTarget(config.id);
505
+ initDataSourceDepTarget(config);
506
+
507
+ collectIdle(root.items, true).then(() => {
508
+ updateDataSourceSchema(root?.items || [], true);
509
+ });
510
+ }
511
+ } else if (root?.dataSources) {
512
+ getApp()?.dataSourceManager?.updateSchema(root.dataSources);
513
+ }
436
514
  };
437
515
 
438
516
  const removeDataSourceTarget = (id: string) => {
@@ -459,6 +537,7 @@ export const initServiceEvents = (
459
537
  onBeforeUnmount(() => {
460
538
  depService.off('add-target', targetAddHandler);
461
539
  depService.off('remove-target', targetRemoveHandler);
540
+ depService.off('collected', depCollectedHandler);
462
541
 
463
542
  editorService.off('history-change', historyChangeHandler);
464
543
  editorService.off('root-change', rootChangeHandler);
@@ -11,8 +11,6 @@
11
11
  </slot>
12
12
 
13
13
  <SplitView
14
- v-loading="stageLoading"
15
- element-loading-text="Runtime 加载中..."
16
14
  v-else
17
15
  ref="splitView"
18
16
  class="m-editor-content"
@@ -102,7 +100,6 @@ const root = computed(() => editorService?.get('root'));
102
100
  const page = computed(() => editorService?.get('page'));
103
101
 
104
102
  const pageLength = computed(() => editorService?.get('pageLength') || 0);
105
- const stageLoading = computed(() => editorService?.get('stageLoading') || false);
106
103
  const showSrc = computed(() => uiService?.get('showSrc'));
107
104
 
108
105
  const LEFT_COLUMN_WIDTH_STORAGE_KEY = '$MagicEditorLeftColumnWidthData';
@@ -43,7 +43,7 @@ import { Document as DocumentIcon } from '@element-plus/icons-vue';
43
43
 
44
44
  import type { MNode } from '@tmagic/core';
45
45
  import { TMagicButton } from '@tmagic/design';
46
- import type { FormState, FormValue } from '@tmagic/form';
46
+ import type { ContainerChangeEventData, FormState, FormValue } from '@tmagic/form';
47
47
  import { MForm } from '@tmagic/form';
48
48
 
49
49
  import MIcon from '@editor/components/Icon.vue';
@@ -110,10 +110,10 @@ watchEffect(() => {
110
110
  }
111
111
  });
112
112
 
113
- const submit = async () => {
113
+ const submit = async (v: FormValue, eventData: ContainerChangeEventData) => {
114
114
  try {
115
115
  const values = await configForm.value?.submitForm();
116
- services?.editorService.update(values);
116
+ services?.editorService.update(values, { changeRecords: eventData.changeRecords });
117
117
  } catch (e: any) {
118
118
  emit('submit-error', e);
119
119
  }
@@ -1,6 +1,10 @@
1
1
  <template>
2
2
  <div class="m-editor-page-bar-tabs">
3
- <PageBarScrollContainer :page-bar-sort-options="pageBarSortOptions" :length="list.length">
3
+ <PageBarScrollContainer
4
+ ref="pageBarScrollContainer"
5
+ :page-bar-sort-options="pageBarSortOptions"
6
+ :length="list.length"
7
+ >
4
8
  <template #prepend>
5
9
  <slot name="page-bar-add-button"><AddButton></AddButton></slot>
6
10
 
@@ -13,8 +17,9 @@
13
17
  <div
14
18
  v-for="item in list"
15
19
  class="m-editor-page-bar-item"
20
+ ref="pageBarItems"
16
21
  :key="item.id"
17
- :page-id="item.id"
22
+ :data-page-id="item.id"
18
23
  :class="{ active: page?.id === item.id }"
19
24
  @click="switchPage(item.id)"
20
25
  >
@@ -63,7 +68,7 @@
63
68
  </template>
64
69
 
65
70
  <script lang="ts" setup>
66
- import { computed, inject, ref } from 'vue';
71
+ import { computed, inject, ref, watch } from 'vue';
67
72
  import { CaretBottom, Delete, DocumentCopy } from '@element-plus/icons-vue';
68
73
 
69
74
  import { type Id, type MPage, type MPageFragment, NodeType } from '@tmagic/core';
@@ -138,4 +143,42 @@ const copy = (node: MPage | MPageFragment) => {
138
143
  const remove = (node: MPage | MPageFragment) => {
139
144
  editorService?.remove(node);
140
145
  };
146
+
147
+ const pageBarScrollContainer = ref<InstanceType<typeof PageBarScrollContainer>>();
148
+ const pageBarItems = ref<HTMLDivElement[]>();
149
+ watch(page, (page) => {
150
+ if (
151
+ !page ||
152
+ !pageBarScrollContainer.value?.itemsContainerWidth ||
153
+ !pageBarItems.value ||
154
+ pageBarItems.value.length < 2
155
+ ) {
156
+ return;
157
+ }
158
+
159
+ const firstItem = pageBarItems.value[0];
160
+ const lastItem = pageBarItems.value[pageBarItems.value.length - 1];
161
+
162
+ if (page.id === firstItem.dataset.pageId) {
163
+ pageBarScrollContainer.value.scroll('start');
164
+ } else if (page.id === lastItem.dataset.pageId) {
165
+ pageBarScrollContainer.value.scroll('end');
166
+ } else {
167
+ const pageItem = pageBarItems.value.find((item) => item.dataset.pageId === page.id);
168
+ if (!pageItem) {
169
+ return;
170
+ }
171
+
172
+ const pageItemRect = pageItem.getBoundingClientRect();
173
+ const offsetLeft = pageItemRect.left - firstItem.getBoundingClientRect().left;
174
+ const { itemsContainerWidth } = pageBarScrollContainer.value;
175
+
176
+ const left = itemsContainerWidth - offsetLeft - pageItemRect.width;
177
+
178
+ const translateLeft = pageBarScrollContainer.value.getTranslateLeft();
179
+ if (offsetLeft + translateLeft < 0 || offsetLeft + pageItemRect.width > itemsContainerWidth - translateLeft) {
180
+ pageBarScrollContainer.value.scrollTo(left);
181
+ }
182
+ }
183
+ });
141
184
  </script>
@@ -1,16 +1,23 @@
1
1
  <template>
2
2
  <div class="m-editor-page-bar" ref="pageBar">
3
3
  <slot name="prepend"></slot>
4
-
5
- <div v-if="canScroll" class="m-editor-page-bar-item m-editor-page-bar-item-icon" @click="scroll('left')">
6
- <Icon :icon="ArrowLeftBold"></Icon>
4
+ <div v-if="length" class="m-editor-page-bar-items" ref="itemsContainer">
5
+ <slot></slot>
7
6
  </div>
8
7
 
9
- <div v-if="length" class="m-editor-page-bar-items" ref="itemsContainer" :style="`width: ${itemsContainerWidth}px`">
10
- <slot></slot>
8
+ <div
9
+ v-if="canScroll"
10
+ class="m-editor-page-bar-item m-editor-page-bar-item-icon m-editor-page-bar-item-left-icon"
11
+ @click="scroll('left')"
12
+ >
13
+ <Icon :icon="ArrowLeftBold"></Icon>
11
14
  </div>
12
15
 
13
- <div v-if="canScroll" class="m-editor-page-bar-item m-editor-page-bar-item-icon" @click="scroll('right')">
16
+ <div
17
+ v-if="canScroll"
18
+ class="m-editor-page-bar-item m-editor-page-bar-item-icon m-editor-page-bar-item-right-icon"
19
+ @click="scroll('right')"
20
+ >
14
21
  <Icon :icon="ArrowRightBold"></Icon>
15
22
  </div>
16
23
  </div>
@@ -80,27 +87,34 @@ onBeforeUnmount(() => {
80
87
  let translateLeft = 0;
81
88
 
82
89
  const scroll = (type: 'left' | 'right' | 'start' | 'end') => {
83
- if (!itemsContainer.value) return;
90
+ if (!itemsContainer.value || !canScroll.value) return;
84
91
 
85
92
  const maxScrollLeft = itemsContainer.value.scrollWidth - itemsContainerWidth.value;
86
93
 
87
94
  if (type === 'left') {
88
- translateLeft += 100;
89
-
90
- if (translateLeft > 0) {
91
- translateLeft = 0;
92
- }
95
+ scrollTo(translateLeft + 200);
93
96
  } else if (type === 'right') {
94
- translateLeft -= 100;
95
-
96
- if (-translateLeft > maxScrollLeft) {
97
- translateLeft = -maxScrollLeft;
98
- }
97
+ scrollTo(translateLeft - 200);
99
98
  } else if (type === 'start') {
100
- translateLeft = 0;
99
+ scrollTo(0);
101
100
  } else if (type === 'end') {
102
- translateLeft = -maxScrollLeft;
101
+ scrollTo(-maxScrollLeft);
103
102
  }
103
+ };
104
+
105
+ const scrollTo = (value: number) => {
106
+ if (!itemsContainer.value || !canScroll.value) return;
107
+ const maxScrollLeft = itemsContainer.value.scrollWidth - itemsContainerWidth.value;
108
+
109
+ if (value >= 0) {
110
+ value = 0;
111
+ }
112
+
113
+ if (-value > maxScrollLeft) {
114
+ value = -maxScrollLeft;
115
+ }
116
+
117
+ translateLeft = value;
104
118
 
105
119
  itemsContainer.value.style.transform = `translate(${translateLeft}px, 0px)`;
106
120
  };
@@ -110,11 +124,13 @@ watch(
110
124
  (length = 0, preLength = 0) => {
111
125
  setTimeout(() => {
112
126
  setCanScroll();
113
- if (length < preLength) {
114
- scroll('start');
115
- } else {
116
- scroll('end');
117
- }
127
+ nextTick(() => {
128
+ if (length < preLength || preLength === 0) {
129
+ scroll('start');
130
+ } else {
131
+ scroll('end');
132
+ }
133
+ });
118
134
  if (length > 1) {
119
135
  const el = document.querySelector('.m-editor-page-bar-items') as HTMLElement;
120
136
  let beforeDragList: Id[] = [];
@@ -150,4 +166,13 @@ watch(
150
166
  immediate: true,
151
167
  },
152
168
  );
169
+
170
+ defineExpose({
171
+ itemsContainerWidth,
172
+ scroll,
173
+ scrollTo,
174
+ getTranslateLeft() {
175
+ return translateLeft;
176
+ },
177
+ });
153
178
  </script>
@@ -18,6 +18,7 @@
18
18
  :data="{
19
19
  type: 'button',
20
20
  text: item.devconfig?.tabName || item.name || item.id,
21
+ className: item.id === page?.id ? 'active' : '',
21
22
  handler: () => switchPage(item.id),
22
23
  }"
23
24
  :key="index"
@@ -55,7 +56,8 @@ const uiService = services?.uiService;
55
56
  const editorService = services?.editorService;
56
57
 
57
58
  const showPageListButton = computed(() => uiService?.get('showPageListButton'));
58
- const switchPage = (id: Id) => {
59
- editorService?.select(id);
59
+ const page = computed(() => editorService?.get('page'));
60
+ const switchPage = async (id: Id) => {
61
+ await editorService?.select(id);
60
62
  };
61
63
  </script>
@@ -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)"
@@ -197,6 +198,8 @@ const props = withDefaults(
197
198
 
198
199
  const services = inject<Services>('services');
199
200
 
201
+ const collecting = computed(() => services?.depService.get('collecting'));
202
+
200
203
  const columnLeftWidth = computed(() => services?.uiService.get('columnWidth')[ColumnLayout.LEFT] || 0);
201
204
  const { height: editorContentHeight } = useEditorContentHeight();
202
205
  const columnLeftHeight = ref(0);
@@ -19,6 +19,9 @@
19
19
  </template>
20
20
 
21
21
  <template #tree-node-tool="{ data }">
22
+ <TMagicTag v-if="collecting && data.type === 'code'" type="info" size="small" style="margin-right: 5px"
23
+ >依赖收集中</TMagicTag
24
+ >
22
25
  <TMagicTooltip v-if="data.type === 'code'" effect="dark" :content="editable ? '编辑' : '查看'" placement="bottom">
23
26
  <Icon :icon="editable ? Edit : View" class="edit-icon" @click.stop="editCode(`${data.key}`)"></Icon>
24
27
  </TMagicTooltip>
@@ -36,7 +39,7 @@ import { Close, Edit, View } from '@element-plus/icons-vue';
36
39
 
37
40
  import type { Id, MNode } from '@tmagic/core';
38
41
  import { DepTargetType } from '@tmagic/core';
39
- import { tMagicMessage, tMagicMessageBox, TMagicTooltip } from '@tmagic/design';
42
+ import { tMagicMessage, tMagicMessageBox, TMagicTag, TMagicTooltip } from '@tmagic/design';
40
43
 
41
44
  import Icon from '@editor/components/Icon.vue';
42
45
  import Tree from '@editor/components/Tree.vue';
@@ -64,6 +67,8 @@ const emit = defineEmits<{
64
67
  const services = inject<Services>('services');
65
68
  const { codeBlockService, depService, editorService } = services || {};
66
69
 
70
+ const collecting = computed(() => depService?.get('collecting'));
71
+
67
72
  // 代码块列表
68
73
  const codeList = computed<TreeNodeData[]>(() =>
69
74
  Object.entries(codeBlockService?.getCodeDsl() || {}).map(([codeId, code]) => {
@@ -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) => {
@@ -18,6 +18,9 @@
18
18
  </div>
19
19
  </template>
20
20
  <template #tree-node-tool="{ data }">
21
+ <TMagicTag v-if="collecting && data.type === 'ds'" type="info" size="small" style="margin-right: 5px"
22
+ >依赖收集中</TMagicTag
23
+ >
21
24
  <TMagicTooltip v-if="data.type === 'ds'" effect="dark" :content="editable ? '编辑' : '查看'" placement="bottom">
22
25
  <Icon :icon="editable ? Edit : View" class="edit-icon" @click.stop="editHandler(`${data.key}`)"></Icon>
23
26
  </TMagicTooltip>
@@ -34,7 +37,7 @@ import { computed, inject } from 'vue';
34
37
  import { Close, Edit, View } from '@element-plus/icons-vue';
35
38
 
36
39
  import { DepData, DepTargetType, Id, MNode } from '@tmagic/core';
37
- import { tMagicMessageBox, TMagicTooltip } from '@tmagic/design';
40
+ import { tMagicMessageBox, TMagicTag, TMagicTooltip } from '@tmagic/design';
38
41
 
39
42
  import Icon from '@editor/components/Icon.vue';
40
43
  import Tree from '@editor/components/Tree.vue';
@@ -60,6 +63,8 @@ const emit = defineEmits<{
60
63
 
61
64
  const { depService, editorService, dataSourceService } = inject<Services>('services') || {};
62
65
 
66
+ const collecting = computed(() => depService?.get('collecting'));
67
+
63
68
  const editable = computed(() => dataSourceService?.get('editable') ?? true);
64
69
 
65
70
  const dataSources = computed(() => dataSourceService?.get('dataSources') || []);
@@ -4,7 +4,8 @@
4
4
 
5
5
  <slot name="stage">
6
6
  <MagicStage
7
- v-if="page"
7
+ v-if="page && (stageOptions?.render || stageOptions?.runtimeUrl)"
8
+ :stage-options="stageOptions"
8
9
  :disabled-stage-overlay="disabledStageOverlay"
9
10
  :stage-content-menu="stageContentMenu"
10
11
  :custom-content-menu="customContentMenu"
@@ -18,7 +19,7 @@
18
19
  <script lang="ts" setup>
19
20
  import { computed, inject } from 'vue';
20
21
 
21
- import type { MenuButton, MenuComponent, Services, WorkspaceSlots } from '@editor/type';
22
+ import type { MenuButton, MenuComponent, Services, StageOptions, WorkspaceSlots } from '@editor/type';
22
23
 
23
24
  import MagicStage from './viewer/Stage.vue';
24
25
  import Breadcrumb from './Breadcrumb.vue';
@@ -40,6 +41,8 @@ withDefaults(
40
41
  },
41
42
  );
42
43
 
44
+ const stageOptions = inject<StageOptions>('stageOptions');
45
+
43
46
  const services = inject<Services>('services');
44
47
 
45
48
  const page = computed(() => services?.editorService.get('page'));
@@ -3,6 +3,8 @@
3
3
  class="m-editor-stage"
4
4
  ref="stageWrap"
5
5
  tabindex="-1"
6
+ v-loading="stageLoading"
7
+ element-loading-text="Runtime 加载中..."
6
8
  :width="stageRect?.width"
7
9
  :height="stageRect?.height"
8
10
  :wrap-width="stageContainerRect?.width"
@@ -62,8 +64,9 @@ defineOptions({
62
64
  name: 'MEditorStage',
63
65
  });
64
66
 
65
- withDefaults(
67
+ const props = withDefaults(
66
68
  defineProps<{
69
+ stageOptions: StageOptions;
67
70
  stageContentMenu: (MenuButton | MenuComponent)[];
68
71
  disabledStageOverlay?: boolean;
69
72
  customContentMenu?: (menus: (MenuButton | MenuComponent)[], type: string) => (MenuButton | MenuComponent)[];
@@ -77,7 +80,8 @@ let stage: StageCore | null = null;
77
80
  let runtime: Runtime | null = null;
78
81
 
79
82
  const services = inject<Services>('services');
80
- const stageOptions = inject<StageOptions>('stageOptions');
83
+
84
+ const stageLoading = computed(() => services?.editorService.get('stageLoading') || false);
81
85
 
82
86
  const stageWrap = ref<InstanceType<typeof ScrollViewer>>();
83
87
  const stageContainer = ref<HTMLDivElement>();
@@ -96,9 +100,9 @@ watchEffect(() => {
96
100
  if (stage || !page.value) return;
97
101
 
98
102
  if (!stageContainer.value) return;
99
- if (!(stageOptions?.runtimeUrl || stageOptions?.render) || !root.value) return;
103
+ if (!(props.stageOptions?.runtimeUrl || props.stageOptions?.render) || !root.value) return;
100
104
 
101
- stage = useStage(stageOptions);
105
+ stage = useStage(props.stageOptions);
102
106
 
103
107
  stage.on('select', () => {
104
108
  stageWrap.value?.container?.focus();
@@ -125,6 +129,7 @@ watchEffect(() => {
125
129
 
126
130
  onBeforeUnmount(() => {
127
131
  stage?.destroy();
132
+ services?.editorService.set('stage', null);
128
133
  });
129
134
 
130
135
  watch(zoom, (zoom) => {
@@ -212,7 +217,9 @@ const dropHandler = async (e: DragEvent) => {
212
217
  e.preventDefault();
213
218
 
214
219
  const doc = stage?.renderer?.contentWindow?.document;
215
- const parentEl: HTMLElement | null | undefined = doc?.querySelector(`.${stageOptions?.containerHighlightClassName}`);
220
+ const parentEl: HTMLElement | null | undefined = doc?.querySelector(
221
+ `.${props.stageOptions?.containerHighlightClassName}`,
222
+ );
216
223
 
217
224
  let parent: MContainer | undefined | null = page.value;
218
225
  const parentId = getIdFromEl()(parentEl);