2025-09-15 17:28:58 +08:00

359 lines
20 KiB
TypeScript

import { Metadata } from 'next';
import Link from 'next/link';
import { Locale } from '../../../lib/i18n';
import { getTranslations } from '../../../lib/translations';
import { generateCanonicalUrl, generateAlternateLinks } from '../../../lib/seo-utils';
interface BlogPageProps {
params: {
locale: Locale;
};
}
export async function generateMetadata({ params }: BlogPageProps): Promise<Metadata> {
const t = getTranslations(params.locale);
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'https://your-domain.com';
const canonicalUrl = generateCanonicalUrl('/blog', params.locale, baseUrl);
const alternateLinks = generateAlternateLinks('/blog', baseUrl);
return {
title: t.blog.title,
description: t.blog.subtitle,
alternates: {
canonical: canonicalUrl,
languages: Object.fromEntries(alternateLinks.map((link) => [link.hrefLang, link.href])),
},
openGraph: {
title: t.blog.title,
description: t.blog.subtitle,
url: canonicalUrl,
type: 'website',
},
};
}
export default function BlogPage({ params }: BlogPageProps) {
const t = getTranslations(params.locale);
// 获取所有博客文章
const blogPosts = [
{
id: 'featured',
...t.blog.posts.featured,
image: 'featured',
featured: true,
},
{
id: 'post1',
...t.blog.posts.post1,
image: 'post1',
featured: false,
},
{
id: 'post2',
...t.blog.posts.post2,
image: 'post2',
featured: false,
},
{
id: 'post3',
...t.blog.posts.post3,
image: 'post3',
featured: false,
},
{
id: 'post4',
...t.blog.posts.post4,
image: 'post4',
featured: false,
},
{
id: 'post5',
...t.blog.posts.post5,
image: 'post5',
featured: false,
},
];
const getCategoryColor = (category: string) => {
switch (category) {
case 'playerStories':
return 'bg-amber-500';
case 'development':
return 'bg-green-500';
case 'ecoFacts':
return 'bg-emerald-500';
default:
return 'bg-blue-500';
}
};
const getCategoryName = (category: string) => {
switch (category) {
case 'playerStories':
return t.blog.categories.playerStories;
case 'development':
return t.blog.categories.development;
case 'ecoFacts':
return t.blog.categories.ecoFacts;
default:
return category;
}
};
const getImageGradient = (imageId: string) => {
const gradients = {
featured: 'from-purple-500 via-blue-500 to-indigo-600',
post1: 'from-amber-500 via-orange-500 to-red-500',
post2: 'from-cyan-500 via-blue-500 to-indigo-500',
post3: 'from-green-500 via-emerald-500 to-teal-500',
post4: 'from-pink-500 via-rose-500 to-red-500',
post5: 'from-violet-500 via-purple-500 to-indigo-500',
};
return gradients[imageId as keyof typeof gradients] || 'from-gray-500 to-gray-600';
};
const featuredPost = blogPosts.find((post) => post.featured);
const regularPosts = blogPosts.filter((post) => !post.featured);
return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-black">
{/* Hero Section */}
<section className="relative py-20 px-4 sm:px-6 lg:px-8">
<div className="max-w-7xl mx-auto">
<div className="text-center mb-16">
<h1 className="text-5xl md:text-6xl font-bold text-white mb-6">
{t.blog.title}
</h1>
<p className="text-xl text-white/80 max-w-3xl mx-auto leading-relaxed">
{t.blog.subtitle}
</p>
</div>
{/* Category Filter */}
<div className="flex flex-wrap justify-center gap-4 mb-12">
<button className="bg-white/10 backdrop-blur-md text-white px-6 py-3 rounded-full border border-white/20 hover:bg-white/20 transition-all duration-200">
{t.blog.categories.all}
</button>
<button className="bg-amber-500/20 backdrop-blur-md text-amber-300 px-6 py-3 rounded-full border border-amber-500/30 hover:bg-amber-500/30 transition-all duration-200">
{t.blog.categories.playerStories}
</button>
<button className="bg-green-500/20 backdrop-blur-md text-green-300 px-6 py-3 rounded-full border border-green-500/30 hover:bg-green-500/30 transition-all duration-200">
{t.blog.categories.development}
</button>
<button className="bg-emerald-500/20 backdrop-blur-md text-emerald-300 px-6 py-3 rounded-full border border-emerald-500/30 hover:bg-emerald-500/30 transition-all duration-200">
{t.blog.categories.ecoFacts}
</button>
</div>
</div>
</section>
{/* Featured Post */}
{featuredPost && (
<section className="px-4 sm:px-6 lg:px-8 mb-20">
<div className="max-w-7xl mx-auto">
<div className="text-center mb-12">
<h2 className="text-3xl font-bold text-white mb-4">
{t.blog.featured}
</h2>
</div>
<Link href={`/${params.locale}/blog/${featuredPost.id}`}>
<article className="group cursor-pointer">
<div className="bg-white/10 backdrop-blur-md rounded-3xl overflow-hidden border border-white/20 shadow-2xl hover:shadow-3xl transition-all duration-500 transform hover:scale-[1.02]">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-0">
{/* Featured Post Image */}
<div className="relative h-80 lg:h-96 overflow-hidden">
<div
className={`absolute inset-0 bg-gradient-to-br ${getImageGradient(featuredPost.image)} opacity-90`}
>
<div className="absolute inset-0 bg-black/20"></div>
{/* Animated particles */}
<div className="absolute inset-0">
{[...Array(8)].map((_, i) => (
<div
key={i}
className="absolute w-2 h-2 bg-white/30 rounded-full animate-pulse group-hover:animate-bounce"
style={{
left: `${20 + i * 10}%`,
top: `${30 + (i % 3) * 20}%`,
animationDelay: `${i * 0.2}s`,
}}
/>
))}
</div>
</div>
<div className="absolute top-6 left-6">
<span
className={`${getCategoryColor(featuredPost.category)} text-white px-4 py-2 rounded-full text-sm font-medium shadow-lg`}
>
{getCategoryName(featuredPost.category)}
</span>
</div>
<div className="absolute top-6 right-6">
<div className="bg-white/20 backdrop-blur-sm rounded-full px-3 py-1">
<span className="text-white text-sm font-medium">
{t.blog.featured}
</span>
</div>
</div>
</div>
{/* Featured Post Content */}
<div className="p-8 lg:p-12 flex flex-col justify-center">
<div className="text-white/70 text-sm mb-4">
<div className="flex items-center space-x-4">
<span>{featuredPost.author}</span>
<span></span>
<span>{featuredPost.date}</span>
<span></span>
<span>{featuredPost.readTime}</span>
</div>
</div>
<h3 className="text-3xl font-bold text-white mb-6 group-hover:text-green-300 transition-colors duration-200 leading-tight">
{featuredPost.title}
</h3>
<p className="text-white/80 mb-8 leading-relaxed text-lg">
{featuredPost.excerpt}
</p>
<div className="flex items-center">
<span className="text-green-300 font-medium group-hover:text-green-200 transition-colors duration-200 flex items-center">
{t.blog.readMore}
<svg
className="w-5 h-5 ml-2 transform group-hover:translate-x-1 transition-transform duration-200"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M17 8l4 4m0 0l-4 4m4-4H3"
/>
</svg>
</span>
</div>
</div>
</div>
</div>
</article>
</Link>
</div>
</section>
)}
{/* Regular Posts Grid */}
<section className="px-4 sm:px-6 lg:px-8 pb-20">
<div className="max-w-7xl mx-auto">
<div className="text-center mb-12">
<h2 className="text-3xl font-bold text-white mb-4">
{t.blog.categories.all}
</h2>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{regularPosts.map((post) => (
<Link key={post.id} href={`/${params.locale}/blog/${post.id}`}>
<article className="group cursor-pointer h-full">
<div className="bg-white/10 backdrop-blur-md rounded-2xl overflow-hidden border border-white/20 shadow-xl hover:shadow-2xl transition-all duration-300 transform hover:scale-[1.02] h-full flex flex-col">
{/* Post Image */}
<div className="relative h-48 overflow-hidden">
<div
className={`absolute inset-0 bg-gradient-to-br ${getImageGradient(post.image)} opacity-90`}
>
<div className="absolute inset-0 bg-black/20"></div>
{/* Mini particles */}
<div className="absolute inset-0">
{[...Array(4)].map((_, i) => (
<div
key={i}
className="absolute w-1 h-1 bg-white/40 rounded-full animate-pulse group-hover:animate-ping"
style={{
left: `${30 + i * 15}%`,
top: `${40 + i * 10}%`,
animationDelay: `${i * 0.3}s`,
}}
/>
))}
</div>
</div>
<div className="absolute top-4 left-4">
<span
className={`${getCategoryColor(post.category)} text-white px-3 py-1 rounded-full text-xs font-medium`}
>
{getCategoryName(post.category)}
</span>
</div>
</div>
{/* Post Content */}
<div className="p-6 flex-1 flex flex-col">
<div className="text-white/60 text-sm mb-3">
<div className="flex items-center space-x-3">
<span>{post.author}</span>
<span></span>
<span>{post.readTime}</span>
</div>
</div>
<h3 className="text-xl font-bold text-white mb-3 group-hover:text-green-300 transition-colors duration-200 line-clamp-2 flex-shrink-0">
{post.title}
</h3>
<p className="text-white/70 mb-4 line-clamp-3 flex-1">
{post.excerpt}
</p>
<div className="flex items-center justify-between mt-auto">
<span className="text-white/50 text-sm">
{post.date}
</span>
<span className="text-green-300 font-medium group-hover:text-green-200 transition-colors duration-200 flex items-center">
{t.blog.readMore}
<svg
className="w-4 h-4 ml-1 transform group-hover:translate-x-1 transition-transform duration-200"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 5l7 7-7 7"
/>
</svg>
</span>
</div>
</div>
</div>
</article>
</Link>
))}
</div>
</div>
</section>
{/* Newsletter Subscription */}
<section className="px-4 sm:px-6 lg:px-8 pb-20">
<div className="max-w-4xl mx-auto">
<div className="bg-gradient-to-r from-green-500/20 to-emerald-500/20 backdrop-blur-md rounded-3xl p-8 lg:p-12 border border-green-500/30 text-center">
<h2 className="text-3xl font-bold text-white mb-4">{t.blog.stayUpdated}</h2>
<p className="text-white/80 mb-8 text-lg max-w-2xl mx-auto">
{t.blog.stayUpdatedDesc}
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center max-w-md mx-auto">
<input
type="email"
placeholder="your@email.com"
className="flex-1 px-6 py-3 rounded-full bg-white/10 backdrop-blur-md border border-white/20 text-white placeholder-white/60 focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent"
/>
<button className="bg-green-500 hover:bg-green-600 text-white px-8 py-3 rounded-full font-semibold transition-all duration-200 transform hover:scale-105 whitespace-nowrap">
{t.blog.subscribe}
</button>
</div>
</div>
</div>
</section>
</div>
);
}