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:
parent
931285b4f2
commit
d7e1ce792f
|
|
@ -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>
|
||||
)}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue