@tmagic/editor 1.4.8 → 1.4.10
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 +26 -0
- package/dist/tmagic-editor.js +3213 -2820
- package/dist/tmagic-editor.umd.cjs +3237 -2839
- package/package.json +14 -12
- package/src/Editor.vue +212 -0
- package/src/components/CodeBlockEditor.vue +268 -0
- package/src/components/CodeParams.vue +60 -0
- package/src/components/ContentMenu.vue +193 -0
- package/src/components/FloatingBox.vue +178 -0
- package/src/components/Icon.vue +27 -0
- package/src/components/Resizer.vue +23 -0
- package/src/components/ScrollBar.vue +151 -0
- package/src/components/ScrollViewer.vue +128 -0
- package/src/components/SearchInput.vue +37 -0
- package/src/components/SplitView.vue +186 -0
- package/src/components/ToolButton.vue +138 -0
- package/src/components/Tree.vue +71 -0
- package/src/components/TreeNode.vue +150 -0
- package/src/editorProps.ts +120 -0
- package/src/fields/Code.vue +50 -0
- package/src/fields/CodeLink.vue +80 -0
- package/src/fields/CodeSelect.vue +132 -0
- package/src/fields/CodeSelectCol.vue +139 -0
- package/src/fields/CondOpSelect.vue +87 -0
- package/src/fields/DataSourceFieldSelect/FieldSelect.vue +152 -0
- package/src/fields/DataSourceFieldSelect/Index.vue +168 -0
- package/src/fields/DataSourceFields.vue +329 -0
- package/src/fields/DataSourceInput.vue +339 -0
- package/src/fields/DataSourceMethodSelect.vue +153 -0
- package/src/fields/DataSourceMethods.vue +104 -0
- package/src/fields/DataSourceMocks.vue +255 -0
- package/src/fields/DataSourceSelect.vue +98 -0
- package/src/fields/DisplayConds.vue +145 -0
- package/src/fields/EventSelect.vue +375 -0
- package/src/fields/KeyValue.vue +137 -0
- package/src/fields/PageFragmentSelect.vue +63 -0
- package/src/fields/UISelect.vue +135 -0
- package/src/hooks/index.ts +24 -0
- package/src/hooks/use-code-block-edit.ts +83 -0
- package/src/hooks/use-data-source-edit.ts +46 -0
- package/src/hooks/use-data-source-method.ts +100 -0
- package/src/hooks/use-editor-content-height.ts +26 -0
- package/src/hooks/use-filter.ts +55 -0
- package/src/hooks/use-float-box.ts +76 -0
- package/src/hooks/use-getso.ts +35 -0
- package/src/hooks/use-next-float-box-position.ts +29 -0
- package/src/hooks/use-node-status.ts +48 -0
- package/src/hooks/use-stage.ts +129 -0
- package/src/hooks/use-window-rect.ts +20 -0
- package/src/icons/AppManageIcon.vue +15 -0
- package/src/icons/CenterIcon.vue +13 -0
- package/src/icons/CodeIcon.vue +28 -0
- package/src/icons/FolderMinusIcon.vue +22 -0
- package/src/icons/PinIcon.vue +25 -0
- package/src/icons/PinnedIcon.vue +25 -0
- package/src/index.ts +124 -0
- package/src/initService.ts +448 -0
- package/src/layouts/AddPageBox.vue +55 -0
- package/src/layouts/CodeEditor.vue +226 -0
- package/src/layouts/Framework.vue +165 -0
- package/src/layouts/NavMenu.vue +200 -0
- package/src/layouts/PropsPanel.vue +131 -0
- package/src/layouts/page-bar/AddButton.vue +48 -0
- package/src/layouts/page-bar/PageBar.vue +165 -0
- package/src/layouts/page-bar/PageBarScrollContainer.vue +192 -0
- package/src/layouts/page-bar/PageList.vue +55 -0
- package/src/layouts/page-bar/SwitchTypeButton.vue +45 -0
- package/src/layouts/sidebar/ComponentListPanel.vue +134 -0
- package/src/layouts/sidebar/Sidebar.vue +278 -0
- package/src/layouts/sidebar/code-block/CodeBlockList.vue +160 -0
- package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +70 -0
- package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +84 -0
- package/src/layouts/sidebar/data-source/DataSourceList.vue +172 -0
- package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +106 -0
- package/src/layouts/sidebar/layer/LayerMenu.vue +128 -0
- package/src/layouts/sidebar/layer/LayerNodeTool.vue +32 -0
- package/src/layouts/sidebar/layer/LayerPanel.vue +119 -0
- package/src/layouts/sidebar/layer/use-click.ts +113 -0
- package/src/layouts/sidebar/layer/use-drag.ts +167 -0
- package/src/layouts/sidebar/layer/use-keybinding.ts +54 -0
- package/src/layouts/sidebar/layer/use-node-status.ts +114 -0
- package/src/layouts/workspace/Breadcrumb.vue +35 -0
- package/src/layouts/workspace/Workspace.vue +46 -0
- package/src/layouts/workspace/viewer/NodeListMenu.vue +115 -0
- package/src/layouts/workspace/viewer/Stage.vue +246 -0
- package/src/layouts/workspace/viewer/StageOverlay.vue +68 -0
- package/src/layouts/workspace/viewer/ViewerMenu.vue +145 -0
- package/src/services/BaseService.ts +230 -0
- package/src/services/codeBlock.ts +327 -0
- package/src/services/componentList.ts +58 -0
- package/src/services/dataSource.ts +216 -0
- package/src/services/dep.ts +152 -0
- package/src/services/editor.ts +1135 -0
- package/src/services/events.ts +93 -0
- package/src/services/history.ts +126 -0
- package/src/services/keybinding.ts +220 -0
- package/src/services/props.ts +286 -0
- package/src/services/stageOverlay.ts +212 -0
- package/src/services/storage.ts +155 -0
- package/src/services/ui.ts +157 -0
- package/src/shims-vue.d.ts +6 -0
- package/src/theme/breadcrumb.scss +6 -0
- package/src/theme/code-block.scss +6 -0
- package/src/theme/code-editor.scss +38 -0
- package/src/theme/common/var.scss +14 -0
- package/src/theme/component-list-panel.scss +95 -0
- package/src/theme/content-menu.scss +76 -0
- package/src/theme/data-source-field-select.scss +16 -0
- package/src/theme/data-source-field.scss +12 -0
- package/src/theme/data-source-fields.scss +13 -0
- package/src/theme/data-source-input.scss +18 -0
- package/src/theme/data-source-methods.scss +13 -0
- package/src/theme/data-source.scss +28 -0
- package/src/theme/event.scss +37 -0
- package/src/theme/floating-box.scss +32 -0
- package/src/theme/framework.scss +69 -0
- package/src/theme/icon.scss +12 -0
- package/src/theme/index.scss +10 -0
- package/src/theme/key-value.scss +20 -0
- package/src/theme/layer-panel.scss +26 -0
- package/src/theme/layout.scss +9 -0
- package/src/theme/nav-menu.scss +78 -0
- package/src/theme/page-bar.scss +102 -0
- package/src/theme/page-fragment-select.scss +14 -0
- package/src/theme/props-panel.scss +55 -0
- package/src/theme/resizer.scss +66 -0
- package/src/theme/ruler.scss +38 -0
- package/src/theme/search-input.scss +14 -0
- package/src/theme/sidebar.scss +79 -0
- package/src/theme/stage.scss +76 -0
- package/src/theme/theme.scss +28 -0
- package/src/theme/tree.scss +89 -0
- package/src/theme/workspace.scss +9 -0
- package/src/type.ts +780 -0
- package/src/utils/compose.ts +52 -0
- package/src/utils/config.ts +29 -0
- package/src/utils/content-menu.ts +95 -0
- package/src/utils/data-source/formConfigs/base.ts +30 -0
- package/src/utils/data-source/formConfigs/http.ts +60 -0
- package/src/utils/data-source/index.ts +249 -0
- package/src/utils/editor.ts +313 -0
- package/src/utils/idle-task.ts +72 -0
- package/src/utils/index.ts +24 -0
- package/src/utils/keybinding-config.ts +125 -0
- package/src/utils/logger.ts +47 -0
- package/src/utils/monaco-editor.ts +8 -0
- package/src/utils/operator.ts +106 -0
- package/src/utils/props.ts +407 -0
- package/src/utils/scroll-viewer.ts +165 -0
- package/src/utils/tree.ts +15 -0
- package/src/utils/undo-redo.ts +76 -0
- package/types/editorProps.d.ts +3 -1
- package/types/fields/CondOpSelect.vue.d.ts +17 -0
- package/types/fields/DataSourceFieldSelect/FieldSelect.vue.d.ts +36 -0
- package/types/fields/{DataSourceFieldSelect.vue.d.ts → DataSourceFieldSelect/Index.vue.d.ts} +1 -1
- package/types/fields/DisplayConds.vue.d.ts +32 -0
- package/types/index.d.ts +3 -1
- package/types/layouts/Framework.vue.d.ts +3 -1
- package/types/layouts/PropsPanel.vue.d.ts +21 -21
- package/types/layouts/page-bar/PageBar.vue.d.ts +6 -0
- package/types/layouts/page-bar/PageBarScrollContainer.vue.d.ts +3 -0
- package/types/layouts/page-bar/PageList.vue.d.ts +25 -0
- package/types/services/ui.d.ts +1 -0
- package/types/type.d.ts +30 -4
- package/types/utils/data-source/index.d.ts +1 -0
- package/types/utils/props.d.ts +12 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="m-editor-props-panel" v-show="nodes.length === 1">
|
|
3
|
+
<slot name="props-panel-header"></slot>
|
|
4
|
+
<MForm
|
|
5
|
+
ref="configForm"
|
|
6
|
+
:class="propsPanelSize"
|
|
7
|
+
:popper-class="`m-editor-props-panel-popper ${propsPanelSize}`"
|
|
8
|
+
:size="propsPanelSize"
|
|
9
|
+
:init-values="values"
|
|
10
|
+
:config="curFormConfig"
|
|
11
|
+
:extend-state="extendState"
|
|
12
|
+
@change="submit"
|
|
13
|
+
@error="errorHandler"
|
|
14
|
+
></MForm>
|
|
15
|
+
|
|
16
|
+
<TMagicButton
|
|
17
|
+
v-if="!disabledShowSrc"
|
|
18
|
+
class="m-editor-props-panel-src-icon"
|
|
19
|
+
circle
|
|
20
|
+
size="large"
|
|
21
|
+
title="源码"
|
|
22
|
+
:type="showSrc ? 'primary' : ''"
|
|
23
|
+
@click="showSrc = !showSrc"
|
|
24
|
+
>
|
|
25
|
+
<MIcon :icon="DocumentIcon"></MIcon>
|
|
26
|
+
</TMagicButton>
|
|
27
|
+
|
|
28
|
+
<CodeEditor
|
|
29
|
+
v-if="showSrc"
|
|
30
|
+
class="m-editor-props-panel-src-code"
|
|
31
|
+
:height="`${editorContentHeight}px`"
|
|
32
|
+
:init-values="values"
|
|
33
|
+
:options="codeOptions"
|
|
34
|
+
:parse="true"
|
|
35
|
+
@save="saveCode"
|
|
36
|
+
></CodeEditor>
|
|
37
|
+
</div>
|
|
38
|
+
</template>
|
|
39
|
+
|
|
40
|
+
<script lang="ts" setup>
|
|
41
|
+
import { computed, getCurrentInstance, inject, onBeforeUnmount, onMounted, ref, watchEffect } from 'vue';
|
|
42
|
+
import { Document as DocumentIcon } from '@element-plus/icons-vue';
|
|
43
|
+
|
|
44
|
+
import { TMagicButton } from '@tmagic/design';
|
|
45
|
+
import type { FormState, FormValue } from '@tmagic/form';
|
|
46
|
+
import { MForm } from '@tmagic/form';
|
|
47
|
+
import type { MNode } from '@tmagic/schema';
|
|
48
|
+
|
|
49
|
+
import MIcon from '@editor/components/Icon.vue';
|
|
50
|
+
import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height';
|
|
51
|
+
import type { PropsPanelSlots, Services } from '@editor/type';
|
|
52
|
+
|
|
53
|
+
import CodeEditor from './CodeEditor.vue';
|
|
54
|
+
|
|
55
|
+
defineSlots<PropsPanelSlots>();
|
|
56
|
+
|
|
57
|
+
defineOptions({
|
|
58
|
+
name: 'MEditorPropsPanel',
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
defineProps<{
|
|
62
|
+
disabledShowSrc?: boolean;
|
|
63
|
+
extendState?: (state: FormState) => Record<string, any> | Promise<Record<string, any>>;
|
|
64
|
+
}>();
|
|
65
|
+
|
|
66
|
+
const codeOptions = inject('codeOptions', {});
|
|
67
|
+
|
|
68
|
+
const emit = defineEmits(['mounted', 'submit-error', 'form-error']);
|
|
69
|
+
|
|
70
|
+
const showSrc = ref(false);
|
|
71
|
+
|
|
72
|
+
const internalInstance = getCurrentInstance();
|
|
73
|
+
const values = ref<FormValue>({});
|
|
74
|
+
const configForm = ref<InstanceType<typeof MForm>>();
|
|
75
|
+
// ts类型应该是FormConfig, 但是打包时会出错,所以暂时用any
|
|
76
|
+
const curFormConfig = ref<any>([]);
|
|
77
|
+
const services = inject<Services>('services');
|
|
78
|
+
const node = computed(() => services?.editorService.get('node'));
|
|
79
|
+
const nodes = computed(() => services?.editorService.get('nodes') || []);
|
|
80
|
+
const propsPanelSize = computed(() => services?.uiService.get('propsPanelSize') || 'small');
|
|
81
|
+
const stage = computed(() => services?.editorService.get('stage'));
|
|
82
|
+
|
|
83
|
+
const { height: editorContentHeight } = useEditorContentHeight();
|
|
84
|
+
|
|
85
|
+
const init = async () => {
|
|
86
|
+
if (!node.value) {
|
|
87
|
+
curFormConfig.value = [];
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const type = node.value.type || (node.value.items ? 'container' : 'text');
|
|
92
|
+
curFormConfig.value = (await services?.propsService.getPropsConfig(type)) || [];
|
|
93
|
+
values.value = node.value;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
watchEffect(init);
|
|
97
|
+
services?.propsService.on('props-configs-change', init);
|
|
98
|
+
|
|
99
|
+
onMounted(() => {
|
|
100
|
+
emit('mounted', internalInstance);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
onBeforeUnmount(() => {
|
|
104
|
+
services?.propsService.off('props-configs-change', init);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
watchEffect(() => {
|
|
108
|
+
if (configForm.value && stage.value) {
|
|
109
|
+
configForm.value.formState.stage = stage.value;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const submit = async () => {
|
|
114
|
+
try {
|
|
115
|
+
const values = await configForm.value?.submitForm();
|
|
116
|
+
services?.editorService.update(values);
|
|
117
|
+
} catch (e: any) {
|
|
118
|
+
emit('submit-error', e);
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const errorHandler = (e: any) => {
|
|
123
|
+
emit('form-error', e);
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const saveCode = (values: MNode) => {
|
|
127
|
+
services?.editorService.update(values);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
defineExpose({ configForm, submit });
|
|
131
|
+
</script>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-if="showAddPageButton"
|
|
4
|
+
id="m-editor-page-bar-add-icon"
|
|
5
|
+
class="m-editor-page-bar-item m-editor-page-bar-item-icon"
|
|
6
|
+
@click="addPage"
|
|
7
|
+
>
|
|
8
|
+
<Icon :icon="Plus"></Icon>
|
|
9
|
+
</div>
|
|
10
|
+
<div v-else style="width: 21px"></div>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<script setup lang="ts">
|
|
14
|
+
import { computed, inject, toRaw } from 'vue';
|
|
15
|
+
import { Plus } from '@element-plus/icons-vue';
|
|
16
|
+
|
|
17
|
+
import { NodeType } from '@tmagic/schema';
|
|
18
|
+
|
|
19
|
+
import Icon from '@editor/components/Icon.vue';
|
|
20
|
+
import type { Services } from '@editor/type';
|
|
21
|
+
import { generatePageNameByApp } from '@editor/utils/editor';
|
|
22
|
+
|
|
23
|
+
defineOptions({
|
|
24
|
+
name: 'MEditorPageBarAddButton',
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const props = defineProps<{
|
|
28
|
+
type: NodeType.PAGE | NodeType.PAGE_FRAGMENT;
|
|
29
|
+
}>();
|
|
30
|
+
|
|
31
|
+
const services = inject<Services>('services');
|
|
32
|
+
const uiService = services?.uiService;
|
|
33
|
+
const editorService = services?.editorService;
|
|
34
|
+
|
|
35
|
+
const showAddPageButton = computed(() => uiService?.get('showAddPageButton'));
|
|
36
|
+
|
|
37
|
+
const addPage = () => {
|
|
38
|
+
if (!editorService) return;
|
|
39
|
+
const root = toRaw(editorService.get('root'));
|
|
40
|
+
if (!root) throw new Error('root 不能为空');
|
|
41
|
+
const pageConfig = {
|
|
42
|
+
type: props.type,
|
|
43
|
+
name: generatePageNameByApp(root, props.type),
|
|
44
|
+
items: [],
|
|
45
|
+
};
|
|
46
|
+
editorService.add(pageConfig);
|
|
47
|
+
};
|
|
48
|
+
</script>
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="m-editor-page-bar-tabs">
|
|
3
|
+
<SwitchTypeButton v-if="!disabledPageFragment" v-model="active" />
|
|
4
|
+
|
|
5
|
+
<PageBarScrollContainer :type="active" :page-bar-sort-options="pageBarSortOptions">
|
|
6
|
+
<template #prepend>
|
|
7
|
+
<AddButton :type="active"></AddButton>
|
|
8
|
+
<PageList :list="list">
|
|
9
|
+
<template #page-list-popover="{ list }"><slot name="page-list-popover" :list="list"></slot></template>
|
|
10
|
+
</PageList>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
v-for="item in list"
|
|
15
|
+
class="m-editor-page-bar-item"
|
|
16
|
+
:key="item.id"
|
|
17
|
+
:page-id="item.id"
|
|
18
|
+
:class="{ active: page?.id === item.id }"
|
|
19
|
+
@click="switchPage(item.id)"
|
|
20
|
+
>
|
|
21
|
+
<div class="m-editor-page-bar-title">
|
|
22
|
+
<slot name="page-bar-title" :page="item">
|
|
23
|
+
<span :title="item.name">{{ item.name || item.id }}</span>
|
|
24
|
+
</slot>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<TMagicPopover popper-class="page-bar-popover" placement="top" :width="160" trigger="hover">
|
|
28
|
+
<div>
|
|
29
|
+
<slot name="page-bar-popover" :page="item">
|
|
30
|
+
<ToolButton
|
|
31
|
+
:data="{
|
|
32
|
+
type: 'button',
|
|
33
|
+
text: '复制',
|
|
34
|
+
icon: DocumentCopy,
|
|
35
|
+
handler: () => copy(item),
|
|
36
|
+
}"
|
|
37
|
+
></ToolButton>
|
|
38
|
+
<ToolButton
|
|
39
|
+
:data="{
|
|
40
|
+
type: 'button',
|
|
41
|
+
text: '删除',
|
|
42
|
+
icon: Delete,
|
|
43
|
+
handler: () => remove(item),
|
|
44
|
+
}"
|
|
45
|
+
></ToolButton>
|
|
46
|
+
</slot>
|
|
47
|
+
</div>
|
|
48
|
+
<template #reference>
|
|
49
|
+
<TMagicIcon class="m-editor-page-bar-menu-icon">
|
|
50
|
+
<CaretBottom></CaretBottom>
|
|
51
|
+
</TMagicIcon>
|
|
52
|
+
</template>
|
|
53
|
+
</TMagicPopover>
|
|
54
|
+
</div>
|
|
55
|
+
</PageBarScrollContainer>
|
|
56
|
+
</div>
|
|
57
|
+
</template>
|
|
58
|
+
|
|
59
|
+
<script lang="ts" setup>
|
|
60
|
+
import { computed, inject, ref, watch } from 'vue';
|
|
61
|
+
import { CaretBottom, Delete, DocumentCopy } from '@element-plus/icons-vue';
|
|
62
|
+
|
|
63
|
+
import { TMagicIcon, TMagicPopover } from '@tmagic/design';
|
|
64
|
+
import { Id, type MPage, type MPageFragment, NodeType } from '@tmagic/schema';
|
|
65
|
+
import { isPage, isPageFragment } from '@tmagic/utils';
|
|
66
|
+
|
|
67
|
+
import ToolButton from '@editor/components/ToolButton.vue';
|
|
68
|
+
import type { PageBarSortOptions, Services } from '@editor/type';
|
|
69
|
+
import { getPageFragmentList, getPageList } from '@editor/utils';
|
|
70
|
+
|
|
71
|
+
import AddButton from './AddButton.vue';
|
|
72
|
+
import PageBarScrollContainer from './PageBarScrollContainer.vue';
|
|
73
|
+
import PageList from './PageList.vue';
|
|
74
|
+
import SwitchTypeButton from './SwitchTypeButton.vue';
|
|
75
|
+
|
|
76
|
+
defineOptions({
|
|
77
|
+
name: 'MEditorPageBar',
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
defineProps<{
|
|
81
|
+
disabledPageFragment: boolean;
|
|
82
|
+
pageBarSortOptions?: PageBarSortOptions;
|
|
83
|
+
}>();
|
|
84
|
+
|
|
85
|
+
const active = ref<NodeType.PAGE | NodeType.PAGE_FRAGMENT>(NodeType.PAGE);
|
|
86
|
+
|
|
87
|
+
const services = inject<Services>('services');
|
|
88
|
+
const editorService = services?.editorService;
|
|
89
|
+
|
|
90
|
+
const root = computed(() => editorService?.get('root'));
|
|
91
|
+
const page = computed(() => editorService?.get('page'));
|
|
92
|
+
const pageList = computed(() => getPageList(root.value));
|
|
93
|
+
const pageFragmentList = computed(() => getPageFragmentList(root.value));
|
|
94
|
+
|
|
95
|
+
const list = computed(() => (active.value === NodeType.PAGE ? pageList.value : pageFragmentList.value));
|
|
96
|
+
|
|
97
|
+
const activePage = ref<Id>('');
|
|
98
|
+
const activePageFragment = ref<Id>('');
|
|
99
|
+
|
|
100
|
+
watch(
|
|
101
|
+
page,
|
|
102
|
+
(page) => {
|
|
103
|
+
if (!page) {
|
|
104
|
+
if (active.value === NodeType.PAGE) {
|
|
105
|
+
activePage.value = '';
|
|
106
|
+
}
|
|
107
|
+
if (active.value === NodeType.PAGE_FRAGMENT) {
|
|
108
|
+
activePageFragment.value = '';
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (isPage(page)) {
|
|
114
|
+
activePage.value = page?.id;
|
|
115
|
+
if (active.value !== NodeType.PAGE) {
|
|
116
|
+
active.value = NodeType.PAGE;
|
|
117
|
+
}
|
|
118
|
+
} else if (isPageFragment(page)) {
|
|
119
|
+
activePageFragment.value = page?.id;
|
|
120
|
+
if (active.value !== NodeType.PAGE_FRAGMENT) {
|
|
121
|
+
active.value = NodeType.PAGE_FRAGMENT;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
immediate: true,
|
|
127
|
+
},
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
watch(active, (active) => {
|
|
131
|
+
if (active === NodeType.PAGE) {
|
|
132
|
+
if (!activePage.value && !pageList.value.length) {
|
|
133
|
+
editorService?.selectRoot();
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
switchPage(activePage.value);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (active === NodeType.PAGE_FRAGMENT) {
|
|
141
|
+
// 之前没有选中过页面片并且当前没有页面片
|
|
142
|
+
if (!activePageFragment.value && !pageFragmentList.value.length) {
|
|
143
|
+
editorService?.selectRoot();
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
switchPage(activePageFragment.value || pageFragmentList.value[0].id);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const switchPage = (id: Id) => {
|
|
151
|
+
editorService?.select(id);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const copy = (node: MPage | MPageFragment) => {
|
|
155
|
+
node && editorService?.copy(node);
|
|
156
|
+
editorService?.paste({
|
|
157
|
+
left: 0,
|
|
158
|
+
top: 0,
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const remove = (node: MPage | MPageFragment) => {
|
|
163
|
+
editorService?.remove(node);
|
|
164
|
+
};
|
|
165
|
+
</script>
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="m-editor-page-bar" ref="pageBar">
|
|
3
|
+
<slot name="prepend"></slot>
|
|
4
|
+
|
|
5
|
+
<div v-if="canScroll" class="m-editor-page-bar-item m-editor-page-bar-item-icon" @click="scroll('left')">
|
|
6
|
+
<Icon :icon="ArrowLeftBold"></Icon>
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
<div
|
|
10
|
+
v-if="(type === NodeType.PAGE && pageLength) || (type === NodeType.PAGE_FRAGMENT && pageFragmentLength)"
|
|
11
|
+
class="m-editor-page-bar-items"
|
|
12
|
+
ref="itemsContainer"
|
|
13
|
+
:style="`width: ${itemsContainerWidth}px`"
|
|
14
|
+
>
|
|
15
|
+
<slot></slot>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<div v-if="canScroll" class="m-editor-page-bar-item m-editor-page-bar-item-icon" @click="scroll('right')">
|
|
19
|
+
<Icon :icon="ArrowRightBold"></Icon>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
23
|
+
|
|
24
|
+
<script setup lang="ts">
|
|
25
|
+
import {
|
|
26
|
+
computed,
|
|
27
|
+
type ComputedRef,
|
|
28
|
+
inject,
|
|
29
|
+
nextTick,
|
|
30
|
+
onBeforeUnmount,
|
|
31
|
+
onMounted,
|
|
32
|
+
ref,
|
|
33
|
+
watch,
|
|
34
|
+
type WatchStopHandle,
|
|
35
|
+
} from 'vue';
|
|
36
|
+
import { ArrowLeftBold, ArrowRightBold } from '@element-plus/icons-vue';
|
|
37
|
+
import Sortable, { SortableEvent } from 'sortablejs';
|
|
38
|
+
|
|
39
|
+
import { Id, NodeType } from '@tmagic/schema';
|
|
40
|
+
|
|
41
|
+
import Icon from '@editor/components/Icon.vue';
|
|
42
|
+
import type { PageBarSortOptions, Services } from '@editor/type';
|
|
43
|
+
|
|
44
|
+
defineOptions({
|
|
45
|
+
name: 'MEditorPageBarScrollContainer',
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const props = defineProps<{
|
|
49
|
+
type: NodeType.PAGE | NodeType.PAGE_FRAGMENT;
|
|
50
|
+
pageBarSortOptions?: PageBarSortOptions;
|
|
51
|
+
}>();
|
|
52
|
+
|
|
53
|
+
const services = inject<Services>('services');
|
|
54
|
+
const editorService = services?.editorService;
|
|
55
|
+
const uiService = services?.uiService;
|
|
56
|
+
|
|
57
|
+
const itemsContainer = ref<HTMLElement>();
|
|
58
|
+
const canScroll = ref(false);
|
|
59
|
+
|
|
60
|
+
const showAddPageButton = computed(() => uiService?.get('showAddPageButton'));
|
|
61
|
+
const showPageListButton = computed(() => uiService?.get('showPageListButton'));
|
|
62
|
+
|
|
63
|
+
const itemsContainerWidth = ref(0);
|
|
64
|
+
|
|
65
|
+
const setCanScroll = () => {
|
|
66
|
+
// 减去新增、左移、右移三个按钮的宽度
|
|
67
|
+
// 37 = icon width 16 + padding 10 * 2 + border-right 1
|
|
68
|
+
itemsContainerWidth.value =
|
|
69
|
+
(pageBar.value?.clientWidth || 0) -
|
|
70
|
+
37 * 2 -
|
|
71
|
+
(showAddPageButton.value ? 37 : 21) -
|
|
72
|
+
(showPageListButton.value ? 37 : 0);
|
|
73
|
+
|
|
74
|
+
nextTick(() => {
|
|
75
|
+
if (itemsContainer.value) {
|
|
76
|
+
canScroll.value = itemsContainer.value.scrollWidth - itemsContainerWidth.value > 1;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
82
|
+
setCanScroll();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const pageBar = ref<HTMLDivElement>();
|
|
86
|
+
onMounted(() => {
|
|
87
|
+
pageBar.value && resizeObserver.observe(pageBar.value);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
onBeforeUnmount(() => {
|
|
91
|
+
resizeObserver.disconnect();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
let translateLeft = 0;
|
|
95
|
+
|
|
96
|
+
const scroll = (type: 'left' | 'right' | 'start' | 'end') => {
|
|
97
|
+
if (!itemsContainer.value) return;
|
|
98
|
+
|
|
99
|
+
const maxScrollLeft = itemsContainer.value.scrollWidth - itemsContainerWidth.value;
|
|
100
|
+
|
|
101
|
+
if (type === 'left') {
|
|
102
|
+
translateLeft += 100;
|
|
103
|
+
|
|
104
|
+
if (translateLeft > 0) {
|
|
105
|
+
translateLeft = 0;
|
|
106
|
+
}
|
|
107
|
+
} else if (type === 'right') {
|
|
108
|
+
translateLeft -= 100;
|
|
109
|
+
|
|
110
|
+
if (-translateLeft > maxScrollLeft) {
|
|
111
|
+
translateLeft = -maxScrollLeft;
|
|
112
|
+
}
|
|
113
|
+
} else if (type === 'start') {
|
|
114
|
+
translateLeft = 0;
|
|
115
|
+
} else if (type === 'end') {
|
|
116
|
+
translateLeft = -maxScrollLeft;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
itemsContainer.value.style.transform = `translate(${translateLeft}px, 0px)`;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const pageLength = computed(() => editorService?.get('pageLength') || 0);
|
|
123
|
+
const pageFragmentLength = computed(() => editorService?.get('pageFragmentLength') || 0);
|
|
124
|
+
|
|
125
|
+
const crateWatchLength = (length: ComputedRef<number>) =>
|
|
126
|
+
watch(
|
|
127
|
+
length,
|
|
128
|
+
(length = 0, preLength = 0) => {
|
|
129
|
+
setTimeout(() => {
|
|
130
|
+
setCanScroll();
|
|
131
|
+
if (length < preLength) {
|
|
132
|
+
scroll('start');
|
|
133
|
+
} else {
|
|
134
|
+
scroll('end');
|
|
135
|
+
}
|
|
136
|
+
if (length > 1) {
|
|
137
|
+
const el = document.querySelector('.m-editor-page-bar-items') as HTMLElement;
|
|
138
|
+
let beforeDragList: Id[] = [];
|
|
139
|
+
const options = {
|
|
140
|
+
...{
|
|
141
|
+
dataIdAttr: 'page-id', // 获取排序后的数据
|
|
142
|
+
onStart: async (event: SortableEvent) => {
|
|
143
|
+
if (typeof props.pageBarSortOptions?.beforeStart === 'function') {
|
|
144
|
+
await props.pageBarSortOptions.beforeStart(event, sortable);
|
|
145
|
+
}
|
|
146
|
+
beforeDragList = sortable.toArray();
|
|
147
|
+
},
|
|
148
|
+
onUpdate: async (event: SortableEvent) => {
|
|
149
|
+
await editorService?.sort(
|
|
150
|
+
beforeDragList[event.oldIndex as number],
|
|
151
|
+
beforeDragList[event.newIndex as number],
|
|
152
|
+
);
|
|
153
|
+
if (typeof props.pageBarSortOptions?.afterUpdate === 'function') {
|
|
154
|
+
await props.pageBarSortOptions.afterUpdate(event, sortable);
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
...{
|
|
159
|
+
...(props.pageBarSortOptions ? props.pageBarSortOptions : {}),
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
if (!el) return;
|
|
163
|
+
const sortable = new Sortable(el, options);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
immediate: true,
|
|
169
|
+
},
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
let unWatchPageLength: WatchStopHandle | null;
|
|
173
|
+
let unWatchPageFragmentLength: WatchStopHandle | null;
|
|
174
|
+
|
|
175
|
+
watch(
|
|
176
|
+
() => props.type,
|
|
177
|
+
(type) => {
|
|
178
|
+
if (type === NodeType.PAGE) {
|
|
179
|
+
unWatchPageFragmentLength?.();
|
|
180
|
+
unWatchPageFragmentLength = null;
|
|
181
|
+
unWatchPageLength = crateWatchLength(pageLength);
|
|
182
|
+
} else {
|
|
183
|
+
unWatchPageLength?.();
|
|
184
|
+
unWatchPageLength = null;
|
|
185
|
+
unWatchPageFragmentLength = crateWatchLength(pageFragmentLength);
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
immediate: true,
|
|
190
|
+
},
|
|
191
|
+
);
|
|
192
|
+
</script>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-if="showPageListButton"
|
|
4
|
+
id="m-editor-page-bar-list-icon"
|
|
5
|
+
class="m-editor-page-bar-item m-editor-page-bar-item-icon"
|
|
6
|
+
>
|
|
7
|
+
<TMagicPopover popper-class="page-bar-popover" placement="top" :width="160" trigger="hover">
|
|
8
|
+
<div>
|
|
9
|
+
<slot name="page-list-popover" :list="list">
|
|
10
|
+
<ToolButton
|
|
11
|
+
v-for="(item, index) in list"
|
|
12
|
+
:data="{
|
|
13
|
+
type: 'button',
|
|
14
|
+
text: item.devconfig?.tabName || item.name || item.id,
|
|
15
|
+
handler: () => switchPage(item.id),
|
|
16
|
+
}"
|
|
17
|
+
:key="index"
|
|
18
|
+
></ToolButton>
|
|
19
|
+
</slot>
|
|
20
|
+
</div>
|
|
21
|
+
<template #reference>
|
|
22
|
+
<TMagicIcon class="m-editor-page-list-menu-icon">
|
|
23
|
+
<Files></Files>
|
|
24
|
+
</TMagicIcon>
|
|
25
|
+
</template>
|
|
26
|
+
</TMagicPopover>
|
|
27
|
+
</div>
|
|
28
|
+
</template>
|
|
29
|
+
|
|
30
|
+
<script setup lang="ts">
|
|
31
|
+
import { computed, inject } from 'vue';
|
|
32
|
+
import { Files } from '@element-plus/icons-vue';
|
|
33
|
+
|
|
34
|
+
import { TMagicIcon, TMagicPopover } from '@tmagic/design';
|
|
35
|
+
import { Id, MPage, MPageFragment } from '@tmagic/schema';
|
|
36
|
+
|
|
37
|
+
import ToolButton from '@editor/components/ToolButton.vue';
|
|
38
|
+
import type { Services } from '@editor/type';
|
|
39
|
+
defineOptions({
|
|
40
|
+
name: 'MEditorPageList',
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
defineProps<{
|
|
44
|
+
list: MPage[] | MPageFragment[];
|
|
45
|
+
}>();
|
|
46
|
+
|
|
47
|
+
const services = inject<Services>('services');
|
|
48
|
+
const uiService = services?.uiService;
|
|
49
|
+
const editorService = services?.editorService;
|
|
50
|
+
|
|
51
|
+
const showPageListButton = computed(() => uiService?.get('showPageListButton'));
|
|
52
|
+
const switchPage = (id: Id) => {
|
|
53
|
+
editorService?.select(id);
|
|
54
|
+
};
|
|
55
|
+
</script>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<TMagicButton
|
|
3
|
+
v-for="item in data"
|
|
4
|
+
class="m-editor-page-bar-switch-type-button"
|
|
5
|
+
size="small"
|
|
6
|
+
:key="item.type"
|
|
7
|
+
link
|
|
8
|
+
:class="{ active: modelValue === item.type }"
|
|
9
|
+
:type="modelValue === item.type ? 'primary' : ''"
|
|
10
|
+
@click="clickHandler(item.type)"
|
|
11
|
+
>{{ item.text }}</TMagicButton
|
|
12
|
+
>
|
|
13
|
+
</template>
|
|
14
|
+
|
|
15
|
+
<script setup lang="ts">
|
|
16
|
+
import { TMagicButton } from '@tmagic/design';
|
|
17
|
+
import { NodeType } from '@tmagic/schema';
|
|
18
|
+
|
|
19
|
+
defineOptions({
|
|
20
|
+
name: 'MEditorPageBarSwitchTypeButton',
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
defineProps<{
|
|
24
|
+
modelValue: NodeType.PAGE | NodeType.PAGE_FRAGMENT;
|
|
25
|
+
}>();
|
|
26
|
+
|
|
27
|
+
const data: { type: NodeType.PAGE | NodeType.PAGE_FRAGMENT; text: string }[] = [
|
|
28
|
+
{
|
|
29
|
+
type: NodeType.PAGE,
|
|
30
|
+
text: '页面',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
type: NodeType.PAGE_FRAGMENT,
|
|
34
|
+
text: '页面片',
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const emit = defineEmits<{
|
|
39
|
+
'update:modelValue': [value: NodeType.PAGE | NodeType.PAGE_FRAGMENT];
|
|
40
|
+
}>();
|
|
41
|
+
|
|
42
|
+
const clickHandler = (value: NodeType.PAGE | NodeType.PAGE_FRAGMENT) => {
|
|
43
|
+
emit('update:modelValue', value);
|
|
44
|
+
};
|
|
45
|
+
</script>
|