import { useState, useCallback, useMemo } from 'react'; import { RoleEntity, TagEntity, AITextEntity, ShotEntity } from '../domain/Entities'; import { RoleItem, TagItem, TextItem, ShotItem } from '../domain/Item'; import { RoleEditUseCase } from '../usecase/RoleEditUseCase'; import { TagEditUseCase } from '../usecase/TagEditUseCase'; import { TextEditUseCase } from '../usecase/TextEditUseCase'; import { getRoleShots } from '@/api/video_flow'; /** * 分镜选择项接口 */ interface ShotSelectionItem { /** 分镜ID */ id: string; /** 分镜名称 */ name: string; /** 是否已选中 */ selected: boolean; /** 是否已应用角色 */ applied: boolean; /** 分镜数据 */ shot: ShotEntity; } /** * 角色服务Hook返回值接口 */ interface UseRoleService { // 响应式数据 /** 角色列表 */ roleList: RoleItem[]; /** 当前选中的角色 */ selectedRole: RoleItem | null; /** 当前角色的AI文本 */ currentRoleText: TextItem | null; /** 当前角色的标签列表 */ currentRoleTags: TagItem[]; /** 角色图片URL */ roleImageUrl: string; /** 分镜选择列表 */ shotSelectionList: ShotSelectionItem[]; /** 是否全选分镜 */ isAllShotsSelected: boolean; /** 已选中的分镜数量 */ selectedShotsCount: number; // 操作方法 /** 选择角色 */ selectRole: (roleId: string) => void; /** 设置当前角色的AI文本和标签 */ setCurrentRoleData: (text: TextItem, tags: TagItem[]) => void; /** 优化AI文本 */ optimizeRoleText: () => Promise; /** 修改AI文本 */ updateRoleText: (newContent: string) => Promise; /** 修改标签内容 */ updateTagContent: (tagId: string, newContent: string | number) => Promise; /** 重新生成角色 */ regenerateRole: () => Promise; /** 获取角色出现的分镜列表 */ fetchRoleShots: () => Promise; /** 切换全选与全不选 */ toggleSelectAllShots: () => void; /** 选择/取消选择单个分镜 */ toggleShotSelection: (shotId: string) => void; /** 应用角色到选中的分镜 */ applyRoleToSelectedShots: () => Promise; } /** * 角色服务Hook * 提供角色相关的所有响应式功能和业务逻辑 */ export const useRoleServiceHook = (): UseRoleService => { // 响应式状态 const [roleList, setRoleList] = useState([]); const [selectedRole, setSelectedRole] = useState(null); const [currentRoleText, setCurrentRoleText] = useState(null); const [currentRoleTags, setCurrentRoleTags] = useState([]); const [shotSelectionList, setShotSelectionList] = useState([]); // UseCase实例 - 在角色选择时初始化 const [roleEditUseCase, setRoleEditUseCase] = useState(null); const [textEditUseCase, setTextEditUseCase] = useState(null); const [tagEditUseCases, setTagEditUseCases] = useState>(new Map()); // 计算属性 /** * 角色图片URL * @description 获取当前选中角色的图片URL */ const roleImageUrl = useMemo(() => { return selectedRole?.entity.imageUrl || ''; }, [selectedRole]); /** * 是否全选分镜 * @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选择角色,并初始化相关的UseCase实例 * @param roleId 角色ID */ const selectRole = useCallback((roleId: string) => { const role = roleList.find(r => r.entity.id === roleId); if (role) { setSelectedRole(role); // 初始化UseCase实例 setRoleEditUseCase(new RoleEditUseCase(role)); setTextEditUseCase(null); // 文本UseCase在获取到文本后初始化 setTagEditUseCases(new Map()); // 标签UseCase在获取到标签后初始化 setCurrentRoleText(null); setCurrentRoleTags([]); } }, [roleList]); /** * 优化AI文本 * @description 对当前角色的AI文本进行优化,无文本时不可进行优化 * @throws {Error} 当没有可优化的文本内容或UseCase未初始化时抛出错误 * @returns {Promise} 优化完成后的Promise */ const optimizeRoleText = useCallback(async () => { if (!textEditUseCase) { throw new Error('文本编辑UseCase未初始化'); } if (!currentRoleText || !currentRoleText.entity.content) { throw new Error('没有可优化的文本内容'); } const optimizedContent = await textEditUseCase.getOptimizedContent(); // 更新文本内容 const updatedTextItem = await textEditUseCase.updateText(optimizedContent); setCurrentRoleText(updatedTextItem); }, [textEditUseCase, currentRoleText]); /** * 修改AI文本 * @description 手动修改当前角色的AI文本内容 * @param newContent 新的文本内容 * @throws {Error} 当没有可编辑的文本或UseCase未初始化时抛出错误 * @returns {Promise} 修改完成后的Promise */ const updateRoleText = useCallback(async (newContent: string) => { if (!textEditUseCase) { throw new Error('文本编辑UseCase未初始化'); } if (!currentRoleText) { throw new Error('没有可编辑的文本'); } const updatedTextItem = await textEditUseCase.updateText(newContent); setCurrentRoleText(updatedTextItem); }, [textEditUseCase, currentRoleText]); /** * 修改标签内容 * @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); // 更新标签列表 setCurrentRoleTags(prev => prev.map(tag => tag.entity.id === tagId ? updatedTagItem : tag ) ); }, [tagEditUseCases]); /** * 重新生成角色 * @description 使用AI文本和标签重新生成角色 * @throws {Error} 当缺少重新生成角色所需的数据或UseCase未初始化时抛出错误 * @returns {Promise} 重新生成完成后的Promise */ const regenerateRole = useCallback(async () => { if (!roleEditUseCase) { throw new Error('角色编辑UseCase未初始化'); } if (!selectedRole || !currentRoleText || currentRoleTags.length === 0) { throw new Error('缺少重新生成角色所需的数据'); } const newRoleEntity = await roleEditUseCase.AIgenerateRole(currentRoleText, currentRoleTags); // 更新角色 const newRoleItem = new RoleItem(newRoleEntity); setSelectedRole(newRoleItem); // 更新角色列表 setRoleList(prev => prev.map(role => role.entity.id === newRoleEntity.id ? newRoleItem : role ) ); }, [roleEditUseCase, selectedRole, currentRoleText, currentRoleTags]); /** * 获取角色出现的分镜列表 * @description 获取当前角色应用到的分镜列表,包括已应用状态 * @throws {Error} 当未选择角色或API调用失败时抛出错误 * @returns {Promise} 获取完成后的Promise */ const fetchRoleShots = useCallback(async () => { if (!selectedRole) { throw new Error('请先选择角色'); } try { const response = await getRoleShots({ roleId: selectedRole.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), // 根据API返回的已应用列表判断 shot })); setShotSelectionList(shotSelectionItems); } else { throw new Error(`获取角色分镜列表失败: ${response.message}`); } } catch (error) { console.error('获取角色分镜列表失败:', error); throw error; } }, [selectedRole]); /** * 切换全选与全不选 * @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 applyRoleToSelectedShots = useCallback(async () => { if (!roleEditUseCase) { throw new Error('角色编辑UseCase未初始化'); } if (!selectedRole) { throw new Error('请先选择角色'); } const selectedShotIds = shotSelectionList .filter(shot => shot.selected) .map(shot => shot.id); if (selectedShotIds.length === 0) { throw new Error('请先选择要应用的分镜'); } await roleEditUseCase.applyRole(selectedShotIds); // 更新分镜列表,标记已应用 setShotSelectionList(prev => prev.map(shot => selectedShotIds.includes(shot.id) ? { ...shot, applied: true, selected: false } : shot ) ); }, [roleEditUseCase, selectedRole, shotSelectionList]); /** * 设置当前角色的AI文本和标签 * @description 设置当前角色的AI文本和标签,并初始化对应的UseCase * @param text AI文本项 * @param tags 标签项列表 */ const setCurrentRoleData = useCallback((text: TextItem, tags: TagItem[]) => { setCurrentRoleText(text); setCurrentRoleTags(tags); // 初始化文本UseCase if (text) { setTextEditUseCase(new TextEditUseCase(text)); } // 初始化标签UseCase const newTagEditUseCases = new Map(); tags.forEach(tag => { newTagEditUseCases.set(tag.entity.id, new TagEditUseCase(tag)); }); setTagEditUseCases(newTagEditUseCases); }, []); return { // 响应式数据 /** 角色列表 */ roleList, /** 当前选中的角色 */ selectedRole, /** 当前角色的AI文本 */ currentRoleText, /** 当前角色的标签列表 */ currentRoleTags, /** 角色图片URL */ roleImageUrl, /** 分镜选择列表 */ shotSelectionList, /** 是否全选分镜 */ isAllShotsSelected, /** 已选中的分镜数量 */ selectedShotsCount, // 操作方法 /** 选择角色 */ selectRole, /** 设置当前角色的AI文本和标签 */ setCurrentRoleData, /** 优化AI文本 */ optimizeRoleText, /** 修改AI文本 */ updateRoleText, /** 修改标签内容 */ updateTagContent, /** 重新生成角色 */ regenerateRole, /** 获取角色出现的分镜列表 */ fetchRoleShots, /** 切换全选与全不选 */ toggleSelectAllShots, /** 选择/取消选择单个分镜 */ toggleShotSelection, /** 应用角色到选中的分镜 */ applyRoleToSelectedShots }; };