@tmagic/editor 1.3.5 → 1.3.7

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 (142) hide show
  1. package/LICENSE +5432 -0
  2. package/dist/style.css +88 -62
  3. package/dist/tmagic-editor.js +2448 -2016
  4. package/dist/tmagic-editor.js.map +1 -1
  5. package/dist/tmagic-editor.umd.cjs +2453 -2019
  6. package/dist/tmagic-editor.umd.cjs.map +1 -1
  7. package/package.json +23 -23
  8. package/src/Editor.vue +15 -7
  9. package/src/components/CodeBlockEditor.vue +2 -2
  10. package/src/components/ContentMenu.vue +5 -5
  11. package/src/components/FloatingBox.vue +117 -0
  12. package/src/components/ScrollBar.vue +2 -2
  13. package/src/components/ScrollViewer.vue +2 -2
  14. package/src/components/SplitView.vue +2 -2
  15. package/src/components/Tree.vue +5 -0
  16. package/src/components/TreeNode.vue +10 -4
  17. package/src/editorProps.ts +20 -3
  18. package/src/fields/PageFragmentSelect.vue +55 -0
  19. package/src/hooks/use-filter.ts +55 -0
  20. package/src/hooks/use-getso.ts +2 -2
  21. package/src/hooks/use-node-status.ts +48 -0
  22. package/src/hooks/use-stage.ts +19 -1
  23. package/src/index.ts +3 -0
  24. package/src/initService.ts +43 -17
  25. package/src/layouts/AddPageBox.vue +16 -4
  26. package/src/layouts/CodeEditor.vue +2 -2
  27. package/src/layouts/Framework.vue +17 -3
  28. package/src/layouts/PropsPanel.vue +2 -2
  29. package/src/layouts/page-bar/AddButton.vue +48 -0
  30. package/src/layouts/page-bar/PageBar.vue +158 -0
  31. package/src/layouts/{workspace → page-bar}/PageBarScrollContainer.vue +61 -35
  32. package/src/layouts/page-bar/SwitchTypeButton.vue +45 -0
  33. package/src/layouts/sidebar/Sidebar.vue +9 -2
  34. package/src/layouts/sidebar/code-block/CodeBlockList.vue +55 -65
  35. package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +2 -1
  36. package/src/layouts/sidebar/data-source/DataSourceList.vue +63 -59
  37. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +1 -1
  38. package/src/layouts/sidebar/layer/LayerMenu.vue +39 -27
  39. package/src/layouts/sidebar/layer/LayerPanel.vue +31 -8
  40. package/src/layouts/sidebar/layer/use-click.ts +5 -3
  41. package/src/layouts/sidebar/layer/use-keybinding.ts +8 -8
  42. package/src/layouts/sidebar/layer/use-node-status.ts +10 -13
  43. package/src/layouts/workspace/Workspace.vue +6 -7
  44. package/src/layouts/workspace/viewer/NodeListMenu.vue +68 -129
  45. package/src/layouts/workspace/viewer/Stage.vue +14 -6
  46. package/src/layouts/workspace/viewer/ViewerMenu.vue +94 -86
  47. package/src/services/BaseService.ts +63 -26
  48. package/src/services/codeBlock.ts +7 -1
  49. package/src/services/dep.ts +12 -8
  50. package/src/services/editor.ts +131 -64
  51. package/src/services/history.ts +6 -2
  52. package/src/services/keybinding.ts +48 -13
  53. package/src/services/props.ts +70 -12
  54. package/src/services/storage.ts +23 -13
  55. package/src/services/ui.ts +4 -1
  56. package/src/theme/code-block.scss +0 -16
  57. package/src/theme/floating-box.scss +30 -0
  58. package/src/theme/layer-panel.scss +19 -0
  59. package/src/theme/page-bar.scss +18 -1
  60. package/src/theme/stage.scss +24 -0
  61. package/src/theme/theme.scss +1 -1
  62. package/src/theme/tree.scss +12 -1
  63. package/src/type.ts +28 -10
  64. package/src/utils/compose.ts +25 -6
  65. package/src/utils/content-menu.ts +6 -7
  66. package/src/utils/editor.ts +34 -18
  67. package/src/utils/operator.ts +5 -6
  68. package/types/components/CodeBlockEditor.vue.d.ts +6 -2
  69. package/types/components/CodeParams.vue.d.ts +3 -1
  70. package/types/components/ContentMenu.vue.d.ts +13 -5
  71. package/types/components/FloatingBox.vue.d.ts +78 -0
  72. package/types/components/Resizer.vue.d.ts +6 -1
  73. package/types/components/ScrollBar.vue.d.ts +3 -1
  74. package/types/components/ScrollViewer.vue.d.ts +3 -3
  75. package/types/components/SearchInput.vue.d.ts +3 -1
  76. package/types/components/SplitView.vue.d.ts +8 -4
  77. package/types/components/ToolButton.vue.d.ts +3 -3
  78. package/types/components/Tree.vue.d.ts +27 -8
  79. package/types/components/TreeNode.vue.d.ts +24 -7
  80. package/types/editorProps.d.ts +16 -1
  81. package/types/fields/Code.vue.d.ts +6 -2
  82. package/types/fields/CodeLink.vue.d.ts +5 -3
  83. package/types/fields/CodeSelect.vue.d.ts +3 -1
  84. package/types/fields/CodeSelectCol.vue.d.ts +3 -1
  85. package/types/fields/DataSourceFieldSelect.vue.d.ts +3 -1
  86. package/types/fields/DataSourceFields.vue.d.ts +5 -3
  87. package/types/fields/DataSourceInput.vue.d.ts +8 -4
  88. package/types/fields/DataSourceMethodSelect.vue.d.ts +3 -1
  89. package/types/fields/DataSourceMethods.vue.d.ts +5 -3
  90. package/types/fields/DataSourceMocks.vue.d.ts +5 -3
  91. package/types/fields/DataSourceSelect.vue.d.ts +5 -3
  92. package/types/fields/EventSelect.vue.d.ts +3 -1
  93. package/types/fields/KeyValue.vue.d.ts +2 -2
  94. package/types/fields/PageFragmentSelect.vue.d.ts +31 -0
  95. package/types/fields/UISelect.vue.d.ts +3 -1
  96. package/types/hooks/use-code-block-edit.d.ts +14 -5
  97. package/types/hooks/use-data-source-method.d.ts +14 -5
  98. package/types/hooks/use-filter.d.ts +7 -0
  99. package/types/hooks/use-node-status.d.ts +6 -0
  100. package/types/index.d.ts +1 -0
  101. package/types/layouts/AddPageBox.vue.d.ts +14 -1
  102. package/types/layouts/CodeEditor.vue.d.ts +4 -1
  103. package/types/layouts/Framework.vue.d.ts +14 -1
  104. package/types/layouts/PropsPanel.vue.d.ts +19 -13
  105. package/types/layouts/{workspace/viewer/NodeListMenuTitle.vue.d.ts → page-bar/AddButton.vue.d.ts} +3 -2
  106. package/types/layouts/page-bar/PageBar.vue.d.ts +28 -0
  107. package/types/layouts/page-bar/PageBarScrollContainer.vue.d.ts +24 -0
  108. package/types/layouts/page-bar/SwitchTypeButton.vue.d.ts +20 -0
  109. package/types/layouts/sidebar/ComponentListPanel.vue.d.ts +1 -1
  110. package/types/layouts/sidebar/Sidebar.vue.d.ts +7 -5
  111. package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +9 -3
  112. package/types/layouts/sidebar/code-block/CodeBlockListPanel.vue.d.ts +1 -1
  113. package/types/layouts/sidebar/data-source/DataSourceConfigPanel.vue.d.ts +3 -1
  114. package/types/layouts/sidebar/data-source/DataSourceList.vue.d.ts +8 -2
  115. package/types/layouts/sidebar/data-source/DataSourceListPanel.vue.d.ts +1 -1
  116. package/types/layouts/sidebar/layer/LayerMenu.vue.d.ts +26 -3
  117. package/types/layouts/sidebar/layer/LayerPanel.vue.d.ts +3 -1
  118. package/types/layouts/sidebar/layer/use-click.d.ts +37 -6
  119. package/types/layouts/sidebar/layer/use-keybinding.d.ts +1 -1
  120. package/types/layouts/sidebar/layer/use-node-status.d.ts +3 -4
  121. package/types/layouts/workspace/Workspace.vue.d.ts +3 -1
  122. package/types/layouts/workspace/viewer/NodeListMenu.vue.d.ts +1 -14
  123. package/types/layouts/workspace/viewer/Stage.vue.d.ts +2 -0
  124. package/types/layouts/workspace/viewer/ViewerMenu.vue.d.ts +9 -1
  125. package/types/services/BaseService.d.ts +4 -1
  126. package/types/services/dataSource.d.ts +1 -0
  127. package/types/services/dep.d.ts +6 -5
  128. package/types/services/editor.d.ts +12 -5
  129. package/types/services/history.d.ts +3 -2
  130. package/types/services/keybinding.d.ts +21 -1
  131. package/types/services/props.d.ts +25 -1
  132. package/types/services/storage.d.ts +7 -7
  133. package/types/type.d.ts +32 -17
  134. package/types/utils/compose.d.ts +1 -1
  135. package/types/utils/editor.d.ts +14 -8
  136. package/src/layouts/sidebar/layer/use-filter.ts +0 -59
  137. package/src/layouts/workspace/PageBar.vue +0 -87
  138. package/src/layouts/workspace/viewer/NodeListMenuTitle.vue +0 -68
  139. package/src/theme/dep-list.scss +0 -59
  140. package/types/layouts/sidebar/layer/use-filter.d.ts +0 -7
  141. package/types/layouts/workspace/PageBar.vue.d.ts +0 -15
  142. package/types/layouts/workspace/PageBarScrollContainer.vue.d.ts +0 -9
@@ -1,4 +1,4 @@
1
- import { computed } from 'vue';
1
+ import { computed, watch } from 'vue';
2
2
 
3
3
  import type { MNode } from '@tmagic/schema';
4
4
  import StageCore, { GuidesType, RemoveEventData, SortEventData, UpdateEventData } from '@tmagic/stage';
@@ -31,6 +31,7 @@ export const useStage = (stageOptions: StageOptions) => {
31
31
  containerHighlightDuration: stageOptions.containerHighlightDuration,
32
32
  containerHighlightType: stageOptions.containerHighlightType,
33
33
  disabledDragStart: stageOptions.disabledDragStart,
34
+ renderType: stageOptions.renderType,
34
35
  canSelect: (el, event, stop) => {
35
36
  const elCanSelect = stageOptions.canSelect(el);
36
37
  // 在组件联动过程中不能再往下选择,返回并触发 ui-select
@@ -43,13 +44,30 @@ export const useStage = (stageOptions: StageOptions) => {
43
44
  },
44
45
  moveableOptions: stageOptions.moveableOptions,
45
46
  updateDragEl: stageOptions.updateDragEl,
47
+ guidesOptions: stageOptions.guidesOptions,
48
+ disabledMultiSelect: stageOptions.disabledMultiSelect,
46
49
  });
47
50
 
51
+ watch(
52
+ () => editorService.get('disabledMultiSelect'),
53
+ (disabledMultiSelect) => {
54
+ if (disabledMultiSelect) {
55
+ stage.disableMultiSelect();
56
+ } else {
57
+ stage.enableMultiSelect();
58
+ }
59
+ },
60
+ );
61
+
48
62
  stage.mask.setGuides([
49
63
  getGuideLineFromCache(getGuideLineKey(H_GUIDE_LINE_STORAGE_KEY)),
50
64
  getGuideLineFromCache(getGuideLineKey(V_GUIDE_LINE_STORAGE_KEY)),
51
65
  ]);
52
66
 
67
+ stage.on('page-el-update', () => {
68
+ editorService.set('stageLoading', false);
69
+ });
70
+
53
71
  stage.on('select', (el: HTMLElement) => {
54
72
  if (`${editorService.get('node')?.id}` === el.id && editorService.get('nodes').length === 1) return;
55
73
  editorService.select(el.id);
package/src/index.ts CHANGED
@@ -30,6 +30,7 @@ import DataSourceMocks from './fields/DataSourceMocks.vue';
30
30
  import DataSourceSelect from './fields/DataSourceSelect.vue';
31
31
  import EventSelect from './fields/EventSelect.vue';
32
32
  import KeyValue from './fields/KeyValue.vue';
33
+ import PageFragmentSelect from './fields/PageFragmentSelect.vue';
33
34
  import uiSelect from './fields/UISelect.vue';
34
35
  import CodeEditor from './layouts/CodeEditor.vue';
35
36
  import { setConfig } from './utils/config';
@@ -79,6 +80,7 @@ export { default as LayoutContainer } from './components/SplitView.vue';
79
80
  export { default as SplitView } from './components/SplitView.vue';
80
81
  export { default as Resizer } from './components/Resizer.vue';
81
82
  export { default as CodeBlockEditor } from './components/CodeBlockEditor.vue';
83
+ export { default as PageFragmentSelect } from './fields/PageFragmentSelect.vue';
82
84
 
83
85
  const defaultInstallOpt: InstallOptions = {
84
86
  // eslint-disable-next-line no-eval
@@ -108,5 +110,6 @@ export default {
108
110
  app.component('m-fields-data-source-methods', DataSourceMethods);
109
111
  app.component('m-fields-data-source-method-select', DataSourceMethodSelect);
110
112
  app.component('m-fields-data-source-field-select', DataSourceFieldSelect);
113
+ app.component('m-fields-page-fragment-select', PageFragmentSelect);
111
114
  },
112
115
  };
@@ -1,16 +1,17 @@
1
- import { onUnmounted, reactive, toRaw, watch } from 'vue';
1
+ import { onBeforeUnmount, reactive, toRaw, watch } from 'vue';
2
2
  import { cloneDeep } from 'lodash-es';
3
3
 
4
4
  import type { EventOption } from '@tmagic/core';
5
- import type { Target } from '@tmagic/dep';
6
5
  import {
7
6
  createCodeBlockTarget,
8
7
  createDataSourceCondTarget,
9
8
  createDataSourceMethodTarget,
10
9
  createDataSourceTarget,
10
+ createRelatedCompTarget,
11
11
  DepTargetType,
12
+ Target,
12
13
  } from '@tmagic/dep';
13
- import type { CodeBlockContent, DataSourceSchema, Id, MApp, MNode, MPage } from '@tmagic/schema';
14
+ import type { CodeBlockContent, DataSourceSchema, Id, MApp, MNode, MPage, MPageFragment } from '@tmagic/schema';
14
15
  import { getNodes } from '@tmagic/utils';
15
16
 
16
17
  import PropsPanel from './layouts/PropsPanel.vue';
@@ -46,6 +47,16 @@ export const initServiceState = (
46
47
  },
47
48
  );
48
49
 
50
+ watch(
51
+ () => props.disabledMultiSelect,
52
+ (disabledMultiSelect) => {
53
+ editorService.set('disabledMultiSelect', disabledMultiSelect || false);
54
+ },
55
+ {
56
+ immediate: true,
57
+ },
58
+ );
59
+
49
60
  watch(
50
61
  () => props.componentGroupList,
51
62
  (componentGroupList) => componentGroupList && componentListService.setList(componentGroupList),
@@ -164,7 +175,7 @@ export const initServiceState = (
164
175
  },
165
176
  );
166
177
 
167
- onUnmounted(() => {
178
+ onBeforeUnmount(() => {
168
179
  editorService.resetState();
169
180
  historyService.resetState();
170
181
  propsService.resetState();
@@ -186,7 +197,7 @@ export const initServiceEvents = (
186
197
  return stage?.renderer.runtime?.getApp?.();
187
198
  };
188
199
 
189
- const updateDataSoucreSchema = () => {
200
+ const updateDataSourceSchema = () => {
190
201
  const root = editorService.get('root');
191
202
 
192
203
  if (root?.dataSources) {
@@ -194,7 +205,7 @@ export const initServiceEvents = (
194
205
  }
195
206
  };
196
207
 
197
- const upateNodeWhenDataSourceChange = (nodes: MNode[]) => {
208
+ const updateNodeWhenDataSourceChange = (nodes: MNode[]) => {
198
209
  const root = editorService.get('root');
199
210
  const stage = editorService.get('stage');
200
211
 
@@ -210,7 +221,7 @@ export const initServiceEvents = (
210
221
  app.dsl.dataSources = root.dataSources;
211
222
  }
212
223
 
213
- updateDataSoucreSchema();
224
+ updateDataSourceSchema();
214
225
 
215
226
  nodes.forEach((node) => {
216
227
  const deps = Object.values(root.dataSourceDeps || {});
@@ -258,11 +269,11 @@ export const initServiceEvents = (
258
269
  };
259
270
 
260
271
  const depUpdateHandler = (node: MNode) => {
261
- upateNodeWhenDataSourceChange([node]);
272
+ updateNodeWhenDataSourceChange([node]);
262
273
  };
263
274
 
264
275
  const collectedHandler = (nodes: MNode[]) => {
265
- upateNodeWhenDataSourceChange(nodes);
276
+ updateNodeWhenDataSourceChange(nodes);
266
277
  };
267
278
 
268
279
  depService.on('add-target', targetAddHandler);
@@ -291,7 +302,7 @@ export const initServiceEvents = (
291
302
  depService.addTarget(createCodeBlockTarget(id, code));
292
303
  });
293
304
 
294
- value.dataSources.forEach((ds) => {
305
+ dataSourceService.get('dataSources').forEach((ds) => {
295
306
  initDataSourceDepTarget(ds);
296
307
  });
297
308
 
@@ -339,7 +350,7 @@ export const initServiceEvents = (
339
350
  };
340
351
 
341
352
  // 由于历史记录变化是更新整个page,所以历史记录变化时,需要重新收集依赖
342
- const historyChangeHandler = (page: MPage) => {
353
+ const historyChangeHandler = (page: MPage | MPageFragment) => {
343
354
  depService.collect([page], true);
344
355
  };
345
356
 
@@ -350,8 +361,8 @@ export const initServiceEvents = (
350
361
  editorService.on('update', nodeUpdateHandler);
351
362
 
352
363
  const codeBlockAddOrUpdateHandler = (id: Id, codeBlock: CodeBlockContent) => {
353
- if (depService.hasTarget(id)) {
354
- depService.getTarget(id)!.name = codeBlock.name;
364
+ if (depService.hasTarget(id, DepTargetType.CODE_BLOCK)) {
365
+ depService.getTarget(id, DepTargetType.CODE_BLOCK)!.name = codeBlock.name;
355
366
  return;
356
367
  }
357
368
 
@@ -359,7 +370,7 @@ export const initServiceEvents = (
359
370
  };
360
371
 
361
372
  const codeBlockRemoveHandler = (id: Id) => {
362
- depService.removeTarget(id);
373
+ depService.removeTarget(id, DepTargetType.CODE_BLOCK);
363
374
  };
364
375
 
365
376
  codeBlockService.on('addOrUpdate', codeBlockAddOrUpdateHandler);
@@ -372,16 +383,26 @@ export const initServiceEvents = (
372
383
 
373
384
  const dataSourceUpdateHandler = (config: DataSourceSchema) => {
374
385
  const root = editorService.get('root');
386
+ removeDataSourceTarget(config.id);
387
+ initDataSourceDepTarget(config);
388
+
389
+ depService.collect(root?.items || [], true);
375
390
 
376
391
  const targets = depService.getTargets(DepTargetType.DATA_SOURCE);
377
392
 
378
393
  const nodes = getNodes(Object.keys(targets[config.id].deps), root?.items);
379
394
 
380
- upateNodeWhenDataSourceChange(nodes);
395
+ updateNodeWhenDataSourceChange(nodes);
396
+ };
397
+
398
+ const removeDataSourceTarget = (id: string) => {
399
+ depService.removeTarget(id, DepTargetType.DATA_SOURCE);
400
+ depService.removeTarget(id, DepTargetType.DATA_SOURCE_COND);
401
+ depService.removeTarget(id, DepTargetType.DATA_SOURCE_METHOD);
381
402
  };
382
403
 
383
404
  const dataSourceRemoveHandler = (id: string) => {
384
- depService.removeTarget(id);
405
+ removeDataSourceTarget(id);
385
406
  getApp()?.dataSourceManager?.removeDataSource(id);
386
407
  };
387
408
 
@@ -389,7 +410,12 @@ export const initServiceEvents = (
389
410
  dataSourceService.on('update', dataSourceUpdateHandler);
390
411
  dataSourceService.on('remove', dataSourceRemoveHandler);
391
412
 
392
- onUnmounted(() => {
413
+ // 初始化复制组件相关的依赖收集器
414
+ if (props.collectorOptions && !depService.hasTarget(DepTargetType.RELATED_COMP_WHEN_COPY)) {
415
+ depService.addTarget(createRelatedCompTarget(props.collectorOptions));
416
+ }
417
+
418
+ onBeforeUnmount(() => {
393
419
  depService.off('add-target', targetAddHandler);
394
420
  depService.off('remove-target', targetRemoveHandler);
395
421
  depService.off('dep-update', depUpdateHandler);
@@ -1,12 +1,19 @@
1
1
  <template>
2
2
  <div class="m-editor-empty-panel">
3
3
  <div class="m-editor-empty-content">
4
- <div class="m-editor-empty-button" @click="clickHandler">
4
+ <div class="m-editor-empty-button" @click="clickHandler(NodeType.PAGE)">
5
5
  <div>
6
6
  <MIcon :icon="Plus"></MIcon>
7
7
  </div>
8
8
  <p>新增页面</p>
9
9
  </div>
10
+
11
+ <div v-if="!disabledPageFragment" class="m-editor-empty-button" @click="clickHandler(NodeType.PAGE_FRAGMENT)">
12
+ <div>
13
+ <MIcon :icon="Plus"></MIcon>
14
+ </div>
15
+ <p>新增页面片</p>
16
+ </div>
10
17
  </div>
11
18
  </div>
12
19
  </template>
@@ -25,9 +32,13 @@ defineOptions({
25
32
  name: 'MEditorAddPageBox',
26
33
  });
27
34
 
35
+ defineProps<{
36
+ disabledPageFragment: boolean;
37
+ }>();
38
+
28
39
  const services = inject<Services>('services');
29
40
 
30
- const clickHandler = () => {
41
+ const clickHandler = (type: NodeType.PAGE | NodeType.PAGE_FRAGMENT) => {
31
42
  const { editorService } = services || {};
32
43
 
33
44
  if (!editorService) return;
@@ -36,8 +47,9 @@ const clickHandler = () => {
36
47
  if (!root) throw new Error('root 不能为空');
37
48
 
38
49
  editorService.add({
39
- type: NodeType.PAGE,
40
- name: generatePageNameByApp(root),
50
+ type,
51
+ name: generatePageNameByApp(root, type),
52
+ items: [],
41
53
  });
42
54
  };
43
55
  </script>
@@ -19,7 +19,7 @@
19
19
  </template>
20
20
 
21
21
  <script lang="ts" setup>
22
- import { onMounted, onUnmounted, ref, watch } from 'vue';
22
+ import { onBeforeUnmount, onMounted, ref, watch } from 'vue';
23
23
  import { FullScreen } from '@element-plus/icons-vue';
24
24
  import { throttle } from 'lodash-es';
25
25
  import * as monaco from 'monaco-editor';
@@ -188,7 +188,7 @@ onMounted(async () => {
188
188
  init();
189
189
  });
190
190
 
191
- onUnmounted(() => {
191
+ onBeforeUnmount(() => {
192
192
  resizeObserver.disconnect();
193
193
  });
194
194
 
@@ -11,6 +11,8 @@
11
11
  </slot>
12
12
 
13
13
  <SplitView
14
+ v-loading="stageLoading"
15
+ element-loading-text="Runtime 加载中..."
14
16
  v-else
15
17
  ref="splitView"
16
18
  class="m-editor-content"
@@ -29,13 +31,18 @@
29
31
  </template>
30
32
 
31
33
  <template #center>
32
- <slot v-if="pageLength > 0" name="workspace"></slot>
34
+ <slot v-if="page" name="workspace"></slot>
33
35
  <slot v-else name="empty">
34
- <AddPageBox></AddPageBox>
36
+ <AddPageBox :disabled-page-fragment="disabledPageFragment"></AddPageBox>
35
37
  </slot>
38
+
39
+ <PageBar :disabled-page-fragment="disabledPageFragment">
40
+ <template #page-bar-title="{ page }"><slot name="page-bar-title" :page="page"></slot></template>
41
+ <template #page-bar-popover="{ page }"><slot name="page-bar-popover" :page="page"></slot></template>
42
+ </PageBar>
36
43
  </template>
37
44
 
38
- <template v-if="pageLength > 0" #right>
45
+ <template v-if="page" #right>
39
46
  <TMagicScrollbar>
40
47
  <slot name="props-panel"></slot>
41
48
  </TMagicScrollbar>
@@ -56,6 +63,7 @@ import SplitView from '@editor/components/SplitView.vue';
56
63
  import type { FrameworkSlots, GetColumnWidth, Services } from '@editor/type';
57
64
  import { getConfig } from '@editor/utils/config';
58
65
 
66
+ import PageBar from './page-bar/PageBar.vue';
59
67
  import AddPageBox from './AddPageBox.vue';
60
68
  import CodeEditor from './CodeEditor.vue';
61
69
 
@@ -65,6 +73,10 @@ defineOptions({
65
73
  name: 'MEditorFramework',
66
74
  });
67
75
 
76
+ defineProps<{
77
+ disabledPageFragment: boolean;
78
+ }>();
79
+
68
80
  const DEFAULT_LEFT_COLUMN_WIDTH = 310;
69
81
  const DEFAULT_RIGHT_COLUMN_WIDTH = 480;
70
82
 
@@ -75,8 +87,10 @@ const content = ref<HTMLDivElement>();
75
87
  const splitView = ref<InstanceType<typeof SplitView>>();
76
88
 
77
89
  const root = computed(() => editorService?.get('root'));
90
+ const page = computed(() => editorService?.get('page'));
78
91
 
79
92
  const pageLength = computed(() => editorService?.get('pageLength') || 0);
93
+ const stageLoading = computed(() => editorService?.get('stageLoading') || false);
80
94
  const showSrc = computed(() => uiService?.get('showSrc'));
81
95
 
82
96
  const LEFT_COLUMN_WIDTH_STORAGE_KEY = '$MagicEditorLeftColumnWidthData';
@@ -15,7 +15,7 @@
15
15
  </template>
16
16
 
17
17
  <script lang="ts" setup>
18
- import { computed, getCurrentInstance, inject, onMounted, onUnmounted, ref, watchEffect } from 'vue';
18
+ import { computed, getCurrentInstance, inject, onBeforeUnmount, onMounted, ref, watchEffect } from 'vue';
19
19
 
20
20
  import { tMagicMessage } from '@tmagic/design';
21
21
  import type { FormState, FormValue } from '@tmagic/form';
@@ -64,7 +64,7 @@ onMounted(() => {
64
64
  emit('mounted', internalInstance);
65
65
  });
66
66
 
67
- onUnmounted(() => {
67
+ onBeforeUnmount(() => {
68
68
  services?.propsService.off('props-configs-change', init);
69
69
  });
70
70
 
@@ -0,0 +1,48 @@
1
+ <template>
2
+ <div
3
+ v-if="showAddPageButton"
4
+ id="m-editor-page-bar-add-icon"
5
+ class="m-editor-page-bar-item m-editor-page-bar-item-icon"
6
+ @click="addPage"
7
+ >
8
+ <Icon :icon="Plus"></Icon>
9
+ </div>
10
+ <div v-else style="width: 21px"></div>
11
+ </template>
12
+
13
+ <script setup lang="ts">
14
+ import { computed, inject, toRaw } from 'vue';
15
+ import { Plus } from '@element-plus/icons-vue';
16
+
17
+ import { NodeType } from '@tmagic/schema';
18
+
19
+ import Icon from '@editor/components/Icon.vue';
20
+ import type { Services } from '@editor/type';
21
+ import { generatePageNameByApp } from '@editor/utils/editor';
22
+
23
+ defineOptions({
24
+ name: 'MEditorPageBarAddButton',
25
+ });
26
+
27
+ const props = defineProps<{
28
+ type: NodeType.PAGE | NodeType.PAGE_FRAGMENT;
29
+ }>();
30
+
31
+ const services = inject<Services>('services');
32
+ const uiService = services?.uiService;
33
+ const editorService = services?.editorService;
34
+
35
+ const showAddPageButton = computed(() => uiService?.get('showAddPageButton'));
36
+
37
+ const addPage = () => {
38
+ if (!editorService) return;
39
+ const root = toRaw(editorService.get('root'));
40
+ if (!root) throw new Error('root 不能为空');
41
+ const pageConfig = {
42
+ type: props.type,
43
+ name: generatePageNameByApp(root, props.type),
44
+ items: [],
45
+ };
46
+ editorService.add(pageConfig);
47
+ };
48
+ </script>
@@ -0,0 +1,158 @@
1
+ <template>
2
+ <div class="m-editor-page-bar-tabs">
3
+ <SwitchTypeButton v-if="!disabledPageFragment" v-model="active" />
4
+
5
+ <PageBarScrollContainer :type="active">
6
+ <template #prepend>
7
+ <AddButton :type="active"></AddButton>
8
+ </template>
9
+
10
+ <div
11
+ v-for="item in list"
12
+ class="m-editor-page-bar-item"
13
+ :key="item.id"
14
+ :class="{ active: page?.id === item.id }"
15
+ @click="switchPage(item.id)"
16
+ >
17
+ <div class="m-editor-page-bar-title">
18
+ <slot name="page-bar-title" :page="item">
19
+ <span :title="item.name">{{ item.name || item.id }}</span>
20
+ </slot>
21
+ </div>
22
+
23
+ <TMagicPopover popper-class="page-bar-popover" placement="top" :width="160" trigger="hover">
24
+ <div>
25
+ <slot name="page-bar-popover" :page="item">
26
+ <ToolButton
27
+ :data="{
28
+ type: 'button',
29
+ text: '复制',
30
+ icon: DocumentCopy,
31
+ handler: () => copy(item),
32
+ }"
33
+ ></ToolButton>
34
+ <ToolButton
35
+ :data="{
36
+ type: 'button',
37
+ text: '删除',
38
+ icon: Delete,
39
+ handler: () => remove(item),
40
+ }"
41
+ ></ToolButton>
42
+ </slot>
43
+ </div>
44
+ <template #reference>
45
+ <TMagicIcon class="m-editor-page-bar-menu-icon">
46
+ <CaretBottom></CaretBottom>
47
+ </TMagicIcon>
48
+ </template>
49
+ </TMagicPopover>
50
+ </div>
51
+ </PageBarScrollContainer>
52
+ </div>
53
+ </template>
54
+
55
+ <script lang="ts" setup>
56
+ import { computed, inject, ref, watch } from 'vue';
57
+ import { CaretBottom, Delete, DocumentCopy } from '@element-plus/icons-vue';
58
+
59
+ import { TMagicIcon, TMagicPopover } from '@tmagic/design';
60
+ import { Id, type MPage, type MPageFragment, NodeType } from '@tmagic/schema';
61
+ import { isPage, isPageFragment } from '@tmagic/utils';
62
+
63
+ import ToolButton from '@editor/components/ToolButton.vue';
64
+ import type { Services } from '@editor/type';
65
+ import { getPageFragmentList, getPageList } from '@editor/utils';
66
+
67
+ import AddButton from './AddButton.vue';
68
+ import PageBarScrollContainer from './PageBarScrollContainer.vue';
69
+ import SwitchTypeButton from './SwitchTypeButton.vue';
70
+
71
+ defineOptions({
72
+ name: 'MEditorPageBar',
73
+ });
74
+
75
+ defineProps<{
76
+ disabledPageFragment: boolean;
77
+ }>();
78
+
79
+ const active = ref<NodeType.PAGE | NodeType.PAGE_FRAGMENT>(NodeType.PAGE);
80
+
81
+ const services = inject<Services>('services');
82
+ const editorService = services?.editorService;
83
+
84
+ const root = computed(() => editorService?.get('root'));
85
+ const page = computed(() => editorService?.get('page'));
86
+ const pageList = computed(() => getPageList(root.value));
87
+ const pageFragmentList = computed(() => getPageFragmentList(root.value));
88
+
89
+ const list = computed(() => (active.value === NodeType.PAGE ? pageList.value : pageFragmentList.value));
90
+
91
+ const activePage = ref<Id>('');
92
+ const activePageFragment = ref<Id>('');
93
+
94
+ watch(
95
+ page,
96
+ (page) => {
97
+ if (!page) {
98
+ if (active.value === NodeType.PAGE) {
99
+ activePage.value = '';
100
+ }
101
+ if (active.value === NodeType.PAGE_FRAGMENT) {
102
+ activePageFragment.value = '';
103
+ }
104
+ return;
105
+ }
106
+
107
+ if (isPage(page)) {
108
+ activePage.value = page?.id;
109
+ if (active.value !== NodeType.PAGE) {
110
+ active.value = NodeType.PAGE;
111
+ }
112
+ } else if (isPageFragment(page)) {
113
+ activePageFragment.value = page?.id;
114
+ if (active.value !== NodeType.PAGE_FRAGMENT) {
115
+ active.value = NodeType.PAGE_FRAGMENT;
116
+ }
117
+ }
118
+ },
119
+ {
120
+ immediate: true,
121
+ },
122
+ );
123
+
124
+ watch(active, (active) => {
125
+ if (active === NodeType.PAGE) {
126
+ if (!activePage.value) {
127
+ editorService?.selectRoot();
128
+ return;
129
+ }
130
+ switchPage(activePage.value);
131
+ return;
132
+ }
133
+
134
+ if (active === NodeType.PAGE_FRAGMENT) {
135
+ if (!activePageFragment.value) {
136
+ editorService?.selectRoot();
137
+ return;
138
+ }
139
+ switchPage(activePageFragment.value);
140
+ }
141
+ });
142
+
143
+ const switchPage = (id: Id) => {
144
+ editorService?.select(id);
145
+ };
146
+
147
+ const copy = (node: MPage | MPageFragment) => {
148
+ node && editorService?.copy(node);
149
+ editorService?.paste({
150
+ left: 0,
151
+ top: 0,
152
+ });
153
+ };
154
+
155
+ const remove = (node: MPage | MPageFragment) => {
156
+ editorService?.remove(node);
157
+ };
158
+ </script>