tmagic-editor/packages/editor/src/initService.ts

454 lines
12 KiB
TypeScript
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.

import { onBeforeUnmount, reactive, toRaw, watch } from 'vue';
import { cloneDeep } from 'lodash-es';
import type {
CodeBlockContent,
DataSourceSchema,
EventOption,
Id,
MApp,
MNode,
MPage,
MPageFragment,
} from '@tmagic/core';
import {
createCodeBlockTarget,
createDataSourceCondTarget,
createDataSourceMethodTarget,
createDataSourceTarget,
DepTargetType,
Target,
} from '@tmagic/core';
import { isPage } from '@tmagic/utils';
import PropsPanel from './layouts/PropsPanel.vue';
import { EditorProps } from './editorProps';
import { Services } from './type';
import { traverseNode } from './utils';
export declare type LooseRequired<T> = {
[P in string & keyof T]: T[P];
};
export const initServiceState = (
props: EditorProps,
{
editorService,
historyService,
componentListService,
propsService,
eventsService,
uiService,
codeBlockService,
keybindingService,
dataSourceService,
}: Services,
) => {
// 初始值变化,重新设置节点信息
watch(
() => props.modelValue,
(modelValue) => {
editorService.set('root', modelValue || null);
},
{
immediate: true,
},
);
watch(
() => props.disabledMultiSelect,
(disabledMultiSelect) => {
editorService.set('disabledMultiSelect', disabledMultiSelect || false);
},
{
immediate: true,
},
);
watch(
() => props.componentGroupList,
(componentGroupList) => componentGroupList && componentListService.setList(componentGroupList),
{
immediate: true,
},
);
watch(
() => props.datasourceList,
(datasourceList) => datasourceList && dataSourceService.set('datasourceTypeList', datasourceList),
{
immediate: true,
},
);
watch(
() => props.propsConfigs,
(configs) => configs && propsService.setPropsConfigs(configs),
{
immediate: true,
},
);
watch(
() => props.propsValues,
(values) => values && propsService.setPropsValues(values),
{
immediate: true,
},
);
watch(
() => props.eventMethodList,
(eventMethodList) => {
const eventsList: Record<string, EventOption[]> = {};
const methodsList: Record<string, EventOption[]> = {};
eventMethodList &&
Object.keys(eventMethodList).forEach((type: string) => {
eventsList[type] = eventMethodList[type].events;
methodsList[type] = eventMethodList[type].methods;
});
eventsService.setEvents(eventsList);
eventsService.setMethods(methodsList);
},
{
immediate: true,
},
);
watch(
() => props.datasourceConfigs,
(configs) => {
configs &&
Object.entries(configs).forEach(([key, value]) => {
dataSourceService.setFormConfig(key, value);
});
},
{
immediate: true,
},
);
watch(
() => props.datasourceValues,
(values) => {
values &&
Object.entries(values).forEach(([key, value]) => {
dataSourceService.setFormValue(key, value);
});
},
{
immediate: true,
},
);
watch(
() => props.datasourceEventMethodList,
(eventMethodList) => {
const eventsList: Record<string, EventOption[]> = {};
const methodsList: Record<string, EventOption[]> = {};
eventMethodList &&
Object.keys(eventMethodList).forEach((type: string) => {
eventsList[type] = eventMethodList[type].events;
methodsList[type] = eventMethodList[type].methods;
});
Object.entries(eventsList).forEach(([key, value]) => {
dataSourceService.setFormEvent(key, value);
});
Object.entries(methodsList).forEach(([key, value]) => {
dataSourceService.setFormMethod(key, value);
});
},
{
immediate: true,
},
);
watch(
() => props.defaultSelected,
(defaultSelected) => defaultSelected && editorService.select(defaultSelected),
{
immediate: true,
},
);
watch(
() => props.stageRect,
(stageRect) => stageRect && uiService.set('stageRect', stageRect),
{
immediate: true,
},
);
onBeforeUnmount(() => {
editorService.resetState();
historyService.resetState();
propsService.resetState();
uiService.resetState();
componentListService.resetState();
codeBlockService.resetState();
keybindingService.reset();
});
};
export const initServiceEvents = (
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 rootChangeHandler = async (value: MApp | null, preValue?: MApp | null) => {
if (!value) return;
value.codeBlocks = value.codeBlocks || {};
value.dataSources = value.dataSources || [];
codeBlockService.setCodeDsl(value.codeBlocks);
dataSourceService.set('dataSources', value.dataSources);
depService.removeTargets(DepTargetType.CODE_BLOCK);
Object.entries(value.codeBlocks).forEach(([id, code]) => {
depService.addTarget(createCodeBlockTarget(id, code));
});
dataSourceService.get('dataSources').forEach((ds) => {
initDataSourceDepTarget(ds);
});
if (Array.isArray(value.items)) {
collectIdle(value.items, true);
} else {
depService.clear();
delete value.dataSourceDeps;
delete value.dataSourceCondDeps;
}
const nodeId = editorService.get('node')?.id || props.defaultSelected;
let node;
if (nodeId) {
node = editorService.getNodeById(nodeId);
}
if (node && node !== value) {
await editorService.select(node.id);
} else if (value.items?.length) {
await editorService.select(value.items[0]);
} else if (value.id) {
editorService.set('nodes', [value]);
editorService.set('parent', null);
editorService.set('page', null);
}
if (toRaw(value) !== toRaw(preValue)) {
emit('update:modelValue', value);
}
};
const getApp = () => {
const stage = editorService.get('stage');
return stage?.renderer?.runtime?.getApp?.();
};
const updateDataSourceSchema = () => {
const root = editorService.get('root');
if (root?.dataSources) {
getApp()?.dataSourceManager?.updateSchema(root.dataSources);
}
};
const targetAddHandler = (target: Target) => {
const root = editorService.get('root');
if (!root) return;
if (target.type === DepTargetType.DATA_SOURCE) {
if (!root.dataSourceDeps) {
root.dataSourceDeps = {};
}
root.dataSourceDeps[target.id] = target.deps;
}
if (target.type === DepTargetType.DATA_SOURCE_COND) {
if (!root.dataSourceCondDeps) {
root.dataSourceCondDeps = {};
}
root.dataSourceCondDeps[target.id] = target.deps;
}
};
const targetRemoveHandler = (id: string | number) => {
const root = editorService.get('root');
if (root?.dataSourceDeps) {
delete root.dataSourceDeps[id];
}
if (root?.dataSourceCondDeps) {
delete root.dataSourceCondDeps[id];
}
};
const collectedHandler = (nodes: MNode[], deep: boolean) => {
const root = editorService.get('root');
const stage = editorService.get('stage');
if (!root || !stage) return;
const app = getApp();
if (!app) return;
if (app.dsl) {
app.dsl.dataSourceDeps = root.dataSourceDeps;
app.dsl.dataSourceCondDeps = root.dataSourceCondDeps;
app.dsl.dataSources = root.dataSources;
}
updateDataSourceSchema();
const allNodes: MNode[] = [];
if (deep) {
nodes.forEach((node) => {
traverseNode<MNode>(node, (node) => {
if (!allNodes.includes(node)) {
allNodes.push(node);
}
});
});
} else {
allNodes.push(...nodes);
}
const deps = Object.values(root.dataSourceDeps || {});
deps.forEach((dep) => {
Object.keys(dep).forEach((id) => {
const node = allNodes.find((node) => node.id === id);
node &&
stage.update({
config: cloneDeep(node),
parentId: editorService.getParentById(node.id)?.id,
root: cloneDeep(root),
});
});
});
};
depService.on('add-target', targetAddHandler);
depService.on('remove-target', targetRemoveHandler);
depService.on('collected', collectedHandler);
const initDataSourceDepTarget = (ds: DataSourceSchema) => {
depService.addTarget(createDataSourceTarget(ds, reactive({})));
depService.addTarget(createDataSourceMethodTarget(ds, reactive({})));
depService.addTarget(createDataSourceCondTarget(ds, reactive({})));
};
const collectIdle = (nodes: MNode[], deep: boolean) => {
nodes.forEach((node) => {
let pageId: Id | undefined;
if (isPage(node)) {
pageId = node.id;
} else {
const info = editorService.getNodeInfo(node.id);
pageId = info.page?.id;
}
depService.collectIdle([node], { pageId }, deep);
});
};
// 新增节点,收集依赖
const nodeAddHandler = (nodes: MNode[]) => {
collectIdle(nodes, true);
};
// 节点更新,收集依赖
const nodeUpdateHandler = (nodes: MNode[]) => {
collectIdle(nodes, true);
};
// 节点删除,清除对齐的依赖收集
const nodeRemoveHandler = (nodes: MNode[]) => {
depService.clear(nodes);
};
// 由于历史记录变化是更新整个page所以历史记录变化时需要重新收集依赖
const historyChangeHandler = (page: MPage | MPageFragment) => {
collectIdle([page], true);
};
editorService.on('history-change', historyChangeHandler);
editorService.on('root-change', rootChangeHandler);
editorService.on('add', nodeAddHandler);
editorService.on('remove', nodeRemoveHandler);
editorService.on('update', nodeUpdateHandler);
const codeBlockAddOrUpdateHandler = (id: Id, codeBlock: CodeBlockContent) => {
if (depService.hasTarget(id, DepTargetType.CODE_BLOCK)) {
depService.getTarget(id, DepTargetType.CODE_BLOCK)!.name = codeBlock.name;
return;
}
depService.addTarget(createCodeBlockTarget(id, codeBlock));
};
const codeBlockRemoveHandler = (id: Id) => {
depService.removeTarget(id, DepTargetType.CODE_BLOCK);
};
codeBlockService.on('addOrUpdate', codeBlockAddOrUpdateHandler);
codeBlockService.on('remove', codeBlockRemoveHandler);
const dataSourceAddHandler = (config: DataSourceSchema) => {
initDataSourceDepTarget(config);
getApp()?.dataSourceManager?.addDataSource(config);
};
const dataSourceUpdateHandler = (config: DataSourceSchema) => {
const root = editorService.get('root');
removeDataSourceTarget(config.id);
initDataSourceDepTarget(config);
collectIdle(root?.items || [], true);
};
const removeDataSourceTarget = (id: string) => {
depService.removeTarget(id, DepTargetType.DATA_SOURCE);
depService.removeTarget(id, DepTargetType.DATA_SOURCE_COND);
depService.removeTarget(id, DepTargetType.DATA_SOURCE_METHOD);
};
const dataSourceRemoveHandler = (id: string) => {
removeDataSourceTarget(id);
getApp()?.dataSourceManager?.removeDataSource(id);
};
dataSourceService.on('add', dataSourceAddHandler);
dataSourceService.on('update', dataSourceUpdateHandler);
dataSourceService.on('remove', dataSourceRemoveHandler);
onBeforeUnmount(() => {
depService.off('add-target', targetAddHandler);
depService.off('remove-target', targetRemoveHandler);
depService.off('collected', collectedHandler);
editorService.off('history-change', historyChangeHandler);
editorService.off('root-change', rootChangeHandler);
editorService.off('add', nodeAddHandler);
editorService.off('remove', nodeRemoveHandler);
editorService.off('update', nodeUpdateHandler);
codeBlockService.off('addOrUpdate', codeBlockAddOrUpdateHandler);
codeBlockService.off('remove', codeBlockRemoveHandler);
dataSourceService.off('add', dataSourceAddHandler);
dataSourceService.off('update', dataSourceUpdateHandler);
dataSourceService.off('remove', dataSourceRemoveHandler);
});
};