auto-sync: 2026-05-19 13:57:55

This commit is contained in:
cfdaily
2026-05-19 13:57:55 +08:00
parent c599d31270
commit fad494c4ea
+14 -43
View File
@@ -45,15 +45,9 @@ const AGENT_EMOJI: Record<string, string> = {
// ── Decision Checkpoint ──
function DecisionCheckpoint({
taskId,
info,
onDone,
}: {
taskId: string;
info: CheckpointInfo;
onDone: () => void;
}) {
const cp = info.checkpoint;
taskId, cp, onDone,
}: { taskId: string; cp: Checkpoint; onDone: () => void }) {
const payload: DecisionPayload = JSON.parse(cp.payload || '{}');
const [selected, setSelected] = useState<string | null>(null);
const [note, setNote] = useState('');
const [loading, setLoading] = useState(false);
@@ -63,12 +57,11 @@ function DecisionCheckpoint({
if (!selected) return;
setLoading(true);
try {
const r = await api.humanInputRespond(taskId, {
node_id: info.node_id,
action: 'decide',
selected_option: selected,
reason: note,
const res = await fetch(`/api/projects/${api._getProjectId()}/tasks/${taskId}/checkpoints/${cp.id}/approve`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ resolved_by: 'user', note: `选择: ${selected}${note ? '\n' + note : ''}` }),
});
const r = await res.json();
if (r.ok) { toast('✅ 已御批', 'ok'); onDone(); }
else toast(r.error || '操作失败', 'err');
} catch { toast('服务器连接失败', 'err'); }
@@ -77,27 +70,14 @@ function DecisionCheckpoint({
return (
<div>
<div style={{ fontSize: 13, color: 'var(--muted)', lineHeight: 1.6, marginBottom: 14, padding: 12, background: 'var(--panel2)', borderRadius: 8 }}>
{cp.description}
</div>
{/* 关联产出物 */}
{cp.artifacts?.length ? (
<div style={{ marginBottom: 14 }}>
<div style={{ fontSize: 11, color: 'var(--muted)', marginBottom: 4 }}>📦 </div>
<div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
{cp.artifacts.map((a, i) => (
<a key={i} href={api.artifactDownloadUrl(taskId, a.path)} target="_blank" rel="noreferrer"
style={{ fontSize: 10, padding: '2px 8px', borderRadius: 4, border: '1px solid var(--line)', color: 'var(--fg)', textDecoration: 'none' }}>
📄 {a.name}
</a>
))}
</div>
{cp.description && (
<div style={{ fontSize: 13, color: 'var(--muted)', lineHeight: 1.6, marginBottom: 14, padding: 12, background: 'var(--panel2)', borderRadius: 8 }}>
{cp.description}
</div>
) : null}
{/* 方案 */}
)}
<div style={{ fontSize: 12, fontWeight: 700, color: 'var(--muted)', marginBottom: 8 }}>📋 </div>
<div style={{ display: 'flex', gap: 8, marginBottom: 14, flexWrap: 'wrap' }}>
{cp.options?.map((opt) => (
{(payload.options || []).map((opt) => (
<div key={opt.id}
onClick={() => setSelected(opt.id)}
style={{
@@ -110,28 +90,19 @@ function DecisionCheckpoint({
<div style={{ float: 'right', background: '#22c55e', color: '#fff', fontSize: 10, fontWeight: 700, padding: '1px 6px', borderRadius: 3 }}></div>
)}
<div style={{ fontSize: 13, fontWeight: 700, marginBottom: 4 }}>{opt.label}</div>
{opt.description && <div style={{ fontSize: 11, color: 'var(--muted)', marginBottom: 6 }}>{opt.description}</div>}
{opt.desc && <div style={{ fontSize: 11, color: 'var(--muted)', marginBottom: 6 }}>{opt.desc}</div>}
{opt.pros?.map((p, i) => <div key={i} style={{ fontSize: 11, color: '#22c55e' }}>👍 {p}</div>)}
{opt.cons?.map((c, i) => <div key={i} style={{ fontSize: 11, color: '#ef4444' }}>👎 {c}</div>)}
</div>
))}
</div>
{/* 兜底自由输入 */}
{(!cp.options || cp.options.length === 0) && (
<div style={{ marginBottom: 14 }}>
<div style={{ fontSize: 11, color: 'var(--muted)', marginBottom: 4 }}></div>
<textarea style={{ width: '100%', height: 60, background: 'var(--panel2)', border: '1px solid var(--line)', borderRadius: 6, padding: 8, color: 'var(--fg)', fontSize: 12 }}
value={note} onChange={(e) => setNote(e.target.value)} placeholder="输入你的决策..." />
</div>
)}
{/* 御批备注 */}
<div style={{ marginBottom: 14 }}>
<div style={{ fontSize: 11, color: 'var(--muted)', marginBottom: 4 }}>📝 </div>
<textarea style={{ width: '100%', height: 48, background: 'var(--panel2)', border: '1px solid var(--line)', borderRadius: 6, padding: 8, color: 'var(--fg)', fontSize: 12 }}
value={note} onChange={(e) => setNote(e.target.value)} placeholder="记录决策理由..." />
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
<button className="btn-primary" disabled={!selected || loading} onClick={handleSubmit}>
<button disabled={!selected || loading} onClick={handleSubmit} style={{ padding: '6px 16px', borderRadius: 6, border: 'none', background: selected ? '#4a90d9' : '#555', color: '#fff', cursor: selected ? 'pointer' : 'not-allowed', fontSize: 13, fontWeight: 600 }}>
🎯
</button>
</div>