video-flow-b/components/pages/work-flow/use-workflow-data.tsx
2025-07-04 19:38:39 +08:00

356 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client';
import { useState, useEffect, useRef, useCallback } from 'react';
import { getRandomMockData, STEP_MESSAGES, MOCK_DELAY_TIME, MOCK_DATA } from '@/components/work-flow/constants';
// 当前选择的mock数据
let selectedMockData: any = null;
export function useWorkflowData() {
const [taskObject, setTaskObject] = useState<any>(null);
const [taskSketch, setTaskSketch] = useState<any[]>([]);
const [taskRoles, setTaskRoles] = useState<any[]>([]);
const [taskVideos, setTaskVideos] = useState<any[]>([]);
const [sketchCount, setSketchCount] = useState(0);
const [isLoading, setIsLoading] = useState(true);
const [currentStep, setCurrentStep] = useState('0');
const [currentSketchIndex, setCurrentSketchIndex] = useState(0);
const [isGeneratingSketch, setIsGeneratingSketch] = useState(false);
const [isGeneratingVideo, setIsGeneratingVideo] = useState(false);
const [currentLoadingText, setCurrentLoadingText] = useState('正在加载项目数据...');
const [dataLoadError, setDataLoadError] = useState<string | null>(null);
const [isLoadingData, setIsLoadingData] = useState(false);
// 异步加载数据 - 改进错误处理和fallback机制
const loadMockData = async () => {
try {
setIsLoadingData(true);
setDataLoadError(null);
setCurrentLoadingText('正在从服务器获取项目数据...');
// 尝试从接口获取数据
selectedMockData = await getRandomMockData();
console.log('成功从接口获取数据:', selectedMockData);
setCurrentLoadingText('项目数据加载完成');
} catch (error) {
console.warn('接口获取数据失败使用本地fallback数据:', error);
try {
// 使用本地fallback数据
setCurrentLoadingText('正在加载本地数据...');
const randomIndex = Math.floor(Math.random() * MOCK_DATA.length);
selectedMockData = MOCK_DATA[randomIndex];
console.log('使用本地fallback数据:', selectedMockData);
setCurrentLoadingText('本地数据加载完成');
} catch (fallbackError) {
console.error('本地数据加载也失败:', fallbackError);
// 最后的fallback - 直接使用第一组数据
selectedMockData = MOCK_DATA[0];
setDataLoadError('服务器连接失败,已切换到演示模式');
setCurrentLoadingText('演示数据加载完成');
}
} finally {
setIsLoadingData(false);
}
};
// 重试加载数据
const retryLoadData = async () => {
console.log('用户点击重试,重新加载数据...');
selectedMockData = null; // 重置数据
setDataLoadError(null);
setIsLoading(true);
setCurrentStep('0');
// 重新初始化整个流程
await initializeWorkflow();
};
// 模拟接口请求 获取任务详情
const getTaskDetail = async (taskId: string) => {
// 确保已经加载了数据
if (!selectedMockData) {
console.warn('selectedMockData为空重新加载数据');
await loadMockData();
}
// 确保数据结构正确
if (!selectedMockData || !selectedMockData.detail) {
throw new Error('数据结构不正确');
}
const data = {
projectId: selectedMockData.detail.projectId,
projectName: selectedMockData.detail.projectName,
taskId: taskId,
taskName: selectedMockData.detail.taskName,
taskDescription: selectedMockData.detail.taskDescription,
taskStatus: selectedMockData.detail.taskStatus,
taskProgress: 0,
mode: selectedMockData.detail.mode,
resolution: selectedMockData.detail.resolution.toString(),
};
return data;
};
// 模拟接口请求 每次获取一个分镜草图 轮询获取
const getTaskSketch = async (taskId: string) => {
if (isGeneratingSketch || taskSketch.length > 0) return;
setIsGeneratingSketch(true);
setTaskSketch([]);
const sketchData = selectedMockData.sketch;
const totalSketches = sketchData.length;
// 模拟分批获取分镜草图
for (let i = 0; i < totalSketches; i++) {
await new Promise(resolve => setTimeout(resolve, MOCK_DELAY_TIME.sketch)); // 10s
const newSketch = {
id: `sketch-${i}`,
url: sketchData[i].url,
script: sketchData[i].script,
bg_rgb: sketchData[i].bg_rgb,
status: 'done'
};
setTaskSketch(prev => {
if (prev.find(sketch => sketch.id === newSketch.id)) {
return prev;
}
return [...prev, newSketch];
});
setCurrentSketchIndex(i);
setSketchCount(i + 1);
}
// 等待最后一个动画完成再设置生成状态为false
await new Promise(resolve => setTimeout(resolve, 1500));
setIsGeneratingSketch(false);
};
// 模拟接口请求 每次获取一个角色 轮询获取
const getTaskRole = async (taskId: string) => {
setTaskRoles([]);
const roleData = selectedMockData.roles;
const totalRoles = roleData.length;
for (let i = 0; i < totalRoles; i++) {
// 先更新loading文字显示当前正在生成的角色
setCurrentLoadingText(STEP_MESSAGES.newCharacter(i, totalRoles));
await new Promise(resolve => setTimeout(resolve, MOCK_DELAY_TIME.character)); // 2s 一个角色
// 添加角色到列表
setTaskRoles(prev => [...prev, roleData[i]]);
// 更新loading文字显示已完成的角色数量
setCurrentLoadingText(STEP_MESSAGES.newCharacter(i + 1, totalRoles));
// 如果不是最后一个角色,稍微延迟一下让用户看到更新
if (i < totalRoles - 1) {
await new Promise(resolve => setTimeout(resolve, 500));
}
}
};
// 模拟接口请求 获取背景音
const getTaskBackgroundAudio = async (taskId: string) => {
await new Promise(resolve => setTimeout(resolve, MOCK_DELAY_TIME.audio)); // 10s
};
// 模拟接口请求 获取最终成品
const getTaskFinalProduct = async (taskId: string) => {
await new Promise(resolve => setTimeout(resolve, MOCK_DELAY_TIME.final)); // 50s
};
// 模拟接口请求 每次获取一个分镜视频 轮询获取
const getTaskVideo = async (taskId: string) => {
setIsGeneratingVideo(true);
setTaskVideos([]);
const videoData = selectedMockData.video;
const totalVideos = videoData.length;
// 模拟分批获取分镜视频
for (let i = 0; i < totalVideos; i++) {
await new Promise(resolve => setTimeout(resolve, MOCK_DELAY_TIME.video)); // 60s
const newVideo = {
id: `video-${i}`,
url: videoData[i].url,
script: videoData[i].script,
status: 'done'
};
setTaskVideos(prev => {
if (prev.find(video => video.id === newVideo.id)) {
return prev;
}
return [...prev, newVideo];
});
setCurrentSketchIndex(i);
}
// 等待最后一个动画完成再设置生成状态为false
await new Promise(resolve => setTimeout(resolve, 1500));
setIsGeneratingVideo(false);
};
// 更新加载文字
useEffect(() => {
if (isLoading) {
// 在初始加载阶段保持当前loading文字不变
return;
}
const totalSketches = selectedMockData?.sketch?.length || 0;
const totalVideos = selectedMockData?.video?.length || 0;
const totalCharacters = selectedMockData?.roles?.length || 0;
if (currentStep === '1') {
if (isGeneratingSketch) {
setCurrentLoadingText(STEP_MESSAGES.sketch(sketchCount, totalSketches));
} else {
setCurrentLoadingText(STEP_MESSAGES.sketchComplete);
}
} else if (currentStep === '2') {
// 在角色生成阶段loading文字已经在 getTaskRole 函数中直接管理
// 这里不需要额外设置,避免覆盖
if (taskRoles.length === totalCharacters) {
setCurrentLoadingText(STEP_MESSAGES.newCharacter(totalCharacters, totalCharacters));
}
} else if (currentStep === '3') {
if (isGeneratingVideo) {
setCurrentLoadingText(STEP_MESSAGES.video(taskVideos.length, totalVideos));
} else {
setCurrentLoadingText(STEP_MESSAGES.videoComplete);
}
} else if (currentStep === '4') {
setCurrentLoadingText(STEP_MESSAGES.audio);
} else if (currentStep === '5') {
setCurrentLoadingText(STEP_MESSAGES.final);
} else {
setCurrentLoadingText(STEP_MESSAGES.complete);
}
}, [isLoading, currentStep, isGeneratingSketch, sketchCount, isGeneratingVideo, taskVideos.length, taskSketch.length, taskRoles.length]);
// 工作流初始化函数
const initializeWorkflow = async () => {
try {
setIsLoading(true);
setCurrentLoadingText('正在初始化工作流...');
const taskId = (typeof window !== 'undefined' ? localStorage.getItem("taskId") : null) || "taskId-123";
// 首先加载数据
await loadMockData();
// 然后获取任务详情
setCurrentLoadingText('正在加载任务详情...');
const data = await getTaskDetail(taskId);
setTaskObject(data);
// 数据加载完成,进入工作流
setIsLoading(false);
setCurrentStep('1');
// 只在任务详情加载完成后获取分镜草图
await getTaskSketch(taskId);
// 修改 taskObject 下的 taskStatus 为 '2'
setTaskObject((prev: any) => ({
...prev,
taskStatus: '2'
}));
setCurrentStep('2');
// 获取分镜草图后,开始绘制角色
await getTaskRole(taskId);
// 修改 taskObject 下的 taskStatus 为 '3'
setTaskObject((prev: any) => ({
...prev,
taskStatus: '3'
}));
setCurrentStep('3');
// 获取绘制角色后,开始获取分镜视频
await getTaskVideo(taskId);
// 修改 taskObject 下的 taskStatus 为 '4'
setTaskObject((prev: any) => ({
...prev,
taskStatus: '4'
}));
setCurrentStep('4');
// 获取分镜视频后,开始获取背景音
await getTaskBackgroundAudio(taskId);
// 后期制作:抽卡中 对口型中 配音中 一致性处理中
setCurrentLoadingText(STEP_MESSAGES.postProduction('Selecting optimal frames'));
await new Promise(resolve => setTimeout(resolve, MOCK_DELAY_TIME.postProduction));
setCurrentLoadingText(STEP_MESSAGES.postProduction('Aligning lip sync'));
await new Promise(resolve => setTimeout(resolve, MOCK_DELAY_TIME.postProduction));
setCurrentLoadingText(STEP_MESSAGES.postProduction('Adding background audio'));
await new Promise(resolve => setTimeout(resolve, MOCK_DELAY_TIME.postProduction));
setCurrentLoadingText(STEP_MESSAGES.postProduction('Consistency processing'));
await new Promise(resolve => setTimeout(resolve, MOCK_DELAY_TIME.postProduction));
// 修改 taskObject 下的 taskStatus 为 '5'
setTaskObject((prev: any) => ({
...prev,
taskStatus: '5'
}));
setCurrentStep('5');
// 获取背景音后,开始获取最终成品
await getTaskFinalProduct(taskId);
await new Promise(resolve => setTimeout(resolve, 2000));
// 修改 taskObject 下的 taskStatus 为 '6'
setTaskObject((prev: any) => ({
...prev,
taskStatus: '6'
}));
setCurrentStep('6');
} catch (error) {
console.error('工作流初始化失败:', error);
setDataLoadError('工作流初始化失败,请刷新页面重试');
setIsLoading(false);
}
};
// 初始化数据
useEffect(() => {
initializeWorkflow();
}, []);
return {
// 状态数据
taskObject,
taskSketch,
taskVideos,
sketchCount,
isLoading: isLoading || isLoadingData, // 合并loading状态
currentStep,
currentSketchIndex,
isGeneratingSketch,
isGeneratingVideo,
currentLoadingText,
totalSketchCount: selectedMockData?.sketch?.length || 0,
roles: selectedMockData?.roles || [],
music: selectedMockData?.music || {},
final: selectedMockData?.final || {},
dataLoadError,
// 操作方法
setCurrentSketchIndex,
retryLoadData,
};
}