@tmagic/editor 1.2.0-beta.9 → 1.2.1
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 +62 -34
- package/dist/{tmagic-editor.mjs → tmagic-editor.js} +1641 -1266
- package/dist/tmagic-editor.js.map +1 -0
- package/dist/{tmagic-editor.umd.js → tmagic-editor.umd.cjs} +1649 -1270
- 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 +137 -0
- package/src/components/ContentMenu.vue +1 -1
- package/src/components/FunctionEditor.vue +190 -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 +102 -99
- package/src/fields/UISelect.vue +3 -3
- package/src/index.ts +2 -1
- package/src/layouts/AddPageBox.vue +5 -2
- package/src/layouts/CodeEditor.vue +2 -3
- package/src/layouts/Framework.vue +12 -10
- package/src/layouts/NavMenu.vue +11 -8
- package/src/layouts/PropsPanel.vue +12 -9
- package/src/layouts/Resizer.vue +1 -1
- package/src/layouts/sidebar/ComponentListPanel.vue +2 -3
- package/src/layouts/sidebar/LayerMenu.vue +9 -7
- package/src/layouts/sidebar/LayerPanel.vue +130 -178
- package/src/layouts/sidebar/Sidebar.vue +110 -26
- package/src/layouts/sidebar/code-block/CodeBlockEditor.vue +33 -118
- package/src/layouts/sidebar/code-block/CodeBlockList.vue +93 -126
- package/src/layouts/workspace/Breadcrumb.vue +6 -7
- package/src/layouts/workspace/PageBar.vue +3 -3
- package/src/layouts/workspace/PageBarScrollContainer.vue +5 -3
- package/src/layouts/workspace/Stage.vue +36 -18
- package/src/layouts/workspace/ViewerMenu.vue +16 -15
- package/src/layouts/workspace/Workspace.vue +9 -6
- package/src/services/BaseService.ts +11 -2
- package/src/services/codeBlock.ts +136 -146
- package/src/services/componentList.ts +7 -2
- package/src/services/editor.ts +161 -85
- package/src/services/events.ts +7 -2
- package/src/services/history.ts +8 -17
- package/src/services/props.ts +8 -3
- package/src/services/storage.ts +1 -0
- package/src/services/ui.ts +20 -10
- package/src/theme/code-block.scss +46 -23
- package/src/theme/component-list-panel.scss +8 -5
- package/src/type.ts +84 -63
- package/src/utils/config.ts +1 -1
- package/src/utils/editor.ts +17 -3
- package/src/utils/index.ts +2 -1
- package/src/utils/logger.ts +1 -1
- package/src/utils/operator.ts +12 -12
- package/src/utils/props.ts +3 -3
- package/src/utils/scroll-viewer.ts +18 -2
- package/src/utils/stage.ts +10 -9
- package/src/utils/undo-redo.ts +1 -1
- package/types/Editor.vue.d.ts +48 -28
- package/types/components/CodeDraftEditor.vue.d.ts +55 -0
- package/types/components/ContentMenu.vue.d.ts +11 -87
- package/types/components/FunctionEditor.vue.d.ts +60 -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 +24 -6
- package/types/layouts/sidebar/code-block/CodeBlockList.vue.d.ts +11 -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 +25 -8
- package/types/services/BaseService.d.ts +2 -1
- package/types/services/codeBlock.d.ts +56 -47
- package/types/services/componentList.d.ts +1 -0
- package/types/services/editor.d.ts +10 -11
- 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 +24 -24
- package/types/type.d.ts +70 -59
- package/types/utils/editor.d.ts +1 -0
- package/types/utils/index.d.ts +1 -0
- package/types/utils/operator.d.ts +1 -1
- 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,7 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="m-editor-props-panel">
|
|
3
3
|
<slot name="props-panel-header"></slot>
|
|
4
|
-
<
|
|
4
|
+
<MForm
|
|
5
5
|
ref="configForm"
|
|
6
6
|
:class="`m-editor-props-panel ${propsPanelSize}`"
|
|
7
7
|
:popper-class="`m-editor-props-panel-popper ${propsPanelSize}`"
|
|
@@ -9,17 +9,16 @@
|
|
|
9
9
|
:init-values="values"
|
|
10
10
|
:config="curFormConfig"
|
|
11
11
|
@change="submit"
|
|
12
|
-
></
|
|
12
|
+
></MForm>
|
|
13
13
|
</div>
|
|
14
14
|
</template>
|
|
15
15
|
|
|
16
|
-
<script lang="ts" setup>
|
|
17
|
-
import { computed, getCurrentInstance, inject, onMounted, ref, watchEffect } from 'vue';
|
|
16
|
+
<script lang="ts" setup name="MEditorPropsPanel">
|
|
17
|
+
import { computed, getCurrentInstance, inject, onMounted, onUnmounted, ref, watchEffect } from 'vue';
|
|
18
18
|
|
|
19
19
|
import { tMagicMessage } from '@tmagic/design';
|
|
20
|
-
import type { FormValue
|
|
21
|
-
import
|
|
22
|
-
import type StageCore from '@tmagic/stage';
|
|
20
|
+
import type { FormValue } from '@tmagic/form';
|
|
21
|
+
import { MForm } from '@tmagic/form';
|
|
23
22
|
|
|
24
23
|
import type { Services } from '../type';
|
|
25
24
|
|
|
@@ -31,9 +30,9 @@ const configForm = ref<InstanceType<typeof MForm>>();
|
|
|
31
30
|
// ts类型应该是FormConfig, 但是打包时会出错,所以暂时用any
|
|
32
31
|
const curFormConfig = ref<any>([]);
|
|
33
32
|
const services = inject<Services>('services');
|
|
34
|
-
const node = computed(() => services?.editorService.get
|
|
33
|
+
const node = computed(() => services?.editorService.get('node'));
|
|
35
34
|
const propsPanelSize = computed(() => services?.uiService.get('propsPanelSize') || 'small');
|
|
36
|
-
const stage = computed(() => services?.editorService.get
|
|
35
|
+
const stage = computed(() => services?.editorService.get('stage'));
|
|
37
36
|
|
|
38
37
|
const init = async () => {
|
|
39
38
|
if (!node.value) {
|
|
@@ -53,6 +52,10 @@ onMounted(() => {
|
|
|
53
52
|
emit('mounted', internalInstance);
|
|
54
53
|
});
|
|
55
54
|
|
|
55
|
+
onUnmounted(() => {
|
|
56
|
+
services?.propsService.off('props-configs-change', init);
|
|
57
|
+
});
|
|
58
|
+
|
|
56
59
|
watchEffect(() => {
|
|
57
60
|
if (configForm.value && stage.value) {
|
|
58
61
|
configForm.value.formState.stage = stage.value;
|
package/src/layouts/Resizer.vue
CHANGED
|
@@ -38,13 +38,12 @@
|
|
|
38
38
|
</TMagicScrollbar>
|
|
39
39
|
</template>
|
|
40
40
|
|
|
41
|
-
<script lang="ts" setup>
|
|
41
|
+
<script lang="ts" setup name="MEditorComponentListPanel">
|
|
42
42
|
import { computed, inject, ref } from 'vue';
|
|
43
43
|
import { Grid, Search } from '@element-plus/icons-vue';
|
|
44
44
|
import serialize from 'serialize-javascript';
|
|
45
45
|
|
|
46
46
|
import { TMagicCollapse, TMagicCollapseItem, TMagicInput, TMagicScrollbar, TMagicTooltip } from '@tmagic/design';
|
|
47
|
-
import type StageCore from '@tmagic/stage';
|
|
48
47
|
import { removeClassNameByClassName } from '@tmagic/utils';
|
|
49
48
|
|
|
50
49
|
import MIcon from '../../components/Icon.vue';
|
|
@@ -54,7 +53,7 @@ const searchText = ref('');
|
|
|
54
53
|
const services = inject<Services>('services');
|
|
55
54
|
const stageOptions = inject<StageOptions>('stageOptions');
|
|
56
55
|
|
|
57
|
-
const stage = computed(() => services?.editorService.get
|
|
56
|
+
const stage = computed(() => services?.editorService.get('stage'));
|
|
58
57
|
const list = computed(() =>
|
|
59
58
|
services?.componentListService.getList().map((group: ComponentGroup) => ({
|
|
60
59
|
...group,
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<ContentMenu :menu-data="menuData" ref="menu" style="overflow: initial"></ContentMenu>
|
|
3
3
|
</template>
|
|
4
4
|
|
|
5
|
-
<script lang="ts" setup>
|
|
5
|
+
<script lang="ts" setup name="MEditorLayerMenu">
|
|
6
6
|
import { computed, inject, markRaw, ref } from 'vue';
|
|
7
|
-
import {
|
|
7
|
+
import { CopyDocument, Delete, Files, Plus } from '@element-plus/icons-vue';
|
|
8
8
|
|
|
9
9
|
import { NodeType } from '@tmagic/schema';
|
|
10
10
|
|
|
11
11
|
import ContentMenu from '../../components/ContentMenu.vue';
|
|
12
12
|
import type { ComponentGroup, MenuButton, MenuComponent, Services } from '../../type';
|
|
13
13
|
|
|
14
|
+
const props = defineProps<{
|
|
15
|
+
layerContentMenu: (MenuButton | MenuComponent)[];
|
|
16
|
+
}>();
|
|
17
|
+
|
|
14
18
|
const services = inject<Services>('services');
|
|
15
19
|
const menu = ref<InstanceType<typeof ContentMenu>>();
|
|
16
20
|
const node = computed(() => services?.editorService.get('node'));
|
|
@@ -18,8 +22,6 @@ const isRoot = computed(() => node.value?.type === NodeType.ROOT);
|
|
|
18
22
|
const isPage = computed(() => node.value?.type === NodeType.PAGE);
|
|
19
23
|
const componentList = computed(() => services?.componentListService.getList() || []);
|
|
20
24
|
|
|
21
|
-
const layerContentMenu = inject<(MenuComponent | MenuButton)[]>('layerContentMenu', []);
|
|
22
|
-
|
|
23
25
|
const createMenuItems = (group: ComponentGroup): MenuButton[] =>
|
|
24
26
|
group.items.map((component) => ({
|
|
25
27
|
text: component.text,
|
|
@@ -82,7 +84,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() => [
|
|
|
82
84
|
{
|
|
83
85
|
type: 'button',
|
|
84
86
|
text: '复制',
|
|
85
|
-
icon: markRaw(
|
|
87
|
+
icon: markRaw(CopyDocument),
|
|
86
88
|
display: () => !isRoot.value,
|
|
87
89
|
handler: () => {
|
|
88
90
|
node.value && services?.editorService.copy(node.value);
|
|
@@ -97,7 +99,7 @@ const menuData = computed<(MenuButton | MenuComponent)[]>(() => [
|
|
|
97
99
|
node.value && services?.editorService.remove(node.value);
|
|
98
100
|
},
|
|
99
101
|
},
|
|
100
|
-
...layerContentMenu,
|
|
102
|
+
...props.layerContentMenu,
|
|
101
103
|
]);
|
|
102
104
|
|
|
103
105
|
const show = (e: MouseEvent) => {
|
|
@@ -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,68 @@
|
|
|
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
|
-
import
|
|
68
|
+
import { getNodePath, isPage } from '@tmagic/utils';
|
|
78
69
|
|
|
79
|
-
import type { Services } from '../../type';
|
|
70
|
+
import type { MenuButton, MenuComponent, Services } from '../../type';
|
|
80
71
|
import { Layout } from '../../type';
|
|
81
72
|
|
|
82
73
|
import LayerMenu from './LayerMenu.vue';
|
|
83
74
|
|
|
75
|
+
defineProps<{
|
|
76
|
+
layerContentMenu: (MenuButton | MenuComponent)[];
|
|
77
|
+
}>();
|
|
78
|
+
|
|
84
79
|
const throttleTime = 150;
|
|
85
80
|
const services = inject<Services>('services');
|
|
81
|
+
const editorService = services?.editorService;
|
|
82
|
+
|
|
86
83
|
const tree = ref<InstanceType<typeof TMagicTree>>();
|
|
87
84
|
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[]>([]);
|
|
85
|
+
|
|
93
86
|
// 多选选中的组件id数组
|
|
94
|
-
const
|
|
87
|
+
const checkedKeys = ref<Id[]>([]);
|
|
95
88
|
// 是否多选
|
|
96
|
-
const
|
|
97
|
-
// 多选场景 取消选中的那个节点id
|
|
98
|
-
const spliceNodeKey = ref<Id>();
|
|
89
|
+
const isCtrlKeyDown = ref(false);
|
|
99
90
|
const filterText = ref('');
|
|
100
91
|
// 默认展开节点
|
|
101
|
-
const
|
|
92
|
+
const expandedKeys = ref<Id[]>([]);
|
|
93
|
+
const currentNodeKey = ref<Id>();
|
|
94
|
+
// 鼠标是否按下标志,用于高亮状态互斥
|
|
95
|
+
const clicked = ref(false);
|
|
102
96
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
97
|
+
const treeProps = {
|
|
98
|
+
children: 'items',
|
|
99
|
+
disabled: (data: MNode) => Boolean(data.items?.length),
|
|
100
|
+
class: (data: MNode) => {
|
|
101
|
+
if (clicked.value || isPage(data)) return '';
|
|
102
|
+
if (data.id === highlightNode?.value?.id && !checkedKeys.value.includes(data.id)) {
|
|
103
|
+
return 'cus-tree-node-hover';
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const isMultiSelect = computed(() => isCtrlKeyDown.value || checkedKeys.value.length > 1);
|
|
109
|
+
|
|
110
|
+
const nodes = computed(() => editorService?.get('nodes') || []);
|
|
111
|
+
const page = computed(() => editorService?.get('page'));
|
|
112
|
+
const values = computed(() => (page.value ? [page.value] : []));
|
|
113
|
+
// 高亮的节点
|
|
114
|
+
const highlightNode = computed(() => editorService?.get('highlightNode'));
|
|
108
115
|
|
|
109
116
|
// 触发画布单选
|
|
110
117
|
const select = async (data: MNode) => {
|
|
@@ -113,13 +120,13 @@ const select = async (data: MNode) => {
|
|
|
113
120
|
}
|
|
114
121
|
|
|
115
122
|
await editorService?.select(data);
|
|
116
|
-
editorService?.get
|
|
123
|
+
editorService?.get('stage')?.select(data.id);
|
|
117
124
|
};
|
|
118
125
|
|
|
119
126
|
// 触发画布多选
|
|
120
127
|
const multiSelect = async (data: Id[]) => {
|
|
121
128
|
await editorService?.multiSelect(data);
|
|
122
|
-
editorService?.get
|
|
129
|
+
editorService?.get('stage')?.multiSelect(data);
|
|
123
130
|
};
|
|
124
131
|
|
|
125
132
|
// 触发画布高亮
|
|
@@ -128,11 +135,9 @@ const highlight = (data: MNode) => {
|
|
|
128
135
|
throw new Error('没有id');
|
|
129
136
|
}
|
|
130
137
|
editorService?.highlight(data);
|
|
131
|
-
editorService?.get
|
|
138
|
+
editorService?.get('stage')?.highlight(data.id);
|
|
132
139
|
};
|
|
133
140
|
|
|
134
|
-
const expandedKeys = new Map<Id, Id>();
|
|
135
|
-
|
|
136
141
|
// tree方法:拖拽时判定目标节点能否成为拖动目标位置
|
|
137
142
|
const allowDrop = (draggingNode: any, dropNode: any, type: string): boolean => {
|
|
138
143
|
const { data } = dropNode || {};
|
|
@@ -165,27 +170,22 @@ const handleDragEnd = async (e: any) => {
|
|
|
165
170
|
editorService?.update(page);
|
|
166
171
|
};
|
|
167
172
|
|
|
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
173
|
// tree事件:节点被关闭时触发的事件
|
|
180
174
|
const handleCollapse = (data: MNode) => {
|
|
181
|
-
expandedKeys.
|
|
175
|
+
expandedKeys.value = expandedKeys.value.filter((id) => id !== data.id);
|
|
182
176
|
};
|
|
183
177
|
|
|
184
178
|
// tree事件:节点被展开时触发的事件
|
|
185
179
|
const handleExpand = (data: MNode) => {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
180
|
+
if (!page.value) {
|
|
181
|
+
expandedKeys.value = [];
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
expandedKeys.value = union(
|
|
186
|
+
expandedKeys.value,
|
|
187
|
+
getNodePath(data.id, [page.value]).map((node) => node.id),
|
|
188
|
+
);
|
|
189
189
|
};
|
|
190
190
|
|
|
191
191
|
// tree方法:对树节点进行筛选时执行的方法
|
|
@@ -207,89 +207,74 @@ const filterTextChangeHandler = (val: string) => {
|
|
|
207
207
|
tree.value?.filter(val);
|
|
208
208
|
};
|
|
209
209
|
|
|
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
|
-
};
|
|
210
|
+
watch(nodes, (nodes) => {
|
|
211
|
+
const ids = nodes?.map((node) => node.id) || [];
|
|
225
212
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
(nodes) => {
|
|
229
|
-
selectedNodes.value = nodes ?? [];
|
|
230
|
-
},
|
|
231
|
-
);
|
|
213
|
+
const idsLength = ids.length;
|
|
214
|
+
const checkedKeysLength = checkedKeys.value.length;
|
|
232
215
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
216
|
+
if (
|
|
217
|
+
difference(
|
|
218
|
+
idsLength > checkedKeysLength ? ids : checkedKeys.value,
|
|
219
|
+
idsLength > checkedKeysLength ? checkedKeys.value : ids,
|
|
220
|
+
).length
|
|
221
|
+
) {
|
|
222
|
+
tree.value?.setCheckedKeys([], false);
|
|
223
|
+
|
|
224
|
+
[currentNodeKey.value] = ids;
|
|
225
|
+
checkedKeys.value = ids.filter((id) => id !== page.value?.id);
|
|
226
|
+
expandedKeys.value = union(expandedKeys.value, ids);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
watch(isMultiSelect, (isMultiSelect) => {
|
|
231
|
+
if (!isMultiSelect) {
|
|
232
|
+
currentNodeKey.value = editorService?.get('node')?.id;
|
|
233
|
+
tree.value?.setCurrentKey(currentNodeKey.value);
|
|
247
234
|
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const editorServiceRemoveHandler = () => {
|
|
238
|
+
setTimeout(() => {
|
|
239
|
+
tree.value?.getNode(editorService?.get('node')?.id)?.updateChildren();
|
|
240
|
+
}, 0);
|
|
248
241
|
};
|
|
249
242
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if (!parent?.id) return;
|
|
243
|
+
const windowBlurHandler = () => {
|
|
244
|
+
isCtrlKeyDown.value = false;
|
|
245
|
+
};
|
|
254
246
|
|
|
255
|
-
|
|
256
|
-
treeNode?.updateChildren();
|
|
257
|
-
await expandNodes();
|
|
247
|
+
let keycon: KeyController;
|
|
258
248
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
});
|
|
249
|
+
onMounted(() => {
|
|
250
|
+
editorService?.on('remove', editorServiceRemoveHandler);
|
|
262
251
|
|
|
263
|
-
|
|
264
|
-
// 监听模式选择
|
|
265
|
-
const addSelectModeListener = () => {
|
|
252
|
+
keycon = new KeyController();
|
|
266
253
|
const isMac = /mac os x/.test(navigator.userAgent.toLowerCase());
|
|
267
254
|
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
255
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
256
|
+
keycon
|
|
257
|
+
.keydown((e) => {
|
|
258
|
+
if (e.key !== ctrl) {
|
|
259
|
+
isCtrlKeyDown.value = false;
|
|
260
|
+
}
|
|
261
|
+
})
|
|
262
|
+
.keydown(ctrl, () => {
|
|
263
|
+
isCtrlKeyDown.value = true;
|
|
264
|
+
})
|
|
265
|
+
.keyup(ctrl, () => {
|
|
266
|
+
isCtrlKeyDown.value = false;
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
globalThis.addEventListener('blur', windowBlurHandler);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
onUnmounted(() => {
|
|
273
|
+
keycon.destroy();
|
|
274
|
+
|
|
275
|
+
editorService?.off('remove', editorServiceRemoveHandler);
|
|
276
|
+
globalThis.removeEventListener('blur', windowBlurHandler);
|
|
277
|
+
});
|
|
293
278
|
|
|
294
279
|
// 鼠标在组件树移动触发高亮
|
|
295
280
|
const highlightHandler = throttle((data: MNode) => {
|
|
@@ -300,58 +285,25 @@ const toggleClickFlag = () => {
|
|
|
300
285
|
clicked.value = !clicked.value;
|
|
301
286
|
};
|
|
302
287
|
|
|
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
288
|
// 选择节点多选框
|
|
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;
|
|
289
|
+
const checkHandler = (data: MNode, { checkedNodes }: any): void => {
|
|
290
|
+
if (checkedNodes.length > 0) {
|
|
291
|
+
multiSelect(checkedNodes.map((node: MNode) => node.id));
|
|
336
292
|
} else {
|
|
337
|
-
|
|
293
|
+
multiSelect(nodes.value.map((node: MNode) => node.id));
|
|
338
294
|
}
|
|
339
|
-
tree.value?.setCheckedKeys(selectedIds.value);
|
|
340
|
-
multiSelect(selectedIds.value);
|
|
341
295
|
};
|
|
342
296
|
|
|
343
297
|
// 点击节点
|
|
344
298
|
const clickHandler = (data: MNode): void => {
|
|
345
|
-
if (
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
select(data);
|
|
352
|
-
} else {
|
|
353
|
-
multiClickHandler(data);
|
|
299
|
+
if (isCtrlKeyDown.value) {
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
if (services?.uiService.get('uiSelectMode')) {
|
|
303
|
+
document.dispatchEvent(new CustomEvent('ui-select', { detail: data }));
|
|
304
|
+
return;
|
|
354
305
|
}
|
|
306
|
+
select(data);
|
|
355
307
|
};
|
|
356
308
|
|
|
357
309
|
// 右键菜单
|