import { useState } from "react"; import { api } from "../api/client"; import type { ThemeProp } from "../types"; import styles from "./BuildPanel.module.css"; interface Props { creativeId: string; creativeStatus: string; selectedPlatforms: string[]; theme: Record; themeProps: ThemeProp[]; onBuildComplete: () => void; } export default function BuildPanel({ creativeId, creativeStatus, selectedPlatforms, theme, themeProps, onBuildComplete, }: Props) { const [building, setBuilding] = useState(false); const [buildId, setBuildId] = useState(null); const [buildError, setBuildError] = useState(""); const canBuild = creativeStatus === "assets_ready" || creativeStatus === "built"; async function handleBuild() { if (selectedPlatforms.length === 0) { setBuildError("请至少选择一个目标平台"); return; } setBuilding(true); setBuildError(""); setBuildId(null); try { // 合并 theme:用 themeProps 的 default 补全缺失值 const mergedTheme: Record = {}; for (const prop of themeProps) { mergedTheme[prop.key] = theme[prop.key] ?? prop.default; } const res = await api.triggerBuild(creativeId, { platforms: selectedPlatforms, theme: mergedTheme, }); const bid = res.data.id; setBuildId(bid); // 轮询构建状态 await pollBuildStatus(bid); onBuildComplete(); } catch (err: any) { setBuildError(err.message); } finally { setBuilding(false); } } async function pollBuildStatus(bid: string): Promise { return new Promise((resolve, reject) => { const interval = setInterval(async () => { try { const res = await api.getBuildStatus(bid); if (res.data.status === "completed") { clearInterval(interval); resolve(); } else if (res.data.status === "failed") { clearInterval(interval); reject(new Error(res.data.errorLog || "构建失败")); } } catch (err: any) { clearInterval(interval); reject(err); } }, 2000); // 超时 120 秒 setTimeout(() => { clearInterval(interval); reject(new Error("构建超时,请刷新查看状态")); }, 120_000); }); } return (
{!canBuild && (

请先上传素材后再构建

)} {buildError &&

构建失败:{buildError}

} {buildId && !building && !buildError && (

构建完成!请在下方下载产物。

)}
); }