diff --git a/components/ChatInputBox/ChatInputBox.tsx b/components/ChatInputBox/ChatInputBox.tsx index 79e65f8..673bf0b 100644 --- a/components/ChatInputBox/ChatInputBox.tsx +++ b/components/ChatInputBox/ChatInputBox.tsx @@ -27,6 +27,7 @@ import { AudioRecorder } from "./AudioRecorder"; import { useTemplateStoryServiceHook } from "@/app/service/Interaction/templateStoryService"; import { useRouter } from "next/navigation"; import { createMovieProjectV1 } from "@/api/video_flow"; +import { createScriptEpisodeNew } from "@/api/script_episode"; import { useLoadScriptText, useUploadFile } from "@/app/service/domain/service"; import { ActionButton } from "../common/ActionButton"; import { HighlightEditor } from "../common/HighlightEditor"; @@ -608,7 +609,7 @@ export function ChatInputBox() { }; // 调用创建剧集API - const episodeResponse = await createMovieProjectV1(episodeData); + const episodeResponse = await createScriptEpisodeNew(episodeData); console.log("episodeResponse", episodeResponse); if (episodeResponse.code !== 0) { console.error(`创建剧集失败: ${episodeResponse.message}`); diff --git a/components/SmartChatBox/InputBar.tsx b/components/SmartChatBox/InputBar.tsx index c95d197..b9c9d26 100644 --- a/components/SmartChatBox/InputBar.tsx +++ b/components/SmartChatBox/InputBar.tsx @@ -1,29 +1,48 @@ -import React, { useRef, useState } from "react"; +import React, { useRef, useState, useEffect } from "react"; import { Image as ImageIcon, Send, Trash2 } from "lucide-react"; import { MessageBlock } from "./types"; import { useUploadFile } from "@/app/service/domain/service"; interface InputBarProps { - onSend: (blocks: MessageBlock[]) => void; + onSend: (blocks: MessageBlock[], videoId?: string) => void; + setVideoPreview?: (url: string, id: string) => void; + initialVideoUrl?: string; + initialVideoId?: string; } -export function InputBar({ onSend }: InputBarProps) { +export function InputBar({ onSend, setVideoPreview, initialVideoUrl, initialVideoId }: InputBarProps) { const [text, setText] = useState(""); const [isUploading, setIsUploading] = useState(false); const [uploadProgress, setUploadProgress] = useState(0); const [imageUrl, setImageUrl] = useState(null); + const [videoUrl, setVideoUrl] = useState(initialVideoUrl || null); + const [videoId, setVideoId] = useState(initialVideoId || null); const { uploadFile } = useUploadFile(); + // 监听初始视频 URL 和 ID 的变化 + useEffect(() => { + if (initialVideoUrl && initialVideoId) { + setVideoUrl(initialVideoUrl); + setVideoId(initialVideoId); + } + }, [initialVideoUrl, initialVideoId]); + const handleSend = () => { const blocks: MessageBlock[] = []; if (text.trim()) blocks.push({ type: "text" as const, text: text.trim() }); if (imageUrl) blocks.push({ type: "image" as const, url: imageUrl }); + if (videoUrl) blocks.push({ type: "video" as const, url: videoUrl }); if (!blocks.length) return; - onSend(blocks); + onSend(blocks, videoId || undefined); setText(""); setImageUrl(null); + if (videoUrl && videoId && setVideoPreview) { + setVideoPreview(videoUrl, videoId); + } + setVideoUrl(null); + setVideoId(null); }; const handleFileUpload = async (file: File) => { @@ -32,7 +51,17 @@ export function InputBar({ onSend }: InputBarProps) { const url = await uploadFile(file, (progress) => { setUploadProgress(progress); }); + // 如果已经有视频,先保存视频状态 + const prevVideoUrl = videoUrl; + const prevVideoId = videoId; + setImageUrl(url); + + // 恢复视频状态 + if (prevVideoUrl && prevVideoId) { + setVideoUrl(prevVideoUrl); + setVideoId(prevVideoId); + } } catch (error) { console.error("上传失败:", error); // 可以添加错误提示 @@ -65,9 +94,10 @@ export function InputBar({ onSend }: InputBarProps) { return (
- {/* 图片预览 */} - {imageUrl && ( -
+ {/* 媒体预览 */} +
+ {/* 图片预览 */} + {imageUrl && (
-
- )} + )} + + {/* 视频预览 */} + {videoUrl && ( +
+
+ )} +
{/* 上传进度 */} {isUploading && ( @@ -134,7 +189,7 @@ export function InputBar({ onSend }: InputBarProps) { onClick={handleSend} className="inline-flex items-center gap-2 p-2 my-2 rounded-full bg-blue-500 hover:bg-blue-400 text-white shadow disabled:bg-gray-500 disabled:hover:bg-gray-500 disabled:cursor-not-allowed" data-alt="send-button" - disabled={!text.trim() && !imageUrl} + disabled={!text.trim() && !imageUrl && !videoUrl} > diff --git a/components/SmartChatBox/SmartChatBox.tsx b/components/SmartChatBox/SmartChatBox.tsx index e486930..6beeb62 100644 --- a/components/SmartChatBox/SmartChatBox.tsx +++ b/components/SmartChatBox/SmartChatBox.tsx @@ -13,6 +13,9 @@ interface SmartChatBoxProps { setIsSmartChatBoxOpen: (v: boolean) => void; projectId: string; userId: number; + previewVideoUrl?: string | null; + previewVideoId?: string | null; + onClearPreview?: () => void; } interface MessageGroup { @@ -32,7 +35,15 @@ function BackToLatestButton({ onClick }: { onClick: () => void }) { ); } -export default function SmartChatBox({ isSmartChatBoxOpen, setIsSmartChatBoxOpen, projectId, userId }: SmartChatBoxProps) { +export default function SmartChatBox({ + isSmartChatBoxOpen, + setIsSmartChatBoxOpen, + projectId, + userId, + previewVideoUrl, + previewVideoId, + onClearPreview +}: SmartChatBoxProps) { // 消息列表引用 const listRef = useRef(null); const [isAtBottom, setIsAtBottom] = useState(true); @@ -52,6 +63,11 @@ export default function SmartChatBox({ isSmartChatBoxOpen, setIsSmartChatBoxOpen checkIfAtBottom(); }, [checkIfAtBottom]); + useEffect(() => { + console.log('previewVideoUrl', previewVideoUrl); + console.log('previewVideoId', previewVideoId); + }, [previewVideoUrl, previewVideoId]); + // 监听滚动事件 useEffect(() => { const listElement = listRef.current; @@ -170,7 +186,16 @@ export default function SmartChatBox({ isSmartChatBoxOpen, setIsSmartChatBoxOpen
{/* Input */} - + { + if (url === previewVideoUrl && id === previewVideoId) { + onClearPreview?.(); + } + }} + initialVideoUrl={previewVideoUrl || undefined} + initialVideoId={previewVideoId || undefined} + /> ); } \ No newline at end of file diff --git a/components/SmartChatBox/api.ts b/components/SmartChatBox/api.ts index 9795258..e1a612c 100644 --- a/components/SmartChatBox/api.ts +++ b/components/SmartChatBox/api.ts @@ -519,11 +519,13 @@ export async function fetchMessages( */ export async function sendMessage( blocks: MessageBlock[], - config: ChatConfig + config: ChatConfig, + videoId?: string ): Promise { - // 提取文本和图片 + // 提取文本、图片和视频 const textBlocks = blocks.filter(b => b.type === "text"); const imageBlocks = blocks.filter(b => b.type === "image"); + const videoBlocks = blocks.filter(b => b.type === "video"); const request: SendMessageRequest = { session_id: `project_${config.projectId}_user_${config.userId}`, @@ -537,6 +539,16 @@ export async function sendMessage( request.image_url = (imageBlocks[0] as { url: string }).url; } + // 如果有视频,添加视频URL + if (videoBlocks.length > 0) { + request.video_url = (videoBlocks[0] as { url: string }).url; + } + + // 如果有视频ID,添加到请求中 + if (videoId) { + request.video_id = videoId; + } + try { console.log('发送消息请求:', request); await post>("/intelligent/chat", request); diff --git a/components/SmartChatBox/types.ts b/components/SmartChatBox/types.ts index a71dad0..66a2fbb 100644 --- a/components/SmartChatBox/types.ts +++ b/components/SmartChatBox/types.ts @@ -54,6 +54,8 @@ export interface SendMessageRequest { session_id: string; user_input: string; image_url?: string; + video_id?: string; + video_url?: string; project_id: string; user_id: string; } diff --git a/components/SmartChatBox/useMessages.ts b/components/SmartChatBox/useMessages.ts index 0699e15..2f679c4 100644 --- a/components/SmartChatBox/useMessages.ts +++ b/components/SmartChatBox/useMessages.ts @@ -135,7 +135,7 @@ export function useMessages({ config, onMessagesUpdate }: UseMessagesProps): [Me }, [latestMessages, filterMessages, onMessagesUpdate]); // 发送消息 - const handleSendMessage = useCallback(async (blocks: MessageBlock[]) => { + const handleSendMessage = useCallback(async (blocks: MessageBlock[], videoId?: string) => { setIsLoading(true); setError(null); @@ -166,7 +166,7 @@ export function useMessages({ config, onMessagesUpdate }: UseMessagesProps): [Me }); // 发送到服务器 - await sendMessage(blocks, configRef.current); + await sendMessage(blocks, configRef.current, videoId); // 立即获取最新的消息列表 await updateMessages(false); diff --git a/components/pages/work-flow.tsx b/components/pages/work-flow.tsx index de947cf..b066504 100644 --- a/components/pages/work-flow.tsx +++ b/components/pages/work-flow.tsx @@ -26,6 +26,8 @@ const WorkFlow = React.memo(function WorkFlow() { const [isEditModalOpen, setIsEditModalOpen] = React.useState(false); const [activeEditTab, setActiveEditTab] = React.useState('1'); const [isSmartChatBoxOpen, setIsSmartChatBoxOpen] = React.useState(true); + const [previewVideoUrl, setPreviewVideoUrl] = React.useState(null); + const [previewVideoId, setPreviewVideoId] = React.useState(null); const searchParams = useSearchParams(); const episodeId = searchParams.get('episodeId') || ''; @@ -145,6 +147,11 @@ const WorkFlow = React.memo(function WorkFlow() { isPauseWorkFlow={isPauseWorkFlow} applyScript={applyScript} mode={mode} + onOpenChat={() => setIsSmartChatBoxOpen(true)} + setVideoPreview={(url, id) => { + setPreviewVideoUrl(url); + setPreviewVideoId(id); + }} /> @@ -227,6 +234,12 @@ const WorkFlow = React.memo(function WorkFlow() { setIsSmartChatBoxOpen={setIsSmartChatBoxOpen} projectId={episodeId} userId={userId} + previewVideoUrl={previewVideoUrl} + previewVideoId={previewVideoId} + onClearPreview={() => { + setPreviewVideoUrl(null); + setPreviewVideoId(null); + }} /> diff --git a/components/pages/work-flow/media-viewer.tsx b/components/pages/work-flow/media-viewer.tsx index 3c889fc..15b4110 100644 --- a/components/pages/work-flow/media-viewer.tsx +++ b/components/pages/work-flow/media-viewer.tsx @@ -9,6 +9,8 @@ import { ScriptRenderer } from '@/components/script-renderer/ScriptRenderer'; import { mockScriptData } from '@/components/script-renderer/mock'; import { Skeleton } from '@/components/ui/skeleton'; import { TaskObject } from '@/api/DTO/movieEdit'; +import { Button, Tooltip } from 'antd'; +import { Video } from 'lucide-react'; interface MediaViewerProps { taskObject: TaskObject; @@ -22,6 +24,8 @@ interface MediaViewerProps { isPauseWorkFlow: boolean; applyScript: any; mode: string; + onOpenChat?: () => void; + setVideoPreview?: (url: string, id: string) => void; } export const MediaViewer = React.memo(function MediaViewer({ @@ -35,7 +39,9 @@ export const MediaViewer = React.memo(function MediaViewer({ setAnyAttribute, isPauseWorkFlow, applyScript, - mode + mode, + onOpenChat, + setVideoPreview }: MediaViewerProps) { const mainVideoRef = useRef(null); const finalVideoRef = useRef(null); @@ -447,28 +453,47 @@ export const MediaViewer = React.memo(function MediaViewer({ {/* 视频 多个 取第一个 */} { taskObject.videos.data[currentSketchIndex].urls && ( - - + <> + + + + {/* 添加到chat去编辑 按钮 */} + + + + )} {/* 操作按钮组 */}