feat: move brand setup to first step
This commit is contained in:
parent
5c10380666
commit
2ae376bdfd
@ -68,7 +68,10 @@ import { AgentWorkflowMaterialSelectionPanel } from "@/components/agents/AgentWo
|
||||
import { AgentPresetPanelNew } from "@/components/agents/AgentPresetPanelNew";
|
||||
// 深色主题组件 V2 (新版布局)
|
||||
import { AgentPresetPanelDarkV2 } from "@/components/agents/AgentPresetPanelDarkV2";
|
||||
import { AgentWorkflowPanelDarkV2 } from "@/components/agents/AgentWorkflowPanelDarkV2";
|
||||
import {
|
||||
AgentWorkflowBrandStorePanel,
|
||||
AgentWorkflowPanelDarkV2,
|
||||
} from "@/components/agents/AgentWorkflowPanelDarkV2";
|
||||
import { AgentGenerationWorkspaceDarkV2 } from "@/components/agents/AgentGenerationWorkspaceDarkV2";
|
||||
import { AgentProjectListSheet } from "@/components/agents/AgentProjectListSheet";
|
||||
import { CanvasWorkspace } from "@/components/canvas";
|
||||
@ -1488,6 +1491,14 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
||||
});
|
||||
}}
|
||||
onClearPreset={() => setSelectedAgentPresetId(null)}
|
||||
brandStorePanel={
|
||||
<AgentWorkflowBrandStorePanel
|
||||
config={resolvedWorkflowUiConfig}
|
||||
inputs={workflowInputs}
|
||||
onInputsChange={setWorkflowInputs}
|
||||
theme="dark"
|
||||
/>
|
||||
}
|
||||
workflowStylePanel={
|
||||
<AgentWorkflowPanelDarkV2
|
||||
embedded
|
||||
@ -1498,6 +1509,7 @@ export default function CanvasEditorPage({ params }: PageProps) {
|
||||
onMaterialsChange={setWorkflowMaterials}
|
||||
selections={workflowSelections}
|
||||
onSelectionsChange={setWorkflowSelections}
|
||||
showBrandStoreSection={false}
|
||||
/>
|
||||
}
|
||||
// 嵌入的智能体选择面板
|
||||
|
||||
@ -68,6 +68,7 @@ interface AgentGenerationWorkspaceDarkV2Props {
|
||||
workflowContext?: AgentWorkflowComposerContext | null;
|
||||
onBack?: () => void;
|
||||
presetPanel?: ReactNode;
|
||||
brandStorePanel?: ReactNode;
|
||||
materialSelectionPanel?: ReactNode;
|
||||
brandModelPanel?: ReactNode;
|
||||
onClearPreset?: () => void;
|
||||
@ -351,6 +352,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
workflowContext = null,
|
||||
onBack,
|
||||
presetPanel,
|
||||
brandStorePanel,
|
||||
materialSelectionPanel,
|
||||
brandModelPanel,
|
||||
onClearPreset,
|
||||
@ -1900,7 +1902,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
<span className="text-sm font-semibold text-white">选择生图模式</span>
|
||||
</div>
|
||||
<p className="text-sm text-white/45">
|
||||
先确定本次使用的模式,后续的商品图、参考图和结果展示都会随模式切换。
|
||||
先确定本次使用的模式,并完成品牌和店铺选择,后续的商品图、品牌模特、参考图和结果展示都会随之联动。
|
||||
</p>
|
||||
{isReplacementPreset ? (
|
||||
<p className="text-xs text-amber-300/80">
|
||||
@ -1953,6 +1955,12 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
暂无可用的生图模式配置。
|
||||
</div>
|
||||
)}
|
||||
|
||||
{brandStorePanel ? (
|
||||
<div className="mt-4 border-t border-white/10 pt-4">
|
||||
{brandStorePanel}
|
||||
</div>
|
||||
) : null}
|
||||
</section>
|
||||
|
||||
{hasMaterialStep ? (
|
||||
@ -1982,7 +1990,7 @@ export function AgentGenerationWorkspaceDarkV2({
|
||||
<span className="text-sm font-semibold text-white">风格步骤卡片</span>
|
||||
</div>
|
||||
<p className="text-sm text-white/45">
|
||||
在同一页完成品牌、店铺、风格卡片、品牌模特和 Prompt 组合,不再跳转第二个页面。
|
||||
在同一页完成风格卡片、品牌模特和 Prompt 组合,不再跳转第二个页面。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@ -99,7 +99,7 @@ export function AgentPresetCardDark({
|
||||
whileHover={{ y: -3, scale: 1.01 }}
|
||||
whileTap={{ scale: 0.98 }}
|
||||
className={cn(
|
||||
"group relative cursor-pointer overflow-hidden rounded-2xl border p-2.5 transition-all duration-300",
|
||||
"group relative cursor-pointer overflow-hidden rounded-2xl border p-3 transition-all duration-300",
|
||||
selected
|
||||
? "border-emerald-400/40 bg-emerald-500/[0.08] shadow-[0_0_24px_rgba(16,185,129,0.14)]"
|
||||
: "border-white/10 bg-white/[0.03] hover:border-white/20 hover:bg-white/[0.06]"
|
||||
@ -142,29 +142,43 @@ export function AgentPresetCardDark({
|
||||
>
|
||||
<Copy className="h-3 w-3" />
|
||||
</button>
|
||||
) : null}
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="relative mx-auto h-[70px] w-[70px] overflow-hidden rounded-2xl border border-white/10 bg-black/30">
|
||||
{previewUrl ? (
|
||||
<img
|
||||
src={previewUrl}
|
||||
alt={preset.name}
|
||||
className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-105"
|
||||
/>
|
||||
) : (
|
||||
<div className={cn("flex h-full w-full items-center justify-center bg-gradient-to-br", typeInfo.gradient)}>
|
||||
<TypeIcon className="h-7 w-7 text-white" />
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="relative h-[76px] w-[76px] shrink-0 overflow-hidden rounded-2xl border border-white/10 bg-black/30">
|
||||
{previewUrl ? (
|
||||
<img
|
||||
src={previewUrl}
|
||||
alt={preset.name}
|
||||
className="h-full w-full object-cover transition-transform duration-500 group-hover:scale-105"
|
||||
/>
|
||||
) : (
|
||||
<div className={cn("flex h-full w-full items-center justify-center bg-gradient-to-br", typeInfo.gradient)}>
|
||||
<TypeIcon className="h-7 w-7 text-white" />
|
||||
</div>
|
||||
)}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/35 to-transparent" />
|
||||
<div className="absolute left-2 top-2 rounded-full border border-white/10 bg-black/45 px-2 py-0.5 text-[10px] text-white/78">
|
||||
{imageModeLabel || typeInfo.label}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="min-w-0 flex-1 pr-8">
|
||||
<div className="line-clamp-2 text-sm font-semibold leading-5 text-white">
|
||||
{preset.name}
|
||||
</div>
|
||||
<div className="mt-2 flex flex-col gap-1.5 text-[11px] text-white/50">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<User className="h-3.5 w-3.5 text-white/34" />
|
||||
<span className="truncate">贡献者:{ownerName}</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Clock className="h-3.5 w-3.5 text-white/34" />
|
||||
<span className="truncate">贡献时间:{publishedAt || "未记录"}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/35 to-transparent" />
|
||||
</div>
|
||||
|
||||
<div className="mt-2 space-y-1 text-center">
|
||||
<div className="line-clamp-2 min-h-[2.5rem] text-[12px] font-medium leading-5 text-white">
|
||||
{preset.name}
|
||||
</div>
|
||||
<div className="text-[10px] text-white/45">{imageModeLabel || typeInfo.label}</div>
|
||||
</div>
|
||||
|
||||
{selected ? (
|
||||
|
||||
@ -338,7 +338,7 @@ export function AgentPresetPanelDarkV2({
|
||||
) : null}
|
||||
|
||||
{/* 卡片网格 */}
|
||||
<div className="grid grid-cols-[repeat(auto-fill,minmax(96px,1fr))] gap-2.5">
|
||||
<div className="grid grid-cols-1 gap-3 xl:grid-cols-2">
|
||||
{isLoading ? (
|
||||
<div className="col-span-full flex items-center justify-center py-8">
|
||||
<Loader2 className="h-6 w-6 animate-spin text-violet-500" />
|
||||
|
||||
@ -58,6 +58,16 @@ interface AgentWorkflowPanelDarkV2Props {
|
||||
showcaseConfig?: AgentWorkflowShowcaseConfig | null;
|
||||
topNavRightSlot?: ReactNode;
|
||||
embedded?: boolean;
|
||||
showBrandStoreSection?: boolean;
|
||||
}
|
||||
|
||||
interface AgentWorkflowBrandStorePanelProps {
|
||||
config?: AgentWorkflowUiConfig | null;
|
||||
inputs: AgentWorkflowInputs;
|
||||
onInputsChange: (next: AgentWorkflowInputs) => void;
|
||||
theme?: "light" | "dark";
|
||||
title?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
function buildSelectionSummary(params: {
|
||||
@ -170,6 +180,77 @@ function StepOptionCard({
|
||||
);
|
||||
}
|
||||
|
||||
export function AgentWorkflowBrandStorePanel({
|
||||
config,
|
||||
inputs,
|
||||
onInputsChange,
|
||||
theme = "dark",
|
||||
title = "品牌和店铺选择",
|
||||
description = "先选择品牌和店铺,后续品牌模特与风格步骤会按这里的选择联动。",
|
||||
}: AgentWorkflowBrandStorePanelProps) {
|
||||
const brandOptions = useMemo(
|
||||
() => config?.inputOptions?.brands || defaultAgentWorkflowUiConfig.inputOptions?.brands || [],
|
||||
[config?.inputOptions?.brands]
|
||||
);
|
||||
const brandStoreEntries = useMemo(
|
||||
() => getAgentWorkflowBrandStoreEntries(config),
|
||||
[config]
|
||||
);
|
||||
const storeOptions = useMemo(
|
||||
() => getAgentWorkflowStoreOptionsByBrand({ config, brand: inputs.brand }),
|
||||
[config, inputs.brand]
|
||||
);
|
||||
const isDark = theme === "dark";
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"rounded-[24px] border p-4",
|
||||
isDark ? "border-white/12 bg-black/20" : "border-border bg-background"
|
||||
)}
|
||||
>
|
||||
<div className="mb-4 space-y-1">
|
||||
<div className={cn("text-sm font-semibold", isDark ? "text-white" : "text-foreground")}>
|
||||
{title}
|
||||
</div>
|
||||
<div className={cn("text-xs leading-5", isDark ? "text-white/48" : "text-muted-foreground")}>
|
||||
{description}
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid max-w-2xl grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<SearchableSelect
|
||||
value={inputs.brand}
|
||||
options={brandOptions}
|
||||
placeholder="选择品牌"
|
||||
searchPlaceholder="输入品牌名筛选"
|
||||
emptyText="暂无品牌可选"
|
||||
theme={theme}
|
||||
onValueChange={(value) =>
|
||||
onInputsChange({
|
||||
...inputs,
|
||||
brand: value,
|
||||
store:
|
||||
value.trim().toLowerCase() === inputs.brand.trim().toLowerCase()
|
||||
? inputs.store
|
||||
: "",
|
||||
})
|
||||
}
|
||||
/>
|
||||
<SearchableSelect
|
||||
value={inputs.store}
|
||||
options={storeOptions}
|
||||
placeholder="选择店铺"
|
||||
searchPlaceholder="输入店铺名筛选"
|
||||
emptyText={inputs.brand ? "当前品牌下暂无店铺可选" : "请先选择品牌"}
|
||||
theme={theme}
|
||||
disabled={!inputs.brand || (brandStoreEntries.length > 0 && storeOptions.length === 0)}
|
||||
onValueChange={(value) => onInputsChange({ ...inputs, store: value })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function AgentWorkflowPanelDarkV2({
|
||||
config,
|
||||
inputs,
|
||||
@ -186,20 +267,8 @@ export function AgentWorkflowPanelDarkV2({
|
||||
showcaseConfig,
|
||||
topNavRightSlot,
|
||||
embedded = false,
|
||||
showBrandStoreSection = true,
|
||||
}: AgentWorkflowPanelDarkV2Props) {
|
||||
const brandOptions = useMemo(
|
||||
() => config?.inputOptions?.brands || defaultAgentWorkflowUiConfig.inputOptions?.brands || [],
|
||||
[config?.inputOptions?.brands]
|
||||
);
|
||||
const brandStoreEntries = useMemo(
|
||||
() => getAgentWorkflowBrandStoreEntries(config),
|
||||
[config]
|
||||
);
|
||||
const storeOptions = useMemo(
|
||||
() => getAgentWorkflowStoreOptionsByBrand({ config, brand: inputs.brand }),
|
||||
[config, inputs.brand]
|
||||
);
|
||||
|
||||
const resolvedConfig = useMemo(() => {
|
||||
const source = config || defaultAgentWorkflowUiConfig;
|
||||
return {
|
||||
@ -385,54 +454,27 @@ export function AgentWorkflowPanelDarkV2({
|
||||
|
||||
const workflowContent = (
|
||||
<div className="space-y-8">
|
||||
{/* 品牌和店铺选择 */}
|
||||
<motion.section
|
||||
className="rounded-2xl border border-white/12 bg-white/[0.04] p-5 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.1 }}
|
||||
>
|
||||
<h2 className="mb-4 flex items-center gap-2 text-lg font-semibold text-white">
|
||||
<span className="flex h-6 w-6 items-center justify-center rounded-md bg-emerald-500/20 text-xs font-bold text-emerald-400">
|
||||
1
|
||||
</span>
|
||||
品牌和店铺选择
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 max-w-2xl">
|
||||
<SearchableSelect
|
||||
value={inputs.brand}
|
||||
options={brandOptions}
|
||||
placeholder="选择品牌"
|
||||
searchPlaceholder="输入品牌名筛选"
|
||||
emptyText="暂无品牌可选"
|
||||
{showBrandStoreSection ? (
|
||||
<motion.section
|
||||
className="rounded-2xl border border-white/12 bg-white/[0.04] p-5 shadow-[inset_0_1px_0_rgba(255,255,255,0.03)]"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.1 }}
|
||||
>
|
||||
<h2 className="mb-4 flex items-center gap-2 text-lg font-semibold text-white">
|
||||
<span className="flex h-6 w-6 items-center justify-center rounded-md bg-emerald-500/20 text-xs font-bold text-emerald-400">
|
||||
1
|
||||
</span>
|
||||
品牌和店铺选择
|
||||
</h2>
|
||||
<AgentWorkflowBrandStorePanel
|
||||
config={config}
|
||||
inputs={inputs}
|
||||
onInputsChange={onInputsChange}
|
||||
theme="dark"
|
||||
onValueChange={(value) =>
|
||||
onInputsChange({
|
||||
...inputs,
|
||||
brand: value,
|
||||
store:
|
||||
value.trim().toLowerCase() === inputs.brand.trim().toLowerCase()
|
||||
? inputs.store
|
||||
: "",
|
||||
})
|
||||
}
|
||||
/>
|
||||
<SearchableSelect
|
||||
value={inputs.store}
|
||||
options={storeOptions}
|
||||
placeholder="选择店铺"
|
||||
searchPlaceholder="输入店铺名筛选"
|
||||
emptyText={
|
||||
inputs.brand
|
||||
? "当前品牌下暂无店铺可选"
|
||||
: "请先选择品牌"
|
||||
}
|
||||
theme="dark"
|
||||
disabled={!inputs.brand || (brandStoreEntries.length > 0 && storeOptions.length === 0)}
|
||||
onValueChange={(value) => onInputsChange({ ...inputs, store: value })}
|
||||
/>
|
||||
</div>
|
||||
</motion.section>
|
||||
</motion.section>
|
||||
) : null}
|
||||
|
||||
{/* 工作流步骤 */}
|
||||
{embedded ? (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user