@tmagic/editor 1.3.6 → 1.3.8

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 (82) hide show
  1. package/LICENSE +5432 -0
  2. package/dist/style.css +34 -11
  3. package/dist/tmagic-editor.js +1360 -957
  4. package/dist/tmagic-editor.js.map +1 -1
  5. package/dist/tmagic-editor.umd.cjs +1382 -977
  6. package/dist/tmagic-editor.umd.cjs.map +1 -1
  7. package/package.json +17 -17
  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/ScrollBar.vue +2 -2
  12. package/src/components/ScrollViewer.vue +2 -2
  13. package/src/components/SplitView.vue +2 -2
  14. package/src/components/Tree.vue +1 -1
  15. package/src/components/TreeNode.vue +3 -3
  16. package/src/editorProps.ts +14 -4
  17. package/src/fields/PageFragmentSelect.vue +63 -0
  18. package/src/hooks/use-getso.ts +2 -2
  19. package/src/hooks/use-stage.ts +18 -1
  20. package/src/index.ts +3 -0
  21. package/src/initService.ts +15 -5
  22. package/src/layouts/AddPageBox.vue +16 -4
  23. package/src/layouts/CodeEditor.vue +2 -2
  24. package/src/layouts/Framework.vue +19 -3
  25. package/src/layouts/PropsPanel.vue +9 -7
  26. package/src/layouts/page-bar/AddButton.vue +48 -0
  27. package/src/layouts/page-bar/PageBar.vue +158 -0
  28. package/src/layouts/{workspace → page-bar}/PageBarScrollContainer.vue +61 -35
  29. package/src/layouts/page-bar/SwitchTypeButton.vue +45 -0
  30. package/src/layouts/sidebar/Sidebar.vue +2 -0
  31. package/src/layouts/sidebar/layer/LayerMenu.vue +39 -27
  32. package/src/layouts/sidebar/layer/LayerPanel.vue +7 -1
  33. package/src/layouts/sidebar/layer/use-click.ts +5 -3
  34. package/src/layouts/sidebar/layer/use-keybinding.ts +8 -8
  35. package/src/layouts/sidebar/layer/use-node-status.ts +3 -3
  36. package/src/layouts/workspace/Workspace.vue +6 -7
  37. package/src/layouts/workspace/viewer/NodeListMenu.vue +2 -2
  38. package/src/layouts/workspace/viewer/Stage.vue +12 -5
  39. package/src/layouts/workspace/viewer/ViewerMenu.vue +93 -80
  40. package/src/services/dataSource.ts +17 -0
  41. package/src/services/dep.ts +4 -0
  42. package/src/services/editor.ts +66 -35
  43. package/src/services/history.ts +6 -2
  44. package/src/services/keybinding.ts +48 -13
  45. package/src/theme/code-block.scss +0 -16
  46. package/src/theme/page-bar.scss +18 -1
  47. package/src/theme/page-fragment-select.scss +14 -0
  48. package/src/theme/theme.scss +1 -0
  49. package/src/theme/tree.scss +6 -0
  50. package/src/type.ts +23 -7
  51. package/src/utils/content-menu.ts +2 -2
  52. package/src/utils/editor.ts +22 -15
  53. package/src/utils/operator.ts +4 -4
  54. package/types/components/ToolButton.vue.d.ts +2 -2
  55. package/types/editorProps.d.ts +11 -2
  56. package/types/fields/PageFragmentSelect.vue.d.ts +31 -0
  57. package/types/index.d.ts +1 -0
  58. package/types/layouts/AddPageBox.vue.d.ts +14 -1
  59. package/types/layouts/Framework.vue.d.ts +14 -1
  60. package/types/layouts/PropsPanel.vue.d.ts +19 -10
  61. package/types/layouts/page-bar/AddButton.vue.d.ts +16 -0
  62. package/types/layouts/page-bar/PageBar.vue.d.ts +28 -0
  63. package/types/layouts/page-bar/PageBarScrollContainer.vue.d.ts +24 -0
  64. package/types/layouts/page-bar/SwitchTypeButton.vue.d.ts +20 -0
  65. package/types/layouts/sidebar/Sidebar.vue.d.ts +2 -0
  66. package/types/layouts/sidebar/layer/LayerMenu.vue.d.ts +22 -3
  67. package/types/layouts/sidebar/layer/LayerPanel.vue.d.ts +2 -0
  68. package/types/layouts/sidebar/layer/use-click.d.ts +25 -3
  69. package/types/layouts/sidebar/layer/use-keybinding.d.ts +1 -1
  70. package/types/layouts/workspace/Workspace.vue.d.ts +2 -0
  71. package/types/layouts/workspace/viewer/Stage.vue.d.ts +2 -0
  72. package/types/layouts/workspace/viewer/ViewerMenu.vue.d.ts +8 -0
  73. package/types/services/dataSource.d.ts +2 -0
  74. package/types/services/dep.d.ts +1 -0
  75. package/types/services/editor.d.ts +3 -2
  76. package/types/services/history.d.ts +3 -2
  77. package/types/services/keybinding.d.ts +21 -1
  78. package/types/type.d.ts +26 -12
  79. package/types/utils/editor.d.ts +9 -7
  80. package/src/layouts/workspace/PageBar.vue +0 -87
  81. package/types/layouts/workspace/PageBar.vue.d.ts +0 -15
  82. package/types/layouts/workspace/PageBarScrollContainer.vue.d.ts +0 -9
@@ -3,15 +3,14 @@
3
3
  <Breadcrumb></Breadcrumb>
4
4
 
5
5
  <slot name="stage">
6
- <MagicStage v-if="page" :stage-content-menu="stageContentMenu"></MagicStage>
6
+ <MagicStage
7
+ v-if="page"
8
+ :stage-content-menu="stageContentMenu"
9
+ :custom-content-menu="customContentMenu"
10
+ ></MagicStage>
7
11
  </slot>
8
12
 
9
13
  <slot name="workspace-content"></slot>
10
-
11
- <PageBar>
12
- <template #page-bar-title="{ page }"><slot name="page-bar-title" :page="page"></slot></template>
13
- <template #page-bar-popover="{ page }"><slot name="page-bar-popover" :page="page"></slot></template>
14
- </PageBar>
15
14
  </div>
16
15
  </template>
17
16
 
@@ -22,7 +21,6 @@ import type { MenuButton, MenuComponent, Services, WorkspaceSlots } from '@edito
22
21
 
23
22
  import MagicStage from './viewer/Stage.vue';
24
23
  import Breadcrumb from './Breadcrumb.vue';
25
- import PageBar from './PageBar.vue';
26
24
 
27
25
  defineSlots<WorkspaceSlots>();
28
26
 
@@ -32,6 +30,7 @@ defineOptions({
32
30
 
33
31
  defineProps<{
34
32
  stageContentMenu: (MenuButton | MenuComponent)[];
33
+ customContentMenu?: (menus: (MenuButton | MenuComponent)[], type: string) => (MenuButton | MenuComponent)[];
35
34
  }>();
36
35
 
37
36
  const services = inject<Services>('services');
@@ -56,6 +56,8 @@ const unWatch = watch(
56
56
  (stage) => {
57
57
  if (!stage) return;
58
58
 
59
+ nextTick(() => unWatch());
60
+
59
61
  stage.on('select', (el: HTMLElement, event: MouseEvent) => {
60
62
  const els = stage.renderer.getElementsFromPoint(event) || [];
61
63
  const ids = els.map((el) => el.id).filter((id) => Boolean(id));
@@ -64,8 +66,6 @@ const unWatch = watch(
64
66
 
65
67
  filterTextChangeHandler(ids);
66
68
  });
67
-
68
- unWatch();
69
69
  },
70
70
  {
71
71
  immediate: true,
@@ -25,13 +25,18 @@
25
25
  <NodeListMenu></NodeListMenu>
26
26
 
27
27
  <Teleport to="body">
28
- <ViewerMenu ref="menu" :is-multi-select="isMultiSelect" :stage-content-menu="stageContentMenu"></ViewerMenu>
28
+ <ViewerMenu
29
+ ref="menu"
30
+ :is-multi-select="isMultiSelect"
31
+ :stage-content-menu="stageContentMenu"
32
+ :custom-content-menu="customContentMenu"
33
+ ></ViewerMenu>
29
34
  </Teleport>
30
35
  </ScrollViewer>
31
36
  </template>
32
37
 
33
38
  <script lang="ts" setup>
34
- import { computed, inject, markRaw, nextTick, onMounted, onUnmounted, ref, toRaw, watch, watchEffect } from 'vue';
39
+ import { computed, inject, markRaw, nextTick, onBeforeUnmount, onMounted, ref, toRaw, watch, watchEffect } from 'vue';
35
40
  import { cloneDeep } from 'lodash-es';
36
41
 
37
42
  import type { MApp, MContainer } from '@tmagic/schema';
@@ -52,6 +57,7 @@ defineOptions({
52
57
 
53
58
  defineProps<{
54
59
  stageContentMenu: (MenuButton | MenuComponent)[];
60
+ customContentMenu?: (menus: (MenuButton | MenuComponent)[], type: string) => (MenuButton | MenuComponent)[];
55
61
  }>();
56
62
 
57
63
  let stage: StageCore | null = null;
@@ -108,6 +114,7 @@ watch(zoom, (zoom) => {
108
114
 
109
115
  watch(page, (page) => {
110
116
  if (runtime && page) {
117
+ services?.editorService.set('stageLoading', true);
111
118
  runtime.updatePageId?.(page.id);
112
119
  nextTick(() => {
113
120
  stage?.select(page.id);
@@ -135,15 +142,15 @@ const resizeObserver = new ResizeObserver((entries) => {
135
142
  onMounted(() => {
136
143
  if (stageWrap.value?.container) {
137
144
  resizeObserver.observe(stageWrap.value.container);
138
- services?.keybindingService.registeEl(KeyBindingContainerKey.STAGE, stageWrap.value.container);
145
+ services?.keybindingService.registerEl(KeyBindingContainerKey.STAGE, stageWrap.value.container);
139
146
  }
140
147
  });
141
148
 
142
- onUnmounted(() => {
149
+ onBeforeUnmount(() => {
143
150
  stage?.destroy();
144
151
  resizeObserver.disconnect();
145
152
  services?.editorService.set('stage', null);
146
- services?.keybindingService.unregisteEl('stage');
153
+ services?.keybindingService.unregisterEl('stage');
147
154
  services?.editorService.off('root-change', rootChangeHandler);
148
155
  });
149
156
 
@@ -7,7 +7,7 @@ import { computed, inject, markRaw, ref, watch } from 'vue';
7
7
  import { Bottom, Top } from '@element-plus/icons-vue';
8
8
 
9
9
  import { NodeType } from '@tmagic/schema';
10
- import { isPage } from '@tmagic/utils';
10
+ import { isPage, isPageFragment } from '@tmagic/utils';
11
11
 
12
12
  import ContentMenu from '@editor/components/ContentMenu.vue';
13
13
  import CenterIcon from '@editor/icons/CenterIcon.vue';
@@ -19,8 +19,16 @@ defineOptions({
19
19
  });
20
20
 
21
21
  const props = withDefaults(
22
- defineProps<{ isMultiSelect?: boolean; stageContentMenu: (MenuButton | MenuComponent)[] }>(),
23
- { isMultiSelect: false },
22
+ defineProps<{
23
+ isMultiSelect?: boolean;
24
+ stageContentMenu: (MenuButton | MenuComponent)[];
25
+ customContentMenu?: (menus: (MenuButton | MenuComponent)[], type: string) => (MenuButton | MenuComponent)[];
26
+ }>(),
27
+ {
28
+ isMultiSelect: false,
29
+ stageContentMenu: () => [],
30
+ customContentMenu: (menus: (MenuButton | MenuComponent)[]) => menus,
31
+ },
24
32
  );
25
33
 
26
34
  const services = inject<Services>('services');
@@ -32,83 +40,88 @@ const node = computed(() => editorService?.get('node'));
32
40
  const nodes = computed(() => editorService?.get('nodes'));
33
41
  const parent = computed(() => editorService?.get('parent'));
34
42
 
35
- const menuData = computed<(MenuButton | MenuComponent)[]>(() => [
36
- {
37
- type: 'button',
38
- text: '水平居中',
39
- icon: markRaw(CenterIcon),
40
- display: () => canCenter.value,
41
- handler: () => {
42
- if (!nodes.value) return;
43
- editorService?.alignCenter(nodes.value);
44
- },
45
- },
46
- useCopyMenu(),
47
- usePasteMenu(menu),
48
- {
49
- type: 'divider',
50
- direction: 'horizontal',
51
- display: () => {
52
- if (!node.value) return false;
53
- return !isPage(node.value);
54
- },
55
- },
56
- {
57
- type: 'button',
58
- text: '上移一层',
59
- icon: markRaw(Top),
60
- display: () => !isPage(node.value) && !props.isMultiSelect,
61
- handler: () => {
62
- editorService?.moveLayer(1);
63
- },
64
- },
65
- {
66
- type: 'button',
67
- text: '下移一层',
68
- icon: markRaw(Bottom),
69
- display: () => !isPage(node.value) && !props.isMultiSelect,
70
- handler: () => {
71
- editorService?.moveLayer(-1);
72
- },
73
- },
74
- {
75
- type: 'button',
76
- text: '置顶',
77
- icon: markRaw(Top),
78
- display: () => !isPage(node.value) && !props.isMultiSelect,
79
- handler: () => {
80
- editorService?.moveLayer(LayerOffset.TOP);
81
- },
82
- },
83
- {
84
- type: 'button',
85
- text: '置底',
86
- icon: markRaw(Bottom),
87
- display: () => !isPage(node.value) && !props.isMultiSelect,
88
- handler: () => {
89
- editorService?.moveLayer(LayerOffset.BOTTOM);
90
- },
91
- },
92
- useMoveToMenu(services),
93
- {
94
- type: 'divider',
95
- direction: 'horizontal',
96
- display: () => !isPage(node.value) && !props.isMultiSelect,
97
- },
98
- useDeleteMenu(),
99
- {
100
- type: 'divider',
101
- direction: 'horizontal',
102
- },
103
- {
104
- type: 'button',
105
- text: '清空参考线',
106
- handler: () => {
107
- editorService?.get('stage')?.clearGuides();
108
- },
109
- },
110
- ...props.stageContentMenu,
111
- ]);
43
+ const menuData = computed<(MenuButton | MenuComponent)[]>(() =>
44
+ props.customContentMenu(
45
+ [
46
+ {
47
+ type: 'button',
48
+ text: '水平居中',
49
+ icon: markRaw(CenterIcon),
50
+ display: () => canCenter.value,
51
+ handler: () => {
52
+ if (!nodes.value) return;
53
+ editorService?.alignCenter(nodes.value);
54
+ },
55
+ },
56
+ useCopyMenu(),
57
+ usePasteMenu(menu),
58
+ {
59
+ type: 'divider',
60
+ direction: 'horizontal',
61
+ display: () => {
62
+ if (!node.value) return false;
63
+ return !isPage(node.value) && !isPageFragment(node.value);
64
+ },
65
+ },
66
+ {
67
+ type: 'button',
68
+ text: '上移一层',
69
+ icon: markRaw(Top),
70
+ display: () => !isPage(node.value) && !isPageFragment(node.value) && !props.isMultiSelect,
71
+ handler: () => {
72
+ editorService?.moveLayer(1);
73
+ },
74
+ },
75
+ {
76
+ type: 'button',
77
+ text: '下移一层',
78
+ icon: markRaw(Bottom),
79
+ display: () => !isPage(node.value) && !isPageFragment(node.value) && !props.isMultiSelect,
80
+ handler: () => {
81
+ editorService?.moveLayer(-1);
82
+ },
83
+ },
84
+ {
85
+ type: 'button',
86
+ text: '置顶',
87
+ icon: markRaw(Top),
88
+ display: () => !isPage(node.value) && !isPageFragment(node.value) && !props.isMultiSelect,
89
+ handler: () => {
90
+ editorService?.moveLayer(LayerOffset.TOP);
91
+ },
92
+ },
93
+ {
94
+ type: 'button',
95
+ text: '置底',
96
+ icon: markRaw(Bottom),
97
+ display: () => !isPage(node.value) && !isPageFragment(node.value) && !props.isMultiSelect,
98
+ handler: () => {
99
+ editorService?.moveLayer(LayerOffset.BOTTOM);
100
+ },
101
+ },
102
+ useMoveToMenu(services),
103
+ {
104
+ type: 'divider',
105
+ direction: 'horizontal',
106
+ display: () => !isPage(node.value) && !isPageFragment(node.value) && !props.isMultiSelect,
107
+ },
108
+ useDeleteMenu(),
109
+ {
110
+ type: 'divider',
111
+ direction: 'horizontal',
112
+ },
113
+ {
114
+ type: 'button',
115
+ text: '清空参考线',
116
+ handler: () => {
117
+ editorService?.get('stage')?.clearGuides();
118
+ },
119
+ },
120
+ ...props.stageContentMenu,
121
+ ],
122
+ 'viewer',
123
+ ),
124
+ );
112
125
 
113
126
  watch(
114
127
  parent,
@@ -33,6 +33,23 @@ class DataSource extends BaseService {
33
33
  methods: {},
34
34
  });
35
35
 
36
+ constructor() {
37
+ super([
38
+ { name: 'getFormConfig', isAsync: false },
39
+ { name: 'setFormConfig', isAsync: false },
40
+ { name: 'getFormValue', isAsync: false },
41
+ { name: 'setFormValue', isAsync: false },
42
+ { name: 'getFormEvent', isAsync: false },
43
+ { name: 'setFormEvent', isAsync: false },
44
+ { name: 'getFormMethod', isAsync: false },
45
+ { name: 'setFormMethod', isAsync: false },
46
+ { name: 'add', isAsync: false },
47
+ { name: 'update', isAsync: false },
48
+ { name: 'remove', isAsync: false },
49
+ { name: 'createId', isAsync: false },
50
+ ]);
51
+ }
52
+
36
53
  public set<K extends StateKey, T extends State[K]>(name: K, value: T) {
37
54
  this.state[name] = value;
38
55
  }
@@ -62,6 +62,10 @@ class Dep extends BaseService {
62
62
  return this.watcher.clear(nodes);
63
63
  }
64
64
 
65
+ public clearByType(type: DepTargetType, nodes?: MNode[]) {
66
+ return this.watcher.clearByType(type, nodes);
67
+ }
68
+
65
69
  public hasTarget(id: Id, type: string = DepTargetType.DEFAULT) {
66
70
  return this.watcher.hasTarget(id, type);
67
71
  }
@@ -20,10 +20,9 @@ import { reactive, toRaw } from 'vue';
20
20
  import { cloneDeep, get, isObject, mergeWith, uniq } from 'lodash-es';
21
21
 
22
22
  import { DepTargetType } from '@tmagic/dep';
23
- import type { Id, MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
23
+ import type { Id, MApp, MComponent, MContainer, MNode, MPage, MPageFragment } from '@tmagic/schema';
24
24
  import { NodeType } from '@tmagic/schema';
25
- import StageCore from '@tmagic/stage';
26
- import { getNodePath, isNumber, isPage, isPop } from '@tmagic/utils';
25
+ import { getNodePath, isNumber, isPage, isPageFragment, isPop } from '@tmagic/utils';
27
26
 
28
27
  import depService from '@editor/services/dep';
29
28
  import historyService from '@editor/services/history';
@@ -37,8 +36,10 @@ import {
37
36
  fixNodePosition,
38
37
  getInitPositionStyle,
39
38
  getNodeIndex,
39
+ getPageFragmentList,
40
+ getPageList,
40
41
  isFixed,
41
- setChilrenLayout,
42
+ setChildrenLayout,
42
43
  setLayout,
43
44
  } from '@editor/utils/editor';
44
45
  import { beforePaste, getAddParent } from '@editor/utils/operator';
@@ -54,9 +55,12 @@ class Editor extends BaseService {
54
55
  node: null,
55
56
  nodes: [],
56
57
  stage: null,
58
+ stageLoading: true,
57
59
  highlightNode: null,
58
60
  modifiedNodeIds: new Map(),
59
61
  pageLength: 0,
62
+ pageFragmentLength: 0,
63
+ disabledMultiSelect: false,
60
64
  });
61
65
  private isHistoryStateChange = false;
62
66
 
@@ -92,7 +96,7 @@ class Editor extends BaseService {
92
96
 
93
97
  /**
94
98
  * 设置当前指点节点配置
95
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength'
99
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength
96
100
  * @param value MNode
97
101
  */
98
102
  public set<K extends StoreStateKey, T extends StoreState[K]>(name: K, value: T) {
@@ -109,10 +113,15 @@ class Editor extends BaseService {
109
113
  throw new Error('root 不能为数组');
110
114
  }
111
115
 
112
- if (value && isObject(value) && !(value instanceof StageCore) && !(value instanceof Map)) {
113
- this.state.pageLength = value.items?.length || 0;
116
+ if (value && isObject(value)) {
117
+ const app = value as MApp;
118
+ this.state.pageLength = getPageList(app).length || 0;
119
+ this.state.pageFragmentLength = getPageFragmentList(app).length || 0;
120
+ this.state.stageLoading = this.state.pageLength !== 0;
114
121
  } else {
115
122
  this.state.pageLength = 0;
123
+ this.state.pageFragmentLength = 0;
124
+ this.state.stageLoading = false;
116
125
  }
117
126
 
118
127
  this.emit('root-change', value, preValue);
@@ -121,7 +130,7 @@ class Editor extends BaseService {
121
130
 
122
131
  /**
123
132
  * 获取当前指点节点配置
124
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength'
133
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength'
125
134
  * @returns MNode
126
135
  */
127
136
  public get<K extends StoreStateKey>(name: K): StoreState[K] {
@@ -163,8 +172,8 @@ class Editor extends BaseService {
163
172
  info.parent = path[path.length - 2] as MContainer;
164
173
 
165
174
  path.forEach((item) => {
166
- if (item.type === NodeType.PAGE) {
167
- info.page = item as MPage;
175
+ if (isPage(item) || isPageFragment(item)) {
176
+ info.page = item as MPage | MPageFragment;
168
177
  return;
169
178
  }
170
179
  });
@@ -312,6 +321,17 @@ class Editor extends BaseService {
312
321
  this.set('nodes', nodes);
313
322
  }
314
323
 
324
+ public selectRoot() {
325
+ const root = this.get('root');
326
+ if (!root) return;
327
+
328
+ this.set('nodes', [root]);
329
+ this.set('parent', null);
330
+ this.set('page', null);
331
+ this.set('stage', null);
332
+ this.set('highlightNode', null);
333
+ }
334
+
315
335
  public async doAdd(node: MNode, parent: MContainer): Promise<MNode> {
316
336
  const root = this.get('root');
317
337
 
@@ -322,11 +342,11 @@ class Editor extends BaseService {
322
342
 
323
343
  if (!curNode) throw new Error('当前选中节点为空');
324
344
 
325
- if ((parent.type === NodeType.ROOT || curNode?.type === NodeType.ROOT) && node.type !== NodeType.PAGE) {
345
+ if ((parent.type === NodeType.ROOT || curNode?.type === NodeType.ROOT) && !(isPage(node) || isPageFragment(node))) {
326
346
  throw new Error('app下不能添加组件');
327
347
  }
328
348
 
329
- if (parent.id !== curNode.id && node.type !== NodeType.PAGE) {
349
+ if (parent.id !== curNode.id && !(isPage(node) || isPageFragment(node))) {
330
350
  const index = parent.items.indexOf(curNode);
331
351
  parent.items?.splice(index + 1, 0, node);
332
352
  } else {
@@ -380,7 +400,7 @@ class Editor extends BaseService {
380
400
  const newNodes = await Promise.all(
381
401
  addNodes.map((node) => {
382
402
  const root = this.get('root');
383
- if (isPage(node) && root) {
403
+ if ((isPage(node) || isPageFragment(node)) && root) {
384
404
  return this.doAdd(node, root);
385
405
  }
386
406
  const parentNode = parent && typeof parent !== 'function' ? parent : getAddParent(node);
@@ -399,13 +419,15 @@ class Editor extends BaseService {
399
419
 
400
420
  if (isPage(newNodes[0])) {
401
421
  this.state.pageLength += 1;
422
+ } else if (isPageFragment(newNodes[0])) {
423
+ this.state.pageFragmentLength += 1;
402
424
  } else {
403
425
  // 新增页面,这个时候页面还有渲染出来,此时select会出错,在runtime-ready的时候回去select
404
426
  stage?.select(newNodes[0].id);
405
427
  }
406
428
  }
407
429
 
408
- if (!isPage(newNodes[0])) {
430
+ if (!(isPage(newNodes[0]) || isPageFragment(newNodes[0]))) {
409
431
  this.pushHistoryState();
410
432
  }
411
433
 
@@ -418,7 +440,7 @@ class Editor extends BaseService {
418
440
  const root = this.get('root');
419
441
  if (!root) throw new Error('root不能为空');
420
442
 
421
- const { parent, node: curNode } = this.getNodeInfo(node.id);
443
+ const { parent, node: curNode } = this.getNodeInfo(node.id, false);
422
444
 
423
445
  if (!parent || !curNode) throw new Error('找不要删除的节点');
424
446
 
@@ -430,29 +452,38 @@ class Editor extends BaseService {
430
452
  const stage = this.get('stage');
431
453
  stage?.remove({ id: node.id, parentId: parent.id, root: cloneDeep(root) });
432
454
 
433
- if (node.type === NodeType.PAGE) {
434
- this.state.pageLength -= 1;
435
-
436
- if (root.items[0]) {
437
- await this.select(root.items[0]);
438
- stage?.select(root.items[0].id);
455
+ const selectDefault = async (pages: MNode[]) => {
456
+ if (pages[0]) {
457
+ await this.select(pages[0]);
458
+ stage?.select(pages[0].id);
439
459
  } else {
440
- this.set('nodes', [root]);
441
- this.set('parent', null);
442
- this.set('page', null);
443
- this.set('stage', null);
444
- this.set('highlightNode', null);
445
- this.resetModifiedNodeId();
446
- historyService.reset();
460
+ this.selectRoot();
447
461
 
448
- return;
462
+ historyService.resetPage();
449
463
  }
464
+ };
465
+
466
+ const rootItems = root.items || [];
467
+
468
+ if (isPage(node)) {
469
+ this.state.pageLength -= 1;
470
+
471
+ await selectDefault(getPageList(root));
472
+ } else if (isPageFragment(node)) {
473
+ this.state.pageFragmentLength -= 1;
474
+
475
+ await selectDefault(getPageFragmentList(root));
450
476
  } else {
451
477
  await this.select(parent);
452
478
  stage?.select(parent.id);
479
+
480
+ this.addModifiedNodeId(parent.id);
453
481
  }
454
482
 
455
- this.addModifiedNodeId(parent.id);
483
+ if (!rootItems.length) {
484
+ this.resetModifiedNodeId();
485
+ historyService.reset();
486
+ }
456
487
  }
457
488
 
458
489
  /**
@@ -464,7 +495,7 @@ class Editor extends BaseService {
464
495
 
465
496
  await Promise.all(nodes.map((node) => this.doRemove(node)));
466
497
 
467
- if (!isPage(nodes[0])) {
498
+ if (!(isPage(nodes[0]) || isPageFragment(nodes[0]))) {
468
499
  // 更新历史记录
469
500
  this.pushHistoryState();
470
501
  }
@@ -514,7 +545,7 @@ class Editor extends BaseService {
514
545
  const newLayout = await this.getLayout(newConfig);
515
546
  const layout = await this.getLayout(node);
516
547
  if (Array.isArray(newConfig.items) && newLayout !== layout) {
517
- newConfig = setChilrenLayout(newConfig as MContainer, newLayout);
548
+ newConfig = setChildrenLayout(newConfig as MContainer, newLayout);
518
549
  }
519
550
 
520
551
  parentNodeItems[index] = newConfig;
@@ -531,8 +562,8 @@ class Editor extends BaseService {
531
562
  root: cloneDeep(root),
532
563
  });
533
564
 
534
- if (newConfig.type === NodeType.PAGE) {
535
- this.set('page', newConfig as MPage);
565
+ if (isPage(newConfig) || isPageFragment(newConfig)) {
566
+ this.set('page', newConfig as MPage | MPageFragment);
536
567
  }
537
568
 
538
569
  this.addModifiedNodeId(newConfig.id);
@@ -613,7 +644,7 @@ class Editor extends BaseService {
613
644
  public copyWithRelated(config: MNode | MNode[]): void {
614
645
  const copyNodes: MNode[] = Array.isArray(config) ? config : [config];
615
646
  // 关联的组件也一并复制
616
- depService.getTarget(DepTargetType.RELATED_COMP_WHEN_COPY, DepTargetType.RELATED_COMP_WHEN_COPY)?.removeDep();
647
+ depService.clearByType(DepTargetType.RELATED_COMP_WHEN_COPY);
617
648
  depService.collect(copyNodes, true, DepTargetType.RELATED_COMP_WHEN_COPY);
618
649
  const customTarget = depService.getTarget(
619
650
  DepTargetType.RELATED_COMP_WHEN_COPY,
@@ -18,7 +18,7 @@
18
18
 
19
19
  import { reactive } from 'vue';
20
20
 
21
- import type { MPage } from '@tmagic/schema';
21
+ import type { MPage, MPageFragment } from '@tmagic/schema';
22
22
 
23
23
  import type { HistoryState, StepValue } from '@editor/type';
24
24
  import { UndoRedo } from '@editor/utils/undo-redo';
@@ -41,12 +41,16 @@ class History extends BaseService {
41
41
 
42
42
  public reset() {
43
43
  this.state.pageSteps = {};
44
+ this.resetPage();
45
+ }
46
+
47
+ public resetPage() {
44
48
  this.state.pageId = undefined;
45
49
  this.state.canRedo = false;
46
50
  this.state.canUndo = false;
47
51
  }
48
52
 
49
- public changePage(page: MPage): void {
53
+ public changePage(page: MPage | MPageFragment): void {
50
54
  if (!page) return;
51
55
 
52
56
  this.state.pageId = page.id;