280 lines
9.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<Framework
:disabled-page-fragment="disabledPageFragment"
:page-bar-sort-options="pageBarSortOptions"
:page-filter-function="pageFilterFunction"
:hide-sidebar="hideSidebar"
>
<template #header>
<slot name="header"></slot>
</template>
<template #nav>
<slot name="nav" :editorService="editorService"><TMagicNavMenu :data="menu"></TMagicNavMenu></slot>
</template>
<template #content-before>
<slot name="content-before"></slot>
</template>
<template #src-code><slot name="src-code" :editorService="editorService"></slot></template>
<template #sidebar>
<slot name="sidebar" :editorService="editorService">
<Sidebar
:data="sidebar"
:layer-content-menu="layerContentMenu"
:custom-content-menu="customContentMenu"
:indent="treeIndent"
:next-level-indent-increment="treeNextLevelIndentIncrement"
:layer-node-is-expandable="layerNodeIsExpandable"
:can-drop-in="canDropIn"
:before-layer-node-dblclick="beforeLayerNodeDblclick"
@layer-node-dblclick="layerNodeDblclickHandler"
>
<template #layer-panel-header>
<slot name="layer-panel-header"></slot>
</template>
<template #layer-node-content="{ data }">
<slot name="layer-node-content" :data="data"></slot>
</template>
<template #layer-node-label="{ data }">
<slot name="layer-node-label" :data="data"></slot>
</template>
<template #layer-node-tool="{ data }">
<slot name="layer-node-tool" :data="data"></slot>
</template>
<template #component-list="{ componentGroupList }">
<slot name="component-list" :component-group-list="componentGroupList"></slot>
</template>
<template #component-list-panel-header>
<slot name="component-list-panel-header"></slot>
</template>
<template #component-list-item="{ component }">
<slot name="component-list-item" :component="component"></slot>
</template>
<template #code-block-panel-header>
<slot name="code-block-panel-header"></slot>
</template>
<template #code-block-panel-tool="{ id, data }">
<slot name="code-block-panel-tool" :id="id" :data="data"></slot>
</template>
<template #code-block-panel-search>
<slot name="code-block-panel-search"></slot>
</template>
<template #data-source-panel-tool="{ data }">
<slot name="data-source-panel-tool" :data="data"></slot>
</template>
<template #data-source-panel-search>
<slot name="data-source-panel-search"></slot>
</template>
</Sidebar>
</slot>
</template>
<template #workspace>
<slot name="workspace" :editorService="editorService">
<Workspace
:disabled-stage-overlay="disabledStageOverlay"
:stage-content-menu="stageContentMenu"
:custom-content-menu="customContentMenu"
>
<template #stage-top><slot name="stage-top"></slot></template>
<template #stage><slot name="stage"></slot></template>
<template #workspace-content><slot name="workspace-content" :editorService="editorService"></slot></template>
</Workspace>
</slot>
</template>
<template #props-panel>
<slot name="props-panel">
<PropsPanel
:extend-state="extendFormState"
:disabled-show-src="disabledShowSrc"
@mounted="propsPanelMountedHandler"
@unmounted="propsPanelUnmountedHandler"
@form-error="propsPanelFormErrorHandler"
@submit-error="propsPanelSubmitErrorHandler"
>
<template #props-panel-header>
<slot name="props-panel-header"></slot>
</template>
</PropsPanel>
</slot>
</template>
<template #empty><slot name="empty" :editorService="editorService"></slot></template>
<template #content-after>
<slot name="content-after"></slot>
</template>
<template #footer>
<slot name="footer"></slot>
</template>
<template #page-bar><slot name="page-bar"></slot></template>
<template #page-bar-add-button><slot name="page-bar-add-button"></slot></template>
<template #page-bar-title="{ page }"><slot name="page-bar-title" :page="page"></slot></template>
<template #page-bar-popover="{ page }"><slot name="page-bar-popover" :page="page"></slot></template>
<template #page-list-popover="{ list }"><slot name="page-list-popover" :list="list"></slot></template>
</Framework>
</template>
<script lang="ts" setup>
import { EventEmitter } from 'events';
import { provide, ref } from 'vue';
import type { MApp } from '@tmagic/core';
import Framework from './layouts/Framework.vue';
import TMagicNavMenu from './layouts/NavMenu.vue';
import FormPanel from './layouts/props-panel/FormPanel.vue';
import PropsPanel from './layouts/props-panel/PropsPanel.vue';
import Sidebar from './layouts/sidebar/Sidebar.vue';
import Workspace from './layouts/workspace/Workspace.vue';
import codeBlockService from './services/codeBlock';
import componentListService from './services/componentList';
import dataSourceService from './services/dataSource';
import depService from './services/dep';
import editorService from './services/editor';
import eventsService from './services/events';
import historyService from './services/history';
import keybindingService from './services/keybinding';
import propsService from './services/props';
import stageOverlayService from './services/stageOverlay';
import storageService from './services/storage';
import uiService from './services/ui';
import keybindingConfig from './utils/keybinding-config';
import { defaultEditorProps, EditorProps } from './editorProps';
import { initServiceEvents, initServiceState } from './initService';
import type { TreeNodeData } from './type';
import type { EditorSlots, EventBus, Services, StageOptions } from './type';
defineSlots<EditorSlots>();
defineOptions({
name: 'MEditor',
});
const emit = defineEmits<{
'props-panel-mounted': [instance: InstanceType<typeof FormPanel>];
'props-panel-unmounted': [];
'update:modelValue': [value: MApp | null];
'props-form-error': [e: any];
'props-submit-error': [e: any];
'layer-node-dblclick': [event: MouseEvent, data: TreeNodeData];
}>();
const props = withDefaults(defineProps<EditorProps>(), defaultEditorProps);
const services: Services = {
componentListService,
eventsService,
historyService,
propsService,
editorService,
uiService,
storageService,
codeBlockService,
depService,
dataSourceService,
keybindingService,
stageOverlayService,
};
initServiceEvents(props, emit, services);
initServiceState(props, services);
keybindingService.register(keybindingConfig);
keybindingService.registerEl('global');
const stageOptions: StageOptions = {
runtimeUrl: props.runtimeUrl,
autoScrollIntoView: props.autoScrollIntoView,
render: props.render,
moveableOptions: props.moveableOptions,
canSelect: props.canSelect,
updateDragEl: props.updateDragEl,
isContainer: props.isContainer,
// sourceIds 为空表示从组件列表新增(尚无 id否则是画布上拖动已有组件
canDropIn: props.canDropIn
? (sourceIds, targetId) =>
props.canDropIn!(sourceIds, targetId, sourceIds.length === 0 ? 'stage-add' : 'stage-drag')
: undefined,
containerHighlightClassName: props.containerHighlightClassName,
containerHighlightDuration: props.containerHighlightDuration,
containerHighlightType: props.containerHighlightType,
disabledDragStart: props.disabledDragStart,
renderType: props.renderType,
guidesOptions: props.guidesOptions,
disabledMultiSelect: props.disabledMultiSelect,
alwaysMultiSelect: props.alwaysMultiSelect,
disabledFlashTip: props.disabledFlashTip,
beforeDblclick: props.beforeDblclick,
};
stageOverlayService.set('stageOptions', stageOptions);
const propsPanelRef = ref<InstanceType<typeof FormPanel> | null>(null);
provide('services', services);
provide('codeOptions', props.codeOptions);
provide('stageOptions', stageOptions);
/**
* 把顶层 `extendFormState` 提供给非 PropsPanel 链路上的组件使用(例如历史差异对话框 HistoryDiffDialog
* 内部的 CompareForm。这样所有依赖业务上下文的表单 filterFunction 都能拿到一致的扩展状态,
* 与 PropsPanel 通过 `:extend-state` 显式传入的方式保持等价。
*/
provide('extendFormState', props.extendFormState);
/**
* 提供 PropsPanel 主属性表单的 formState getter供历史差异弹窗复用
* 让 CompareForm 与 PropsPanel 的 filterFunction 上下文保持一致。
*/
provide('getPropsPanelFormState', () => propsPanelRef.value?.configForm?.formState);
/**
* 把历史记录面板的自定义扩展 tab 提供给深层的 HistoryListPanel它挂在 NavMenu 中,
* 以 markRaw component 形式渲染,无法直接通过 props 透传)。业务方可借此在历史记录
* 面板内追加自定义模块的历史 tab。
*/
provide('historyListExtraTabs', props.historyListExtraTabs);
provide<EventBus>('eventBus', new EventEmitter());
const propsPanelMountedHandler = (e: InstanceType<typeof FormPanel>) => {
propsPanelRef.value = e;
emit('props-panel-mounted', e);
};
const propsPanelUnmountedHandler = () => {
propsPanelRef.value = null;
emit('props-panel-unmounted');
};
const propsPanelSubmitErrorHandler = (e: any) => {
emit('props-submit-error', e);
};
const propsPanelFormErrorHandler = (e: any) => {
emit('props-form-error', e);
};
const layerNodeDblclickHandler = (event: MouseEvent, data: TreeNodeData) => {
emit('layer-node-dblclick', event, data);
};
defineExpose(services);
</script>