272 lines
13 KiB
TypeScript
272 lines
13 KiB
TypeScript
'use client';
|
||
|
||
import { useState, useEffect } from 'react';
|
||
import Link from 'next/link';
|
||
import { Locale, generateLocalizedPath } from '@/lib/i18n';
|
||
import { NewsArticle } from '@/lib/markdown';
|
||
|
||
interface HomePageClientProps {
|
||
locale: Locale;
|
||
home: any;
|
||
common: any;
|
||
latestNews: NewsArticle[];
|
||
}
|
||
|
||
export default function HomePageClient({ locale, home, common, latestNews }: HomePageClientProps) {
|
||
const [currentSlide, setCurrentSlide] = useState(0);
|
||
|
||
// 生成新闻文章的链接
|
||
const getNewsArticleUrl = (articleId: string) => {
|
||
return generateLocalizedPath(`/news/${articleId}`, locale);
|
||
};
|
||
|
||
// 生成新闻缩略图(使用Unsplash随机图片作为占位符)
|
||
const getNewsImage = (index: number) => {
|
||
const imageIds = [
|
||
'photo-1560472354-b33ff0c44a43', // 科技主题
|
||
'photo-1551434678-e076c223a692', // 云计算主题
|
||
'photo-1581091226825-a6a2a5aee158' // AI主题
|
||
];
|
||
const imageId = imageIds[index % imageIds.length];
|
||
return `https://images.unsplash.com/${imageId}?w=400&h=225&fit=crop`;
|
||
};
|
||
|
||
// Banner carousel data
|
||
const bannerSlides = [
|
||
{
|
||
id: 1,
|
||
image: 'https://images.unsplash.com/photo-1451187580459-43490279c0fa?w=1200&h=600&fit=crop',
|
||
},
|
||
{
|
||
id: 2,
|
||
image: 'https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=1200&h=600&fit=crop',
|
||
},
|
||
{
|
||
id: 3,
|
||
image: 'https://images.unsplash.com/photo-1557804506-669a67965ba0?w=1200&h=600&fit=crop',
|
||
},
|
||
];
|
||
|
||
// Auto-advance carousel
|
||
useEffect(() => {
|
||
const timer = setInterval(() => {
|
||
setCurrentSlide((prev) => (prev + 1) % bannerSlides.length);
|
||
}, 5000);
|
||
return () => clearInterval(timer);
|
||
}, []);
|
||
|
||
const nextSlide = () => {
|
||
setCurrentSlide((prev) => (prev + 1) % bannerSlides.length);
|
||
};
|
||
|
||
const prevSlide = () => {
|
||
setCurrentSlide((prev) => (prev - 1 + bannerSlides.length) % bannerSlides.length);
|
||
};
|
||
|
||
return (
|
||
<>
|
||
{/* Banner Carousel */}
|
||
<section className="relative h-screen overflow-hidden">
|
||
<div className="relative w-full h-full">
|
||
{bannerSlides.map((slide, index) => (
|
||
<div
|
||
key={slide.id}
|
||
className={`absolute inset-0 transition-opacity duration-1000 ${
|
||
index === currentSlide ? 'opacity-100' : 'opacity-0'
|
||
}`}
|
||
>
|
||
<div
|
||
className="w-full h-full bg-cover bg-center bg-gray-900"
|
||
style={{ backgroundImage: `url(${slide.image})` }}
|
||
>
|
||
<div className="absolute inset-0 bg-black/40"></div>
|
||
<div className="relative z-10 flex items-center justify-center h-full">
|
||
<div className="text-center text-white max-w-4xl px-4">
|
||
<h2 className="text-4xl md:text-6xl font-light mb-6 leading-tight">
|
||
{home.banner[index]?.title}
|
||
</h2>
|
||
<p className="text-xl md:text-2xl font-light opacity-90">
|
||
{home.banner[index]?.subtitle}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
{/* Carousel Controls */}
|
||
<button
|
||
onClick={prevSlide}
|
||
className="absolute left-4 top-1/2 transform -translate-y-1/2 bg-white/20 hover:bg-white/30 text-white p-2 rounded-full transition-colors"
|
||
>
|
||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
strokeWidth={2}
|
||
d="M15 19l-7-7 7-7"
|
||
/>
|
||
</svg>
|
||
</button>
|
||
<button
|
||
onClick={nextSlide}
|
||
className="absolute right-4 top-1/2 transform -translate-y-1/2 bg-white/20 hover:bg-white/30 text-white p-2 rounded-full transition-colors"
|
||
>
|
||
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path
|
||
strokeLinecap="round"
|
||
strokeLinejoin="round"
|
||
strokeWidth={2}
|
||
d="M9 5l7 7-7 7"
|
||
/>
|
||
</svg>
|
||
</button>
|
||
|
||
{/* Carousel Indicators */}
|
||
<div className="absolute bottom-4 left-1/2 transform -translate-x-1/2 flex space-x-2">
|
||
{bannerSlides.map((_, index) => (
|
||
<button
|
||
key={index}
|
||
onClick={() => setCurrentSlide(index)}
|
||
className={`w-3 h-3 rounded-full transition-colors ${
|
||
index === currentSlide ? 'bg-white' : 'bg-white/50'
|
||
}`}
|
||
/>
|
||
))}
|
||
</div>
|
||
</section>
|
||
|
||
{/* Hero Section */}
|
||
<section className="py-20 px-4 sm:px-6 lg:px-8">
|
||
<div className="max-w-4xl mx-auto text-center">
|
||
<h1 className="text-4xl md:text-6xl font-light leading-tight mb-8 text-gray-900">
|
||
{home.hero.title}
|
||
</h1>
|
||
<p className="text-xl md:text-2xl text-gray-600 mb-12 font-light">
|
||
{home.hero.subtitle}
|
||
</p>
|
||
<button className="bg-gray-900 text-white px-8 py-3 rounded-full hover:bg-gray-800 transition-colors font-light">
|
||
{home.hero.button}
|
||
</button>
|
||
</div>
|
||
</section>
|
||
|
||
{/* Services Section */}
|
||
<section className="py-20 px-4 sm:px-6 lg:px-8 bg-gray-50">
|
||
<div className="max-w-6xl mx-auto">
|
||
<div className="text-center mb-16">
|
||
<h2 className="text-3xl md:text-4xl font-light mb-4 text-gray-900">
|
||
{home.services.title}
|
||
</h2>
|
||
<p className="text-xl text-gray-600 font-light">{home.services.subtitle}</p>
|
||
</div>
|
||
<div className="grid md:grid-cols-3 gap-8">
|
||
{home.services.items.map((service: any, index: number) => (
|
||
<div
|
||
key={index}
|
||
className="bg-white p-8 rounded-lg shadow-sm hover:shadow-md transition-shadow"
|
||
>
|
||
<h3 className="text-xl font-light mb-4 text-gray-900">
|
||
{service.title}
|
||
</h3>
|
||
<p className="text-gray-600 font-light leading-relaxed">
|
||
{service.description}
|
||
</p>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
{/* News Section */}
|
||
<section className="py-20 px-4 sm:px-6 lg:px-8">
|
||
<div className="max-w-6xl mx-auto">
|
||
<div className="text-center mb-16">
|
||
<h2 className="text-3xl md:text-4xl font-light mb-4 text-gray-900">
|
||
{home.news.title}
|
||
</h2>
|
||
</div>
|
||
<div className="grid md:grid-cols-3 gap-8">
|
||
{latestNews.length > 0 ? latestNews.map((article, index) => (
|
||
<Link key={article.id} href={getNewsArticleUrl(article.id)}>
|
||
<article className="group cursor-pointer">
|
||
<div className="aspect-video bg-gray-200 rounded-lg mb-4 overflow-hidden">
|
||
<img
|
||
src={getNewsImage(index)}
|
||
alt={article.title}
|
||
className="w-full h-full object-cover group-hover:scale-105 transition-transform duration-300"
|
||
/>
|
||
</div>
|
||
<div className="space-y-2">
|
||
<div className="flex items-center gap-2 text-xs text-gray-500">
|
||
<span className="bg-blue-100 text-blue-600 px-2 py-1 rounded">
|
||
{article.category}
|
||
</span>
|
||
<span>{article.date}</span>
|
||
<span>•</span>
|
||
<span>{article.readTime}</span>
|
||
</div>
|
||
<h3 className="text-lg font-light mb-2 text-gray-900 group-hover:text-gray-600 transition-colors line-clamp-2">
|
||
{article.title}
|
||
</h3>
|
||
<p className="text-gray-600 font-light text-sm line-clamp-3">
|
||
{article.excerpt}
|
||
</p>
|
||
{article.author && (
|
||
<p className="text-xs text-gray-500 mt-2">
|
||
{home.news.author}: {article.author}
|
||
</p>
|
||
)}
|
||
</div>
|
||
</article>
|
||
</Link>
|
||
)) : (
|
||
// 如果没有新闻数据,显示占位符
|
||
[1, 2, 3].map((item) => (
|
||
<article key={item} className="group cursor-pointer">
|
||
<div className="aspect-video bg-gray-200 rounded-lg mb-4 overflow-hidden">
|
||
<div className="w-full h-full bg-gradient-to-br from-gray-100 to-gray-200 group-hover:from-gray-200 group-hover:to-gray-300 transition-colors"></div>
|
||
</div>
|
||
<h3 className="text-lg font-light mb-2 text-gray-900 group-hover:text-gray-600 transition-colors">
|
||
{home.news.sampleTitle} {item}
|
||
</h3>
|
||
<p className="text-gray-600 font-light text-sm">
|
||
{home.news.sampleDate}
|
||
</p>
|
||
</article>
|
||
))
|
||
)}
|
||
</div>
|
||
|
||
{/* 查看更多新闻按钮 */}
|
||
{latestNews.length > 0 && (
|
||
<div className="text-center mt-12">
|
||
<Link
|
||
href={generateLocalizedPath('/news', locale)}
|
||
className="inline-flex items-center px-6 py-3 border border-gray-300 text-gray-700 hover:text-gray-900 hover:border-gray-400 rounded-full transition-colors font-light"
|
||
>
|
||
{home.news.viewMore}
|
||
<svg className="ml-2 w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||
</svg>
|
||
</Link>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</section>
|
||
|
||
{/* Contact Section */}
|
||
<section className="py-20 px-4 sm:px-6 lg:px-8 bg-gray-900 text-white">
|
||
<div className="max-w-4xl mx-auto text-center">
|
||
<h2 className="text-3xl md:text-4xl font-light mb-4">{home.contact.title}</h2>
|
||
<p className="text-xl font-light mb-8 opacity-90">{home.contact.description}</p>
|
||
<button className="bg-white text-gray-900 px-8 py-3 rounded-full hover:bg-gray-100 transition-colors font-light">
|
||
{common.common.contactNow}
|
||
</button>
|
||
</div>
|
||
</section>
|
||
</>
|
||
);
|
||
}
|