refactor(editor): editor组件改成setup

This commit is contained in:
roymondchen 2023-10-07 17:01:33 +08:00
parent 65854d9c0a
commit 803bf323ce
4 changed files with 132 additions and 210 deletions

View File

@ -5,7 +5,7 @@ import { EntryType } from '../types';
export const prepareEntryFile = async (app: App) => {
const { moduleMainFilePath, options } = app;
const { componentFileAffix, dynamicImport, hooks, useTs } = options;
const { componentFileAffix, dynamicImport, hooks, useTs = true } = options;
let contentMap: Record<string, string> = {
'comp-entry': generateContent(useTs, EntryType.COMPONENT, moduleMainFilePath.componentMap, componentFileAffix),

View File

@ -80,11 +80,13 @@
</Framework>
</template>
<script lang="ts">
import { defineComponent, provide, reactive } from 'vue';
<script lang="ts" setup>
import { provide, reactive } from 'vue';
import { MApp } from '@tmagic/schema';
import Framework from './layouts/Framework.vue';
import NavMenu from './layouts/NavMenu.vue';
import TMagicNavMenu from './layouts/NavMenu.vue';
import PropsPanel from './layouts/PropsPanel.vue';
import Sidebar from './layouts/sidebar/Sidebar.vue';
import Workspace from './layouts/workspace/Workspace.vue';
@ -100,66 +102,59 @@ import propsService from './services/props';
import storageService from './services/storage';
import uiService from './services/ui';
import keybindingConfig from './utils/keybinding-config';
import editorProps from './editorProps';
import { defaultEditorProps, EditorProps } from './editorProps';
import { initServiceEvents, initServiceState } from './initService';
import type { Services } from './type';
export default defineComponent({
defineOptions({
name: 'MEditor',
components: {
TMagicNavMenu: NavMenu,
Sidebar,
Workspace,
PropsPanel,
Framework,
},
props: editorProps,
emits: ['props-panel-mounted', 'update:modelValue'],
setup(props, { emit }) {
const services: Services = {
componentListService,
eventsService,
historyService,
propsService,
editorService,
uiService,
storageService,
codeBlockService,
depService,
dataSourceService,
keybindingService,
};
initServiceEvents(props, emit, services);
initServiceState(props, services);
keybindingService.registe(keybindingConfig);
keybindingService.registeEl('global');
provide('services', services);
provide('codeOptions', props.codeOptions);
provide(
'stageOptions',
reactive({
runtimeUrl: props.runtimeUrl,
autoScrollIntoView: props.autoScrollIntoView,
render: props.render,
moveableOptions: props.moveableOptions,
canSelect: props.canSelect,
updateDragEl: props.updateDragEl,
isContainer: props.isContainer,
containerHighlightClassName: props.containerHighlightClassName,
containerHighlightDuration: props.containerHighlightDuration,
containerHighlightType: props.containerHighlightType,
disabledDragStart: props.disabledDragStart,
}),
);
return services;
},
});
const emit = defineEmits<{
'props-panel-mounted': [instance: InstanceType<typeof PropsPanel>];
'update:modelValue': [value: MApp | null];
}>();
const props = withDefaults(defineProps<EditorProps>(), defaultEditorProps);
const services: Services = {
componentListService,
eventsService,
historyService,
propsService,
editorService,
uiService,
storageService,
codeBlockService,
depService,
dataSourceService,
keybindingService,
};
initServiceEvents(props, emit, services);
initServiceState(props, services);
keybindingService.registe(keybindingConfig);
keybindingService.registeEl('global');
provide('services', services);
provide('codeOptions', props.codeOptions);
provide(
'stageOptions',
reactive({
runtimeUrl: props.runtimeUrl,
autoScrollIntoView: props.autoScrollIntoView,
render: props.render,
moveableOptions: props.moveableOptions,
canSelect: props.canSelect,
updateDragEl: props.updateDragEl,
isContainer: props.isContainer,
containerHighlightClassName: props.containerHighlightClassName,
containerHighlightDuration: props.containerHighlightDuration,
containerHighlightType: props.containerHighlightType,
disabledDragStart: props.disabledDragStart,
}),
);
defineExpose(services);
</script>

View File

@ -1,8 +1,6 @@
import type { PropType } from 'vue';
import type { EventOption } from '@tmagic/core';
import type { FormConfig, FormState } from '@tmagic/form';
import type { DataSourceSchema, MApp, MNode } from '@tmagic/schema';
import type { DataSourceSchema, Id, MApp, MNode } from '@tmagic/schema';
import StageCore, {
CONTAINER_HIGHLIGHT_CLASS_NAME,
ContainerHighlightType,
@ -21,145 +19,68 @@ import type {
StageRect,
} from './type';
export default {
export interface EditorProps {
/** 页面初始值 */
modelValue: {
type: Object as PropType<MApp>,
default: () => ({}),
require: true,
},
/** 左侧面板中的组件列表 */
componentGroupList: {
type: Array as PropType<ComponentGroup[]>,
default: () => [],
},
/** 左侧面板中的组件列表 */
datasourceList: {
type: Array as PropType<DatasourceTypeOption[]>,
default: () => [],
},
modelValue?: MApp;
/** 左侧面板中的组件类型列表 */
componentGroupList?: ComponentGroup[];
/** 左侧面板中的数据源类型列表 */
datasourceList?: DatasourceTypeOption[];
/** 左侧面板配置 */
sidebar: {
type: Object as PropType<SideBarData>,
},
sidebar?: SideBarData;
/** 顶部工具栏配置 */
menu: {
type: Object as PropType<MenuBarData>,
default: () => ({ left: [], right: [] }),
},
menu?: MenuBarData;
/** 组件树右键菜单 */
layerContentMenu: {
type: Array as PropType<(MenuButton | MenuComponent)[]>,
default: () => [],
},
layerContentMenu?: (MenuButton | MenuComponent)[];
/** 画布右键菜单 */
stageContentMenu: {
type: Array as PropType<(MenuButton | MenuComponent)[]>,
default: () => [],
},
stageContentMenu?: (MenuButton | MenuComponent)[];
/** 中间工作区域中画布渲染的内容 */
render: {
type: Function as PropType<(stage: StageCore) => HTMLDivElement | Promise<HTMLDivElement>>,
},
render?: (stage: StageCore) => HTMLDivElement | Promise<HTMLDivElement>;
/** 中间工作区域中画布通过iframe渲染时的页面url */
runtimeUrl: String,
runtimeUrl?: string;
/** 选中时是否自动滚动到可视区域 */
autoScrollIntoView: Boolean,
autoScrollIntoView?: boolean;
/** 组件的属性配置表单的dsl */
propsConfigs: {
type: Object as PropType<Record<string, FormConfig>>,
default: () => ({}),
},
propsConfigs?: Record<string, FormConfig>;
/** 添加组件时的默认值 */
propsValues: {
type: Object as PropType<Record<string, Partial<MNode>>>,
default: () => ({}),
},
propsValues?: Record<string, Partial<MNode>>;
/** 组件联动事件选项列表 */
eventMethodList: {
type: Object as PropType<Record<string, { events: EventOption[]; methods: EventOption[] }>>,
default: () => ({}),
},
eventMethodList?: Record<string, { events: EventOption[]; methods: EventOption[] }>;
/** 添加数据源时的默认值 */
datasourceValues: {
type: Object as PropType<Record<string, Partial<DataSourceSchema>>>,
default: () => ({}),
},
datasourceValues?: Record<string, Partial<DataSourceSchema>>;
/** 数据源的属性配置表单的dsl */
datasourceConfigs: {
type: Object as PropType<Record<string, FormConfig>>,
default: () => ({}),
},
datasourceConfigs?: Record<string, FormConfig>;
/** 画布中组件选中框的移动范围 */
moveableOptions: {
type: [Object, Function] as PropType<
MoveableOptions | ((config?: CustomizeMoveableOptionsCallbackConfig) => MoveableOptions)
>,
},
moveableOptions?: MoveableOptions | ((config?: CustomizeMoveableOptionsCallbackConfig) => MoveableOptions);
/** 编辑器初始化时默认选中的组件ID */
defaultSelected: {
type: [Number, String],
},
defaultSelected?: Id;
canSelect?: (el: HTMLElement) => boolean | Promise<boolean>;
isContainer?: (el: HTMLElement) => boolean | Promise<boolean>;
containerHighlightClassName?: string;
containerHighlightDuration?: number;
containerHighlightType?: ContainerHighlightType;
stageRect?: StageRect;
codeOptions?: { [key: string]: any };
updateDragEl?: UpdateDragEl;
disabledDragStart?: boolean;
extendFormState?: (state: FormState) => Record<string, any> | Promise<Record<string, any>>;
}
canSelect: {
type: Function as PropType<(el: HTMLElement) => boolean | Promise<boolean>>,
default: (el: HTMLElement) => Boolean(el.id),
},
isContainer: {
type: Function as PropType<(el: HTMLElement) => boolean | Promise<boolean>>,
default: (el: HTMLElement) => el.classList.contains('magic-ui-container'),
},
containerHighlightClassName: {
type: String,
default: CONTAINER_HIGHLIGHT_CLASS_NAME,
},
containerHighlightDuration: {
type: Number,
default: 800,
},
containerHighlightType: {
type: String as PropType<ContainerHighlightType>,
default: ContainerHighlightType.DEFAULT,
},
stageRect: {
type: [String, Object] as PropType<StageRect>,
},
codeOptions: {
type: Object,
default: () => ({}),
},
updateDragEl: {
type: Function as PropType<UpdateDragEl>,
},
disabledDragStart: {
type: Boolean,
},
extendFormState: {
type: Function as PropType<(state: FormState) => Record<string, any> | Promise<Record<string, any>>>,
},
export const defaultEditorProps = {
componentGroupList: () => [],
datasourceList: () => [],
menu: () => ({ left: [], right: [] }),
layerContentMenu: () => [],
stageContentMenu: () => [],
propsConfigs: () => ({}),
propsValues: () => ({}),
eventMethodList: () => ({}),
datasourceValues: () => ({}),
datasourceConfigs: () => ({}),
canSelect: (el: HTMLElement) => Boolean(el.id),
isContainer: (el: HTMLElement) => el.classList.contains('magic-ui-container'),
containerHighlightClassName: CONTAINER_HIGHLIGHT_CLASS_NAME,
containerHighlightDuration: 800,
containerHighlightType: ContainerHighlightType.DEFAULT,
codeOptions: () => ({}),
};

View File

@ -1,4 +1,3 @@
import type { ExtractPropTypes } from 'vue';
import { onUnmounted, toRaw, watch } from 'vue';
import { cloneDeep } from 'lodash-es';
@ -6,6 +5,7 @@ import type { EventOption } from '@tmagic/core';
import type { CodeBlockContent, DataSourceSchema, Id, MApp, MNode, MPage } from '@tmagic/schema';
import { getNodes } from '@tmagic/utils';
import PropsPanel from './layouts/PropsPanel.vue';
import type { Target } from './services/dep';
import {
createCodeBlockTarget,
@ -13,7 +13,7 @@ import {
createDataSourceMethodTarget,
createDataSourceTarget,
} from './utils/dep';
import editorProps from './editorProps';
import { EditorProps } from './editorProps';
import { DepTargetType, Services } from './type';
export declare type LooseRequired<T> = {
@ -21,7 +21,7 @@ export declare type LooseRequired<T> = {
};
export const initServiceState = (
props: Readonly<LooseRequired<Readonly<ExtractPropTypes<typeof editorProps>>>>,
props: EditorProps,
{
editorService,
historyService,
@ -38,7 +38,7 @@ export const initServiceState = (
watch(
() => props.modelValue,
(modelValue) => {
editorService.set('root', modelValue);
editorService.set('root', modelValue || null);
},
{
immediate: true,
@ -47,7 +47,7 @@ export const initServiceState = (
watch(
() => props.componentGroupList,
(componentGroupList) => componentListService.setList(componentGroupList),
(componentGroupList) => componentGroupList && componentListService.setList(componentGroupList),
{
immediate: true,
},
@ -55,7 +55,7 @@ export const initServiceState = (
watch(
() => props.datasourceList,
(datasourceList) => dataSourceService.set('datasourceTypeList', datasourceList),
(datasourceList) => datasourceList && dataSourceService.set('datasourceTypeList', datasourceList),
{
immediate: true,
},
@ -63,7 +63,7 @@ export const initServiceState = (
watch(
() => props.propsConfigs,
(configs) => propsService.setPropsConfigs(configs),
(configs) => configs && propsService.setPropsConfigs(configs),
{
immediate: true,
},
@ -71,7 +71,7 @@ export const initServiceState = (
watch(
() => props.propsValues,
(values) => propsService.setPropsValues(values),
(values) => values && propsService.setPropsValues(values),
{
immediate: true,
},
@ -83,10 +83,11 @@ export const initServiceState = (
const eventsList: Record<string, EventOption[]> = {};
const methodsList: Record<string, EventOption[]> = {};
Object.keys(eventMethodList).forEach((type: string) => {
eventsList[type] = eventMethodList[type].events;
methodsList[type] = eventMethodList[type].methods;
});
eventMethodList &&
Object.keys(eventMethodList).forEach((type: string) => {
eventsList[type] = eventMethodList[type].events;
methodsList[type] = eventMethodList[type].methods;
});
eventsService.setEvents(eventsList);
eventsService.setMethods(methodsList);
@ -99,9 +100,10 @@ export const initServiceState = (
watch(
() => props.datasourceConfigs,
(configs) => {
Object.entries(configs).forEach(([key, value]) => {
dataSourceService.setFormConfig(key, value);
});
configs &&
Object.entries(configs).forEach(([key, value]) => {
dataSourceService.setFormConfig(key, value);
});
},
{
immediate: true,
@ -111,9 +113,10 @@ export const initServiceState = (
watch(
() => props.datasourceValues,
(values) => {
Object.entries(values).forEach(([key, value]) => {
dataSourceService.setFormValue(key, value);
});
values &&
Object.entries(values).forEach(([key, value]) => {
dataSourceService.setFormValue(key, value);
});
},
{
immediate: true,
@ -148,8 +151,9 @@ export const initServiceState = (
};
export const initServiceEvents = (
props: Readonly<LooseRequired<Readonly<ExtractPropTypes<typeof editorProps>>>>,
emit: (event: 'props-panel-mounted' | 'update:modelValue', ...args: any[]) => void,
props: EditorProps,
emit: ((event: 'props-panel-mounted', instance: InstanceType<typeof PropsPanel>) => void) &
((event: 'update:modelValue', value: MApp | null) => void),
{ editorService, codeBlockService, dataSourceService, depService }: Services,
) => {
const getApp = () => {
@ -247,7 +251,7 @@ export const initServiceEvents = (
depService.addTarget(createDataSourceCondTarget(ds.id));
};
const rootChangeHandler = async (value: MApp, preValue?: MApp | null) => {
const rootChangeHandler = async (value: MApp | null, preValue?: MApp | null) => {
const nodeId = editorService.get('node')?.id || props.defaultSelected;
let node;
if (nodeId) {
@ -268,6 +272,8 @@ export const initServiceEvents = (
emit('update:modelValue', value);
}
if (!value) return;
value.codeBlocks = value.codeBlocks || {};
value.dataSources = value.dataSources || [];