forked from 77media/video-flow
263 lines
8.6 KiB
TypeScript
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 { VideoTabContent } from './video-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 (
|
|
<VideoTabContent
|
|
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>
|
|
);
|
|
}
|