feat: 增强ComfyUI V2工作流模板参数配置,支持专业AI工作流参数类型

根据ComfyUI SDK规范和AI工作流需求,大幅增强参数配置功能:

🎯 新增专业参数类型:
-  图片 (Image) - 支持JPG/PNG/WebP等格式,文件大小限制,尺寸推荐
-  音频 (Audio) - 支持MP3/WAV/FLAC等格式,时长限制
-  视频 (Video) - 支持MP4/AVI/MOV等格式,尺寸和时长限制
-  整数 (Integer) - 默认步长1,范围0-100,默认值10
-  浮点数 (Float) - 默认步长0.01,范围0.0-1.0,默认值0.3

🛠 智能默认配置:
- 根据参数类型自动应用合适的默认值和约束
- 整数类型:步长1,最小0,最大100,默认10
- 浮点数类型:步长0.01,最小0.00,最大1.0,默认0.3
- 媒体类型:预设文件格式、大小限制和质量要求

🎨 增强用户界面:
- 分组显示参数类型(基础/数值/媒体)
- 根据类型动态显示相应的配置选项
- 智能输入控件(数字步长、布尔选择器、文件格式提示)
- 专业的媒体参数配置(格式、大小、尺寸、时长)

🔧 技术特性:
- 扩展ParameterSchema接口支持媒体属性
- 类型切换时自动应用默认配置
- 完整的参数验证和约束设置
- 符合ComfyUI SDK的参数规范

这个增强使工作流模板能够处理复杂的AI工作流场景,
特别适合图像生成、音频处理、视频编辑等专业应用。
This commit is contained in:
root 2025-08-08 22:25:57 +08:00
parent 931285b4f2
commit d7e1ce792f
1 changed files with 252 additions and 22 deletions

View File

@ -28,16 +28,23 @@ interface TemplateMetadata {
}
interface ParameterSchema {
param_type: 'string' | 'number' | 'boolean' | 'array' | 'object';
param_type: 'string' | 'number' | 'boolean' | 'array' | 'object' | 'image' | 'audio' | 'video' | 'integer' | 'float';
required?: boolean;
default?: any;
description?: string;
enum?: any[];
min?: number;
max?: number;
step?: number;
pattern?: string;
items?: ParameterSchema;
properties?: Record<string, ParameterSchema>;
// 媒体文件相关属性
accept?: string; // 文件类型限制
maxSize?: number; // 最大文件大小(字节)
width?: number; // 图片/视频宽度
height?: number; // 图片/视频高度
duration?: number; // 音频/视频时长限制
}
interface WorkflowTemplateData {
@ -183,6 +190,61 @@ export const WorkflowTemplateCreator: React.FC<WorkflowTemplateCreatorProps> = (
setNewParameterName('');
};
// 根据参数类型获取默认配置
const getDefaultConfigForType = (type: string): Partial<ParameterSchema> => {
switch (type) {
case 'integer':
return {
default: 10,
min: 0,
max: 100,
step: 1,
};
case 'float':
return {
default: 0.3,
min: 0.0,
max: 1.0,
step: 0.01,
};
case 'image':
return {
default: '',
accept: '.jpg,.jpeg,.png,.webp,.bmp,.tiff',
maxSize: 10 * 1024 * 1024, // 10MB
description: '支持的图片格式JPG, PNG, WebP, BMP, TIFF',
};
case 'audio':
return {
default: '',
accept: '.mp3,.wav,.flac,.aac,.ogg',
maxSize: 50 * 1024 * 1024, // 50MB
description: '支持的音频格式MP3, WAV, FLAC, AAC, OGG',
};
case 'video':
return {
default: '',
accept: '.mp4,.avi,.mov,.mkv,.webm',
maxSize: 500 * 1024 * 1024, // 500MB
description: '支持的视频格式MP4, AVI, MOV, MKV, WebM',
};
case 'boolean':
return {
default: false,
};
case 'number':
return {
default: 0,
min: 0,
max: 100,
};
default:
return {
default: '',
};
}
};
// 验证表单
const validateForm = (): boolean => {
const newErrors: Record<string, string> = {};
@ -567,17 +629,33 @@ export const WorkflowTemplateCreator: React.FC<WorkflowTemplateCreatorProps> = (
</label>
<select
value={schema.param_type}
onChange={(e) => updateParameter(paramName, {
...schema,
param_type: e.target.value as any
})}
onChange={(e) => {
const newType = e.target.value as any;
const defaultConfig = getDefaultConfigForType(newType);
updateParameter(paramName, {
...schema,
param_type: newType,
...defaultConfig
});
}}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value="string"> (String)</option>
<option value="number"> (Number)</option>
<option value="boolean"> (Boolean)</option>
<option value="array"> (Array)</option>
<option value="object"> (Object)</option>
<optgroup label="基础类型">
<option value="string"> (String)</option>
<option value="boolean"> (Boolean)</option>
<option value="array"> (Array)</option>
<option value="object"> (Object)</option>
</optgroup>
<optgroup label="数值类型">
<option value="integer"> (Integer)</option>
<option value="float"> (Float)</option>
<option value="number"> (Number)</option>
</optgroup>
<optgroup label="媒体类型">
<option value="image"> (Image)</option>
<option value="audio"> (Audio)</option>
<option value="video"> (Video)</option>
</optgroup>
</select>
</div>
@ -585,16 +663,60 @@ export const WorkflowTemplateCreator: React.FC<WorkflowTemplateCreatorProps> = (
<label className="block text-sm font-medium text-gray-700 mb-1">
</label>
<input
type={schema.param_type === 'number' ? 'number' : 'text'}
value={schema.default || ''}
onChange={(e) => updateParameter(paramName, {
...schema,
default: schema.param_type === 'number' ? Number(e.target.value) : e.target.value
})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="设置默认值"
/>
{schema.param_type === 'boolean' ? (
<select
value={schema.default ? 'true' : 'false'}
onChange={(e) => updateParameter(paramName, {
...schema,
default: e.target.value === 'true'
})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value="false">False</option>
<option value="true">True</option>
</select>
) : schema.param_type === 'integer' || schema.param_type === 'float' || schema.param_type === 'number' ? (
<input
type="number"
step={schema.step || (schema.param_type === 'float' ? 0.01 : 1)}
min={schema.min}
max={schema.max}
value={schema.default || ''}
onChange={(e) => updateParameter(paramName, {
...schema,
default: schema.param_type === 'integer' ? parseInt(e.target.value) || 0 : parseFloat(e.target.value) || 0
})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="设置默认值"
/>
) : schema.param_type === 'image' || schema.param_type === 'audio' || schema.param_type === 'video' ? (
<div className="space-y-2">
<input
type="text"
value={schema.default || ''}
onChange={(e) => updateParameter(paramName, {
...schema,
default: e.target.value
})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="默认文件名或路径"
/>
<p className="text-xs text-gray-500">
: {schema.accept || '所有格式'}
</p>
</div>
) : (
<input
type="text"
value={schema.default || ''}
onChange={(e) => updateParameter(paramName, {
...schema,
default: e.target.value
})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="设置默认值"
/>
)}
</div>
<div className="md:col-span-2">
@ -628,14 +750,15 @@ export const WorkflowTemplateCreator: React.FC<WorkflowTemplateCreatorProps> = (
</label>
</div>
{schema.param_type === 'number' && (
<div className="grid grid-cols-2 gap-2">
{(schema.param_type === 'number' || schema.param_type === 'integer' || schema.param_type === 'float') && (
<div className="grid grid-cols-3 gap-2">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
</label>
<input
type="number"
step={schema.param_type === 'float' ? 0.01 : 1}
value={schema.min || ''}
onChange={(e) => updateParameter(paramName, {
...schema,
@ -651,6 +774,7 @@ export const WorkflowTemplateCreator: React.FC<WorkflowTemplateCreatorProps> = (
</label>
<input
type="number"
step={schema.param_type === 'float' ? 0.01 : 1}
value={schema.max || ''}
onChange={(e) => updateParameter(paramName, {
...schema,
@ -660,6 +784,112 @@ export const WorkflowTemplateCreator: React.FC<WorkflowTemplateCreatorProps> = (
placeholder="最大值"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
</label>
<input
type="number"
step={schema.param_type === 'float' ? 0.001 : 1}
value={schema.step || ''}
onChange={(e) => updateParameter(paramName, {
...schema,
step: e.target.value ? Number(e.target.value) : undefined
})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder={schema.param_type === 'float' ? '0.01' : '1'}
/>
</div>
</div>
)}
{(schema.param_type === 'image' || schema.param_type === 'audio' || schema.param_type === 'video') && (
<div className="md:col-span-2 space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
</label>
<input
type="text"
value={schema.accept || ''}
onChange={(e) => updateParameter(paramName, {
...schema,
accept: e.target.value
})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder=".jpg,.png,.webp"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
(MB)
</label>
<input
type="number"
value={schema.maxSize ? Math.round(schema.maxSize / (1024 * 1024)) : ''}
onChange={(e) => updateParameter(paramName, {
...schema,
maxSize: e.target.value ? Number(e.target.value) * 1024 * 1024 : undefined
})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="10"
/>
</div>
</div>
{(schema.param_type === 'image' || schema.param_type === 'video') && (
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
(px)
</label>
<input
type="number"
value={schema.width || ''}
onChange={(e) => updateParameter(paramName, {
...schema,
width: e.target.value ? Number(e.target.value) : undefined
})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="1920"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
(px)
</label>
<input
type="number"
value={schema.height || ''}
onChange={(e) => updateParameter(paramName, {
...schema,
height: e.target.value ? Number(e.target.value) : undefined
})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="1080"
/>
</div>
</div>
)}
{(schema.param_type === 'audio' || schema.param_type === 'video') && (
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
()
</label>
<input
type="number"
value={schema.duration || ''}
onChange={(e) => updateParameter(paramName, {
...schema,
duration: e.target.value ? Number(e.target.value) : undefined
})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="300"
/>
</div>
)}
</div>
)}