video-flow-b/components/ui/edit-modal.tsx

263 lines
8.6 KiB
TypeScript

'use client';
import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { X, Image, Users, Video, Music, Settings, FileText, Maximize, Minimize } from 'lucide-react';
import { cn } from '@/public/lib/utils';
import ScriptTabContent from './script-tab-content';
import { SceneTabContent } from './scene-tab-content';
import { ShotTabContent } from './shot-tab-content';
import { SettingsTabContent } from './settings-tab-content';
import { CharacterTabContent } from './character-tab-content';
import { MusicTabContent } from './music-tab-content';
interface EditModalProps {
isOpen: boolean;
onClose: () => void;
activeEditTab: string;
taskStatus: string;
taskSketch: any[];
sketchVideo: any[];
taskScenes: any[];
currentSketchIndex: number;
onSketchSelect: (index: number) => void;
roles?: any[];
music?: any;
}
const tabs = [
{ id: '0', label: 'Script', icon: FileText },
{ id: '1', label: 'Character', icon: Users },
{ id: '2', label: 'Scene', icon: Image },
{ id: '3', label: 'Shot', icon: Video },
{ id: '4', label: 'Music', icon: Music },
// { id: '5', label: '剪辑', icon: Scissors },
{ id: 'settings', label: 'Settings', icon: Settings },
];
export function EditModal({
isOpen,
onClose,
activeEditTab,
taskStatus,
taskSketch,
sketchVideo,
taskScenes,
currentSketchIndex,
onSketchSelect,
roles = [],
music
}: EditModalProps) {
const [activeTab, setActiveTab] = useState(activeEditTab);
const [currentIndex, setCurrentIndex] = useState(currentSketchIndex);
const [currentRoleIndex, setCurrentRoleIndex] = useState(0);
useEffect(() => {
setCurrentIndex(currentSketchIndex);
}, [isOpen]);
// 当 activeEditTab 改变时更新 activeTab
useEffect(() => {
setActiveTab(activeEditTab);
}, [activeEditTab]);
const isTabDisabled = (tabId: string) => {
if (tabId === 'settings') return false;
// 换成 如果对应标签下 数据存在 就不禁用
if (tabId === '1') return roles.length === 0;
if (tabId === '2') return taskScenes.length === 0;
if (tabId === '3') return sketchVideo.length === 0;
if (tabId === '4') return false;
return false;
};
const hanldeChangeSelect = (index: number) => {
if (activeTab === '1') {
setCurrentRoleIndex(index);
} else {
setCurrentIndex(index);
}
}
const handleChangeTab = (tabId: string, disabled: boolean) => {
if (disabled) return;
setActiveTab(tabId);
setCurrentIndex(0);
}
const renderTabContent = () => {
switch (activeTab) {
case '0':
return (
<ScriptTabContent />
);
case '1':
return (
<CharacterTabContent
taskSketch={taskSketch}
currentRoleIndex={currentRoleIndex}
onSketchSelect={hanldeChangeSelect}
roles={roles}
/>
);
case '2':
return (
<SceneTabContent
taskSketch={taskScenes}
currentSketchIndex={currentIndex}
onSketchSelect={hanldeChangeSelect}
/>
);
case '3':
return (
<ShotTabContent
taskSketch={sketchVideo}
currentSketchIndex={currentIndex}
onSketchSelect={hanldeChangeSelect}
isPlaying={false}
/>
);
case '4':
return (
<MusicTabContent
taskSketch={taskSketch}
currentSketchIndex={currentIndex}
onSketchSelect={onSketchSelect}
music={music}
/>
);
case 'settings':
return (
<SettingsTabContent
onSettingChange={(key, value) => {
console.log('Setting changed:', key, value);
// TODO: 实现设置更新逻辑
}}
/>
);
default:
return (
<div className="min-h-[400px] flex items-center justify-center text-white/50">
{tabs.find(tab => tab.id === activeTab)?.label} Content area
</div>
);
}
};
return (
<AnimatePresence>
{isOpen && (
<>
{/* 背景遮罩 */}
<motion.div
className="fixed inset-0 bg-black/60 backdrop-blur-sm z-50"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={onClose}
/>
{/* 弹窗内容 */}
<div className="fixed inset-x-0 bottom-0 z-50 flex justify-center">
<motion.div
className={cn(
"w-[88%] min-w-[888px] h-[95vh] bg-[#1a1b1e] rounded-t-2xl overflow-hidden"
)}
initial={{ y: '100%' }}
animate={{ y: 0 }}
exit={{ y: '100%' }}
transition={{
type: 'spring',
damping: 25,
stiffness: 200,
}}
>
{/* 标签栏 */}
<div className="flex items-center gap-1 p-2 overflow-x-auto hide-scrollbar bg-white/5">
<div className="flex-1 flex gap-1">
{tabs.map((tab) => {
const Icon = tab.icon;
const disabled = isTabDisabled(tab.id);
return (
<motion.button
key={tab.id}
className={cn(
'flex items-center gap-2 px-4 py-2 rounded-lg transition-colors relative',
activeTab === tab.id ? 'text-white' : 'text-white/50',
disabled ? 'opacity-50 cursor-not-allowed' : 'hover:bg-white/10',
)}
onClick={() => handleChangeTab(tab.id, disabled)}
whileHover={disabled ? undefined : { scale: 1.02 }}
whileTap={disabled ? undefined : { scale: 0.98 }}
>
<Icon className="w-4 h-4" />
<span>{tab.label}</span>
{activeTab === tab.id && (
<motion.div
className="absolute bottom-0 left-0 right-0 h-0.5 bg-blue-500"
layoutId="activeTab"
transition={{ type: 'spring', damping: 20 }}
/>
)}
</motion.button>
);
})}
</div>
{/* 弹窗操作按钮 */}
<div className="pl-4 border-l border-white/10">
{/* 关闭按钮 */}
<motion.button
className="p-2 rounded-full hover:bg-white/10 transition-colors"
onClick={onClose}
whileHover={{ rotate: 90 }}
whileTap={{ scale: 0.9 }}
>
<X className="w-5 h-5 text-white/70" />
</motion.button>
</div>
</div>
{/* 内容区域 */}
<div className="overflow-y-auto p-4" style={{ height: 'calc(100% - 8rem)' }}>
<AnimatePresence mode="wait">
<motion.div
key={activeTab}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -20 }}
transition={{ duration: 0.2 }}
className="h-full"
>
{renderTabContent()}
</motion.div>
</AnimatePresence>
</div>
{/* 底部操作栏 */}
<div className="p-4 border-t border-white/10 bg-black/20">
<div className="flex justify-end gap-3">
<motion.button
className="px-4 py-2 rounded-lg bg-white/10 text-white hover:bg-white/20 transition-colors"
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
onClick={onClose}
>
Reset
</motion.button>
<motion.button
className="px-4 py-2 rounded-lg bg-blue-500 text-white hover:bg-blue-600 transition-colors"
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
>
Save
</motion.button>
</div>
</div>
</motion.div>
</div>
</>
)}
</AnimatePresence>
);
}