@tmagic/editor 1.2.0-beta.9 → 1.2.0
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 +59 -35
- package/dist/{tmagic-editor.mjs → tmagic-editor.js} +1473 -1154
- package/dist/tmagic-editor.js.map +1 -0
- package/dist/{tmagic-editor.umd.js → tmagic-editor.umd.cjs} +1483 -1159
- package/dist/tmagic-editor.umd.cjs.map +1 -0
- package/package.json +16 -14
- package/src/Editor.vue +66 -40
- package/src/components/CodeDraftEditor.vue +145 -0
- package/src/components/ContentMenu.vue +1 -1
- package/src/components/FunctionEditor.vue +145 -0
- package/src/components/Icon.vue +1 -1
- package/src/{layouts → components}/Layout.vue +54 -15
- package/src/components/ScrollBar.vue +1 -1
- package/src/components/ScrollViewer.vue +10 -1
- package/src/components/ToolButton.vue +1 -1
- package/src/fields/Code.vue +1 -1
- package/src/fields/CodeLink.vue +1 -1
- package/src/fields/CodeSelect.vue +89 -102
- package/src/fields/UISelect.vue +1 -1
- package/src/index.ts +1 -0
- package/src/layouts/AddPageBox.vue +1 -1
- package/src/layouts/CodeEditor.vue +2 -3
- package/src/layouts/Framework.vue +2 -2
- package/src/layouts/NavMenu.vue +2 -2
- package/src/layouts/PropsPanel.vue +13 -6
- package/src/layouts/Resizer.vue +1 -1
- package/src/layouts/sidebar/ComponentListPanel.vue +1 -1
- package/src/layouts/sidebar/LayerMenu.vue +9 -7
- package/src/layouts/sidebar/LayerPanel.vue +127 -174
- package/src/layouts/sidebar/Sidebar.vue +110 -26
- package/src/layouts/sidebar/code-block/CodeBlockEditor.vue +27 -83
- package/src/layouts/sidebar/code-block/CodeBlockList.vue +90 -122
- package/src/layouts/workspace/Breadcrumb.vue +1 -1
- package/src/layouts/workspace/PageBar.vue +1 -1
- 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 +7 -3
- package/src/services/BaseService.ts +10 -1
- package/src/services/codeBlock.ts +124 -125
- package/src/services/componentList.ts +6 -1
- package/src/services/editor.ts +54 -18
- 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/code-block.scss +43 -24
- package/src/theme/component-list-panel.scss +8 -5
- package/src/type.ts +77 -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 +4 -3
- package/types/Editor.vue.d.ts +47 -27
- package/types/components/CodeDraftEditor.vue.d.ts +56 -0
- package/types/components/ContentMenu.vue.d.ts +11 -87
- package/types/components/FunctionEditor.vue.d.ts +43 -0
- package/types/components/Icon.vue.d.ts +4 -50
- package/types/{layouts → components}/Layout.vue.d.ts +12 -1
- package/types/components/ScrollBar.vue.d.ts +7 -65
- package/types/components/ScrollViewer.vue.d.ts +45 -1
- package/types/components/ToolButton.vue.d.ts +12 -78
- package/types/fields/Code.vue.d.ts +11 -77
- package/types/fields/CodeLink.vue.d.ts +12 -82
- package/types/fields/CodeSelect.vue.d.ts +13 -77
- package/types/fields/UISelect.vue.d.ts +7 -65
- package/types/index.d.ts +1 -0
- package/types/layouts/AddPageBox.vue.d.ts +1 -43
- package/types/layouts/CodeEditor.vue.d.ts +22 -130
- package/types/layouts/NavMenu.vue.d.ts +9 -69
- package/types/layouts/sidebar/LayerMenu.vue.d.ts +16 -48
- package/types/layouts/sidebar/LayerPanel.vue.d.ts +24 -7
- 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/workspace/Breadcrumb.vue.d.ts +1 -43
- package/types/layouts/workspace/Stage.vue.d.ts +15 -43
- package/types/layouts/workspace/ViewerMenu.vue.d.ts +11 -66
- 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 +65 -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/sidebar/TabPane.vue +0 -118
- package/types/layouts/sidebar/TabPane.vue.d.ts +0 -82
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<TMagicScrollbar
|
|
3
|
-
class="magic-editor-layer-panel"
|
|
4
|
-
@mouseenter="addSelectModeListener"
|
|
5
|
-
@mouseleave="removeSelectModeListener"
|
|
6
|
-
>
|
|
2
|
+
<TMagicScrollbar class="magic-editor-layer-panel">
|
|
7
3
|
<slot name="layer-panel-header"></slot>
|
|
8
4
|
|
|
9
5
|
<TMagicInput
|
|
@@ -23,33 +19,28 @@
|
|
|
23
19
|
node-key="id"
|
|
24
20
|
empty-text="页面空荡荡的"
|
|
25
21
|
draggable
|
|
26
|
-
:default-expanded-keys="
|
|
27
|
-
:
|
|
22
|
+
:default-expanded-keys="expandedKeys"
|
|
23
|
+
:default-checked-keys="checkedKeys"
|
|
24
|
+
:current-node-key="currentNodeKey"
|
|
28
25
|
:data="values"
|
|
29
26
|
:expand-on-click-node="false"
|
|
30
|
-
:highlight-current="
|
|
31
|
-
:
|
|
32
|
-
|
|
33
|
-
}"
|
|
27
|
+
:highlight-current="!isMultiSelect"
|
|
28
|
+
:check-on-click-node="true"
|
|
29
|
+
:props="treeProps"
|
|
34
30
|
:filter-node-method="filterNode"
|
|
35
31
|
:allow-drop="allowDrop"
|
|
36
|
-
:show-checkbox="
|
|
32
|
+
:show-checkbox="isMultiSelect"
|
|
37
33
|
@node-click="clickHandler"
|
|
38
34
|
@node-contextmenu="contextmenu"
|
|
39
35
|
@node-drag-end="handleDragEnd"
|
|
40
36
|
@node-collapse="handleCollapse"
|
|
41
37
|
@node-expand="handleExpand"
|
|
42
|
-
@check="
|
|
38
|
+
@check="checkHandler"
|
|
43
39
|
@mousedown="toggleClickFlag"
|
|
44
40
|
@mouseup="toggleClickFlag"
|
|
45
41
|
>
|
|
46
42
|
<template #default="{ node, data }">
|
|
47
|
-
<div
|
|
48
|
-
:id="data.id"
|
|
49
|
-
class="cus-tree-node"
|
|
50
|
-
@mouseenter="highlightHandler(data)"
|
|
51
|
-
:class="{ 'cus-tree-node-hover': canHighlight(data) }"
|
|
52
|
-
>
|
|
43
|
+
<div class="cus-tree-node" :id="data.id" @mouseenter="highlightHandler(data)">
|
|
53
44
|
<slot name="layer-node-content" :node="node" :data="data">
|
|
54
45
|
<span>
|
|
55
46
|
{{ `${data.name} (${data.id})` }}
|
|
@@ -59,52 +50,69 @@
|
|
|
59
50
|
</template>
|
|
60
51
|
</TMagicTree>
|
|
61
52
|
|
|
62
|
-
<
|
|
63
|
-
<
|
|
64
|
-
</
|
|
53
|
+
<Teleport to="body">
|
|
54
|
+
<LayerMenu ref="menu" :layer-content-menu="layerContentMenu"></LayerMenu>
|
|
55
|
+
</Teleport>
|
|
65
56
|
</TMagicScrollbar>
|
|
66
57
|
</template>
|
|
67
58
|
|
|
68
|
-
<script lang="ts" setup>
|
|
69
|
-
import { computed, inject,
|
|
59
|
+
<script lang="ts" setup name="MEditorLayerPanel">
|
|
60
|
+
import { computed, inject, onMounted, onUnmounted, ref, watch } from 'vue';
|
|
70
61
|
import { Search } from '@element-plus/icons-vue';
|
|
71
62
|
import KeyController from 'keycon';
|
|
72
|
-
import { throttle } from 'lodash-es';
|
|
63
|
+
import { difference, throttle, union } from 'lodash-es';
|
|
73
64
|
|
|
74
65
|
import { TMagicInput, TMagicScrollbar, TMagicTree } from '@tmagic/design';
|
|
75
66
|
import type { Id, MNode, MPage } from '@tmagic/schema';
|
|
76
67
|
import { MContainer, NodeType } from '@tmagic/schema';
|
|
77
68
|
import StageCore from '@tmagic/stage';
|
|
69
|
+
import { getNodePath, isPage } from '@tmagic/utils';
|
|
78
70
|
|
|
79
|
-
import type { Services } from '../../type';
|
|
71
|
+
import type { MenuButton, MenuComponent, Services } from '../../type';
|
|
80
72
|
import { Layout } from '../../type';
|
|
81
73
|
|
|
82
74
|
import LayerMenu from './LayerMenu.vue';
|
|
83
75
|
|
|
76
|
+
defineProps<{
|
|
77
|
+
layerContentMenu: (MenuButton | MenuComponent)[];
|
|
78
|
+
}>();
|
|
79
|
+
|
|
84
80
|
const throttleTime = 150;
|
|
85
81
|
const services = inject<Services>('services');
|
|
82
|
+
const editorService = services?.editorService;
|
|
83
|
+
|
|
86
84
|
const tree = ref<InstanceType<typeof TMagicTree>>();
|
|
87
85
|
const menu = ref<InstanceType<typeof LayerMenu>>();
|
|
88
|
-
|
|
89
|
-
const page = computed(() => editorService?.get('page'));
|
|
90
|
-
const values = computed(() => (page.value ? [page.value] : []));
|
|
91
|
-
// 多选选中的节点数组
|
|
92
|
-
const selectedNodes = ref<MNode[]>([]);
|
|
86
|
+
|
|
93
87
|
// 多选选中的组件id数组
|
|
94
|
-
const
|
|
88
|
+
const checkedKeys = ref<Id[]>([]);
|
|
95
89
|
// 是否多选
|
|
96
|
-
const
|
|
97
|
-
// 多选场景 取消选中的那个节点id
|
|
98
|
-
const spliceNodeKey = ref<Id>();
|
|
90
|
+
const isCtrlKeyDown = ref(false);
|
|
99
91
|
const filterText = ref('');
|
|
100
92
|
// 默认展开节点
|
|
101
|
-
const
|
|
93
|
+
const expandedKeys = ref<Id[]>([]);
|
|
94
|
+
const currentNodeKey = ref<Id>();
|
|
95
|
+
// 鼠标是否按下标志,用于高亮状态互斥
|
|
96
|
+
const clicked = ref(false);
|
|
102
97
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
98
|
+
const treeProps = {
|
|
99
|
+
children: 'items',
|
|
100
|
+
disabled: (data: MNode) => Boolean(data.items?.length),
|
|
101
|
+
class: (data: MNode) => {
|
|
102
|
+
if (clicked.value || isPage(data)) return '';
|
|
103
|
+
if (data.id === highlightNode?.value?.id && !checkedKeys.value.includes(data.id)) {
|
|
104
|
+
return 'cus-tree-node-hover';
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const isMultiSelect = computed(() => isCtrlKeyDown.value || checkedKeys.value.length > 1);
|
|
110
|
+
|
|
111
|
+
const nodes = computed(() => editorService?.get<MNode[]>('nodes') || []);
|
|
112
|
+
const page = computed(() => editorService?.get<MPage>('page'));
|
|
113
|
+
const values = computed(() => (page.value ? [page.value] : []));
|
|
114
|
+
// 高亮的节点
|
|
115
|
+
const highlightNode = computed(() => editorService?.get('highlightNode'));
|
|
108
116
|
|
|
109
117
|
// 触发画布单选
|
|
110
118
|
const select = async (data: MNode) => {
|
|
@@ -131,8 +139,6 @@ const highlight = (data: MNode) => {
|
|
|
131
139
|
editorService?.get<StageCore>('stage')?.highlight(data.id);
|
|
132
140
|
};
|
|
133
141
|
|
|
134
|
-
const expandedKeys = new Map<Id, Id>();
|
|
135
|
-
|
|
136
142
|
// tree方法:拖拽时判定目标节点能否成为拖动目标位置
|
|
137
143
|
const allowDrop = (draggingNode: any, dropNode: any, type: string): boolean => {
|
|
138
144
|
const { data } = dropNode || {};
|
|
@@ -165,27 +171,22 @@ const handleDragEnd = async (e: any) => {
|
|
|
165
171
|
editorService?.update(page);
|
|
166
172
|
};
|
|
167
173
|
|
|
168
|
-
// tree方法: 加载子树数据的方法
|
|
169
|
-
const loadItems = (node: any, resolve: Function) => {
|
|
170
|
-
if (Array.isArray(node.data)) {
|
|
171
|
-
return resolve(node.data);
|
|
172
|
-
}
|
|
173
|
-
if (Array.isArray(node.data?.items)) {
|
|
174
|
-
return resolve(node.data?.items);
|
|
175
|
-
}
|
|
176
|
-
resolve([]);
|
|
177
|
-
};
|
|
178
|
-
|
|
179
174
|
// tree事件:节点被关闭时触发的事件
|
|
180
175
|
const handleCollapse = (data: MNode) => {
|
|
181
|
-
expandedKeys.
|
|
176
|
+
expandedKeys.value = expandedKeys.value.filter((id) => id !== data.id);
|
|
182
177
|
};
|
|
183
178
|
|
|
184
179
|
// tree事件:节点被展开时触发的事件
|
|
185
180
|
const handleExpand = (data: MNode) => {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
181
|
+
if (!page.value) {
|
|
182
|
+
expandedKeys.value = [];
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
expandedKeys.value = union(
|
|
187
|
+
expandedKeys.value,
|
|
188
|
+
getNodePath(data.id, [page.value]).map((node) => node.id),
|
|
189
|
+
);
|
|
189
190
|
};
|
|
190
191
|
|
|
191
192
|
// tree方法:对树节点进行筛选时执行的方法
|
|
@@ -207,89 +208,74 @@ const filterTextChangeHandler = (val: string) => {
|
|
|
207
208
|
tree.value?.filter(val);
|
|
208
209
|
};
|
|
209
210
|
|
|
210
|
-
|
|
211
|
-
const
|
|
212
|
-
if (!tree.value) return;
|
|
213
|
-
await nextTick();
|
|
214
|
-
tree.value &&
|
|
215
|
-
Object.entries(tree.value.getStore().nodesMap).forEach(([id, node]: [string, any]) => {
|
|
216
|
-
if (node.expanded && node.data.items) {
|
|
217
|
-
expandedKeys.set(id, id);
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
expandedKeys.forEach((key) => {
|
|
221
|
-
if (!tree.value) return;
|
|
222
|
-
tree.value.getNode(key)?.expand();
|
|
223
|
-
});
|
|
224
|
-
};
|
|
211
|
+
watch(nodes, (nodes) => {
|
|
212
|
+
const ids = nodes?.map((node) => node.id) || [];
|
|
225
213
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
(nodes) => {
|
|
229
|
-
selectedNodes.value = nodes ?? [];
|
|
230
|
-
},
|
|
231
|
-
);
|
|
214
|
+
const idsLength = ids.length;
|
|
215
|
+
const checkedKeysLength = checkedKeys.value.length;
|
|
232
216
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
217
|
+
if (
|
|
218
|
+
difference(
|
|
219
|
+
idsLength > checkedKeysLength ? ids : checkedKeys.value,
|
|
220
|
+
idsLength > checkedKeysLength ? checkedKeys.value : ids,
|
|
221
|
+
).length
|
|
222
|
+
) {
|
|
223
|
+
tree.value?.setCheckedKeys([], false);
|
|
224
|
+
|
|
225
|
+
[currentNodeKey.value] = ids;
|
|
226
|
+
checkedKeys.value = ids.filter((id) => id !== page.value?.id);
|
|
227
|
+
expandedKeys.value = union(expandedKeys.value, ids);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
watch(isMultiSelect, (isMultiSelect) => {
|
|
232
|
+
if (!isMultiSelect) {
|
|
233
|
+
currentNodeKey.value = editorService?.get<MNode>('node').id;
|
|
234
|
+
tree.value?.setCurrentKey(currentNodeKey.value);
|
|
247
235
|
}
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const editorServiceRemoveHandler = () => {
|
|
239
|
+
setTimeout(() => {
|
|
240
|
+
tree.value?.getNode(editorService?.get('node').id)?.updateChildren();
|
|
241
|
+
}, 0);
|
|
248
242
|
};
|
|
249
243
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if (!parent?.id) return;
|
|
244
|
+
const windowBlurHandler = () => {
|
|
245
|
+
isCtrlKeyDown.value = false;
|
|
246
|
+
};
|
|
254
247
|
|
|
255
|
-
|
|
256
|
-
treeNode?.updateChildren();
|
|
257
|
-
await expandNodes();
|
|
248
|
+
let keycon: KeyController;
|
|
258
249
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
});
|
|
250
|
+
onMounted(() => {
|
|
251
|
+
editorService?.on('remove', editorServiceRemoveHandler);
|
|
262
252
|
|
|
263
|
-
|
|
264
|
-
// 监听模式选择
|
|
265
|
-
const addSelectModeListener = () => {
|
|
253
|
+
keycon = new KeyController();
|
|
266
254
|
const isMac = /mac os x/.test(navigator.userAgent.toLowerCase());
|
|
267
255
|
const ctrl = isMac ? 'meta' : 'ctrl';
|
|
268
|
-
keycon.value = new KeyController();
|
|
269
|
-
keycon.value.keydown(ctrl, (e) => {
|
|
270
|
-
e.inputEvent.preventDefault();
|
|
271
|
-
isMultiSelectStatus.value = true;
|
|
272
|
-
});
|
|
273
|
-
// ctrl+tab切到其他窗口,需要将多选状态置为false
|
|
274
|
-
keycon.value.on('blur', () => {
|
|
275
|
-
isMultiSelectStatus.value = false;
|
|
276
|
-
});
|
|
277
|
-
keycon.value.keyup(ctrl, (e) => {
|
|
278
|
-
e.inputEvent.preventDefault();
|
|
279
|
-
isMultiSelectStatus.value = false;
|
|
280
|
-
});
|
|
281
|
-
};
|
|
282
|
-
// 移除监听
|
|
283
|
-
const removeSelectModeListener = () => {
|
|
284
|
-
keycon.value?.destroy();
|
|
285
|
-
// 如果鼠标移出监听范围,且当前只选中了一个,置为单选模式(修复按住ctrl不放但鼠标移出的情况)
|
|
286
|
-
if (selectedIds.value.length === 1) isMultiSelectStatus.value = false;
|
|
287
|
-
};
|
|
288
256
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
257
|
+
keycon
|
|
258
|
+
.keydown((e) => {
|
|
259
|
+
if (e.key !== ctrl) {
|
|
260
|
+
isCtrlKeyDown.value = false;
|
|
261
|
+
}
|
|
262
|
+
})
|
|
263
|
+
.keydown(ctrl, () => {
|
|
264
|
+
isCtrlKeyDown.value = true;
|
|
265
|
+
})
|
|
266
|
+
.keyup(ctrl, () => {
|
|
267
|
+
isCtrlKeyDown.value = false;
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
globalThis.addEventListener('blur', windowBlurHandler);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
onUnmounted(() => {
|
|
274
|
+
keycon.destroy();
|
|
275
|
+
|
|
276
|
+
editorService?.off('remove', editorServiceRemoveHandler);
|
|
277
|
+
globalThis.removeEventListener('blur', windowBlurHandler);
|
|
278
|
+
});
|
|
293
279
|
|
|
294
280
|
// 鼠标在组件树移动触发高亮
|
|
295
281
|
const highlightHandler = throttle((data: MNode) => {
|
|
@@ -300,58 +286,25 @@ const toggleClickFlag = () => {
|
|
|
300
286
|
clicked.value = !clicked.value;
|
|
301
287
|
};
|
|
302
288
|
|
|
303
|
-
// 是否满足展示高亮
|
|
304
|
-
const canHighlight = (data: MNode) => {
|
|
305
|
-
if (clicked.value) return false;
|
|
306
|
-
return (
|
|
307
|
-
data.id === highlightNode?.value?.id && !selectedIds.value.includes(data.id) && spliceNodeKey.value !== data.id
|
|
308
|
-
);
|
|
309
|
-
};
|
|
310
|
-
|
|
311
|
-
// 监听选择模式,针对多选情况做一些处理
|
|
312
|
-
watch(isMultiSelectStatus, () => {
|
|
313
|
-
// 多选模式如果已存在第一个选中的元素是页面(magic-ui-page) 剔除页面选中状态
|
|
314
|
-
if (isMultiSelectStatus.value && selectedNodes.value.length === 1 && selectedNodes.value[0].type === NodeType.PAGE) {
|
|
315
|
-
selectedNodes.value = [];
|
|
316
|
-
}
|
|
317
|
-
});
|
|
318
|
-
|
|
319
289
|
// 选择节点多选框
|
|
320
|
-
const
|
|
321
|
-
if (
|
|
322
|
-
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// 页面(magic-ui-page)不可选中
|
|
326
|
-
if (data.type === NodeType.PAGE) {
|
|
327
|
-
tree.value?.setCheckedKeys([]);
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
const index = selectedNodes.value.findIndex((node) => node.id === data.id);
|
|
332
|
-
if (index !== -1) {
|
|
333
|
-
// 已经包含就移除掉
|
|
334
|
-
selectedNodes.value.splice(index, 1);
|
|
335
|
-
spliceNodeKey.value = data.id;
|
|
290
|
+
const checkHandler = (data: MNode, { checkedNodes }: any): void => {
|
|
291
|
+
if (checkedNodes.length > 0) {
|
|
292
|
+
multiSelect(checkedNodes.map((node: MNode) => node.id));
|
|
336
293
|
} else {
|
|
337
|
-
|
|
294
|
+
multiSelect(nodes.value.map((node: MNode) => node.id));
|
|
338
295
|
}
|
|
339
|
-
tree.value?.setCheckedKeys(selectedIds.value);
|
|
340
|
-
multiSelect(selectedIds.value);
|
|
341
296
|
};
|
|
342
297
|
|
|
343
298
|
// 点击节点
|
|
344
299
|
const clickHandler = (data: MNode): void => {
|
|
345
|
-
if (
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
select(data);
|
|
352
|
-
} else {
|
|
353
|
-
multiClickHandler(data);
|
|
300
|
+
if (isCtrlKeyDown.value) {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (services?.uiService.get<boolean>('uiSelectMode')) {
|
|
304
|
+
document.dispatchEvent(new CustomEvent('ui-select', { detail: data }));
|
|
305
|
+
return;
|
|
354
306
|
}
|
|
307
|
+
select(data);
|
|
355
308
|
};
|
|
356
309
|
|
|
357
310
|
// 右键菜单
|
|
@@ -6,58 +6,142 @@
|
|
|
6
6
|
type="card"
|
|
7
7
|
tab-position="left"
|
|
8
8
|
>
|
|
9
|
-
<
|
|
10
|
-
<template #
|
|
11
|
-
<
|
|
9
|
+
<component :is="uiComponent.component" v-for="(config, index) in sideBarItems" :key="index" :name="config.text">
|
|
10
|
+
<template #label>
|
|
11
|
+
<div :key="config.text">
|
|
12
|
+
<MIcon v-if="config.icon" :icon="config.icon"></MIcon>
|
|
13
|
+
<div v-if="config.text" class="magic-editor-tab-panel-title">{{ config.text }}</div>
|
|
14
|
+
</div>
|
|
12
15
|
</template>
|
|
13
16
|
|
|
14
|
-
<
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
+
<component v-if="config" :is="config.component" v-bind="config.props || {}" v-on="config?.listeners || {}">
|
|
18
|
+
<template
|
|
19
|
+
#component-list-panel-header
|
|
20
|
+
v-if="config.$key === 'component-list' || config.slots?.componentListPanelHeader"
|
|
21
|
+
>
|
|
22
|
+
<slot v-if="config.$key === 'component-list'" name="component-list-panel-header"></slot>
|
|
23
|
+
<component v-else-if="config.slots?.componentListPanelHeader" :is="config.slots.componentListPanelHeader" />
|
|
24
|
+
</template>
|
|
17
25
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
26
|
+
<template
|
|
27
|
+
#component-list-item="{ component }"
|
|
28
|
+
v-if="config.$key === 'component-list' || config.slots?.componentListItem"
|
|
29
|
+
>
|
|
30
|
+
<slot v-if="config.$key === 'component-list'" name="component-list-item" :component="component"></slot>
|
|
31
|
+
<component
|
|
32
|
+
v-else-if="config.slots?.componentListItem"
|
|
33
|
+
:is="config.slots.componentListItem"
|
|
34
|
+
:component="component"
|
|
35
|
+
/>
|
|
36
|
+
</template>
|
|
21
37
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
38
|
+
<template #layer-panel-header v-if="config.$key === 'layer' || config.slots?.layerPanelHeader">
|
|
39
|
+
<slot v-if="config.$key === 'layer'" name="layer-panel-header"></slot>
|
|
40
|
+
<component v-else-if="config.slots?.layerPanelHeader" :is="config.slots.layerPanelHeader" />
|
|
41
|
+
</template>
|
|
25
42
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
43
|
+
<template #code-block-panel-header v-if="config.$key === 'code-block' || config.slots?.codeBlockPanelHeader">
|
|
44
|
+
<slot v-if="config.$key === 'code-block'" name="code-block-panel-header"></slot>
|
|
45
|
+
<component v-else-if="config.slots?.codeBlockPanelHeader" :is="config.slots.codeBlockPanelHeader" />
|
|
46
|
+
</template>
|
|
29
47
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
48
|
+
<template
|
|
49
|
+
#code-block-panel-tool="{ id, data }"
|
|
50
|
+
v-if="config.$key === 'code-block' || config.slots?.codeBlockPanelTool"
|
|
51
|
+
>
|
|
52
|
+
<slot v-if="config.$key === 'code-block'" name="code-block-panel-tool" :id="id" :data="data"></slot>
|
|
53
|
+
<component v-else-if="config.slots?.codeBlockPanelTool" :is="config.slots.codeBlockPanelTool" />
|
|
54
|
+
</template>
|
|
33
55
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
56
|
+
<template
|
|
57
|
+
#code-block-edit-panel-header="{ id }"
|
|
58
|
+
v-if="config.$key === 'code-block' || config.slots?.codeBlockEditPanelHeader"
|
|
59
|
+
>
|
|
60
|
+
<slot v-if="config.$key === 'code-block'" name="code-block-edit-panel-header" :id="id"></slot>
|
|
61
|
+
<component v-else-if="config.slots?.codeBlockEditPanelHeader" :is="config.slots.codeBlockEditPanelHeader" />
|
|
62
|
+
</template>
|
|
63
|
+
|
|
64
|
+
<template
|
|
65
|
+
#layer-node-content="{ data: nodeData, node }"
|
|
66
|
+
v-if="config.$key === 'layer' || config.slots?.layerNodeContent"
|
|
67
|
+
>
|
|
68
|
+
<slot v-if="config.$key === 'layer'" name="layer-node-content" :data="nodeData" :node="node"></slot>
|
|
69
|
+
<component
|
|
70
|
+
v-else-if="config.slots?.layerNodeContent"
|
|
71
|
+
:is="config.slots.layerNodeContent"
|
|
72
|
+
:data="nodeData"
|
|
73
|
+
:node="node"
|
|
74
|
+
/>
|
|
75
|
+
</template>
|
|
76
|
+
</component>
|
|
77
|
+
</component>
|
|
38
78
|
</TMagicTabs>
|
|
39
79
|
</template>
|
|
40
80
|
|
|
41
|
-
<script lang="ts" setup>
|
|
42
|
-
import { ref, watch } from 'vue';
|
|
81
|
+
<script lang="ts" setup name="MEditorSidebar">
|
|
82
|
+
import { computed, ref, watch } from 'vue';
|
|
83
|
+
import { Coin, EditPen, Files } from '@element-plus/icons-vue';
|
|
43
84
|
|
|
44
|
-
import { TMagicTabs } from '@tmagic/design';
|
|
85
|
+
import { getConfig, TMagicTabs } from '@tmagic/design';
|
|
45
86
|
|
|
87
|
+
import MIcon from '../../components/Icon.vue';
|
|
88
|
+
import type { MenuButton, MenuComponent, SideComponent, SideItem } from '../../type';
|
|
46
89
|
import { SideBarData } from '../../type';
|
|
47
90
|
|
|
48
|
-
import
|
|
91
|
+
import CodeBlockList from './code-block/CodeBlockList.vue';
|
|
92
|
+
import ComponentListPanel from './ComponentListPanel.vue';
|
|
93
|
+
import LayerPanel from './LayerPanel.vue';
|
|
49
94
|
|
|
50
95
|
const props = withDefaults(
|
|
51
96
|
defineProps<{
|
|
52
97
|
data?: SideBarData;
|
|
98
|
+
layerContentMenu: (MenuButton | MenuComponent)[];
|
|
53
99
|
}>(),
|
|
54
100
|
{
|
|
55
101
|
data: () => ({ type: 'tabs', status: '组件', items: ['component-list', 'layer', 'code-block'] }),
|
|
56
102
|
},
|
|
57
103
|
);
|
|
58
104
|
|
|
105
|
+
const uiComponent = getConfig('components').tabPane;
|
|
106
|
+
|
|
59
107
|
const activeTabName = ref(props.data?.status);
|
|
60
108
|
|
|
109
|
+
const getItemConfig = (data: SideItem): SideComponent => {
|
|
110
|
+
const map: Record<string, SideComponent> = {
|
|
111
|
+
'component-list': {
|
|
112
|
+
$key: 'component-list',
|
|
113
|
+
type: 'component',
|
|
114
|
+
icon: Coin,
|
|
115
|
+
text: '组件',
|
|
116
|
+
component: ComponentListPanel,
|
|
117
|
+
slots: {},
|
|
118
|
+
},
|
|
119
|
+
layer: {
|
|
120
|
+
$key: 'layer',
|
|
121
|
+
type: 'component',
|
|
122
|
+
icon: Files,
|
|
123
|
+
text: '已选组件',
|
|
124
|
+
props: {
|
|
125
|
+
layerContentMenu: props.layerContentMenu,
|
|
126
|
+
},
|
|
127
|
+
component: LayerPanel,
|
|
128
|
+
slots: {},
|
|
129
|
+
},
|
|
130
|
+
'code-block': {
|
|
131
|
+
$key: 'code-block',
|
|
132
|
+
type: 'component',
|
|
133
|
+
icon: EditPen,
|
|
134
|
+
text: '代码编辑',
|
|
135
|
+
component: CodeBlockList,
|
|
136
|
+
slots: {},
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return typeof data === 'string' ? map[data] : data;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const sideBarItems = computed(() => props.data.items.map((item) => getItemConfig(item)));
|
|
144
|
+
|
|
61
145
|
watch(
|
|
62
146
|
() => props.data.status,
|
|
63
147
|
(status) => {
|