forked from 77media/video-flow
存--替换视频进度
This commit is contained in:
parent
06840ab17a
commit
19ef2792f6
@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useState, useRef, useEffect } from 'react';
|
||||||
import { motion, AnimatePresence } from 'framer-motion';
|
import { motion, AnimatePresence } from 'framer-motion';
|
||||||
import { ImageUp, Library, Play, Pause, RefreshCw, Wand2, Users, Check, ReplaceAll, X, TriangleAlert, Loader2 } from 'lucide-react';
|
import { ImageUp, Library, Play, Pause, RefreshCw, Wand2, Users, CircleX, ReplaceAll, X, TriangleAlert, Loader2 } from 'lucide-react';
|
||||||
import { cn } from '@/public/lib/utils';
|
import { cn } from '@/public/lib/utils';
|
||||||
import { CharacterEditor } from './character-editor';
|
import { CharacterEditor } from './character-editor';
|
||||||
import ImageBlurTransition from './ImageBlurTransition';
|
import ImageBlurTransition from './ImageBlurTransition';
|
||||||
@ -34,23 +34,31 @@ interface Role {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mock 数据
|
// Mock 数据
|
||||||
const mockRole: Role = {
|
const mockReplaceVideoData = {
|
||||||
name: "青春女学生",
|
count: 10,
|
||||||
url: "/assets/3dr_chihiro.png",
|
completed: 2, // 已完成个数
|
||||||
sound: "",
|
data: [
|
||||||
soundDescription: "",
|
{
|
||||||
roleDescription: "一位充满活力和梦想的高中女生,蓝色长发随风飘扬,眼神中透露着对未来的憧憬。她身着整洁的校服,举止优雅而不失活力。",
|
id: '1',
|
||||||
age: 16,
|
name: '替换视频1',
|
||||||
gender: 'female',
|
videoUrl: ['https://video-base-imf.oss-ap-southeast-7.aliyuncs.com/uploads/FJ1_76a6cd5d-e4dd-4bed-8d5f-65306f146caf-20250812112433.mp4'],
|
||||||
ethnicity: '亚洲人',
|
status: 1
|
||||||
appearance: {
|
}, {
|
||||||
hairStyle: "鲜艳蓝色长发",
|
id: '2',
|
||||||
skinTone: "白皙",
|
name: '替换视频1',
|
||||||
facialFeatures: "大眼睛,清秀五官",
|
videoUrl: [],
|
||||||
bodyType: "苗条"
|
status: 2
|
||||||
},
|
},
|
||||||
tags: ['高中生', '校服', '蓝色长发', '大眼睛', '清秀五官', '苗条']
|
{ id: '3', name: '替换视频1', videoUrl: [], status: 0 },
|
||||||
};
|
{ id: '4', name: '替换视频1', videoUrl: [], status: 0 },
|
||||||
|
{ id: '5', name: '替换视频1', videoUrl: [], status: 0 },
|
||||||
|
{ id: '6', name: '替换视频1', videoUrl: [], status: 0 },
|
||||||
|
{ id: '7', name: '替换视频1', videoUrl: [], status: 0 },
|
||||||
|
{ id: '8', name: '替换视频1', videoUrl: [], status: 0 },
|
||||||
|
{ id: '9', name: '替换视频1', videoUrl: [], status: 0 },
|
||||||
|
{ id: '10', name: '替换视频1', videoUrl: [], status: 0 },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
interface CharacterTabContentProps {
|
interface CharacterTabContentProps {
|
||||||
taskSketch: any[];
|
taskSketch: any[];
|
||||||
@ -63,7 +71,7 @@ export function CharacterTabContent({
|
|||||||
taskSketch,
|
taskSketch,
|
||||||
currentRoleIndex,
|
currentRoleIndex,
|
||||||
onSketchSelect,
|
onSketchSelect,
|
||||||
roles = [mockRole]
|
roles = []
|
||||||
}: CharacterTabContentProps) {
|
}: CharacterTabContentProps) {
|
||||||
const [isReplacePanelOpen, setIsReplacePanelOpen] = useState(false);
|
const [isReplacePanelOpen, setIsReplacePanelOpen] = useState(false);
|
||||||
const [replacePanelKey, setReplacePanelKey] = useState(0);
|
const [replacePanelKey, setReplacePanelKey] = useState(0);
|
||||||
@ -394,6 +402,56 @@ export function CharacterTabContent({
|
|||||||
|
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
|
{/* 替换视频进度 预览区域 */}
|
||||||
|
<motion.div
|
||||||
|
className="grid grid-cols-1 gap-6"
|
||||||
|
initial={{ opacity: 0, y: 20 }}
|
||||||
|
animate={{ opacity: 1, y: 0 }}
|
||||||
|
transition={{ delay: 0.2 }}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col gap-1">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-sm text-gray-500">替换视频进度</span>
|
||||||
|
<span className="text-sm text-blue-500">
|
||||||
|
{mockReplaceVideoData.completed}/{mockReplaceVideoData.count}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<HorizontalScroller
|
||||||
|
itemWidth={128}
|
||||||
|
gap={0}
|
||||||
|
selectedIndex={-1}
|
||||||
|
onItemClick={() => {}}
|
||||||
|
>
|
||||||
|
{mockReplaceVideoData.data.map((shot) => (
|
||||||
|
<div key={shot.id} className="flex items-center w-[128px] h-full rounded-sm overflow-hidden">
|
||||||
|
{shot.status === 0 && (
|
||||||
|
<div className="w-full h-full flex items-center justify-center bg-black/10">
|
||||||
|
<Loader2 className="w-4 h-4 animate-spin text-blue-500" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{shot.status === 1 && (
|
||||||
|
<video
|
||||||
|
src={shot.videoUrl[0]}
|
||||||
|
className="w-full h-full object-cover"
|
||||||
|
muted
|
||||||
|
loop
|
||||||
|
playsInline
|
||||||
|
onMouseEnter={(e) => e.currentTarget.play()}
|
||||||
|
onMouseLeave={(e) => e.currentTarget.pause()}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{/* 任务失败 */}
|
||||||
|
{shot.status === 2 && (
|
||||||
|
<div className="w-full h-full flex items-center justify-center bg-red-500/10">
|
||||||
|
<CircleX className="w-4 h-4 text-red-500" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</HorizontalScroller>
|
||||||
|
</div>
|
||||||
|
</motion.div>
|
||||||
|
|
||||||
<FloatingGlassPanel
|
<FloatingGlassPanel
|
||||||
open={isReplacePanelOpen}
|
open={isReplacePanelOpen}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user