video-flow-b/components/pages/home-page2.tsx

209 lines
7.0 KiB
TypeScript

"use client";
import { useState, useRef, useEffect } from "react";
import { Table, AlignHorizontalSpaceAround, Loader2, Clapperboard } from "lucide-react";
import "./style/home-page2.css";
import { useRouter } from "next/navigation";
import { VideoCarouselLayout } from '@/components/video-carousel-layout';
import { VideoGridLayout } from '@/components/video-grid-layout';
import { motion, AnimatePresence } from "framer-motion";
import { LiquidButton } from "@/components/ui/liquid-glass-button";
import {
createScriptProject,
CreateScriptProjectRequest
} from '@/api/script_project';
import {
ProjectTypeEnum,
ModeEnum,
ResolutionEnum
} from '@/app/model/enums';
import {
getResourcesList,
Resource
} from '@/api/resources';
export function HomePage2() {
const router = useRouter();
const [activeTool, setActiveTool] = useState("stretch");
const [dropPosition, setDropPosition] = useState<"left" | "right">("left");
const [isCreating, setIsCreating] = useState(false);
const [createdProjectId, setCreatedProjectId] = useState<number | null>(null);
const [resources, setResources] = useState<Resource[]>([]);
const [isLoadingResources, setIsLoadingResources] = useState(false);
const containerRef = useRef<HTMLDivElement>(null);
// 将资源数据转换为视频格式
const videos = resources.map(resource => ({
id: resource.id.toString(),
url: resource.url,
title: resource.title
}));
// 获取资源列表
const fetchResources = async () => {
try {
setIsLoadingResources(true);
const response = await getResourcesList({ published: 1 });
console.log(response);
if (response.code === 0) {
setResources(response.data);
} else {
console.error('获取资源列表失败:', response.message);
}
} catch (error) {
console.error('获取资源列表出错:', error);
} finally {
setIsLoadingResources(false);
}
};
// 组件挂载时获取资源
useEffect(() => {
fetchResources();
}, []);
// 处理编辑视频
const handleEdit = (id: string) => {
// TODO: 实现编辑功能
};
// 处理删除视频
const handleDelete = (id: string) => {
// TODO: 实现删除功能
};
// 处理创建项目
const handleCreateProject = async () => {
if (isCreating) return;
try {
setIsCreating(true);
router.push(`/create`);
return;
// 使用默认值
const projectType = ProjectTypeEnum.SCRIPT_TO_VIDEO;
// 构建项目数据并调用API
const projectData: CreateScriptProjectRequest = {
title: "script default", // 默认剧本名称
project_type: projectType,
mode: ModeEnum.MANUAL,
resolution: ResolutionEnum.FULL_HD_1080P
};
const projectResponse = await createScriptProject(projectData);
if (projectResponse.code === 0 && projectResponse.data.id) {
const projectId = projectResponse.data.id;
setCreatedProjectId(projectId);
// projectId 作为参数传递给 create 页面
router.push(`/create?projectId=${projectId}`);
} else {
alert(`创建项目失败: ${projectResponse.message}`);
}
} catch (error) {
alert("创建项目时发生错误,请稍后重试");
} finally {
setIsCreating(false);
}
};
// 处理工具切换
const handleToolChange = (position: "left" | "right") => {
setDropPosition(position);
setActiveTool(position === "left" ? "stretch" : "table");
};
return (
<div className="min-h-screen bg-[var(--background)]" ref={containerRef}>
<div className="flex relative" style={{height: '100vh'}}>
{/* 工具栏-列表形式切换 */}
<div className="absolute top-[8rem] z-[50] right-6 w-[8rem] flex justify-end">
<LiquidButton className="w-[8rem] h-[3rem] text-sm"
onClick={(e) => {
e.stopPropagation();
handleToolChange(activeTool === "stretch" ? "right" : "left");
}}
>
<div className="relative flex items-center justify-around gap-4 w-[8rem] h-[3rem] p-2">
<div
className={`cursor-pointer relative z-10 transition-opacity duration-300 ${activeTool === "stretch" ? "opacity-100" : "opacity-50"}`}>
<AlignHorizontalSpaceAround className="w-4 h-4 text-white" />
</div>
<div
className={`cursor-pointer relative z-10 transition-opacity duration-300 ${activeTool === "table" ? "opacity-100" : "opacity-50"}`}>
<Table className="w-4 h-4 text-white" />
</div>
{/* 水滴动画 */}
<AnimatePresence>
<motion.div
className="absolute w-10 h-8 bg-[rgba(255,255,255,0.35)] rounded-full backdrop-blur-[2px] z-[1]"
initial={{ x: dropPosition === "left" ? -32 : 32 }}
animate={{
x: dropPosition === "left" ? -32 : 32,
scale: [1, 1.2, 1],
}}
transition={{
type: "spring",
stiffness: 300,
damping: 30,
scale: {
duration: 0.2,
}
}}
/>
</AnimatePresence>
</div>
</LiquidButton>
</div>
{/* 屏风式视频布局 */}
<div
className={`absolute w-full h-[calc(100vh - 4rem)] transition-all duration-500
${activeTool === "stretch" ? "opacity-100 translate-x-0" : "opacity-0 translate-x-[-100%] pointer-events-none"}
`}
style={{
height: '100%',
display: 'flex',
alignItems: 'center',
marginTop: '4rem'
}}
>
<VideoCarouselLayout videos={videos} />
</div>
{/* 网格式视频布局 */}
<div
className={`absolute top-[8rem] w-full transition-all duration-500 max-h-[calc(100vh-8rem)] overflow-y-auto hide-scrollbar
${activeTool === "table" ? "opacity-100 translate-x-0" : "opacity-0 translate-x-[100%] pointer-events-none"}
`}
>
<VideoGridLayout
videos={videos}
onEdit={handleEdit}
onDelete={handleDelete}
/>
</div>
<div className="w-[8rem] h-[8rem] rounded-[50%] overflow-hidden fixed bottom-[3rem] left-[50%] -translate-x-1/2 z-50">
<LiquidButton className="w-[8rem] h-[8rem] text-lg">
<div className="flex items-center justify-center gap-2"
onClick={(e) => {
e.stopPropagation();
handleCreateProject();
}}>
{isCreating ? (
<Loader2 className="w-6 h-6 text-white animate-spin" />
) : (
<Clapperboard className="w-6 h-6 text-white" />
)}
{isCreating ? "Action..." : "Action"}
</div>
</LiquidButton>
</div>
</div>
</div>
);
}