import React, { useState, useRef, useEffect, forwardRef } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { ImageUp, Library, Play, Pause, RefreshCw, Wand2, Users, CircleX, ReplaceAll, X, TriangleAlert, Loader2 } from 'lucide-react'; import { cn } from '@/public/lib/utils'; import { CharacterEditor } from './character-editor'; import ImageBlurTransition from './ImageBlurTransition'; import FloatingGlassPanel from './FloatingGlassPanel'; import { ReplaceCharacterPanel } from './replace-character-panel'; import { CharacterLibrarySelector } from './character-library-selector'; import HorizontalScroller from './HorizontalScroller'; import { useEditData } from '@/components/pages/work-flow/use-edit-data'; import { useSearchParams } from 'next/navigation'; import { RoleEntity } from '@/app/service/domain/Entities'; import { Role } from '@/api/DTO/movieEdit'; interface CharacterTabContentProps { originalRoles: Role[]; onClose: () => void; onApply: () => void; setActiveTab: (tabId: string) => void; } export const CharacterTabContent = forwardRef< { switchBefore: (tabId: string) => boolean, saveOrCloseBefore: () => void }, CharacterTabContentProps >((props, ref) => { const { onClose, onApply, setActiveTab, originalRoles } = props; const [isReplacePanelOpen, setIsReplacePanelOpen] = useState(false); const [replacePanelKey, setReplacePanelKey] = useState(0); const [ignoreReplace, setIgnoreReplace] = useState(false); const [isReplaceLibraryOpen, setIsReplaceLibraryOpen] = useState(false); const [isRemindReplacePanelOpen, setIsRemindReplacePanelOpen] = useState(false); const fileInputRef = useRef(null); const [enableAnimation, setEnableAnimation] = useState(true); const [showAddToLibrary, setShowAddToLibrary] = useState(true); const characterEditorRef = useRef(null); const [isInitialized, setIsInitialized] = useState(false); const [isRegenerate, setIsRegenerate] = useState(false); const [isLoadingShots, setIsLoadingShots] = useState(false); const [isLoadingLibrary, setIsLoadingLibrary] = useState(false); const [isUploading, setIsUploading] = useState(false); const [isUpdate, setIsUpdate] = useState(false); const [triggerType, setTriggerType] = useState<'tab' | 'apply' | 'user'>('tab'); const [nextToTabId, setNextToTabId] = useState(''); const [nextToUserIndex, setNextToUserIndex] = useState(-1); const { loading, roleData, selectRole, selectedRole, userRoleLibrary, optimizeRoleText, updateRoleText, regenerateRole, fetchUserRoleLibrary, uploadImageAndUpdateRole, // role shot shotSelectionList, fetchRoleShots, applyRoleToSelectedShots, saveRoleToLibrary } = useEditData('role'); const searchParams = useSearchParams(); const episodeId = searchParams.get('episodeId'); // 暴露方法给父组件 React.useImperativeHandle(ref, () => ({ switchBefore: (tabId: string) => { setNextToTabId(tabId); // 判断 角色是否修改 const currentIndex = getCurrentIndex(); const isChange = currentIndex !== -1 && isRoleChange(originalRoles[currentIndex]); console.log('switchBefore', isChange); if (isChange) { setTriggerType('tab'); setIsRemindReplacePanelOpen(true); } return isChange; }, saveOrCloseBefore: () => { console.log('saveOrCloseBefore'); // 判断 角色是否修改 const currentIndex = getCurrentIndex(); if (currentIndex !== -1 && isRoleChange(originalRoles[currentIndex])) { setTriggerType('apply'); handleStartReplaceCharacter(); } else { onClose(); } } })); useEffect(() => { console.log('-==========roleData===========-', roleData); // 只在初始化且有角色数据时执行 if (!isInitialized && roleData.length > 0) { selectRole(roleData[0]); setIsInitialized(true); } }, [roleData, isInitialized]); useEffect(() => { console.log('获取shotSelectionList数据', shotSelectionList); }, [shotSelectionList]); useEffect(() => { console.log('获取角色库数据', userRoleLibrary); }, [userRoleLibrary]); const handleSmartPolish = (text: string) => { // 然后调用优化角色文本 optimizeRoleText(text); }; const handleStartReplaceCharacter = async () => { setIsLoadingShots(true); setIsReplacePanelOpen(true); // 获取当前角色对应的视频片段 await fetchRoleShots(selectedRole?.name || ''); // 打开替换角色面板 setIsLoadingShots(false); }; const handleConfirmGotoReplace = () => { setIsRemindReplacePanelOpen(false); handleStartReplaceCharacter(); }; const handleCloseRemindReplacePanel = () => { setIsRemindReplacePanelOpen(false); console.log('忽略替换', triggerType, nextToTabId, nextToUserIndex); if (triggerType === 'apply') { onClose(); } else if (triggerType === 'tab') { setActiveTab(nextToTabId); } else { selectRole(roleData[nextToUserIndex]); } }; // President Alfred King Samuel Ryan const handleConfirmReplace = async (selectedShots: string[], addToLibrary: boolean) => { // 处理替换确认逻辑 console.log('Selected shots:', selectedShots); console.log('Add to library:', addToLibrary); setIsReplacePanelOpen(false); onClose(); await applyRoleToSelectedShots(selectedRole || {} as RoleEntity); if(addToLibrary){ await saveRoleToLibrary(); } onApply(); }; // 取消替换 const handleCloseReplacePanel = () => { setIsReplacePanelOpen(false); }; // 对比角色是否修改 const isRoleChange = (role: Role) => { console.log('对比角色是否修改', role, selectedRole); return role.name !== selectedRole?.name || role.url !== selectedRole?.imageUrl; }; // 获取当前选中下标 const getCurrentIndex = () => { return originalRoles.findIndex(role => role.name === selectedRole?.name); }; const handleChangeRole = (index: number) => { console.log('切换角色前对比'); const currentIndex = getCurrentIndex(); if (currentIndex === index) return; if (currentIndex !== -1 && isRoleChange(originalRoles[currentIndex])) { setTriggerType('user'); setIsRemindReplacePanelOpen(true); setNextToUserIndex(index); return; } // 重置替换规则 setEnableAnimation(false); setIgnoreReplace(false); setIsRegenerate(false); selectRole(roleData[index]); }; // 从角色库中选择角色 const handleSelectCharacter = (index: number) => { console.log('选择的角色索引:', index); console.log('选择的角色数据:', userRoleLibrary[index]); setIsReplaceLibraryOpen(false); setShowAddToLibrary(false); // 使用真实的角色数据 const role = userRoleLibrary[index]; if (role) { selectRole({ ...role, name: selectedRole?.name || '' }); // handleStartReplaceCharacter(); } }; const handleOpenReplaceLibrary = async () => { setIsLoadingLibrary(true); setIsReplaceLibraryOpen(true); setShowAddToLibrary(true); await fetchUserRoleLibrary(); setIsLoadingLibrary(false); }; const handleRegenerate = async () => { console.log('Regenerate'); setIsRegenerate(true); // const text = characterEditorRef.current.getRoleText(); // console.log('-==========text===========-', text); // // 重生前 更新 当前项 generateText // updateRoleText(text); // 然后调用重新生成角色 await regenerateRole(); setIsRegenerate(false); // handleStartReplaceCharacter(); }; const handleUploadClick = () => { fileInputRef.current?.click(); }; const handleFileChange = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (!file) { setIsUploading(false); return; }; // 检查文件类型 if (!file.type.startsWith('image/')) { alert('Please select an image file'); setIsUploading(false); return; } setIsUploading(true); uploadImageAndUpdateRole(file).then((data) => { console.log('上传图片成功', data); // 清空input的值,这样同一个文件可以重复选择 event.target.value = ''; setIsUploading(false); }); }; // 如果没有角色数据,显示占位内容 if (originalRoles.length === 0) { return (

No role data

); } return (
{/* 隐藏的文件输入框 */} {/* 上部分:角色缩略图 */}
role.name === selectedRole?.name)} onItemClick={(i: number) => handleChangeRole(i)} > {originalRoles.map((role, index) => ( {role.name}
{role.name}
))}
{/* 下部分:角色详情 */} { loading ? (

Loading...

) : ( {/* 左列:角色预览 */}
{/* 角色预览图 */}
{/* 应用角色按钮 */}
{isUploading ? : } handleOpenReplaceLibrary()} >
{/* 右列:角色信息 */}
updateRoleText(text)} /> {/* 重新生成按钮、替换形象按钮 */}
handleRegenerate()} className="flex items-center justify-center gap-2 px-4 py-3 bg-blue-500/10 hover:bg-blue-500/20 text-blue-500 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed" whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }} disabled={isRegenerate} > {isRegenerate ? 'Regenerating...' : 'Regenerate'}
)} handleCloseReplacePanel()} > handleCloseReplacePanel()} onConfirm={handleConfirmReplace} /> {/* 从角色库中选择角色 */} {/* 提醒用户角色已修改 是否需要替换 */}

The role has been modified, if you do not replace it, the modification will be lost, do you need to replace it?

); }); CharacterTabContent.displayName = 'CharacterTabContent';