@tmagic/editor 1.4.14 → 1.4.15
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/tmagic-editor.js +246 -127
- package/dist/tmagic-editor.umd.cjs +244 -125
- package/package.json +9 -9
- package/src/Editor.vue +4 -0
- package/src/components/ContentMenu.vue +8 -1
- package/src/components/TreeNode.vue +7 -0
- package/src/fields/CodeSelect.vue +3 -3
- package/src/fields/DataSourceFieldSelect/FieldSelect.vue +12 -8
- package/src/fields/DataSourceFieldSelect/Index.vue +10 -9
- package/src/fields/DataSourceMethodSelect.vue +8 -8
- package/src/fields/DataSourceSelect.vue +6 -8
- package/src/hooks/use-float-box.ts +52 -12
- package/src/layouts/sidebar/ComponentListPanel.vue +30 -27
- package/src/layouts/sidebar/Sidebar.vue +24 -3
- package/src/layouts/sidebar/layer/LayerPanel.vue +1 -1
- package/src/layouts/sidebar/layer/use-click.ts +9 -0
- package/src/layouts/sidebar/layer/use-drag.ts +28 -4
- package/src/services/editor.ts +13 -2
- package/src/type.ts +5 -0
- package/types/components/TreeNode.vue.d.ts +7 -0
- package/types/hooks/use-float-box.d.ts +1 -1
- package/types/layouts/sidebar/layer/LayerPanel.vue.d.ts +1 -1
- package/types/services/ui.d.ts +2 -0
- package/types/type.d.ts +7 -0
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.4.
|
|
2
|
+
"version": "1.4.15",
|
|
3
3
|
"name": "@tmagic/editor",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": [
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
"moveable": "^0.53.0",
|
|
56
56
|
"serialize-javascript": "^6.0.0",
|
|
57
57
|
"sortablejs": "^1.15.2",
|
|
58
|
-
"@tmagic/dep": "1.4.
|
|
59
|
-
"@tmagic/table": "1.4.
|
|
58
|
+
"@tmagic/dep": "1.4.15",
|
|
59
|
+
"@tmagic/table": "1.4.15"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@types/events": "^3.0.0",
|
|
@@ -78,12 +78,12 @@
|
|
|
78
78
|
"monaco-editor": "^0.48.0",
|
|
79
79
|
"typescript": "*",
|
|
80
80
|
"vue": "^3.4.27",
|
|
81
|
-
"@tmagic/core": "1.4.
|
|
82
|
-
"@tmagic/
|
|
83
|
-
"@tmagic/schema": "1.4.
|
|
84
|
-
"@tmagic/
|
|
85
|
-
"@tmagic/
|
|
86
|
-
"@tmagic/utils": "1.4.
|
|
81
|
+
"@tmagic/core": "1.4.15",
|
|
82
|
+
"@tmagic/form": "1.4.15",
|
|
83
|
+
"@tmagic/schema": "1.4.15",
|
|
84
|
+
"@tmagic/stage": "1.4.15",
|
|
85
|
+
"@tmagic/design": "1.4.15",
|
|
86
|
+
"@tmagic/utils": "1.4.15"
|
|
87
87
|
},
|
|
88
88
|
"peerDependenciesMeta": {
|
|
89
89
|
"typescript": {
|
package/src/Editor.vue
CHANGED
|
@@ -33,6 +33,10 @@
|
|
|
33
33
|
<slot name="layer-node-tool" :data="data"></slot>
|
|
34
34
|
</template>
|
|
35
35
|
|
|
36
|
+
<template #component-list="{ componentGroupList }">
|
|
37
|
+
<slot name="component-list" :component-group-list="componentGroupList"></slot>
|
|
38
|
+
</template>
|
|
39
|
+
|
|
36
40
|
<template #component-list-panel-header>
|
|
37
41
|
<slot name="component-list-panel-header"></slot>
|
|
38
42
|
</template>
|
|
@@ -38,6 +38,8 @@
|
|
|
38
38
|
<script lang="ts" setup>
|
|
39
39
|
import { computed, nextTick, onBeforeUnmount, onMounted, ref } from 'vue';
|
|
40
40
|
|
|
41
|
+
import { useZIndex } from '@tmagic/design';
|
|
42
|
+
|
|
41
43
|
import { MenuButton, MenuComponent } from '@editor/type';
|
|
42
44
|
|
|
43
45
|
import ToolButton from './ToolButton.vue';
|
|
@@ -71,6 +73,8 @@ const buttons = ref<InstanceType<typeof ToolButton>[]>();
|
|
|
71
73
|
const subMenu = ref<any>();
|
|
72
74
|
const visible = ref(false);
|
|
73
75
|
const subMenuData = ref<(MenuButton | MenuComponent)[]>([]);
|
|
76
|
+
const zIndex = useZIndex();
|
|
77
|
+
const curZIndex = ref<number>(0);
|
|
74
78
|
|
|
75
79
|
const menuPosition = ref({
|
|
76
80
|
left: 0,
|
|
@@ -80,6 +84,7 @@ const menuPosition = ref({
|
|
|
80
84
|
const menuStyle = computed(() => ({
|
|
81
85
|
top: `${menuPosition.value.top}px`,
|
|
82
86
|
left: `${menuPosition.value.left}px`,
|
|
87
|
+
zIndex: curZIndex.value,
|
|
83
88
|
}));
|
|
84
89
|
|
|
85
90
|
const contains = (el: HTMLElement) => menu.value?.contains(el) || subMenu.value?.contains(el);
|
|
@@ -127,13 +132,15 @@ const setPosition = (e: { clientY: number; clientX: number }) => {
|
|
|
127
132
|
};
|
|
128
133
|
|
|
129
134
|
const show = (e?: { clientY: number; clientX: number }) => {
|
|
130
|
-
// 加
|
|
135
|
+
// 加setTimeout是以为,如果菜单中的按钮监听的是mouseup,那么菜单显示出来时鼠标如果正好在菜单上就会马上触发按钮的mouseup
|
|
131
136
|
setTimeout(() => {
|
|
132
137
|
visible.value = true;
|
|
133
138
|
|
|
134
139
|
nextTick(() => {
|
|
135
140
|
e && setPosition(e);
|
|
136
141
|
|
|
142
|
+
curZIndex.value = zIndex.nextZIndex();
|
|
143
|
+
|
|
137
144
|
emit('show');
|
|
138
145
|
});
|
|
139
146
|
}, 300);
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
class="m-editor-tree-node"
|
|
5
5
|
:draggable="draggable"
|
|
6
6
|
:data-node-id="data.id"
|
|
7
|
+
:data-parent-id="parent?.id"
|
|
8
|
+
:data-parents-id="parentsId"
|
|
7
9
|
:data-is-container="Array.isArray(data.items)"
|
|
8
10
|
@dragstart="handleDragStart"
|
|
9
11
|
@dragleave="handleDragLeave"
|
|
@@ -40,6 +42,8 @@
|
|
|
40
42
|
v-for="item in data.items"
|
|
41
43
|
:key="item.id"
|
|
42
44
|
:data="item"
|
|
45
|
+
:parent="data"
|
|
46
|
+
:parentsId="[...parentsId, data.id]"
|
|
43
47
|
:node-status-map="nodeStatusMap"
|
|
44
48
|
:indent="indent + 11"
|
|
45
49
|
>
|
|
@@ -91,11 +95,14 @@ const treeEmit = inject<typeof emit>('treeEmit');
|
|
|
91
95
|
const props = withDefaults(
|
|
92
96
|
defineProps<{
|
|
93
97
|
data: TreeNodeData;
|
|
98
|
+
parent?: TreeNodeData;
|
|
99
|
+
parentsId?: Id[];
|
|
94
100
|
nodeStatusMap: Map<Id, LayerNodeStatus>;
|
|
95
101
|
indent?: number;
|
|
96
102
|
}>(),
|
|
97
103
|
{
|
|
98
104
|
indent: 0,
|
|
105
|
+
parentsId: () => [],
|
|
99
106
|
},
|
|
100
107
|
);
|
|
101
108
|
|
|
@@ -72,7 +72,7 @@ const codeConfig = computed(() => ({
|
|
|
72
72
|
{
|
|
73
73
|
type: 'select',
|
|
74
74
|
name: 'codeType',
|
|
75
|
-
span:
|
|
75
|
+
span: 6,
|
|
76
76
|
options: [
|
|
77
77
|
{ value: HookCodeType.CODE, text: '代码块' },
|
|
78
78
|
{ value: HookCodeType.DATA_SOURCE_METHOD, text: '数据源方法' },
|
|
@@ -91,7 +91,7 @@ const codeConfig = computed(() => ({
|
|
|
91
91
|
{
|
|
92
92
|
type: 'code-select-col',
|
|
93
93
|
name: 'codeId',
|
|
94
|
-
span:
|
|
94
|
+
span: 18,
|
|
95
95
|
labelWidth: 0,
|
|
96
96
|
display: (mForm: FormState, { model }: any) => model.codeType !== HookCodeType.DATA_SOURCE_METHOD,
|
|
97
97
|
notEditable: () => !services?.codeBlockService.getEditStatus(),
|
|
@@ -99,7 +99,7 @@ const codeConfig = computed(() => ({
|
|
|
99
99
|
{
|
|
100
100
|
type: 'data-source-method-select',
|
|
101
101
|
name: 'codeId',
|
|
102
|
-
span:
|
|
102
|
+
span: 18,
|
|
103
103
|
labelWidth: 0,
|
|
104
104
|
display: (mForm: FormState, { model }: any) => model.codeType === HookCodeType.DATA_SOURCE_METHOD,
|
|
105
105
|
notEditable: () => !services?.dataSourceService.get('editable'),
|
|
@@ -57,13 +57,11 @@
|
|
|
57
57
|
@change="onChangeHandler"
|
|
58
58
|
></TMagicCascader>
|
|
59
59
|
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
><MIcon :icon="!notEditable ? Edit : View"></MIcon
|
|
66
|
-
></TMagicButton>
|
|
60
|
+
<TMagicTooltip v-if="selectDataSourceId && hasDataSourceSidePanel" :content="notEditable ? '查看' : '编辑'">
|
|
61
|
+
<TMagicButton class="m-fields-select-action-button" :size="size" @click="editHandler(selectDataSourceId)"
|
|
62
|
+
><MIcon :icon="!notEditable ? Edit : View"></MIcon
|
|
63
|
+
></TMagicButton>
|
|
64
|
+
</TMagicTooltip>
|
|
67
65
|
</div>
|
|
68
66
|
</template>
|
|
69
67
|
|
|
@@ -71,7 +69,13 @@
|
|
|
71
69
|
import { computed, inject, ref, watch } from 'vue';
|
|
72
70
|
import { Edit, View } from '@element-plus/icons-vue';
|
|
73
71
|
|
|
74
|
-
import {
|
|
72
|
+
import {
|
|
73
|
+
getConfig as getDesignConfig,
|
|
74
|
+
TMagicButton,
|
|
75
|
+
TMagicCascader,
|
|
76
|
+
TMagicSelect,
|
|
77
|
+
TMagicTooltip,
|
|
78
|
+
} from '@tmagic/design';
|
|
75
79
|
import { type FilterFunction, filterFunction, type FormState, type SelectOption } from '@tmagic/form';
|
|
76
80
|
import { DataSourceFieldType } from '@tmagic/schema';
|
|
77
81
|
import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX } from '@tmagic/utils';
|
|
@@ -26,14 +26,15 @@
|
|
|
26
26
|
@change="onChangeHandler"
|
|
27
27
|
></component>
|
|
28
28
|
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
<TMagicTooltip v-if="config.fieldConfig" :disabled="showDataSourceFieldSelect" content="选择数据源">
|
|
30
|
+
<TMagicButton
|
|
31
|
+
style="margin-left: 5px"
|
|
32
|
+
:type="showDataSourceFieldSelect ? 'primary' : 'default'"
|
|
33
|
+
:size="size"
|
|
34
|
+
@click="showDataSourceFieldSelect = !showDataSourceFieldSelect"
|
|
35
|
+
><MIcon :icon="Coin"></MIcon
|
|
36
|
+
></TMagicButton>
|
|
37
|
+
</TMagicTooltip>
|
|
37
38
|
</div>
|
|
38
39
|
</template>
|
|
39
40
|
|
|
@@ -41,7 +42,7 @@
|
|
|
41
42
|
import { computed, inject, ref, resolveComponent, watch } from 'vue';
|
|
42
43
|
import { Coin } from '@element-plus/icons-vue';
|
|
43
44
|
|
|
44
|
-
import { TMagicButton, tMagicMessage } from '@tmagic/design';
|
|
45
|
+
import { TMagicButton, tMagicMessage, TMagicTooltip } from '@tmagic/design';
|
|
45
46
|
import type { FieldProps, FormState } from '@tmagic/form';
|
|
46
47
|
import { DataSchema } from '@tmagic/schema';
|
|
47
48
|
import { DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX } from '@tmagic/utils';
|
|
@@ -9,14 +9,14 @@
|
|
|
9
9
|
@change="onChangeHandler"
|
|
10
10
|
></MContainer>
|
|
11
11
|
|
|
12
|
-
<
|
|
12
|
+
<TMagicTooltip
|
|
13
13
|
v-if="model[name] && isCustomMethod && hasDataSourceSidePanel"
|
|
14
|
-
|
|
15
|
-
:size="size"
|
|
16
|
-
@click="editCodeHandler"
|
|
14
|
+
:content="notEditable ? '查看' : '编辑'"
|
|
17
15
|
>
|
|
18
|
-
<
|
|
19
|
-
|
|
16
|
+
<TMagicButton class="m-fields-select-action-button" :size="size" @click="editCodeHandler">
|
|
17
|
+
<MIcon :icon="!notEditable ? Edit : View"></MIcon>
|
|
18
|
+
</TMagicButton>
|
|
19
|
+
</TMagicTooltip>
|
|
20
20
|
</div>
|
|
21
21
|
|
|
22
22
|
<CodeParams
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
import { computed, inject, ref } from 'vue';
|
|
36
36
|
import { Edit, View } from '@element-plus/icons-vue';
|
|
37
37
|
|
|
38
|
-
import { TMagicButton } from '@tmagic/design';
|
|
38
|
+
import { TMagicButton, TMagicTooltip } from '@tmagic/design';
|
|
39
39
|
import { createValues, type FieldProps, filterFunction, type FormState, MContainer } from '@tmagic/form';
|
|
40
40
|
import type { Id } from '@tmagic/schema';
|
|
41
41
|
|
|
@@ -91,7 +91,7 @@ const getParamItemsConfig = ([dataSourceId, methodName]: [Id, string] = ['', '']
|
|
|
91
91
|
}));
|
|
92
92
|
};
|
|
93
93
|
|
|
94
|
-
const paramsConfig = ref<CodeParamStatement[]>(getParamItemsConfig(props.model.dataSourceMethod));
|
|
94
|
+
const paramsConfig = ref<CodeParamStatement[]>(getParamItemsConfig(props.model[props.name || 'dataSourceMethod']));
|
|
95
95
|
|
|
96
96
|
const setParamsConfig = (dataSourceMethod: [Id, string], formState: any = {}) => {
|
|
97
97
|
// 通过下拉框选择的codeId变化后修正model的值,避免写入其他codeId的params
|
|
@@ -11,13 +11,11 @@
|
|
|
11
11
|
@change="changeHandler"
|
|
12
12
|
></MSelect>
|
|
13
13
|
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
><MIcon :icon="!notEditable ? Edit : View"></MIcon
|
|
20
|
-
></TMagicButton>
|
|
14
|
+
<TMagicTooltip v-if="model[name] && hasDataSourceSidePanel" :content="notEditable ? '查看' : '编辑'">
|
|
15
|
+
<TMagicButton class="m-fields-select-action-button" :size="size" @click="editHandler"
|
|
16
|
+
><MIcon :icon="!notEditable ? Edit : View"></MIcon
|
|
17
|
+
></TMagicButton>
|
|
18
|
+
</TMagicTooltip>
|
|
21
19
|
</div>
|
|
22
20
|
</template>
|
|
23
21
|
|
|
@@ -25,7 +23,7 @@
|
|
|
25
23
|
import { computed, inject } from 'vue';
|
|
26
24
|
import { Edit, View } from '@element-plus/icons-vue';
|
|
27
25
|
|
|
28
|
-
import { TMagicButton } from '@tmagic/design';
|
|
26
|
+
import { TMagicButton, TMagicTooltip } from '@tmagic/design';
|
|
29
27
|
import { type FieldProps, filterFunction, type FormState, MSelect, type SelectConfig } from '@tmagic/form';
|
|
30
28
|
|
|
31
29
|
import MIcon from '@editor/components/Icon.vue';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { computed, ComputedRef, inject, ref, watch } from 'vue';
|
|
1
|
+
import { computed, ComputedRef, inject, onBeforeUnmount, ref, watch } from 'vue';
|
|
2
2
|
|
|
3
3
|
import type { Services } from '@editor/type';
|
|
4
4
|
|
|
@@ -30,22 +30,62 @@ export const useFloatBox = (slideKeys: ComputedRef<string[]>) => {
|
|
|
30
30
|
const showingBoxKeys = computed(() =>
|
|
31
31
|
Object.keys(floatBoxStates.value).filter((key) => floatBoxStates.value[key].status),
|
|
32
32
|
);
|
|
33
|
-
const isDragging = ref(false);
|
|
34
33
|
|
|
35
|
-
const
|
|
34
|
+
const dragState = {
|
|
35
|
+
startOffset: {
|
|
36
|
+
x: 0,
|
|
37
|
+
y: 0,
|
|
38
|
+
},
|
|
39
|
+
isDragging: false,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const dragstartHandler = (e: DragEvent) => {
|
|
43
|
+
dragState.isDragging = true;
|
|
44
|
+
|
|
45
|
+
dragState.startOffset.x = e.clientX;
|
|
46
|
+
dragState.startOffset.y = e.clientY;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const effectiveDistance = 20;
|
|
50
|
+
|
|
36
51
|
const dragendHandler = (key: string, e: DragEvent) => {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
52
|
+
if (!dragState.isDragging) return;
|
|
53
|
+
|
|
54
|
+
const { startOffset } = dragState;
|
|
55
|
+
|
|
56
|
+
if (
|
|
57
|
+
Math.abs(startOffset.x - e.clientX) > effectiveDistance ||
|
|
58
|
+
Math.abs(startOffset.y - e.clientY) > effectiveDistance
|
|
59
|
+
) {
|
|
60
|
+
const navMenuRect = services?.uiService?.get('navMenuRect');
|
|
61
|
+
floatBoxStates.value[key] = {
|
|
62
|
+
left: e.clientX,
|
|
63
|
+
top: (navMenuRect?.top ?? 0) + (navMenuRect?.height ?? 0),
|
|
64
|
+
status: true,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
dragState.isDragging = false;
|
|
44
69
|
};
|
|
45
70
|
|
|
46
|
-
|
|
47
|
-
if (!isDragging
|
|
71
|
+
const dragoverHandler = (e: DragEvent) => {
|
|
72
|
+
if (!dragState.isDragging) return;
|
|
48
73
|
e.preventDefault();
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const blurHandler = () => {
|
|
77
|
+
dragState.startOffset.x = 0;
|
|
78
|
+
dragState.startOffset.y = 0;
|
|
79
|
+
|
|
80
|
+
dragState.isDragging = false;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
globalThis.document.body.addEventListener('dragover', dragoverHandler);
|
|
84
|
+
globalThis.addEventListener('blur', blurHandler);
|
|
85
|
+
|
|
86
|
+
onBeforeUnmount(() => {
|
|
87
|
+
globalThis.document.body.removeEventListener('dragover', dragoverHandler);
|
|
88
|
+
globalThis.removeEventListener('blur', blurHandler);
|
|
49
89
|
});
|
|
50
90
|
|
|
51
91
|
watch(
|
|
@@ -2,31 +2,34 @@
|
|
|
2
2
|
<TMagicScrollbar>
|
|
3
3
|
<slot name="component-list-panel-header"></slot>
|
|
4
4
|
|
|
5
|
-
<
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
5
|
+
<SearchInput @search="filterTextChangeHandler"></SearchInput>
|
|
6
|
+
|
|
7
|
+
<slot name="component-list" :component-group-list="list">
|
|
8
|
+
<TMagicCollapse class="ui-component-panel" :model-value="collapseValue">
|
|
9
|
+
<template v-for="(group, index) in list">
|
|
10
|
+
<TMagicCollapseItem v-if="group.items && group.items.length" :key="index" :name="`${index}`">
|
|
11
|
+
<template #title><MIcon :icon="Grid"></MIcon>{{ group.title }}</template>
|
|
12
|
+
<div
|
|
13
|
+
v-for="item in group.items"
|
|
14
|
+
class="component-item"
|
|
15
|
+
draggable="true"
|
|
16
|
+
:key="item.type"
|
|
17
|
+
@click="appendComponent(item)"
|
|
18
|
+
@dragstart="dragstartHandler(item, $event)"
|
|
19
|
+
@dragend="dragendHandler"
|
|
20
|
+
@drag="dragHandler"
|
|
21
|
+
>
|
|
22
|
+
<slot name="component-list-item" :component="item">
|
|
23
|
+
<TMagicTooltip placement="right" :disabled="!Boolean(item.desc)" :content="item.desc">
|
|
24
|
+
<MIcon :icon="item.icon"></MIcon>
|
|
25
|
+
</TMagicTooltip>
|
|
26
|
+
<span :title="item.text">{{ item.text }}</span>
|
|
27
|
+
</slot>
|
|
28
|
+
</div>
|
|
29
|
+
</TMagicCollapseItem>
|
|
30
|
+
</template>
|
|
31
|
+
</TMagicCollapse>
|
|
32
|
+
</slot>
|
|
30
33
|
</TMagicScrollbar>
|
|
31
34
|
</template>
|
|
32
35
|
|
|
@@ -65,8 +68,8 @@ const services = inject<Services>('services');
|
|
|
65
68
|
const stageOptions = inject<StageOptions>('stageOptions');
|
|
66
69
|
|
|
67
70
|
const stage = computed(() => services?.editorService.get('stage'));
|
|
68
|
-
const list = computed(() =>
|
|
69
|
-
services?.componentListService.getList().map((group: ComponentGroup) => ({
|
|
71
|
+
const list = computed<ComponentGroup[]>(() =>
|
|
72
|
+
(services?.componentListService.getList() || []).map((group: ComponentGroup) => ({
|
|
70
73
|
...group,
|
|
71
74
|
items: group.items.filter((item: ComponentItem) => item.text.includes(searchText.value)),
|
|
72
75
|
})),
|
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
class="m-editor-sidebar-header-item"
|
|
6
6
|
v-for="(config, index) in sideBarItems"
|
|
7
7
|
v-show="!floatBoxStates[config.$key]?.status"
|
|
8
|
-
draggable="true"
|
|
8
|
+
:draggable="config.draggable ?? true"
|
|
9
9
|
:key="config.$key ?? index"
|
|
10
10
|
:class="{ 'is-active': activeTabName === config.text }"
|
|
11
11
|
:style="config.tabStyle || {}"
|
|
12
|
-
@click="
|
|
12
|
+
@click="headerItemClickHandler(config, index)"
|
|
13
13
|
@dragstart="dragstartHandler"
|
|
14
14
|
@dragend="dragendHandler(config.$key, $event)"
|
|
15
15
|
>
|
|
@@ -24,11 +24,23 @@
|
|
|
24
24
|
v-show="[config.text, config.$key, `${index}`].includes(activeTabName)"
|
|
25
25
|
>
|
|
26
26
|
<component
|
|
27
|
-
v-if="config && !floatBoxStates[config.$key]?.status"
|
|
27
|
+
v-if="config?.component && !floatBoxStates[config.$key]?.status"
|
|
28
28
|
:is="config.component"
|
|
29
29
|
v-bind="config.props || {}"
|
|
30
30
|
v-on="config?.listeners || {}"
|
|
31
31
|
>
|
|
32
|
+
<template
|
|
33
|
+
#component-list="{ componentGroupList }"
|
|
34
|
+
v-if="config.$key === 'component-list' || config.slots?.componentList"
|
|
35
|
+
>
|
|
36
|
+
<slot
|
|
37
|
+
v-if="config.$key === 'component-list'"
|
|
38
|
+
name="component-list"
|
|
39
|
+
:component-group-list="componentGroupList"
|
|
40
|
+
></slot>
|
|
41
|
+
<component v-else-if="config.slots?.componentList" :is="config.slots.componentList" />
|
|
42
|
+
</template>
|
|
43
|
+
|
|
32
44
|
<template
|
|
33
45
|
#component-list-panel-header
|
|
34
46
|
v-if="config.$key === 'component-list' || config.slots?.componentListPanelHeader"
|
|
@@ -272,6 +284,15 @@ watch(
|
|
|
272
284
|
},
|
|
273
285
|
);
|
|
274
286
|
|
|
287
|
+
const headerItemClickHandler = async (config: SideComponent, index: number) => {
|
|
288
|
+
if (typeof config.beforeClick === 'function') {
|
|
289
|
+
if ((await config.beforeClick(config)) === false) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
activeTabName.value = config.text || config.$key || `${index}`;
|
|
294
|
+
};
|
|
295
|
+
|
|
275
296
|
defineExpose({
|
|
276
297
|
activeTabName,
|
|
277
298
|
});
|
|
@@ -53,7 +53,7 @@ import type { MNode } from '@tmagic/schema';
|
|
|
53
53
|
import SearchInput from '@editor/components/SearchInput.vue';
|
|
54
54
|
import Tree from '@editor/components/Tree.vue';
|
|
55
55
|
import { useFilter } from '@editor/hooks/use-filter';
|
|
56
|
-
import { LayerPanelSlots, MenuButton, MenuComponent, Services, TreeNodeData } from '@editor/type';
|
|
56
|
+
import type { LayerPanelSlots, MenuButton, MenuComponent, Services, TreeNodeData } from '@editor/type';
|
|
57
57
|
|
|
58
58
|
import LayerMenu from './LayerMenu.vue';
|
|
59
59
|
import LayerNodeTool from './LayerNodeTool.vue';
|
|
@@ -2,6 +2,7 @@ import { computed, type ComputedRef, nextTick, type Ref, ref } from 'vue';
|
|
|
2
2
|
import { throttle } from 'lodash-es';
|
|
3
3
|
|
|
4
4
|
import { Id, MNode } from '@tmagic/schema';
|
|
5
|
+
import { isPage, isPageFragment } from '@tmagic/utils';
|
|
5
6
|
|
|
6
7
|
import { LayerNodeStatus, Services, TreeNodeData, UI_SELECT_MODE_EVENT_NAME } from '@editor/type';
|
|
7
8
|
import { updateStatus } from '@editor/utils/tree';
|
|
@@ -31,6 +32,10 @@ export const useClick = (
|
|
|
31
32
|
};
|
|
32
33
|
|
|
33
34
|
const multiSelect = async (data: MNode) => {
|
|
35
|
+
if (isPage(data) || isPageFragment(data)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
34
39
|
const nodes = services?.editorService.get('nodes') || [];
|
|
35
40
|
|
|
36
41
|
const newNodes: Id[] = [];
|
|
@@ -41,6 +46,10 @@ export const useClick = (
|
|
|
41
46
|
return;
|
|
42
47
|
}
|
|
43
48
|
|
|
49
|
+
if (isPage(node) || isPageFragment(node)) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
44
53
|
newNodes.push(node.id);
|
|
45
54
|
});
|
|
46
55
|
|
|
@@ -10,6 +10,7 @@ const dragState: {
|
|
|
10
10
|
dragOverNodeId: Id;
|
|
11
11
|
dropType: NodeDropType | '';
|
|
12
12
|
container: HTMLElement | null;
|
|
13
|
+
nodeId?: Id;
|
|
13
14
|
} = {
|
|
14
15
|
dragOverNodeId: '',
|
|
15
16
|
dropType: '',
|
|
@@ -50,6 +51,7 @@ export const useDrag = (services: Services | undefined) => {
|
|
|
50
51
|
if (!targetEl || targetEl !== event.currentTarget) return;
|
|
51
52
|
|
|
52
53
|
event.dataTransfer.effectAllowed = 'move';
|
|
54
|
+
dragState.nodeId = targetEl.dataset.nodeId;
|
|
53
55
|
|
|
54
56
|
try {
|
|
55
57
|
event.dataTransfer.setData(
|
|
@@ -70,30 +72,52 @@ export const useDrag = (services: Services | undefined) => {
|
|
|
70
72
|
const labelEl = targetEl.children[0];
|
|
71
73
|
if (!labelEl) return;
|
|
72
74
|
|
|
75
|
+
removeClassName(labelEl, 'drag-before', 'drag-after', 'drag-inner');
|
|
76
|
+
|
|
73
77
|
const { top: targetTop, height: targetHeight } = labelEl.getBoundingClientRect();
|
|
74
78
|
|
|
75
79
|
const distance = event.clientY - targetTop;
|
|
76
80
|
const isContainer = targetEl.dataset.isContainer === 'true';
|
|
77
81
|
|
|
82
|
+
const targetNodeId = targetEl.dataset.nodeId;
|
|
83
|
+
const { nodeId } = dragState;
|
|
84
|
+
const parentsId = targetEl.dataset.parentsId?.split(',');
|
|
85
|
+
|
|
86
|
+
if (!targetNodeId) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 如果是悬浮在拖动的节点上方,则不响应
|
|
91
|
+
if (parentsId) {
|
|
92
|
+
let targetIdIndex = -1;
|
|
93
|
+
for (let i = 0, l = parentsId.length; i < l; i++) {
|
|
94
|
+
const id = parentsId[i];
|
|
95
|
+
if (nodeId === id) {
|
|
96
|
+
targetIdIndex = i;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (parentsId.includes(`${nodeId}`) && i >= targetIdIndex) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
78
105
|
if (distance < targetHeight / 3) {
|
|
79
106
|
dragState.dropType = 'before';
|
|
80
107
|
addClassName(labelEl, globalThis.document, 'drag-before');
|
|
81
|
-
removeClassName(labelEl, 'drag-after', 'drag-inner');
|
|
82
108
|
} else if (distance > (targetHeight * 2) / 3) {
|
|
83
109
|
dragState.dropType = 'after';
|
|
84
110
|
addClassName(labelEl, globalThis.document, 'drag-after');
|
|
85
|
-
removeClassName(labelEl, 'drag-before', 'drag-inner');
|
|
86
111
|
} else if (isContainer) {
|
|
87
112
|
dragState.dropType = 'inner';
|
|
88
113
|
addClassName(labelEl, globalThis.document, 'drag-inner');
|
|
89
|
-
removeClassName(labelEl, 'drag-before', 'drag-after');
|
|
90
114
|
}
|
|
91
115
|
|
|
92
116
|
if (!dragState.dropType) {
|
|
93
117
|
return;
|
|
94
118
|
}
|
|
95
119
|
|
|
96
|
-
dragState.dragOverNodeId =
|
|
120
|
+
dragState.dragOverNodeId = targetNodeId;
|
|
97
121
|
dragState.container = event.currentTarget as HTMLElement;
|
|
98
122
|
|
|
99
123
|
event.preventDefault();
|
package/src/services/editor.ts
CHANGED
|
@@ -899,9 +899,20 @@ class Editor extends BaseService {
|
|
|
899
899
|
|
|
900
900
|
const newLayout = await this.getLayout(targetParent);
|
|
901
901
|
|
|
902
|
-
|
|
902
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
903
|
+
forConfigs: for (const config of configs) {
|
|
903
904
|
const { parent, node: curNode } = this.getNodeInfo(config.id, false);
|
|
904
|
-
if (!parent || !curNode)
|
|
905
|
+
if (!parent || !curNode) {
|
|
906
|
+
continue;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
const path = getNodePath(curNode.id, parent.items);
|
|
910
|
+
|
|
911
|
+
for (const node of path) {
|
|
912
|
+
if (targetParent.id === node.id) {
|
|
913
|
+
continue forConfigs;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
905
916
|
|
|
906
917
|
const index = getNodeIndex(curNode.id, parent);
|
|
907
918
|
|
package/src/type.ts
CHANGED
|
@@ -81,6 +81,7 @@ export interface WorkspaceSlots {
|
|
|
81
81
|
|
|
82
82
|
export interface ComponentListPanelSlots {
|
|
83
83
|
'component-list-panel-header'(props: {}): any;
|
|
84
|
+
'component-list'(props: { componentGroupList: ComponentGroup[] }): any;
|
|
84
85
|
'component-list-item'(props: { component: ComponentItem }): any;
|
|
85
86
|
}
|
|
86
87
|
|
|
@@ -384,6 +385,10 @@ export interface SideComponent extends MenuComponent {
|
|
|
384
385
|
icon?: any;
|
|
385
386
|
/** slide 唯一标识 key */
|
|
386
387
|
$key: string;
|
|
388
|
+
/** 是否可以将面板拖出,默认为true */
|
|
389
|
+
draggable?: boolean;
|
|
390
|
+
/** 点击切换tab前调用,返回false阻止切换 */
|
|
391
|
+
beforeClick?: (config: SideComponent) => boolean | Promise<boolean>;
|
|
387
392
|
|
|
388
393
|
/** 组件扩展参数 */
|
|
389
394
|
boxComponentConfig?: {
|