import type { DataSchema, DataSourceFieldType, DataSourceSchema } from '@tmagic/core'; import { type CascaderOption, defineFormItem, type FormConfig } from '@tmagic/form'; import { dataSourceTemplateRegExp, getKeysArray, isNumber } from '@tmagic/utils'; import BaseFormConfig from './formConfigs/base'; import HttpFormConfig from './formConfigs/http'; const dataSourceFormConfig = defineFormItem({ 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, { model }) => model.type === 'http', items: [ { name: 'beforeRequest', type: 'vs-code', parse: true, autosize: { minRows: 10, maxRows: 30 }, }, ], }, { title: '响应数据裁剪', display: (_formStat, { model }) => model.type === 'http', items: [ { name: 'afterResponse', type: 'vs-code', parse: true, autosize: { minRows: 10, maxRows: 30 }, }, ], }, ], }); const fillConfig = (config: FormConfig): FormConfig => [...BaseFormConfig(), ...config, dataSourceFormConfig]; export const getFormConfig = (type: string, configs: Record): FormConfig => { switch (type) { case 'base': return fillConfig([]); case 'http': return fillConfig(HttpFormConfig); default: return fillConfig(configs[type] || []); } }; export const getFormValue = (type: string, values: Partial): Partial => { if (type !== 'http') { return values; } return { beforeRequest: `(options, context) => { /** * 用户可以直接编写函数,在原始接口调用之前,会运行该函数,将这个函数的返回值作为该数据源接口的入参 * * options: HttpOptions * * interface HttpOptions { * // 请求链接 * url: string; * // query参数 * params?: Record; * // body数据 * data?: Record; * // 请求头 * headers?: Record; * // 请求方法 GET/POST * method?: Method; * } * * context:上下文对象 * * interface Content { * app: TMagicApp; * dataSource: HttpDataSource; * } * * return: HttpOptions */ // 此处的返回值会作为这个接口的入参 return options; }`, afterResponse: `(response, context) => { /** * 用户可以直接编写函数,在原始接口返回之后,会运行该函数,将这个函数的返回值作为该数据源接口的返回 * context:上下文对象 * * interface Content { * app: TMagicApp; * dataSource: HttpDataSource; * } * */ // 此处的返回值会作为这个接口的返回值 return response; }`, ...values, }; }; export const getDisplayField = (dataSources: DataSourceSchema[], key: string) => { const displayState: { value: string; type: 'var' | 'text' }[] = []; // 匹配es6字符串模块 const matches = key.matchAll(dataSourceTemplateRegExp); 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; // 将模块解析成数据源对应的值 getKeysArray(match[1]).forEach((item, index) => { if (index === 0) { ds = dataSources.find((ds) => ds.id === item); dsText += ds?.title || item; fields = ds?.fields; return; } if (isNumber(item)) { dsText += `[${item}]`; } else { 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 typeSet = new Set(dataSourceFieldType.length ? dataSourceFieldType : ['any']); const includesAny = typeSet.has('any'); const result: CascaderOption[] = []; for (const field of fields) { const fieldType = field.type || 'any'; const isContainerType = fieldType === 'any' || fieldType === 'array' || fieldType === 'object'; const children = isContainerType ? getCascaderOptionsFromFields(field.fields, dataSourceFieldType) : []; const matchesType = includesAny || typeSet.has(fieldType); if (matchesType || (isContainerType && children.length)) { result.push({ label: `${field.title || field.name}(${field.type})`, value: field.name, children, }); } } return result; }; export const getFieldType = (ds: DataSourceSchema | undefined, fieldNames: string[]) => { let fields = ds?.fields; let type = ''; for (const fieldName of fieldNames) { if (!fields?.length) return ''; const field = fields.find((f) => f.name === fieldName); if (!field) return ''; type = field.type || ''; fields = field.fields; } return type; };