mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-09-19 21:06:06 +08:00
feat(editor): 优化依赖收集,数据源代码块分开收集
This commit is contained in:
parent
b215062177
commit
040b787e8f
@ -1,4 +1,4 @@
|
|||||||
import { computed, onBeforeUnmount, reactive, toRaw, watch } from 'vue';
|
import { onBeforeUnmount, reactive, toRaw, watch } from 'vue';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
import type TMagicCore from '@tmagic/core';
|
import type TMagicCore from '@tmagic/core';
|
||||||
@ -8,7 +8,7 @@ import type {
|
|||||||
EventOption,
|
EventOption,
|
||||||
Id,
|
Id,
|
||||||
MApp,
|
MApp,
|
||||||
MNode,
|
MComponent,
|
||||||
MPage,
|
MPage,
|
||||||
MPageFragment,
|
MPageFragment,
|
||||||
} from '@tmagic/core';
|
} from '@tmagic/core';
|
||||||
@ -22,7 +22,7 @@ import {
|
|||||||
Target,
|
Target,
|
||||||
} from '@tmagic/core';
|
} from '@tmagic/core';
|
||||||
import { ChangeRecord } from '@tmagic/form';
|
import { ChangeRecord } from '@tmagic/form';
|
||||||
import { getNodes, isPage, isValueIncludeDataSource, traverseNode } from '@tmagic/utils';
|
import { getNodes, isPage, isValueIncludeDataSource } from '@tmagic/utils';
|
||||||
|
|
||||||
import PropsPanel from './layouts/PropsPanel.vue';
|
import PropsPanel from './layouts/PropsPanel.vue';
|
||||||
import { isIncludeDataSource } from './utils/editor';
|
import { isIncludeDataSource } from './utils/editor';
|
||||||
@ -106,11 +106,12 @@ export const initServiceState = (
|
|||||||
const eventsList: Record<string, EventOption[]> = {};
|
const eventsList: Record<string, EventOption[]> = {};
|
||||||
const methodsList: Record<string, EventOption[]> = {};
|
const methodsList: Record<string, EventOption[]> = {};
|
||||||
|
|
||||||
eventMethodList &&
|
if (eventMethodList) {
|
||||||
Object.keys(eventMethodList).forEach((type: string) => {
|
for (const type of Object.keys(eventMethodList)) {
|
||||||
eventsList[type] = eventMethodList[type].events;
|
eventsList[type] = eventMethodList[type].events;
|
||||||
methodsList[type] = eventMethodList[type].methods;
|
methodsList[type] = eventMethodList[type].methods;
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
eventsService.setEvents(eventsList);
|
eventsService.setEvents(eventsList);
|
||||||
eventsService.setMethods(methodsList);
|
eventsService.setMethods(methodsList);
|
||||||
@ -123,10 +124,13 @@ export const initServiceState = (
|
|||||||
watch(
|
watch(
|
||||||
() => props.datasourceConfigs,
|
() => props.datasourceConfigs,
|
||||||
(configs) => {
|
(configs) => {
|
||||||
configs &&
|
if (!configs) {
|
||||||
Object.entries(configs).forEach(([key, value]) => {
|
return;
|
||||||
dataSourceService.setFormConfig(key, value);
|
}
|
||||||
});
|
|
||||||
|
for (const [key, value] of Object.entries(configs)) {
|
||||||
|
dataSourceService.setFormConfig(key, value);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
@ -136,10 +140,13 @@ export const initServiceState = (
|
|||||||
watch(
|
watch(
|
||||||
() => props.datasourceValues,
|
() => props.datasourceValues,
|
||||||
(values) => {
|
(values) => {
|
||||||
values &&
|
if (!values) {
|
||||||
Object.entries(values).forEach(([key, value]) => {
|
return;
|
||||||
dataSourceService.setFormValue(key, value);
|
}
|
||||||
});
|
|
||||||
|
for (const [key, value] of Object.entries(values)) {
|
||||||
|
dataSourceService.setFormValue(key, value);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
@ -152,18 +159,20 @@ export const initServiceState = (
|
|||||||
const eventsList: Record<string, EventOption[]> = {};
|
const eventsList: Record<string, EventOption[]> = {};
|
||||||
const methodsList: Record<string, EventOption[]> = {};
|
const methodsList: Record<string, EventOption[]> = {};
|
||||||
|
|
||||||
eventMethodList &&
|
if (eventMethodList) {
|
||||||
Object.keys(eventMethodList).forEach((type: string) => {
|
for (const type of Object.keys(eventMethodList)) {
|
||||||
eventsList[type] = eventMethodList[type].events;
|
eventsList[type] = eventMethodList[type].events;
|
||||||
methodsList[type] = eventMethodList[type].methods;
|
methodsList[type] = eventMethodList[type].methods;
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Object.entries(eventsList).forEach(([key, value]) => {
|
for (const [key, value] of Object.entries(eventsList)) {
|
||||||
dataSourceService.setFormEvent(key, value);
|
dataSourceService.setFormEvent(key, value);
|
||||||
});
|
}
|
||||||
Object.entries(methodsList).forEach(([key, value]) => {
|
|
||||||
|
for (const [key, value] of Object.entries(methodsList)) {
|
||||||
dataSourceService.setFormMethod(key, value);
|
dataSourceService.setFormMethod(key, value);
|
||||||
});
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
@ -203,6 +212,113 @@ export const initServiceEvents = (
|
|||||||
((event: 'update:modelValue', value: MApp | null) => void),
|
((event: 'update:modelValue', value: MApp | null) => void),
|
||||||
{ editorService, codeBlockService, dataSourceService, depService }: Services,
|
{ editorService, codeBlockService, dataSourceService, depService }: Services,
|
||||||
) => {
|
) => {
|
||||||
|
const getTMagicApp = () => {
|
||||||
|
const renderer = editorService.get('stage')?.renderer;
|
||||||
|
if (!renderer) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renderer.runtime) {
|
||||||
|
return renderer.runtime.getApp?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise<TMagicCore | undefined>((resolve) => {
|
||||||
|
// 设置 10s 超时
|
||||||
|
const timeout = globalThis.setTimeout(() => {
|
||||||
|
resolve(undefined);
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
|
renderer.on('runtime-ready', () => {
|
||||||
|
if (timeout) {
|
||||||
|
globalThis.clearTimeout(timeout);
|
||||||
|
}
|
||||||
|
resolve(renderer.runtime?.getApp?.());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateStageNodes = (nodes: MComponent[]) => {
|
||||||
|
for (const node of nodes) {
|
||||||
|
updateStageNode(node);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateStageNode = (node: MComponent) => {
|
||||||
|
const root = editorService.get('root');
|
||||||
|
if (!root) return;
|
||||||
|
|
||||||
|
return editorService.get('stage')?.update({
|
||||||
|
config: cloneDeep(node),
|
||||||
|
parentId: editorService.getParentById(node.id)?.id,
|
||||||
|
root: cloneDeep(root),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateDataSourceSchema = async () => {
|
||||||
|
const root = editorService.get('root');
|
||||||
|
const app = await getTMagicApp();
|
||||||
|
|
||||||
|
if (!app || !root) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.dsl) {
|
||||||
|
app.dsl.dataSources = root.dataSources;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root.dataSources) {
|
||||||
|
app.dataSourceManager?.updateSchema(root.dataSources);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const dsDepCollectedHandler = async () => {
|
||||||
|
const root = editorService.get('root');
|
||||||
|
const app = await getTMagicApp();
|
||||||
|
|
||||||
|
if (root && app?.dsl) {
|
||||||
|
app.dsl.dataSourceDeps = root.dataSourceDeps;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const collectIdle = (nodes: MComponent[], deep: boolean, type?: DepTargetType) =>
|
||||||
|
Promise.all(
|
||||||
|
nodes.map((node) => {
|
||||||
|
let pageId: Id | undefined;
|
||||||
|
|
||||||
|
if (isPage(node)) {
|
||||||
|
pageId = node.id;
|
||||||
|
} else {
|
||||||
|
const info = editorService.getNodeInfo(node.id);
|
||||||
|
pageId = info.page?.id;
|
||||||
|
}
|
||||||
|
return depService.collectIdle([node], { pageId }, deep, type);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => editorService.get('stage'),
|
||||||
|
(stage) => {
|
||||||
|
if (!stage) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stage.on('rerender', async () => {
|
||||||
|
const node = editorService.get('node');
|
||||||
|
|
||||||
|
if (!node) return;
|
||||||
|
|
||||||
|
await collectIdle([node], true, DepTargetType.DATA_SOURCE);
|
||||||
|
updateStageNode(node);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const initDataSourceDepTarget = (ds: DataSourceSchema) => {
|
||||||
|
depService.addTarget(createDataSourceTarget(ds, reactive({})));
|
||||||
|
depService.addTarget(createDataSourceMethodTarget(ds, reactive({})));
|
||||||
|
depService.addTarget(createDataSourceCondTarget(ds, reactive({})));
|
||||||
|
};
|
||||||
|
|
||||||
const rootChangeHandler = async (value: MApp | null, preValue?: MApp | null) => {
|
const rootChangeHandler = async (value: MApp | null, preValue?: MApp | null) => {
|
||||||
if (!value) return;
|
if (!value) return;
|
||||||
|
|
||||||
@ -214,17 +330,19 @@ export const initServiceEvents = (
|
|||||||
|
|
||||||
depService.removeTargets(DepTargetType.CODE_BLOCK);
|
depService.removeTargets(DepTargetType.CODE_BLOCK);
|
||||||
|
|
||||||
Object.entries(value.codeBlocks).forEach(([id, code]) => {
|
for (const [id, code] of Object.entries(value.codeBlocks)) {
|
||||||
depService.addTarget(createCodeBlockTarget(id, code));
|
depService.addTarget(createCodeBlockTarget(id, code));
|
||||||
});
|
}
|
||||||
|
|
||||||
dataSourceService.get('dataSources').forEach((ds) => {
|
for (const ds of dataSourceService.get('dataSources')) {
|
||||||
initDataSourceDepTarget(ds);
|
initDataSourceDepTarget(ds);
|
||||||
});
|
}
|
||||||
|
|
||||||
if (Array.isArray(value.items)) {
|
if (Array.isArray(value.items)) {
|
||||||
depService.clearIdleTasks();
|
depService.clearIdleTasks();
|
||||||
collectIdle(value.items, true);
|
collectIdle(value.items, true).then(() => {
|
||||||
|
updateStageNodes(value.items);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
depService.clear();
|
depService.clear();
|
||||||
delete value.dataSourceDeps;
|
delete value.dataSourceDeps;
|
||||||
@ -252,61 +370,203 @@ export const initServiceEvents = (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const stage = computed(() => editorService.get('stage'));
|
// 新增节点,收集依赖
|
||||||
|
const nodeAddHandler = async (nodes: MComponent[]) => {
|
||||||
|
await collectIdle(nodes, true);
|
||||||
|
|
||||||
watch(stage, (stage) => {
|
updateStageNodes(nodes);
|
||||||
if (!stage) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
stage.on('rerender', () => {
|
|
||||||
const node = editorService.get('node');
|
|
||||||
|
|
||||||
if (!node) return;
|
|
||||||
|
|
||||||
collectIdle([node], true);
|
|
||||||
updateStage([node]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const getApp = () => {
|
|
||||||
const renderer = stage.value?.renderer;
|
|
||||||
if (!renderer) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (renderer.runtime) {
|
|
||||||
return renderer.runtime.getApp?.();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Promise<TMagicCore | undefined>((resolve) => {
|
|
||||||
const timeout = globalThis.setTimeout(() => {
|
|
||||||
resolve(undefined);
|
|
||||||
}, 10000);
|
|
||||||
|
|
||||||
renderer.on('runtime-ready', () => {
|
|
||||||
if (timeout) {
|
|
||||||
globalThis.clearTimeout(timeout);
|
|
||||||
}
|
|
||||||
resolve(renderer.runtime?.getApp?.());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateDataSourceSchema = async () => {
|
// 节点更新,收集依赖
|
||||||
const root = editorService.get('root');
|
// 仅当修改到数据源相关的才收集
|
||||||
const app = await getApp();
|
const nodeUpdateHandler = async (
|
||||||
|
data: { newNode: MComponent; oldNode: MComponent; changeRecords?: ChangeRecord[] }[],
|
||||||
|
) => {
|
||||||
|
const needRecollectNodes: MComponent[] = [];
|
||||||
|
const normalNodes: MComponent[] = [];
|
||||||
|
for (const { newNode, oldNode, changeRecords } of data) {
|
||||||
|
if (changeRecords?.length) {
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
forChangeRecords: for (const record of changeRecords) {
|
||||||
|
if (!record.propPath) {
|
||||||
|
needRecollectNodes.push(newNode);
|
||||||
|
break forChangeRecords;
|
||||||
|
}
|
||||||
|
|
||||||
if (root && app?.dsl) {
|
// NODE_CONDS_KEY为显示条件key
|
||||||
app.dsl.dataSourceDeps = root.dataSourceDeps;
|
if (
|
||||||
app.dsl.dataSources = root.dataSources;
|
new RegExp(`${NODE_CONDS_KEY}.(\\d)+.cond`).test(record.propPath) ||
|
||||||
|
new RegExp(`${NODE_CONDS_KEY}.(\\d)+.cond.(\\d)+.value`).test(record.propPath) ||
|
||||||
|
record.propPath === NODE_CONDS_KEY ||
|
||||||
|
isValueIncludeDataSource(record.value)
|
||||||
|
) {
|
||||||
|
needRecollectNodes.push(newNode);
|
||||||
|
break forChangeRecords;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改的key在收集的依赖中,则需要触发重新收集
|
||||||
|
for (const target of Object.values(depService.getTargets(DepTargetType.DATA_SOURCE))) {
|
||||||
|
if (!target.deps[newNode.id]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (target.deps[newNode.id].keys.includes(record.propPath)) {
|
||||||
|
needRecollectNodes.push(newNode);
|
||||||
|
break forChangeRecords;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
normalNodes.push(newNode);
|
||||||
|
}
|
||||||
|
} else if (isIncludeDataSource(newNode, oldNode)) {
|
||||||
|
needRecollectNodes.push(newNode);
|
||||||
|
} else {
|
||||||
|
normalNodes.push(newNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root?.dataSources) {
|
if (needRecollectNodes.length) {
|
||||||
|
// 有数据源依赖,需要等依赖重新收集完才更新stage
|
||||||
|
await collectIdle(needRecollectNodes, true, DepTargetType.DATA_SOURCE);
|
||||||
|
await collectIdle(needRecollectNodes, true, DepTargetType.DATA_SOURCE_COND);
|
||||||
|
updateStageNodes(needRecollectNodes);
|
||||||
|
} else {
|
||||||
|
updateStageNodes(normalNodes);
|
||||||
|
// 在上面判断是否需要收集数据源依赖中已经更新stage
|
||||||
|
Promise.all([
|
||||||
|
collectIdle(normalNodes, true, DepTargetType.CODE_BLOCK),
|
||||||
|
collectIdle(normalNodes, true, DepTargetType.DATA_SOURCE_METHOD),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 节点删除,清除对齐的依赖收集
|
||||||
|
const nodeRemoveHandler = (nodes: MComponent[]) => {
|
||||||
|
depService.clear(nodes);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 由于历史记录变化是更新整个page,所以历史记录变化时,需要重新收集依赖
|
||||||
|
const historyChangeHandler = async (page: MPage | MPageFragment) => {
|
||||||
|
await collectIdle([page], true);
|
||||||
|
updateStageNode(page);
|
||||||
|
};
|
||||||
|
|
||||||
|
editorService.on('history-change', historyChangeHandler);
|
||||||
|
editorService.on('root-change', rootChangeHandler);
|
||||||
|
editorService.on('add', nodeAddHandler);
|
||||||
|
editorService.on('remove', nodeRemoveHandler);
|
||||||
|
editorService.on('update', nodeUpdateHandler);
|
||||||
|
|
||||||
|
const dataSourceAddHandler = async (config: DataSourceSchema) => {
|
||||||
|
initDataSourceDepTarget(config);
|
||||||
|
const app = await getTMagicApp();
|
||||||
|
app?.dataSourceManager?.addDataSource(config);
|
||||||
|
};
|
||||||
|
|
||||||
|
const dataSourceUpdateHandler = async (
|
||||||
|
config: DataSourceSchema,
|
||||||
|
{ changeRecords }: { changeRecords: ChangeRecord[] },
|
||||||
|
) => {
|
||||||
|
let needRecollectDep = false;
|
||||||
|
let isModifyField = false;
|
||||||
|
let isModifyMock = false;
|
||||||
|
let isModifyMethod = false;
|
||||||
|
for (const changeRecord of changeRecords) {
|
||||||
|
if (!changeRecord.propPath) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
isModifyField =
|
||||||
|
changeRecord.propPath === 'fields' ||
|
||||||
|
/fields.(\d)+.name/.test(changeRecord.propPath) ||
|
||||||
|
/fields.(\d)+$/.test(changeRecord.propPath);
|
||||||
|
|
||||||
|
isModifyMock = changeRecord.propPath === 'mocks';
|
||||||
|
|
||||||
|
isModifyMethod =
|
||||||
|
changeRecord.propPath === 'methods' ||
|
||||||
|
/methods.(\d)+.name/.test(changeRecord.propPath) ||
|
||||||
|
/methods.(\d)+$/.test(changeRecord.propPath);
|
||||||
|
|
||||||
|
needRecollectDep = isModifyField || isModifyMock || isModifyMethod;
|
||||||
|
|
||||||
|
if (needRecollectDep) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = editorService.get('root');
|
||||||
|
if (needRecollectDep) {
|
||||||
|
if (Array.isArray(root?.items)) {
|
||||||
|
depService.clearIdleTasks();
|
||||||
|
|
||||||
|
removeDataSourceTarget(config.id);
|
||||||
|
initDataSourceDepTarget(config);
|
||||||
|
|
||||||
|
let collectIdlePromises: Promise<void[]>[] = [];
|
||||||
|
if (isModifyField) {
|
||||||
|
collectIdlePromises = [
|
||||||
|
collectIdle(root.items, true, DepTargetType.DATA_SOURCE),
|
||||||
|
collectIdle(root.items, true, DepTargetType.DATA_SOURCE_COND),
|
||||||
|
];
|
||||||
|
} else if (isModifyMock) {
|
||||||
|
collectIdlePromises = [collectIdle(root.items, true, DepTargetType.DATA_SOURCE)];
|
||||||
|
} else if (isModifyMethod) {
|
||||||
|
collectIdlePromises = [collectIdle(root.items, true, DepTargetType.DATA_SOURCE_METHOD)];
|
||||||
|
}
|
||||||
|
Promise.all(collectIdlePromises).then(() => {
|
||||||
|
updateDataSourceSchema();
|
||||||
|
updateStageNodes(root.items);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (root?.dataSources) {
|
||||||
|
const app = await getTMagicApp();
|
||||||
app?.dataSourceManager?.updateSchema(root.dataSources);
|
app?.dataSourceManager?.updateSchema(root.dataSources);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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) => {
|
||||||
|
const root = editorService.get('root');
|
||||||
|
const nodeIds = Object.keys(root?.dataSourceDeps?.[id] || {});
|
||||||
|
const nodes = getNodes(nodeIds, root?.items);
|
||||||
|
|
||||||
|
Promise.all([
|
||||||
|
collectIdle(nodes, false, DepTargetType.DATA_SOURCE),
|
||||||
|
collectIdle(nodes, false, DepTargetType.DATA_SOURCE_COND),
|
||||||
|
collectIdle(nodes, false, DepTargetType.DATA_SOURCE_METHOD),
|
||||||
|
]).then(() => {
|
||||||
|
updateDataSourceSchema();
|
||||||
|
updateStageNodes(nodes);
|
||||||
|
});
|
||||||
|
|
||||||
|
removeDataSourceTarget(id);
|
||||||
|
};
|
||||||
|
|
||||||
|
dataSourceService.on('add', dataSourceAddHandler);
|
||||||
|
dataSourceService.on('update', dataSourceUpdateHandler);
|
||||||
|
dataSourceService.on('remove', dataSourceRemoveHandler);
|
||||||
|
|
||||||
|
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 targetAddHandler = (target: Target) => {
|
const targetAddHandler = (target: Target) => {
|
||||||
const root = editorService.get('root');
|
const root = editorService.get('root');
|
||||||
if (!root) return;
|
if (!root) return;
|
||||||
@ -340,239 +600,10 @@ export const initServiceEvents = (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改dsl后会执行依赖收集并调用此方法
|
|
||||||
* 所以这个方法是会被执行两次,当时updateStage应当时一次,所以通过inDeps参数来区分调用时机
|
|
||||||
* inDeps为true是则更新依赖收集到的节点,否则则更新没有依赖的节点
|
|
||||||
* @param nodes 需要更新的节点
|
|
||||||
* @param inDeps 是否依赖收集完成事件中执行的
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
const updateStage = (nodes: MNode[], inDeps = false) => {
|
|
||||||
const root = editorService.get('root');
|
|
||||||
if (!root) return;
|
|
||||||
|
|
||||||
const update = (node: MNode) =>
|
|
||||||
stage.value?.update({
|
|
||||||
config: cloneDeep(node),
|
|
||||||
parentId: editorService.getParentById(node.id)?.id,
|
|
||||||
root: cloneDeep(root),
|
|
||||||
});
|
|
||||||
|
|
||||||
nodes.forEach((node) => {
|
|
||||||
const inDepsNodeId: Id[] = [];
|
|
||||||
const deps = Object.values(root.dataSourceDeps || {});
|
|
||||||
deps.forEach((dep) => {
|
|
||||||
Object.keys(dep).forEach((id) => {
|
|
||||||
inDepsNodeId.push(id);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (inDeps) {
|
|
||||||
if (inDepsNodeId.includes(node.id)) {
|
|
||||||
update(node);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!inDepsNodeId.includes(node.id)) {
|
|
||||||
update(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const dsDepCollectedHandler = async (nodes: MNode[], deep: boolean) => {
|
|
||||||
const root = editorService.get('root');
|
|
||||||
if (!root) return;
|
|
||||||
const app = await getApp();
|
|
||||||
if (app?.dsl) {
|
|
||||||
app.dsl.dataSourceDeps = root.dataSourceDeps;
|
|
||||||
}
|
|
||||||
if (deep) {
|
|
||||||
nodes.forEach((node) => {
|
|
||||||
traverseNode<MNode>(
|
|
||||||
node,
|
|
||||||
(node) => {
|
|
||||||
updateStage([node], true);
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
updateStage(nodes, true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
depService.on('add-target', targetAddHandler);
|
depService.on('add-target', targetAddHandler);
|
||||||
depService.on('remove-target', targetRemoveHandler);
|
depService.on('remove-target', targetRemoveHandler);
|
||||||
depService.on('ds-collected', dsDepCollectedHandler);
|
depService.on('ds-collected', dsDepCollectedHandler);
|
||||||
|
|
||||||
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) =>
|
|
||||||
Promise.all(
|
|
||||||
nodes.map((node) => {
|
|
||||||
let pageId: Id | undefined;
|
|
||||||
|
|
||||||
if (isPage(node)) {
|
|
||||||
pageId = node.id;
|
|
||||||
} else {
|
|
||||||
const info = editorService.getNodeInfo(node.id);
|
|
||||||
pageId = info.page?.id;
|
|
||||||
}
|
|
||||||
return depService.collectIdle([node], { pageId }, deep);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
// 新增节点,收集依赖
|
|
||||||
const nodeAddHandler = (nodes: MNode[]) => {
|
|
||||||
collectIdle(nodes, true);
|
|
||||||
|
|
||||||
updateStage(nodes);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 节点更新,收集依赖
|
|
||||||
// 仅当修改到数据源相关的才收集
|
|
||||||
const nodeUpdateHandler = (data: { newNode: MNode; oldNode: MNode; changeRecords?: ChangeRecord[] }[]) => {
|
|
||||||
const needRecollectNodes: MNode[] = [];
|
|
||||||
const normalNodes: MNode[] = [];
|
|
||||||
data.forEach(({ newNode, oldNode, changeRecords }) => {
|
|
||||||
if (changeRecords?.length) {
|
|
||||||
for (const record of changeRecords) {
|
|
||||||
// NODE_CONDS_KEY为显示条件key
|
|
||||||
if (
|
|
||||||
!record.propPath ||
|
|
||||||
new RegExp(`${NODE_CONDS_KEY}.(\\d)+.cond`).test(record.propPath) ||
|
|
||||||
new RegExp(`${NODE_CONDS_KEY}.(\\d)+.cond.(\\d)+.value`).test(record.propPath) ||
|
|
||||||
record.propPath === NODE_CONDS_KEY ||
|
|
||||||
isValueIncludeDataSource(record.value)
|
|
||||||
) {
|
|
||||||
needRecollectNodes.push(newNode);
|
|
||||||
} else {
|
|
||||||
normalNodes.push(newNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (isIncludeDataSource(newNode, oldNode)) {
|
|
||||||
needRecollectNodes.push(newNode);
|
|
||||||
} else {
|
|
||||||
normalNodes.push(newNode);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (needRecollectNodes.length) {
|
|
||||||
collectIdle(needRecollectNodes, true);
|
|
||||||
updateStage(needRecollectNodes);
|
|
||||||
} else if (normalNodes.length) {
|
|
||||||
updateStage(normalNodes);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 节点删除,清除对齐的依赖收集
|
|
||||||
const nodeRemoveHandler = (nodes: MNode[]) => {
|
|
||||||
depService.clear(nodes);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 由于历史记录变化是更新整个page,所以历史记录变化时,需要重新收集依赖
|
|
||||||
const historyChangeHandler = (page: MPage | MPageFragment) => {
|
|
||||||
collectIdle([page], true).then(() => {
|
|
||||||
updateDataSourceSchema();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
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 = async (config: DataSourceSchema) => {
|
|
||||||
initDataSourceDepTarget(config);
|
|
||||||
const app = await getApp();
|
|
||||||
app?.dataSourceManager?.addDataSource(config);
|
|
||||||
};
|
|
||||||
|
|
||||||
const dataSourceUpdateHandler = async (
|
|
||||||
config: DataSourceSchema,
|
|
||||||
{ changeRecords }: { changeRecords: ChangeRecord[] },
|
|
||||||
) => {
|
|
||||||
let needRecollectDep = false;
|
|
||||||
for (const changeRecord of changeRecords) {
|
|
||||||
if (!changeRecord.propPath) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
needRecollectDep =
|
|
||||||
changeRecord.propPath === 'fields' ||
|
|
||||||
changeRecord.propPath === 'methods' ||
|
|
||||||
/fields.(\d)+.name/.test(changeRecord.propPath) ||
|
|
||||||
/fields.(\d)+$/.test(changeRecord.propPath) ||
|
|
||||||
/methods.(\d)+.name/.test(changeRecord.propPath) ||
|
|
||||||
/methods.(\d)+$/.test(changeRecord.propPath);
|
|
||||||
|
|
||||||
if (needRecollectDep) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const root = editorService.get('root');
|
|
||||||
if (needRecollectDep) {
|
|
||||||
if (Array.isArray(root?.items)) {
|
|
||||||
depService.clearIdleTasks();
|
|
||||||
|
|
||||||
removeDataSourceTarget(config.id);
|
|
||||||
initDataSourceDepTarget(config);
|
|
||||||
|
|
||||||
collectIdle(root.items, true).then(() => {
|
|
||||||
updateDataSourceSchema();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (root?.dataSources) {
|
|
||||||
const app = await getApp();
|
|
||||||
app?.dataSourceManager?.updateSchema(root.dataSources);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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) => {
|
|
||||||
const root = editorService.get('root');
|
|
||||||
const nodeIds = Object.keys(root?.dataSourceDeps?.[id] || {});
|
|
||||||
const nodes = getNodes(nodeIds, root?.items);
|
|
||||||
collectIdle(nodes, false).then(() => {
|
|
||||||
updateDataSourceSchema();
|
|
||||||
});
|
|
||||||
|
|
||||||
removeDataSourceTarget(id);
|
|
||||||
};
|
|
||||||
|
|
||||||
dataSourceService.on('add', dataSourceAddHandler);
|
|
||||||
dataSourceService.on('update', dataSourceUpdateHandler);
|
|
||||||
dataSourceService.on('remove', dataSourceRemoveHandler);
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
depService.off('add-target', targetAddHandler);
|
depService.off('add-target', targetAddHandler);
|
||||||
depService.off('remove-target', targetRemoveHandler);
|
depService.off('remove-target', targetRemoveHandler);
|
||||||
|
@ -119,10 +119,10 @@ class Dep extends BaseService {
|
|||||||
idleTask.once('finish', () => {
|
idleTask.once('finish', () => {
|
||||||
this.emit('collected', nodes, deep);
|
this.emit('collected', nodes, deep);
|
||||||
this.set('collecting', false);
|
this.set('collecting', false);
|
||||||
resolve();
|
|
||||||
});
|
});
|
||||||
idleTask.once('hight-level-finish', () => {
|
idleTask.once('hight-level-finish', () => {
|
||||||
this.emit('ds-collected', nodes, deep);
|
this.emit('ds-collected', nodes, deep);
|
||||||
|
resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -130,11 +130,11 @@ class Dep extends BaseService {
|
|||||||
public collectNode(node: MNode, target: Target, depExtendedData: DepExtendedData = {}, deep = false) {
|
public collectNode(node: MNode, target: Target, depExtendedData: DepExtendedData = {}, deep = false) {
|
||||||
// 先删除原有依赖,重新收集
|
// 先删除原有依赖,重新收集
|
||||||
if (isPage(node)) {
|
if (isPage(node)) {
|
||||||
Object.entries(target.deps).forEach(([depKey, dep]) => {
|
for (const [depKey, dep] of Object.entries(target.deps)) {
|
||||||
if (dep.data?.pageId && dep.data.pageId === depExtendedData.pageId) {
|
if (dep.data?.pageId && dep.data.pageId === depExtendedData.pageId) {
|
||||||
delete target.deps[depKey];
|
delete target.deps[depKey];
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
} else {
|
} else {
|
||||||
this.watcher.removeTargetDep(target, node);
|
this.watcher.removeTargetDep(target, node);
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ export class IdleTask<T = any> extends EventEmitter {
|
|||||||
// 动画会占用空闲时间,当任务一直无法执行时,看看是否有动画正在播放
|
// 动画会占用空闲时间,当任务一直无法执行时,看看是否有动画正在播放
|
||||||
// 根据空闲时间的多少来决定执行的任务数,保证页面不卡死的情况下尽量多执行任务,不然当任务数巨大时,执行时间会很久
|
// 根据空闲时间的多少来决定执行的任务数,保证页面不卡死的情况下尽量多执行任务,不然当任务数巨大时,执行时间会很久
|
||||||
// 执行不完不会影响配置,但是会影响画布渲染
|
// 执行不完不会影响配置,但是会影响画布渲染
|
||||||
while (deadline.timeRemaining() > 0 && taskList.length) {
|
while (deadline.timeRemaining() > 0 && (taskList.length || hightLevelTaskList.length)) {
|
||||||
const timeRemaining = deadline.timeRemaining();
|
const timeRemaining = deadline.timeRemaining();
|
||||||
let times = 0;
|
let times = 0;
|
||||||
if (timeRemaining <= 5) {
|
if (timeRemaining <= 5) {
|
||||||
|
@ -508,7 +508,7 @@ export const traverseNode = <T extends NodeItem = NodeItem>(
|
|||||||
if (Array.isArray(node.items) && node.items.length) {
|
if (Array.isArray(node.items) && node.items.length) {
|
||||||
parents.push(node);
|
parents.push(node);
|
||||||
node.items.forEach((item) => {
|
node.items.forEach((item) => {
|
||||||
traverseNode(item as T, cb, [...parents]);
|
traverseNode(item as T, cb, [...parents], evalCbAfter);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user