video-flow-b/components/pages/home-page2.tsx
2025-07-14 03:56:30 +08:00

209 lines
7.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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 === 'manual' ? 1 : 2, // 1 表示手动模式2 表示自动模式
resolution: 1080 // 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: React.MouseEvent) => {
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>
);
}