fix: 修复多个运行时崩溃和功能bug,优化画布连接线和剧本评分
- 修复 EcommercePage generateEcommerceImage 调用不存在变量导致运行时崩溃 - 修复 DigitalHumanPage/ImageWorkbenchPage 变量名错误导致页面不可用 - 修复 ecommerceVideoService token 读取用错 key 导致请求 401 - 修复画布连接线在弹窗出现后仍跟随鼠标的问题 - 剧本评分 .docx 文件改为服务端 mammoth 解析(新增 /api/files/extract-text) - ErrorBoundary 加 key 支持切换页面时自动重置 - Vite proxy 改为指向公网域名 omniai.net.cn - 新增视频生成历史记录面板和删除确认弹窗 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -4,13 +4,13 @@ import {
|
||||
CopyOutlined,
|
||||
DownloadOutlined,
|
||||
FolderAddOutlined,
|
||||
HistoryOutlined,
|
||||
LoadingOutlined,
|
||||
PlayCircleOutlined,
|
||||
ReloadOutlined,
|
||||
SendOutlined,
|
||||
StopOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { runVideoPlan, renderSceneImage, renderScene, buildSceneTasks } from "./ecommerceVideoService";
|
||||
import { runVideoPlan, renderSceneImage, renderScene, buildSceneTasks, saveVideoHistory } from "./ecommerceVideoService";
|
||||
import {
|
||||
PLAN_STEP_LABELS,
|
||||
PLAN_STEPS_DISPLAY,
|
||||
@@ -40,6 +40,8 @@ interface EcommerceVideoWorkspaceProps {
|
||||
durationSeconds: number;
|
||||
resolution: string;
|
||||
onRequestLogin?: () => void;
|
||||
onOpenHistory?: () => void;
|
||||
triggerPlan?: number;
|
||||
}
|
||||
|
||||
const ALL_STEPS: PlanStep[] = [
|
||||
@@ -101,6 +103,8 @@ export default function EcommerceVideoWorkspace({
|
||||
durationSeconds,
|
||||
resolution,
|
||||
onRequestLogin,
|
||||
onOpenHistory,
|
||||
triggerPlan,
|
||||
}: EcommerceVideoWorkspaceProps) {
|
||||
const [stage, setStage] = useState<EcommerceVideoStage>("idle");
|
||||
const [planResult, setPlanResult] = useState<EcommerceVideoPlanResult | null>(null);
|
||||
@@ -160,6 +164,32 @@ export default function EcommerceVideoWorkspace({
|
||||
}
|
||||
}, [stage, scenes, planResult]);
|
||||
|
||||
// ── External trigger: start plan from parent ────────────────
|
||||
const triggerPlanPrevRef = useRef(triggerPlan);
|
||||
useEffect(() => {
|
||||
if (triggerPlan != null && triggerPlan !== triggerPlanPrevRef.current) {
|
||||
triggerPlanPrevRef.current = triggerPlan;
|
||||
void handlePlan();
|
||||
}
|
||||
}, [triggerPlan]);
|
||||
|
||||
// ── Auto-save: persist completed results to server ──────────
|
||||
const historySavedRef = useRef(false);
|
||||
useEffect(() => {
|
||||
if (stage !== "completed") { historySavedRef.current = false; return; }
|
||||
if (historySavedRef.current) return;
|
||||
if (!planResult || !scenes.length) return;
|
||||
historySavedRef.current = true;
|
||||
const title = planResult.storyboard?.video_title || planResult.summary?.product_name || "电商广告视频";
|
||||
saveVideoHistory({
|
||||
title,
|
||||
config: { platform, aspectRatio, durationSeconds, resolution },
|
||||
plan: planResult as unknown as Record<string, unknown>,
|
||||
scenes: scenes.map((s) => ({ sceneId: s.sceneId, prompt: s.prompt, imageUrl: s.imageUrl, videoUrl: s.resultUrl })),
|
||||
sourceImageUrls,
|
||||
}).catch(() => {});
|
||||
}, [stage, planResult, scenes, sourceImageUrls, platform, aspectRatio, durationSeconds, resolution]);
|
||||
|
||||
// ── Keep-alive: resume polling for running tasks ──────────
|
||||
useEffect(() => {
|
||||
if (keepalivePollingStartedRef.current) return;
|
||||
@@ -568,6 +598,11 @@ export default function EcommerceVideoWorkspace({
|
||||
</div>
|
||||
|
||||
<div className="ecom-video-flowbar__actions">
|
||||
{onOpenHistory ? (
|
||||
<button type="button" className="ecom-video-flow-action ecom-video-flow-action--ghost" onClick={onOpenHistory} title="生成记录">
|
||||
<HistoryOutlined />
|
||||
</button>
|
||||
) : null}
|
||||
{error ? <span className="ecom-video-flowbar__error" role="alert">{error}</span> : null}
|
||||
{stage === "idle" && planProgress && (planProgress.summary || planProgress.creatives || planProgress.storyboard) ? (
|
||||
<button type="button" className="ecom-video-flow-action ecom-video-flow-action--ghost"
|
||||
@@ -575,12 +610,6 @@ export default function EcommerceVideoWorkspace({
|
||||
<ReloadOutlined /> 继续
|
||||
</button>
|
||||
) : null}
|
||||
{stage !== "planning" && stage !== "imaging" && stage !== "rendering" ? (
|
||||
<button type="button" className="ecom-video-flow-action"
|
||||
onClick={() => void handlePlan()} title={planProgress ? "从头重新策划" : "一键策划"}>
|
||||
<PlayCircleOutlined />
|
||||
</button>
|
||||
) : null}
|
||||
{stage === "planned" || stage === "imaged" ? (
|
||||
<button type="button" className="ecom-video-flow-action ecom-video-flow-action--ghost"
|
||||
onClick={() => void handleGenerateImages()} title={stage === "imaged" ? "重新生成分镜图" : "生成图片"}>
|
||||
|
||||
Reference in New Issue
Block a user