/** * ComfyUI V2 工作流模板创建器组件 * 专门用于创建符合ComfyUI SDK规范的WorkflowTemplateData格式的模板 */ import React, { useState, useEffect } from 'react'; import { X, Save, Upload, FileText, Settings, Info, AlertCircle, CheckCircle, Plus, Trash2, } from 'lucide-react'; // 定义符合ComfyUI SDK的模板数据结构 interface TemplateMetadata { id: string; name: string; description?: string; version?: string; author?: string; tags?: string[]; category?: string; } interface ParameterSchema { param_type: 'string' | 'number' | 'boolean' | 'array' | 'object'; required?: boolean; default?: any; description?: string; enum?: any[]; min?: number; max?: number; pattern?: string; } interface WorkflowTemplateData { metadata: TemplateMetadata; workflow: any; // ComfyUI workflow JSON parameters: Record; } interface CreateTemplateRequest { name: string; category?: string; description?: string; template_data: WorkflowTemplateData; parameter_schema: Record; tags: string[]; author?: string; } interface WorkflowV2CreatorProps { /** 是否显示模态框 */ isOpen: boolean; /** 关闭回调 */ onClose: () => void; /** 保存回调 */ onSave: (templateData: CreateTemplateRequest) => Promise; /** 编辑的模板(为空时表示创建新模板) */ editingTemplate?: any; } /** * ComfyUI V2 工作流创建器组件 */ export const WorkflowV2Creator: React.FC = ({ isOpen, onClose, onSave, editingTemplate }) => { const [activeTab, setActiveTab] = useState<'basic' | 'workflow' | 'parameters'>('basic'); const [templateData, setTemplateData] = useState({ metadata: { id: '', name: '', description: '', version: '1.0.0', author: '', tags: [], category: '', }, workflow: {}, parameters: {}, }); const [errors, setErrors] = useState>({}); const [isSaving, setIsSaving] = useState(false); const [workflowJsonText, setWorkflowJsonText] = useState('{}'); const [newParameterName, setNewParameterName] = useState(''); // 重置表单数据 useEffect(() => { if (isOpen) { if (editingTemplate) { setTemplateData({ metadata: { id: editingTemplate.id || '', name: editingTemplate.name || '', description: editingTemplate.description || '', version: editingTemplate.version || '1.0.0', author: editingTemplate.author || '', tags: editingTemplate.tags || [], category: editingTemplate.category || '', }, workflow: editingTemplate.workflow_data || {}, parameters: editingTemplate.parameters || {}, }); setWorkflowJsonText(JSON.stringify(editingTemplate.workflow_data || {}, null, 2)); } else { // 重置为默认值 setTemplateData({ metadata: { id: '', name: '', description: '', version: '1.0.0', author: '', tags: [], category: '', }, workflow: {}, parameters: {}, }); setWorkflowJsonText('{}'); } setErrors({}); setActiveTab('basic'); } }, [editingTemplate, isOpen]); // 更新模板元数据字段 const updateMetadata = (field: keyof TemplateMetadata, value: any) => { setTemplateData(prev => ({ ...prev, metadata: { ...prev.metadata, [field]: value } })); // 清除该字段的错误 if (errors[field]) { setErrors(prev => { const newErrors = { ...prev }; delete newErrors[field]; return newErrors; }); } }; // 更新参数 const updateParameter = (paramName: string, schema: ParameterSchema) => { setTemplateData(prev => ({ ...prev, parameters: { ...prev.parameters, [paramName]: schema } })); }; // 删除参数 const removeParameter = (paramName: string) => { setTemplateData(prev => { const newParameters = { ...prev.parameters }; delete newParameters[paramName]; return { ...prev, parameters: newParameters }; }); }; // 验证表单 const validateForm = (): boolean => { const newErrors: Record = {}; if (!templateData.metadata.name.trim()) { newErrors.name = '模板名称不能为空'; } if (!templateData.metadata.id.trim()) { newErrors.id = '模板ID不能为空'; } if (!workflowJsonText.trim() || workflowJsonText.trim() === '{}') { newErrors.workflow = '工作流数据不能为空'; } else { try { const parsed = JSON.parse(workflowJsonText); if (typeof parsed !== 'object' || parsed === null) { newErrors.workflow = '工作流数据必须是有效的JSON对象'; } } catch (error) { newErrors.workflow = 'JSON格式错误,请检查语法'; } } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; // 处理保存 const handleSave = async () => { if (!validateForm()) { return; } setIsSaving(true); try { const workflowData = JSON.parse(workflowJsonText); // 构建符合ComfyUI SDK规范的模板数据 const finalTemplateData: WorkflowTemplateData = { ...templateData, workflow: workflowData, }; const requestData: CreateTemplateRequest = { name: templateData.metadata.name, category: templateData.metadata.category, description: templateData.metadata.description, template_data: finalTemplateData, parameter_schema: templateData.parameters, tags: templateData.metadata.tags || [], author: templateData.metadata.author, }; await onSave(requestData); } catch (error) { console.error('保存模板失败:', error); setErrors({ general: `保存失败: ${error}` }); } finally { setIsSaving(false); } }; // 导入ComfyUI工作流 const handleImportWorkflow = () => { const input = document.createElement('input'); input.type = 'file'; input.accept = '.json'; input.onchange = (e) => { const file = (e.target as HTMLInputElement).files?.[0]; if (file) { const reader = new FileReader(); reader.onload = (e) => { try { const workflow = JSON.parse(e.target?.result as string); setWorkflowJsonText(JSON.stringify(workflow, null, 2)); setTemplateData(prev => ({ ...prev, workflow })); } catch (error) { console.error('解析ComfyUI工作流失败:', error); setErrors({ workflow: '文件格式错误,请选择有效的JSON文件' }); } }; reader.readAsText(file); } }; input.click(); }; // 处理标签输入 const handleTagsChange = (tagsString: string) => { const tags = tagsString .split(',') .map(tag => tag.trim()) .filter(tag => tag.length > 0); updateMetadata('tags', tags); }; if (!isOpen) return null; return (
{/* 头部 */}

{editingTemplate ? '编辑工作流' : '创建工作流'}

{/* 错误提示 */} {errors.general && (
{errors.general}
)} {/* 标签页导航 */}
{[ { id: 'basic', label: '基本信息', icon: FileText }, { id: 'workflow', label: '工作流配置', icon: Settings }, { id: 'advanced', label: '高级设置', icon: Info }, ].map(tab => { const Icon = tab.icon; return ( ); })}
{/* 内容区域 */}
{/* 基本信息标签页 */} {activeTab === 'basic' && (
setTemplateData(prev => ({ ...prev, metadata: { ...prev.metadata, name: e.target.value } }))} className={`w-full px-3 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent ${ errors.name ? 'border-red-300' : 'border-gray-300' }`} placeholder="例如:图像生成工作流" /> {errors.name && (

{errors.name}

)}