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

248 lines
6.1 KiB
TypeScript
Executable File
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.

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',
},
{
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',
},
];
// 模拟选项页面数据
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}
loopAdditionalSlides={5}
speed={5000}
autoplay={{
delay: 0,
disableOnInteraction: false,
pauseOnMouseEnter: true,
// waitForTransition: false,
waitForTransition: true,
stopOnLastSlide: false,
reverseDirection: false,
}}
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
}
>
{displayItems.map((item) => (
<SwiperSlide key={item.id}>
<a
href={item.relative_link}
target="_blank"
rel="noopener noreferrer"
className="block hover:scale-105"
>
<img
className="ml-15 mx-auto object-fill select-none"
style={{ width: '395px', height: '515px' }}
src={item.cover}
alt={item.title}
onError={(e) => {
e.currentTarget.src = '/placeholder-image.jpg';
}}
/>
</a>
</SwiperSlide>
))}
</Swiper>
</div>
)}
</div>
</>
);
};
export default CulturePage;