AwsLinker/app/components/news/NewsArticleServerComponent.tsx
2025-09-16 17:19:58 +08:00

210 lines
12 KiB
TypeScript

import Image from 'next/image';
import Link from 'next/link';
import { Article } from '@/lib/types';
import { formatDate } from '@/lib/utils';
import { Card, CardContent } from '@/app/components/ui/card';
import { Badge } from '@/app/components/ui/badge';
import { Share2, ArrowLeft, Calendar, User, Tag, Clock, Eye } from 'lucide-react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import rehypeRaw from 'rehype-raw';
import rehypeSanitize from 'rehype-sanitize';
import ServerHeader from '@/app/components/ServerHeader';
import Footer from '@/app/components/Footer';
import { newsDetailTranslations } from '@/translations/news-detail';
import ShareButton from './ShareButton';
import BackButton from './BackButton';
interface NewsArticleServerComponentProps {
article: Article;
relatedArticles: Article[];
locale: string;
}
export default function NewsArticleServerComponent({
article,
relatedArticles,
locale
}: NewsArticleServerComponentProps) {
// 获取当前语言的翻译
const currentTranslations = newsDetailTranslations[locale as keyof typeof newsDetailTranslations] || newsDetailTranslations['zh-CN'];
// 估算阅读时间
const estimateReadingTime = (content: string) => {
const wordsPerMinute = 200;
const words = content.split(/\s+/).length;
return Math.ceil(words / wordsPerMinute);
};
const readingTime = estimateReadingTime(article.content);
return (
<div className="min-h-screen bg-gray-50">
<ServerHeader
language={locale}
translations={currentTranslations}
locale={locale}
/>
<main className="container mx-auto px-4 py-8">
<div className="max-w-4xl mx-auto">
{/* 返回按钮 */}
<BackButton
text={currentTranslations.backToNews}
href={`/${locale}/news`}
/>
{/* 文章主体 */}
<article className="bg-white rounded-lg shadow-sm overflow-hidden mb-8">
{/* 文章标题区域 */}
<div className="p-4 sm:p-6 lg:p-8 border-b border-gray-200">
<div className="flex flex-wrap gap-2 mb-4">
<Badge variant="secondary" className="bg-blue-100 text-blue-800">
{article.metadata.category}
</Badge>
{article.metadata.tags.map((tag, index) => (
<Badge key={index} variant="outline" className="text-gray-600">
<Tag size={12} className="mr-1" />
{tag}
</Badge>
))}
</div>
<h1 className="text-2xl sm:text-3xl lg:text-4xl font-bold text-gray-900 mb-6 leading-tight">
{article.metadata.title}
</h1>
<div className="flex flex-wrap items-center gap-4 sm:gap-6 text-gray-600 mb-6 text-sm sm:text-base">
<div className="flex items-center gap-2">
<User size={16} />
<span>{article.metadata.author}</span>
</div>
<div className="flex items-center gap-2">
<Calendar size={16} />
<span>{formatDate(article.metadata.date, locale)}</span>
</div>
<div className="flex items-center gap-2">
<Clock size={16} />
<span>{readingTime} </span>
</div>
</div>
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<p className="text-lg text-gray-700 leading-relaxed flex-1">
{article.metadata.description}
</p>
<div className="flex-shrink-0">
<ShareButton article={article} />
</div>
</div>
</div>
{/* 文章封面图 */}
{article.metadata.image && (
<div className="relative h-96 bg-gray-200">
<Image
src={article.metadata.image}
alt={article.metadata.title}
fill
className="object-cover"
priority
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 80vw, 1200px"
/>
</div>
)}
{/* 文章内容 */}
<div className="p-4 sm:p-6 lg:p-8">
<div className="prose prose-base sm:prose-lg prose-gray max-w-none prose-headings:text-gray-900 prose-a:text-blue-600 prose-strong:text-gray-900 prose-code:text-blue-800 prose-code:bg-blue-50 prose-code:px-1 prose-code:py-0.5 prose-code:rounded">
<ReactMarkdown
remarkPlugins={[remarkGfm]}
rehypePlugins={[rehypeRaw, rehypeSanitize]}
components={{
img: ({ src, alt, ...props }) => (
<div className="relative my-8">
<Image
src={src || ''}
alt={alt || ''}
width={800}
height={400}
className="rounded-lg shadow-md"
/>
</div>
),
a: ({ href, children, ...props }) => (
<Link
href={href || ''}
className="text-blue-600 hover:text-blue-800 underline"
{...props}
>
{children}
</Link>
),
}}
>
{article.content}
</ReactMarkdown>
</div>
</div>
</article>
{/* 相关文章推荐 */}
{relatedArticles.length > 0 && (
<section className="mt-12 pt-8 border-t border-gray-200">
<div className="flex items-center mb-8">
<div className="flex-1">
<h2 className="text-xl sm:text-2xl lg:text-3xl font-bold text-gray-900 mb-2">
{currentTranslations.relatedArticles}
</h2>
<p className="text-gray-600 text-sm sm:text-base">
{currentTranslations.relatedArticlesSubtitle}
</p>
</div>
<div className="hidden sm:block w-16 h-1 bg-gradient-to-r from-blue-500 to-purple-600 rounded-full"></div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{relatedArticles.map((relatedArticle) => (
<Card
key={relatedArticle.id}
className="group hover:shadow-xl transition-all duration-300 hover:-translate-y-2 border-0 shadow-md bg-white"
>
<CardContent className="p-0">
<Link href={`/${locale}/news/${relatedArticle.metadata.slug}`}>
<div className="relative h-48 sm:h-56 rounded-t-lg overflow-hidden bg-gray-100">
<Image
src={relatedArticle.metadata.image || '/api/placeholder/400/250'}
alt={relatedArticle.metadata.title}
fill
className="object-cover transition-transform duration-500 group-hover:scale-110"
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
/>
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/10 transition-colors duration-300"></div>
</div>
<div className="p-4 sm:p-5">
<h3 className="font-bold text-gray-900 mb-3 line-clamp-2 text-base sm:text-lg leading-tight group-hover:text-blue-600 transition-colors duration-200">
{relatedArticle.metadata.title}
</h3>
<p className="text-gray-600 text-sm mb-4 line-clamp-2 leading-relaxed">
{relatedArticle.metadata.excerpt}
</p>
<div className="flex items-center justify-between pt-3 border-t border-gray-100">
<span className="text-xs text-gray-500 font-medium">{formatDate(relatedArticle.metadata.date, locale)}</span>
<Badge variant="outline" className="text-xs bg-blue-50 text-blue-700 border-blue-200 hover:bg-blue-100 transition-colors">
{relatedArticle.metadata.category}
</Badge>
</div>
</div>
</Link>
</CardContent>
</Card>
))}
</div>
</section>
)}
</div>
</main>
<Footer translations={currentTranslations} />
</div>
);
}