@tmagic/editor 1.2.0-beta.2 → 1.2.0-beta.21
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.
- package/dist/style.css +41 -28
- package/dist/{tmagic-editor.mjs → tmagic-editor.js} +1693 -1272
- package/dist/tmagic-editor.js.map +1 -0
- package/dist/{tmagic-editor.umd.js → tmagic-editor.umd.cjs} +1700 -1274
- package/dist/tmagic-editor.umd.cjs.map +1 -0
- package/package.json +16 -14
- package/src/Editor.vue +54 -37
- package/src/components/CodeDraftEditor.vue +145 -0
- package/src/components/ContentMenu.vue +4 -4
- package/src/components/FunctionEditor.vue +145 -0
- package/src/components/Icon.vue +7 -5
- package/src/components/Layout.vue +149 -0
- package/src/components/ScrollBar.vue +1 -1
- package/src/components/ScrollViewer.vue +10 -1
- package/src/components/ToolButton.vue +30 -15
- package/src/fields/Code.vue +1 -1
- package/src/fields/CodeLink.vue +1 -1
- package/src/fields/CodeSelect.vue +89 -110
- package/src/fields/UISelect.vue +9 -8
- package/src/index.ts +1 -0
- package/src/layouts/AddPageBox.vue +3 -2
- package/src/layouts/CodeEditor.vue +2 -3
- package/src/layouts/Framework.vue +62 -40
- package/src/layouts/NavMenu.vue +1 -1
- package/src/layouts/PropsPanel.vue +11 -13
- package/src/layouts/Resizer.vue +1 -1
- package/src/layouts/sidebar/ComponentListPanel.vue +15 -13
- package/src/layouts/sidebar/LayerMenu.vue +9 -7
- package/src/layouts/sidebar/LayerPanel.vue +277 -177
- package/src/layouts/sidebar/Sidebar.vue +113 -27
- package/src/layouts/sidebar/code-block/CodeBlockEditor.vue +34 -85
- package/src/layouts/sidebar/code-block/CodeBlockList.vue +55 -89
- package/src/layouts/workspace/Breadcrumb.vue +32 -0
- package/src/layouts/workspace/PageBar.vue +12 -11
- package/src/layouts/workspace/PageBarScrollContainer.vue +1 -1
- package/src/layouts/workspace/Stage.vue +26 -9
- package/src/layouts/workspace/ViewerMenu.vue +11 -9
- package/src/layouts/workspace/Workspace.vue +10 -3
- package/src/services/BaseService.ts +6 -1
- package/src/services/codeBlock.ts +124 -125
- package/src/services/componentList.ts +6 -1
- package/src/services/editor.ts +43 -19
- package/src/services/events.ts +6 -1
- package/src/services/history.ts +7 -16
- package/src/services/props.ts +7 -2
- package/src/services/storage.ts +1 -0
- package/src/services/ui.ts +12 -0
- package/src/theme/breadcrumb.scss +6 -0
- package/src/theme/code-block.scss +30 -21
- package/src/theme/component-list-panel.scss +3 -7
- package/src/theme/layer-panel.scss +7 -2
- package/src/theme/theme.scss +1 -0
- package/src/type.ts +76 -50
- package/src/utils/editor.ts +9 -1
- package/src/utils/index.ts +1 -0
- package/src/utils/props.ts +2 -2
- package/src/utils/scroll-viewer.ts +18 -2
- package/src/utils/stage.ts +9 -2
- package/types/Editor.vue.d.ts +42 -29
- package/types/components/CodeDraftEditor.vue.d.ts +148 -0
- package/types/components/ContentMenu.vue.d.ts +1 -3
- package/types/components/FunctionEditor.vue.d.ts +120 -0
- package/types/components/Icon.vue.d.ts +1 -3
- package/types/{layouts → components}/Layout.vue.d.ts +12 -1
- package/types/components/ScrollBar.vue.d.ts +1 -3
- package/types/components/ScrollViewer.vue.d.ts +45 -1
- package/types/components/ToolButton.vue.d.ts +1 -3
- package/types/fields/Code.vue.d.ts +1 -3
- package/types/fields/CodeLink.vue.d.ts +1 -3
- package/types/fields/CodeSelect.vue.d.ts +10 -12
- package/types/fields/UISelect.vue.d.ts +1 -3
- package/types/index.d.ts +1 -0
- package/types/layouts/AddPageBox.vue.d.ts +1 -3
- package/types/layouts/CodeEditor.vue.d.ts +6 -15
- package/types/layouts/NavMenu.vue.d.ts +1 -3
- package/types/layouts/sidebar/LayerMenu.vue.d.ts +25 -9
- package/types/layouts/sidebar/LayerPanel.vue.d.ts +38 -886
- package/types/layouts/sidebar/Sidebar.vue.d.ts +10 -5
- package/types/layouts/sidebar/code-block/CodeBlockEditor.vue.d.ts +1 -1
- package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +6 -5
- package/types/layouts/{sidebar/TabPane.vue.d.ts → workspace/Breadcrumb.vue.d.ts} +5 -43
- package/types/layouts/workspace/Stage.vue.d.ts +25 -8
- package/types/layouts/workspace/ViewerMenu.vue.d.ts +6 -3
- package/types/layouts/workspace/Workspace.vue.d.ts +23 -5
- package/types/services/BaseService.d.ts +2 -1
- package/types/services/codeBlock.d.ts +57 -36
- package/types/services/componentList.d.ts +1 -0
- package/types/services/editor.d.ts +5 -6
- package/types/services/events.d.ts +1 -0
- package/types/services/history.d.ts +5 -15
- package/types/services/props.d.ts +3 -2
- package/types/services/ui.d.ts +2 -1
- package/types/type.d.ts +64 -48
- package/types/utils/editor.d.ts +1 -0
- package/types/utils/index.d.ts +1 -0
- package/types/utils/props.d.ts +1 -1
- package/types/utils/scroll-viewer.d.ts +5 -0
- package/dist/tmagic-editor.mjs.map +0 -1
- package/dist/tmagic-editor.umd.js.map +0 -1
- package/src/layouts/Layout.vue +0 -100
- package/src/layouts/sidebar/TabPane.vue +0 -116
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<TMagicScrollbar class="magic-editor-layer-panel">
|
|
3
3
|
<slot name="layer-panel-header"></slot>
|
|
4
4
|
|
|
5
|
-
<
|
|
6
|
-
|
|
5
|
+
<TMagicInput
|
|
6
|
+
v-model="filterText"
|
|
7
|
+
class="search-input"
|
|
7
8
|
size="small"
|
|
8
9
|
placeholder="输入关键字进行过滤"
|
|
9
10
|
clearable
|
|
10
|
-
|
|
11
|
+
:prefix-icon="Search"
|
|
11
12
|
@change="filterTextChangeHandler"
|
|
12
|
-
></
|
|
13
|
+
></TMagicInput>
|
|
13
14
|
|
|
14
|
-
<
|
|
15
|
+
<TMagicTree
|
|
15
16
|
v-if="values.length"
|
|
17
|
+
tabindex="-1"
|
|
18
|
+
class="magic-editor-layer-tree"
|
|
16
19
|
ref="tree"
|
|
17
20
|
node-key="id"
|
|
18
21
|
empty-text="页面空荡荡的"
|
|
19
22
|
draggable
|
|
20
|
-
:default-expanded-keys="
|
|
23
|
+
:default-expanded-keys="defaultExpandedKeys"
|
|
21
24
|
:load="loadItems"
|
|
22
25
|
:data="values"
|
|
26
|
+
:default-expand-all="true"
|
|
23
27
|
:expand-on-click-node="false"
|
|
24
28
|
:highlight-current="true"
|
|
25
29
|
:props="{
|
|
@@ -27,20 +31,24 @@
|
|
|
27
31
|
}"
|
|
28
32
|
:filter-node-method="filterNode"
|
|
29
33
|
:allow-drop="allowDrop"
|
|
34
|
+
:show-checkbox="isMultiSelectStatus || selectedIds.length > 1"
|
|
30
35
|
@node-click="clickHandler"
|
|
31
36
|
@node-contextmenu="contextmenu"
|
|
32
37
|
@node-drag-end="handleDragEnd"
|
|
33
38
|
@node-collapse="handleCollapse"
|
|
34
39
|
@node-expand="handleExpand"
|
|
40
|
+
@check="multiClickHandler"
|
|
41
|
+
@mousedown="toggleClickFlag"
|
|
42
|
+
@mouseup="toggleClickFlag"
|
|
43
|
+
@mouseenter="mouseenterHandler"
|
|
44
|
+
@mouseleave="mouseleaveHandler"
|
|
35
45
|
>
|
|
36
46
|
<template #default="{ node, data }">
|
|
37
47
|
<div
|
|
38
48
|
:id="data.id"
|
|
39
49
|
class="cus-tree-node"
|
|
40
|
-
@mousedown="toggleClickFlag"
|
|
41
|
-
@mouseup="toggleClickFlag"
|
|
42
50
|
@mouseenter="highlightHandler(data)"
|
|
43
|
-
:class="{ 'cus-tree-node-hover': canHighlight
|
|
51
|
+
:class="{ 'cus-tree-node-hover': canHighlight(data) }"
|
|
44
52
|
>
|
|
45
53
|
<slot name="layer-node-content" :node="node" :data="data">
|
|
46
54
|
<span>
|
|
@@ -49,32 +57,61 @@
|
|
|
49
57
|
</slot>
|
|
50
58
|
</div>
|
|
51
59
|
</template>
|
|
52
|
-
</
|
|
60
|
+
</TMagicTree>
|
|
53
61
|
|
|
54
|
-
<
|
|
55
|
-
<
|
|
56
|
-
</
|
|
57
|
-
</
|
|
62
|
+
<Teleport to="body">
|
|
63
|
+
<LayerMenu ref="menu" :layer-content-menu="layerContentMenu"></LayerMenu>
|
|
64
|
+
</Teleport>
|
|
65
|
+
</TMagicScrollbar>
|
|
58
66
|
</template>
|
|
59
67
|
|
|
60
|
-
<script lang="ts">
|
|
61
|
-
import { computed,
|
|
62
|
-
import
|
|
68
|
+
<script lang="ts" setup name="MEditorLayerPanel">
|
|
69
|
+
import { computed, inject, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
|
|
70
|
+
import { Search } from '@element-plus/icons-vue';
|
|
71
|
+
import KeyController from 'keycon';
|
|
63
72
|
import { throttle } from 'lodash-es';
|
|
64
73
|
|
|
74
|
+
import { TMagicInput, TMagicScrollbar, TMagicTree } from '@tmagic/design';
|
|
65
75
|
import type { Id, MNode, MPage } from '@tmagic/schema';
|
|
66
76
|
import { MContainer, NodeType } from '@tmagic/schema';
|
|
67
77
|
import StageCore from '@tmagic/stage';
|
|
68
78
|
|
|
69
|
-
import type {
|
|
70
|
-
import type { Services } from '../../type';
|
|
79
|
+
import type { MenuButton, MenuComponent, Services } from '../../type';
|
|
71
80
|
import { Layout } from '../../type';
|
|
72
81
|
|
|
73
82
|
import LayerMenu from './LayerMenu.vue';
|
|
74
83
|
|
|
84
|
+
defineProps<{
|
|
85
|
+
layerContentMenu: (MenuButton | MenuComponent)[];
|
|
86
|
+
}>();
|
|
87
|
+
|
|
75
88
|
const throttleTime = 150;
|
|
89
|
+
const services = inject<Services>('services');
|
|
90
|
+
const tree = ref<InstanceType<typeof TMagicTree>>();
|
|
91
|
+
const menu = ref<InstanceType<typeof LayerMenu>>();
|
|
92
|
+
const editorService = services?.editorService;
|
|
93
|
+
const page = computed(() => editorService?.get('page'));
|
|
94
|
+
const values = computed(() => (page.value ? [page.value] : []));
|
|
95
|
+
// 多选选中的节点数组
|
|
96
|
+
const selectedNodes = ref<MNode[]>([]);
|
|
97
|
+
// 多选选中的组件id数组
|
|
98
|
+
const selectedIds = computed(() => selectedNodes.value.map((node: MNode) => node.id));
|
|
99
|
+
// 是否多选
|
|
100
|
+
const isMultiSelectStatus = ref(false);
|
|
101
|
+
// 多选场景 取消选中的那个节点id
|
|
102
|
+
const spliceNodeKey = ref<Id>();
|
|
103
|
+
const filterText = ref('');
|
|
104
|
+
// 默认展开节点
|
|
105
|
+
const defaultExpandedKeys = computed(() => (selectedIds.value.length > 0 ? selectedIds.value : []));
|
|
106
|
+
|
|
107
|
+
editorService?.on('remove', () => {
|
|
108
|
+
setTimeout(() => {
|
|
109
|
+
tree.value?.getNode(editorService.get('node').id)?.updateChildren();
|
|
110
|
+
}, 0);
|
|
111
|
+
});
|
|
76
112
|
|
|
77
|
-
|
|
113
|
+
// 触发画布单选
|
|
114
|
+
const select = async (data: MNode) => {
|
|
78
115
|
if (!data.id) {
|
|
79
116
|
throw new Error('没有id');
|
|
80
117
|
}
|
|
@@ -83,7 +120,14 @@ const select = async (data: MNode, editorService?: EditorService) => {
|
|
|
83
120
|
editorService?.get<StageCore>('stage')?.select(data.id);
|
|
84
121
|
};
|
|
85
122
|
|
|
86
|
-
|
|
123
|
+
// 触发画布多选
|
|
124
|
+
const multiSelect = async (data: Id[]) => {
|
|
125
|
+
await editorService?.multiSelect(data);
|
|
126
|
+
editorService?.get<StageCore>('stage')?.multiSelect(data);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// 触发画布高亮
|
|
130
|
+
const highlight = (data: MNode) => {
|
|
87
131
|
if (!data?.id) {
|
|
88
132
|
throw new Error('没有id');
|
|
89
133
|
}
|
|
@@ -91,182 +135,238 @@ const highlight = (data: MNode, editorService?: EditorService) => {
|
|
|
91
135
|
editorService?.get<StageCore>('stage')?.highlight(data.id);
|
|
92
136
|
};
|
|
93
137
|
|
|
94
|
-
const
|
|
95
|
-
allowDrop: (draggingNode: any, dropNode: any, type: string): boolean => {
|
|
96
|
-
const { data } = dropNode || {};
|
|
97
|
-
const { data: ingData } = draggingNode;
|
|
138
|
+
const expandedKeys = new Map<Id, Id>();
|
|
98
139
|
|
|
99
|
-
|
|
140
|
+
// tree方法:拖拽时判定目标节点能否成为拖动目标位置
|
|
141
|
+
const allowDrop = (draggingNode: any, dropNode: any, type: string): boolean => {
|
|
142
|
+
const { data } = dropNode || {};
|
|
143
|
+
const { data: ingData } = draggingNode;
|
|
100
144
|
|
|
101
|
-
|
|
102
|
-
if (ingType === NodeType.PAGE && data.type !== NodeType.PAGE) return false;
|
|
103
|
-
if (!data || !data.type) return false;
|
|
104
|
-
if (['prev', 'next'].includes(type)) return true;
|
|
105
|
-
if (data.items || data.type === 'container') return true;
|
|
145
|
+
const { type: ingType } = ingData;
|
|
106
146
|
|
|
107
|
-
|
|
108
|
-
|
|
147
|
+
if (ingType !== NodeType.PAGE && data.type === NodeType.PAGE) return false;
|
|
148
|
+
if (ingType === NodeType.PAGE && data.type !== NodeType.PAGE) return false;
|
|
149
|
+
if (!data || !data.type) return false;
|
|
150
|
+
if (['prev', 'next'].includes(type)) return true;
|
|
151
|
+
if (data.items || data.type === 'container') return true;
|
|
109
152
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const { data: node } = e;
|
|
113
|
-
const parent = editorService?.getParentById(node.id, false) as MContainer;
|
|
114
|
-
const layout = await editorService?.getLayout(parent);
|
|
115
|
-
node.style.position = layout;
|
|
116
|
-
if (layout === Layout.RELATIVE) {
|
|
117
|
-
node.style.top = 0;
|
|
118
|
-
node.style.left = 0;
|
|
119
|
-
}
|
|
120
|
-
const { data } = tree.value;
|
|
121
|
-
const [page] = data as [MPage];
|
|
122
|
-
editorService?.update(page);
|
|
123
|
-
},
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
const useStatus = (tree: Ref<InstanceType<typeof ElTree> | undefined>, editorService?: EditorService) => {
|
|
127
|
-
const node = ref<MNode>();
|
|
128
|
-
const page = computed(() => editorService?.get('page'));
|
|
129
|
-
const expandedKeys = new Map<Id, Id>();
|
|
153
|
+
return false;
|
|
154
|
+
};
|
|
130
155
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
156
|
+
// tree事件:拖拽结束时(可能未成功)触发的事件
|
|
157
|
+
const handleDragEnd = async (e: any) => {
|
|
158
|
+
if (!tree.value) return;
|
|
159
|
+
const { data: node } = e;
|
|
160
|
+
const parent = editorService?.getParentById(node.id, false) as MContainer;
|
|
161
|
+
const layout = await editorService?.getLayout(parent);
|
|
162
|
+
node.style.position = layout;
|
|
163
|
+
if (layout === Layout.RELATIVE) {
|
|
164
|
+
node.style.top = 0;
|
|
165
|
+
node.style.left = 0;
|
|
166
|
+
}
|
|
167
|
+
const data = tree.value.getData();
|
|
168
|
+
const [page] = data as [MPage];
|
|
169
|
+
editorService?.update(page);
|
|
170
|
+
};
|
|
137
171
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
172
|
+
// tree方法: 加载子树数据的方法
|
|
173
|
+
const loadItems = (node: any, resolve: Function) => {
|
|
174
|
+
if (Array.isArray(node.data)) {
|
|
175
|
+
return resolve(node.data);
|
|
176
|
+
}
|
|
177
|
+
if (Array.isArray(node.data?.items)) {
|
|
178
|
+
return resolve(node.data?.items);
|
|
179
|
+
}
|
|
180
|
+
resolve([]);
|
|
181
|
+
};
|
|
142
182
|
|
|
143
|
-
|
|
183
|
+
// tree事件:节点被关闭时触发的事件
|
|
184
|
+
const handleCollapse = (data: MNode) => {
|
|
185
|
+
expandedKeys.delete(data.id);
|
|
186
|
+
};
|
|
144
187
|
|
|
145
|
-
|
|
188
|
+
// tree事件:节点被展开时触发的事件
|
|
189
|
+
const handleExpand = (data: MNode) => {
|
|
190
|
+
const parent = editorService?.getParentById(data.id);
|
|
191
|
+
if (!parent?.id) return;
|
|
192
|
+
expandedKeys.set(parent.id, parent.id);
|
|
193
|
+
};
|
|
146
194
|
|
|
147
|
-
|
|
148
|
-
|
|
195
|
+
// tree方法:对树节点进行筛选时执行的方法
|
|
196
|
+
const filterNode = (value: string, data: MNode): boolean => {
|
|
197
|
+
if (!value) {
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
let name = '';
|
|
201
|
+
if (data.name) {
|
|
202
|
+
name = data.name;
|
|
203
|
+
} else if (data.items) {
|
|
204
|
+
name = 'container';
|
|
205
|
+
}
|
|
206
|
+
return `${data.id}${name}${data.type}`.indexOf(value) !== -1;
|
|
207
|
+
};
|
|
149
208
|
|
|
150
|
-
|
|
151
|
-
|
|
209
|
+
// 过滤关键字
|
|
210
|
+
const filterTextChangeHandler = (val: string) => {
|
|
211
|
+
tree.value?.filter(val);
|
|
212
|
+
};
|
|
152
213
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
214
|
+
// 树节点更新后展开上次展开过的节点
|
|
215
|
+
const expandNodes = async () => {
|
|
216
|
+
if (!tree.value) return;
|
|
217
|
+
await nextTick();
|
|
218
|
+
tree.value &&
|
|
219
|
+
Object.entries(tree.value.getStore().nodesMap).forEach(([id, node]: [string, any]) => {
|
|
220
|
+
if (node.expanded && node.data.items) {
|
|
221
|
+
expandedKeys.set(id, id);
|
|
222
|
+
}
|
|
161
223
|
});
|
|
224
|
+
expandedKeys.forEach((key) => {
|
|
225
|
+
if (!tree.value) return;
|
|
226
|
+
tree.value.getNode(key)?.expand();
|
|
162
227
|
});
|
|
228
|
+
};
|
|
163
229
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const parent = editorService?.getParentById(data.id);
|
|
187
|
-
if (!parent?.id) return;
|
|
188
|
-
expandedKeys.set(parent.id, parent.id);
|
|
189
|
-
},
|
|
190
|
-
};
|
|
230
|
+
watch(
|
|
231
|
+
() => editorService?.get<MNode[]>('nodes'),
|
|
232
|
+
(nodes) => {
|
|
233
|
+
selectedNodes.value = nodes ?? [];
|
|
234
|
+
},
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
// 设置树节点选中状态
|
|
238
|
+
const setTreeKeyStatus = () => {
|
|
239
|
+
if (!tree.value) return;
|
|
240
|
+
if (selectedIds.value.length === 0) {
|
|
241
|
+
tree.value.setCheckedKeys([]);
|
|
242
|
+
tree.value.setCurrentKey();
|
|
243
|
+
} else if (selectedIds.value.length === 1 && !isMultiSelectStatus.value) {
|
|
244
|
+
// 选中1个
|
|
245
|
+
tree.value.setCurrentKey(selectedIds.value[0], true);
|
|
246
|
+
tree.value.setCheckedKeys([]);
|
|
247
|
+
} else {
|
|
248
|
+
// 多选框选中多个
|
|
249
|
+
tree.value.setCheckedKeys(selectedIds.value);
|
|
250
|
+
tree.value.setCurrentKey();
|
|
251
|
+
}
|
|
191
252
|
};
|
|
192
253
|
|
|
193
|
-
|
|
194
|
-
|
|
254
|
+
watch([selectedIds, tree], async () => {
|
|
255
|
+
if (!tree.value || !editorService) return;
|
|
256
|
+
const parent = editorService.get('parent');
|
|
257
|
+
if (!parent?.id) return;
|
|
195
258
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
let name = '';
|
|
201
|
-
if (data.name) {
|
|
202
|
-
name = data.name;
|
|
203
|
-
} else if (data.items) {
|
|
204
|
-
name = 'container';
|
|
205
|
-
}
|
|
206
|
-
return `${data.id}${name}${data.type}`.indexOf(value) !== -1;
|
|
207
|
-
},
|
|
259
|
+
const treeNode = tree.value.getNode(parent.id);
|
|
260
|
+
treeNode?.updateChildren();
|
|
261
|
+
await expandNodes();
|
|
208
262
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
},
|
|
263
|
+
// 设置高亮节点操作一定要在刷新展开状态之后,否则可能导致设置的高亮无效
|
|
264
|
+
setTreeKeyStatus();
|
|
212
265
|
});
|
|
213
266
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
editorService?.on('remove', () => {
|
|
239
|
-
setTimeout(() => {
|
|
240
|
-
tree.value?.getNode(editorService.get('node').id)?.updateChildren();
|
|
241
|
-
}, 0);
|
|
267
|
+
const mouseenterHandler = () => {
|
|
268
|
+
tree.value?.$el.focus();
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const mouseleaveHandler = () => {
|
|
272
|
+
tree.value?.$el.blur();
|
|
273
|
+
isMultiSelectStatus.value = false;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
let keycon: KeyController;
|
|
277
|
+
|
|
278
|
+
onMounted(() => {
|
|
279
|
+
keycon = new KeyController(tree.value?.$el);
|
|
280
|
+
const isMac = /mac os x/.test(navigator.userAgent.toLowerCase());
|
|
281
|
+
const ctrl = isMac ? 'meta' : 'ctrl';
|
|
282
|
+
|
|
283
|
+
keycon
|
|
284
|
+
.keydown(ctrl, (e) => {
|
|
285
|
+
e.inputEvent.preventDefault();
|
|
286
|
+
isMultiSelectStatus.value = true;
|
|
287
|
+
})
|
|
288
|
+
.keyup(ctrl, (e) => {
|
|
289
|
+
e.inputEvent.preventDefault();
|
|
290
|
+
isMultiSelectStatus.value = false;
|
|
242
291
|
});
|
|
292
|
+
});
|
|
243
293
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
294
|
+
onUnmounted(() => {
|
|
295
|
+
keycon.destroy();
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// 鼠标是否按下标志,用于高亮状态互斥
|
|
299
|
+
const clicked = ref(false);
|
|
300
|
+
// 高亮的节点
|
|
301
|
+
const highlightNode = computed(() => editorService?.get('highlightNode'));
|
|
302
|
+
|
|
303
|
+
// 鼠标在组件树移动触发高亮
|
|
304
|
+
const highlightHandler = throttle((data: MNode) => {
|
|
305
|
+
highlight(data);
|
|
306
|
+
}, throttleTime);
|
|
307
|
+
|
|
308
|
+
const toggleClickFlag = () => {
|
|
309
|
+
clicked.value = !clicked.value;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// 是否满足展示高亮
|
|
313
|
+
const canHighlight = (data: MNode) => {
|
|
314
|
+
if (clicked.value) return false;
|
|
315
|
+
return (
|
|
316
|
+
data.id === highlightNode?.value?.id && !selectedIds.value.includes(data.id) && spliceNodeKey.value !== data.id
|
|
317
|
+
);
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// 监听选择模式,针对多选情况做一些处理
|
|
321
|
+
watch(isMultiSelectStatus, () => {
|
|
322
|
+
// 多选模式如果已存在第一个选中的元素是页面(magic-ui-page) 剔除页面选中状态
|
|
323
|
+
if (isMultiSelectStatus.value && selectedNodes.value.length === 1 && selectedNodes.value[0].type === NodeType.PAGE) {
|
|
324
|
+
selectedNodes.value = [];
|
|
325
|
+
}
|
|
271
326
|
});
|
|
327
|
+
|
|
328
|
+
// 选择节点多选框
|
|
329
|
+
const multiClickHandler = (data: MNode): void => {
|
|
330
|
+
if (!data?.id) {
|
|
331
|
+
throw new Error('没有id');
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// 页面(magic-ui-page)不可选中
|
|
335
|
+
if (data.type === NodeType.PAGE) {
|
|
336
|
+
tree.value?.setCheckedKeys([]);
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const index = selectedNodes.value.findIndex((node) => node.id === data.id);
|
|
341
|
+
if (index !== -1) {
|
|
342
|
+
// 已经包含就移除掉
|
|
343
|
+
selectedNodes.value.splice(index, 1);
|
|
344
|
+
spliceNodeKey.value = data.id;
|
|
345
|
+
} else {
|
|
346
|
+
selectedNodes.value = [...selectedNodes.value, data];
|
|
347
|
+
}
|
|
348
|
+
tree.value?.setCheckedKeys(selectedIds.value);
|
|
349
|
+
multiSelect(selectedIds.value);
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
// 点击节点
|
|
353
|
+
const clickHandler = (data: MNode): void => {
|
|
354
|
+
if (!isMultiSelectStatus.value) {
|
|
355
|
+
if (services?.uiService.get<boolean>('uiSelectMode')) {
|
|
356
|
+
document.dispatchEvent(new CustomEvent('ui-select', { detail: data }));
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
tree.value?.setCurrentKey(data.id);
|
|
360
|
+
select(data);
|
|
361
|
+
} else {
|
|
362
|
+
multiClickHandler(data);
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
// 右键菜单
|
|
367
|
+
const contextmenu = async (event: MouseEvent, data: MNode): Promise<void> => {
|
|
368
|
+
event.preventDefault();
|
|
369
|
+
await select(data);
|
|
370
|
+
menu.value?.show(event);
|
|
371
|
+
};
|
|
272
372
|
</script>
|