主要功能: - 实现UI字段与ComfyUI工作流节点的映射配置 - 添加节点映射编辑器组件(NodeMappingEditor) - 实现工作流执行服务(WorkflowExecutionService) - 添加工作流执行页面和结果展示组件 - 完善错误处理和用户反馈机制 修复问题: - 修复滑块/数字输入最小值不能填0的问题 - 修复图片上传组件不可用的问题 - 修复React渲染对象错误(LayerMask问题) - 添加拖拽上传功能和进度显示 技术改进: - 支持0-1浮点数范围和步长配置 - 实现完整的文件上传流程(本地路径云端URL) - 添加类型安全的节点映射配置 - 优化用户界面交互体验
287 lines
8.0 KiB
TypeScript
287 lines
8.0 KiB
TypeScript
/**
|
||
* 错误处理工具类
|
||
* 提供统一的错误处理和用户友好的错误消息
|
||
*/
|
||
|
||
export interface ErrorInfo {
|
||
code: string;
|
||
message: string;
|
||
details?: string;
|
||
suggestions?: string[];
|
||
}
|
||
|
||
export class ErrorHandler {
|
||
/**
|
||
* 解析Tauri命令错误
|
||
*/
|
||
static parseTauriError(error: any): ErrorInfo {
|
||
const errorStr = String(error);
|
||
|
||
// 工作流模板相关错误
|
||
if (errorStr.includes('工作流模板不存在')) {
|
||
return {
|
||
code: 'WORKFLOW_TEMPLATE_NOT_FOUND',
|
||
message: '工作流模板不存在',
|
||
details: '请确认工作流模板ID是否正确',
|
||
suggestions: [
|
||
'检查工作流模板是否已被删除',
|
||
'刷新页面重新加载模板列表',
|
||
'联系管理员确认模板状态'
|
||
]
|
||
};
|
||
}
|
||
|
||
// 输入数据验证错误
|
||
if (errorStr.includes('输入数据验证失败')) {
|
||
const details = errorStr.split('输入数据验证失败:')[1]?.trim();
|
||
return {
|
||
code: 'INPUT_VALIDATION_FAILED',
|
||
message: '输入数据验证失败',
|
||
details: details || '请检查输入的参数是否符合要求',
|
||
suggestions: [
|
||
'检查必填字段是否已填写',
|
||
'确认数值字段的取值范围',
|
||
'验证文本字段的格式要求',
|
||
'检查选择字段的选项是否正确'
|
||
]
|
||
};
|
||
}
|
||
|
||
// 节点映射错误
|
||
if (errorStr.includes('节点映射') || errorStr.includes('node mapping')) {
|
||
return {
|
||
code: 'NODE_MAPPING_ERROR',
|
||
message: '节点映射配置错误',
|
||
details: '工作流的节点映射配置有问题',
|
||
suggestions: [
|
||
'检查UI字段是否正确映射到ComfyUI节点',
|
||
'确认目标节点ID是否存在于工作流中',
|
||
'验证输入字段名称是否正确',
|
||
'检查数据转换类型是否匹配'
|
||
]
|
||
};
|
||
}
|
||
|
||
// ComfyUI连接错误
|
||
if (errorStr.includes('ComfyUI') && (errorStr.includes('连接') || errorStr.includes('connection'))) {
|
||
return {
|
||
code: 'COMFYUI_CONNECTION_ERROR',
|
||
message: 'ComfyUI服务连接失败',
|
||
details: '无法连接到ComfyUI服务器',
|
||
suggestions: [
|
||
'检查ComfyUI服务是否正在运行',
|
||
'确认ComfyUI服务器地址和端口配置',
|
||
'检查网络连接是否正常',
|
||
'查看ComfyUI服务器日志'
|
||
]
|
||
};
|
||
}
|
||
|
||
// 工作流执行错误
|
||
if (errorStr.includes('执行工作流失败') || errorStr.includes('workflow execution failed')) {
|
||
return {
|
||
code: 'WORKFLOW_EXECUTION_FAILED',
|
||
message: '工作流执行失败',
|
||
details: '工作流在执行过程中遇到错误',
|
||
suggestions: [
|
||
'检查输入参数是否正确',
|
||
'确认ComfyUI工作流配置是否有效',
|
||
'查看ComfyUI服务器日志获取详细错误信息',
|
||
'尝试使用默认参数重新执行'
|
||
]
|
||
};
|
||
}
|
||
|
||
// 数据库错误
|
||
if (errorStr.includes('数据库') || errorStr.includes('database')) {
|
||
return {
|
||
code: 'DATABASE_ERROR',
|
||
message: '数据库操作失败',
|
||
details: '数据库操作过程中发生错误',
|
||
suggestions: [
|
||
'检查数据库连接是否正常',
|
||
'确认数据库文件是否存在',
|
||
'尝试重启应用程序',
|
||
'检查磁盘空间是否充足'
|
||
]
|
||
};
|
||
}
|
||
|
||
// 文件系统错误
|
||
if (errorStr.includes('文件') || errorStr.includes('file') || errorStr.includes('路径') || errorStr.includes('path')) {
|
||
return {
|
||
code: 'FILE_SYSTEM_ERROR',
|
||
message: '文件系统操作失败',
|
||
details: '文件读取、写入或路径访问失败',
|
||
suggestions: [
|
||
'检查文件路径是否正确',
|
||
'确认文件是否存在',
|
||
'检查文件访问权限',
|
||
'确认磁盘空间是否充足'
|
||
]
|
||
};
|
||
}
|
||
|
||
// 网络错误
|
||
if (errorStr.includes('网络') || errorStr.includes('network') || errorStr.includes('timeout')) {
|
||
return {
|
||
code: 'NETWORK_ERROR',
|
||
message: '网络请求失败',
|
||
details: '网络连接或请求超时',
|
||
suggestions: [
|
||
'检查网络连接是否正常',
|
||
'确认服务器地址是否正确',
|
||
'尝试稍后重新请求',
|
||
'检查防火墙设置'
|
||
]
|
||
};
|
||
}
|
||
|
||
// 权限错误
|
||
if (errorStr.includes('权限') || errorStr.includes('permission') || errorStr.includes('access denied')) {
|
||
return {
|
||
code: 'PERMISSION_ERROR',
|
||
message: '权限不足',
|
||
details: '没有足够的权限执行此操作',
|
||
suggestions: [
|
||
'检查文件或目录的访问权限',
|
||
'尝试以管理员身份运行应用',
|
||
'确认用户账户权限设置',
|
||
'联系系统管理员'
|
||
]
|
||
};
|
||
}
|
||
|
||
// 默认错误
|
||
return {
|
||
code: 'UNKNOWN_ERROR',
|
||
message: '未知错误',
|
||
details: errorStr,
|
||
suggestions: [
|
||
'尝试重新执行操作',
|
||
'检查应用程序日志',
|
||
'重启应用程序',
|
||
'联系技术支持'
|
||
]
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 格式化错误消息用于显示
|
||
*/
|
||
static formatErrorMessage(errorInfo: ErrorInfo): string {
|
||
let message = errorInfo.message;
|
||
|
||
if (errorInfo.details) {
|
||
message += `\n\n详细信息:${errorInfo.details}`;
|
||
}
|
||
|
||
if (errorInfo.suggestions && errorInfo.suggestions.length > 0) {
|
||
message += '\n\n建议解决方案:';
|
||
errorInfo.suggestions.forEach((suggestion, index) => {
|
||
message += `\n${index + 1}. ${suggestion}`;
|
||
});
|
||
}
|
||
|
||
return message;
|
||
}
|
||
|
||
/**
|
||
* 显示错误通知
|
||
*/
|
||
static showErrorNotification(error: any, title: string = '操作失败') {
|
||
const errorInfo = this.parseTauriError(error);
|
||
const message = this.formatErrorMessage(errorInfo);
|
||
|
||
// 这里可以集成具体的通知组件
|
||
console.error(`${title}:`, message);
|
||
|
||
// 如果有全局通知系统,可以在这里调用
|
||
// notificationService.error(title, message);
|
||
|
||
return errorInfo;
|
||
}
|
||
|
||
/**
|
||
* 检查是否为可重试的错误
|
||
*/
|
||
static isRetryableError(errorInfo: ErrorInfo): boolean {
|
||
const retryableCodes = [
|
||
'COMFYUI_CONNECTION_ERROR',
|
||
'NETWORK_ERROR',
|
||
'WORKFLOW_EXECUTION_FAILED'
|
||
];
|
||
|
||
return retryableCodes.includes(errorInfo.code);
|
||
}
|
||
|
||
/**
|
||
* 检查是否为用户输入错误
|
||
*/
|
||
static isUserInputError(errorInfo: ErrorInfo): boolean {
|
||
const userInputCodes = [
|
||
'INPUT_VALIDATION_FAILED',
|
||
'NODE_MAPPING_ERROR'
|
||
];
|
||
|
||
return userInputCodes.includes(errorInfo.code);
|
||
}
|
||
|
||
/**
|
||
* 获取错误的严重程度
|
||
*/
|
||
static getErrorSeverity(errorInfo: ErrorInfo): 'low' | 'medium' | 'high' | 'critical' {
|
||
switch (errorInfo.code) {
|
||
case 'INPUT_VALIDATION_FAILED':
|
||
case 'NODE_MAPPING_ERROR':
|
||
return 'low';
|
||
|
||
case 'COMFYUI_CONNECTION_ERROR':
|
||
case 'NETWORK_ERROR':
|
||
case 'WORKFLOW_EXECUTION_FAILED':
|
||
return 'medium';
|
||
|
||
case 'DATABASE_ERROR':
|
||
case 'FILE_SYSTEM_ERROR':
|
||
case 'PERMISSION_ERROR':
|
||
return 'high';
|
||
|
||
case 'WORKFLOW_TEMPLATE_NOT_FOUND':
|
||
case 'UNKNOWN_ERROR':
|
||
default:
|
||
return 'critical';
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 错误边界组件的错误处理
|
||
*/
|
||
export const handleComponentError = (error: Error, errorInfo: any) => {
|
||
console.error('组件错误:', error, errorInfo);
|
||
|
||
// 可以在这里发送错误报告到监控服务
|
||
// errorReportingService.captureException(error, { extra: errorInfo });
|
||
};
|
||
|
||
/**
|
||
* Promise 错误处理装饰器
|
||
*/
|
||
export const withErrorHandling = <T extends (...args: any[]) => Promise<any>>(
|
||
fn: T,
|
||
errorHandler?: (error: any) => void
|
||
): T => {
|
||
return (async (...args: any[]) => {
|
||
try {
|
||
return await fn(...args);
|
||
} catch (error) {
|
||
if (errorHandler) {
|
||
errorHandler(error);
|
||
} else {
|
||
ErrorHandler.showErrorNotification(error);
|
||
}
|
||
throw error;
|
||
}
|
||
}) as T;
|
||
};
|