mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-06 03:57:56 +08:00
272 lines
7.0 KiB
TypeScript
272 lines
7.0 KiB
TypeScript
import { cloneDeep } from 'lodash-es';
|
||
|
||
import type { DepData, DisplayCond, DisplayCondItem, MApp, MNode, MPage, MPageFragment } from '@tmagic/schema';
|
||
import { NODE_CONDS_KEY } from '@tmagic/schema';
|
||
import {
|
||
compiledCond,
|
||
compiledNode,
|
||
DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX,
|
||
dataSourceTemplateRegExp,
|
||
getValueByKeyPath,
|
||
isPage,
|
||
isPageFragment,
|
||
replaceChildNode,
|
||
} from '@tmagic/utils';
|
||
|
||
import type { AsyncDataSourceResolveResult, DataSourceManagerData } from './types';
|
||
|
||
/**
|
||
* 编译显示条件
|
||
* @param cond 条件配置
|
||
* @param data 上下文数据(数据源数据)
|
||
* @returns boolean
|
||
*/
|
||
export const compiledCondition = (cond: DisplayCondItem[], data: DataSourceManagerData) => {
|
||
let result = true;
|
||
for (const { op, value, range, field } of cond) {
|
||
const [sourceId, ...fields] = field;
|
||
|
||
const dsData = data[sourceId];
|
||
|
||
if (!dsData || !fields.length) {
|
||
break;
|
||
}
|
||
|
||
try {
|
||
const fieldValue = getValueByKeyPath(fields.join('.'), dsData);
|
||
|
||
if (!compiledCond(op, fieldValue, value, range)) {
|
||
result = false;
|
||
break;
|
||
}
|
||
} catch (e) {
|
||
console.warn(e);
|
||
}
|
||
}
|
||
|
||
return result;
|
||
};
|
||
|
||
/**
|
||
* 编译数据源条件组
|
||
* @param node dsl节点
|
||
* @param data 数据源数据
|
||
* @returns boolean
|
||
*/
|
||
export const compliedConditions = (node: { [NODE_CONDS_KEY]?: DisplayCond[] }, data: DataSourceManagerData) => {
|
||
if (!node[NODE_CONDS_KEY] || !Array.isArray(node[NODE_CONDS_KEY]) || !node[NODE_CONDS_KEY].length) return true;
|
||
|
||
for (const { cond } of node[NODE_CONDS_KEY]) {
|
||
if (!cond) continue;
|
||
|
||
if (compiledCondition(cond, data)) {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
};
|
||
|
||
export const updateNode = (node: MNode, dsl: MApp) => {
|
||
if (isPage(node) || isPageFragment(node)) {
|
||
const index = dsl.items?.findIndex((child: MNode) => child.id === node.id);
|
||
dsl.items.splice(index, 1, node as MPage | MPageFragment);
|
||
} else {
|
||
replaceChildNode(node, dsl!.items);
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 创建迭代器容器编译的数据上下文
|
||
* @param itemData 迭代数据
|
||
* @param dsId 数据源id
|
||
* @param fields dsl节点字段,如a.b.c
|
||
* @returns 数据上下文
|
||
*/
|
||
export const createIteratorContentData = (
|
||
itemData: any,
|
||
dsId: string,
|
||
fields: string[] = [],
|
||
dsData: DataSourceManagerData = {},
|
||
) => {
|
||
const data: DataSourceManagerData = {
|
||
...dsData,
|
||
[dsId]: {},
|
||
};
|
||
|
||
let rawData = cloneDeep(dsData[dsId]);
|
||
let obj: Record<string, any> = data[dsId];
|
||
|
||
fields.forEach((key, index) => {
|
||
Object.assign(obj, rawData);
|
||
|
||
if (index === fields.length - 1) {
|
||
obj[key] = itemData;
|
||
return;
|
||
}
|
||
|
||
if (Array.isArray(rawData[key])) {
|
||
rawData[key] = {};
|
||
obj[key] = {};
|
||
}
|
||
|
||
rawData = rawData[key];
|
||
obj = obj[key];
|
||
});
|
||
|
||
return data;
|
||
};
|
||
|
||
/**
|
||
* 编译通过tmagic-editor的数据源源选择器配(data-source-field-select)
|
||
* 格式为 [`${DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX}${id}`, 'field']
|
||
* DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX常量可通过@tmagic/utils获取
|
||
*
|
||
* @param value dsl节点中的数据源配置
|
||
* @param data 数据源数据
|
||
* @returns 编译好的配置
|
||
*/
|
||
export const compliedDataSourceField = (value: any, data: DataSourceManagerData) => {
|
||
const [prefixId, ...fields] = value;
|
||
const prefixIndex = prefixId.indexOf(DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX);
|
||
|
||
if (prefixIndex > -1) {
|
||
const dsId = prefixId.substring(prefixIndex + DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX.length);
|
||
|
||
const dsData = data[dsId];
|
||
|
||
if (!dsData) return value;
|
||
|
||
try {
|
||
return getValueByKeyPath(fields.join('.'), dsData);
|
||
} catch (e) {
|
||
return value;
|
||
}
|
||
}
|
||
|
||
return value;
|
||
};
|
||
|
||
export const template = (value: string, data?: DataSourceManagerData) =>
|
||
value.replaceAll(dataSourceTemplateRegExp, (match, $1) => {
|
||
try {
|
||
return getValueByKeyPath($1, data);
|
||
} catch (e: any) {
|
||
return match;
|
||
}
|
||
});
|
||
|
||
/**
|
||
* 编译通过tmagic-editor的数据源源选择器(data-source-input,data-source-select,data-source-field-select)配置出来的数据,或者其他符合规范的配置
|
||
* @param value dsl节点中的数据源配置
|
||
* @param data 数据源数据
|
||
* @returns 编译好的配置
|
||
*/
|
||
export const compiledNodeField = (value: any, data: DataSourceManagerData) => {
|
||
// 使用data-source-input等表单控件配置的字符串模板,如:`xxx${id.field}xxx`
|
||
if (typeof value === 'string') {
|
||
return template(value, data);
|
||
}
|
||
|
||
// 使用data-source-select等表单控件配置的数据源,如:{ isBindDataSource: true, dataSourceId: 'xxx'}
|
||
if (value?.isBindDataSource && value.dataSourceId) {
|
||
return data[value.dataSourceId];
|
||
}
|
||
|
||
// 指定数据源的字符串模板,如:{ isBindDataSourceField: true, dataSourceId: 'id', template: `xxx${field}xxx`}
|
||
if (value?.isBindDataSourceField && value.dataSourceId && typeof value.template === 'string') {
|
||
return template(value.template, data[value.dataSourceId]);
|
||
}
|
||
|
||
// 使用data-source-field-select等表单控件的数据源字段,如:[`${DATA_SOURCE_FIELDS_SELECT_VALUE_PREFIX}${id}`, 'field']
|
||
if (Array.isArray(value) && typeof value[0] === 'string') {
|
||
return compliedDataSourceField(value, data);
|
||
}
|
||
|
||
return value;
|
||
};
|
||
|
||
export const compliedIteratorItem = ({
|
||
compile,
|
||
dsId,
|
||
item,
|
||
deps,
|
||
condDeps,
|
||
inEditor,
|
||
ctxData,
|
||
}: {
|
||
compile: (value: any) => any;
|
||
dsId: string;
|
||
item: MNode;
|
||
deps: DepData;
|
||
condDeps: DepData;
|
||
inEditor: boolean;
|
||
ctxData: DataSourceManagerData;
|
||
}) => {
|
||
const { items, ...node } = item;
|
||
const newNode = cloneDeep(node);
|
||
|
||
if (condDeps[node.id]?.keys.length && !inEditor) {
|
||
newNode.condResult = compliedConditions(node, ctxData);
|
||
}
|
||
|
||
if (Array.isArray(items) && items.length) {
|
||
newNode.items = items.map((item) =>
|
||
compliedIteratorItem({ compile, dsId, item, deps, condDeps, inEditor, ctxData }),
|
||
);
|
||
} else if (items) {
|
||
newNode.items = items;
|
||
}
|
||
|
||
if (!deps[newNode.id]?.keys.length) {
|
||
return newNode;
|
||
}
|
||
|
||
return compiledNode(
|
||
compile,
|
||
newNode,
|
||
{
|
||
[dsId]: deps,
|
||
},
|
||
dsId,
|
||
);
|
||
};
|
||
|
||
/**
|
||
* 按需加载数据源
|
||
*/
|
||
export const registerDataSourceOnDemand = async (
|
||
dsl: MApp,
|
||
dataSourceModules: Record<string, () => Promise<AsyncDataSourceResolveResult>>,
|
||
) => {
|
||
const { dataSourceMethodsDeps = {}, dataSourceCondDeps = {}, dataSourceDeps = {}, dataSources = [] } = dsl;
|
||
|
||
const dsModuleMap: Record<string, () => Promise<AsyncDataSourceResolveResult>> = {};
|
||
|
||
dataSources.forEach((ds) => {
|
||
let dep = dataSourceCondDeps[ds.id] || {};
|
||
|
||
if (!Object.keys(dep).length) {
|
||
dep = dataSourceDeps[ds.id] || {};
|
||
}
|
||
|
||
if (!Object.keys(dep).length) {
|
||
dep = dataSourceMethodsDeps[ds.id] || {};
|
||
}
|
||
|
||
if (Object.keys(dep).length && dataSourceModules[ds.type]) {
|
||
dsModuleMap[ds.type] = dataSourceModules[ds.type];
|
||
}
|
||
});
|
||
|
||
const modules = await Promise.all(Object.values(dsModuleMap).map((asyncModule) => asyncModule()));
|
||
|
||
const moduleMap: Record<string, any> = {};
|
||
modules.forEach((module, index) => {
|
||
const type = Object.keys(dsModuleMap)[index];
|
||
moduleMap[type] = module.default;
|
||
});
|
||
|
||
return moduleMap;
|
||
};
|