deploy-ease-platform/frontend/src/utils/jsonSchemaUtils.ts
dengqichen df07edc18f 1
2025-10-20 13:18:42 +08:00

310 lines
12 KiB
TypeScript

import type {ProFormColumnsType} from '@ant-design/pro-components';
import request from '@/utils/request';
interface EditorConfig {
language?: string;
theme?: string;
minimap?: boolean;
lineNumbers?: boolean;
wordWrap?: boolean;
fontSize?: number;
tabSize?: number;
autoComplete?: boolean;
folding?: boolean;
}
interface DataSourceConfig {
type: 'api';
url: string;
valueField: string;
labelField: string;
dependsOn?: string[];
params?: Record<string, string>;
}
interface JsonSchemaProperty {
type: string;
title?: string;
description?: string;
default?: any;
enum?: any[];
enumNames?: string[];
format?: string;
editorConfig?: EditorConfig;
dataSource?: DataSourceConfig;
items?: {
type: string;
enum?: any[];
enumNames?: string[];
};
minimum?: number;
maximum?: number;
minLength?: number;
maxLength?: number;
pattern?: string;
readOnly?: boolean;
}
interface JsonSchema {
type: string;
properties: Record<string, JsonSchemaProperty>;
required?: string[];
}
// 处理参数替换,将 ${fieldName} 替换为实际值
const replaceParams = (params: Record<string, string>, formValues: Record<string, any>): Record<string, any> => {
const result: Record<string, any> = {};
for (const [key, value] of Object.entries(params)) {
if (typeof value === 'string' && value.startsWith('${') && value.endsWith('}')) {
const fieldName = value.slice(2, -1);
result[key] = formValues[fieldName] || formValues[key];
} else {
result[key] = value;
}
}
return result;
};
// 获取远程数据源的选项
const fetchDataSourceOptions = async (
dataSource: DataSourceConfig,
formValues: Record<string, any> = {}
) => {
try {
// 处理依赖参数
const params = {
...formValues, // 包含所有表单值
...(dataSource.params ? replaceParams(dataSource.params, formValues) : {}) // 覆盖特定参数
};
console.log('Fetching data from:', dataSource.url);
console.log('With params:', params);
// 发起请求
const response = await request.get(dataSource.url, { params });
console.log('API Response:', response);
// 转换响应数据为选项格式
if (!Array.isArray(response)) {
console.warn('Response is not an array:', response);
return [];
}
const options = response.map(item => {
const option = {
label: item[dataSource.labelField],
value: item[dataSource.valueField],
};
console.log('Mapped option:', option);
return option;
});
console.log('Final options:', options);
return options;
} catch (error) {
console.error('Failed to fetch data source options:', error);
return [];
}
};
export const convertJsonSchemaToColumns = (schema: JsonSchema): ProFormColumnsType[] => {
if (!schema?.properties) return [];
return Object.entries(schema.properties).map(([key, value]: [string, JsonSchemaProperty]) => {
const baseConfig = {
title: value.description ? `${value.title || key} (${value.description})` : (value.title || key),
dataIndex: key,
formItemProps: {
required: schema.required?.includes(key),
},
initialValue: value.default,
readonly: value.readOnly,
};
// 根据类型处理不同的表单项
switch (value.type) {
case 'string':
// 如果有数据源配置,使用远程数据源的选择器
if (value.dataSource && value.dataSource.type === 'api') {
const dataSource = value.dataSource;
return {
...baseConfig,
valueType: 'select',
fieldProps: {
showSearch: true,
placeholder: `请选择${value.title || key}`,
disabled: value.readOnly,
allowClear: true,
showArrow: true,
filterOption: (input: string, option: any) =>
option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0,
onChange: (value: any, option: any) => {
console.log('Select value changed:', value, option);
},
},
dependencies: dataSource.dependsOn,
request: async (params: any) => {
console.log('Select field request triggered:', key);
console.log('Request params:', params);
// 获取表单当前值,包括当前字段的值
const formValues = {
...params?.form?.getFieldsValue(),
[key]: params?.value, // 包含当前字段的值
...params // 包含其他参数
};
console.log('Current form values:', formValues);
// 检查依赖字段是否都有值
const hasDependencies = !dataSource.dependsOn?.some(
field => {
const value = formValues[field];
return value === undefined || value === null || value === '';
}
);
console.log('Dependencies check:', {
required: dataSource.dependsOn,
formValues,
hasDependencies
});
// 如果有依赖且依赖值都存在,或者没有依赖,则发起请求
if (!dataSource.dependsOn || hasDependencies) {
return await fetchDataSourceOptions(dataSource, formValues);
}
console.log('Skipping request due to missing dependencies');
return [];
},
};
}
// 如果有枚举值,使用选择器
if (value.enum) {
return {
...baseConfig,
valueType: 'select',
fieldProps: {
options: value.enum.map((item, index) => ({
label: value.enumNames?.[index] || item,
value: item,
})),
placeholder: `请选择${value.title || key}`,
disabled: value.readOnly,
},
};
}
// 处理 monaco-editor 格式
if (value.format === 'monaco-editor') {
const editorConfig = value.editorConfig || {};
return {
...baseConfig,
valueType: 'code',
fieldProps: {
language: editorConfig.language || 'shell',
height: 300,
options: {
minimap: { enabled: editorConfig.minimap ?? false },
fontSize: editorConfig.fontSize || 14,
lineNumbers: editorConfig.lineNumbers ? 'on' : 'off',
wordWrap: editorConfig.wordWrap ? 'on' : 'off',
tabSize: editorConfig.tabSize || 2,
scrollBeyondLastLine: false,
automaticLayout: true,
folding: editorConfig.folding ?? true,
autoClosingBrackets: 'always',
autoClosingQuotes: 'always',
formatOnPaste: true,
formatOnType: true,
suggestOnTriggerCharacters: editorConfig.autoComplete ?? true,
renderWhitespace: 'boundary',
readOnly: value.readOnly,
}
}
};
}
// 否则使用文本输入框
return {
...baseConfig,
valueType: 'text',
fieldProps: {
placeholder: `请输入${value.title || key}`,
disabled: value.readOnly,
},
};
case 'number':
case 'integer':
return {
...baseConfig,
valueType: 'digit',
fieldProps: {
min: value.minimum,
max: value.maximum,
placeholder: `请输入${value.title || key}`,
style: { width: '100%' },
disabled: value.readOnly,
},
};
case 'boolean':
return {
...baseConfig,
valueType: 'switch',
fieldProps: {
disabled: value.readOnly,
},
};
case 'array':
// 处理数组类型的枚举值
if (value.items?.enum || value.enum) {
const enumValues = value.items?.enum || value.enum || [];
const enumNames = value.items?.enumNames || value.enumNames;
return {
...baseConfig,
valueType: 'select',
fieldProps: {
mode: 'multiple',
options: enumValues.map((item, index) => ({
label: enumNames?.[index] || item,
value: item,
})),
placeholder: `请选择${value.title || key}`,
disabled: value.readOnly,
},
};
}
// 否则使用标签输入
return {
...baseConfig,
valueType: 'select',
fieldProps: {
mode: 'tags',
placeholder: `请输入${value.title || key}`,
disabled: value.readOnly,
},
};
case 'object':
return {
...baseConfig,
valueType: 'jsonCode',
fieldProps: {
placeholder: `请输入${value.title || key}`,
disabled: value.readOnly,
},
};
default:
return {
...baseConfig,
valueType: 'text',
fieldProps: {
placeholder: `请输入${value.title || key}`,
disabled: value.readOnly,
},
};
}
});
};