From 404bbbd1043c38acdd4355f1414e14d6e49fe5a6 Mon Sep 17 00:00:00 2001 From: ddCat <2695092045@qq.com> Date: Wed, 2 Sep 2026 12:00:42 +0800 Subject: [PATCH] =?UTF-8?q?feat(canvas):=20=E5=88=86=E9=95=9C=E5=88=B6?= =?UTF-8?q?=E4=BD=9C=20-=20=E6=94=AF=E6=8C=81=E5=9B=BE=E7=89=87=E5=BC=B9?= =?UTF-8?q?=E7=AA=97=E9=A2=84=E8=A7=88=E4=B8=8E=E9=95=9C=E5=A4=B4=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E9=A6=96=E5=B8=A7=E7=82=B9=E5=87=BB=E6=92=AD=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/workflow-production-workbench.tsx | 60 ++++++++++++++-- web/src/pages/projects/detail/workflow.css | 68 ++++++++++++++++++- 2 files changed, 121 insertions(+), 7 deletions(-) diff --git a/web/src/pages/projects/detail/workflow-production-workbench.tsx b/web/src/pages/projects/detail/workflow-production-workbench.tsx index c33d7a823..d0c771633 100644 --- a/web/src/pages/projects/detail/workflow-production-workbench.tsx +++ b/web/src/pages/projects/detail/workflow-production-workbench.tsx @@ -1,7 +1,7 @@ import { useEffect, useMemo, useRef, useState, type ReactNode } from "react"; import { useMutation, useQuery } from "@tanstack/react-query"; -import { App, Button, Empty, Form, Image, Input, InputNumber, Segmented, Select, Tag } from "antd"; -import { Box, ChevronDown, ChevronLeft, ChevronRight, Download, Film, Image as ImageIcon, Layers3, List, Play, Plus, RefreshCcw, Save, Search, SlidersHorizontal, Trash2, UsersRound, WandSparkles, X } from "lucide-react"; +import { App, Button, Empty, Form, Image, Input, InputNumber, Modal, Segmented, Select, Tag } from "antd"; +import { Box, ChevronDown, ChevronLeft, ChevronRight, Download, Film, Image as ImageIcon, Layers3, List, Maximize2, Play, Plus, RefreshCcw, Save, Search, SlidersHorizontal, Trash2, UsersRound, WandSparkles, X } from "lucide-react"; import { Link, useNavigate } from "react-router"; import { CanvasResourceMentionTextarea } from "@/components/canvas/canvas-resource-mention-textarea"; @@ -13,6 +13,7 @@ import { modelQuoteRequest } from "@/lib/model-pricing"; import { customShotTitle, formatShotOrdinal, normalizeDefaultShotTitle } from "@/lib/shot-label"; import { modelCompatibilityError, resolveCompatibleModel, resolveModelVideoBooleanOptions, type ModelRequirements } from "@/lib/model-selection"; import { formatVideoResolutionLabel } from "@/lib/video-generation-options"; +import { captureVideoPoster } from "@/lib/video-poster"; import { submitBackendGenerationTask } from "@/services/api/generation-task"; import { quoteLogicalModel } from "@/services/api/logical-models"; import { type GenerationTask } from "@/services/api/task-center"; @@ -85,6 +86,7 @@ export default function WorkflowProductionWorkbench(props: Props) { const [leftTab, setLeftTab] = useState<"assets" | "episodes" | "shots">("episodes"); const [previewTab, setPreviewTab] = useState<"latest" | "history">("latest"); const [previewArtifactId, setPreviewArtifactId] = useState(""); + const [imagePreviewArtifact, setImagePreviewArtifact] = useState(null); const [editorDirty, setEditorDirty] = useState(false); const [submittingShotIds, setSubmittingShotIds] = useState>(() => new Set()); const [taskClock, setTaskClock] = useState(() => Date.now()); @@ -242,6 +244,7 @@ export default function WorkflowProductionWorkbench(props: Props) { continuityNotes: revision?.continuityNotes || "", }); setPreviewArtifactId(""); + setImagePreviewArtifact(null); setEditorDirty(!revision || videoPrompt !== revision.videoPrompt); }, [effectiveConfig, form, generationCapability, initialModel, revision?.id, selectedShot?.id, shotAssetReferenceContext.mentionReferences]); @@ -567,7 +570,7 @@ export default function WorkflowProductionWorkbench(props: Props) { />
- {previewTab === "latest" ? : { setPreviewArtifactId(artifact.id); setPreviewTab("latest"); }} />} + {previewTab === "latest" ? : { setPreviewArtifactId(artifact.id); setPreviewTab("latest"); }} />}
当前产物
{newestArtifact ? `${formatDuration(selectedShot.durationMs)} · ${resolution}p · v${newestArtifact.version}` : "当前镜头还没有生成产物"}
setPreviewArtifactId(artifact.id)} compact /> @@ -575,6 +578,19 @@ export default function WorkflowProductionWorkbench(props: Props) {
+ setImagePreviewArtifact(null)} + styles={{ body: { padding: 0 } }} + > + {imagePreviewArtifact?.resourceId ? {imagePreviewArtifact.type : null} + + ); @@ -669,11 +685,43 @@ function BoundAssets({ detail, shotId, changing, onUnlink }: { detail: ProjectDe ); } -function LatestPreview({ artifact, emptyText }: { artifact?: ShotArtifact; emptyText: string }) { +function LatestPreview({ artifact, emptyText, onPreviewImage }: { artifact?: ShotArtifact; emptyText: string; onPreviewImage: (artifact: ShotArtifact) => void }) { if (!artifact?.resourceId) return

{emptyText}

; const src = resourceFileUrl(artifact.resourceId); - if (artifact.type === "video") return