Files
video-gen/video-gen-app/src/pages/CreativePlazaPage.tsx
T
2026-08-04 17:33:52 +08:00

537 lines
17 KiB
TypeScript

import React, { useState, useMemo } from 'react';
import { Button, Typography, Modal, Input, Select } from 'antd';
import {
StarOutlined,
GiftOutlined,
CrownOutlined,
DownloadOutlined,
VideoCameraOutlined,
PictureOutlined,
PlayCircleOutlined,
SearchOutlined,
} from '@ant-design/icons';
const CreativePlazaPage: React.FC = () => {
const [previewVisible, setPreviewVisible] = useState(false);
const [previewUrl, setPreviewUrl] = useState('');
const [previewType, setPreviewType] = useState<'image' | 'video'>('image');
const [searchText, setSearchText] = useState('');
const [selectedIndustry, setSelectedIndustry] = useState<string>('all');
const openPreview = (url: string, type: 'image' | 'video') => {
setPreviewUrl(url);
setPreviewType(type);
setPreviewVisible(true);
};
const handleDownload = (url: string, filename: string) => {
const link = document.createElement('a');
link.href = url;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
// 行业选项
const industryOptions = [
{ value: 'all', label: '全部行业' },
{ value: 'fashion', label: '时尚穿搭' },
{ value: 'food', label: '美食探店' },
{ value: 'travel', label: '旅行风景' },
{ value: 'tech', label: '数码科技' },
{ value: 'beauty', label: '美妆护肤' },
{ value: 'home', label: '家居生活' },
{ value: '母婴', label: '母婴亲子' },
];
// 瀑布流数据 - 包含图片和视频
const waterfallItems = [
{
id: 1,
type: 'image' as const,
url: 'https://images.unsplash.com/photo-1522337360788-8b13dee7a37e?w=400&q=80',
title: '时尚街拍',
tag: '热门',
industry: 'fashion',
aspectRatio: 1.3,
},
{
id: 2,
type: 'video' as const,
url: 'https://www.w3schools.com/html/mov_bbb.mp4',
poster: 'https://images.unsplash.com/photo-1492691527719-9d1e07e534b4?w=400&q=80',
title: '自然风景',
tag: '推荐',
industry: 'travel',
aspectRatio: 1.5,
},
{
id: 3,
type: 'image' as const,
url: 'https://images.unsplash.com/photo-1506744038136-46273834b3fb?w=400&q=80',
title: '湖光山色',
tag: '热门',
industry: 'travel',
aspectRatio: 0.75,
},
{
id: 4,
type: 'image' as const,
url: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&q=80',
title: '山脉风光',
tag: '新品',
industry: 'travel',
aspectRatio: 1.4,
},
{
id: 5,
type: 'video' as const,
url: 'https://www.w3schools.com/html/movie.mp4',
poster: 'https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=400&q=80',
title: '森林探索',
tag: '推荐',
industry: 'travel',
aspectRatio: 0.67,
},
{
id: 6,
type: 'image' as const,
url: 'https://images.unsplash.com/photo-1470770841072-f978cf4d019e?w=400&q=80',
title: '山间湖泊',
tag: '热门',
industry: 'travel',
aspectRatio: 1.7,
},
{
id: 7,
type: 'image' as const,
url: 'https://images.unsplash.com/photo-1501785888041-af3ef285b470?w=400&q=80',
title: '海岸风光',
tag: '推荐',
industry: 'travel',
aspectRatio: 0.8,
},
{
id: 8,
type: 'video' as const,
url: 'https://www.w3schools.com/html/mov_bbb.mp4',
poster: 'https://images.unsplash.com/photo-1472214103451-9374bd1c798e?w=400&q=80',
title: '城市夜景',
tag: '新品',
industry: 'travel',
aspectRatio: 1.33,
},
{
id: 9,
type: 'image' as const,
url: 'https://images.unsplash.com/photo-1447752875215-b2761acb3c5d?w=400&q=80',
title: '森林小径',
tag: '热门',
industry: 'travel',
aspectRatio: 1.5,
},
{
id: 10,
type: 'image' as const,
url: 'https://images.unsplash.com/photo-1504674900247-0877df9cc836?w=400&q=80',
title: '美食摆盘',
tag: '热门',
industry: 'food',
aspectRatio: 1.2,
},
{
id: 11,
type: 'image' as const,
url: 'https://images.unsplash.com/photo-1560448204-e02f11c3d0e2?w=400&q=80',
title: '时尚穿搭',
tag: '推荐',
industry: 'fashion',
aspectRatio: 1.0,
},
{
id: 12,
type: 'video' as const,
url: 'https://www.w3schools.com/html/mov_bbb.mp4',
poster: 'https://images.unsplash.com/photo-1498050108023-c5249f4df085?w=400&q=80',
title: '数码产品',
tag: '新品',
industry: 'tech',
aspectRatio: 1.4,
},
];
// 筛选后的数据
const filteredItems = useMemo(() => {
return waterfallItems.filter((item) => {
const matchSearch = searchText
? item.title.toLowerCase().includes(searchText.toLowerCase())
: true;
const matchIndustry = selectedIndustry === 'all' ? true : item.industry === selectedIndustry;
return matchSearch && matchIndustry;
});
}, [searchText, selectedIndustry]);
const handleItemClick = (item: typeof waterfallItems[0]) => {
const previewUrl = item.type === 'image' ? item.url : item.url;
openPreview(previewUrl, item.type);
};
return (
<div className="content_box">
{/* 顶部 Hero 区域 */}
<div
className="animate-fadeInUp"
style={{
padding: '28px 32px',
borderRadius: 16,
background: 'linear-gradient(135deg, #f5f3ff 0%, #ede9fe 50%, #fce7f3 100%)',
border: '1px solid rgba(139, 92, 246, 0.12)',
marginBottom: 24,
position: 'relative',
overflow: 'hidden',
}}
>
<div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 12 }}>
<div
style={{
width: 52,
height: 52,
borderRadius: 14,
background: 'linear-gradient(135deg, #8b5cf6 0%, #6366f1 100%)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
boxShadow: '0 6px 20px rgba(139, 92, 246, 0.3)',
}}
>
<GiftOutlined style={{ fontSize: 24, color: '#fff' }} />
</div>
<div>
<div className="shiny-text-container" style={{ marginBottom: 4 }}>
<span className="shiny-text" style={{ fontSize: 26, fontWeight: 700 }}>
创意素材案例
</span>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, color: '#7c3aed' }}>
<CrownOutlined style={{ fontSize: 12 }} />
<span>探索 AI 创作无限可能</span>
</div>
</div>
</div>
<Typography.Text style={{ color: '#64748b', fontSize: 14, lineHeight: 1.7 }}>
精选 AI 生成的优秀素材案例,展示 AI 创作的无限潜力与创意灵感
</Typography.Text>
</div>
{/* 特性卡片 */}
{/* 精选案例展示区域 */}
<div
className="animate-fadeInUp"
style={{
padding: '24px 28px',
borderRadius: 16,
background: '#fff',
// border: '1px solid #e2e8f0',
position: 'relative',
}}
>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 20,
}}
>
<div style={{ display: 'flex', alignItems: 'center' }}>
<div
style={{
width: 4,
height: 18,
background: 'linear-gradient(180deg, #6366f1, #8b5cf6)',
borderRadius: 2,
marginRight: 10,
}}
/>
<StarOutlined style={{ marginRight: 8, color: '#f59e0b', fontSize: 16 }} />
<span style={{ fontSize: 17, fontWeight: 600, color: '#1f2937' }}>精选案例展示</span>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<Select
value={selectedIndustry}
onChange={(value) => setSelectedIndustry(value)}
style={{ width: 140 }}
options={industryOptions}
/>
<Input.Search
placeholder="搜索行业案例"
allowClear
enterButton={<SearchOutlined />}
size="middle"
value={searchText}
onChange={(e) => setSearchText(e.target.value)}
onSearch={(value) => setSearchText(value)}
style={{ width: 240 }}
/>
</div>
</div>
{/* 统计信息 */}
<div style={{ marginBottom: 16, color: '#64748b', fontSize: 13 }}>
共找到 <span style={{ color: '#6366f1', fontWeight: 600 }}>{filteredItems.length}</span> 个案例
</div>
{/* 瀑布流展示 */}
<div
className="stagger-children"
style={{
columnCount: 5,
columnGap: 12,
marginBottom: 16,
}}
>
{filteredItems.map((item, index) => (
<div
key={item.id}
onClick={() => handleItemClick(item)}
style={{
breakInside: 'avoid',
marginBottom: 12,
borderRadius: 10,
overflow: 'hidden',
cursor: 'pointer',
position: 'relative',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.08)',
transition: 'transform 0.3s ease, box-shadow 0.3s ease',
animation: `fadeInUp 0.5s ease ${index * 0.1}s both`,
}}
onMouseEnter={(e) => {
(e.currentTarget as HTMLElement).style.transform = 'translateY(-4px)';
(e.currentTarget as HTMLElement).style.boxShadow = '0 8px 24px rgba(0, 0, 0, 0.15)';
}}
onMouseLeave={(e) => {
(e.currentTarget as HTMLElement).style.transform = 'translateY(0)';
(e.currentTarget as HTMLElement).style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.08)';
}}
>
{/* 图片或视频 */}
<div
style={{
position: 'relative',
width: '100%',
paddingBottom: `${(1 / item.aspectRatio) * 100}%`,
backgroundColor: '#f3f4f6',
}}
>
{item.type === 'image' ? (
<img
src={item.url}
alt={item.title}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
objectFit: 'cover',
}}
/>
) : (
<>
<img
src={item.poster}
alt={item.title}
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
objectFit: 'cover',
}}
/>
<div
style={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
width: 48,
height: 48,
borderRadius: '50%',
background: 'rgba(0, 0, 0, 0.6)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<PlayCircleOutlined style={{ fontSize: 32, color: '#fff' }} />
</div>
</>
)}
{/* 类型标签 */}
<div
style={{
position: 'absolute',
top: 12,
left: 12,
padding: '4px 8px',
borderRadius: 6,
background: 'rgba(0, 0, 0, 0.6)',
backdropFilter: 'blur(4px)',
display: 'flex',
alignItems: 'center',
gap: 4,
}}
>
{item.type === 'image' ? (
<PictureOutlined style={{ fontSize: 12, color: '#fff' }} />
) : (
<VideoCameraOutlined style={{ fontSize: 12, color: '#fff' }} />
)}
<span style={{ fontSize: 11, color: '#fff', fontWeight: 500 }}>
{item.type === 'image' ? '图片' : '视频'}
</span>
</div>
</div>
{/* 底部信息 */}
<div
style={{
padding: 12,
background: '#fff',
}}
>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
}}
>
<span
style={{
fontSize: 14,
fontWeight: 500,
color: '#1f2937',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
flex: 1,
marginRight: 8,
}}
>
{item.title}
</span>
<span
style={{
fontSize: 11,
color: '#fff',
padding: '2px 8px',
borderRadius: 4,
background: item.tag === '热门' ? '#ef4444' : item.tag === '推荐' ? '#6366f1' : '#f59e0b',
whiteSpace: 'nowrap',
}}
>
{item.tag}
</span>
</div>
</div>
</div>
))}
</div>
{/* 空状态 */}
{filteredItems.length === 0 && (
<div
style={{
textAlign: 'center',
padding: '60px 20px',
color: '#94a3b8',
}}
>
<SearchOutlined style={{ fontSize: 48, marginBottom: 16, opacity: 0.5 }} />
<div style={{ fontSize: 15 }}>暂无符合条件的案例</div>
<div style={{ fontSize: 13, marginTop: 8 }}>请尝试调整搜索关键词或行业筛选</div>
<Button
style={{ marginTop: 16, borderRadius: 8 }}
onClick={() => {
setSearchText('');
setSelectedIndustry('all');
}}
>
重置筛选
</Button>
</div>
)}
</div>
{/* 预览弹窗 */}
<Modal
open={previewVisible}
onCancel={() => setPreviewVisible(false)}
footer={[
<Button
key="download"
icon={<DownloadOutlined />}
onClick={() => handleDownload(previewUrl, previewType === 'image' ? 'image.jpg' : 'video.mp4')}
type="primary"
style={{ borderRadius: 8 }}
>
下载
</Button>,
]}
width={800}
centered
styles={{ body: { padding: 0 } }}
>
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
minHeight: 400,
// background: '#1a1a2e',
borderRadius: 8,
overflow: 'hidden',
}}
>
{previewType === 'image' ? (
<img
src={previewUrl}
alt="预览"
style={{
maxWidth: '100%',
maxHeight: '60vh',
objectFit: 'contain',
}}
/>
) : (
<video
src={previewUrl}
controls
autoPlay
style={{
maxWidth: '100%',
maxHeight: '60vh',
objectFit: 'contain',
}}
/>
)}
</div>
</Modal>
</div>
);
};
export default CreativePlazaPage;