检测角色改变

This commit is contained in:
北枳 2025-08-14 16:01:32 +08:00
parent 90fb57f0fd
commit e6ef62a3d7
3 changed files with 98 additions and 51 deletions

View File

@ -43,6 +43,7 @@ export const useEditData = (tabType: string, originalText?: string) => {
regenerateRole, regenerateRole,
uploadImageAndUpdateRole, uploadImageAndUpdateRole,
saveRoleToLibrary, saveRoleToLibrary,
changeTabCallback,
} = useRoleServiceHook(); } = useRoleServiceHook();
const { const {
@ -134,6 +135,7 @@ export const useEditData = (tabType: string, originalText?: string) => {
regenerateRole, regenerateRole,
fetchUserRoleLibrary, fetchUserRoleLibrary,
uploadImageAndUpdateRole, uploadImageAndUpdateRole,
changeTabCallback,
// role shot // role shot
shotSelectionList, shotSelectionList,
selectedRoleId, selectedRoleId,

View File

@ -1,4 +1,4 @@
import React, { useState, useRef, useEffect } from 'react'; import React, { useState, useRef, useEffect, forwardRef } from 'react';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { ImageUp, Library, Play, Pause, RefreshCw, Wand2, Users, CircleX, ReplaceAll, X, TriangleAlert, Loader2 } from 'lucide-react'; import { ImageUp, Library, Play, Pause, RefreshCw, Wand2, Users, CircleX, ReplaceAll, X, TriangleAlert, Loader2 } from 'lucide-react';
import { cn } from '@/public/lib/utils'; import { cn } from '@/public/lib/utils';
@ -35,18 +35,17 @@ interface Role {
interface CharacterTabContentProps { interface CharacterTabContentProps {
taskSketch: any[]; onClose: () => void;
currentRoleIndex: number; onApply: () => void;
onSketchSelect: (index: number) => void; setActiveTab: (tabId: string) => void;
roles: Role[];
} }
export function CharacterTabContent({
taskSketch, export const CharacterTabContent = forwardRef<
currentRoleIndex, { switchBefore: (tabId: string) => boolean, saveBefore: () => void },
onSketchSelect, CharacterTabContentProps
roles = [] >((props, ref) => {
}: CharacterTabContentProps) { const { onClose, onApply, setActiveTab } = props;
const [isReplacePanelOpen, setIsReplacePanelOpen] = useState(false); const [isReplacePanelOpen, setIsReplacePanelOpen] = useState(false);
const [replacePanelKey, setReplacePanelKey] = useState(0); const [replacePanelKey, setReplacePanelKey] = useState(0);
const [ignoreReplace, setIgnoreReplace] = useState(false); const [ignoreReplace, setIgnoreReplace] = useState(false);
@ -61,6 +60,10 @@ export function CharacterTabContent({
const [isLoadingShots, setIsLoadingShots] = useState(false); const [isLoadingShots, setIsLoadingShots] = useState(false);
const [isLoadingLibrary, setIsLoadingLibrary] = useState(false); const [isLoadingLibrary, setIsLoadingLibrary] = useState(false);
const [isUploading, setIsUploading] = useState(false); const [isUploading, setIsUploading] = useState(false);
const [isUpdate, setIsUpdate] = useState(false);
const [triggerType, setTriggerType] = useState<'tab' | 'apply' | 'user'>('tab');
const [nextToTabId, setNextToTabId] = useState<string>('');
const [nextToUserIndex, setNextToUserIndex] = useState<number>(-1);
const { const {
loading, loading,
@ -73,6 +76,7 @@ export function CharacterTabContent({
regenerateRole, regenerateRole,
fetchUserRoleLibrary, fetchUserRoleLibrary,
uploadImageAndUpdateRole, uploadImageAndUpdateRole,
changeTabCallback,
// role shot // role shot
shotSelectionList, shotSelectionList,
fetchRoleShots, fetchRoleShots,
@ -82,6 +86,31 @@ export function CharacterTabContent({
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const episodeId = searchParams.get('episodeId'); const episodeId = searchParams.get('episodeId');
// 暴露方法给父组件
React.useImperativeHandle(ref, () => ({
switchBefore: (tabId: string) => {
setNextToTabId(tabId);
// 判断 角色是否修改
const isChange = selectedRole!.isChangeRole
console.log('switchBefore', isChange);
if (isChange) {
setTriggerType('tab');
setIsRemindReplacePanelOpen(true);
}
return isChange;
},
saveBefore: () => {
console.log('saveBefore');
// 判断 角色是否修改
changeTabCallback((isChange: Boolean) => {
if (isChange) {
setTriggerType('apply');
handleStartReplaceCharacter();
}
});
}
}));
useEffect(() => { useEffect(() => {
console.log('-==========roleData===========-', roleData); console.log('-==========roleData===========-', roleData);
@ -116,24 +145,32 @@ export function CharacterTabContent({
const handleConfirmGotoReplace = () => { const handleConfirmGotoReplace = () => {
setIsRemindReplacePanelOpen(false); setIsRemindReplacePanelOpen(false);
setIsReplacePanelOpen(true); handleStartReplaceCharacter();
}; };
const handleCloseRemindReplacePanel = () => { const handleCloseRemindReplacePanel = () => {
setIsRemindReplacePanelOpen(false); setIsRemindReplacePanelOpen(false);
setIgnoreReplace(true); console.log('忽略替换', triggerType, nextToTabId, nextToUserIndex);
if (triggerType === 'apply') {
onClose();
} else if (triggerType === 'tab') {
setActiveTab(nextToTabId);
} else {
selectRole(roleData[nextToUserIndex]);
}
}; };
// President Alfred King Samuel Ryan // President Alfred King Samuel Ryan
const handleConfirmReplace = (selectedShots: string[], addToLibrary: boolean) => { const handleConfirmReplace = async (selectedShots: string[], addToLibrary: boolean) => {
// 处理替换确认逻辑 // 处理替换确认逻辑
console.log('Selected shots:', selectedShots); console.log('Selected shots:', selectedShots);
console.log('Add to library:', addToLibrary); console.log('Add to library:', addToLibrary);
applyRoleToSelectedShots(selectedRole || {} as RoleEntity); await applyRoleToSelectedShots(selectedRole || {} as RoleEntity);
setIsReplacePanelOpen(false); setIsReplacePanelOpen(false);
if(addToLibrary){ if(addToLibrary){
saveRoleToLibrary(); saveRoleToLibrary();
} }
onApply();
}; };
// 取消替换 // 取消替换
@ -143,18 +180,22 @@ export function CharacterTabContent({
const handleChangeRole = (index: number) => { const handleChangeRole = (index: number) => {
const oldRole = roleData.find(role => role.id === selectedRole?.id); const oldRole = roleData.find(role => role.id === selectedRole?.id);
console.log('切换角色前对比', selectedRole?.imageUrl, oldRole?.imageUrl); console.log('切换角色前对比');
if (selectedRole?.imageUrl !== oldRole?.imageUrl && !ignoreReplace) { changeTabCallback((isChange: Boolean) => {
// 提示 角色已修改,弹出替换角色面板 if (isChange) {
setTriggerType('user');
setIsRemindReplacePanelOpen(true); setIsRemindReplacePanelOpen(true);
setNextToUserIndex(index);
return; return;
} }
// 重置替换规则 // 重置替换规则
setEnableAnimation(false); setEnableAnimation(false);
setIgnoreReplace(false); setIgnoreReplace(false);
setIsRegenerate(false); setIsRegenerate(false);
selectRole(roleData[index]); selectRole(roleData[index]);
});
}; };
// 从角色库中选择角色 // 从角色库中选择角色
@ -172,7 +213,7 @@ export function CharacterTabContent({
...role, ...role,
name: selectedRole?.name || '' name: selectedRole?.name || ''
}); });
handleStartReplaceCharacter(); // handleStartReplaceCharacter();
} }
}; };
@ -194,7 +235,7 @@ export function CharacterTabContent({
// 然后调用重新生成角色 // 然后调用重新生成角色
await regenerateRole(); await regenerateRole();
setIsRegenerate(false); setIsRegenerate(false);
handleStartReplaceCharacter(); // handleStartReplaceCharacter();
}; };
const handleUploadClick = () => { const handleUploadClick = () => {
@ -347,17 +388,7 @@ export function CharacterTabContent({
onUpdateText={(text: string) => updateRoleText(text)} onUpdateText={(text: string) => updateRoleText(text)}
/> />
{/* 重新生成按钮、替换形象按钮 */} {/* 重新生成按钮、替换形象按钮 */}
<div className="grid grid-cols-2 gap-2"> <div className="grid grid-cols-1 gap-2">
<motion.button
onClick={() => handleStartReplaceCharacter()}
className="flex items-center justify-center gap-2 px-4 py-3 bg-pink-500/10 hover:bg-pink-500/20
text-pink-500 rounded-lg transition-colors"
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
<ReplaceAll className="w-4 h-4" />
<span>Replace</span>
</motion.button>
<motion.button <motion.button
onClick={() => handleRegenerate()} onClick={() => handleRegenerate()}
className="flex items-center justify-center gap-2 px-4 py-3 bg-blue-500/10 hover:bg-blue-500/20 className="flex items-center justify-center gap-2 px-4 py-3 bg-blue-500/10 hover:bg-blue-500/20
@ -410,7 +441,7 @@ export function CharacterTabContent({
<div className="flex flex-col items-center gap-4 text-white py-4"> <div className="flex flex-col items-center gap-4 text-white py-4">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<TriangleAlert className="w-6 h-6 text-yellow-400" /> <TriangleAlert className="w-6 h-6 text-yellow-400" />
<p className="text-lg font-medium"></p> <p className="text-lg font-medium"></p>
</div> </div>
<div className="flex gap-3 mt-2"> <div className="flex gap-3 mt-2">
@ -436,4 +467,4 @@ export function CharacterTabContent({
</FloatingGlassPanel> </FloatingGlassPanel>
</div> </div>
); );
} });

View File

@ -66,6 +66,7 @@ export function EditModal({
const [resetKey, setResetKey] = useState(0); const [resetKey, setResetKey] = useState(0);
const [remindFallbackText, setRemindFallbackText] = useState('The task will be regenerated and edited. Do you want to continue?'); const [remindFallbackText, setRemindFallbackText] = useState('The task will be regenerated and edited. Do you want to continue?');
const scriptTabContentRef = useRef<any>(null); const scriptTabContentRef = useRef<any>(null);
const characterTabContentRef = useRef<any>(null);
// 添加一个状态来标记是否是从切换tab触发的提醒 // 添加一个状态来标记是否是从切换tab触发的提醒
const [pendingSwitchTabId, setPendingSwitchTabId] = useState<string | null>(null); const [pendingSwitchTabId, setPendingSwitchTabId] = useState<string | null>(null);
@ -97,11 +98,16 @@ const [pendingSwitchTabId, setPendingSwitchTabId] = useState<string | null>(null
} }
const checkUpdate = (tabId: string) => { const checkUpdate = (tabId: string) => {
if (tabId === '0') { if (activeTab === '0') {
const scriptTabContent = scriptTabContentRef.current; const scriptTabContent = scriptTabContentRef.current;
if (scriptTabContent) { if (scriptTabContent) {
return scriptTabContent.checkUpdate(); return scriptTabContent.checkUpdate();
} }
} else if (activeTab === '1') {
const characterTabContent = characterTabContentRef.current;
if (characterTabContent) {
return characterTabContent.switchBefore(tabId);
}
} }
return false; return false;
} }
@ -109,11 +115,11 @@ const [pendingSwitchTabId, setPendingSwitchTabId] = useState<string | null>(null
const handleChangeTab = (tabId: string, disabled: boolean) => { const handleChangeTab = (tabId: string, disabled: boolean) => {
if (disabled) return; if (disabled) return;
// 切换前 检查是否更新 // 切换前 检查是否更新
const isUpdate = checkUpdate(activeTab); const isUpdate = checkUpdate(tabId);
if (isUpdate) { if (isUpdate) {
setPendingSwitchTabId(tabId); // 记录要切换到的目标tab // setPendingSwitchTabId(tabId); // 记录要切换到的目标tab
setRemindFallbackText('You must click Apply button to save the current changes.'); // setRemindFallbackText('You must click Apply button to save the current changes.');
setIsRemindFallbackOpen(true); // setIsRemindFallbackOpen(true);
return; return;
} }
setActiveTab(tabId); setActiveTab(tabId);
@ -122,7 +128,15 @@ const [pendingSwitchTabId, setPendingSwitchTabId] = useState<string | null>(null
const handleSave = () => { const handleSave = () => {
console.log('handleSave'); console.log('handleSave');
setIsRemindFallbackOpen(true); // setIsRemindFallbackOpen(true);
if (activeTab === '1') {
characterTabContentRef.current.saveBefore();
}
}
const handleApply = () => {
console.log('handleApply');
handleConfirmGotoFallback();
} }
const handleConfirmGotoFallback = () => { const handleConfirmGotoFallback = () => {
@ -174,10 +188,10 @@ const [pendingSwitchTabId, setPendingSwitchTabId] = useState<string | null>(null
case '1': case '1':
return ( return (
<CharacterTabContent <CharacterTabContent
taskSketch={taskSketch} ref={characterTabContentRef}
currentRoleIndex={currentRoleIndex} onClose={onClose}
onSketchSelect={hanldeChangeSelect} onApply={handleApply}
roles={roles} setActiveTab={setActiveTab}
/> />
); );
case '2': case '2':