交互原型大版本
This commit is contained in:
110
art-agent/frontend/src/components/workbench/candidate-panel.tsx
Normal file
110
art-agent/frontend/src/components/workbench/candidate-panel.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
"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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user