qwertyyb 88c04c6dac
feat: 支持数据源事件 (#605)
* feat: 添加observedData

* feat: 修改错误

* fix: 修复单测报错问题

* feat: 完善数据源事件

* fix: 修复数据源事件调用组件方法时报错的异常

* fix: 修复多个相同类型的数据源数据变化的事件混淆的问题

* chore: 删除无用代码

* feat: 默认使用SimpleObservedData

* feat: 删除无用代码

---------

Co-authored-by: marchyang <marchyang@tencent.com>
2024-05-13 17:23:23 +08:00

237 lines
5.6 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 { CascaderOption, FormConfig, FormState } from '@tmagic/form';
import { DataSchema, DataSourceFieldType, DataSourceSchema } from '@tmagic/schema';
import BaseFormConfig from './formConfigs/base';
import HttpFormConfig from './formConfigs/http';
const fillConfig = (config: FormConfig): FormConfig => [
...BaseFormConfig(),
...config,
{
type: 'tab',
items: [
{
title: '数据定义',
items: [
{
name: 'fields',
type: 'data-source-fields',
defaultValue: () => [],
},
],
},
{
title: '方法定义',
items: [
{
name: 'methods',
type: 'data-source-methods',
defaultValue: () => [],
},
],
},
{
title: '事件配置',
items: [
{
name: 'events',
src: 'datasource',
type: 'event-select',
},
],
},
{
title: 'mock数据',
items: [
{
name: 'mocks',
type: 'data-source-mocks',
defaultValue: () => [],
},
],
},
{
title: '请求参数裁剪',
display: (formState: FormState, { model }: any) => model.type === 'http',
items: [
{
name: 'beforeRequest',
type: 'vs-code',
parse: true,
height: '600px',
},
],
},
{
title: '响应数据裁剪',
display: (formState: FormState, { model }: any) => model.type === 'http',
items: [
{
name: 'afterResponse',
type: 'vs-code',
parse: true,
height: '600px',
},
],
},
],
},
];
export const getFormConfig = (type: string, configs: Record<string, FormConfig>): FormConfig => {
switch (type) {
case 'base':
return fillConfig([]);
case 'http':
return fillConfig(HttpFormConfig);
default:
return fillConfig(configs[type] || []);
}
};
export const getFormValue = (type: string, values: Partial<DataSourceSchema>): Partial<DataSourceSchema> => {
if (type !== 'http') {
return values;
}
return {
beforeRequest: `(options, context) => {
/**
* 用户可以直接编写函数,在原始接口调用之前,会运行该函数,将这个函数的返回值作为该数据源接口的入参
*
* options: HttpOptions
*
* interface HttpOptions {
* // 请求链接
* url: string;
* // query参数
* params?: Record<string, string>;
* // body数据
* data?: Record<string, any>;
* // 请求头
* headers?: Record<string, string>;
* // 请求方法 GET/POST
* method?: Method;
* }
*
* context上下文对象
*
* interface Content {
* app: AppCore;
* dataSource: HttpDataSource;
* }
*
* return: HttpOptions
*/
// 此处的返回值会作为这个接口的入参
return options;
}`,
afterResponse: `(response, context) => {
/**
* 用户可以直接编写函数,在原始接口返回之后,会运行该函数,将这个函数的返回值作为该数据源接口的返回
* context上下文对象
*
* interface Content {
* app: AppCore;
* dataSource: HttpDataSource;
* }
*
*/
// 此处的返回值会作为这个接口的返回值
return response;
}`,
...values,
};
};
export const getDisplayField = (dataSources: DataSourceSchema[], key: string) => {
const displayState: { value: string; type: 'var' | 'text' }[] = [];
// 匹配es6字符串模块
const matches = key.matchAll(/\$\{([\s\S]+?)\}/g);
let index = 0;
for (const match of matches) {
if (typeof match.index === 'undefined') break;
// 字符串常量
displayState.push({
type: 'text',
value: key.substring(index, match.index),
});
let dsText = '';
let ds: DataSourceSchema | undefined;
let fields: DataSchema[] | undefined;
// 将模块解析成数据源对应的值
match[1].split('.').forEach((item, index) => {
if (index === 0) {
ds = dataSources.find((ds) => ds.id === item);
dsText += ds?.title || item;
fields = ds?.fields;
return;
}
const field = fields?.find((field) => field.name === item);
fields = field?.fields;
dsText += `.${field?.title || item}`;
});
displayState.push({
type: 'var',
value: dsText,
});
index = match.index + match[0].length;
}
if (index < key.length) {
displayState.push({
type: 'text',
value: key.substring(index),
});
}
return displayState;
};
export const getCascaderOptionsFromFields = (
fields: DataSchema[] = [],
dataSourceFieldType: DataSourceFieldType[] = ['any'],
): CascaderOption[] => {
const child: CascaderOption[] = [];
fields.forEach((field) => {
if (!dataSourceFieldType.length) {
dataSourceFieldType.push('any');
}
const children = getCascaderOptionsFromFields(field.fields, dataSourceFieldType);
const item = {
label: field.title || field.name,
value: field.name,
children,
};
const fieldType = field.type || 'any';
if (dataSourceFieldType.includes('any') || dataSourceFieldType.includes(fieldType)) {
child.push(item);
return;
}
if (!dataSourceFieldType.includes(fieldType) && !['array', 'object', 'any'].includes(fieldType)) {
return;
}
if (!children.length && ['object', 'array', 'any'].includes(field.type || '')) {
return;
}
child.push(item);
});
return child;
};