forked from 77media/video-flow
209 lines
7.1 KiB
TypeScript
209 lines
7.1 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 === '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>
|
||
);
|
||
} |