更新 ScriptTabContent 以支持新的剧本数据结构和属性设置逻辑。

This commit is contained in:
北枳 2025-08-07 19:50:15 +08:00
parent e6ed351cfe
commit 7568336be6
4 changed files with 76 additions and 90 deletions

View File

@ -7,6 +7,7 @@ interface AISuggestionBarProps {
suggestions: string[]; suggestions: string[];
onSuggestionClick: (suggestion: string) => void; onSuggestionClick: (suggestion: string) => void;
onSubmit: (text: string) => void; onSubmit: (text: string) => void;
onFocus: () => void;
placeholder?: string; placeholder?: string;
} }
@ -14,6 +15,7 @@ export function AISuggestionBar({
suggestions, suggestions,
onSuggestionClick, onSuggestionClick,
onSubmit, onSubmit,
onFocus,
placeholder = "输入你的想法,或点击预设词条获取 AI 建议..." placeholder = "输入你的想法,或点击预设词条获取 AI 建议..."
}: AISuggestionBarProps) { }: AISuggestionBarProps) {
const [inputText, setInputText] = useState(''); const [inputText, setInputText] = useState('');
@ -177,6 +179,7 @@ export function AISuggestionBar({
if (isCollapsed) { if (isCollapsed) {
toggleCollapse(); toggleCollapse();
} }
onFocus();
}} }}
onBlur={() => setIsFocused(false)} onBlur={() => setIsFocused(false)}
placeholder={isCollapsed ? "点击展开..." : placeholder} placeholder={isCollapsed ? "点击展开..." : placeholder}

View File

@ -248,6 +248,7 @@ export default function WorkFlow() {
suggestions={mockSuggestions} suggestions={mockSuggestions}
onSuggestionClick={handleSuggestionClick} onSuggestionClick={handleSuggestionClick}
onSubmit={handleSubmit} onSubmit={handleSubmit}
onFocus={() => setIsPauseWorkFlow(true)}
placeholder="Please input your ideas, or click the predefined tags to receive AI advice..." placeholder="Please input your ideas, or click the predefined tags to receive AI advice..."
/> />
</ErrorBoundary> </ErrorBoundary>
@ -265,6 +266,10 @@ export default function WorkFlow() {
onSketchSelect={setCurrentSketchIndex} onSketchSelect={setCurrentSketchIndex}
roles={roles} roles={roles}
music={music} music={music}
scriptData={scriptData}
setIsPauseWorkFlow={setIsPauseWorkFlow}
setAnyAttribute={setAnyAttribute}
isPauseWorkFlow={isPauseWorkFlow}
/> />
</ErrorBoundary> </ErrorBoundary>
</div> </div>

View File

@ -1,10 +1,10 @@
'use client'; 'use client';
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect, SetStateAction } from 'react';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { X, Image, Users, Video, Music, Settings, FileText, Maximize, Minimize } from 'lucide-react'; import { X, Image, Users, Video, Music, Settings, FileText, Maximize, Minimize } from 'lucide-react';
import { cn } from '@/public/lib/utils'; import { cn } from '@/public/lib/utils';
import ScriptTabContent from './script-tab-content'; import { ScriptTabContent } from './script-tab-content';
import { SceneTabContent } from './scene-tab-content'; import { SceneTabContent } from './scene-tab-content';
import { ShotTabContent } from './shot-tab-content'; import { ShotTabContent } from './shot-tab-content';
import { SettingsTabContent } from './settings-tab-content'; import { SettingsTabContent } from './settings-tab-content';
@ -23,6 +23,10 @@ interface EditModalProps {
onSketchSelect: (index: number) => void; onSketchSelect: (index: number) => void;
roles?: any[]; roles?: any[];
music?: any; music?: any;
setIsPauseWorkFlow: (isPauseWorkFlow: boolean) => void;
setAnyAttribute: (type: string, value: SetStateAction<string>, tags?: string[]) => void;
isPauseWorkFlow: boolean;
scriptData: any[] | null;
} }
const tabs = [ const tabs = [
@ -46,7 +50,11 @@ export function EditModal({
currentSketchIndex, currentSketchIndex,
onSketchSelect, onSketchSelect,
roles = [], roles = [],
music music,
setIsPauseWorkFlow,
setAnyAttribute,
isPauseWorkFlow,
scriptData
}: EditModalProps) { }: EditModalProps) {
const [activeTab, setActiveTab] = useState(activeEditTab); const [activeTab, setActiveTab] = useState(activeEditTab);
const [currentIndex, setCurrentIndex] = useState(currentSketchIndex); const [currentIndex, setCurrentIndex] = useState(currentSketchIndex);
@ -89,7 +97,12 @@ export function EditModal({
switch (activeTab) { switch (activeTab) {
case '0': case '0':
return ( return (
<ScriptTabContent /> <ScriptTabContent
scriptData={scriptData}
setIsPauseWorkFlow={setIsPauseWorkFlow}
setAnyAttribute={setAnyAttribute}
isPauseWorkFlow={isPauseWorkFlow}
/>
); );
case '1': case '1':
return ( return (

View File

@ -1,97 +1,62 @@
import React, { useState, useCallback, useEffect } from 'react'; import React, { useState, useCallback, useEffect, SetStateAction } from 'react';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { X, Check, ChevronDown } from 'lucide-react'; import { FileText } from 'lucide-react';
import * as Popover from '@radix-ui/react-popover'; import { ScriptRenderer } from '@/components/script-renderer/ScriptRenderer';
import { mockSceneOptions, mockCharacterOptions } from '@/app/model/enums';
import { Button } from './button';
import { Input } from './input';
const ScriptTabContent: React.FC = () => {
// 获取当前项目ID这里需要根据实际项目路由或上下文获取
const projectId = 'current-project-id'; // TODO: 从路由或上下文获取实际项目ID
// 组件挂载时获取项目剧本数据 interface ScriptTabContentProps {
useEffect(() => { scriptData: any[] | null;
const initializeScript = async () => { setIsPauseWorkFlow: (isPauseWorkFlow: boolean) => void;
try { setAnyAttribute: (type: string, value: SetStateAction<string>, tags?: string[]) => void;
await fetchProjectScript(projectId); isPauseWorkFlow: boolean;
} catch (error) { }
console.error('初始化剧本数据失败:', error);
}
};
initializeScript(); export function ScriptTabContent({
}, [projectId, fetchProjectScript]); scriptData = [],
setIsPauseWorkFlow,
// 处理AI生成按钮点击 setAnyAttribute,
const handleAiGenerate = useCallback(async () => { isPauseWorkFlow
if (!userPrompt.trim()) return; }: ScriptTabContentProps) {
try {
await fetchScriptData(userPrompt);
} catch (error) {
console.error('生成剧本失败:', error);
}
}, [userPrompt, fetchScriptData]);
// 处理重置按钮点击
const handleReset = useCallback(() => {
resetScript();
}, [resetScript]);
// 处理确认按钮点击
const handleConfirm = useCallback(async () => {
try {
await applyScript();
} catch (error) {
console.error('应用剧本失败:', error);
}
}, [applyScript]);
// 处理提示词输入变化
const handlePromptChange = useCallback((value: string) => {
updateUserPrompt(value);
}, [updateUserPrompt]);
// 处理剧本片段文本变化
const handleScriptSliceChange = useCallback((sliceId: string, text: string) => {
setFocusedSlice(sliceId);
updateScriptSliceText(text);
}, [setFocusedSlice, updateScriptSliceText]);
// 如果没有数据,显示空状态
if (!scriptData || scriptData.length === 0) {
return (
<div className="flex flex-col items-center justify-center min-h-[400px] text-white/50">
<FileText className="w-16 h-16 mb-4" />
<p>No script data</p>
</div>
);
}
return ( return (
<div className="flex flex-col h-full"> <div className="flex flex-col h-full">
<motion.div <motion.div
className="relative w-full h-[90vh] backdrop-blur-xl rounded-2xl shadow-2xl overflow-hidden flex flex-col" className="relative w-full h-[90vh] backdrop-blur-xl rounded-2xl shadow-2xl overflow-hidden flex flex-col"
initial={{ scale: 0.95, y: 10, opacity: 0 }} initial={{ scale: 0.95, y: 10, opacity: 0 }}
animate={{ animate={{
scale: 1, scale: 1,
y: 0, y: 0,
opacity: 1, opacity: 1,
transition: { transition: {
type: "spring", type: "spring",
duration: 0.3, duration: 0.3,
bounce: 0.15, bounce: 0.15,
stiffness: 300, stiffness: 300,
damping: 25 damping: 25
} }
}} }}
exit={{ exit={{
scale: 0.95, scale: 0.95,
y: 10, y: 10,
opacity: 0, opacity: 0,
transition: { transition: {
type: "tween", type: "tween",
duration: 0.1, duration: 0.1,
ease: "easeOut" ease: "easeOut"
} }
}} }}
> >
<ScriptRenderer data={scriptData} setIsPauseWorkFlow={setIsPauseWorkFlow} setAnyAttribute={setAnyAttribute} isPauseWorkFlow={isPauseWorkFlow} />
</motion.div> </motion.div>
</div> </div>
); );
}; };
export default ScriptTabContent;