From 89a3a0cc3abd9d759fa2ce3a1e27ced9395890e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=97=E6=9E=B3?= <7854742+wang_rumeng@user.noreply.gitee.com> Date: Sun, 17 Aug 2025 14:05:07 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E6=B5=81=E6=A0=B7=E5=BC=8F=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/pages/style/work-flow.css | 7 +- components/pages/work-flow.tsx | 165 +++++++++--------- components/pages/work-flow/media-viewer.tsx | 4 +- components/pages/work-flow/thumbnail-grid.tsx | 2 +- 4 files changed, 89 insertions(+), 89 deletions(-) diff --git a/components/pages/style/work-flow.css b/components/pages/style/work-flow.css index de416bb..6f02097 100644 --- a/components/pages/style/work-flow.css +++ b/components/pages/style/work-flow.css @@ -159,6 +159,10 @@ display: flex; overflow: hidden; min-height: 0; + + display: grid; + grid-auto-rows: auto; + justify-content: center; } .videoContainer-qteKNi { flex: 1; @@ -166,13 +170,14 @@ display: flex; position: relative; justify-content: center; + + width: max-content; } .heroVideo-FIzuK1 { object-fit: cover; object-position: center; background-color: #0003; border-radius: 8px; - width: 100%; height: 100%; } .container-kIPoeH { diff --git a/components/pages/work-flow.tsx b/components/pages/work-flow.tsx index 4712df5..bfe7e94 100644 --- a/components/pages/work-flow.tsx +++ b/components/pages/work-flow.tsx @@ -21,7 +21,6 @@ const WorkFlow = React.memo(function WorkFlow() { console.log("init-WorkFlow"); return () => console.log("unmount-WorkFlow"); }, []); - const containerRef = useRef(null); const [isEditModalOpen, setIsEditModalOpen] = React.useState(false); const [activeEditTab, setActiveEditTab] = React.useState('1'); @@ -92,102 +91,98 @@ const WorkFlow = React.memo(function WorkFlow() { return (
-
-
-
-
-
- - - -
+
+
+
+
+ + +
-
-
- {dataLoadError ? ( +
+
+
+ {dataLoadError ? ( + - - -

数据加载失败

-
- -

- {dataLoadError} -

- - retryLoadData?.()} - whileHover={{ scale: 1.05 }} - whileTap={{ scale: 0.95 }} - > - - 重试加载 - + +

数据加载失败

- ) : isLoading ? ( - - ) : ( -
- - - -
- )} -
- {taskObject.currentStage !== 'final_video' && taskObject.currentStage !== 'script' && ( -
+ +

+ {dataLoadError} +

+ + retryLoadData?.()} + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + + 重试加载 + + + ) : isLoading ? ( + + ) : ( +
-
)} -
+ {taskObject.currentStage !== 'final_video' && taskObject.currentStage !== 'script' && ( +
+ +
+ )} +
diff --git a/components/pages/work-flow/media-viewer.tsx b/components/pages/work-flow/media-viewer.tsx index 27d4c89..b6aa3f0 100644 --- a/components/pages/work-flow/media-viewer.tsx +++ b/components/pages/work-flow/media-viewer.tsx @@ -137,7 +137,7 @@ export const MediaViewer = React.memo(function MediaViewer({ return (
-
+
{/* 智能预设词条 英文 */} {showSuggestions && !isCollapsed && ( @@ -179,7 +179,6 @@ export function AISuggestionBar({ if (isCollapsed) { toggleCollapse(); } - onFocus(); }} onBlur={() => setIsFocused(false)} placeholder={isCollapsed ? "点击展开..." : placeholder} diff --git a/components/pages/work-flow.tsx b/components/pages/work-flow.tsx index a4bc0f0..991adbf 100644 --- a/components/pages/work-flow.tsx +++ b/components/pages/work-flow.tsx @@ -168,7 +168,7 @@ const WorkFlow = React.memo(function WorkFlow() { {taskObject.currentStage !== 'final_video' && taskObject.currentStage !== 'script' && (
setIsPauseWorkFlow(true)} + onClick={() => setIsPauseWorkFlow(true)} placeholder="Please input your ideas, or click the predefined tags to receive AI advice..." /> diff --git a/components/pages/work-flow/thumbnail-grid.tsx b/components/pages/work-flow/thumbnail-grid.tsx index f284d5f..edba93e 100644 --- a/components/pages/work-flow/thumbnail-grid.tsx +++ b/components/pages/work-flow/thumbnail-grid.tsx @@ -8,7 +8,7 @@ import { Loader2, X } from 'lucide-react'; import { TaskObject } from '@/api/DTO/movieEdit'; interface ThumbnailGridProps { - isEditModalOpen: boolean; + isDisabledFocus: boolean; taskObject: TaskObject; isLoading: boolean; currentSketchIndex: number; @@ -22,7 +22,7 @@ interface ThumbnailGridProps { } export function ThumbnailGrid({ - isEditModalOpen, + isDisabledFocus, taskObject, isLoading, currentSketchIndex, @@ -124,7 +124,7 @@ export function ThumbnailGrid({ // 监听键盘事件 useEffect(() => { // 组件挂载时自动聚焦 - if (thumbnailsRef.current && !isEditModalOpen) { + if (thumbnailsRef.current && !isDisabledFocus) { thumbnailsRef.current.focus(); } @@ -134,7 +134,7 @@ export function ThumbnailGrid({ // 确保在数据变化时保持焦点 useEffect(() => { - if (thumbnailsRef.current && !isFocused && !isEditModalOpen) { + if (thumbnailsRef.current && !isFocused && !isDisabledFocus) { thumbnailsRef.current.focus(); } }, [taskObject.currentStage, isFocused]); From ab3eecc3ea1072a8d9bb83e642f815be14be5031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=97=E6=9E=B3?= <7854742+wang_rumeng@user.noreply.gitee.com> Date: Sun, 17 Aug 2025 19:44:30 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E6=8D=A2=E5=88=9B=E5=BB=BA=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/video_flow.ts | 2 ++ components/common/ChatInputBox.tsx | 3 ++- components/pages/work-flow/thumbnail-grid.tsx | 4 ---- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/api/video_flow.ts b/api/video_flow.ts index 864b443..218f41e 100644 --- a/api/video_flow.ts +++ b/api/video_flow.ts @@ -738,6 +738,8 @@ export const createMovieProjectV1 = async (request: { resolution: "720p" | "1080p" | "4k"; /** 语言 */ language: string; + /** 视频时长 */ + video_duration: string; }) => { return post
- Generating...
)} @@ -275,7 +274,6 @@ export function ThumbnailGrid({
- Failed
)} @@ -329,7 +327,6 @@ export function ThumbnailGrid({
- Generating...
)} @@ -337,7 +334,6 @@ export function ThumbnailGrid({
- Failed
)} From ea7e336d1e18e1ddd104354bb6bbf5e50981eb48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=97=E6=9E=B3?= <7854742+wang_rumeng@user.noreply.gitee.com> Date: Sun, 17 Aug 2025 20:17:47 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/service/Interaction/ScriptService.ts | 9 ++++++--- app/service/adapter/textToShot.ts | 4 ++-- app/service/test/Script.test.ts | 3 ++- app/service/usecase/ScriptEditUseCase.ts | 4 +++- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/service/Interaction/ScriptService.ts b/app/service/Interaction/ScriptService.ts index c04d3e7..0fdc306 100644 --- a/app/service/Interaction/ScriptService.ts +++ b/app/service/Interaction/ScriptService.ts @@ -85,7 +85,8 @@ export interface UseScriptService { userId: string, mode: "automatic" | "manual", resolution: string, - language: string + language: string, + video_duration: string ) => Promise; /** 设置任何属性 */ setAnyAttribute: any; @@ -158,7 +159,8 @@ export const useScriptService = (): UseScriptService => { userId: string, mode: "automatic" | "manual", resolution: string, - language: string + language: string, + video_duration: string ): Promise => { try { setLoading(true); @@ -169,7 +171,8 @@ export const useScriptService = (): UseScriptService => { userId, mode as "automatic" | "manual", resolution as "720p" | "1080p" | "4k", - language + language, + video_duration ); setProjectId(projectData.project_id); diff --git a/app/service/adapter/textToShot.ts b/app/service/adapter/textToShot.ts index 5dd57ca..81a2c71 100644 --- a/app/service/adapter/textToShot.ts +++ b/app/service/adapter/textToShot.ts @@ -60,10 +60,10 @@ export class TextToShotAdapter { let currentText = text; // 按角色名称长度降序排序,避免短名称匹配到长名称的一部分 - // 既要兼容 首字母大写 其余小写、还要兼容 全部大写 + // 既要兼容 每个单词 首字母大写 其余小写、还要兼容 全部大写 const sortedRoles = [...roles].sort((a, b) => b.name.length - a.name.length).map(role => ({ ...role, - name: role.name.charAt(0).toUpperCase() + role.name.slice(1).toLowerCase() + name: role.name.split(' ').map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(' ') })).concat([...roles].map(role => ({ ...role, name: role.name.toUpperCase() diff --git a/app/service/test/Script.test.ts b/app/service/test/Script.test.ts index 7bf3e22..81b0bbe 100644 --- a/app/service/test/Script.test.ts +++ b/app/service/test/Script.test.ts @@ -40,7 +40,8 @@ describe("ScriptService 业务逻辑测试", () => { "user123", "automatic", "720p", - "en" + "en", + "10" ); expect(createRes.project_id).toBeDefined(); projectId = createRes.project_id; diff --git a/app/service/usecase/ScriptEditUseCase.ts b/app/service/usecase/ScriptEditUseCase.ts index dc5b5b0..76877b7 100644 --- a/app/service/usecase/ScriptEditUseCase.ts +++ b/app/service/usecase/ScriptEditUseCase.ts @@ -126,7 +126,8 @@ export class ScriptEditUseCase { userId: string , mode: "automatic" | "manual" = "automatic", resolution: "720p" | "1080p" | "4k" = "720p", - language: string + language: string, + video_duration: string ) { try { // 调用创建项目API @@ -136,6 +137,7 @@ export class ScriptEditUseCase { mode, resolution, language, + video_duration, }); if (!response.successful) { From 3a713f988f2a1b9f322258e14c192470d879eee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=97=E6=9E=B3?= <7854742+wang_rumeng@user.noreply.gitee.com> Date: Mon, 18 Aug 2025 14:17:07 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20=E5=88=86=E9=95=9C?= =?UTF-8?q?=E6=8F=8F=E8=BF=B0=EF=BC=8C=E9=87=8D=E6=96=B0=E7=94=9F=E6=88=90?= =?UTF-8?q?=E8=A7=86=E9=A2=91=EF=BC=8C=E7=BB=99=E6=8E=A5=E5=8F=A3=E4=BC=A0?= =?UTF-8?q?=E9=80=92=E6=8F=8F=E8=BF=B0=E6=96=87=E5=AD=97=20=E8=BF=98?= =?UTF-8?q?=E6=98=AF=20=E6=97=A7=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/service/adapter/textToShot.ts | 18 ++++++++++-------- components/ui/shot-editor/ShotsEditor.tsx | 9 +++++---- components/ui/shot-tab-content.tsx | 15 +++++++++++---- hooks/useDeepCompareEffect.ts | 20 ++++++++++++++++++++ 4 files changed, 46 insertions(+), 16 deletions(-) create mode 100644 hooks/useDeepCompareEffect.ts diff --git a/app/service/adapter/textToShot.ts b/app/service/adapter/textToShot.ts index 81a2c71..b9c8111 100644 --- a/app/service/adapter/textToShot.ts +++ b/app/service/adapter/textToShot.ts @@ -257,14 +257,16 @@ export class TextToShotAdapter { if (shotData.shotDescContent.length > 0) { // 合并所有描述段落的文本内容 shotData.shotDescContent.forEach(paragraph => { - paragraph.content.forEach(node => { - if (node.type === 'text') { - currentScript += node.text; - } - if (node.type === 'characterToken') { - currentScript += node.attrs.name; - } - }); + if (paragraph.content) { + paragraph.content.forEach(node => { + if (node.type === 'text') { + currentScript += node.text; + } + if (node.type === 'characterToken') { + currentScript += node.attrs.name; + } + }); + } }); } diff --git a/components/ui/shot-editor/ShotsEditor.tsx b/components/ui/shot-editor/ShotsEditor.tsx index 0dacc6a..9a562d9 100644 --- a/components/ui/shot-editor/ShotsEditor.tsx +++ b/components/ui/shot-editor/ShotsEditor.tsx @@ -1,4 +1,5 @@ -import React, { forwardRef, useEffect, useRef, useState } from "react"; +import React, { forwardRef, useRef, useState } from "react"; +import { useDeepCompareEffect } from "@/hooks/useDeepCompareEffect"; import { Plus, X, UserRoundPlus, MessageCirclePlus, MessageCircleMore, ClipboardType } from "lucide-react"; import ShotEditor from "./ShotEditor"; import { toast } from "sonner"; @@ -51,16 +52,16 @@ export const ShotsEditor = forwardRef(function ShotsEdito const descEditorRef = useRef(null); const dialogEditorRef = useRef(null); - useEffect(() => { - console.log('-==========shotInfo===========-', shotInfo); + useDeepCompareEffect(() => { if (shotInfo) { + console.log('-==========shotInfo===========-', shotInfo); const shots = shotInfo.map((shot) => { return TextToShotAdapter.fromLensType(shot, roles); }); console.log('-==========shots===========-', shots); setShots(shots as Shot[]); } - }, [shotInfo]); + }, [shotInfo, roles]); const handleDescContentChange = (content: any) => { const shot = shots[currentShotIndex]; diff --git a/components/ui/shot-tab-content.tsx b/components/ui/shot-tab-content.tsx index bf381ca..e22aec4 100644 --- a/components/ui/shot-tab-content.tsx +++ b/components/ui/shot-tab-content.tsx @@ -47,11 +47,19 @@ export const ShotTabContent = (props: ShotTabContentProps) => { const [isLoadingShots, setIsLoadingShots] = useState(false); const shotsEditorRef = useRef(null); const [isRegenerate, setIsRegenerate] = useState(false); + const [pendingRegeneration, setPendingRegeneration] = useState(false); useEffect(() => { console.log('shotTabContent-----roles', roles); }, [roles]); + useEffect(() => { + if (pendingRegeneration) { + regenerateVideoSegment(); + setPendingRegeneration(false); + } + }, [shotData[selectedIndex]?.lens]); + // 监听当前选中index变化 useEffect(() => { console.log('shotTabContent-----shotData', shotData); @@ -165,13 +173,12 @@ export const ShotTabContent = (props: ShotTabContentProps) => { console.log('regenerate'); setIsRegenerate(true); const shotInfo = shotsEditorRef.current.getShotInfo(); - console.log('shotTabContent-----shotInfo', shotInfo); + console.log('shotInfo', shotInfo); setSelectedSegment({ ...shotData[selectedIndex], lens: shotInfo }); - await regenerateVideoSegment(); - setIsRegenerate(false); + setPendingRegeneration(true); }; // 新增分镜 @@ -408,7 +415,7 @@ export const ShotTabContent = (props: ShotTabContentProps) => { handleRegenerate()} className="flex items-center justify-center gap-2 px-4 py-3 bg-blue-500/10 hover:bg-blue-500/20 - text-blue-500 rounded-lg transition-colors" + text-blue-500 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed" whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }} disabled={isRegenerate} diff --git a/hooks/useDeepCompareEffect.ts b/hooks/useDeepCompareEffect.ts new file mode 100644 index 0000000..2851cda --- /dev/null +++ b/hooks/useDeepCompareEffect.ts @@ -0,0 +1,20 @@ +import { useEffect, useRef } from 'react'; +import isEqual from 'lodash/isEqual'; + +function useDeepCompareMemoize(value: T) { + const ref = useRef(); + + if (!isEqual(value, ref.current)) { + ref.current = value; + } + + return ref.current; +} + +export function useDeepCompareEffect( + callback: () => void | (() => void), + dependencies: any[] +) { + // eslint-disable-next-line react-hooks/exhaustive-deps + useEffect(callback, dependencies.map(useDeepCompareMemoize)); +} \ No newline at end of file