feat: polish agent workflow material and banner 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-21 12:41:47 +08:00
parent 21a915cb13
commit e2f4d5a3db
6 changed files with 52 additions and 17 deletions

View File

@ -11,8 +11,8 @@ import type {
} from "@/types/agent"; } from "@/types/agent";
interface AgentWorkflowMaterialPickerProps { interface AgentWorkflowMaterialPickerProps {
title: string; title?: string;
description: string; description?: string;
value: string; value: string;
queryMode: AgentWorkflowMaterialQueryType; queryMode: AgentWorkflowMaterialQueryType;
placeholder: string; placeholder: string;
@ -90,12 +90,18 @@ export function AgentWorkflowMaterialPicker({
isDark ? "border-white/12 bg-black/20" : "bg-muted/20" isDark ? "border-white/12 bg-black/20" : "bg-muted/20"
)} )}
> >
<div className="space-y-1"> {title || description ? (
<div className={cn("text-sm font-medium", isDark ? "text-white/92" : "")}>{title}</div> <div className="space-y-1">
<div className={cn("text-xs", isDark ? "text-white/62" : "text-muted-foreground")}> {title ? (
{description} <div className={cn("text-sm font-medium", isDark ? "text-white/92" : "")}>{title}</div>
) : null}
{description ? (
<div className={cn("text-xs", isDark ? "text-white/62" : "text-muted-foreground")}>
{description}
</div>
) : null}
</div> </div>
</div> ) : null}
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{(["sku", "spu"] as const).map((mode) => ( {(["sku", "spu"] as const).map((mode) => (

View File

@ -211,8 +211,6 @@ export function AgentWorkflowMaterialSelectionPanel({
) : null} ) : null}
<AgentWorkflowMaterialPicker <AgentWorkflowMaterialPicker
title="主 SKU / SPU"
description="主商品图会优先自动填充到“产品图1”;如果没有搭配图,再继续补到“产品图2”。"
value={inputs.primarySku} value={inputs.primarySku}
queryMode={materialSearchMode.primary} queryMode={materialSearchMode.primary}
placeholder={materialSearchMode.primary === "spu" ? "输入主 SPU" : "输入主 SKU"} placeholder={materialSearchMode.primary === "spu" ? "输入主 SPU" : "输入主 SKU"}
@ -281,8 +279,6 @@ export function AgentWorkflowMaterialSelectionPanel({
{isSecondaryMaterialVisible ? ( {isSecondaryMaterialVisible ? (
<AgentWorkflowMaterialPicker <AgentWorkflowMaterialPicker
title="搭配 SKU"
description="搭配商品图会优先自动填充到“产品图2”;品牌模特图会在上方单独选择并进入模特图槽位。"
value={inputs.secondarySku} value={inputs.secondarySku}
queryMode={materialSearchMode.secondary} queryMode={materialSearchMode.secondary}
placeholder={materialSearchMode.secondary === "spu" ? "输入搭配 SPU" : "输入搭配 SKU"} placeholder={materialSearchMode.secondary === "spu" ? "输入搭配 SPU" : "输入搭配 SKU"}

View File

@ -385,7 +385,7 @@ export function AgentWorkflowPanelDarkV2({
const workflowContent = ( const workflowContent = (
<div className="space-y-8"> <div className="space-y-8">
{/* 基础配置 */} {/* 品牌和店铺选择 */}
<motion.section <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)]" 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 }} initial={{ opacity: 0, y: 20 }}
@ -396,7 +396,7 @@ export function AgentWorkflowPanelDarkV2({
<span className="flex h-6 w-6 items-center justify-center rounded-md bg-emerald-500/20 text-xs font-bold text-emerald-400"> <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 1
</span> </span>
基础配置 品牌和店铺选择
</h2> </h2>
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 max-w-2xl"> <div className="grid grid-cols-1 gap-4 md:grid-cols-2 max-w-2xl">
<SearchableSelect <SearchableSelect

View File

@ -4,11 +4,26 @@ import { cn } from "@/lib/utils";
type AgentWorkflowParticleScatterProps = { type AgentWorkflowParticleScatterProps = {
className?: string; className?: string;
particleSize?: number;
particleCount?: number;
}; };
export function AgentWorkflowParticleScatter({ export function AgentWorkflowParticleScatter({
className, className,
particleSize,
particleCount,
}: AgentWorkflowParticleScatterProps) { }: AgentWorkflowParticleScatterProps) {
const searchParams = new URLSearchParams();
if (typeof particleSize === "number" && Number.isFinite(particleSize) && particleSize > 0) {
searchParams.set("particleSize", String(particleSize));
}
if (typeof particleCount === "number" && Number.isFinite(particleCount) && particleCount > 0) {
searchParams.set("particleCount", String(Math.round(particleCount)));
}
const src = searchParams.size
? `/particle-scatter-effect.html?${searchParams.toString()}`
: "/particle-scatter-effect.html";
return ( return (
<div <div
className={cn( className={cn(
@ -18,9 +33,9 @@ export function AgentWorkflowParticleScatter({
> >
<iframe <iframe
aria-hidden="true" aria-hidden="true"
className="h-full w-full border-0 bg-transparent [mask-image:linear-gradient(to_top,black_72%,transparent_100%)]" className="h-full w-full border-0 bg-transparent"
loading="eager" loading="eager"
src="/particle-scatter-effect.html" src={src}
tabIndex={-1} tabIndex={-1}
title="Particle Scatter Background" title="Particle Scatter Background"
/> />

View File

@ -44,7 +44,13 @@ export function AgentWorkflowShowcaseBanner({
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }} transition={{ duration: 0.6 }}
> >
<AgentWorkflowParticleScatter className="top-0 h-full opacity-20" /> <AgentWorkflowParticleScatter
className="top-0 h-full"
particleSize={110}
particleCount={76000}
/>
<div className="pointer-events-none absolute inset-0 bg-black/80" />
<div className="pointer-events-none absolute inset-x-0 top-0 h-[24%] bg-gradient-to-b from-black via-black/72 to-transparent" />
<div className="pointer-events-none absolute inset-x-0 bottom-0 h-[34%] bg-gradient-to-t from-black via-black/28 to-transparent" /> <div className="pointer-events-none absolute inset-x-0 bottom-0 h-[34%] bg-gradient-to-t from-black via-black/28 to-transparent" />
<div className="relative z-10"> <div className="relative z-10">

View File

@ -25,6 +25,17 @@
interactionMode: "auto", interactionMode: "auto",
manualControlTarget: "camera" manualControlTarget: "camera"
}; };
try {
const urlParams = new URLSearchParams(window.location.search);
const particleSizeOverride = Number(urlParams.get("particleSize"));
const particleCountOverride = Number(urlParams.get("particleCount"));
if (Number.isFinite(particleSizeOverride) && particleSizeOverride > 0) {
CONFIG.particleSize = particleSizeOverride;
}
if (Number.isFinite(particleCountOverride) && particleCountOverride > 0) {
CONFIG.particleCount = Math.floor(particleCountOverride);
}
} catch (error) {}
let uploadedImage = CONFIG.uploadedImage; let uploadedImage = CONFIG.uploadedImage;
let particleSize = CONFIG.particleSize; let particleSize = CONFIG.particleSize;
const PARTICLE_COUNT = CONFIG.particleCount; const PARTICLE_COUNT = CONFIG.particleCount;
@ -153,6 +164,7 @@
if (!sceneData) return; if (!sceneData) return;
sceneData.camera.aspect = window.innerWidth / window.innerHeight; sceneData.camera.aspect = window.innerWidth / window.innerHeight;
sceneData.camera.updateProjectionMatrix(); sceneData.camera.updateProjectionMatrix();
sceneData.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
sceneData.renderer.setSize(window.innerWidth, window.innerHeight); sceneData.renderer.setSize(window.innerWidth, window.innerHeight);
}); });
renderer.domElement.addEventListener("mousedown", function(e) { renderer.domElement.addEventListener("mousedown", function(e) {