diff --git a/components/SmartChatBox/useMessages.ts b/components/SmartChatBox/useMessages.ts index 79f2312..a3e5de4 100644 --- a/components/SmartChatBox/useMessages.ts +++ b/components/SmartChatBox/useMessages.ts @@ -267,6 +267,15 @@ export function useMessages({ config, onMessagesUpdate }: UseMessagesProps): [Me } }, [updateMessages]); + // 监听 消息列表中是否存在 pending 状态的消息,有的话 将 loading 置为 true + useEffect(() => { + if (displayMessages.some(msg => msg.status === 'pending')) { + setIsLoading(true); + } else { + setIsLoading(false); + } + }, [displayMessages]); + return [ { messages: displayMessages, diff --git a/components/pages/work-flow.tsx b/components/pages/work-flow.tsx index 1eb56e8..edd8e2f 100644 --- a/components/pages/work-flow.tsx +++ b/components/pages/work-flow.tsx @@ -216,7 +216,7 @@ const WorkFlow = React.memo(function WorkFlow() { {isLoading ? ( ) : ( -
+
)}
- {taskObject.currentStage !== 'final_video' && taskObject.currentStage !== 'script' && ( + {taskObject.currentStage !== 'script' && (
{ if (isSmartChatBoxOpen) { @@ -364,6 +365,14 @@ export const MediaViewer = React.memo(function MediaViewer({ onClick={() => handleEditClick('3', 'final')} /> + {/* 下载所有视频按钮 */} + + { + setIsLoadingDownloadAllVideosBtn(true); + await downloadAllVideos(taskObject.videos.data.flatMap((video: any) => video.urls)); + setIsLoadingDownloadAllVideosBtn(false); + }} /> + {/* 下载按钮 */} { @@ -493,6 +502,14 @@ export const MediaViewer = React.memo(function MediaViewer({ } }} /> + {/* 下载所有视频按钮 */} + + { + setIsLoadingDownloadAllVideosBtn(true); + await downloadAllVideos(taskObject.videos.data.flatMap((video: any) => video.urls)); + setIsLoadingDownloadAllVideosBtn(false); + }} /> + {/* 下载按钮 */} { diff --git a/components/pages/work-flow/thumbnail-grid.tsx b/components/pages/work-flow/thumbnail-grid.tsx index 11e279c..5a4636c 100644 --- a/components/pages/work-flow/thumbnail-grid.tsx +++ b/components/pages/work-flow/thumbnail-grid.tsx @@ -15,6 +15,9 @@ interface ThumbnailGridProps { onRetryVideo: (video_id: string) => void; } +/** + * 视频缩略图网格组件,支持hover时播放视频预览 + */ export function ThumbnailGrid({ isDisabledFocus, taskObject, @@ -22,6 +25,17 @@ export function ThumbnailGrid({ onSketchSelect, onRetryVideo }: ThumbnailGridProps) { + const [hoveredIndex, setHoveredIndex] = useState(null); + + /** 处理鼠标进入缩略图事件 */ + const handleMouseEnter = useCallback((index: number) => { + setHoveredIndex(index); + }, []); + + /** 处理鼠标离开缩略图事件 */ + const handleMouseLeave = useCallback((index: number) => { + setHoveredIndex(null); + }, []); const thumbnailsRef = useRef(null); const [isDragging, setIsDragging] = useState(false); const [startX, setStartX] = useState(0); @@ -159,13 +173,8 @@ export function ThumbnailGrid({ console.log('taskObject.currentStage_thumbnail-grid', taskObject.currentStage); }, [taskObject.currentStage]); - // 粗剪/精剪最终成片阶段不显示缩略图 - if (taskObject.currentStage === 'final_video') { - return null; - } - // 渲染视频阶段的缩略图 - const renderVideoThumbnails = () => ( + const renderVideoThumbnails = (disabled: boolean = false) => ( taskObject.videos.data.map((video, index) => { const urls: string = video.urls ? video.urls.join(',') : ''; @@ -173,8 +182,8 @@ export function ThumbnailGrid({
!isDragging && onSketchSelect(index)} + ${(currentSketchIndex === index && !disabled) ? 'ring-2 ring-blue-500 z-10' : 'hover:ring-2 hover:ring-blue-500/50'}`} + onClick={() => !isDragging && !disabled && onSketchSelect(index)} > {/* 视频层 */} @@ -202,11 +211,28 @@ export function ThumbnailGrid({ // loop // muted // /> - +
handleMouseEnter(index)} + onMouseLeave={() => handleMouseLeave(index)} + > + video thumbnail + {hoveredIndex === index && ( +
) : (
@@ -316,6 +342,7 @@ export function ThumbnailGrid({ > {taskObject.currentStage === 'video' && renderVideoThumbnails()} {(taskObject.currentStage === 'scene' || taskObject.currentStage === 'character') && renderSketchThumbnails(getCurrentData())} + {taskObject.currentStage === 'final_video' && renderVideoThumbnails(true)}
); } diff --git a/components/pages/work-flow/use-workflow-data.tsx b/components/pages/work-flow/use-workflow-data.tsx index 11971b5..ab3f6d2 100644 --- a/components/pages/work-flow/use-workflow-data.tsx +++ b/components/pages/work-flow/use-workflow-data.tsx @@ -716,7 +716,6 @@ export function useWorkflowData({ onEditPlanGenerated }: UseWorkflowDataProps = applyScript, fallbackToStep, originalText: state.originalText, - // showGotoCutButton: from && currentLoadingText.includes('Post-production') ? true : false, showGotoCutButton: (canGoToCut && (isGenerateEditPlan || taskObject.currentStage === 'final_video') || isShowError) ? true : false, generateEditPlan: openEditPlan, handleRetryVideo diff --git a/utils/tools.ts b/utils/tools.ts index 7a79fd3..8bc2f84 100644 --- a/utils/tools.ts +++ b/utils/tools.ts @@ -93,4 +93,14 @@ export const downloadVideo = async (url: string) => { } catch (error) { console.error('下载视频失败:', error); } +}; + +/** + * 下载所有视频 + * @param urls 视频URL列表 + */ +export const downloadAllVideos = async (urls: string[]) => { + for (const url of urls) { + await downloadVideo(url); + } }; \ No newline at end of file