更新工作流组件,新增originalText参数以支持脚本数据的初始化,同时优化编辑模态中的重置逻辑,提升用户体验和代码可读性。

This commit is contained in:
北枳 2025-08-13 02:23:26 +08:00
parent c08983ce12
commit 26006bde15
5 changed files with 115 additions and 53 deletions

View File

@ -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>

View File

@ -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,

View File

@ -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
};
}

View File

@ -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>

View File

@ -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) {