diff --git a/components/pages/work-flow/use-edit-data.tsx b/components/pages/work-flow/use-edit-data.tsx index 54495de..eddb315 100644 --- a/components/pages/work-flow/use-edit-data.tsx +++ b/components/pages/work-flow/use-edit-data.tsx @@ -3,6 +3,7 @@ import { useEffect, useState } from "react"; import { useShotService } from "@/app/service/Interaction/ShotService"; import { useSearchParams } from 'next/navigation'; import { useRoleServiceHook } from "@/app/service/Interaction/RoleService"; +import { useRoleShotServiceHook } from "@/app/service/Interaction/RoleShotService"; const mockRoleData = [{ id: '1', @@ -46,6 +47,18 @@ export const useEditData = (tabType: string) => { regenerateRole } = useRoleServiceHook(); + const { + shotSelectionList, + selectedRoleId, + isAllVideoSegmentSelected, + selectedVideoSegmentCount, + fetchRoleShots, + toggleSelectAllShots, + toggleShotSelection, + applyRoleToSelectedShots, + clearShotSelection + } = useRoleShotServiceHook(projectId); + useEffect(() => { if (tabType === 'shot') { getVideoSegmentList(projectId).then(() => { @@ -56,7 +69,7 @@ export const useEditData = (tabType: string) => { setLoading(false); }); } else if (tabType === 'role') { - fetchUserRoleLibrary(); + // fetchUserRoleLibrary(); fetchRoleList(projectId).then(() => { setLoading(false); }).catch((err) => { @@ -73,8 +86,8 @@ export const useEditData = (tabType: string) => { }, [videoSegments]); useEffect(() => { - // setRoleData(roleList); - setRoleData(mockRoleData); + setRoleData(roleList); + // setRoleData(mockRoleData); }, [roleList]); return { @@ -91,6 +104,16 @@ export const useEditData = (tabType: string) => { userRoleLibrary, optimizeRoleText, updateRoleText, - regenerateRole + regenerateRole, + // role shot + shotSelectionList, + selectedRoleId, + isAllVideoSegmentSelected, + selectedVideoSegmentCount, + fetchRoleShots, + toggleSelectAllShots, + toggleShotSelection, + applyRoleToSelectedShots, + clearShotSelection } } \ No newline at end of file diff --git a/components/ui/character-editor.tsx b/components/ui/character-editor.tsx index 1834e9d..9d57552 100644 --- a/components/ui/character-editor.tsx +++ b/components/ui/character-editor.tsx @@ -11,35 +11,15 @@ interface CharacterEditorProps { description: string; highlight: TagValueObject[]; onSmartPolish: (text: string) => void; + onUpdateText: (text: string) => void; } -const mockContent = [ - { - type: 'paragraph', - content: [ - { type: 'highlightText', attrs: { text: 'Hello, world!', color: 'blue' } }, - { type: 'text', text: 'Hello, world!' }, - { type: 'highlightText', attrs: { text: 'Hello, world!', color: 'red' } }, - { type: 'text', text: 'Hello, world!' }, - { type: 'highlightText', attrs: { text: 'Hello, world!', color: 'green' } }, - { type: 'text', text: 'Hello, world!' }, - { type: 'highlightText', attrs: { text: 'Hello, world!', color: 'yellow' } }, - { type: 'text', text: 'Hello, world!' }, - { type: 'highlightText', attrs: { text: 'Hello, world!', color: 'purple' } }, - { type: 'text', text: 'Hello, world!' }, - { type: 'highlightText', attrs: { text: 'Hello, world!', color: 'orange' } }, - { type: 'text', text: 'Hello, world!' }, - { type: 'highlightText', attrs: { text: 'Hello, world!', color: 'pink' } }, - { type: 'text', text: 'Hello, world!' }, - ], - }, -]; - export const CharacterEditor = forwardRef(({ className, description, highlight, - onSmartPolish + onSmartPolish, + onUpdateText }, ref) => { const [isOptimizing, setIsOptimizing] = useState(false); const [content, setContent] = useState([]); @@ -53,6 +33,12 @@ export const CharacterEditor = forwardRef(({ onSmartPolish(text); }; + const handleChangeContent = (content: any) => { + console.log('-==========handleChangeContent===========-', content); + onUpdateText(TextToShotAdapter.fromRoleToText(content)); + setContent(content); + }; + useEffect(() => { setIsInit(true); console.log('-==========description===========-', description); @@ -64,7 +50,7 @@ export const CharacterEditor = forwardRef(({ setIsInit(false); setIsOptimizing(false); }, 100); - }, [description, highlight]); + }, [highlight]); // 暴露方法给父组件 React.useImperativeHandle(ref, () => ({ @@ -77,7 +63,7 @@ export const CharacterEditor = forwardRef(({
{/* 自由输入区域 */} { - !isInit && + !isInit && } {/* 智能润色按钮 */} diff --git a/components/ui/character-tab-content.tsx b/components/ui/character-tab-content.tsx index f24fc3b..865980d 100644 --- a/components/ui/character-tab-content.tsx +++ b/components/ui/character-tab-content.tsx @@ -5,7 +5,7 @@ import { cn } from '@/public/lib/utils'; import { CharacterEditor } from './character-editor'; import ImageBlurTransition from './ImageBlurTransition'; import FloatingGlassPanel from './FloatingGlassPanel'; -import { ReplaceCharacterPanel, mockShots, mockCharacter } from './replace-character-panel'; +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'; @@ -69,11 +69,12 @@ export function CharacterTabContent({ const [ignoreReplace, setIgnoreReplace] = useState(false); const [isReplaceLibraryOpen, setIsReplaceLibraryOpen] = useState(false); const [isRemindReplacePanelOpen, setIsRemindReplacePanelOpen] = useState(false); - const [selectRoleIndex, setSelectRoleIndex] = useState(0); 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 { loading, @@ -83,7 +84,17 @@ export function CharacterTabContent({ userRoleLibrary, optimizeRoleText, updateRoleText, - regenerateRole + regenerateRole, + // role shot + shotSelectionList, + selectedRoleId, + isAllVideoSegmentSelected, + selectedVideoSegmentCount, + fetchRoleShots, + toggleSelectAllShots, + toggleShotSelection, + applyRoleToSelectedShots, + clearShotSelection } = useEditData('role'); const searchParams = useSearchParams(); const episodeId = searchParams.get('episodeId'); @@ -91,19 +102,29 @@ export function CharacterTabContent({ useEffect(() => { console.log('-==========roleData===========-', roleData); - if (roleData.length > 0) { - selectRole(roleData[selectRoleIndex].id); - + // 只在初始化且有角色数据时执行 + if (!isInitialized && roleData.length > 0) { + selectRole(roleData[0].id); + setIsInitialized(true); } - }, [selectRoleIndex, roleData]); + }, [roleData, isInitialized]); + + useEffect(() => { + console.log('获取选中项数据', selectedRole); + }, [selectedRole]); const handleSmartPolish = (text: string) => { - // 首先更新 - updateRoleText(text); // 然后调用优化角色文本 optimizeRoleText(text); }; + const handleStartReplaceCharacter = () => { + // 获取当前角色对应的视频片段 + fetchRoleShots(selectedRole?.id || ''); + // 打开替换角色面板 + setIsReplacePanelOpen(true); + }; + const handleConfirmGotoReplace = () => { setIsRemindReplacePanelOpen(false); setIsReplacePanelOpen(true); @@ -134,7 +155,9 @@ export function CharacterTabContent({ }; const handleChangeRole = (index: number) => { - if (selectedRole?.imageUrl !== roleData[selectRoleIndex].imageUrl && !ignoreReplace) { + const oldRole = roleData.find(role => role.id === selectedRole?.id); + console.log('切换角色前对比', selectedRole?.imageUrl, oldRole?.imageUrl); + if (selectedRole?.imageUrl !== oldRole?.imageUrl && !ignoreReplace) { // 提示 角色已修改,弹出替换角色面板 setIsRemindReplacePanelOpen(true); return; @@ -142,8 +165,9 @@ export function CharacterTabContent({ // 重置替换规则 setEnableAnimation(false); setIgnoreReplace(false); + setIsRegenerate(false); - setSelectRoleIndex(index); + selectRole(roleData[index].id); }; // 从角色库中选择角色 @@ -166,14 +190,16 @@ export function CharacterTabContent({ setShowAddToLibrary(true); }; - const handleRegenerate = () => { + const handleRegenerate = async () => { console.log('Regenerate'); - const text = characterEditorRef.current.getRoleText(); - console.log('-==========text===========-', text); - // 重生前 更新 当前项 generateText - updateRoleText(text); + setIsRegenerate(true); + // const text = characterEditorRef.current.getRoleText(); + // console.log('-==========text===========-', text); + // // 重生前 更新 当前项 generateText + // updateRoleText(text); // 然后调用重新生成角色 - regenerateRole(); + await regenerateRole(); + setIsRegenerate(false); }; const handleUploadClick = () => { @@ -239,7 +265,7 @@ export function CharacterTabContent({ role.id === selectedRole?.id)} onItemClick={(i: number) => handleChangeRole(i)} > {roleData.map((role, index) => ( @@ -248,7 +274,7 @@ export function CharacterTabContent({ className={cn( 'relative flex-shrink-0 w-24 rounded-lg overflow-hidden cursor-pointer', 'aspect-[9/16]', - selectRoleIndex === index ? 'ring-2 ring-blue-500' : 'hover:ring-2 hover:ring-blue-500/50' + role.id === selectedRole?.id ? 'ring-2 ring-blue-500' : 'hover:ring-2 hover:ring-blue-500/50' )} whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} @@ -280,8 +306,8 @@ export function CharacterTabContent({ {/* 角色预览图 */}
updateRoleText(text)} /> {/* 重新生成按钮、替换形象按钮 */}
handleReplaceCharacter('https://c.huiying.video/images/5740cb7c-6e08-478f-9e7c-bca7f78a2bf6.jpg')} + 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 }} @@ -334,12 +361,13 @@ export function CharacterTabContent({ 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" + text-blue-500 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed" whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }} + disabled={isRegenerate} > - Regenerate + {isRegenerate ? 'Regenerating...' : 'Regenerate'}
@@ -356,8 +384,8 @@ export function CharacterTabContent({ onClose={() => handleCloseReplacePanel()} > handleCloseReplacePanel()} onConfirm={handleConfirmReplace} diff --git a/components/ui/main-editor/HighlightText.tsx b/components/ui/main-editor/HighlightText.tsx index f763f18..cfdb317 100644 --- a/components/ui/main-editor/HighlightText.tsx +++ b/components/ui/main-editor/HighlightText.tsx @@ -23,7 +23,7 @@ export function HighlightText(props: ReactNodeViewProps) { {text} diff --git a/components/ui/replace-character-panel.tsx b/components/ui/replace-character-panel.tsx index 211ed01..14b7051 100644 --- a/components/ui/replace-character-panel.tsx +++ b/components/ui/replace-character-panel.tsx @@ -2,7 +2,7 @@ import { ReplacePanel } from './replace-panel'; import { Shot, Character } from '@/app/model/types'; interface ReplaceCharacterPanelProps { - shots: Shot[]; + shots: any[]; character: Character; showAddToLibrary?: boolean; onClose: () => void; @@ -39,15 +39,9 @@ export const mockShots: Shot[] = [ }, ]; -export const mockCharacter: Character = { - id: '1', - name: '千寻', - avatarUrl: '/assets/3dr_chihiro.png', -}; - export function ReplaceCharacterPanel({ - shots = mockShots, - character = mockCharacter, + shots = [], + character, showAddToLibrary = true, onClose, onConfirm, diff --git a/components/ui/replace-panel.tsx b/components/ui/replace-panel.tsx index 8d42538..ca69d2e 100644 --- a/components/ui/replace-panel.tsx +++ b/components/ui/replace-panel.tsx @@ -3,25 +3,10 @@ import { motion } from 'framer-motion'; import { Check, X, CircleAlert, ArrowLeft, ArrowRight } from 'lucide-react'; import { cn } from '@/public/lib/utils'; -// 定义类型 -interface Shot { - id: string; - videoUrl?: string; - thumbnailUrl: string; - isGenerating: boolean; - isSelected: boolean; -} - -interface Item { - id: string; - name: string; - avatarUrl: string; -} - interface ReplacePanelProps { title: string; - shots: Shot[]; - item: Item; + shots: any[]; + item: any; showAddToLibrary?: boolean; addToLibraryText?: string; onClose: () => void; @@ -175,16 +160,16 @@ export function ReplacePanel({ /> )} {!shot.videoUrl && ( - {`Shot - )} - {shot.isGenerating && ( -
-
生成中...
-
+ <> + {`Shot +
+
生成中...
+
+ )} {selectedShots.includes(shot.id) && (
@@ -219,7 +204,7 @@ export function ReplacePanel({ {/* 预览信息 */}
{item.name}