forked from 77media/video-flow
更新工作流组件,新增originalText参数以支持脚本数据的初始化,同时优化编辑模态中的重置逻辑,提升用户体验和代码可读性。
This commit is contained in:
parent
c08983ce12
commit
26006bde15
@ -47,7 +47,8 @@ export default function WorkFlow() {
|
||||
setIsPauseWorkFlow,
|
||||
setAnyAttribute,
|
||||
applyScript,
|
||||
fallbackToStep
|
||||
fallbackToStep,
|
||||
originalText
|
||||
} = useWorkflowData();
|
||||
|
||||
const {
|
||||
@ -270,12 +271,10 @@ export default function WorkFlow() {
|
||||
onSketchSelect={setCurrentSketchIndex}
|
||||
roles={roles}
|
||||
music={music}
|
||||
scriptData={scriptData}
|
||||
setIsPauseWorkFlow={setIsPauseWorkFlow}
|
||||
setAnyAttribute={setAnyAttribute}
|
||||
isPauseWorkFlow={isPauseWorkFlow}
|
||||
applyScript={applyScript}
|
||||
fallbackToStep={fallbackToStep}
|
||||
originalText={originalText}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
|
||||
@ -4,29 +4,24 @@ import { useShotService } from "@/app/service/Interaction/ShotService";
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { useRoleServiceHook } from "@/app/service/Interaction/RoleService";
|
||||
import { useRoleShotServiceHook } from "@/app/service/Interaction/RoleShotService";
|
||||
import { useScriptService } from "@/app/service/Interaction/ScriptService";
|
||||
|
||||
const mockRoleData = [{
|
||||
id: '1',
|
||||
name: 'KAPI',
|
||||
imageUrl: 'https://c.huiying.video/images/420bfb4f-b5d4-475c-a2fb-5e40af770b29.jpg',
|
||||
generateText: 'A 3 to 5-year-old boy with a light to medium olive skin tone, full cheeks, and warm brown eyes. He has short, straight, dark brown hair, neatly styled with a part on his left side. His facial structure includes a small, slightly upturned nose. His lips are typically held in a slight, gentle, closed-mouth smile, which can part to show his small, white teeth.',
|
||||
tags: [
|
||||
{ id: '1', content: 'boy', color: 'red' },
|
||||
{ id: '2', content: '3 to 5-year-old', color: 'yellow' },
|
||||
{ id: '3', content: 'light to medium olive skin tone', color: 'green' },
|
||||
{ id: '4', content: 'full cheeks', color: 'blue' },
|
||||
{ id: '5', content: 'warm brown eyes', color: 'purple' },
|
||||
]
|
||||
}]
|
||||
|
||||
export const useEditData = (tabType: string) => {
|
||||
export const useEditData = (tabType: string, originalText?: string) => {
|
||||
const searchParams = useSearchParams();
|
||||
const projectId = searchParams.get('episodeId') || '';
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [scriptData, setScriptData] = useState<any[]>([]);
|
||||
const [shotData, setShotData] = useState<any[]>([]);
|
||||
|
||||
const [roleData, setRoleData] = useState<any[]>([]);
|
||||
|
||||
const {
|
||||
scriptBlocksMemo, // 渲染剧本数据
|
||||
initializeFromProject,
|
||||
setAnyAttribute,
|
||||
applyScript
|
||||
} = useScriptService();
|
||||
|
||||
const {
|
||||
videoSegments,
|
||||
getVideoSegmentList,
|
||||
@ -72,7 +67,15 @@ export const useEditData = (tabType: string) => {
|
||||
}, [selectedRole]);
|
||||
|
||||
useEffect(() => {
|
||||
if (tabType === 'shot') {
|
||||
if (tabType === 'script') {
|
||||
initializeFromProject(projectId, originalText || '').then(() => {
|
||||
setLoading(false);
|
||||
}).catch((err) => {
|
||||
console.log('useEditData-----err', err);
|
||||
setScriptData([]);
|
||||
setLoading(false);
|
||||
});
|
||||
} else if (tabType === 'shot') {
|
||||
getVideoSegmentList(projectId).then(() => {
|
||||
setLoading(false);
|
||||
}).catch((err) => {
|
||||
@ -91,6 +94,12 @@ export const useEditData = (tabType: string) => {
|
||||
}
|
||||
}, [tabType]);
|
||||
|
||||
useEffect(() => {
|
||||
if (scriptBlocksMemo.length > 0) {
|
||||
setScriptData(scriptBlocksMemo);
|
||||
}
|
||||
}, [scriptBlocksMemo]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('useEditData-----videoSegments', videoSegments);
|
||||
setShotData(videoSegments);
|
||||
@ -103,6 +112,10 @@ export const useEditData = (tabType: string) => {
|
||||
|
||||
return {
|
||||
loading,
|
||||
// script
|
||||
scriptData,
|
||||
setAnyAttribute,
|
||||
applyScript,
|
||||
// shot
|
||||
shotData,
|
||||
setSelectedSegment,
|
||||
|
||||
@ -449,10 +449,9 @@ export function useWorkflowData() {
|
||||
}
|
||||
|
||||
// 如果有已完成的数据,同步到状态
|
||||
let finalStep = '0';
|
||||
if (data) {
|
||||
if (data.sketch && data.sketch.data) {
|
||||
finalStep = '1';
|
||||
taskData.status = '1';
|
||||
const realSketchResultData = data.sketch.data.filter((item: any) => item.image_path);
|
||||
const sketchList = [];
|
||||
for (const sketch of realSketchResultData) {
|
||||
@ -461,6 +460,8 @@ export function useWorkflowData() {
|
||||
script: sketch.sketch_name,
|
||||
});
|
||||
}
|
||||
taskData.sketch.data = sketchList;
|
||||
taskData.sketch.total_count = data.sketch.total_count;
|
||||
setTaskSketch(sketchList);
|
||||
setTaskScenes(sketchList);
|
||||
updateSketchCount(sketchList.length);
|
||||
@ -469,7 +470,7 @@ export function useWorkflowData() {
|
||||
setIsGeneratingSketch(true);
|
||||
loadingText = LOADING_TEXT_MAP.sketch(realSketchResultData.length, data.sketch.total_count);
|
||||
} else {
|
||||
finalStep = '2';
|
||||
taskData.status = '2';
|
||||
if (!data.character || !data.character.data || !data.character.data.length) {
|
||||
loadingText = LOADING_TEXT_MAP.newCharacter(0, data.character.total_count);
|
||||
}
|
||||
@ -486,11 +487,13 @@ export function useWorkflowData() {
|
||||
roleDescription: character.character_description
|
||||
});
|
||||
}
|
||||
taskData.character.data = characterList;
|
||||
taskData.character.total_count = data.character.total_count;
|
||||
setRoles(characterList);
|
||||
if (data.character.total_count > data.character.data.length) {
|
||||
loadingText = LOADING_TEXT_MAP.newCharacter(data.character.data.length, data.character.total_count);
|
||||
} else {
|
||||
finalStep = '3';
|
||||
taskData.status = '3';
|
||||
if (!data.video || !data.video.data || !data.video.data.length) {
|
||||
loadingText = LOADING_TEXT_MAP.getShotSketchStatus;
|
||||
}
|
||||
@ -505,6 +508,8 @@ export function useWorkflowData() {
|
||||
script: sketch.description,
|
||||
});
|
||||
}
|
||||
taskData.shot_sketch.data = sketchList;
|
||||
taskData.shot_sketch.total_count = data.shot_sketch.total_count;
|
||||
setTaskSketch(sketchList);
|
||||
setTaskShotSketch(sketchList);
|
||||
updateSketchCount(sketchList.length);
|
||||
@ -513,7 +518,7 @@ export function useWorkflowData() {
|
||||
setIsGeneratingSketch(true);
|
||||
loadingText = LOADING_TEXT_MAP.shotSketch(realShotResultData.length, data.shot_sketch.total_count);
|
||||
} else {
|
||||
finalStep = '3';
|
||||
taskData.status = '3';
|
||||
setIsGeneratingVideo(true);
|
||||
if (!data.character || !data.character.data || !data.character.data.length) {
|
||||
loadingText = LOADING_TEXT_MAP.getVideoStatus;
|
||||
@ -522,7 +527,7 @@ export function useWorkflowData() {
|
||||
}
|
||||
if (data.video.data) {
|
||||
const realDataVideoData = data.video.data.filter((item: any) => item.urls && item.urls.length > 0);
|
||||
if (realDataVideoData.length === 0 && finalStep === '3') {
|
||||
if (realDataVideoData.length === 0 && taskData.status === '3') {
|
||||
loadingText = LOADING_TEXT_MAP.video(0, data.video.total_count);
|
||||
}
|
||||
if (realDataVideoData.length > 0) {
|
||||
@ -537,6 +542,8 @@ export function useWorkflowData() {
|
||||
video_id: video.video_id,
|
||||
});
|
||||
}
|
||||
taskData.video.data = videoList;
|
||||
taskData.video.total_count = data.video.total_count;
|
||||
setTaskVideos(videoList);
|
||||
updateVideoCount(videoList.length);
|
||||
// 如果在视频步骤,设置为最后一个视频
|
||||
@ -544,11 +551,11 @@ export function useWorkflowData() {
|
||||
setIsGeneratingVideo(true);
|
||||
loadingText = LOADING_TEXT_MAP.video(realDataVideoData.length, data.video.total_count);
|
||||
} else {
|
||||
finalStep = '4';
|
||||
taskData.status = '4';
|
||||
loadingText = LOADING_TEXT_MAP.audio;
|
||||
|
||||
// 暂时没有音频生成 直接跳过
|
||||
finalStep = '5';
|
||||
taskData.status = '5';
|
||||
loadingText = LOADING_TEXT_MAP.postProduction('generating rough cut video...');
|
||||
}
|
||||
}
|
||||
@ -559,7 +566,7 @@ export function useWorkflowData() {
|
||||
setFinal({
|
||||
url: data.final_simple_video.video
|
||||
});
|
||||
finalStep = '5.5';
|
||||
taskData.status = '5.5';
|
||||
loadingText = LOADING_TEXT_MAP.postProduction('generating fine-grained video clips...');
|
||||
}
|
||||
|
||||
@ -567,25 +574,25 @@ export function useWorkflowData() {
|
||||
setFinal({
|
||||
url: data.final_video.video
|
||||
});
|
||||
finalStep = '6';
|
||||
taskData.status = '6';
|
||||
loadingText = LOADING_TEXT_MAP.complete;
|
||||
}
|
||||
}
|
||||
|
||||
// 设置步骤
|
||||
setCurrentStep(finalStep);
|
||||
setCurrentStep(taskData.status);
|
||||
setTaskObject(prev => {
|
||||
if (!prev) return null;
|
||||
return {
|
||||
...prev,
|
||||
taskStatus: finalStep
|
||||
taskStatus: taskData.status
|
||||
};
|
||||
});
|
||||
console.log('---------loadingText', loadingText);
|
||||
setCurrentLoadingText(loadingText);
|
||||
|
||||
// 设置是否需要获取流式数据
|
||||
setNeedStreamData(status !== 'COMPLETED' && finalStep !== '6');
|
||||
setNeedStreamData(status !== 'COMPLETED' && taskData.status !== '6');
|
||||
|
||||
} catch (error) {
|
||||
console.error('初始化失败:', error);
|
||||
@ -650,6 +657,7 @@ export function useWorkflowData() {
|
||||
setIsPauseWorkFlow,
|
||||
setAnyAttribute,
|
||||
applyScript,
|
||||
fallbackToStep
|
||||
fallbackToStep,
|
||||
originalText
|
||||
};
|
||||
}
|
||||
|
||||
@ -25,11 +25,9 @@ interface EditModalProps {
|
||||
roles?: any[];
|
||||
music?: any;
|
||||
setIsPauseWorkFlow: (isPauseWorkFlow: boolean) => void;
|
||||
setAnyAttribute: any;
|
||||
isPauseWorkFlow: boolean;
|
||||
scriptData: any[] | null;
|
||||
applyScript: any;
|
||||
fallbackToStep: any;
|
||||
originalText?: string;
|
||||
}
|
||||
|
||||
const tabs = [
|
||||
@ -55,16 +53,16 @@ export function EditModal({
|
||||
roles = [],
|
||||
music,
|
||||
setIsPauseWorkFlow,
|
||||
setAnyAttribute,
|
||||
isPauseWorkFlow,
|
||||
scriptData,
|
||||
applyScript,
|
||||
fallbackToStep
|
||||
fallbackToStep,
|
||||
originalText
|
||||
}: EditModalProps) {
|
||||
const [activeTab, setActiveTab] = useState(activeEditTab);
|
||||
const [currentIndex, setCurrentIndex] = useState(currentSketchIndex);
|
||||
const [currentRoleIndex, setCurrentRoleIndex] = useState(0);
|
||||
const [isRemindFallbackOpen, setIsRemindFallbackOpen] = useState(false);
|
||||
const [isRemindResetOpen, setIsRemindResetOpen] = useState(false);
|
||||
const [resetKey, setResetKey] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentIndex(currentSketchIndex);
|
||||
@ -109,7 +107,6 @@ export function EditModal({
|
||||
if (activeTab === '0') {
|
||||
fallbackToStep('0');
|
||||
// 应用剧本
|
||||
applyScript();
|
||||
} else {
|
||||
fallbackToStep('1');
|
||||
}
|
||||
@ -121,7 +118,13 @@ export function EditModal({
|
||||
const handleReset = () => {
|
||||
console.log('handleReset');
|
||||
// 重置当前tab修改的数据
|
||||
|
||||
setIsRemindResetOpen(true);
|
||||
}
|
||||
|
||||
const handleConfirmReset = () => {
|
||||
console.log('handleConfirmReset');
|
||||
setIsRemindResetOpen(false);
|
||||
setResetKey(resetKey + 1);
|
||||
}
|
||||
|
||||
const renderTabContent = () => {
|
||||
@ -129,11 +132,9 @@ export function EditModal({
|
||||
case '0':
|
||||
return (
|
||||
<ScriptTabContent
|
||||
scriptData={scriptData}
|
||||
setIsPauseWorkFlow={setIsPauseWorkFlow}
|
||||
setAnyAttribute={setAnyAttribute}
|
||||
isPauseWorkFlow={isPauseWorkFlow}
|
||||
applyScript={applyScript}
|
||||
originalText={originalText}
|
||||
/>
|
||||
);
|
||||
case '1':
|
||||
@ -265,7 +266,7 @@ export function EditModal({
|
||||
<div className="overflow-y-auto p-4" style={{ height: 'calc(100% - 8rem)' }}>
|
||||
<AnimatePresence mode="wait">
|
||||
<motion.div
|
||||
key={activeTab}
|
||||
key={`${activeTab}-${resetKey}`}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -20 }}
|
||||
@ -334,6 +335,39 @@ export function EditModal({
|
||||
</div>
|
||||
</div>
|
||||
</FloatingGlassPanel>
|
||||
|
||||
{/* 提醒用户 重置当前tab修改的数据 */}
|
||||
<FloatingGlassPanel
|
||||
open={isRemindResetOpen}
|
||||
width='500px'
|
||||
clickMaskClose={false}
|
||||
>
|
||||
<div className="flex flex-col items-center gap-4 text-white py-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<TriangleAlert className="w-6 h-6 text-yellow-400" />
|
||||
<p className="text-lg font-medium">Are you sure you want to reset the current tab?</p>
|
||||
</div>
|
||||
<div className="flex gap-3 mt-2">
|
||||
<button
|
||||
onClick={() => handleConfirmReset()}
|
||||
data-alt="confirm-replace-button"
|
||||
className="px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-md transition-colors duration-200 flex items-center gap-2"
|
||||
>
|
||||
<Undo2 className="w-4 h-4" />
|
||||
Reset
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => setIsRemindResetOpen(false)}
|
||||
data-alt="ignore-button"
|
||||
className="px-4 py-2 bg-gray-600 hover:bg-gray-700 rounded-md transition-colors duration-200 flex items-center gap-2"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</FloatingGlassPanel>
|
||||
</>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
|
||||
@ -2,23 +2,31 @@ import React, { useState, useCallback, useEffect, SetStateAction } from 'react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { FileText } from 'lucide-react';
|
||||
import { ScriptRenderer } from '@/components/script-renderer/ScriptRenderer';
|
||||
import { useEditData } from '@/components/pages/work-flow/use-edit-data';
|
||||
|
||||
|
||||
interface ScriptTabContentProps {
|
||||
scriptData: any[] | null;
|
||||
setIsPauseWorkFlow: (isPauseWorkFlow: boolean) => void;
|
||||
setAnyAttribute: any;
|
||||
isPauseWorkFlow: boolean;
|
||||
applyScript: any;
|
||||
originalText?: string;
|
||||
}
|
||||
|
||||
export function ScriptTabContent({
|
||||
scriptData = [],
|
||||
setIsPauseWorkFlow,
|
||||
setAnyAttribute,
|
||||
isPauseWorkFlow,
|
||||
applyScript
|
||||
originalText,
|
||||
}: ScriptTabContentProps) {
|
||||
const { loading, scriptData, setAnyAttribute, applyScript } = useEditData('script', originalText);
|
||||
|
||||
// 如果loading 显示loading状态
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center min-h-[400px] text-white/50">
|
||||
<div className="w-12 h-12 mb-4 animate-spin rounded-full border-b-2 border-blue-600" />
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// 如果没有数据,显示空状态
|
||||
if (!scriptData || scriptData.length === 0) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user