import { useState, useEffect } from 'react'; import { Workflow, CheckCircle, FileText, Upload, ChevronRight, RefreshCw, Zap, Users, ArrowRight, AlertTriangle, } from 'lucide-react'; import { api } from '../lib/api'; import type { Workflow as WorkflowType, WorkflowMeeting } from '../types'; // 工作流详情面板 function WorkflowDetailPanel({ workflow, onClose, onReload, }: { workflow: WorkflowType; onClose: () => void; onReload: () => void; }) { const [loading, setLoading] = useState(null); const [executionStatus, setExecutionStatus] = useState>({}); // 刷新执行节点状态 const refreshExecutionStatus = async (meetingId: string) => { if (workflow.meetings.find(m => m.meeting_id === meetingId)?.node_type === 'execution') { try { const status = await api.workflow.getExecutionStatus(workflow.workflow_id, meetingId); setExecutionStatus(prev => ({ ...prev, [meetingId]: status })); } catch (err) { // 忽略错误 } } }; useEffect(() => { // 刷新所有执行节点的状态 workflow.meetings.forEach(m => { if (m.node_type === 'execution') { refreshExecutionStatus(m.meeting_id); } }); }, [workflow]); const handleCompleteMeeting = async (meetingId: string) => { setLoading(meetingId); try { await api.workflow.complete(workflow.workflow_id, meetingId); onReload(); } catch (err) { alert('标记完成失败'); } finally { setLoading(null); } }; const handleFailAndJump = async (meetingId: string) => { const meeting = workflow.meetings.find(m => m.meeting_id === meetingId); if (!meeting?.on_failure) { alert('此节点未配置失败跳转目标'); return; } if (!confirm(`确定要跳转到 "${meeting.on_failure}" 吗?`)) { return; } setLoading(`fail-${meetingId}`); try { const result = await api.workflow.handleFailure(workflow.workflow_id, meetingId); if (result.target) { onReload(); } else { alert('未配置失败跳转'); } } catch (err) { alert('操作失败'); } finally { setLoading(null); } }; const getMeetingStatus = (meeting: WorkflowMeeting) => { if (meeting.completed) return 'completed'; const allPrevCompleted = workflow.meetings .slice(0, workflow.meetings.findIndex((m) => m.meeting_id === meeting.meeting_id)) .every((m) => m.completed); return allPrevCompleted ? 'active' : 'pending'; }; return (

工作流详情

{/* Workflow Info */}

{workflow.name}

{workflow.workflow_id}

{workflow.description}

{workflow.status === 'completed' ? '已完成' : workflow.status === 'in_progress' ? '进行中' : '等待中'} 进度: {workflow.progress}
{/* Progress Bar */}
{/* Meetings */}

流程节点 ({workflow.meetings.length})

{workflow.meetings.map((meeting, index) => { const status = getMeetingStatus(meeting); const isExecution = meeting.node_type === 'execution'; const execStatus = executionStatus[meeting.meeting_id]; return (
{/* 节点图标 */}
{status === 'completed' ? : isExecution ? : index + 1}
{/* 标题和类型 */}

{meeting.title}

{/* 节点类型标签 */} {isExecution ? '执行' : '会议'} {/* 失败跳转标签 */} {meeting.on_failure && ( 失败 → {meeting.on_failure} )}
{/* ID 和参会者 */}

{meeting.meeting_id}

{/* 参会者列表 */}
{meeting.attendees.map((attendee, i) => ( {attendee} ))}
{/* 执行节点进度 */} {isExecution && execStatus && (
进度: {execStatus.progress} {execStatus.is_ready ? '就绪' : '进行中'}
{execStatus.missing && execStatus.missing.length > 0 && (
等待中: {execStatus.missing.join(', ')}
)}
)} {/* 依赖关系 */} {meeting.depends_on && meeting.depends_on.length > 0 && (
依赖: {meeting.depends_on.join(', ')}
)}
{/* 操作按钮 */}
{status === 'active' && !meeting.completed && ( )} {meeting.completed && meeting.on_failure && ( )} {meeting.completed && ( 已完成 )}
{/* 节点间的连接线 */} {index < workflow.meetings.length - 1 && (
)}
); })}
); } export function WorkflowPage() { const [workflows, setWorkflows] = useState([]); const [selectedWorkflow, setSelectedWorkflow] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [uploading, setUploading] = useState(false); const [starting, setStarting] = useState(null); const loadWorkflows = async () => { try { setLoading(true); // 加载已加载的工作流列表 const listRes = await api.workflow.list(); const loadedWorkflows: WorkflowType[] = []; // 从已加载的工作流中获取详情 for (const wf of listRes.workflows) { try { const detail = await api.workflow.get(wf.workflow_id); loadedWorkflows.push(detail); } catch { // 忽略加载失败的 } } setWorkflows(loadedWorkflows); setError(null); } catch (err) { setError(err instanceof Error ? err.message : '加载失败'); } finally { setLoading(false); } }; const startDefaultWorkflow = async () => { setStarting('default-dev-flow'); try { const workflow = await api.workflow.start('default-dev-flow.yaml'); setWorkflows(prev => [...prev.filter(w => w.workflow_id !== workflow.workflow_id), workflow]); setSelectedWorkflow(workflow); } catch (err) { alert('启动默认工作流失败: ' + (err instanceof Error ? err.message : '未知错误')); } finally { setStarting(null); } }; useEffect(() => { loadWorkflows(); const interval = setInterval(loadWorkflows, 30000); return () => clearInterval(interval); }, []); const handleFileUpload = async (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; if (!file.name.endsWith('.yaml') && !file.name.endsWith('.yml')) { alert('请上传 YAML 文件'); return; } setUploading(true); try { await file.text(); // 这里应该调用后端 API 上传文件 // 暂时模拟成功 alert(`文件 ${file.name} 上传成功`); loadWorkflows(); } catch (err) { alert('上传失败'); } finally { setUploading(false); } }; const getStatusColor = (status: string) => { switch (status) { case 'completed': return '#00ff9d'; case 'in_progress': return '#00f0ff'; default: return '#ff9500'; } }; if (loading && workflows.length === 0) { return (
加载中...
); } return (
{/* Header */}

工作流

管理和执行工作流 - 包含会议节点和执行节点

{error && (
连接后端失败: {error}
)} {/* Workflows Grid */}
{workflows.map((workflow) => (
setSelectedWorkflow(workflow)} style={{ padding: 20, background: 'rgba(17, 24, 39, 0.7)', borderRadius: 12, border: '1px solid rgba(0, 240, 255, 0.1)', cursor: 'pointer', transition: 'all 0.2s ease', }} onMouseEnter={(e) => { e.currentTarget.style.borderColor = 'rgba(0, 240, 255, 0.3)'; e.currentTarget.style.transform = 'translateY(-2px)'; }} onMouseLeave={(e) => { e.currentTarget.style.borderColor = 'rgba(0, 240, 255, 0.1)'; e.currentTarget.style.transform = 'translateY(0)'; }} >
{workflow.status === 'completed' ? '已完成' : workflow.status === 'in_progress' ? '进行中' : '等待中'}

{workflow.name}

{workflow.workflow_id}

{workflow.description}

{/* Progress */}
{workflow.meetings.length} 个节点 {/* 统计节点类型 */} ({workflow.meetings.filter(m => m.node_type === 'execution').length} 执行 + {workflow.meetings.filter(m => m.node_type === 'meeting').length} 会议)
查看详情
))}
{workflows.length === 0 && !loading && (

暂无工作流

上传 YAML 文件创建

)} {/* Detail Panel */} {selectedWorkflow && ( setSelectedWorkflow(null)} onReload={loadWorkflows} /> )}
); }