diff --git a/apps/web/src/pages/generate/components/material/ManualMaterialList.tsx b/apps/web/src/pages/generate/components/material/ManualMaterialList.tsx index dbee4a1b0..240f4cffd 100644 --- a/apps/web/src/pages/generate/components/material/ManualMaterialList.tsx +++ b/apps/web/src/pages/generate/components/material/ManualMaterialList.tsx @@ -1,7 +1,8 @@ /** * 手动选择素材列表 — 竖屏 9:16 卡片网格 + * 交互:默认显示封面,点击播放按钮播放,播放中隐藏按钮,点击视频区域暂停 */ -import React, { useRef, useCallback } from "react" +import React, { useRef, useState, useCallback } from "react" import { Typography } from "antd" import type { AssetItem } from "@/api/assets" @@ -22,37 +23,230 @@ const fmtDuration = (seconds?: number): string => { return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}` } +/** 单个素材卡片 */ +const MaterialCard: React.FC<{ + asset: AssetItem + checked: boolean + onToggle: () => void +}> = ({ asset, checked, onToggle }) => { + const videoRef = useRef(null) + const [isPlaying, setIsPlaying] = useState(false) + const isVideo = asset.mime_type?.startsWith("video/") ?? false + const thumbSrc = asset.thumbnail_url || undefined + + const handlePlayToggle = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation() + const video = videoRef.current + if (!video || !isVideo) return + + if (isPlaying) { + video.pause() + setIsPlaying(false) + } else { + video.play().catch(() => {}) + setIsPlaying(true) + } + }, + [isPlaying, isVideo], + ) + + const handleVideoEnded = useCallback(() => { + setIsPlaying(false) + }, []) + + const handleCardClick = useCallback(() => { + // 如果视频正在播放,点击卡片空白区域暂停视频 + if (isPlaying) { + const video = videoRef.current + if (video) { + video.pause() + setIsPlaying(false) + } + return + } + onToggle() + }, [isPlaying, onToggle]) + + return ( +
+ {/* 视频元素 */} + {isVideo && asset.file_url ? ( +