@tmagic/editor 1.3.0-beta.2 → 1.3.0-beta.4

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 (78) hide show
  1. package/dist/style.css +52 -60
  2. package/dist/tmagic-editor.js +1634 -1065
  3. package/dist/tmagic-editor.js.map +1 -1
  4. package/dist/tmagic-editor.umd.cjs +1659 -1087
  5. package/dist/tmagic-editor.umd.cjs.map +1 -1
  6. package/package.json +10 -10
  7. package/src/Editor.vue +70 -65
  8. package/src/components/ContentMenu.vue +5 -1
  9. package/src/components/SearchInput.vue +5 -1
  10. package/src/editorProps.ts +51 -129
  11. package/src/fields/Code.vue +9 -3
  12. package/src/fields/DataSourceFieldSelect.vue +28 -16
  13. package/src/fields/DataSourceFields.vue +95 -7
  14. package/src/fields/DataSourceMethodSelect.vue +23 -14
  15. package/src/fields/DataSourceMocks.vue +237 -0
  16. package/src/fields/EventSelect.vue +18 -11
  17. package/src/index.ts +4 -1
  18. package/src/initService.ts +50 -20
  19. package/src/layouts/CodeEditor.vue +20 -4
  20. package/src/layouts/Framework.vue +3 -1
  21. package/src/layouts/PropsPanel.vue +3 -1
  22. package/src/layouts/sidebar/ComponentListPanel.vue +18 -8
  23. package/src/layouts/sidebar/Sidebar.vue +7 -10
  24. package/src/layouts/sidebar/code-block/CodeBlockList.vue +3 -1
  25. package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +3 -1
  26. package/src/layouts/sidebar/layer/LayerNode.vue +206 -0
  27. package/src/layouts/sidebar/layer/LayerNodeTool.vue +32 -0
  28. package/src/layouts/sidebar/layer/LayerPanel.vue +85 -0
  29. package/src/layouts/sidebar/layer/use-drag.ts +157 -0
  30. package/src/layouts/sidebar/layer/use-filter.ts +70 -0
  31. package/src/layouts/sidebar/layer/use-keybinding.ts +47 -0
  32. package/src/layouts/sidebar/layer/use-node-status.ts +103 -0
  33. package/src/layouts/workspace/Workspace.vue +3 -1
  34. package/src/layouts/workspace/viewer/NodeListMenu.vue +1 -1
  35. package/src/layouts/workspace/viewer/Stage.vue +19 -17
  36. package/src/services/dataSource.ts +10 -0
  37. package/src/services/editor.ts +45 -5
  38. package/src/theme/common/var.scss +2 -2
  39. package/src/theme/framework.scss +2 -0
  40. package/src/theme/layer-panel.scss +63 -63
  41. package/src/theme/props-panel.scss +8 -0
  42. package/src/theme/sidebar.scss +1 -5
  43. package/src/type.ts +60 -0
  44. package/src/utils/data-source/index.ts +11 -0
  45. package/src/utils/dep.ts +26 -11
  46. package/src/utils/editor.ts +14 -3
  47. package/src/utils/props.ts +3 -0
  48. package/types/editorProps.d.ts +61 -107
  49. package/types/fields/Code.vue.d.ts +10 -6
  50. package/types/fields/DataSourceMocks.vue.d.ts +32 -0
  51. package/types/index.d.ts +2 -1
  52. package/types/initService.d.ts +5 -4
  53. package/types/layouts/CodeEditor.vue.d.ts +5 -0
  54. package/types/layouts/Framework.vue.d.ts +2 -12
  55. package/types/layouts/PropsPanel.vue.d.ts +2 -3
  56. package/types/layouts/sidebar/ComponentListPanel.vue.d.ts +2 -7
  57. package/types/layouts/sidebar/Sidebar.vue.d.ts +2 -17
  58. package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +2 -7
  59. package/types/layouts/sidebar/code-block/CodeBlockListPanel.vue.d.ts +2 -9
  60. package/types/layouts/sidebar/{LayerMenu.vue.d.ts → layer/LayerMenu.vue.d.ts} +1 -1
  61. package/types/layouts/sidebar/layer/LayerNode.vue.d.ts +50 -0
  62. package/types/layouts/sidebar/{LayerNode.vue.d.ts → layer/LayerNodeTool.vue.d.ts} +1 -1
  63. package/types/layouts/sidebar/{LayerPanel.vue.d.ts → layer/LayerPanel.vue.d.ts} +2 -8
  64. package/types/layouts/sidebar/layer/use-drag.d.ts +14 -0
  65. package/types/layouts/sidebar/layer/use-filter.d.ts +8 -0
  66. package/types/layouts/sidebar/layer/use-keybinding.d.ts +4 -0
  67. package/types/layouts/sidebar/layer/use-node-status.d.ts +7 -0
  68. package/types/layouts/workspace/Workspace.vue.d.ts +2 -11
  69. package/types/layouts/workspace/viewer/Stage.vue.d.ts +1 -1
  70. package/types/services/dataSource.d.ts +3 -0
  71. package/types/services/editor.d.ts +1 -0
  72. package/types/type.d.ts +60 -0
  73. package/types/utils/dep.d.ts +1 -1
  74. package/types/utils/editor.d.ts +3 -2
  75. package/src/layouts/sidebar/LayerNode.vue +0 -40
  76. package/src/layouts/sidebar/LayerPanel.vue +0 -369
  77. package/types/Editor.vue.d.ts +0 -233
  78. /package/src/layouts/sidebar/{LayerMenu.vue → layer/LayerMenu.vue} +0 -0
@@ -1,4 +1,3 @@
1
- import type { ExtractPropTypes } from 'vue';
2
1
  import { onUnmounted, toRaw, watch } from 'vue';
3
2
  import { cloneDeep } from 'lodash-es';
4
3
 
@@ -6,6 +5,7 @@ import type { EventOption } from '@tmagic/core';
6
5
  import type { CodeBlockContent, DataSourceSchema, Id, MApp, MNode, MPage } from '@tmagic/schema';
7
6
  import { getNodes } from '@tmagic/utils';
8
7
 
8
+ import PropsPanel from './layouts/PropsPanel.vue';
9
9
  import type { Target } from './services/dep';
10
10
  import {
11
11
  createCodeBlockTarget,
@@ -13,7 +13,7 @@ import {
13
13
  createDataSourceMethodTarget,
14
14
  createDataSourceTarget,
15
15
  } from './utils/dep';
16
- import editorProps from './editorProps';
16
+ import { EditorProps } from './editorProps';
17
17
  import { DepTargetType, Services } from './type';
18
18
 
19
19
  export declare type LooseRequired<T> = {
@@ -21,7 +21,7 @@ export declare type LooseRequired<T> = {
21
21
  };
22
22
 
23
23
  export const initServiceState = (
24
- props: Readonly<LooseRequired<Readonly<ExtractPropTypes<typeof editorProps>>>>,
24
+ props: EditorProps,
25
25
  {
26
26
  editorService,
27
27
  historyService,
@@ -38,7 +38,7 @@ export const initServiceState = (
38
38
  watch(
39
39
  () => props.modelValue,
40
40
  (modelValue) => {
41
- editorService.set('root', modelValue);
41
+ editorService.set('root', modelValue || null);
42
42
  },
43
43
  {
44
44
  immediate: true,
@@ -47,7 +47,7 @@ export const initServiceState = (
47
47
 
48
48
  watch(
49
49
  () => props.componentGroupList,
50
- (componentGroupList) => componentListService.setList(componentGroupList),
50
+ (componentGroupList) => componentGroupList && componentListService.setList(componentGroupList),
51
51
  {
52
52
  immediate: true,
53
53
  },
@@ -55,7 +55,7 @@ export const initServiceState = (
55
55
 
56
56
  watch(
57
57
  () => props.datasourceList,
58
- (datasourceList) => dataSourceService.set('datasourceTypeList', datasourceList),
58
+ (datasourceList) => datasourceList && dataSourceService.set('datasourceTypeList', datasourceList),
59
59
  {
60
60
  immediate: true,
61
61
  },
@@ -63,7 +63,7 @@ export const initServiceState = (
63
63
 
64
64
  watch(
65
65
  () => props.propsConfigs,
66
- (configs) => propsService.setPropsConfigs(configs),
66
+ (configs) => configs && propsService.setPropsConfigs(configs),
67
67
  {
68
68
  immediate: true,
69
69
  },
@@ -71,7 +71,7 @@ export const initServiceState = (
71
71
 
72
72
  watch(
73
73
  () => props.propsValues,
74
- (values) => propsService.setPropsValues(values),
74
+ (values) => values && propsService.setPropsValues(values),
75
75
  {
76
76
  immediate: true,
77
77
  },
@@ -83,10 +83,11 @@ export const initServiceState = (
83
83
  const eventsList: Record<string, EventOption[]> = {};
84
84
  const methodsList: Record<string, EventOption[]> = {};
85
85
 
86
- Object.keys(eventMethodList).forEach((type: string) => {
87
- eventsList[type] = eventMethodList[type].events;
88
- methodsList[type] = eventMethodList[type].methods;
89
- });
86
+ eventMethodList &&
87
+ Object.keys(eventMethodList).forEach((type: string) => {
88
+ eventsList[type] = eventMethodList[type].events;
89
+ methodsList[type] = eventMethodList[type].methods;
90
+ });
90
91
 
91
92
  eventsService.setEvents(eventsList);
92
93
  eventsService.setMethods(methodsList);
@@ -99,9 +100,10 @@ export const initServiceState = (
99
100
  watch(
100
101
  () => props.datasourceConfigs,
101
102
  (configs) => {
102
- Object.entries(configs).forEach(([key, value]) => {
103
- dataSourceService.setFormConfig(key, value);
104
- });
103
+ configs &&
104
+ Object.entries(configs).forEach(([key, value]) => {
105
+ dataSourceService.setFormConfig(key, value);
106
+ });
105
107
  },
106
108
  {
107
109
  immediate: true,
@@ -111,8 +113,33 @@ export const initServiceState = (
111
113
  watch(
112
114
  () => props.datasourceValues,
113
115
  (values) => {
114
- Object.entries(values).forEach(([key, value]) => {
115
- dataSourceService.setFormValue(key, value);
116
+ values &&
117
+ Object.entries(values).forEach(([key, value]) => {
118
+ dataSourceService.setFormValue(key, value);
119
+ });
120
+ },
121
+ {
122
+ immediate: true,
123
+ },
124
+ );
125
+
126
+ watch(
127
+ () => props.datasourceEventMethodList,
128
+ (eventMethodList) => {
129
+ const eventsList: Record<string, EventOption[]> = {};
130
+ const methodsList: Record<string, EventOption[]> = {};
131
+
132
+ eventMethodList &&
133
+ Object.keys(eventMethodList).forEach((type: string) => {
134
+ eventsList[type] = eventMethodList[type].events;
135
+ methodsList[type] = eventMethodList[type].methods;
136
+ });
137
+
138
+ Object.entries(eventsList).forEach(([key, value]) => {
139
+ dataSourceService.setFormEvent(key, value);
140
+ });
141
+ Object.entries(methodsList).forEach(([key, value]) => {
142
+ dataSourceService.setFormMethod(key, value);
116
143
  });
117
144
  },
118
145
  {
@@ -148,8 +175,9 @@ export const initServiceState = (
148
175
  };
149
176
 
150
177
  export const initServiceEvents = (
151
- props: Readonly<LooseRequired<Readonly<ExtractPropTypes<typeof editorProps>>>>,
152
- emit: (event: 'props-panel-mounted' | 'update:modelValue', ...args: any[]) => void,
178
+ props: EditorProps,
179
+ emit: ((event: 'props-panel-mounted', instance: InstanceType<typeof PropsPanel>) => void) &
180
+ ((event: 'update:modelValue', value: MApp | null) => void),
153
181
  { editorService, codeBlockService, dataSourceService, depService }: Services,
154
182
  ) => {
155
183
  const getApp = () => {
@@ -247,7 +275,7 @@ export const initServiceEvents = (
247
275
  depService.addTarget(createDataSourceCondTarget(ds.id));
248
276
  };
249
277
 
250
- const rootChangeHandler = async (value: MApp, preValue?: MApp | null) => {
278
+ const rootChangeHandler = async (value: MApp | null, preValue?: MApp | null) => {
251
279
  const nodeId = editorService.get('node')?.id || props.defaultSelected;
252
280
  let node;
253
281
  if (nodeId) {
@@ -268,6 +296,8 @@ export const initServiceEvents = (
268
296
  emit('update:modelValue', value);
269
297
  }
270
298
 
299
+ if (!value) return;
300
+
271
301
  value.codeBlocks = value.codeBlocks || {};
272
302
  value.dataSources = value.dataSources || [];
273
303
 
@@ -27,6 +27,8 @@ import serialize from 'serialize-javascript';
27
27
 
28
28
  import { TMagicButton } from '@tmagic/design';
29
29
 
30
+ import { getConfig } from '@editor/utils/config';
31
+
30
32
  defineOptions({
31
33
  name: 'MEditorCodeEditor',
32
34
  });
@@ -42,6 +44,7 @@ const props = withDefaults(
42
44
  };
43
45
  height?: string;
44
46
  autoSave?: boolean;
47
+ parse?: boolean;
45
48
  }>(),
46
49
  {
47
50
  autoSave: true,
@@ -49,6 +52,7 @@ const props = withDefaults(
49
52
  options: () => ({
50
53
  tabSize: 2,
51
54
  }),
55
+ parse: false,
52
56
  },
53
57
  );
54
58
 
@@ -57,7 +61,7 @@ const emit = defineEmits(['initd', 'save']);
57
61
  const toString = (v: string | any, language: string): string => {
58
62
  let value = '';
59
63
  if (typeof v !== 'string') {
60
- if (props.language.toLocaleLowerCase() === 'json') {
64
+ if (language === 'json') {
61
65
  value = JSON.stringify(v, null, 2);
62
66
  } else {
63
67
  value = serialize(v, {
@@ -74,6 +78,18 @@ const toString = (v: string | any, language: string): string => {
74
78
  return value;
75
79
  };
76
80
 
81
+ const parse = (v: string | any, language: string): any => {
82
+ if (typeof v !== 'string') {
83
+ return v;
84
+ }
85
+
86
+ if (language === 'json') {
87
+ return JSON.parse(v);
88
+ }
89
+
90
+ return getConfig('parseDSL')(v);
91
+ };
92
+
77
93
  let vsEditor: monaco.editor.IStandaloneCodeEditor | null = null;
78
94
  let vsDiffEditor: monaco.editor.IStandaloneDiffEditor | null = null;
79
95
 
@@ -89,7 +105,7 @@ const resizeObserver = new globalThis.ResizeObserver(
89
105
  );
90
106
 
91
107
  const setEditorValue = (v: string | any, m: string | any) => {
92
- values.value = toString(v, props.language);
108
+ values.value = toString(v, props.language.toLocaleLowerCase());
93
109
 
94
110
  if (props.type === 'diff') {
95
111
  const originalModel = monaco.editor.createModel(values.value, 'text/javascript');
@@ -135,7 +151,7 @@ const init = async () => {
135
151
  e.stopPropagation();
136
152
  const newValue = getEditorValue();
137
153
  values.value = newValue;
138
- emit('save', newValue);
154
+ emit('save', props.parse ? parse(newValue, props.language) : newValue);
139
155
  }
140
156
  });
141
157
 
@@ -144,7 +160,7 @@ const init = async () => {
144
160
  const newValue = getEditorValue();
145
161
  if (values.value !== newValue) {
146
162
  values.value = newValue;
147
- emit('save', newValue);
163
+ emit('save', props.parse ? parse(newValue, props.language) : newValue);
148
164
  }
149
165
  });
150
166
  }
@@ -51,12 +51,14 @@ import { computed, inject, ref, watch } from 'vue';
51
51
  import { TMagicScrollbar } from '@tmagic/design';
52
52
 
53
53
  import SplitView from '@editor/components/SplitView.vue';
54
- import type { GetColumnWidth, Services } from '@editor/type';
54
+ import type { FrameworkSlots, GetColumnWidth, Services } from '@editor/type';
55
55
  import { getConfig } from '@editor/utils/config';
56
56
 
57
57
  import AddPageBox from './AddPageBox.vue';
58
58
  import CodeEditor from './CodeEditor.vue';
59
59
 
60
+ defineSlots<FrameworkSlots>();
61
+
60
62
  defineOptions({
61
63
  name: 'MEditorFramework',
62
64
  });
@@ -21,7 +21,9 @@ import { tMagicMessage } from '@tmagic/design';
21
21
  import type { FormState, FormValue } from '@tmagic/form';
22
22
  import { MForm } from '@tmagic/form';
23
23
 
24
- import type { Services } from '@editor/type';
24
+ import type { PropsPanelSlots, Services } from '@editor/type';
25
+
26
+ defineSlots<PropsPanelSlots>();
25
27
 
26
28
  defineOptions({
27
29
  name: 'MEditorPropsPanel',
@@ -39,7 +39,16 @@ import { removeClassNameByClassName } from '@tmagic/utils';
39
39
 
40
40
  import MIcon from '@editor/components/Icon.vue';
41
41
  import SearchInput from '@editor/components/SearchInput.vue';
42
- import type { ComponentGroup, ComponentItem, Services, StageOptions } from '@editor/type';
42
+ import {
43
+ type ComponentGroup,
44
+ type ComponentItem,
45
+ ComponentListPanelSlots,
46
+ DragType,
47
+ type Services,
48
+ type StageOptions,
49
+ } from '@editor/type';
50
+
51
+ defineSlots<ComponentListPanelSlots>();
43
52
 
44
53
  defineOptions({
45
54
  name: 'MEditorComponentListPanel',
@@ -80,16 +89,17 @@ const appendComponent = ({ text, type, data = {} }: ComponentItem): void => {
80
89
  };
81
90
 
82
91
  const dragstartHandler = ({ text, type, data = {} }: ComponentItem, e: DragEvent) => {
83
- if (e.dataTransfer) {
84
- e.dataTransfer.setData(
85
- 'text/json',
86
- serialize({
92
+ e.dataTransfer?.setData(
93
+ 'text/json',
94
+ serialize({
95
+ dragType: DragType.COMPONENT_LIST,
96
+ data: {
87
97
  name: text,
88
98
  type,
89
99
  ...data,
90
- }),
91
- );
92
- }
100
+ },
101
+ }),
102
+ );
93
103
  };
94
104
 
95
105
  const dragendHandler = () => {
@@ -58,16 +58,11 @@
58
58
  </template>
59
59
 
60
60
  <template
61
- #layer-node-content="{ data: nodeData, node }"
61
+ #layer-node-content="{ data: nodeData }"
62
62
  v-if="config.$key === 'layer' || config.slots?.layerNodeContent"
63
63
  >
64
- <slot v-if="config.$key === 'layer'" name="layer-node-content" :data="nodeData" :node="node"></slot>
65
- <component
66
- v-else-if="config.slots?.layerNodeContent"
67
- :is="config.slots.layerNodeContent"
68
- :data="nodeData"
69
- :node="node"
70
- />
64
+ <slot v-if="config.$key === 'layer'" name="layer-node-content" :data="nodeData"></slot>
65
+ <component v-else-if="config.slots?.layerNodeContent" :is="config.slots.layerNodeContent" :data="nodeData" />
71
66
  </template>
72
67
  </component>
73
68
  </div>
@@ -79,13 +74,15 @@ import { computed, ref, watch } from 'vue';
79
74
  import { Coin, EditPen, Goods, List } from '@element-plus/icons-vue';
80
75
 
81
76
  import MIcon from '@editor/components/Icon.vue';
82
- import type { MenuButton, MenuComponent, SideComponent, SideItem } from '@editor/type';
77
+ import type { MenuButton, MenuComponent, SidebarSlots, SideComponent, SideItem } from '@editor/type';
83
78
  import { SideBarData } from '@editor/type';
84
79
 
85
80
  import CodeBlockListPanel from './code-block/CodeBlockListPanel.vue';
86
81
  import DataSourceListPanel from './data-source/DataSourceListPanel.vue';
82
+ import LayerPanel from './layer/LayerPanel.vue';
87
83
  import ComponentListPanel from './ComponentListPanel.vue';
88
- import LayerPanel from './LayerPanel.vue';
84
+
85
+ defineSlots<SidebarSlots>();
89
86
 
90
87
  defineOptions({
91
88
  name: 'MEditorSidebar',
@@ -50,7 +50,9 @@ import type { Id } from '@tmagic/schema';
50
50
  import Icon from '@editor/components/Icon.vue';
51
51
  import AppManageIcon from '@editor/icons/AppManageIcon.vue';
52
52
  import CodeIcon from '@editor/icons/CodeIcon.vue';
53
- import { CodeDeleteErrorType, CodeDslItem, DepTargetType, Services } from '@editor/type';
53
+ import { CodeBlockListSlots, CodeDeleteErrorType, CodeDslItem, DepTargetType, Services } from '@editor/type';
54
+
55
+ defineSlots<CodeBlockListSlots>();
54
56
 
55
57
  defineOptions({
56
58
  name: 'MEditorCodeBlockList',
@@ -37,10 +37,12 @@ import type { Id } from '@tmagic/schema';
37
37
  import CodeBlockEditor from '@editor/components/CodeBlockEditor.vue';
38
38
  import SearchInput from '@editor/components/SearchInput.vue';
39
39
  import { useCodeBlockEdit } from '@editor/hooks/use-code-block-edit';
40
- import type { CodeDeleteErrorType, Services } from '@editor/type';
40
+ import type { CodeBlockListPanelSlots, CodeDeleteErrorType, Services } from '@editor/type';
41
41
 
42
42
  import CodeBlockList from './CodeBlockList.vue';
43
43
 
44
+ defineSlots<CodeBlockListPanelSlots>();
45
+
44
46
  defineOptions({
45
47
  name: 'MEditorCodeBlockListPanel',
46
48
  });
@@ -0,0 +1,206 @@
1
+ <template>
2
+ <div
3
+ v-show="visible"
4
+ class="magic-editor-layer-node"
5
+ ref="nodeEl"
6
+ :draggable="!isPage(data)"
7
+ :data-node-id="data.id"
8
+ :data-is-container="Array.isArray(data.items)"
9
+ @dragstart="handleDragStart"
10
+ @dragleave="handleDragLeave"
11
+ @dragend="handleDragEnd($event, data)"
12
+ >
13
+ <div
14
+ class="layer-node"
15
+ :class="{ selected, expanded }"
16
+ :style="`padding-left: ${indent}px`"
17
+ @contextmenu="contextmenuHandler"
18
+ @mouseenter="highlightHandler()"
19
+ >
20
+ <MIcon
21
+ class="expand-icon"
22
+ :style="hasChilren ? '' : 'color: transparent; cursor: default'"
23
+ :icon="expanded ? ArrowDown : ArrowRight"
24
+ @click="expandHandler"
25
+ ></MIcon>
26
+
27
+ <div class="layer-node-content" @click="nodeClickHandler">
28
+ <slot name="layer-node-content" :data="data">
29
+ <div class="layer-node-label">{{ `${data.name} (${data.id})` }}</div>
30
+ <div class="layer-node-tool">
31
+ <LayerNodeTool :data="data"></LayerNodeTool>
32
+ </div>
33
+ </slot>
34
+ </div>
35
+ </div>
36
+
37
+ <div v-if="hasChilren && expanded" class="magic-editor-layer-node-children">
38
+ <LayerNode
39
+ v-for="item in data.items"
40
+ :data="item"
41
+ :parent="(data as MContainer)"
42
+ :key="item.id"
43
+ :indent="indent + 11"
44
+ :filter-text="filterText"
45
+ :is-ctrl-key-down="isCtrlKeyDown"
46
+ @node-contextmenu="nodeContentmenuHandler"
47
+ >
48
+ <template #layer-node-content="{ data: nodeData }">
49
+ <slot name="layer-node-content" :data="nodeData"> </slot>
50
+ </template>
51
+ </LayerNode>
52
+ </div>
53
+ </div>
54
+ </template>
55
+
56
+ <script lang="ts" setup>
57
+ import { computed, type ComputedRef, inject, nextTick, ref } from 'vue';
58
+ import { ArrowDown, ArrowRight } from '@element-plus/icons-vue';
59
+ import { throttle } from 'lodash-es';
60
+
61
+ import type { Id, MContainer, MNode } from '@tmagic/schema';
62
+ import { isPage } from '@tmagic/utils';
63
+
64
+ import MIcon from '@editor/components/Icon.vue';
65
+ import type { LayerNodeSlots, LayerNodeStatus, Services } from '@editor/type';
66
+
67
+ import LayerNodeTool from './LayerNodeTool.vue';
68
+ import { useDrag } from './use-drag';
69
+ import { updateStatus } from './use-filter';
70
+
71
+ defineSlots<LayerNodeSlots>();
72
+
73
+ defineOptions({
74
+ name: 'MEditorLayerNode',
75
+ });
76
+
77
+ const props = withDefaults(
78
+ defineProps<{
79
+ data: MNode;
80
+ parent?: MContainer;
81
+ indent?: number;
82
+ filterText?: string;
83
+ isCtrlKeyDown?: boolean;
84
+ }>(),
85
+ {
86
+ indent: 0,
87
+ filterText: '',
88
+ isCtrlKeyDown: false,
89
+ },
90
+ );
91
+
92
+ const emit = defineEmits<{
93
+ 'node-contextmenu': [event: MouseEvent, node: MNode];
94
+ }>();
95
+
96
+ const services = inject<Services>('services');
97
+ const nodeStatusMap = inject<ComputedRef<Map<Id, LayerNodeStatus>>>('nodeStatusMap');
98
+ const editorService = services?.editorService;
99
+ const uiService = services?.uiService;
100
+
101
+ const nodeStatus = computed(
102
+ () =>
103
+ nodeStatusMap?.value?.get(props.data.id) || {
104
+ selected: false,
105
+ expand: false,
106
+ visible: false,
107
+ },
108
+ );
109
+
110
+ const expanded = computed(() => nodeStatus.value.expand);
111
+ const selected = computed(() => nodeStatus.value.selected);
112
+ const visible = computed(() => nodeStatus.value.visible);
113
+
114
+ const hasChilren = computed(() => props.data.items?.length > 0);
115
+
116
+ const nodeEl = ref<HTMLDivElement>();
117
+ const { handleDragStart, handleDragEnd, handleDragLeave } = useDrag(services);
118
+
119
+ const expandHandler = () => {
120
+ if (!nodeStatusMap?.value) return;
121
+
122
+ updateStatus(nodeStatusMap.value, props.data.id, {
123
+ expand: !expanded.value,
124
+ });
125
+ };
126
+
127
+ const nodeClickHandler = () => {
128
+ if (!nodeStatusMap?.value) return;
129
+
130
+ if (uiService?.get('uiSelectMode')) {
131
+ document.dispatchEvent(new CustomEvent('ui-select', { detail: props.data }));
132
+ return;
133
+ }
134
+
135
+ if (hasChilren.value && !props.isCtrlKeyDown) {
136
+ updateStatus(nodeStatusMap.value, props.data.id, {
137
+ expand: true,
138
+ });
139
+ }
140
+
141
+ nextTick(() => {
142
+ select(props.data);
143
+ });
144
+ };
145
+
146
+ const contextmenuHandler = (event: MouseEvent) => {
147
+ event.preventDefault();
148
+
149
+ nodeClickHandler();
150
+
151
+ emit('node-contextmenu', event, props.data);
152
+ };
153
+
154
+ const nodeContentmenuHandler = (event: MouseEvent, node: MNode) => {
155
+ emit('node-contextmenu', event, node);
156
+ };
157
+
158
+ // 触发画布单选
159
+ const select = async (data: MNode) => {
160
+ if (!data.id) {
161
+ throw new Error('没有id');
162
+ }
163
+
164
+ if (props.isCtrlKeyDown) {
165
+ multiSelect(data);
166
+ } else {
167
+ await editorService?.select(data);
168
+ editorService?.get('stage')?.select(data.id);
169
+ }
170
+ };
171
+
172
+ const multiSelect = async (data: MNode) => {
173
+ const nodes = editorService?.get('nodes') || [];
174
+
175
+ const newNodes: Id[] = [];
176
+ let isCancel = false;
177
+ nodes.forEach((node) => {
178
+ if (node.id === data.id) {
179
+ isCancel = true;
180
+ return;
181
+ }
182
+
183
+ newNodes.push(node.id);
184
+ });
185
+
186
+ // 只剩一个不能取消选中
187
+ if (!isCancel || newNodes.length === 0) {
188
+ newNodes.push(data.id);
189
+ }
190
+
191
+ await editorService?.multiSelect(newNodes);
192
+ editorService?.get('stage')?.multiSelect(newNodes);
193
+ };
194
+
195
+ const throttleTime = 300;
196
+ // 鼠标在组件树移动触发高亮
197
+ const highlightHandler = throttle(() => {
198
+ highlight();
199
+ }, throttleTime);
200
+
201
+ // 触发画布高亮
202
+ const highlight = () => {
203
+ editorService?.highlight(props.data);
204
+ editorService?.get('stage')?.highlight(props.data.id);
205
+ };
206
+ </script>
@@ -0,0 +1,32 @@
1
+ <template>
2
+ <template v-if="data.type !== 'page'">
3
+ <MIcon v-if="data.visible === false" :icon="Hide" @click.stop="setNodeVisible(true)" title="点击显示"></MIcon>
4
+ <MIcon v-else :icon="View" @click.stop="setNodeVisible(false)" class="node-lock" title="点击隐藏"></MIcon>
5
+ </template>
6
+ </template>
7
+
8
+ <script setup lang="ts">
9
+ import { inject } from 'vue';
10
+ import { Hide, View } from '@element-plus/icons-vue';
11
+
12
+ import type { MNode } from '@tmagic/schema';
13
+
14
+ import MIcon from '@editor/components/Icon.vue';
15
+ import { Services } from '@editor/type';
16
+
17
+ const props = defineProps<{
18
+ data: MNode;
19
+ }>();
20
+
21
+ const services = inject<Services>('services');
22
+ const editorService = services?.editorService;
23
+
24
+ const setNodeVisible = (visible: boolean) => {
25
+ if (!editorService) return;
26
+
27
+ editorService.update({
28
+ id: props.data.id,
29
+ visible,
30
+ });
31
+ };
32
+ </script>