This commit is contained in:
海龙 2025-08-12 17:39:32 +08:00
commit 55a61424e8
5 changed files with 73 additions and 30 deletions

View File

@ -57,7 +57,7 @@ export const useEditData = (tabType: string) => {
toggleShotSelection,
applyRoleToSelectedShots,
clearShotSelection
} = useRoleShotServiceHook(projectId);
} = useRoleShotServiceHook(projectId, selectedRole);
useEffect(() => {
if (tabType === 'shot') {
@ -69,7 +69,6 @@ export const useEditData = (tabType: string) => {
setLoading(false);
});
} else if (tabType === 'role') {
// fetchUserRoleLibrary();
fetchRoleList(projectId).then(() => {
setLoading(false);
}).catch((err) => {
@ -105,6 +104,7 @@ export const useEditData = (tabType: string) => {
optimizeRoleText,
updateRoleText,
regenerateRole,
fetchUserRoleLibrary,
// role shot
shotSelectionList,
selectedRoleId,

View File

@ -3,6 +3,7 @@ import { ImageWave } from '@/components/ui/ImageWave';
import { RoleEntity } from '@/app/service/domain/Entities';
interface CharacterLibrarySelectorProps {
isLoading: boolean;
isReplaceLibraryOpen: boolean;
setIsReplaceLibraryOpen: (open: boolean) => void;
onSelect: (index: number) => void;
@ -11,13 +12,28 @@ interface CharacterLibrarySelectorProps {
}
export function CharacterLibrarySelector({
isLoading,
isReplaceLibraryOpen,
setIsReplaceLibraryOpen,
onSelect,
userRoleLibrary = []
}: CharacterLibrarySelectorProps) {
// 将 RoleEntity[] 转换为图片URL数组
const imageUrls = userRoleLibrary.map(role => role.imageUrl);
if (isLoading) {
return (
<FloatingGlassPanel
open={isReplaceLibraryOpen}
width='90vw'
panel_style={{ background: 'unset', border: 'unset', backdropFilter: 'unset', boxShadow: 'none' }}
onClose={() => setIsReplaceLibraryOpen(false)}
>
<div className="flex flex-col items-center justify-center w-full h-64 text-white/50">
<div className="w-12 h-12 mb-4 animate-spin rounded-full border-b-2 border-blue-600" />
<p>Loading...</p>
</div>
</FloatingGlassPanel>
);
}
// 如果没有数据,显示空状态
if (userRoleLibrary.length === 0) {
@ -44,7 +60,7 @@ export function CharacterLibrarySelector({
>
{/* 内容 */}
<ImageWave
images={imageUrls}
images={userRoleLibrary.map(role => role.imageUrl)}
containerWidth="90vw"
containerHeight="calc(var(--index) * 15)"
itemWidth="calc(var(--index) * 2)"

View File

@ -10,6 +10,7 @@ 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';
interface Appearance {
hairStyle: string;
@ -75,6 +76,8 @@ export function CharacterTabContent({
const characterEditorRef = useRef<any>(null);
const [isInitialized, setIsInitialized] = useState(false);
const [isRegenerate, setIsRegenerate] = useState(false);
const [isLoadingShots, setIsLoadingShots] = useState(false);
const [isLoadingLibrary, setIsLoadingLibrary] = useState(false);
const {
loading,
@ -85,16 +88,11 @@ export function CharacterTabContent({
optimizeRoleText,
updateRoleText,
regenerateRole,
fetchUserRoleLibrary,
// role shot
shotSelectionList,
selectedRoleId,
isAllVideoSegmentSelected,
selectedVideoSegmentCount,
fetchRoleShots,
toggleSelectAllShots,
toggleShotSelection,
applyRoleToSelectedShots,
clearShotSelection
applyRoleToSelectedShots
} = useEditData('role');
const searchParams = useSearchParams();
const episodeId = searchParams.get('episodeId');
@ -113,16 +111,22 @@ export function CharacterTabContent({
console.log('获取选中项数据', selectedRole);
}, [selectedRole]);
useEffect(() => {
console.log('获取角色库数据', userRoleLibrary);
}, [userRoleLibrary]);
const handleSmartPolish = (text: string) => {
// 然后调用优化角色文本
optimizeRoleText(text);
};
const handleStartReplaceCharacter = () => {
// 获取当前角色对应的视频片段
fetchRoleShots(selectedRole?.id || '');
// 打开替换角色面板
const handleStartReplaceCharacter = async () => {
setIsLoadingShots(true);
setIsReplacePanelOpen(true);
// 获取当前角色对应的视频片段
await fetchRoleShots(selectedRole?.name || '');
// 打开替换角色面板
setIsLoadingShots(false);
};
const handleConfirmGotoReplace = () => {
@ -146,6 +150,7 @@ export function CharacterTabContent({
// 处理替换确认逻辑
console.log('Selected shots:', selectedShots);
console.log('Add to library:', addToLibrary);
applyRoleToSelectedShots(selectedRole || {} as RoleEntity);
setIsReplacePanelOpen(false);
};
@ -185,9 +190,12 @@ export function CharacterTabContent({
}
};
const handleOpenReplaceLibrary = () => {
const handleOpenReplaceLibrary = async () => {
setIsLoadingLibrary(true);
setIsReplaceLibraryOpen(true);
setShowAddToLibrary(true);
await fetchUserRoleLibrary();
setIsLoadingLibrary(false);
};
const handleRegenerate = async () => {
@ -200,6 +208,7 @@ export function CharacterTabContent({
// 然后调用重新生成角色
await regenerateRole();
setIsRegenerate(false);
handleStartReplaceCharacter();
};
const handleUploadClick = () => {
@ -384,8 +393,9 @@ export function CharacterTabContent({
onClose={() => handleCloseReplacePanel()}
>
<ReplaceCharacterPanel
isLoading={isLoadingShots}
shots={shotSelectionList}
character={selectedRole}
role={selectedRole}
showAddToLibrary={showAddToLibrary}
onClose={() => handleCloseReplacePanel()}
onConfirm={handleConfirmReplace}
@ -394,6 +404,7 @@ export function CharacterTabContent({
{/* 从角色库中选择角色 */}
<CharacterLibrarySelector
isLoading={isLoadingLibrary}
isReplaceLibraryOpen={isReplaceLibraryOpen}
setIsReplaceLibraryOpen={setIsReplaceLibraryOpen}
onSelect={handleSelectCharacter}

View File

@ -1,9 +1,11 @@
import { ReplacePanel } from './replace-panel';
import { Shot, Character } from '@/app/model/types';
import { useEffect, useState } from 'react';
interface ReplaceCharacterPanelProps {
isLoading: boolean;
shots: any[];
character: Character;
role: any;
showAddToLibrary?: boolean;
onClose: () => void;
onConfirm: (selectedShots: string[], addToLibrary: boolean) => void;
@ -40,21 +42,24 @@ export const mockShots: Shot[] = [
];
export function ReplaceCharacterPanel({
shots = [],
character,
isLoading,
shots,
role,
showAddToLibrary = true,
onClose,
onConfirm,
}: ReplaceCharacterPanelProps) {
return (
<ReplacePanel
title="替换新形象"
shots={shots}
item={character}
item={role}
showAddToLibrary={showAddToLibrary}
addToLibraryText="新形象同步添加至角色库"
onClose={onClose}
onConfirm={onConfirm}
isLoading={isLoading}
/>
);
}

View File

@ -4,6 +4,7 @@ import { Check, X, CircleAlert, ArrowLeft, ArrowRight } from 'lucide-react';
import { cn } from '@/public/lib/utils';
interface ReplacePanelProps {
isLoading: boolean;
title: string;
shots: any[];
item: any;
@ -14,6 +15,7 @@ interface ReplacePanelProps {
}
export function ReplacePanel({
isLoading,
title,
shots,
item,
@ -56,11 +58,11 @@ export function ReplacePanel({
}, []);
const handleShotToggle = (shotId: string) => {
setSelectedShots(prev =>
prev.includes(shotId)
? prev.filter(id => id !== shotId)
: [...prev, shotId]
);
// setSelectedShots(prev =>
// prev.includes(shotId)
// ? prev.filter(id => id !== shotId)
// : [...prev, shotId]
// );
};
const handleSelectAllShots = (checked: boolean) => {
@ -104,6 +106,15 @@ export function ReplacePanel({
});
};
if (isLoading) {
return (
<div className="flex flex-col items-center justify-center w-full max-w-5xl text-white/50">
<div className="w-12 h-12 mb-4 animate-spin rounded-full border-b-2 border-blue-600" />
<p>Loading...</p>
</div>
)
}
return (
<div className="space-y-2 w-full max-w-5xl">
{/* 标题 */}
@ -115,7 +126,7 @@ export function ReplacePanel({
<CircleAlert className="w-4 h-4" />
<span className="text-blue-500">{shots.length}</span>
</div>
<div className="flex items-center gap-2">
{/* <div className="flex items-center gap-2">
<input
type="checkbox"
checked={selectedShots.length === shots.length}
@ -123,12 +134,12 @@ export function ReplacePanel({
className="w-4 h-4 rounded border-white/20"
/>
<label className="text-white/80"></label>
</div>
</div> */}
</div>
{/* 分镜展示区 */}
<div className="space-y-2 relative">
<div className="text-white/80 text-sm"></div>
{/* <div className="text-white/80 text-sm">选择需要替换的分镜:</div> */}
<div className="relative flex gap-4 overflow-x-auto pb-4 hide-scrollbar h-64" ref={shotsRef}>
{shots.map((shot) => (
<motion.div