@tmagic/editor 1.0.0-beta.6 → 1.0.0-beta.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,10 +2,14 @@ import type { Translator } from 'element-plus';
2
2
  import type { Nullable } from 'element-plus/es/utils';
3
3
  import type { default } from 'element-plus/es/components/tree/src/model/node';
4
4
  import type { TreeOptionProps, TreeKey, LoadFunction, FilterNodeMethodFunction, TreeNodeData, FakeNode, TreeNodeLoadedDefaultProps, TreeData } from 'element-plus/es/components/tree/src/tree.type';
5
+ import type { DebouncedFunc } from 'lodash';
5
6
  import type { DefineComponent, ComputedRef, ComponentInternalInstance, ExtractPropTypes, PropType, Component, ComputedOptions, MethodOptions, VNodeProps, AllowedComponentProps, ComponentCustomProps, Slot, ComponentPublicInstance, ComponentOptionsBase, ComponentOptionsMixin, DebuggerEvent, nextTick, WatchOptions, WatchStopHandle, ShallowUnwrapRef, ComponentCustomProperties, EmitsOptions, Ref } from 'vue';
6
7
  import type { MNode } from '@tmagic/schema';
7
8
  declare const _default: DefineComponent<{}, {
8
9
  clickHandler(data: MNode): void;
10
+ highlightHandler: DebouncedFunc<(data: MNode) => void>;
11
+ toogleClickFlag: () => void;
12
+ canHighlight: ComputedRef<boolean>;
9
13
  menuStyle: Ref<{
10
14
  position: string;
11
15
  left: string;
@@ -16,10 +20,12 @@ declare const _default: DefineComponent<{}, {
16
20
  filterText: Ref<string>;
17
21
  filterNode: (value: string, data: MNode) => boolean;
18
22
  filterTextChangeHandler(val: string): void;
19
- values: ComputedRef<MNode[]>;
20
- loadItems: (node: any, resolve: Function) => any;
21
23
  allowDrop: (draggingNode: any, dropNode: any, type: string) => boolean;
22
24
  handleDragEnd(): void;
25
+ values: ComputedRef<MNode[]>;
26
+ loadItems: (node: any, resolve: Function) => any;
27
+ highlightNode: Ref<MNode | undefined>;
28
+ clickNode: Ref<MNode | undefined>;
23
29
  tree: Ref<({
24
30
  $: ComponentInternalInstance;
25
31
  $data: {};
@@ -166,7 +166,7 @@ declare const _default: DefineComponent<{
166
166
  stageContainer: Ref<HTMLDivElement | undefined>;
167
167
  stageRect: ComputedRef<StageRect | undefined>;
168
168
  zoom: ComputedRef<number | undefined>;
169
- }, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, ("select" | "update" | "sort")[], "sort" | "select" | "update", VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly<ExtractPropTypes<{
169
+ }, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, ("select" | "update" | "sort" | "highlight")[], "sort" | "select" | "update" | "highlight", VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly<ExtractPropTypes<{
170
170
  render: {
171
171
  type: PropType<() => HTMLDivElement>;
172
172
  };
@@ -185,6 +185,7 @@ declare const _default: DefineComponent<{
185
185
  onSelect?: ((...args: any[]) => any) | undefined;
186
186
  onUpdate?: ((...args: any[]) => any) | undefined;
187
187
  onSort?: ((...args: any[]) => any) | undefined;
188
+ onHighlight?: ((...args: any[]) => any) | undefined;
188
189
  }, {
189
190
  canSelect: (el: HTMLElement) => boolean | Promise<boolean>;
190
191
  moveableOptions: MoveableOptions | ((core?: StageCore | undefined) => MoveableOptions);
@@ -18,6 +18,7 @@ declare const _default: DefineComponent<{
18
18
  selectHandler(el: HTMLElement): void;
19
19
  updateNodeHandler(node: MComponent | MContainer | MPage): void;
20
20
  sortNodeHandler(ev: SortEventData): void;
21
+ highlightHandler(el: HTMLElement): void;
21
22
  }, unknown, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, Record<string, any>, string, VNodeProps & AllowedComponentProps & ComponentCustomProps, Readonly<ExtractPropTypes<{
22
23
  runtimeUrl: StringConstructor;
23
24
  render: {
@@ -9,7 +9,7 @@ declare class Editor extends BaseService {
9
9
  constructor();
10
10
  /**
11
11
  * 设置当前指点节点配置
12
- * @param name 'root' | 'page' | 'parent' | 'node'
12
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode'
13
13
  * @param value MNode
14
14
  * @returns MNode
15
15
  */
@@ -43,11 +43,17 @@ declare class Editor extends BaseService {
43
43
  */
44
44
  getLayout(node: MNode): Promise<Layout>;
45
45
  /**
46
- * 选中指点节点(将指点节点设置成当前选中状态)
47
- * @param config 指点节点配置或者ID
46
+ * 选中指定节点(将指定节点设置成当前选中状态)
47
+ * @param config 指定节点配置或者ID
48
48
  * @returns 当前选中的节点配置
49
49
  */
50
- select(config: MNode | Id): Promise<MNode>;
50
+ select(config: MNode | Id): Promise<MNode> | never;
51
+ /**
52
+ * 高亮指定节点
53
+ * @param config 指定节点配置或者ID
54
+ * @returns 当前高亮的节点配置
55
+ */
56
+ highlight(config: MNode | Id): void;
51
57
  /**
52
58
  * 向指点容器添加组件节点
53
59
  * @param addConfig 将要添加的组件节点配置
@@ -116,6 +122,7 @@ declare class Editor extends BaseService {
116
122
  private pushHistoryState;
117
123
  private changeHistoryState;
118
124
  private toggleFixedPosition;
125
+ private selectedConfigExceptionHandler;
119
126
  }
120
127
  export declare type EditorService = Editor;
121
128
  declare const _default: Editor;
@@ -26,6 +26,7 @@ export interface StoreState {
26
26
  page: MPage | null;
27
27
  parent: MContainer | null;
28
28
  node: MNode | null;
29
+ highlightNode: MNode | null;
29
30
  stage: StageCore | null;
30
31
  modifiedNodeIds: Map<Id, Id>;
31
32
  }
@@ -1,4 +1,4 @@
1
- import { MApp, MContainer, MNode, MPage } from '@tmagic/schema';
1
+ import type { MApp, MContainer, MNode, MPage } from '@tmagic/schema';
2
2
  import { Layout } from '../type';
3
3
  export declare const COPY_STORAGE_KEY = "$MagicEditorCopyData";
4
4
  export declare const generateId: (type: string | number) => string;
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.0.0-beta.6",
2
+ "version": "1.0.0-beta.9",
3
3
  "name": "@tmagic/editor",
4
4
  "sideEffects": false,
5
5
  "main": "dist/tmagic-editor.umd.js",
@@ -28,13 +28,13 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "@element-plus/icons": "0.0.11",
31
- "@tmagic/core": "^1.0.0-beta.6",
32
- "@tmagic/form": "^1.0.0-beta.6",
33
- "@tmagic/schema": "^1.0.0-beta.6",
34
- "@tmagic/stage": "^1.0.0-beta.6",
35
- "@tmagic/utils": "^1.0.0-beta.6",
31
+ "@tmagic/core": "^1.0.0-beta.9",
32
+ "@tmagic/form": "^1.0.0-beta.8",
33
+ "@tmagic/schema": "^1.0.0-beta.9",
34
+ "@tmagic/stage": "^1.0.0-beta.9",
35
+ "@tmagic/utils": "^1.0.0-beta.8",
36
36
  "color": "^3.1.3",
37
- "element-plus": "^2.0.2",
37
+ "element-plus": "^2.1.7",
38
38
  "events": "^3.3.0",
39
39
  "lodash-es": "^4.17.21",
40
40
  "monaco-editor": "^0.32.1",
@@ -55,5 +55,6 @@
55
55
  "vite": "^2.3.7",
56
56
  "vite-plugin-dts": "^0.9.6",
57
57
  "vue-tsc": "^0.0.24"
58
- }
58
+ },
59
+ "gitHead": "6314078c100ddcf513eb5581b38e2292f9b93eb9"
59
60
  }
@@ -41,6 +41,8 @@
41
41
  import { computed, defineComponent, inject, PropType } from 'vue';
42
42
  import { ArrowDown, Back, Delete, Grid, Right, ScaleToOriginal, ZoomIn, ZoomOut } from '@element-plus/icons';
43
43
 
44
+ import { NodeType } from '@tmagic/schema';
45
+
44
46
  import MIcon from '@editor/components/Icon.vue';
45
47
  import type { MenuButton, MenuComponent, MenuItem, Services } from '@editor/type';
46
48
 
@@ -87,7 +89,7 @@ export default defineComponent({
87
89
  type: 'button',
88
90
  icon: Delete,
89
91
  tooltip: '刪除',
90
- disabled: () => services?.editorService.get('node')?.type === 'page',
92
+ disabled: () => services?.editorService.get('node')?.type === NodeType.PAGE,
91
93
  handler: () => services?.editorService.remove(services?.editorService.get('node')),
92
94
  };
93
95
  case 'undo':
@@ -15,6 +15,8 @@
15
15
  import { defineComponent, inject, toRaw } from 'vue';
16
16
  import { Plus } from '@element-plus/icons';
17
17
 
18
+ import { NodeType } from '@tmagic/schema';
19
+
18
20
  import { Services } from '@editor/type';
19
21
  import { generatePageNameByApp } from '@editor/utils';
20
22
 
@@ -31,7 +33,7 @@ export default defineComponent({
31
33
  if (!editorService) return;
32
34
 
33
35
  editorService.add({
34
- type: 'page',
36
+ type: NodeType.PAGE,
35
37
  name: generatePageNameByApp(toRaw(editorService.get('root'))),
36
38
  });
37
39
  },
@@ -1,6 +1,7 @@
1
1
  <template>
2
2
  <m-form
3
3
  class="m-editor-props-panel"
4
+ popper-class="m-editor-props-panel-popper"
4
5
  ref="configForm"
5
6
  size="small"
6
7
  :init-values="values"
@@ -29,11 +29,20 @@
29
29
  empty-text="页面空荡荡的"
30
30
  >
31
31
  <template #default="{ node, data }">
32
- <slot name="layer-node-content" :node="node" :data="data">
33
- <span>
34
- {{ `${data.name} (${data.id})` }}
35
- </span>
36
- </slot>
32
+ <div
33
+ :id="data.id"
34
+ class="cus-tree-node"
35
+ @mousedown="toogleClickFlag"
36
+ @mouseup="toogleClickFlag"
37
+ @mouseenter="highlightHandler(data)"
38
+ :class="{ 'cus-tree-node-hover': canHighlight && data.id === highlightNode?.id }"
39
+ >
40
+ <slot name="layer-node-content" :node="node" :data="data">
41
+ <span>
42
+ {{ `${data.name} (${data.id})` }}
43
+ </span>
44
+ </slot>
45
+ </div>
37
46
  </template>
38
47
  </el-tree>
39
48
 
@@ -46,14 +55,18 @@
46
55
  <script lang="ts">
47
56
  import { computed, defineComponent, inject, onMounted, Ref, ref, watchEffect } from 'vue';
48
57
  import type { ElTree } from 'element-plus';
58
+ import { throttle } from 'lodash-es';
49
59
 
50
60
  import type { MNode, MPage } from '@tmagic/schema';
61
+ import { NodeType } from '@tmagic/schema';
51
62
 
52
63
  import type { EditorService } from '@editor/services/editor';
53
64
  import type { Services } from '@editor/type';
54
65
 
55
66
  import LayerMenu from './LayerMenu.vue';
56
67
 
68
+ const throttleTime = 150;
69
+
57
70
  const select = (data: MNode, editorService?: EditorService) => {
58
71
  if (!data.id) {
59
72
  throw new Error('没有id');
@@ -62,6 +75,13 @@ const select = (data: MNode, editorService?: EditorService) => {
62
75
  editorService?.select(data);
63
76
  };
64
77
 
78
+ const highlight = (data: MNode, editorService?: EditorService) => {
79
+ if (!data?.id) {
80
+ throw new Error('没有id');
81
+ }
82
+ editorService?.highlight(data);
83
+ };
84
+
65
85
  const useDrop = (tree: Ref<InstanceType<typeof ElTree> | undefined>, editorService?: EditorService) => ({
66
86
  allowDrop: (draggingNode: any, dropNode: any, type: string): boolean => {
67
87
  const { data } = dropNode || {};
@@ -69,8 +89,8 @@ const useDrop = (tree: Ref<InstanceType<typeof ElTree> | undefined>, editorServi
69
89
 
70
90
  const { type: ingType } = ingData;
71
91
 
72
- if (ingType !== 'page' && data.type === 'page') return false;
73
- if (ingType === 'page' && data.type !== 'page') return false;
92
+ if (ingType !== NodeType.PAGE && data.type === NodeType.PAGE) return false;
93
+ if (ingType === NodeType.PAGE && data.type !== NodeType.PAGE) return false;
74
94
  if (!data || !data.type) return false;
75
95
  if (['prev', 'next'].includes(type)) return true;
76
96
  if (data.items || data.type === 'container') return true;
@@ -87,19 +107,22 @@ const useDrop = (tree: Ref<InstanceType<typeof ElTree> | undefined>, editorServi
87
107
  });
88
108
 
89
109
  const useStatus = (tree: Ref<InstanceType<typeof ElTree> | undefined>, editorService?: EditorService) => {
110
+ const highlightNode = ref<MNode>();
111
+ const node = ref<MNode>();
90
112
  const page = computed(() => editorService?.get('page'));
91
113
 
92
114
  watchEffect(() => {
93
115
  if (!tree.value) return;
94
-
95
- const node = editorService?.get('node');
96
- node && tree.value.setCurrentKey(node.id, true);
116
+ node.value = editorService?.get('node');
117
+ node.value && tree.value.setCurrentKey(node.value.id, true);
97
118
 
98
119
  const parent = editorService?.get('parent');
99
120
  if (!parent?.id) return;
100
121
 
101
122
  const treeNode = tree.value.getNode(parent.id);
102
123
  treeNode?.updateChildren();
124
+
125
+ highlightNode.value = editorService?.get('highlightNode');
103
126
  });
104
127
 
105
128
  return {
@@ -114,6 +137,9 @@ const useStatus = (tree: Ref<InstanceType<typeof ElTree> | undefined>, editorSer
114
137
  }
115
138
  resolve([]);
116
139
  },
140
+
141
+ highlightNode,
142
+ clickNode: node,
117
143
  };
118
144
  };
119
145
 
@@ -188,11 +214,25 @@ export default defineComponent({
188
214
  setup() {
189
215
  const services = inject<Services>('services');
190
216
  const tree = ref<InstanceType<typeof ElTree>>();
217
+ const clicked = ref(false);
191
218
  const editorService = services?.editorService;
219
+ const highlightHandler = throttle((data: MNode) => {
220
+ highlight(data, editorService);
221
+ }, throttleTime);
222
+
223
+ const toogleClickFlag = () => {
224
+ clicked.value = !clicked.value;
225
+ };
226
+
227
+ const statusData = useStatus(tree, editorService);
228
+ const canHighlight = computed(
229
+ () => statusData.highlightNode.value?.id !== statusData.clickNode.value?.id && !clicked.value,
230
+ );
231
+
192
232
  return {
193
233
  tree,
234
+ ...statusData,
194
235
  ...useDrop(tree, editorService),
195
- ...useStatus(tree, editorService),
196
236
  ...useFilter(tree),
197
237
  ...useContentMenu(editorService),
198
238
 
@@ -201,8 +241,12 @@ export default defineComponent({
201
241
  document.dispatchEvent(new CustomEvent('ui-select', { detail: data }));
202
242
  return;
203
243
  }
244
+ tree.value?.setCurrentKey(data.id);
204
245
  select(data, editorService);
205
246
  },
247
+ highlightHandler,
248
+ toogleClickFlag,
249
+ canHighlight,
206
250
  };
207
251
  },
208
252
  });
@@ -11,9 +11,13 @@
11
11
  :class="{ active: page?.id === item.id }"
12
12
  @click="switchPage(item)"
13
13
  >
14
- <slot name="page-bar-title" :page="item">
15
- <span>{{ item.name }}</span>
16
- </slot>
14
+ <div class="m-editor-page-bar-title">
15
+ <slot name="page-bar-title" :page="item">
16
+ <el-tooltip effect="dark" placement="top-start" :content="item.name">
17
+ <span>{{ item.name }}</span>
18
+ </el-tooltip>
19
+ </slot>
20
+ </div>
17
21
 
18
22
  <el-popover placement="top" :width="160" trigger="hover">
19
23
  <div>
@@ -38,6 +42,7 @@ import { computed, defineComponent, inject, toRaw } from 'vue';
38
42
  import { CaretBottom, Plus } from '@element-plus/icons';
39
43
 
40
44
  import type { MPage } from '@tmagic/schema';
45
+ import { NodeType } from '@tmagic/schema';
41
46
 
42
47
  import type { Services } from '@editor/type';
43
48
  import { generatePageNameByApp } from '@editor/utils/editor';
@@ -60,7 +65,7 @@ export default defineComponent({
60
65
  addPage() {
61
66
  if (!editorService) return;
62
67
  const pageConfig = {
63
- type: 'page',
68
+ type: NodeType.PAGE,
64
69
  name: generatePageNameByApp(toRaw(editorService.get('root'))),
65
70
  };
66
71
  editorService.add(pageConfig);
@@ -110,7 +110,7 @@ export default defineComponent({
110
110
  },
111
111
  },
112
112
 
113
- emits: ['select', 'update', 'sort'],
113
+ emits: ['select', 'update', 'sort', 'highlight'],
114
114
 
115
115
  setup(props, { emit }) {
116
116
  const services = inject<Services>('services');
@@ -124,6 +124,7 @@ export default defineComponent({
124
124
  const page = computed(() => services?.editorService.get<MPage>('page'));
125
125
  const zoom = computed(() => services?.uiService.get<number>('zoom'));
126
126
  const node = computed(() => services?.editorService.get<MNode>('node'));
127
+ const highlightNode = computed(() => services?.editorService.get<MNode>('highlightNode'));
127
128
 
128
129
  let stage: StageCore | null = null;
129
130
  let runtime: Runtime | null = null;
@@ -155,7 +156,13 @@ export default defineComponent({
155
156
 
156
157
  stage?.mount(stageContainer.value);
157
158
 
158
- stage?.on('select', (el: HTMLElement) => emit('select', el));
159
+ stage?.on('select', (el: HTMLElement) => {
160
+ emit('select', el);
161
+ });
162
+
163
+ stage?.on('highlight', (el: HTMLElement) => {
164
+ emit('highlight', el);
165
+ });
159
166
 
160
167
  stage?.on('update', (ev: UpdateEventData) => {
161
168
  emit('update', { id: ev.el.id, style: ev.style });
@@ -202,6 +209,15 @@ export default defineComponent({
202
209
  },
203
210
  );
204
211
 
212
+ watch(
213
+ () => highlightNode.value?.id,
214
+ (id) => {
215
+ nextTick(() => {
216
+ id && stage?.highlight(id);
217
+ });
218
+ },
219
+ );
220
+
205
221
  const resizeObserver = new ResizeObserver((entries) => {
206
222
  for (const { contentRect } of entries) {
207
223
  services?.uiService.set('stageContainerRect', {
@@ -24,6 +24,7 @@
24
24
  <script lang="ts">
25
25
  import { computed, defineComponent, inject, onMounted, ref, watch } from 'vue';
26
26
 
27
+ import { NodeType } from '@tmagic/schema';
27
28
  import type StageCore from '@tmagic/stage';
28
29
 
29
30
  import { LayerOffset, Layout, Services } from '@editor/type';
@@ -53,7 +54,8 @@ export default defineComponent({
53
54
  if (!parent.value || !editorService) return (canCenter.value = false);
54
55
  const layout = await editorService.getLayout(parent.value);
55
56
  canCenter.value =
56
- [Layout.ABSOLUTE, Layout.FIXED].includes(layout) && !['app', 'page', 'pop'].includes(`${node.value?.type}`);
57
+ [Layout.ABSOLUTE, Layout.FIXED].includes(layout) &&
58
+ ![NodeType.ROOT, NodeType.PAGE, 'pop'].includes(`${node.value?.type}`);
57
59
  },
58
60
  { immediate: true },
59
61
  );
@@ -62,8 +64,8 @@ export default defineComponent({
62
64
  menu,
63
65
  canPaste,
64
66
 
65
- canDelete: computed(() => node.value?.type !== 'page'),
66
- canMoveZPos: computed(() => node.value?.type !== 'page'),
67
+ canDelete: computed(() => node.value?.type !== NodeType.PAGE),
68
+ canMoveZPos: computed(() => node.value?.type !== NodeType.PAGE),
67
69
  canCenter,
68
70
 
69
71
  center() {
@@ -7,6 +7,7 @@
7
7
  :moveable-options="moveableOptions"
8
8
  :can-select="canSelect"
9
9
  @select="selectHandler"
10
+ @highlight="highlightHandler"
10
11
  @update="updateNodeHandler"
11
12
  @sort="sortNodeHandler"
12
13
  ></magic-stage>
@@ -73,6 +74,10 @@ export default defineComponent({
73
74
  sortNodeHandler(ev: SortEventData) {
74
75
  services?.editorService.sort(ev.src, ev.dist);
75
76
  },
77
+
78
+ highlightHandler(el: HTMLElement) {
79
+ services?.editorService.highlight(el.id);
80
+ },
76
81
  };
77
82
  },
78
83
  });
@@ -21,6 +21,7 @@ import { cloneDeep, mergeWith } from 'lodash-es';
21
21
  import serialize from 'serialize-javascript';
22
22
 
23
23
  import type { Id, MApp, MComponent, MContainer, MNode, MPage } from '@tmagic/schema';
24
+ import { NodeType } from '@tmagic/schema';
24
25
  import type StageCore from '@tmagic/stage';
25
26
  import { getNodePath, isPop } from '@tmagic/utils';
26
27
 
@@ -51,6 +52,7 @@ class Editor extends BaseService {
51
52
  parent: null,
52
53
  node: null,
53
54
  stage: null,
55
+ highlightNode: null,
54
56
  modifiedNodeIds: new Map(),
55
57
  });
56
58
 
@@ -68,12 +70,13 @@ class Editor extends BaseService {
68
70
  'moveLayer',
69
71
  'undo',
70
72
  'redo',
73
+ 'highlight',
71
74
  ]);
72
75
  }
73
76
 
74
77
  /**
75
78
  * 设置当前指点节点配置
76
- * @param name 'root' | 'page' | 'parent' | 'node'
79
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode'
77
80
  * @param value MNode
78
81
  * @returns MNode
79
82
  */
@@ -119,7 +122,7 @@ class Editor extends BaseService {
119
122
  info.parent = path[path.length - 2] as MContainer;
120
123
 
121
124
  path.forEach((item) => {
122
- if (item.type === 'page') {
125
+ if (item.type === NodeType.PAGE) {
123
126
  info.page = item as MPage;
124
127
  return;
125
128
  }
@@ -166,28 +169,12 @@ class Editor extends BaseService {
166
169
  }
167
170
 
168
171
  /**
169
- * 选中指点节点(将指点节点设置成当前选中状态)
170
- * @param config 指点节点配置或者ID
172
+ * 选中指定节点(将指定节点设置成当前选中状态)
173
+ * @param config 指定节点配置或者ID
171
174
  * @returns 当前选中的节点配置
172
175
  */
173
- public async select(config: MNode | Id): Promise<MNode> {
174
- let id: Id;
175
- if (typeof config === 'string' || typeof config === 'number') {
176
- id = config;
177
- } else {
178
- id = config.id;
179
- }
180
- if (!id) {
181
- throw new Error('没有ID,无法选中');
182
- }
183
-
184
- const { node, parent, page } = this.getNodeInfo(id);
185
- if (!node) throw new Error('获取不到组件信息');
186
-
187
- if (node.id === this.state.root?.id) {
188
- throw new Error('不能选根节点');
189
- }
190
-
176
+ public async select(config: MNode | Id): Promise<MNode> | never {
177
+ const { node, page, parent } = this.selectedConfigExceptionHandler(config);
191
178
  this.set('node', node);
192
179
  this.set('page', page || null);
193
180
  this.set('parent', parent || null);
@@ -197,8 +184,19 @@ class Editor extends BaseService {
197
184
  } else {
198
185
  historyService.empty();
199
186
  }
187
+ return node!;
188
+ }
200
189
 
201
- return node;
190
+ /**
191
+ * 高亮指定节点
192
+ * @param config 指定节点配置或者ID
193
+ * @returns 当前高亮的节点配置
194
+ */
195
+ public highlight(config: MNode | Id): void {
196
+ const { node } = this.selectedConfigExceptionHandler(config);
197
+ const currentHighligtNode = this.get('highlightNode');
198
+ if (currentHighligtNode === node) return;
199
+ this.set('highlightNode', node);
202
200
  }
203
201
 
204
202
  /**
@@ -212,7 +210,7 @@ class Editor extends BaseService {
212
210
 
213
211
  let parentNode: MNode | undefined;
214
212
 
215
- if (type === 'page') {
213
+ if (type === NodeType.PAGE) {
216
214
  parentNode = this.get<MApp>('root');
217
215
  // 由于支持中间件扩展,在parent参数为undefined时,parent会变成next函数
218
216
  } else if (parent && typeof parent !== 'function') {
@@ -228,7 +226,7 @@ class Editor extends BaseService {
228
226
  const layout = await this.getLayout(parentNode);
229
227
  const newNode = initPosition({ ...toRaw(await propsService.getPropsValue(type)), ...config }, layout);
230
228
 
231
- if ((parentNode?.type === 'app' || curNode.type === 'app') && newNode.type !== 'page') {
229
+ if ((parentNode?.type === NodeType.ROOT || curNode.type === NodeType.ROOT) && newNode.type !== NodeType.PAGE) {
232
230
  throw new Error('app下不能添加组件');
233
231
  }
234
232
 
@@ -239,7 +237,9 @@ class Editor extends BaseService {
239
237
  await this.select(newNode);
240
238
 
241
239
  this.addModifiedNodeId(newNode.id);
242
- this.pushHistoryState();
240
+ if (type !== NodeType.PAGE) {
241
+ this.pushHistoryState();
242
+ }
243
243
 
244
244
  return newNode;
245
245
  }
@@ -267,7 +267,7 @@ class Editor extends BaseService {
267
267
  parent.items?.splice(index, 1);
268
268
  this.get<StageCore>('stage')?.remove({ id: node.id, root: this.get('root') });
269
269
 
270
- if (node.type === 'page') {
270
+ if (node.type === NodeType.PAGE) {
271
271
  await this.select(root.items[0] || root);
272
272
  } else {
273
273
  await this.select(parent);
@@ -295,7 +295,7 @@ class Editor extends BaseService {
295
295
 
296
296
  let newConfig = await this.toggleFixedPosition(toRaw(config), node, this.get<MApp>('root'));
297
297
 
298
- newConfig = mergeWith(node, newConfig, (objValue, srcValue) => {
298
+ newConfig = mergeWith(cloneDeep(node), newConfig, (objValue, srcValue) => {
299
299
  if (Array.isArray(srcValue)) {
300
300
  return srcValue;
301
301
  }
@@ -303,7 +303,7 @@ class Editor extends BaseService {
303
303
 
304
304
  if (!newConfig.type) throw new Error('配置缺少type值');
305
305
 
306
- if (newConfig.type === 'app') {
306
+ if (newConfig.type === NodeType.ROOT) {
307
307
  this.set('root', newConfig);
308
308
  return newConfig;
309
309
  }
@@ -330,7 +330,7 @@ class Editor extends BaseService {
330
330
 
331
331
  this.get<StageCore>('stage')?.update({ config: cloneDeep(newConfig), root: this.get('root') });
332
332
 
333
- if (newConfig.type === 'page') {
333
+ if (newConfig.type === NodeType.PAGE) {
334
334
  this.set('page', newConfig);
335
335
  }
336
336
 
@@ -359,6 +359,8 @@ class Editor extends BaseService {
359
359
  await this.update(parent);
360
360
  await this.select(node);
361
361
 
362
+ this.get<StageCore>('stage')?.update({ config: cloneDeep(node), root: this.get('root') });
363
+
362
364
  this.addModifiedNodeId(parent.id);
363
365
  this.pushHistoryState();
364
366
  }
@@ -522,6 +524,30 @@ class Editor extends BaseService {
522
524
 
523
525
  return newConfig;
524
526
  }
527
+
528
+ private selectedConfigExceptionHandler(config: MNode | Id): EditorNodeInfo {
529
+ let id: Id;
530
+ if (typeof config === 'string' || typeof config === 'number') {
531
+ id = config;
532
+ } else {
533
+ id = config.id;
534
+ }
535
+ if (!id) {
536
+ throw new Error('没有ID,无法选中');
537
+ }
538
+
539
+ const { node, parent, page } = this.getNodeInfo(id);
540
+ if (!node) throw new Error('获取不到组件信息');
541
+
542
+ if (node.id === this.state.root?.id) {
543
+ throw new Error('不能选根节点');
544
+ }
545
+ return {
546
+ node,
547
+ parent,
548
+ page,
549
+ };
550
+ }
525
551
  }
526
552
 
527
553
  export type EditorService = Editor;
@@ -5,9 +5,9 @@ $--border-color: #d9dbdd;
5
5
 
6
6
  $--nav-height: 35px;
7
7
  $--nav-color: #070303;
8
- $--nav--background-color: #f8fbff;
8
+ $--nav--background-color: #ffffff;
9
9
 
10
10
  $--sidebar-heder-background-color: $--theme-color;
11
- $--sidebar-content-background-color: #f8fbff;
11
+ $--sidebar-content-background-color: #ffffff;
12
12
 
13
13
  $--page-bar-height: 32px;