auto-sync: 2026-05-19 14:00:04

This commit is contained in:
cfdaily
2026-05-19 14:00:04 +08:00
parent c354bf11df
commit 1baf26237e
+32 -20
View File
@@ -282,33 +282,46 @@ function ActionCheckpoint({
export default function CheckpointPanel({
taskId,
checkpoints,
onDone,
}: {
taskId: string;
checkpoints: CheckpointInfo[];
onDone: () => void;
}) {
const [checkpoints, setCheckpoints] = useState<Checkpoint[]>([]);
const [activeIdx, setActiveIdx] = useState(0);
const info = checkpoints[activeIdx];
if (!info) return null;
const cp = info.checkpoint;
const typeIcon = cp.type === 'verify' ? '🔍' : cp.type === 'decision' ? '🎯' : '🔧';
const typeColor = cp.type === 'verify' ? '#6a9eff' : cp.type === 'decision' ? '#818cf8' : '#f59e0b';
const typeLabel = cp.type === 'verify' ? '验证 Checkpoint' : cp.type === 'decision' ? '决策 Checkpoint' : '执行 Checkpoint';
useEffect(() => {
const pid = api._getProjectId();
if (!pid) return;
fetch(`/api/projects/${pid}/tasks/${taskId}/checkpoints`)
.then(r => r.json())
.then(d => setCheckpoints(d.checkpoints || []))
.catch(() => {});
}, [taskId]);
const cp = checkpoints[activeIdx];
if (!cp) return null;
// 只显示 pending 的
const pendingCps = checkpoints.filter(c => c.status === 'pending');
const activeCp = pendingCps[activeIdx] || pendingCps[0];
if (!activeCp) return <div style={{ fontSize: 12, color: 'var(--muted)', textAlign: 'center', padding: 20 }}> Checkpoint </div>;
const typeIcon = activeCp.type === 'verify' ? '🔍' : activeCp.type === 'decision' ? '🎯' : '🔧';
const typeColor = activeCp.type === 'verify' ? '#6a9eff' : activeCp.type === 'decision' ? '#818cf8' : '#f59e0b';
const typeLabel = activeCp.type === 'verify' ? '验证 Checkpoint' : activeCp.type === 'decision' ? '决策 Checkpoint' : '执行 Checkpoint';
return (
<div>
{/* 多节点 Tab */}
{checkpoints.length > 1 && (
{/* 多 checkpoint Tab */}
{pendingCps.length > 1 && (
<div style={{ display: 'flex', borderBottom: '1px solid var(--line)', marginBottom: 12 }}>
{checkpoints.map((c, i) => (
<button key={c.node_id}
className={`tab-btn ${i === activeIdx ? 'active' : ''}`}
{pendingCps.map((c, i) => (
<button key={c.id}
onClick={() => setActiveIdx(i)}
style={{ fontSize: 12, color: i === activeIdx ? typeColor : 'var(--muted)', borderBottomColor: i === activeIdx ? typeColor : 'transparent' }}
>🛐 {c.node_name}</button>
style={{ fontSize: 12, padding: '6px 12px', border: 'none', background: 'transparent', cursor: 'pointer',
color: i === activeIdx ? typeColor : 'var(--muted)', borderBottom: `2px solid ${i === activeIdx ? typeColor : 'transparent'}` }}
>{typeIcon} {c.title}</button>
))}
</div>
)}
@@ -318,17 +331,16 @@ export default function CheckpointPanel({
<div style={{ width: 36, height: 36, borderRadius: '50%', background: `${typeColor}22`, display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 16 }}>{typeIcon}</div>
<div>
<div style={{ fontSize: 14, fontWeight: 700, color: typeColor }}>{typeLabel}</div>
<div style={{ fontSize: 11, color: 'var(--muted)' }}> {info.node_name}{AGENT_EMOJI[info.agent_id] || ''} {info.agent_id}</div>
</div>
</div>
{/* 标题 */}
<div style={{ fontSize: 15, fontWeight: 700, marginBottom: 6 }}>{cp.title}</div>
<div style={{ fontSize: 15, fontWeight: 700, marginBottom: 6 }}>{activeCp.title}</div>
{/* 按类型渲染 */}
{cp.type === 'verify' && <VerifyCheckpoint taskId={taskId} info={info} onDone={onDone} />}
{cp.type === 'decision' && <DecisionCheckpoint taskId={taskId} info={info} onDone={onDone} />}
{cp.type === 'action' && <ActionCheckpoint taskId={taskId} info={info} onDone={onDone} />}
{activeCp.type === 'verify' && <VerifyCheckpoint taskId={taskId} cp={activeCp} onDone={onDone} />}
{activeCp.type === 'decision' && <DecisionCheckpoint taskId={taskId} cp={activeCp} onDone={onDone} />}
{activeCp.type === 'action' && <ActionCheckpoint taskId={taskId} cp={activeCp} onDone={onDone} />}
</div>
);
}