@tmagic/editor 1.5.0-beta.9 → 1.5.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 → tmagic-editor.css} +228 -22
- package/dist/tmagic-editor.js +1670 -1012
- package/dist/tmagic-editor.umd.cjs +1705 -1048
- package/package.json +14 -12
- package/src/Editor.vue +24 -6
- package/src/components/CodeBlockEditor.vue +14 -8
- package/src/components/CodeParams.vue +5 -5
- package/src/components/ContentMenu.vue +18 -18
- package/src/components/FloatingBox.vue +3 -3
- package/src/components/Resizer.vue +4 -4
- package/src/components/ScrollBar.vue +3 -3
- package/src/components/ScrollViewer.vue +3 -3
- package/src/components/SplitView.vue +2 -2
- package/src/components/ToolButton.vue +2 -1
- package/src/editorProps.ts +6 -2
- package/src/fields/Code.vue +1 -2
- package/src/fields/CodeSelect.vue +13 -11
- package/src/fields/CodeSelectCol.vue +32 -5
- package/src/fields/CondOpSelect.vue +5 -2
- package/src/fields/DataSourceFields.vue +31 -12
- package/src/fields/DataSourceInput.vue +2 -2
- package/src/fields/DataSourceMethods.vue +72 -14
- package/src/fields/DataSourceMocks.vue +0 -1
- package/src/fields/DisplayConds.vue +8 -3
- package/src/fields/EventSelect.vue +28 -12
- package/src/fields/KeyValue.vue +17 -12
- package/src/fields/UISelect.vue +6 -3
- package/src/hooks/index.ts +0 -1
- package/src/hooks/use-code-block-edit.ts +3 -3
- package/src/hooks/use-data-source-edit.ts +3 -2
- package/src/hooks/use-filter.ts +1 -1
- package/src/hooks/use-getso.ts +7 -7
- package/src/hooks/use-node-status.ts +2 -2
- package/src/index.ts +2 -1
- package/src/initService.ts +177 -74
- package/src/layouts/CodeEditor.vue +2 -2
- package/src/layouts/Framework.vue +13 -12
- package/src/layouts/NavMenu.vue +2 -2
- package/src/layouts/page-bar/AddButton.vue +29 -9
- package/src/layouts/page-bar/PageBar.vue +79 -66
- package/src/layouts/page-bar/PageBarScrollContainer.vue +84 -98
- package/src/layouts/page-bar/PageList.vue +5 -3
- package/src/layouts/page-bar/Search.vue +67 -0
- package/src/layouts/props-panel/FormPanel.vue +123 -0
- package/src/layouts/props-panel/PropsPanel.vue +146 -0
- package/src/layouts/props-panel/use-style-panel.ts +29 -0
- package/src/layouts/sidebar/Sidebar.vue +7 -1
- package/src/layouts/sidebar/code-block/CodeBlockList.vue +18 -2
- package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +35 -4
- package/src/layouts/sidebar/code-block/useContentMenu.ts +83 -0
- package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +7 -5
- package/src/layouts/sidebar/data-source/DataSourceList.vue +13 -8
- package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +40 -4
- package/src/layouts/sidebar/data-source/useContentMenu.ts +81 -0
- package/src/layouts/sidebar/layer/LayerMenu.vue +7 -13
- package/src/layouts/sidebar/layer/LayerPanel.vue +11 -4
- package/src/layouts/sidebar/layer/use-click.ts +2 -2
- package/src/layouts/sidebar/layer/use-keybinding.ts +2 -2
- package/src/layouts/sidebar/layer/use-node-status.ts +2 -3
- package/src/layouts/workspace/Workspace.vue +13 -3
- package/src/layouts/workspace/viewer/NodeListMenu.vue +3 -3
- package/src/layouts/workspace/viewer/Stage.vue +41 -11
- package/src/layouts/workspace/viewer/StageOverlay.vue +2 -2
- package/src/layouts/workspace/viewer/ViewerMenu.vue +4 -6
- package/src/services/dataSource.ts +12 -5
- package/src/services/dep.ts +61 -13
- package/src/services/editor.ts +46 -51
- package/src/services/storage.ts +2 -1
- package/src/services/ui.ts +1 -0
- package/src/theme/common/var.scss +12 -10
- package/src/theme/component-list-panel.scss +9 -7
- package/src/theme/content-menu.scss +7 -5
- package/src/theme/data-source.scss +3 -1
- package/src/theme/floating-box.scss +4 -2
- package/src/theme/framework.scss +7 -5
- package/src/theme/index.scss +4 -5
- package/src/theme/key-value.scss +2 -2
- package/src/theme/layer-panel.scss +3 -1
- package/src/theme/layout.scss +1 -1
- package/src/theme/nav-menu.scss +7 -5
- package/src/theme/page-bar.scss +51 -27
- package/src/theme/props-panel.scss +81 -1
- package/src/theme/resizer.scss +1 -1
- package/src/theme/search-input.scss +1 -0
- package/src/theme/sidebar.scss +4 -2
- package/src/theme/stage.scss +3 -1
- package/src/theme/theme.scss +28 -28
- package/src/theme/tree.scss +14 -12
- package/src/type.ts +10 -1
- package/src/utils/content-menu.ts +2 -2
- package/src/utils/data-source/formConfigs/http.ts +2 -0
- package/src/utils/data-source/index.ts +2 -2
- package/src/utils/editor.ts +78 -22
- package/src/utils/idle-task.ts +36 -4
- package/src/utils/props.ts +48 -6
- package/types/index.d.ts +2312 -2080
- package/src/hooks/use-data-source-method.ts +0 -100
- package/src/layouts/PropsPanel.vue +0 -131
- package/src/layouts/page-bar/SwitchTypeButton.vue +0 -45
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { inject, markRaw, useTemplateRef } from 'vue';
|
|
2
|
+
import { CopyDocument, Delete, Edit } from '@element-plus/icons-vue';
|
|
3
|
+
import { cloneDeep } from 'lodash-es';
|
|
4
|
+
|
|
5
|
+
import ContentMenu from '@editor/components/ContentMenu.vue';
|
|
6
|
+
import type { EventBus, MenuButton, MenuComponent, Services, TreeNodeData } from '@editor/type';
|
|
7
|
+
|
|
8
|
+
export const useContentMenu = () => {
|
|
9
|
+
const eventBus = inject<EventBus>('eventBus');
|
|
10
|
+
const menuRef = useTemplateRef<InstanceType<typeof ContentMenu>>('menu');
|
|
11
|
+
|
|
12
|
+
let selectId = '';
|
|
13
|
+
|
|
14
|
+
const menuData: (MenuButton | MenuComponent)[] = [
|
|
15
|
+
{
|
|
16
|
+
type: 'button',
|
|
17
|
+
text: '编辑',
|
|
18
|
+
icon: Edit,
|
|
19
|
+
display: (services) => services?.dataSourceService?.get('editable') ?? true,
|
|
20
|
+
handler: () => {
|
|
21
|
+
if (!selectId) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
eventBus?.emit('edit-data-source', selectId);
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type: 'button',
|
|
30
|
+
text: '复制并粘贴至当前',
|
|
31
|
+
icon: markRaw(CopyDocument),
|
|
32
|
+
handler: ({ dataSourceService }: Services) => {
|
|
33
|
+
if (!selectId) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const ds = dataSourceService.getDataSourceById(selectId);
|
|
38
|
+
if (!ds) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
dataSourceService.add(cloneDeep(ds));
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
type: 'button',
|
|
47
|
+
text: '删除',
|
|
48
|
+
icon: Delete,
|
|
49
|
+
handler: () => {
|
|
50
|
+
if (!selectId) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
eventBus?.emit('remove-data-source', selectId);
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
const nodeContentMenuHandler = (event: MouseEvent, data: TreeNodeData) => {
|
|
60
|
+
event.preventDefault();
|
|
61
|
+
|
|
62
|
+
if (data.type === 'ds') {
|
|
63
|
+
menuRef.value?.show(event);
|
|
64
|
+
if (data.id) {
|
|
65
|
+
selectId = `${data.id}`;
|
|
66
|
+
} else {
|
|
67
|
+
selectId = '';
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const contentMenuHideHandler = () => {
|
|
73
|
+
selectId = '';
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
menuData,
|
|
78
|
+
nodeContentMenuHandler,
|
|
79
|
+
contentMenuHideHandler,
|
|
80
|
+
};
|
|
81
|
+
};
|
|
@@ -3,37 +3,31 @@
|
|
|
3
3
|
</template>
|
|
4
4
|
|
|
5
5
|
<script lang="ts" setup>
|
|
6
|
-
import { computed, inject, markRaw,
|
|
6
|
+
import { computed, inject, markRaw, useTemplateRef } from 'vue';
|
|
7
7
|
import { Files, Plus } from '@element-plus/icons-vue';
|
|
8
8
|
|
|
9
9
|
import { isPage, isPageFragment } from '@tmagic/utils';
|
|
10
10
|
|
|
11
11
|
import ContentMenu from '@editor/components/ContentMenu.vue';
|
|
12
12
|
import FolderMinusIcon from '@editor/icons/FolderMinusIcon.vue';
|
|
13
|
-
import type { ComponentGroup, MenuButton, MenuComponent, Services } from '@editor/type';
|
|
13
|
+
import type { ComponentGroup, CustomContentMenuFunction, MenuButton, MenuComponent, Services } from '@editor/type';
|
|
14
14
|
import { useCopyMenu, useDeleteMenu, useMoveToMenu, usePasteMenu } from '@editor/utils/content-menu';
|
|
15
15
|
|
|
16
16
|
defineOptions({
|
|
17
17
|
name: 'MEditorLayerMenu',
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
const props =
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}>(),
|
|
25
|
-
{
|
|
26
|
-
layerContentMenu: () => [],
|
|
27
|
-
customContentMenu: (menus: (MenuButton | MenuComponent)[]) => menus,
|
|
28
|
-
},
|
|
29
|
-
);
|
|
20
|
+
const props = defineProps<{
|
|
21
|
+
layerContentMenu: (MenuButton | MenuComponent)[];
|
|
22
|
+
customContentMenu: CustomContentMenuFunction;
|
|
23
|
+
}>();
|
|
30
24
|
|
|
31
25
|
const emit = defineEmits<{
|
|
32
26
|
'collapse-all': [];
|
|
33
27
|
}>();
|
|
34
28
|
|
|
35
29
|
const services = inject<Services>('services');
|
|
36
|
-
const menu =
|
|
30
|
+
const menu = useTemplateRef<InstanceType<typeof ContentMenu>>('menu');
|
|
37
31
|
const node = computed(() => services?.editorService.get('node'));
|
|
38
32
|
const nodes = computed(() => services?.editorService.get('nodes'));
|
|
39
33
|
const componentList = computed(() => services?.componentListService.getList() || []);
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
</template>
|
|
48
48
|
|
|
49
49
|
<script setup lang="ts">
|
|
50
|
-
import { computed, inject,
|
|
50
|
+
import { computed, inject, useTemplateRef } from 'vue';
|
|
51
51
|
|
|
52
52
|
import type { MNode } from '@tmagic/core';
|
|
53
53
|
import { TMagicScrollbar } from '@tmagic/design';
|
|
@@ -55,7 +55,14 @@ import { TMagicScrollbar } from '@tmagic/design';
|
|
|
55
55
|
import SearchInput from '@editor/components/SearchInput.vue';
|
|
56
56
|
import Tree from '@editor/components/Tree.vue';
|
|
57
57
|
import { useFilter } from '@editor/hooks/use-filter';
|
|
58
|
-
import type {
|
|
58
|
+
import type {
|
|
59
|
+
CustomContentMenuFunction,
|
|
60
|
+
LayerPanelSlots,
|
|
61
|
+
MenuButton,
|
|
62
|
+
MenuComponent,
|
|
63
|
+
Services,
|
|
64
|
+
TreeNodeData,
|
|
65
|
+
} from '@editor/type';
|
|
59
66
|
|
|
60
67
|
import LayerMenu from './LayerMenu.vue';
|
|
61
68
|
import LayerNodeTool from './LayerNodeTool.vue';
|
|
@@ -74,13 +81,13 @@ defineProps<{
|
|
|
74
81
|
layerContentMenu: (MenuButton | MenuComponent)[];
|
|
75
82
|
indent?: number;
|
|
76
83
|
nextLevelIndentIncrement?: number;
|
|
77
|
-
customContentMenu
|
|
84
|
+
customContentMenu: CustomContentMenuFunction;
|
|
78
85
|
}>();
|
|
79
86
|
|
|
80
87
|
const services = inject<Services>('services');
|
|
81
88
|
const editorService = services?.editorService;
|
|
82
89
|
|
|
83
|
-
const tree =
|
|
90
|
+
const tree = useTemplateRef<InstanceType<typeof Tree>>('tree');
|
|
84
91
|
|
|
85
92
|
const page = computed(() => editorService?.get('page'));
|
|
86
93
|
const nodeData = computed<TreeNodeData[]>(() => (!page.value ? [] : [page.value]));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { computed, type ComputedRef, nextTick, type Ref,
|
|
1
|
+
import { computed, type ComputedRef, nextTick, type Ref, useTemplateRef } from 'vue';
|
|
2
2
|
import { throttle } from 'lodash-es';
|
|
3
3
|
|
|
4
4
|
import { Id, MNode } from '@tmagic/core';
|
|
@@ -99,7 +99,7 @@ export const useClick = (
|
|
|
99
99
|
};
|
|
100
100
|
|
|
101
101
|
// 右键菜单
|
|
102
|
-
const menu =
|
|
102
|
+
const menu = useTemplateRef<InstanceType<typeof LayerMenu>>('menu');
|
|
103
103
|
|
|
104
104
|
return {
|
|
105
105
|
menu,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { ref, type ShallowRef, watchEffect } from 'vue';
|
|
2
2
|
|
|
3
3
|
import Tree from '@editor/components/Tree.vue';
|
|
4
4
|
import type { Services } from '@editor/type';
|
|
@@ -6,7 +6,7 @@ import { KeyBindingContainerKey } from '@editor/utils/keybinding-config';
|
|
|
6
6
|
|
|
7
7
|
export const useKeybinding = (
|
|
8
8
|
services: Services | undefined,
|
|
9
|
-
container:
|
|
9
|
+
container: ShallowRef<InstanceType<typeof Tree> | null>,
|
|
10
10
|
) => {
|
|
11
11
|
const keybindingService = services?.keybindingService;
|
|
12
12
|
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { computed, ref, watch } from 'vue';
|
|
2
2
|
|
|
3
3
|
import type { Id, MNode, MPage, MPageFragment } from '@tmagic/core';
|
|
4
|
-
import { getNodePath, isPage, isPageFragment } from '@tmagic/utils';
|
|
4
|
+
import { getNodePath, isPage, isPageFragment, traverseNode } from '@tmagic/utils';
|
|
5
5
|
|
|
6
|
-
import { LayerNodeStatus, Services } from '@editor/type';
|
|
7
|
-
import { traverseNode } from '@editor/utils';
|
|
6
|
+
import type { LayerNodeStatus, Services } from '@editor/type';
|
|
8
7
|
import { updateStatus } from '@editor/utils/tree';
|
|
9
8
|
|
|
10
9
|
const createPageNodeStatus = (page: MPage | MPageFragment, initialLayerNodeStatus?: Map<Id, LayerNodeStatus>) => {
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
<slot name="stage">
|
|
6
6
|
<MagicStage
|
|
7
|
-
v-if="page"
|
|
7
|
+
v-if="page && (stageOptions?.render || stageOptions?.runtimeUrl)"
|
|
8
|
+
:stage-options="stageOptions"
|
|
8
9
|
:disabled-stage-overlay="disabledStageOverlay"
|
|
9
10
|
:stage-content-menu="stageContentMenu"
|
|
10
11
|
:custom-content-menu="customContentMenu"
|
|
@@ -18,7 +19,14 @@
|
|
|
18
19
|
<script lang="ts" setup>
|
|
19
20
|
import { computed, inject } from 'vue';
|
|
20
21
|
|
|
21
|
-
import type {
|
|
22
|
+
import type {
|
|
23
|
+
CustomContentMenuFunction,
|
|
24
|
+
MenuButton,
|
|
25
|
+
MenuComponent,
|
|
26
|
+
Services,
|
|
27
|
+
StageOptions,
|
|
28
|
+
WorkspaceSlots,
|
|
29
|
+
} from '@editor/type';
|
|
22
30
|
|
|
23
31
|
import MagicStage from './viewer/Stage.vue';
|
|
24
32
|
import Breadcrumb from './Breadcrumb.vue';
|
|
@@ -33,13 +41,15 @@ withDefaults(
|
|
|
33
41
|
defineProps<{
|
|
34
42
|
stageContentMenu: (MenuButton | MenuComponent)[];
|
|
35
43
|
disabledStageOverlay?: boolean;
|
|
36
|
-
customContentMenu
|
|
44
|
+
customContentMenu: CustomContentMenuFunction;
|
|
37
45
|
}>(),
|
|
38
46
|
{
|
|
39
47
|
disabledStageOverlay: false,
|
|
40
48
|
},
|
|
41
49
|
);
|
|
42
50
|
|
|
51
|
+
const stageOptions = inject<StageOptions>('stageOptions');
|
|
52
|
+
|
|
43
53
|
const services = inject<Services>('services');
|
|
44
54
|
|
|
45
55
|
const page = computed(() => services?.editorService.get('page'));
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
</template>
|
|
23
23
|
|
|
24
24
|
<script lang="ts" setup>
|
|
25
|
-
import { computed, inject, nextTick, ref, watch } from 'vue';
|
|
25
|
+
import { computed, inject, nextTick, ref, useTemplateRef, watch } from 'vue';
|
|
26
26
|
|
|
27
27
|
import type { MNode } from '@tmagic/core';
|
|
28
28
|
import { TMagicTooltip } from '@tmagic/design';
|
|
@@ -39,8 +39,8 @@ const editorService = services?.editorService;
|
|
|
39
39
|
|
|
40
40
|
const visible = ref(false);
|
|
41
41
|
const buttonVisible = ref(false);
|
|
42
|
-
const button =
|
|
43
|
-
const box =
|
|
42
|
+
const button = useTemplateRef<HTMLDivElement>('button');
|
|
43
|
+
const box = useTemplateRef<InstanceType<typeof FloatingBox>>('box');
|
|
44
44
|
|
|
45
45
|
const stage = computed(() => editorService?.get('stage'));
|
|
46
46
|
const page = computed(() => editorService?.get('page'));
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
class="m-editor-stage"
|
|
4
4
|
ref="stageWrap"
|
|
5
5
|
tabindex="-1"
|
|
6
|
+
v-loading="stageLoading"
|
|
7
|
+
element-loading-text="Runtime 加载中..."
|
|
6
8
|
:width="stageRect?.width"
|
|
7
9
|
:height="stageRect?.height"
|
|
8
10
|
:wrap-width="stageContainerRect?.width"
|
|
@@ -41,7 +43,18 @@
|
|
|
41
43
|
</template>
|
|
42
44
|
|
|
43
45
|
<script lang="ts" setup>
|
|
44
|
-
import {
|
|
46
|
+
import {
|
|
47
|
+
computed,
|
|
48
|
+
inject,
|
|
49
|
+
markRaw,
|
|
50
|
+
nextTick,
|
|
51
|
+
onBeforeUnmount,
|
|
52
|
+
onMounted,
|
|
53
|
+
toRaw,
|
|
54
|
+
useTemplateRef,
|
|
55
|
+
watch,
|
|
56
|
+
watchEffect,
|
|
57
|
+
} from 'vue';
|
|
45
58
|
import { cloneDeep } from 'lodash-es';
|
|
46
59
|
|
|
47
60
|
import type { MApp, MContainer } from '@tmagic/core';
|
|
@@ -50,7 +63,8 @@ import { calcValueByFontsize, getIdFromEl } from '@tmagic/utils';
|
|
|
50
63
|
|
|
51
64
|
import ScrollViewer from '@editor/components/ScrollViewer.vue';
|
|
52
65
|
import { useStage } from '@editor/hooks/use-stage';
|
|
53
|
-
import {
|
|
66
|
+
import type { CustomContentMenuFunction, MenuButton, MenuComponent, Services, StageOptions } from '@editor/type';
|
|
67
|
+
import { DragType, Layout } from '@editor/type';
|
|
54
68
|
import { getEditorConfig } from '@editor/utils/config';
|
|
55
69
|
import { KeyBindingContainerKey } from '@editor/utils/keybinding-config';
|
|
56
70
|
|
|
@@ -62,11 +76,12 @@ defineOptions({
|
|
|
62
76
|
name: 'MEditorStage',
|
|
63
77
|
});
|
|
64
78
|
|
|
65
|
-
withDefaults(
|
|
79
|
+
const props = withDefaults(
|
|
66
80
|
defineProps<{
|
|
81
|
+
stageOptions: StageOptions;
|
|
67
82
|
stageContentMenu: (MenuButton | MenuComponent)[];
|
|
68
83
|
disabledStageOverlay?: boolean;
|
|
69
|
-
customContentMenu
|
|
84
|
+
customContentMenu: CustomContentMenuFunction;
|
|
70
85
|
}>(),
|
|
71
86
|
{
|
|
72
87
|
disabledStageOverlay: false,
|
|
@@ -77,11 +92,12 @@ let stage: StageCore | null = null;
|
|
|
77
92
|
let runtime: Runtime | null = null;
|
|
78
93
|
|
|
79
94
|
const services = inject<Services>('services');
|
|
80
|
-
const stageOptions = inject<StageOptions>('stageOptions');
|
|
81
95
|
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
const
|
|
96
|
+
const stageLoading = computed(() => services?.editorService.get('stageLoading') || false);
|
|
97
|
+
|
|
98
|
+
const stageWrap = useTemplateRef<InstanceType<typeof ScrollViewer>>('stageWrap');
|
|
99
|
+
const stageContainer = useTemplateRef<HTMLDivElement>('stageContainer');
|
|
100
|
+
const menu = useTemplateRef<InstanceType<typeof ViewerMenu>>('menu');
|
|
85
101
|
|
|
86
102
|
const nodes = computed(() => services?.editorService.get('nodes') || []);
|
|
87
103
|
const isMultiSelect = computed(() => nodes.value.length > 1);
|
|
@@ -96,9 +112,9 @@ watchEffect(() => {
|
|
|
96
112
|
if (stage || !page.value) return;
|
|
97
113
|
|
|
98
114
|
if (!stageContainer.value) return;
|
|
99
|
-
if (!(stageOptions?.runtimeUrl || stageOptions?.render) || !root.value) return;
|
|
115
|
+
if (!(props.stageOptions?.runtimeUrl || props.stageOptions?.render) || !root.value) return;
|
|
100
116
|
|
|
101
|
-
stage = useStage(stageOptions);
|
|
117
|
+
stage = useStage(props.stageOptions);
|
|
102
118
|
|
|
103
119
|
stage.on('select', () => {
|
|
104
120
|
stageWrap.value?.container?.focus();
|
|
@@ -125,6 +141,7 @@ watchEffect(() => {
|
|
|
125
141
|
|
|
126
142
|
onBeforeUnmount(() => {
|
|
127
143
|
stage?.destroy();
|
|
144
|
+
services?.editorService.set('stage', null);
|
|
128
145
|
});
|
|
129
146
|
|
|
130
147
|
watch(zoom, (zoom) => {
|
|
@@ -132,9 +149,20 @@ watch(zoom, (zoom) => {
|
|
|
132
149
|
stage.setZoom(zoom);
|
|
133
150
|
});
|
|
134
151
|
|
|
152
|
+
let timeoutId: NodeJS.Timeout | null = null;
|
|
135
153
|
watch(page, (page) => {
|
|
136
154
|
if (runtime && page) {
|
|
137
155
|
services?.editorService.set('stageLoading', true);
|
|
156
|
+
|
|
157
|
+
if (timeoutId) {
|
|
158
|
+
globalThis.clearTimeout(timeoutId);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
timeoutId = globalThis.setTimeout(() => {
|
|
162
|
+
services?.editorService.set('stageLoading', false);
|
|
163
|
+
timeoutId = null;
|
|
164
|
+
}, 3000);
|
|
165
|
+
|
|
138
166
|
runtime.updatePageId?.(page.id);
|
|
139
167
|
nextTick(() => {
|
|
140
168
|
stage?.select(page.id);
|
|
@@ -201,7 +229,9 @@ const dropHandler = async (e: DragEvent) => {
|
|
|
201
229
|
e.preventDefault();
|
|
202
230
|
|
|
203
231
|
const doc = stage?.renderer?.contentWindow?.document;
|
|
204
|
-
const parentEl: HTMLElement | null | undefined = doc?.querySelector(
|
|
232
|
+
const parentEl: HTMLElement | null | undefined = doc?.querySelector(
|
|
233
|
+
`.${props.stageOptions?.containerHighlightClassName}`,
|
|
234
|
+
);
|
|
205
235
|
|
|
206
236
|
let parent: MContainer | undefined | null = page.value;
|
|
207
237
|
const parentId = getIdFromEl()(parentEl);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
</template>
|
|
9
9
|
|
|
10
10
|
<script setup lang="ts">
|
|
11
|
-
import { computed, inject, onBeforeUnmount,
|
|
11
|
+
import { computed, inject, onBeforeUnmount, useTemplateRef, watch } from 'vue';
|
|
12
12
|
import { CloseBold } from '@element-plus/icons-vue';
|
|
13
13
|
|
|
14
14
|
import { TMagicIcon } from '@tmagic/design';
|
|
@@ -19,7 +19,7 @@ const services = inject<Services>('services');
|
|
|
19
19
|
|
|
20
20
|
const stageOptions = inject<StageOptions>('stageOptions');
|
|
21
21
|
|
|
22
|
-
const stageOverlay =
|
|
22
|
+
const stageOverlay = useTemplateRef<HTMLDivElement>('stageOverlay');
|
|
23
23
|
|
|
24
24
|
const stageOverlayVisible = computed(() => services?.stageOverlayService.get('stageOverlayVisible'));
|
|
25
25
|
const wrapWidth = computed(() => services?.stageOverlayService.get('wrapWidth') || 0);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
</template>
|
|
4
4
|
|
|
5
5
|
<script lang="ts" setup>
|
|
6
|
-
import { computed, inject, markRaw, ref, watch } from 'vue';
|
|
6
|
+
import { computed, inject, markRaw, ref, useTemplateRef, watch } from 'vue';
|
|
7
7
|
import { Bottom, Top } from '@element-plus/icons-vue';
|
|
8
8
|
|
|
9
9
|
import { NodeType } from '@tmagic/core';
|
|
@@ -11,7 +11,7 @@ import { isPage, isPageFragment } from '@tmagic/utils';
|
|
|
11
11
|
|
|
12
12
|
import ContentMenu from '@editor/components/ContentMenu.vue';
|
|
13
13
|
import CenterIcon from '@editor/icons/CenterIcon.vue';
|
|
14
|
-
import { LayerOffset, Layout, MenuButton, MenuComponent, Services } from '@editor/type';
|
|
14
|
+
import { CustomContentMenuFunction, LayerOffset, Layout, MenuButton, MenuComponent, Services } from '@editor/type';
|
|
15
15
|
import { useCopyMenu, useDeleteMenu, useMoveToMenu, usePasteMenu } from '@editor/utils/content-menu';
|
|
16
16
|
|
|
17
17
|
defineOptions({
|
|
@@ -22,18 +22,16 @@ const props = withDefaults(
|
|
|
22
22
|
defineProps<{
|
|
23
23
|
isMultiSelect?: boolean;
|
|
24
24
|
stageContentMenu: (MenuButton | MenuComponent)[];
|
|
25
|
-
customContentMenu
|
|
25
|
+
customContentMenu: CustomContentMenuFunction;
|
|
26
26
|
}>(),
|
|
27
27
|
{
|
|
28
28
|
isMultiSelect: false,
|
|
29
|
-
stageContentMenu: () => [],
|
|
30
|
-
customContentMenu: (menus: (MenuButton | MenuComponent)[]) => menus,
|
|
31
29
|
},
|
|
32
30
|
);
|
|
33
31
|
|
|
34
32
|
const services = inject<Services>('services');
|
|
35
33
|
const editorService = services?.editorService;
|
|
36
|
-
const menu =
|
|
34
|
+
const menu = useTemplateRef<InstanceType<typeof ContentMenu>>('menu');
|
|
37
35
|
const canCenter = ref(false);
|
|
38
36
|
|
|
39
37
|
const node = computed(() => editorService?.get('node'));
|
|
@@ -4,7 +4,7 @@ import type { Writable } from 'type-fest';
|
|
|
4
4
|
|
|
5
5
|
import type { DataSourceSchema, EventOption, Id, MNode, TargetOptions } from '@tmagic/core';
|
|
6
6
|
import { Target, Watcher } from '@tmagic/core';
|
|
7
|
-
import type { FormConfig } from '@tmagic/form';
|
|
7
|
+
import type { ChangeRecord, FormConfig } from '@tmagic/form';
|
|
8
8
|
import { guid, toLine } from '@tmagic/utils';
|
|
9
9
|
|
|
10
10
|
import editorService from '@editor/services/editor';
|
|
@@ -115,15 +115,22 @@ class DataSource extends BaseService {
|
|
|
115
115
|
return newConfig;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
public update(config: DataSourceSchema) {
|
|
118
|
+
public update(config: DataSourceSchema, { changeRecords = [] }: { changeRecords?: ChangeRecord[] } = {}) {
|
|
119
119
|
const dataSources = this.get('dataSources');
|
|
120
120
|
|
|
121
121
|
const index = dataSources.findIndex((ds) => ds.id === config.id);
|
|
122
|
-
dataSources[index] = cloneDeep(config);
|
|
123
122
|
|
|
124
|
-
|
|
123
|
+
const oldConfig = dataSources[index];
|
|
124
|
+
const newConfig = cloneDeep(config);
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
dataSources[index] = newConfig;
|
|
127
|
+
|
|
128
|
+
this.emit('update', newConfig, {
|
|
129
|
+
oldConfig,
|
|
130
|
+
changeRecords,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
return newConfig;
|
|
127
134
|
}
|
|
128
135
|
|
|
129
136
|
public remove(id: string) {
|
package/src/services/dep.ts
CHANGED
|
@@ -31,11 +31,29 @@ export interface DepEvents {
|
|
|
31
31
|
collected: [nodes: MNode[], deep: boolean];
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
interface State {
|
|
35
|
+
collecting: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
type StateKey = keyof State;
|
|
39
|
+
|
|
34
40
|
const idleTask = new IdleTask<{ node: TargetNode; deep: boolean; target: Target }>();
|
|
35
41
|
|
|
36
42
|
class Dep extends BaseService {
|
|
43
|
+
private state = reactive<State>({
|
|
44
|
+
collecting: false,
|
|
45
|
+
});
|
|
46
|
+
|
|
37
47
|
private watcher = new Watcher({ initialTargets: reactive({}) });
|
|
38
48
|
|
|
49
|
+
public set<K extends StateKey, T extends State[K]>(name: K, value: T) {
|
|
50
|
+
this.state[name] = value;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public get<K extends StateKey>(name: K): State[K] {
|
|
54
|
+
return this.state[name];
|
|
55
|
+
}
|
|
56
|
+
|
|
39
57
|
public removeTargets(type: string = DepTargetType.DEFAULT) {
|
|
40
58
|
this.watcher.removeTargets(type);
|
|
41
59
|
|
|
@@ -71,33 +89,40 @@ class Dep extends BaseService {
|
|
|
71
89
|
}
|
|
72
90
|
|
|
73
91
|
public collect(nodes: MNode[], depExtendedData: DepExtendedData = {}, deep = false, type?: DepTargetType) {
|
|
92
|
+
this.set('collecting', true);
|
|
74
93
|
this.watcher.collectByCallback(nodes, type, ({ node, target }) => {
|
|
75
94
|
this.collectNode(node, target, depExtendedData, deep);
|
|
76
95
|
});
|
|
96
|
+
this.set('collecting', false);
|
|
77
97
|
|
|
78
98
|
this.emit('collected', nodes, deep);
|
|
79
99
|
}
|
|
80
100
|
|
|
81
101
|
public collectIdle(nodes: MNode[], depExtendedData: DepExtendedData = {}, deep = false, type?: DepTargetType) {
|
|
102
|
+
this.set('collecting', true);
|
|
103
|
+
let startTask = false;
|
|
82
104
|
this.watcher.collectByCallback(nodes, type, ({ node, target }) => {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
node,
|
|
89
|
-
deep,
|
|
90
|
-
target,
|
|
91
|
-
},
|
|
92
|
-
);
|
|
105
|
+
startTask = true;
|
|
106
|
+
|
|
107
|
+
this.enqueueTask(node, target, depExtendedData, deep);
|
|
93
108
|
});
|
|
94
109
|
|
|
95
|
-
|
|
96
|
-
|
|
110
|
+
return new Promise<void>((resolve) => {
|
|
111
|
+
if (!startTask) {
|
|
112
|
+
this.emit('collected', nodes, deep);
|
|
113
|
+
this.set('collecting', false);
|
|
114
|
+
resolve();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
idleTask.once('finish', () => {
|
|
118
|
+
this.emit('collected', nodes, deep);
|
|
119
|
+
this.set('collecting', false);
|
|
120
|
+
resolve();
|
|
121
|
+
});
|
|
97
122
|
});
|
|
98
123
|
}
|
|
99
124
|
|
|
100
|
-
collectNode(node: MNode, target: Target, depExtendedData: DepExtendedData = {}, deep = false) {
|
|
125
|
+
public collectNode(node: MNode, target: Target, depExtendedData: DepExtendedData = {}, deep = false) {
|
|
101
126
|
// 先删除原有依赖,重新收集
|
|
102
127
|
if (isPage(node)) {
|
|
103
128
|
Object.entries(target.deps).forEach(([depKey, dep]) => {
|
|
@@ -128,6 +153,10 @@ class Dep extends BaseService {
|
|
|
128
153
|
return this.watcher.hasSpecifiedTypeTarget(type);
|
|
129
154
|
}
|
|
130
155
|
|
|
156
|
+
public clearIdleTasks() {
|
|
157
|
+
idleTask.clearTasks();
|
|
158
|
+
}
|
|
159
|
+
|
|
131
160
|
public on<Name extends keyof DepEvents, Param extends DepEvents[Name]>(
|
|
132
161
|
eventName: Name,
|
|
133
162
|
listener: (...args: Param) => void | Promise<void>,
|
|
@@ -145,6 +174,25 @@ class Dep extends BaseService {
|
|
|
145
174
|
public emit<Name extends keyof DepEvents, Param extends DepEvents[Name]>(eventName: Name, ...args: Param) {
|
|
146
175
|
return super.emit(eventName, ...args);
|
|
147
176
|
}
|
|
177
|
+
|
|
178
|
+
private enqueueTask(node: MNode, target: Target, depExtendedData: DepExtendedData, deep: boolean) {
|
|
179
|
+
idleTask.enqueueTask(
|
|
180
|
+
({ node, deep, target }) => {
|
|
181
|
+
this.collectNode(node, target, depExtendedData, deep);
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
node,
|
|
185
|
+
deep: false,
|
|
186
|
+
target,
|
|
187
|
+
},
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
if (deep && Array.isArray(node.items) && node.items.length) {
|
|
191
|
+
node.items.forEach((item) => {
|
|
192
|
+
this.enqueueTask(item, target, depExtendedData, deep);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
148
196
|
}
|
|
149
197
|
|
|
150
198
|
export type DepService = Dep;
|