fenghuo/apps/web/components/content/jcdt/fhwh/culture.tsx

248 lines
6.1 KiB
TypeScript
Raw Normal View History

2025-06-16 23:16:59 +08:00
import React, { useState, useEffect, useRef } from 'react';
import { Swiper, SwiperSlide } from 'swiper/react';
import { EffectCoverflow, Pagination, Autoplay, Navigation } from 'swiper/modules';
// 导入 Swiper 样式
import 'swiper/css';
import 'swiper/css/effect-coverflow';
import 'swiper/css/pagination';
import 'swiper/css/navigation';
// 模拟数据接口
interface Article {
id: number;
cover: string;
title: string;
relative_link: string;
}
interface OptionPage {
model_bg: {
url: string;
};
}
const CulturePage: React.FC = () => {
const [posts, setPosts] = useState<Article[]>([]);
const [isPostsFetching, setIsPostsFetching] = useState(true);
const [optionPage, setOptionPage] = useState<OptionPage | null>(null);
const [displayItems, setDisplayItems] = useState<Article[]>([]);
// 模拟数据获取
useEffect(() => {
// 模拟异步数据加载
const fetchData = async () => {
setIsPostsFetching(true);
// 模拟API延迟
await new Promise((resolve) => setTimeout(resolve, 1000));
// 模拟获取文章数据
const mockPosts: Article[] = [
{
id: 1,
cover: '/x4.png',
title: '烽火文化活动1',
relative_link: '/culture/1',
},
{
id: 2,
cover: '/x4.png',
title: '烽火文化活动2',
relative_link: '/culture/2',
},
{
id: 3,
cover: '/x4.png',
title: '烽火文化活动3',
relative_link: '/culture/3',
},
{
id: 4,
cover: '/x4.png',
title: '烽火文化活动4',
relative_link: '/culture/4',
},
{
id: 5,
cover: '/x4.png',
title: '烽火文化活动5',
relative_link: '/culture/5',
},
2025-11-18 11:21:51 +08:00
{
id: 6,
cover: '/x4.png',
title: '烽火文化活动6',
relative_link: '/culture/5',
},
{
id: 7,
cover: '/x4.png',
title: '烽火文化活动7',
relative_link: '/culture/5',
},
{
id: 8,
cover: '/x4.png',
title: '烽火文化活动8',
relative_link: '/culture/5',
},
{
id: 9,
cover: '/x4.png',
title: '烽火文化活动9',
relative_link: '/culture/5',
},
{
id: 10,
cover: '/x4.png',
title: '烽火文化活动10',
relative_link: '/culture/5',
},
{
id: 11,
cover: '/x4.png',
title: '烽火文化活动11',
relative_link: '/culture/5',
},
{
id: 12,
cover: '/x4.png',
title: '烽火文化活动12',
relative_link: '/culture/5',
},
{
id: 13,
cover: '/x4.png',
title: '烽火文化活动13',
relative_link: '/culture/5',
},
2025-06-16 23:16:59 +08:00
];
// 模拟选项页面数据
const mockOptionPage: OptionPage = {
model_bg: {
url: '/culture-bg.jpg',
},
};
setPosts(mockPosts);
setOptionPage(mockOptionPage);
setIsPostsFetching(false);
};
fetchData();
}, []);
// 监听posts变化过滤有封面的文章
useEffect(() => {
if (posts.length > 0) {
const filteredItems = posts.filter((post: Article) => post.cover);
setDisplayItems(filteredItems);
}
}, [posts]);
// 空状态组件
const EmptyState = () => (
<div className="bg-white p-6 rounded-xl h-full flex items-center justify-center">
<div className="text-center text-gray-400">
<div className="text-6xl mb-4">📝</div>
<p className="text-lg">稿</p>
</div>
</div>
);
return (
<>
<div className="h-[80px] w-[1514px] mt-10 flex mx-auto relative">
<h1 className="pt-12 text-4xl font-bold absolute right-15 text-white italic"></h1>
</div>
<div className="h-[610px] w-[1920px] mt-5 mx-auto relative">
{/* 加载状态 */}
{isPostsFetching && (
<div className="absolute inset-0 flex items-center justify-center rounded-xl">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500 mx-auto mb-4"></div>
<p className="text-gray-600">...</p>
</div>
</div>
)}
{/* 空状态 */}
{!isPostsFetching && displayItems.length === 0 && <EmptyState />}
{/* 轮播图 */}
{!isPostsFetching && displayItems.length > 0 && (
<div
className="flex justify-center rounded items-center w-full py-5 h-full"
style={{
backgroundImage: optionPage?.model_bg.url ? `url(${optionPage.model_bg.url})` : 'none',
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
}}
>
<Swiper
grabCursor={true}
centeredSlides={true}
slidesPerView={4}
spaceBetween={100}
loop={true}
2025-11-18 11:21:51 +08:00
loopAdditionalSlides={5}
speed={5000}
2025-06-16 23:16:59 +08:00
autoplay={{
2025-11-18 11:21:51 +08:00
delay: 0,
2025-06-16 23:16:59 +08:00
disableOnInteraction: false,
2025-11-18 11:21:51 +08:00
pauseOnMouseEnter: true,
// waitForTransition: false,
waitForTransition: true,
stopOnLastSlide: false,
reverseDirection: false,
2025-06-16 23:16:59 +08:00
}}
2025-11-18 11:21:51 +08:00
allowTouchMove={false}
resistance={false}
resistanceRatio={0}
watchSlidesProgress={true}
preventInteractionOnTransition={false}
runCallbacksOnInit={false}
modules={[Autoplay]}
className="mySwiper w-full h-full bg-cover bg-center"
style={
{
'--swiper-transition-timing-function': 'linear',
'--swiper-wrapper-transition-timing-function': 'linear',
'--swiper-pagination-color': 'transparent',
'--swiper-navigation-color': 'transparent',
} as React.CSSProperties
}
2025-06-16 23:16:59 +08:00
>
{displayItems.map((item) => (
<SwiperSlide key={item.id}>
<a
href={item.relative_link}
target="_blank"
rel="noopener noreferrer"
2025-11-18 11:21:51 +08:00
className="block hover:scale-105"
2025-06-16 23:16:59 +08:00
>
<img
2025-11-18 11:21:51 +08:00
className="ml-15 mx-auto object-fill select-none"
2025-06-16 23:16:59 +08:00
style={{ width: '395px', height: '515px' }}
src={item.cover}
alt={item.title}
onError={(e) => {
2025-11-18 11:21:51 +08:00
e.currentTarget.src = '/placeholder-image.jpg';
2025-06-16 23:16:59 +08:00
}}
/>
</a>
</SwiperSlide>
))}
</Swiper>
</div>
)}
</div>
</>
);
};
export default CulturePage;