feat: refine agent generation workflow ui
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-03-20 17:55:49 +08:00
parent 8d2066a3bf
commit c754383ffc
3 changed files with 72 additions and 77 deletions

View File

@ -298,6 +298,8 @@ export function AgentGenerationWorkspaceDarkV2({
onResultPreviewUrlsChange,
topNavRightSlot,
}: AgentGenerationWorkspaceDarkV2Props) {
const darkSelectContentClass =
"border-white/10 bg-zinc-950 text-white shadow-2xl [--popover:hsl(240_10%_3.9%)] [--popover-foreground:hsl(0_0%_98%)] [--accent:rgba(255,255,255,0.08)] [--accent-foreground:hsl(0_0%_98%)] [--muted:rgba(255,255,255,0.04)] [--muted-foreground:rgba(255,255,255,0.56)]";
const rawChatId = useMemo(
() => (chatId.startsWith("canvas-") ? chatId.replace("canvas-", "") : chatId),
[chatId]
@ -769,6 +771,10 @@ export function AgentGenerationWorkspaceDarkV2({
(image) => image.source === "primary" || image.source === "secondary"
);
const hasSatisfiedSlotRequirement = !hasAgentUploadSlots || attachmentCount >= requiredUploadCount;
const mergedSettingsStepLabel = hasMaterialStep ? "选图与生成" : "生成参数";
const mergedSettingsStepDescription = hasMaterialStep
? "搜索素材、补充要求、确认槽位并开始生成"
: "补充要求并开始生成";
const toolSteps = useMemo(
() => [
@ -794,35 +800,15 @@ export function AgentGenerationWorkspaceDarkV2({
},
]
: []),
...(hasMaterialStep
? [
{
key: "materials" as const,
label: "SKU / SPU 选图",
description: "搜索主图和搭配图",
icon: Upload,
disabled: !selectedAgentPresetId,
completed: hasMaterialSelections,
optional: true,
},
]
: []),
{
key: "prompt" as const,
label: "补充要求",
description: "补充风格、构图和细节要求",
icon: Sparkles,
disabled: !selectedAgentPresetId,
completed: Boolean(input.trim()),
optional: true,
},
{
key: "settings" as const,
label: "生成参数",
description: "选择模型并开始生成",
label: mergedSettingsStepLabel,
description: mergedSettingsStepDescription,
icon: Settings2,
disabled: !selectedAgentPresetId,
completed: Boolean(selectedAgentPresetId),
completed:
Boolean(selectedAgentPresetId) &&
(Boolean(input.trim()) || hasMaterialSelections || hasSatisfiedSlotRequirement),
optional: false,
},
],
@ -830,8 +816,10 @@ export function AgentGenerationWorkspaceDarkV2({
hasBrandModelSelection,
hasBrandModelStep,
hasMaterialSelections,
hasMaterialStep,
hasSatisfiedSlotRequirement,
mergedSettingsStepDescription,
mergedSettingsStepLabel,
hasMaterialStep,
input,
selectedAgentPresetId,
]
@ -943,28 +931,27 @@ export function AgentGenerationWorkspaceDarkV2({
}
setActiveToolStep((current) => {
if (current !== "preset") return current;
if (isDetailReplacementPreset || isSkuReplacementPreset) {
if (hasMaterialStep) return "materials";
return "prompt";
if (
current !== "preset" &&
current !== "materials" &&
current !== "prompt"
) {
return current;
}
if (hasBrandModelStep) return "brand-model";
if (hasMaterialStep) return "materials";
return "prompt";
return "settings";
});
}, [
hasBrandModelStep,
hasMaterialStep,
isDetailReplacementPreset,
isSkuReplacementPreset,
selectedAgentPresetId,
]);
useEffect(() => {
if (!toolSteps.some((step) => step.key === activeToolStep)) {
setActiveToolStep("preset");
setActiveToolStep(selectedAgentPresetId ? "settings" : "preset");
}
}, [activeToolStep, toolSteps]);
}, [activeToolStep, selectedAgentPresetId, toolSteps]);
useEffect(() => {
if (!syncGeneratedResultsToCanvas) {
@ -1581,7 +1568,7 @@ export function AgentGenerationWorkspaceDarkV2({
</div>
{assignableSourceLibrary.length > 0 ? (
<div className="grid grid-cols-2 gap-3 xl:grid-cols-4">
<div className="grid grid-cols-1 gap-3 md:grid-cols-3">
{assignableSourceLibrary.map((source) => {
const assignedToActiveSlot =
activeAssignmentSlotIndex !== null &&
@ -1613,7 +1600,7 @@ export function AgentGenerationWorkspaceDarkV2({
{source.sourceLabel}
</div>
</div>
<div className="space-y-1 px-3 py-2">
<div className="space-y-1 border-t border-white/10 px-3 py-2">
<div className="truncate text-xs font-medium text-white/88">
{source.label}
</div>
@ -1629,7 +1616,7 @@ export function AgentGenerationWorkspaceDarkV2({
<div className="flex min-h-[92px] items-center justify-center rounded-[20px] border border-dashed border-white/10 bg-white/[0.02] px-4 text-center text-xs text-white/40">
{activeAssignmentIsModelSlot
? "当前模特槽位还没有可分配的品牌模特图,请先在“品牌模特”步骤里选择。"
: "当前产品槽位还没有来自已选素材区的图片,请先到“SKU / SPU 选图”里完成选择。"}
: "当前产品槽位还没有来自已选素材区的图片,请先在当前页上方完成 SKU / SPU 选图。"}
</div>
)}
</div>
@ -1733,29 +1720,21 @@ export function AgentGenerationWorkspaceDarkV2({
</div>
) : null}
{hasMaterialStep ? (
<div className={cn(activeToolStep === "materials" ? "block" : "hidden")}>
{materialSelectionPanel}
</div>
) : null}
<div className={cn(activeToolStep === "prompt" ? "block" : "hidden")}>
<div className="rounded-[28px] border border-white/12 bg-white/[0.02] p-4">
<h3 className="mb-3 flex items-center gap-2 text-sm font-semibold text-white">
<Sparkles className="h-4 w-4 text-emerald-300" />
补充要求
</h3>
<Textarea
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="描述你想要的风格、氛围、构图..."
className="min-h-[160px] resize-none rounded-[24px] border-white/12 bg-black text-sm text-white placeholder:text-white/20 focus:border-emerald-400/30"
/>
</div>
</div>
<div className={cn(activeToolStep === "settings" ? "block" : "hidden")}>
<div className="space-y-4">
{hasMaterialStep ? materialSelectionPanel : null}
<div className="rounded-[28px] border border-white/12 bg-white/[0.02] p-4">
<h3 className="mb-3 flex items-center gap-2 text-sm font-semibold text-white">
<Sparkles className="h-4 w-4 text-emerald-300" />
补充要求
</h3>
<Textarea
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="描述你想要的风格、氛围、构图..."
className="min-h-[160px] resize-none rounded-[24px] border-white/12 bg-black text-sm text-white placeholder:text-white/20 focus:border-emerald-400/30"
/>
</div>
{attachmentSlotPanel}
<div className="rounded-[28px] border border-white/12 bg-white/[0.02] p-4">
<h3 className="mb-3 flex items-center gap-2 text-sm font-semibold text-white">
@ -1776,9 +1755,13 @@ export function AgentGenerationWorkspaceDarkV2({
<SelectTrigger className="h-10 rounded-[24px] border-white/12 bg-black text-white">
<SelectValue placeholder="选择模型" />
</SelectTrigger>
<SelectContent className="border-white/10 bg-zinc-900">
<SelectContent className={darkSelectContentClass}>
{selectableModels.map((model) => (
<SelectItem key={model.id} value={model.id} className="text-white">
<SelectItem
key={model.id}
value={model.id}
className="text-white focus:bg-white/10 focus:text-white"
>
{model.displayName}
</SelectItem>
))}
@ -1836,7 +1819,9 @@ export function AgentGenerationWorkspaceDarkV2({
<div className="mt-5 flex flex-wrap items-center justify-between gap-3 border-t border-white/10 pt-4">
<div className="text-xs text-white/40">
完成当前模块后,可点击下一步继续,也可以直接点击顶部流程线跳转。
{hasMaterialStep
? "当前页已合并 SKU / SPU 选图、补充要求、槽位确认和生成参数,可在这里直接完成后开始生成。"
: "当前页已集中补充要求与生成参数,确认后即可直接开始生成。"}
</div>
<div className="flex items-center gap-2">
<button
@ -1993,7 +1978,7 @@ export function AgentGenerationWorkspaceDarkV2({
))
) : (
<div className="flex min-h-[160px] flex-col items-center justify-center rounded-[22px] border border-dashed border-white/12 bg-black/20 px-4 text-center text-sm text-white/42">
请先在“生成参数”里的输入图槽位中确认用于详情替换的白底输入图。
请先在当前“选图与生成”页的输入图槽位中确认用于详情替换的白底输入图。
</div>
)}
@ -2300,7 +2285,7 @@ export function AgentGenerationWorkspaceDarkV2({
))
) : (
<div className="flex min-h-[160px] flex-col items-center justify-center rounded-[22px] border border-dashed border-white/12 bg-black/20 px-4 text-center text-sm text-white/42">
请先在“生成参数”里的输入图槽位中确认用于 SKU 替换的白底输入图。
请先在当前“选图与生成”页的输入图槽位中确认用于 SKU 替换的白底输入图。
</div>
)}
@ -2671,7 +2656,9 @@ export function AgentGenerationWorkspaceDarkV2({
</p>
<p className="mt-2 text-sm text-white/30">
{selectedAgentPresetId
? "顶部流程线配置完成后,切到“生成参数”即可开始生成"
? hasMaterialStep
? "在“选图与生成”页完成搜图、槽位确认和参数设置后,即可开始生成"
: "在当前页补充要求并确认参数后,即可开始生成"
: "请先从顶部流程线选择一个生图模式"}
</p>
</motion.div>

View File

@ -149,6 +149,9 @@ export function AgentPresetDialog({
const dialogTitle = initialPreset ? `编辑${entityLabel}` : `新建${entityLabel}`;
const dialogDescription = `配置${entityLabel}名称、预览图、模型和槽位提示词。`;
const submitLabel = initialPreset ? `保存${entityLabel}` : `创建${entityLabel}`;
const darkSelectContentClass = isDark
? "border-white/10 bg-zinc-950 text-white shadow-2xl [--popover:hsl(240_10%_3.9%)] [--popover-foreground:hsl(0_0%_98%)] [--accent:rgba(255,255,255,0.08)] [--accent-foreground:hsl(0_0%_98%)] [--muted:rgba(255,255,255,0.04)] [--muted-foreground:rgba(255,255,255,0.56)]"
: "";
const isStructuredImageModeBuilder = type === "image" && modeConfig.modeType !== "legacy";
const isSceneModeBuilder = type === "image" && modeConfig.modeType === "scene_generation";
const isDetailReplacementModeBuilder =
@ -716,7 +719,7 @@ export function AgentPresetDialog({
>
<SelectValue />
</SelectTrigger>
<SelectContent className={cn(isDark ? "border-white/10 bg-zinc-950 text-white" : "")}>
<SelectContent className={darkSelectContentClass}>
<SelectItem value="private" className={cn(isDark ? "text-white focus:bg-white/10 focus:text-white" : "")}>私有</SelectItem>
<SelectItem value="public" className={cn(isDark ? "text-white focus:bg-white/10 focus:text-white" : "")}>公开</SelectItem>
</SelectContent>
@ -744,7 +747,7 @@ export function AgentPresetDialog({
>
<SelectValue />
</SelectTrigger>
<SelectContent className={cn(isDark ? "border-white/10 bg-zinc-950 text-white" : "")}>
<SelectContent className={darkSelectContentClass}>
{imageModeSelectOptions.map((option) => (
<SelectItem
key={option.value}
@ -777,7 +780,7 @@ export function AgentPresetDialog({
>
<SelectValue />
</SelectTrigger>
<SelectContent className={cn(isDark ? "border-white/10 bg-zinc-950 text-white" : "")}>
<SelectContent className={darkSelectContentClass}>
<SelectItem value="image" className={cn(isDark ? "text-white focus:bg-white/10 focus:text-white" : "")}>生图</SelectItem>
<SelectItem value="video" className={cn(isDark ? "text-white focus:bg-white/10 focus:text-white" : "")}>视频</SelectItem>
<SelectItem value="text" className={cn(isDark ? "text-white focus:bg-white/10 focus:text-white" : "")}>生文</SelectItem>
@ -797,7 +800,7 @@ export function AgentPresetDialog({
>
<SelectValue placeholder="选择模型" />
</SelectTrigger>
<SelectContent className={cn(isDark ? "border-white/10 bg-zinc-950 text-white" : "")}>
<SelectContent className={darkSelectContentClass}>
{filteredModels.map((model) => (
<SelectItem
key={model.id}
@ -852,7 +855,7 @@ export function AgentPresetDialog({
>
<SelectValue placeholder="选择顶部分类页" />
</SelectTrigger>
<SelectContent className={cn(isDark ? "border-white/10 bg-zinc-950 text-white" : "")}>
<SelectContent className={darkSelectContentClass}>
<SelectItem value="__none__" className={cn(isDark ? "text-white focus:bg-white/10 focus:text-white" : "")}>
不分类
</SelectItem>
@ -891,7 +894,7 @@ export function AgentPresetDialog({
>
<SelectValue placeholder="选择一级分类" />
</SelectTrigger>
<SelectContent className={cn(isDark ? "border-white/10 bg-zinc-950 text-white" : "")}>
<SelectContent className={darkSelectContentClass}>
<SelectItem value="__none__" className={cn(isDark ? "text-white focus:bg-white/10 focus:text-white" : "")}>
不选择
</SelectItem>
@ -929,7 +932,7 @@ export function AgentPresetDialog({
>
<SelectValue placeholder="选择二级分类" />
</SelectTrigger>
<SelectContent className={cn(isDark ? "border-white/10 bg-zinc-950 text-white" : "")}>
<SelectContent className={darkSelectContentClass}>
<SelectItem value="__none__" className={cn(isDark ? "text-white focus:bg-white/10 focus:text-white" : "")}>
不选择
</SelectItem>
@ -965,7 +968,7 @@ export function AgentPresetDialog({
>
<SelectValue placeholder="选择三级分类" />
</SelectTrigger>
<SelectContent className={cn(isDark ? "border-white/10 bg-zinc-950 text-white" : "")}>
<SelectContent className={darkSelectContentClass}>
<SelectItem value="__none__" className={cn(isDark ? "text-white focus:bg-white/10 focus:text-white" : "")}>
不选择
</SelectItem>
@ -1055,7 +1058,7 @@ export function AgentPresetDialog({
>
<SelectValue />
</SelectTrigger>
<SelectContent className={cn(isDark ? "border-white/10 bg-zinc-950 text-white" : "")}>
<SelectContent className={darkSelectContentClass}>
{[1, 2, 3, 4].map((count) => (
<SelectItem
key={count}
@ -1192,7 +1195,7 @@ export function AgentPresetDialog({
>
<SelectValue />
</SelectTrigger>
<SelectContent className={cn(isDark ? "border-white/10 bg-zinc-950 text-white" : "")}>
<SelectContent className={darkSelectContentClass}>
{[1, 2, 3, 4].map((count) => (
<SelectItem
key={count}

View File

@ -164,6 +164,11 @@ export function AgentWorkflowBrandModelPicker({
type="button"
variant="outline"
size="sm"
className={cn(
isDark
? "border-white/14 bg-white/[0.03] text-white/82 hover:border-white/22 hover:bg-white/[0.08] hover:text-white disabled:border-white/8 disabled:bg-white/[0.02] disabled:text-white/28"
: ""
)}
onClick={() => onSelectBrandModel(null)}
disabled={!selectedBrandModel}
>