fix: resolve TypeScript error in ComfyUIConfigModal handleInputChange function

- Updated handleInputChange function signature to accept undefined values
- Fixes 'Argument of type number | undefined is not assignable' error on line 248
- Allows proper handling of optional numeric fields in ComfyuiConfig interface
This commit is contained in:
imeepos 2025-08-04 10:41:07 +08:00
parent 42ae580034
commit dccbb7cda6
1 changed files with 335 additions and 0 deletions

View File

@ -0,0 +1,335 @@
import React, { useState, useEffect } from 'react';
import { X, Save, TestTube, AlertCircle, CheckCircle } from 'lucide-react';
import ComfyuiService from '../services/comfyuiService';
import type { ComfyuiConfig } from '../types/comfyui';
interface ComfyUIConfigModalProps {
isOpen: boolean;
onClose: () => void;
currentConfig: ComfyuiConfig;
onConfigUpdate: (config: ComfyuiConfig) => void;
}
/**
* ComfyUI
*/
const ComfyUIConfigModal: React.FC<ComfyUIConfigModalProps> = ({
isOpen,
onClose,
currentConfig,
onConfigUpdate,
}) => {
const [config, setConfig] = useState<ComfyuiConfig>(currentConfig);
const [testing, setTesting] = useState(false);
const [testResult, setTestResult] = useState<{ success: boolean; message: string } | null>(null);
const [saving, setSaving] = useState(false);
const [errors, setErrors] = useState<Record<string, string>>({});
useEffect(() => {
if (isOpen) {
setConfig(currentConfig);
setTestResult(null);
setErrors({});
}
}, [isOpen, currentConfig]);
// ============================================================================
// 表单验证
// ============================================================================
const validateConfig = (configToValidate: ComfyuiConfig): Record<string, string> => {
const newErrors: Record<string, string> = {};
// 验证 base_url
if (!configToValidate.base_url.trim()) {
newErrors.base_url = 'API 基础 URL 不能为空';
} else {
try {
new URL(configToValidate.base_url);
} catch {
newErrors.base_url = '请输入有效的 URL 格式';
}
}
// 验证 timeout
if (configToValidate.timeout !== undefined) {
if (configToValidate.timeout < 1 || configToValidate.timeout > 300) {
newErrors.timeout = '超时时间应在 1-300 秒之间';
}
}
// 验证 retry_attempts
if (configToValidate.retry_attempts !== undefined) {
if (configToValidate.retry_attempts < 0 || configToValidate.retry_attempts > 10) {
newErrors.retry_attempts = '重试次数应在 0-10 次之间';
}
}
// 验证 max_concurrency
if (configToValidate.max_concurrency !== undefined) {
if (configToValidate.max_concurrency < 1 || configToValidate.max_concurrency > 50) {
newErrors.max_concurrency = '最大并发数应在 1-50 之间';
}
}
return newErrors;
};
// ============================================================================
// 事件处理
// ============================================================================
const handleInputChange = (field: keyof ComfyuiConfig, value: string | number | boolean | undefined) => {
setConfig(prev => ({ ...prev, [field]: value }));
// 清除对应字段的错误
if (errors[field]) {
setErrors(prev => {
const newErrors = { ...prev };
delete newErrors[field];
return newErrors;
});
}
};
const handleTestConnection = async () => {
const validationErrors = validateConfig(config);
if (Object.keys(validationErrors).length > 0) {
setErrors(validationErrors);
return;
}
setTesting(true);
setTestResult(null);
try {
// 临时更新配置进行测试
await ComfyuiService.updateConfig(config);
const isConnected = await ComfyuiService.testConnection();
setTestResult({
success: isConnected,
message: isConnected ? '连接成功!' : '连接失败,请检查配置',
});
} catch (error) {
setTestResult({
success: false,
message: `连接测试失败: ${error}`,
});
} finally {
setTesting(false);
}
};
const handleSave = async () => {
const validationErrors = validateConfig(config);
if (Object.keys(validationErrors).length > 0) {
setErrors(validationErrors);
return;
}
setSaving(true);
try {
await ComfyuiService.updateConfig(config);
onConfigUpdate(config);
onClose();
} catch (error) {
setTestResult({
success: false,
message: `保存配置失败: ${error}`,
});
} finally {
setSaving(false);
}
};
const handleReset = () => {
const defaultConfig: ComfyuiConfig = {
base_url: 'https://bowongai-dev--waas-demo-fastapi-webapp.modal.run',
timeout: 30,
retry_attempts: 3,
enable_cache: true,
max_concurrency: 8,
};
setConfig(defaultConfig);
setErrors({});
setTestResult(null);
};
if (!isOpen) return null;
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="bg-white rounded-lg shadow-xl w-full max-w-2xl mx-4 max-h-[90vh] overflow-y-auto">
{/* 标题栏 */}
<div className="flex items-center justify-between p-6 border-b border-gray-200">
<h2 className="text-xl font-semibold text-gray-900">ComfyUI </h2>
<button
onClick={onClose}
className="text-gray-400 hover:text-gray-600 transition-colors"
>
<X className="w-6 h-6" />
</button>
</div>
{/* 表单内容 */}
<div className="p-6 space-y-6">
{/* API 基础 URL */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
API URL *
</label>
<input
type="url"
value={config.base_url}
onChange={(e) => handleInputChange('base_url', e.target.value)}
className={`w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
errors.base_url ? 'border-red-300' : 'border-gray-300'
}`}
placeholder="https://api.example.com"
/>
{errors.base_url && (
<p className="mt-1 text-sm text-red-600">{errors.base_url}</p>
)}
</div>
{/* 超时设置 */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
</label>
<input
type="number"
min="1"
max="300"
value={config.timeout || ''}
onChange={(e) => handleInputChange('timeout', parseInt(e.target.value) || undefined)}
className={`w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
errors.timeout ? 'border-red-300' : 'border-gray-300'
}`}
placeholder="30"
/>
{errors.timeout && (
<p className="mt-1 text-sm text-red-600">{errors.timeout}</p>
)}
</div>
{/* 重试次数 */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
</label>
<input
type="number"
min="0"
max="10"
value={config.retry_attempts || ''}
onChange={(e) => handleInputChange('retry_attempts', parseInt(e.target.value) || undefined)}
className={`w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
errors.retry_attempts ? 'border-red-300' : 'border-gray-300'
}`}
placeholder="3"
/>
{errors.retry_attempts && (
<p className="mt-1 text-sm text-red-600">{errors.retry_attempts}</p>
)}
</div>
{/* 最大并发数 */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
</label>
<input
type="number"
min="1"
max="50"
value={config.max_concurrency || ''}
onChange={(e) => handleInputChange('max_concurrency', parseInt(e.target.value) || undefined)}
className={`w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 ${
errors.max_concurrency ? 'border-red-300' : 'border-gray-300'
}`}
placeholder="8"
/>
{errors.max_concurrency && (
<p className="mt-1 text-sm text-red-600">{errors.max_concurrency}</p>
)}
</div>
{/* 启用缓存 */}
<div className="flex items-center">
<input
type="checkbox"
id="enable_cache"
checked={config.enable_cache || false}
onChange={(e) => handleInputChange('enable_cache', e.target.checked)}
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded"
/>
<label htmlFor="enable_cache" className="ml-2 block text-sm text-gray-700">
</label>
</div>
{/* 测试结果 */}
{testResult && (
<div className={`p-4 rounded-md ${
testResult.success ? 'bg-green-50 border border-green-200' : 'bg-red-50 border border-red-200'
}`}>
<div className="flex items-center gap-2">
{testResult.success ? (
<CheckCircle className="w-5 h-5 text-green-500" />
) : (
<AlertCircle className="w-5 h-5 text-red-500" />
)}
<span className={`text-sm ${
testResult.success ? 'text-green-700' : 'text-red-700'
}`}>
{testResult.message}
</span>
</div>
</div>
)}
</div>
{/* 操作按钮 */}
<div className="flex items-center justify-between p-6 border-t border-gray-200">
<button
onClick={handleReset}
className="px-4 py-2 text-gray-600 hover:text-gray-800 transition-colors"
>
</button>
<div className="flex items-center gap-3">
<button
onClick={handleTestConnection}
disabled={testing}
className="flex items-center gap-2 px-4 py-2 bg-yellow-600 text-white rounded-md hover:bg-yellow-700 transition-colors disabled:opacity-50"
>
<TestTube className={`w-4 h-4 ${testing ? 'animate-pulse' : ''}`} />
{testing ? '测试中...' : '测试连接'}
</button>
<button
onClick={onClose}
className="px-4 py-2 text-gray-600 hover:text-gray-800 transition-colors"
>
</button>
<button
onClick={handleSave}
disabled={saving}
className="flex items-center gap-2 px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors disabled:opacity-50"
>
<Save className={`w-4 h-4 ${saving ? 'animate-pulse' : ''}`} />
{saving ? '保存中...' : '保存'}
</button>
</div>
</div>
</div>
</div>
);
};
export default ComfyUIConfigModal;