fix(ecommerce): video 400 error — use OSS URLs instead of data URLs for video generation

The renderScene function was passing local data URLs (data:image/png;base64,...)
as imageUrl and referenceUrls to createVideoTask, which the /api/ai/video endpoint
rejects with 400 Bad Request. The planning phase already uploads images to OSS
but the resulting URLs were not returned to the component.

- Add imageUrls field to EcommerceVideoPlanResult
- Return OSS imageUrls from runVideoPlan alongside existing plan data
- Use planResult.imageUrls[0] in handleRender instead of productImageDataUrls[0]
- Use planResult?.imageUrls[0] for sourceImage display fallback

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-02 19:37:29 +08:00
parent e5e5af5b54
commit 5fcd225825
7 changed files with 82 additions and 52 deletions
+43 -9
View File
@@ -6,6 +6,7 @@ interface PageTransitionProps {
}
const EXIT_DURATION_MS = 180;
const ENTER_DURATION_MS = 220;
const NAV_ORDER: string[] = [
"home",
@@ -39,8 +40,8 @@ function getNavIndex(key: string): number {
export default function PageTransition({ viewKey, children }: PageTransitionProps) {
const [displayedChildren, setDisplayedChildren] = useState(children);
const [phase, setPhase] = useState<"idle" | "exit">("idle");
const [direction, setDirection] = useState<"forward" | "backward" | "neutral">("neutral");
const [phase, setPhase] = useState<"idle" | "exit" | "enter">("idle");
const [exitDirection, setExitDirection] = useState<"forward" | "backward" | "neutral">("neutral");
const prevKeyRef = useRef(viewKey);
const timerRef = useRef<ReturnType<typeof setTimeout>>();
@@ -49,29 +50,62 @@ export default function PageTransition({ viewKey, children }: PageTransitionProp
setDisplayedChildren(children);
return;
}
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (prefersReducedMotion) {
prevKeyRef.current = viewKey;
setDisplayedChildren(children);
setPhase("idle");
return;
}
const prevIndex = getNavIndex(prevKeyRef.current);
const nextIndex = getNavIndex(viewKey);
if (prevIndex < nextIndex) {
setDirection("forward");
setExitDirection("forward");
} else if (prevIndex > nextIndex) {
setDirection("backward");
setExitDirection("backward");
} else {
setDirection("neutral");
setExitDirection("neutral");
}
prevKeyRef.current = viewKey;
setPhase("exit");
timerRef.current = setTimeout(() => {
setDisplayedChildren(children);
setPhase("idle");
setPhase("enter");
}, EXIT_DURATION_MS);
return () => clearTimeout(timerRef.current);
}, [viewKey, children]);
const dirClass = direction === "forward" ? " is-forward" : direction === "backward" ? " is-backward" : "";
// After enter animation completes, go back to idle
useEffect(() => {
if (phase !== "enter") return;
const timer = setTimeout(() => setPhase("idle"), ENTER_DURATION_MS);
return () => clearTimeout(timer);
}, [phase]);
const dirClass = exitDirection === "forward" ? " is-forward" : exitDirection === "backward" ? " is-backward" : "";
if (phase === "exit") {
return (
<div className={`page-transition-wrap page-motion--exit${dirClass}`}>
{displayedChildren}
</div>
);
}
if (phase === "enter") {
return (
<div className="page-transition-wrap page-motion--enter">
{displayedChildren}
</div>
);
}
return (
<div className={phase === "exit" ? `page-transition-wrap page-motion--exit${dirClass}` : `page-transition-wrap${phase === "idle" && direction !== "neutral" ? ` page-motion--enter${dirClass}` : ""}`}>
<div className="page-transition-wrap">
{displayedChildren}
</div>
);
}
}