import { useState, useCallback, useMemo } from 'react'; import { SceneEntity, TagEntity, AITextEntity, VideoSegmentEntity } from '../domain/Entities'; import { SceneItem, TagItem, TextItem, ShotItem } from '../domain/Item'; import { SceneEditUseCase } from '../usecase/SceneEditUseCase'; import { TagEditUseCase } from '../usecase/TagEditUseCase'; import { TextEditUseCase } from '../usecase/TextEditUseCase'; import { getSceneShots, getSceneData, getSceneList } from '@/api/video_flow'; /** * 分镜选择项接口 */ interface ShotSelectionItem { /** 分镜ID */ id: string; /** 分镜名称 */ name: string; /** 是否已选中 */ selected: boolean; /** 是否已应用场景 */ applied: boolean; /** 分镜数据 */ shot: VideoSegmentEntity; } /** * 场景服务Hook返回值接口 */ interface UseSceneService { // 响应式数据 /** 场景列表 */ sceneList: SceneItem[]; /** 当前选中的场景 */ selectedScene: SceneItem | null; /** 当前场景的AI文本 */ currentSceneText: TextItem | null; /** 当前场景的标签列表 */ currentSceneTags: TagItem[]; /** 场景图片URL */ sceneImageUrl: string; /** 分镜选择列表 */ shotSelectionList: ShotSelectionItem[]; /** 是否全选分镜 */ isAllShotsSelected: boolean; /** 已选中的分镜数量 */ selectedShotsCount: number; // 操作方法 /** 获取场景列表 */ fetchSceneList: (projectId: string) => Promise; /** 选择场景 */ selectScene: (sceneId: string) => void; /** 初始化当前选中场景的AI文本和标签数据 */ initializeSceneData: () => Promise; /** 优化AI文本 */ optimizeSceneText: () => Promise; /** 修改AI文本 */ updateSceneText: (newContent: string) => Promise; /** 修改标签内容 */ updateTagContent: (tagId: string, newContent: string | number) => Promise; /** 重新生成场景 */ regenerateScene: () => Promise; /** 获取场景出现的分镜列表 */ fetchSceneShots: () => Promise; /** 切换全选与全不选 */ toggleSelectAllShots: () => void; /** 选择/取消选择单个分镜 */ toggleShotSelection: (shotId: string) => void; /** 应用场景到选中的分镜 */ applySceneToSelectedShots: () => Promise; } /** * 场景服务Hook * 提供场景相关的所有响应式功能和业务逻辑 */ export const useSceneServiceHook = (): UseSceneService => { // 响应式状态 const [sceneList, setSceneList] = useState([]); const [selectedScene, setSelectedScene] = useState(null); const [currentSceneText, setCurrentSceneText] = useState(null); const [currentSceneTags, setCurrentSceneTags] = useState([]); const [shotSelectionList, setShotSelectionList] = useState([]); // UseCase实例 - 在场景选择时初始化 const [sceneEditUseCase, setSceneEditUseCase] = useState(null); const [textEditUseCase, setTextEditUseCase] = useState(null); const [tagEditUseCases, setTagEditUseCases] = useState>(new Map()); // 计算属性 /** * 场景图片URL * @description 获取当前选中场景的图片URL */ const sceneImageUrl = useMemo(() => { return selectedScene?.entity.imageUrl || ''; }, [selectedScene]); /** * 是否全选分镜 * @description 判断是否所有分镜都被选中 */ const isAllShotsSelected = useMemo(() => { return shotSelectionList.length > 0 && shotSelectionList.every(shot => shot.selected); }, [shotSelectionList]); /** * 已选中的分镜数量 * @description 获取当前选中的分镜数量 */ const selectedShotsCount = useMemo(() => { return shotSelectionList.filter(shot => shot.selected).length; }, [shotSelectionList]); /** * 获取场景列表 * @description 根据项目ID获取所有场景列表 * @param projectId 项目ID * @throws {Error} 当API调用失败时抛出错误 * @returns {Promise} 获取完成后的Promise */ const fetchSceneList = useCallback(async (projectId: string) => { try { const response = await getSceneList({ projectId: projectId }); if (response.successful) { const sceneItems = response.data.map(scene => new SceneItem(scene)); setSceneList(sceneItems); } else { throw new Error(`获取场景列表失败: ${response.message}`); } } catch (error) { console.error('获取场景列表失败:', error); throw error; } }, []); /** * 选择场景 * @description 根据场景ID选择场景,并初始化相关的UseCase实例 * @param sceneId 场景ID */ const selectScene = useCallback(async (sceneId: string) => { const scene = sceneList.find(s => s.entity.id === sceneId); if (scene) { setSelectedScene(scene); // 初始化场景编辑UseCase实例 setSceneEditUseCase(new SceneEditUseCase(scene)); // 清空文本和标签相关状态 setTextEditUseCase(null); setTagEditUseCases(new Map()); setCurrentSceneText(null); setCurrentSceneTags([]); await initializeSceneData(); } }, [sceneList]); /** * 初始化场景数据 * @description 初始化当前选中场景的AI文本和标签数据 * @throws {Error} 当未选择场景或API调用失败时抛出错误 * @returns {Promise} 初始化完成后的Promise */ const initializeSceneData = useCallback(async () => { if (!selectedScene) { throw new Error('请先选择场景'); } try { const response = await getSceneData({ sceneId: selectedScene.entity.id }); if (response.successful) { const { text, tags } = response.data; const textItem = new TextItem(text); const tagItems = tags.map(tag => new TagItem(tag)); // 设置当前场景的AI文本和标签 setCurrentSceneText(textItem); setCurrentSceneTags(tagItems); // 初始化文本UseCase setTextEditUseCase(new TextEditUseCase(textItem)); // 初始化标签UseCase const newTagEditUseCases = new Map(); tagItems.forEach(tag => { newTagEditUseCases.set(tag.entity.id, new TagEditUseCase(tag)); }); setTagEditUseCases(newTagEditUseCases); } else { throw new Error(`获取场景数据失败: ${response.message}`); } } catch (error) { console.error('初始化场景数据失败:', error); throw error; } }, [selectedScene]); /** * 优化AI文本 * @description 对当前场景的AI文本进行优化,无文本时不可进行优化 * @throws {Error} 当没有可优化的文本内容或UseCase未初始化时抛出错误 * @returns {Promise} 优化完成后的Promise */ const optimizeSceneText = useCallback(async () => { if (!textEditUseCase) { throw new Error('文本编辑UseCase未初始化'); } if (!currentSceneText || !currentSceneText.entity.content) { throw new Error('没有可优化的文本内容'); } const optimizedContent = await textEditUseCase.getOptimizedContent(); const updatedTextItem = await textEditUseCase.updateText(optimizedContent); setCurrentSceneText(updatedTextItem); }, [textEditUseCase, currentSceneText]); /** * 修改AI文本 * @description 手动修改当前场景的AI文本内容 * @param newContent 新的文本内容 * @throws {Error} 当没有可编辑的文本或UseCase未初始化时抛出错误 * @returns {Promise} 修改完成后的Promise */ const updateSceneText = useCallback(async (newContent: string) => { if (!textEditUseCase) { throw new Error('文本编辑UseCase未初始化'); } if (!currentSceneText) { throw new Error('没有可编辑的文本'); } const updatedTextItem = await textEditUseCase.updateText(newContent); setCurrentSceneText(updatedTextItem); }, [textEditUseCase, currentSceneText]); /** * 修改标签内容 * @description 修改指定标签的内容 * @param tagId 标签ID * @param newContent 新的标签内容 * @throws {Error} 当标签不存在或UseCase未初始化时抛出错误 * @returns {Promise} 修改完成后的Promise */ const updateTagContent = useCallback(async (tagId: string, newContent: string | number) => { const tagEditUseCase = tagEditUseCases.get(tagId); if (!tagEditUseCase) { throw new Error(`标签编辑UseCase未初始化,标签ID: ${tagId}`); } const updatedTagItem = await tagEditUseCase.updateTag(newContent); setCurrentSceneTags(prev => prev.map(tag => tag.entity.id === tagId ? updatedTagItem : tag ) ); }, [tagEditUseCases]); /** * 重新生成场景 * @description 使用AI文本和标签重新生成场景 * @throws {Error} 当缺少重新生成场景所需的数据或UseCase未初始化时抛出错误 * @returns {Promise} 重新生成完成后的Promise */ const regenerateScene = useCallback(async () => { if (!sceneEditUseCase) { throw new Error('场景编辑UseCase未初始化'); } if (!selectedScene || !currentSceneText || currentSceneTags.length === 0) { throw new Error('缺少重新生成场景所需的数据'); } const newSceneEntity = await sceneEditUseCase.AIgenerateScene(currentSceneText, currentSceneTags); const newSceneItem = new SceneItem(newSceneEntity); setSelectedScene(newSceneItem); setSceneList(prev => prev.map(scene => scene.entity.id === newSceneEntity.id ? newSceneItem : scene ) ); }, [sceneEditUseCase, selectedScene, currentSceneText, currentSceneTags]); /** * 获取场景出现的分镜列表 * @description 获取当前场景应用到的分镜列表,包括已应用状态 * @throws {Error} 当未选择场景或API调用失败时抛出错误 * @returns {Promise} 获取完成后的Promise */ const fetchSceneShots = useCallback(async () => { if (!selectedScene) { throw new Error('请先选择场景'); } try { const response = await getSceneShots({ sceneId: selectedScene.entity.id }); if (response.successful) { const { shots, appliedShotIds } = response.data; const shotSelectionItems: ShotSelectionItem[] = shots.map(shot => ({ id: shot.id, name: shot.name, selected: false, applied: appliedShotIds.includes(shot.id), shot })); setShotSelectionList(shotSelectionItems); } else { throw new Error(`获取场景分镜列表失败: ${response.message}`); } } catch (error) { console.error('获取场景分镜列表失败:', error); throw error; } }, [selectedScene]); /** * 切换全选与全不选 * @description 如果当前是全选状态则全不选,否则全选 */ const toggleSelectAllShots = useCallback(() => { setShotSelectionList(prev => { const isAllSelected = prev.length > 0 && prev.every(shot => shot.selected); return prev.map(shot => ({ ...shot, selected: !isAllSelected })); }); }, []); /** * 选择/取消选择单个分镜 * @description 切换指定分镜的选择状态 * @param shotId 分镜ID */ const toggleShotSelection = useCallback((shotId: string) => { setShotSelectionList(prev => prev.map(shot => shot.id === shotId ? { ...shot, selected: !shot.selected } : shot ) ); }, []); /** * 应用场景到选中的分镜 * @description 将当前场景应用到选中的分镜,并更新应用状态 * @throws {Error} 当未选择场景、未选择分镜或UseCase未初始化时抛出错误 * @returns {Promise} 应用完成后的Promise */ const applySceneToSelectedShots = useCallback(async () => { if (!sceneEditUseCase) { throw new Error('场景编辑UseCase未初始化'); } if (!selectedScene) { throw new Error('请先选择场景'); } const selectedShotIds = shotSelectionList .filter(shot => shot.selected) .map(shot => shot.id); if (selectedShotIds.length === 0) { throw new Error('请先选择要应用的分镜'); } await sceneEditUseCase.applyScene(selectedShotIds); setShotSelectionList(prev => prev.map(shot => selectedShotIds.includes(shot.id) ? { ...shot, applied: true, selected: false } : shot ) ); }, [sceneEditUseCase, selectedScene, shotSelectionList]); return { // 响应式数据 sceneList, selectedScene, currentSceneText, currentSceneTags, sceneImageUrl, shotSelectionList, isAllShotsSelected, selectedShotsCount, // 操作方法 /** 获取场景列表 */ fetchSceneList, /** 选择场景 */ selectScene, /** 初始化当前选中场景的AI文本和标签数据 */ initializeSceneData, /** 优化AI文本 */ optimizeSceneText, /** 修改AI文本 */ updateSceneText, /** 修改标签内容 */ updateTagContent, /** 重新生成场景 */ regenerateScene, /** 获取场景出现的分镜列表 */ fetchSceneShots, /** 切换全选与全不选 */ toggleSelectAllShots, /** 选择/取消选择单个分镜 */ toggleShotSelection, /** 应用场景到选中的分镜 */ applySceneToSelectedShots }; };