Files
EPEEAIKit/art-agent/frontend/src/components/workbench/candidate-panel.tsx
Nostars Developer b12e55a776 交互原型大版本
2026-04-17 19:30:23 +08:00

111 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState } from "react";
import type { ImageAsset } from "@/lib/types";
import { getImageUrl } from "@/lib/api";
/**
* 候选评估面板Phase 1 占位)
*
* 对应 INFOLAYER.md 3.3.3 节:同轮生成的一组候选图的并排比较、
* 快速评分、标注差异、挑选定稿。
*/
interface CandidatePanelProps {
candidates: ImageAsset[];
onPick?: (id: string) => void;
onClose?: () => void;
}
export function CandidatePanel({ candidates, onPick, onClose }: CandidatePanelProps) {
const [ratings, setRatings] = useState<Record<string, number>>({});
const [notes, setNotes] = useState<Record<string, string>>({});
if (candidates.length === 0) return null;
return (
<div className="fixed inset-y-0 right-0 w-[min(680px,100vw)] z-40
bg-[var(--bg-secondary)]/95 backdrop-blur-xl
border-l border-[var(--border)] shadow-2xl
flex flex-col animate-in slide-in-from-right">
<div className="flex items-center justify-between px-5 py-3 border-b border-[var(--border)]">
<div className="flex items-center gap-2">
<h2 className="typo-h2"></h2>
<span className="phase-chip">Phase 1</span>
<span className="text-xs text-[var(--text-secondary)]">
{candidates.length}
</span>
</div>
<button
onClick={onClose}
className="w-7 h-7 rounded-lg flex items-center justify-center
text-[var(--text-secondary)] hover:text-[var(--text-primary)]
hover:bg-[var(--bg-tertiary)] cursor-pointer transition-all"
>
</button>
</div>
<div className="flex-1 overflow-y-auto p-5 space-y-4">
<div className="grid grid-cols-2 gap-4">
{candidates.map((c, i) => (
<div key={c.id} className="rounded-xl surface-2 overflow-hidden">
<div className="relative aspect-square bg-[var(--bg-tertiary)]">
{c.url ? (
<img src={getImageUrl(c.url)} alt={`候选 ${i + 1}`} className="w-full h-full object-cover" />
) : (
<div className="w-full h-full flex items-center justify-center text-xs text-[var(--text-secondary)]">
{i + 1}
</div>
)}
<div className="absolute top-2 left-2 px-2 py-0.5 rounded-md
bg-[var(--bg-primary)]/70 backdrop-blur-sm
text-[10px] font-mono text-[var(--text-primary)]">
#{i + 1}
</div>
</div>
<div className="p-2.5">
<div className="flex gap-0.5 mb-1.5">
{[1, 2, 3, 4, 5].map((n) => (
<button
key={n}
onClick={() =>
setRatings((prev) => ({ ...prev, [c.id]: prev[c.id] === n ? 0 : n }))
}
className={`text-base leading-none cursor-pointer transition-colors ${
(ratings[c.id] ?? 0) >= n ? "text-[var(--accent)]" : "text-[var(--border)]"
}`}
>
</button>
))}
</div>
<input
value={notes[c.id] ?? ""}
onChange={(e) => setNotes((prev) => ({ ...prev, [c.id]: e.target.value }))}
placeholder="点评(占位)"
className="w-full px-2 py-1 text-[11px] rounded-md bg-[var(--bg-primary)]
border border-[var(--border)] focus:border-[var(--accent)]/40 outline-none"
/>
<button
onClick={() => onPick?.(c.id)}
className="mt-1.5 w-full text-[11px] py-1 rounded-md
bg-[var(--accent)]/10 text-[var(--accent)] border border-[var(--accent)]/30
hover:bg-[var(--accent)]/20 cursor-pointer transition-all"
>
稿
</button>
</div>
</div>
))}
</div>
<div className="placeholder-card rounded-lg p-3 text-xs text-[var(--text-secondary)]">
<div className="typo-strong text-xs mb-1 text-[var(--text-primary)]"></div>
/ / Phase 2
Agent
</div>
</div>
</div>
);
}