video-flow-b/components/pages/home-page2.tsx
2025-08-27 00:15:11 +08:00

294 lines
11 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, CreditCard } from "lucide-react";
import "./style/home-page2.css";
import { useRouter, useSearchParams } 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 searchParams = useSearchParams();
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 [showPaymentSuccess, setShowPaymentSuccess] = useState(false);
const [paymentData, setPaymentData] = useState<any>(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(() => {
const sessionId = searchParams.get('session_id');
const payment = searchParams.get('payment');
if (sessionId && payment === 'success') {
// 显示支付成功提示
setShowPaymentSuccess(true);
// 获取支付详情
fetchPaymentDetails(sessionId);
// 清除URL参数避免刷新页面时重复显示
const newUrl = new URL(window.location.href);
newUrl.searchParams.delete('session_id');
newUrl.searchParams.delete('payment');
window.history.replaceState({}, '', newUrl.pathname);
}
}, [searchParams]);
// 获取支付详情
const fetchPaymentDetails = async (sessionId: string) => {
try {
const response = await fetch(`/api/payment/checkout-status/${sessionId}?user_id=test_user_123`);
const result = await response.json();
if (result.successful && result.data) {
setPaymentData(result.data);
}
} catch (error) {
console.error('获取支付详情失败:', error);
}
};
// 组件挂载时获取资源
useEffect(() => {
fetchResources();
}, []);
// 处理编辑视频
const handleEdit = (id: string) => {
// TODO: 实现编辑功能
};
// 处理删除视频
const handleDelete = (id: string) => {
// TODO: 实现删除功能
};
// 处理创建项目
const handleCreateProject = async () => {
// console.log('isCreating', isCreating);
router.push(`/create`);
// 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}>
{/* 支付成功提示 */}
{showPaymentSuccess && paymentData && (
<div className="fixed top-4 left-1/2 transform -translate-x-1/2 z-50 bg-green-50 border border-green-200 rounded-lg p-4 shadow-lg max-w-md">
<div className="flex items-center">
<div className="flex-shrink-0">
<svg className="h-5 w-5 text-green-400" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
</svg>
</div>
<div className="ml-3">
<h3 className="text-sm font-medium text-green-800"></h3>
<p className="text-sm text-green-700 mt-1">
: {paymentData.biz_order_no}
</p>
</div>
<div className="ml-auto pl-3">
<button
onClick={() => setShowPaymentSuccess(false)}
className="text-green-400 hover:text-green-600"
>
<svg className="h-4 w-4" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clipRule="evenodd" />
</svg>
</button>
</div>
</div>
</div>
)}
<div className="flex relative" style={{height: '100vh'}}>
{/* 工具栏-列表形式切换 */}
<div className="absolute top-[8rem] z-[49] right-6 w-[128px] flex justify-end">
<LiquidButton className="w-[128px] 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-[128px] 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-[40px] 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>
{/* Pricing 入口 */}
<div className="fixed bottom-[3rem] right-[3rem] z-50">
<LiquidButton className="w-[120px] h-[48px] text-sm">
<div className="flex items-center justify-center gap-2"
onClick={(e) => {
e.stopPropagation();
router.push('/pricing');
}}>
<CreditCard className="w-5 h-5 text-white" />
Pricing
</div>
</LiquidButton>
</div>
<div className="w-[128px] h-[128px] rounded-[50%] overflow-hidden fixed bottom-[3rem] left-[50%] -translate-x-1/2 z-50">
<LiquidButton className="w-[128px] h-[128px] 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>
);
}