Initial commit: expo-popcore project
This commit is contained in:
165
lib/utils/form-schema-transformer.ts
Normal file
165
lib/utils/form-schema-transformer.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import { FormFieldSchema, RunFormSchema } from '@/lib/types/template-run';
|
||||
|
||||
// 定义工作流节点的数据结构
|
||||
interface WorkflowNode {
|
||||
id: string;
|
||||
type: 'image' | 'video' | 'text' | 'audio';
|
||||
data: {
|
||||
label: string;
|
||||
description?: string;
|
||||
flowType: 'start' | 'normal' | 'end';
|
||||
actionData?: {
|
||||
prompt?: string;
|
||||
aspectRatio?: string;
|
||||
selectedModel?: string;
|
||||
duration?: string;
|
||||
advancedParams?: Record<string, any>;
|
||||
};
|
||||
output?: {
|
||||
images?: Array<{ url: string }>;
|
||||
videos?: Array<{ url: string }>;
|
||||
coverUrl?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// 节点类型到表单字段类型的映射
|
||||
const nodeTypeToFieldType = {
|
||||
image: 'image',
|
||||
video: 'video',
|
||||
text: 'textarea',
|
||||
audio: 'text', // 暂时用文本字段处理音频
|
||||
};
|
||||
|
||||
// 生成字段名称
|
||||
const generateFieldName = (node: WorkflowNode, index: number): string => {
|
||||
const typePrefix = node.type;
|
||||
const flowType = node.data.flowType;
|
||||
return `${typePrefix}_${flowType}_${index}`;
|
||||
};
|
||||
|
||||
// 生成字段标签
|
||||
const generateFieldLabel = (node: WorkflowNode): string => {
|
||||
const baseLabel = node.data.label || `节点 ${node.id.slice(-8)}`;
|
||||
const flowTypeLabel = {
|
||||
start: '(输入)',
|
||||
normal: '(处理)',
|
||||
end: '(输出)'
|
||||
};
|
||||
return baseLabel + (flowTypeLabel[node.data.flowType] || '');
|
||||
};
|
||||
|
||||
// 转换单个节点为表单字段
|
||||
const transformNodeToFormField = (node: WorkflowNode, index: number): FormFieldSchema | null => {
|
||||
// 只为start节点生成表单字段,因为用户只需要输入数据
|
||||
if (node.data.flowType !== 'start') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const fieldName = generateFieldName(node, index);
|
||||
const fieldType = nodeTypeToFieldType[node.type] || 'text';
|
||||
const fieldLabel = generateFieldLabel(node);
|
||||
|
||||
const field: FormFieldSchema = {
|
||||
name: fieldName,
|
||||
label: fieldLabel,
|
||||
type: fieldType as any,
|
||||
required: true,
|
||||
description: node.data.description || `请上传${node.type === 'image' ? '图片' : node.type === 'video' ? '视频' : '内容'}`,
|
||||
};
|
||||
|
||||
// 根据节点类型设置默认值和占位符
|
||||
switch (node.type) {
|
||||
case 'image':
|
||||
field.placeholder = '点击上传图片';
|
||||
if (node.data.output?.coverUrl) {
|
||||
field.defaultValue = node.data.output.coverUrl;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'video':
|
||||
field.placeholder = '点击上传视频';
|
||||
if (node.data.output?.videos?.[0]?.url) {
|
||||
field.defaultValue = node.data.output.videos[0].url;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'text':
|
||||
field.placeholder = '请输入文本内容';
|
||||
field.min = 1;
|
||||
field.max = 1000;
|
||||
break;
|
||||
}
|
||||
|
||||
// 如果节点有预填的prompt,作为提示信息
|
||||
if (node.data.actionData?.prompt) {
|
||||
field.description += `\n\n提示:${node.data.actionData.prompt}`;
|
||||
}
|
||||
|
||||
return field;
|
||||
};
|
||||
|
||||
// 主要的转换函数
|
||||
export function transformWorkflowToFormSchema(formSchemaData: unknown): RunFormSchema {
|
||||
try {
|
||||
// 类型守卫:检查数据格式
|
||||
if (!formSchemaData || typeof formSchemaData !== 'object') {
|
||||
console.warn('formSchema数据格式无效');
|
||||
return { fields: [] };
|
||||
}
|
||||
|
||||
const data = formSchemaData as any;
|
||||
|
||||
// 检查是否包含nodes数组(新格式)或startNodes/endNodes(旧格式)
|
||||
let nodesToProcess: WorkflowNode[] = [];
|
||||
|
||||
if (Array.isArray(data.nodes)) {
|
||||
// 新格式:从nodes数组中筛选start节点
|
||||
nodesToProcess = data.nodes.filter((node: any) =>
|
||||
node.data?.flowType === 'start'
|
||||
);
|
||||
} else if (Array.isArray(data.startNodes)) {
|
||||
// 旧格式:使用startNodes
|
||||
nodesToProcess = data.startNodes;
|
||||
}
|
||||
|
||||
if (nodesToProcess.length === 0) {
|
||||
return { fields: [] };
|
||||
}
|
||||
|
||||
// 转换节点为表单字段
|
||||
const fields: FormFieldSchema[] = [];
|
||||
|
||||
nodesToProcess.forEach((node: any, index: number) => {
|
||||
const formField = transformNodeToFormField(node, index);
|
||||
if (formField) {
|
||||
fields.push(formField);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
fields,
|
||||
validation: {}
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
console.error('转换formSchema失败:', error);
|
||||
return { fields: [] };
|
||||
}
|
||||
}
|
||||
|
||||
// 验证转换后的表单配置
|
||||
export function validateFormSchema(schema: RunFormSchema): boolean {
|
||||
if (!schema || !Array.isArray(schema.fields)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return schema.fields.every(field => {
|
||||
return (
|
||||
field.name &&
|
||||
field.label &&
|
||||
field.type &&
|
||||
['text', 'number', 'textarea', 'select', 'image', 'video', 'color'].includes(field.type)
|
||||
);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user