diff --git a/app/service/Interaction/ScriptService.ts b/app/service/Interaction/ScriptService.ts index bcf2d4a..c7cf145 100644 --- a/app/service/Interaction/ScriptService.ts +++ b/app/service/Interaction/ScriptService.ts @@ -385,7 +385,7 @@ export const useScriptService = (): UseScriptService => { const scriptBlocksMemo = useMemo((): ScriptBlock[] => { return [ parseScriptBlock('synopsis', 'Logline', synopsis || ''), - parseScriptBlock('categories', 'GENRE', categories.join(', ') || ''), + parseScriptBlock('categories', 'GENRE', categories.join(', ') || '', 'tag'), parseScriptBlock('protagonist', 'Core Identity', protagonist || ''), parseScriptBlock('incitingIncident', 'The Inciting Incident', incitingIncident || ''), parseScriptBlock('problem', 'The Problem & New Goal', problem || ''), diff --git a/app/service/domain/service.ts b/app/service/domain/service.ts index 4a05ed3..8b9a3c4 100644 --- a/app/service/domain/service.ts +++ b/app/service/domain/service.ts @@ -1,4 +1,4 @@ -import { ScriptBlock, ScriptData } from "@/components/script-renderer/types"; + import { ScriptEditKey } from "../usecase/ScriptEditUseCase"; /** * 渲染数据转换器 @@ -10,15 +10,15 @@ import { ScriptEditKey } from "../usecase/ScriptEditUseCase"; export function parseScriptBlock( key: ScriptEditKey, headerName: string, - scriptText: string -): ScriptBlock { + scriptText: string, + contentType?: 'paragraph' | 'bold' | 'italic' | 'heading' | 'tag', +) { return { id: key, title: headerName, - type: "core", content: [ { - type: "paragraph", + type: contentType || "paragraph", text: scriptText, }, ], diff --git a/components/pages/work-flow.tsx b/components/pages/work-flow.tsx index 8b28dec..c995c56 100644 --- a/components/pages/work-flow.tsx +++ b/components/pages/work-flow.tsx @@ -13,6 +13,7 @@ import { usePlaybackControls } from "./work-flow/use-playback-controls"; import { AlertCircle, RefreshCw, Pause, Play, ChevronLast } from "lucide-react"; import { motion } from "framer-motion"; import { GlassIconButton } from '@/components/ui/glass-icon-button'; +import { useScriptData } from "./work-flow/use-script-data"; export default function WorkFlow() { const containerRef = useRef(null); @@ -22,7 +23,6 @@ export default function WorkFlow() { // 使用自定义 hooks 管理状态 const { taskObject, - scriptData, taskSketch, taskScenes, taskShotSketch, @@ -57,6 +57,10 @@ export default function WorkFlow() { playTimerRef, } = usePlaybackControls(taskSketch, taskVideos, currentStep); + const { + scriptData + } = useScriptData(); + // 跟踪是否已经自动开始播放过,避免重复触发 const hasAutoStartedRef = useRef(false); @@ -188,12 +192,13 @@ export default function WorkFlow() { onToggleVideoPlay={toggleVideoPlay} onTogglePlay={togglePlay} final={final} + setIsPauseWorkFlow={setIsPauseWorkFlow} /> )} -
+
void; onTogglePlay: () => void; final?: any; + setIsPauseWorkFlow: (isPause: boolean) => void; } export function MediaViewer({ @@ -42,7 +43,8 @@ export function MediaViewer({ onEditModalOpen, onToggleVideoPlay, onTogglePlay, - final + final, + setIsPauseWorkFlow }: MediaViewerProps) { const mainVideoRef = useRef(null); const finalVideoRef = useRef(null); @@ -806,7 +808,7 @@ export function MediaViewer({
{ mockScriptData ? ( - + ) : (
diff --git a/components/pages/work-flow/use-script-data.tsx b/components/pages/work-flow/use-script-data.tsx new file mode 100644 index 0000000..c2a7d0e --- /dev/null +++ b/components/pages/work-flow/use-script-data.tsx @@ -0,0 +1,44 @@ + + +import { useEffect, useState } from "react"; +import { useScriptService } from "@/app/service/Interaction/ScriptService"; +import { useSearchParams } from 'next/navigation'; + +export const useScriptData = () => { + const searchParams = useSearchParams(); + const projectId = searchParams.get('episodeId') || ''; + + const { + loading, // 加载状态 + synopsis, //故事梗概 + categories, //故事分类 + protagonist, //主角 + incitingIncident, //激励事件 + problem, //问题与新目标 + conflict, //冲突与障碍 + stakes, //赌注 + characterArc, //人物弧线完成 + planId, //计划ID + aiOptimizing, //AI优化要求 + scriptBlocksMemo, // 渲染数据 + initializeFromProject, + } = useScriptService(); + + const [scriptData, setScriptData] = useState(null); + + // 初始化剧本 + useEffect(() => { + initializeFromProject(projectId); + }, []); + + // 监听剧本加载完毕 + useEffect(() => { + if (!loading) { + console.log('scriptBlocksMemo', scriptBlocksMemo); + } + }, [loading, scriptBlocksMemo]); + + return { + scriptData + } +} \ No newline at end of file diff --git a/components/pages/work-flow/use-workflow-data.tsx b/components/pages/work-flow/use-workflow-data.tsx index f263aaa..0b4db21 100644 --- a/components/pages/work-flow/use-workflow-data.tsx +++ b/components/pages/work-flow/use-workflow-data.tsx @@ -57,7 +57,6 @@ export function useWorkflowData() { // 更新 taskObject 的类型 const [taskObject, setTaskObject] = useState(null); - const [scriptData, setScriptData] = useState(null); const [taskSketch, setTaskSketch] = useState([]); const [taskScenes, setTaskScenes] = useState([]); const [taskShotSketch, setTaskShotSketch] = useState([]); @@ -557,7 +556,6 @@ export function useWorkflowData() { return { taskObject, - scriptData, taskSketch, taskScenes, taskShotSketch, diff --git a/components/script-renderer/ScriptRenderer.tsx b/components/script-renderer/ScriptRenderer.tsx index 62ffe98..cb43b45 100644 --- a/components/script-renderer/ScriptRenderer.tsx +++ b/components/script-renderer/ScriptRenderer.tsx @@ -9,9 +9,10 @@ import { TypewriterText } from '@/components/workflow/work-office/common/Typewri interface ScriptRendererProps { data: ScriptData; + setIsPauseWorkFlow: (isPause: boolean) => void; } -export const ScriptRenderer: React.FC = ({ data }) => { +export const ScriptRenderer: React.FC = ({ data, setIsPauseWorkFlow }) => { const [activeBlockId, setActiveBlockId] = useState(null); const [hoveredBlockId, setHoveredBlockId] = useState(null); const contentRefs = useRef<{ [key: string]: HTMLDivElement | null }>({}); @@ -21,9 +22,11 @@ export const ScriptRenderer: React.FC = ({ data }) => { const [isInit, setIsInit] = useState(true); useEffect(() => { - const themeBlock = data.blocks.find(block => block.type === 'theme'); - if (themeBlock) { - setAddThemeTag(themeBlock.content.map(item => item.text || '')); + const themeBlock = data.blocks.find(block => block.id === 'categories'); + if (themeBlock && themeBlock.content.length > 0) { + const themeTag = themeBlock.content[0].text.split(',').map(item => item.trim()); + console.log('themeTag', themeTag); + setAddThemeTag(themeTag); } }, [data.blocks]); @@ -118,6 +121,7 @@ export const ScriptRenderer: React.FC = ({ data }) => { }; const handleEditBlock = (block: ScriptBlock) => { + setIsPauseWorkFlow(true); setIsInit(false); setEditBlockId(block.id); setActiveBlockId(block.id); @@ -145,8 +149,8 @@ export const ScriptRenderer: React.FC = ({ data }) => { }; const renderTypeBlock = (block: ScriptBlock, isHovered: boolean, isActive: boolean, isEditing: boolean) => { - switch (block.type) { - case 'theme': + switch (block.id) { + case 'categories': return (
{addThemeTag.map((item, index) => ( @@ -168,7 +172,7 @@ export const ScriptRenderer: React.FC = ({ data }) => { placeholder="Select Theme Type" onChange={(value) => { console.log('主题标签更改', value); - handleThemeTagChange(value); + handleThemeTagChange(value as string[]); }} />
diff --git a/components/script-renderer/mock.ts b/components/script-renderer/mock.ts index 99c5020..559c696 100644 --- a/components/script-renderer/mock.ts +++ b/components/script-renderer/mock.ts @@ -5,7 +5,6 @@ export const mockScriptData: ScriptData = { { id: 'core', title: "SUMMARY", - type: 'core', content: [ { type: 'paragraph', @@ -14,52 +13,28 @@ export const mockScriptData: ScriptData = { ] }, { - id: 'theme', + id: 'categories', title: 'THEME', - type: 'theme', content: [ { type: 'tag', - text: 'Satire' - }, - { - type: 'tag', - text: 'Absurdist Comedy' - }, - { - type: 'tag', - text: 'Disaster' + text: 'Satire, Absurdist Comedy, Disaster' } ] }, { id: 'roles', title: 'ROLES', - type: 'roles', content: [ - { - type: 'bold', - text: 'Anna' - }, { type: 'paragraph', text: 'Anna is a young woman who is trying to find her place in the world. She is a bit of a mess, but she is also a bit of a mess.' - }, - { - type: 'bold', - text: 'Mark' - }, - { - type: 'card', - text: 'Mark starts as resentful and aimless, harboring guilt over past family conflicts. His involvement in Anna’s quest rekindles his sense of responsibility and belonging. He moves from avoidance and cynicism to active support, ultimately risking his own safety for Anna and the creature. Mark’s arc is one of redemption and reconnection.' } ] }, { id: 'scene1', title: 'SCENE 1', - type: 'scene', - sceneNumber: 1, content: [ { type: 'heading', @@ -98,7 +73,6 @@ export const mockScriptData: ScriptData = { { id: 'summary', title: '总结', - type: 'summary', content: [ { type: 'paragraph', diff --git a/components/script-renderer/types.ts b/components/script-renderer/types.ts index c340846..6f89a63 100644 --- a/components/script-renderer/types.ts +++ b/components/script-renderer/types.ts @@ -2,20 +2,12 @@ export interface ScriptBlock { id: string; title: string; content: ScriptContent[]; - type: 'core' | 'scene' | 'summary' | 'theme' | 'roles'; sceneNumber?: number; } export interface ScriptContent { - type: 'paragraph' | 'bold' | 'italic' | 'heading' | 'tag' | 'card'; - text?: string; - roleInfo?: { - name: string; - gender: string; - role: string; - desc: string; - color: string; - }; + type: 'paragraph' | 'bold' | 'italic' | 'heading' | 'tag'; + text: string; } export interface ScriptData { diff --git a/components/ui/script-tab-content.tsx b/components/ui/script-tab-content.tsx index 8759c03..1fbe84b 100644 --- a/components/ui/script-tab-content.tsx +++ b/components/ui/script-tab-content.tsx @@ -102,179 +102,7 @@ const ScriptTabContent: React.FC = () => { } }} > - {/* 标题 */} - -

- Edit Script -

-
- {/* 内容区域 */} - - {/* 剧本片段渲染区域 */} - - {error && ( -
-

{error}

-
- )} - - {/* 剧本片段列表 */} -
- {scriptSlices.map((slice, index) => ( - - handleScriptSliceChange(slice.id, e.target.value)} - placeholder={`输入${slice.type}内容...`} - className="w-full bg-white/50 dark:bg-[#5b75ac20] border border-gray-200 dark:border-gray-600 focus:ring-2 focus:ring-blue-500/20 transition-all duration-200" - /> -
- - {slice.type} - -
-
- ))} -
- - {/* 加载状态 */} - {loading && ( - -
-
- - 正在生成剧本... - -
- - )} - - - {/* 修改建议输入区域 */} - -
-
- handlePromptChange(e.target.value)} - placeholder="输入提示词,然后点击AI生成按钮..." - className="outline-none box-shadow-none bg-white/50 dark:bg-[#5b75ac20] border-0 focus:ring-2 focus:ring-blue-500/20 transition-all duration-200" - disabled={loading} - /> -
- - - -
-
- - - {/* 底部按钮 */} - - - -
);