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:
2026-06-04 01:12:51 +08:00
parent 6bb71fcc19
commit 7c6129555b
12 changed files with 637 additions and 105 deletions
@@ -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" ? "重新生成分镜图" : "生成图片"}>