2025-09-16 18:00:27 +08:00

617 lines
39 KiB
TypeScript

'use client';
import { useState, useEffect } from 'react';
import Link from 'next/link';
import SEOHead from '../../../../components/SEOHead';
interface SlugPageProps {
params: { locale: string; slug: string };
}
export default function NewsSlugPage({ params }: SlugPageProps) {
const [article, setArticle] = useState<any>(null);
const [loading, setLoading] = useState(true);
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [readingProgress, setReadingProgress] = useState(0);
const [relatedArticles, setRelatedArticles] = useState<any[]>([]);
const [showBackToTop, setShowBackToTop] = useState(false);
// 计算阅读时间(基于平均阅读速度)
const calculateReadingTime = (content: string) => {
const wordsPerMinute = 200; // 中文阅读速度约200字/分钟
const wordCount = content.length;
const readingTime = Math.ceil(wordCount / wordsPerMinute);
return readingTime;
};
// 返回顶部功能
const scrollToTop = () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
};
// 阅读进度监听
useEffect(() => {
const handleScroll = () => {
const scrollTop = window.scrollY;
const docHeight = document.documentElement.scrollHeight - window.innerHeight;
const progress = (scrollTop / docHeight) * 100;
setReadingProgress(Math.min(100, Math.max(0, progress)));
// 显示返回顶部按钮
setShowBackToTop(scrollTop > 300);
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
useEffect(() => {
const loadArticle = async () => {
try {
// 直接使用 params.locale 而不是 currentLang 状态
const markdownRes = await fetch(`/docs/${params.locale}/news/${params.slug}.md`);
if (markdownRes.ok) {
const markdownContent = await markdownRes.text();
const { frontmatter, content } = parseMarkdown(markdownContent);
setArticle({ ...frontmatter, content });
// 加载相关文章
await loadRelatedArticles(frontmatter.category, frontmatter.tags);
} else {
// 如果当前语言的文章不存在,尝试检查是否有其他语言版本
const fallbackLanguages = ['zh-CN', 'en', 'zh-TW'].filter(lang => lang !== params.locale);
let found = false;
for (const fallbackLang of fallbackLanguages) {
const fallbackRes = await fetch(`/docs/${fallbackLang}/news/${params.slug}.md`);
if (fallbackRes.ok) {
setArticle({
error: 'language_not_available',
availableLanguage: fallbackLang,
slug: params.slug
});
found = true;
break;
}
}
if (!found) {
setArticle(null);
}
}
setLoading(false);
} catch (error) {
console.error('Failed to load article:', error);
setArticle(null);
setLoading(false);
}
};
loadArticle();
}, [params.locale, params.slug]);
// 加载相关文章
const loadRelatedArticles = async (category: string, tags: string[]) => {
try {
const articlesApiRes = await fetch(`/api/articles/${params.locale}`);
const articlesApiData = await articlesApiRes.json();
const articleSlugs = articlesApiData.articles || [];
const related: any[] = [];
for (const slug of articleSlugs) {
if (slug === params.slug) continue; // 排除当前文章
try {
const markdownRes = await fetch(`/docs/${params.locale}/news/${slug}.md`);
if (markdownRes.ok) {
const markdownContent = await markdownRes.text();
const { frontmatter } = parseMarkdown(markdownContent);
// 简单的相关性匹配:相同分类或有共同标签
if (frontmatter.category === category ||
(tags && frontmatter.tags && frontmatter.tags.some((tag: string) => tags.includes(tag)))) {
related.push({ ...frontmatter, slug });
}
}
} catch (error) {
// 忽略错误
}
if (related.length >= 3) break; // 最多3篇相关文章
}
setRelatedArticles(related);
} catch (error) {
console.error('Failed to load related articles:', error);
}
};
// 分享功能
const shareArticle = (platform: string) => {
const url = encodeURIComponent(window.location.href);
const title = encodeURIComponent(article?.title || '');
const shareUrls = {
twitter: `https://twitter.com/intent/tweet?url=${url}&text=${title}`,
facebook: `https://www.facebook.com/sharer/sharer.php?u=${url}`,
linkedin: `https://www.linkedin.com/sharing/share-offsite/?url=${url}`,
wechat: `javascript:void(0)`, // 微信需要特殊处理
};
if (platform === 'wechat') {
// 复制链接到剪贴板
navigator.clipboard.writeText(window.location.href).then(() => {
alert('链接已复制到剪贴板,可以分享到微信了!');
});
} else {
window.open(shareUrls[platform as keyof typeof shareUrls], '_blank', 'width=600,height=400');
}
};
// Helper function to parse markdown frontmatter
const parseMarkdown = (markdown: string) => {
// 使用与列表页相同的强健正则表达式
const frontmatterRegex = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/;
const match = markdown.match(frontmatterRegex);
if (!match) {
return {
frontmatter: {},
content: markdown,
};
}
const frontmatterText = match[1];
const content = match[2];
const frontmatter: any = {};
frontmatterText.split('\n').forEach((line) => {
const colonIndex = line.indexOf(':');
if (colonIndex === -1) return;
const key = line.substring(0, colonIndex).trim();
const value = line.substring(colonIndex + 1).trim();
if (key && value) {
if (key === 'tags') {
if (value.startsWith('[') && value.endsWith(']')) {
frontmatter[key] = value
.slice(1, -1)
.split(',')
.map((tag) => tag.trim().replace(/['"]/g, ''))
.filter((tag) => tag.length > 0);
} else {
frontmatter[key] = [value.replace(/['"]/g, '')];
}
} else if (key === 'featured') {
frontmatter[key] = value.toLowerCase() === 'true';
} else {
frontmatter[key] = value.replace(/['"]/g, '');
}
}
});
return { frontmatter, content };
};
// 简单的Markdown渲染器
const renderMarkdown = (markdown: string) => {
if (!markdown) return '';
return markdown
// 标题处理
.replace(/^### (.*$)/gim, '<h3 class="text-xl font-bold mt-6 mb-4 text-gray-800">$1</h3>')
.replace(/^## (.*$)/gim, '<h2 class="text-2xl font-bold mt-8 mb-6 text-gray-800">$1</h2>')
.replace(/^# (.*$)/gim, '<h1 class="text-3xl font-bold mt-8 mb-6 text-gray-800">$1</h1>')
// 粗体和斜体
.replace(/\*\*(.*?)\*\*/g, '<strong class="font-bold">$1</strong>')
.replace(/\*(.*?)\*/g, '<em class="italic">$1</em>')
// 列表处理
.replace(/^\s*\* (.*$)/gim, '<li class="ml-4 mb-2">• $1</li>')
.replace(/^\s*- (.*$)/gim, '<li class="ml-4 mb-2">• $1</li>')
.replace(/^\s*\d+\. (.*$)/gim, '<li class="ml-4 mb-2 list-decimal">$1</li>')
// 链接处理
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" class="text-blue-600 hover:text-blue-800 underline">$1</a>')
// 代码块处理
.replace(/```([^`]+)```/g, '<pre class="bg-gray-100 p-4 rounded-lg my-4 overflow-x-auto"><code>$1</code></pre>')
.replace(/`([^`]+)`/g, '<code class="bg-gray-100 px-2 py-1 rounded text-sm">$1</code>')
// 段落处理
.replace(/\n\n/g, '</p><p class="mb-4">')
// 换行处理
.replace(/\n/g, '<br />');
};
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-50 to-blue-50">
<div className="text-center">
<div className="w-16 h-16 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin mx-auto mb-4"></div>
<p className="text-gray-600">...</p>
</div>
</div>
);
}
if (!article) {
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-50 to-blue-50">
<div className="text-center">
<h1 className="text-2xl font-bold text-gray-900 mb-4">
{params.locale === 'en' ? 'Article Not Found' :
params.locale === 'zh-TW' ? '文章未找到' : '文章未找到'}
</h1>
<p className="text-gray-600 mb-8">
{params.locale === 'en' ? 'Sorry, the article you are looking for does not exist.' :
params.locale === 'zh-TW' ? '抱歉,您訪問的文章不存在。' : '抱歉,您访问的文章不存在。'}
</p>
<Link
href={`/${params.locale}/news`}
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
{params.locale === 'en' ? 'Back to News' :
params.locale === 'zh-TW' ? '返回新聞列表' : '返回新闻列表'}
</Link>
</div>
</div>
);
}
// 处理语言不可用的情况
if (article.error === 'language_not_available') {
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-50 to-blue-50">
<div className="text-center max-w-md">
<div className="bg-amber-50 border border-amber-200 rounded-lg p-6 mb-6">
<svg className="w-12 h-12 text-amber-500 mx-auto mb-4" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clipRule="evenodd" />
</svg>
<h1 className="text-xl font-bold text-gray-900 mb-2">
{params.locale === 'en' ? 'Article Not Available in English' :
params.locale === 'zh-TW' ? '文章暫無繁體中文版本' : '文章暂无简体中文版本'}
</h1>
<p className="text-gray-600 mb-4">
{params.locale === 'en' ? 'This article is available in other languages.' :
params.locale === 'zh-TW' ? '此文章有其他語言版本可供閱讀。' : '此文章有其他语言版本可供阅读。'}
</p>
<div className="space-y-3">
<Link
href={`/${article.availableLanguage}/news/${article.slug}`}
className="block px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
{params.locale === 'en' ? `Read in ${article.availableLanguage === 'zh-CN' ? 'Chinese (Simplified)' : article.availableLanguage === 'zh-TW' ? 'Chinese (Traditional)' : 'English'}` :
params.locale === 'zh-TW' ? `閱讀${article.availableLanguage === 'zh-CN' ? '簡體中文' : article.availableLanguage === 'en' ? '英文' : '繁體中文'}版本` :
`阅读${article.availableLanguage === 'zh-TW' ? '繁体中文' : article.availableLanguage === 'en' ? '英文' : '简体中文'}版本`}
</Link>
<Link
href={`/${params.locale}/news`}
className="block px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors"
>
{params.locale === 'en' ? 'Back to News' :
params.locale === 'zh-TW' ? '返回新聞列表' : '返回新闻列表'}
</Link>
</div>
</div>
</div>
</div>
);
}
return (
<>
<SEOHead
page="news"
locale={params.locale as 'zh-CN' | 'zh-TW' | 'en'}
canonicalUrl={`/news/${params.slug}`}
/>
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-blue-50">
{/* 优雅的阅读进度条 */}
<div className="fixed top-0 left-0 w-full h-1 bg-transparent z-[60]">
<div
className="h-full bg-gradient-to-r from-blue-400/80 via-purple-500/80 to-pink-500/80 transition-all duration-500 ease-out"
style={{
width: `${readingProgress}%`,
boxShadow: readingProgress > 0 ? '0 0 10px rgba(139, 92, 246, 0.3)' : 'none'
}}
></div>
</div>
{/* 悬浮分享按钮 */}
<div className="fixed right-6 top-1/2 transform -translate-y-1/2 z-40 space-y-3">
<div className="bg-white/95 backdrop-blur-sm rounded-2xl shadow-xl p-3 flex flex-col space-y-3 border border-gray-100">
<button
onClick={() => shareArticle('wechat')}
className="w-10 h-10 bg-green-500 text-white rounded-full flex items-center justify-center hover:bg-green-600 transition-colors"
title="分享到微信"
>
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M8.691 2.188C3.891 2.188 0 5.476 0 9.53c0 2.212 1.17 4.203 3.002 5.55a.59.59 0 0 1 .213.665l-.39 1.48c-.019.07-.048.141-.048.213 0 .163.13.295.29.295a.326.326 0 0 0 .167-.054l1.903-1.114a.864.864 0 0 1 .717-.098 10.16 10.16 0 0 0 2.837.403c.276 0 .543-.027.811-.05-.857-2.578.157-4.972 1.932-6.446 1.703-1.415 3.882-1.98 5.853-1.838-.576-3.583-4.196-6.348-8.596-6.348zM5.785 5.991c.642 0 1.162.529 1.162 1.18 0 .659-.52 1.188-1.162 1.188-.642 0-1.162-.529-1.162-1.188 0-.651.52-1.18 1.162-1.18zm5.813 0c.642 0 1.162.529 1.162 1.18 0 .659-.52 1.188-1.162 1.188-.642 0-1.162-.529-1.162-1.188 0-.651.52-1.18 1.162-1.18z"/>
<path d="M24 14.388c0-3.14-2.956-5.689-6.594-5.689-3.638 0-6.594 2.549-6.594 5.689 0 3.14 2.956 5.689 6.594 5.689a7.84 7.84 0 0 0 2.276-.336c.237-.063.473-.018.675.078l1.485.87a.24.24 0 0 0 .13.04c.127 0 .23-.103.23-.23 0-.056-.022-.107-.037-.158l-.305-1.115a.463.463 0 0 1 .167-.513C23.05 17.898 24 16.213 24 14.388z"/>
</svg>
</button>
<button
onClick={() => shareArticle('facebook')}
className="w-10 h-10 bg-blue-600 text-white rounded-full flex items-center justify-center hover:bg-blue-700 transition-colors"
title="分享到Facebook"
>
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/>
</svg>
</button>
<button
onClick={() => shareArticle('twitter')}
className="w-10 h-10 bg-sky-500 text-white rounded-full flex items-center justify-center hover:bg-sky-600 transition-colors"
title="分享到Twitter"
>
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/>
</svg>
</button>
<button
onClick={() => shareArticle('linkedin')}
className="w-10 h-10 bg-blue-700 text-white rounded-full flex items-center justify-center hover:bg-blue-800 transition-colors"
title="分享到LinkedIn"
>
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 24 24">
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
</svg>
</button>
</div>
</div>
{/* 返回顶部按钮 */}
{showBackToTop && (
<button
onClick={scrollToTop}
className="fixed bottom-8 right-6 w-12 h-12 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-full shadow-lg hover:shadow-xl transition-all duration-200 z-40 flex items-center justify-center"
title="返回顶部"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 10l7-7m0 0l7 7m-7-7v18" />
</svg>
</button>
)}
{/* Navigation */}
<nav className="bg-white/80 backdrop-blur-md border-b border-white/20 sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center h-16">
<Link href={`/${params.locale}`} className="flex items-center space-x-2">
<div className="w-8 h-8 bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg flex items-center justify-center">
<svg className="w-5 h-5 text-white" fill="currentColor" viewBox="0 0 20 20">
<path d="M3 4a1 1 0 011-1h12a1 1 0 011 1v2a1 1 0 01-1 1H4a1 1 0 01-1-1V4zM3 10a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H4a1 1 0 01-1-1v-6zM14 9a1 1 0 00-1 1v6a1 1 0 001 1h2a1 1 0 001-1v-6a1 1 0 00-1-1h-2z" />
</svg>
</div>
<span className="text-xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">
CloudTech
</span>
</Link>
<div className="hidden md:flex items-center space-x-8">
<Link href={`/${params.locale}`} className="text-gray-700 hover:text-blue-600 font-medium transition-colors">
{params.locale === 'en' ? 'Home' : params.locale === 'zh-TW' ? '首頁' : '首页'}
</Link>
<Link href={`/${params.locale}/products`} className="text-gray-700 hover:text-blue-600 font-medium transition-colors">
{params.locale === 'en' ? 'Products' : params.locale === 'zh-TW' ? '產品與服務' : '产品与服务'}
</Link>
<Link href={`/${params.locale}/news`} className="text-blue-600 font-medium">
{params.locale === 'en' ? 'News' : params.locale === 'zh-TW' ? '新聞資訊' : '新闻资讯'}
</Link>
<Link href={`/${params.locale}/support`} className="text-gray-700 hover:text-blue-600 font-medium transition-colors">
{params.locale === 'en' ? 'Support' : params.locale === 'zh-TW' ? '客戶支持' : '客户支持'}
</Link>
<Link href={`/${params.locale}/about`} className="text-gray-700 hover:text-blue-600 font-medium transition-colors">
{params.locale === 'en' ? 'About' : params.locale === 'zh-TW' ? '關於我們' : '关于我们'}
</Link>
</div>
<div className="flex items-center space-x-4">
<select
value={params.locale}
onChange={(e) => {
const newLocale = e.target.value;
const currentPath = window.location.pathname;
const pathWithoutLocale = currentPath.replace(/^\/[^\/]+/, '');
window.location.href = `/${newLocale}${pathWithoutLocale}`;
}}
className="bg-white/50 border border-white/30 rounded-lg px-3 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
title={params.locale === 'en' ? 'Select Language' : params.locale === 'zh-TW' ? '選擇語言' : '选择语言'}
>
<option value="zh-CN"></option>
<option value="zh-TW"></option>
<option value="en">English</option>
</select>
</div>
</div>
</div>
</nav>
{/* Article Content */}
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
<div className="bg-white rounded-3xl shadow-lg overflow-hidden">
{/* Article Header */}
<div className="bg-gradient-to-r from-blue-500 to-purple-600 text-white p-8">
<div className="flex items-center mb-4">
<Link
href={`/${params.locale}/news`}
className="text-white/80 hover:text-white font-medium flex items-center"
>
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
</svg>
{params.locale === 'en' ? 'Back to News' : params.locale === 'zh-TW' ? '返回新聞列表' : '返回新闻列表'}
</Link>
</div>
<h1 className="text-3xl md:text-4xl font-bold mb-4">{article.title}</h1>
<div className="flex flex-wrap items-center space-x-6 text-white/80">
<div className="flex items-center">
<svg className="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z" clipRule="evenodd" />
</svg>
{article.date}
</div>
{article.author && (
<div className="flex items-center">
<svg className="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z" clipRule="evenodd" />
</svg>
{article.author}
</div>
)}
{article.category && (
<div className="flex items-center">
<svg className="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M17.707 9.293a1 1 0 010 1.414l-7 7a1 1 0 01-1.414 0l-7-7A.997.997 0 012 10V5a3 3 0 013-3h5c.256 0 .512.098.707.293l7 7zM5 6a1 1 0 100-2 1 1 0 000 2z" clipRule="evenodd" />
</svg>
{article.category}
</div>
)}
<div className="flex items-center">
<svg className="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clipRule="evenodd" />
</svg>
{params.locale === 'en' ? `About ${calculateReadingTime(article.content || '')} min read` :
params.locale === 'zh-TW' ? `${calculateReadingTime(article.content || '')} 分鐘閱讀` :
`${calculateReadingTime(article.content || '')} 分钟阅读`}
</div>
</div>
{article.tags && article.tags.length > 0 && (
<div className="flex flex-wrap gap-2 mt-4">
{article.tags.map((tag: string, index: number) => (
<span
key={index}
className="px-3 py-1 bg-white/20 rounded-full text-sm font-medium"
>
#{tag}
</span>
))}
</div>
)}
</div>
{/* Article Body */}
<div className="p-8">
{article.summary && (
<div className="bg-blue-50 border-l-4 border-blue-500 p-4 mb-8">
<p className="text-blue-700 font-medium">{article.summary}</p>
</div>
)}
<div className="prose prose-lg max-w-none">
<div
dangerouslySetInnerHTML={{
__html: `<p class="mb-4">${renderMarkdown(article.content)}</p>`
}}
/>
</div>
</div>
</div>
{/* 相关文章推荐 */}
{relatedArticles.length > 0 && (
<div className="mt-12">
<div className="bg-white rounded-3xl shadow-lg p-8">
<h3 className="text-2xl font-bold text-gray-900 mb-6 flex items-center">
<svg className="w-6 h-6 mr-2 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
{params.locale === 'en' ? 'Related Articles' : params.locale === 'zh-TW' ? '相關推薦' : '相关推荐'}
</h3>
<div className="grid md:grid-cols-3 gap-6">
{relatedArticles.map((relatedArticle, index) => (
<Link
key={index}
href={`/${params.locale}/news/${relatedArticle.slug}`}
className="group block"
>
<div className="bg-gradient-to-br from-slate-50 to-blue-50 rounded-xl p-6 h-full hover:shadow-md transition-all duration-200 border border-gray-100 group-hover:border-blue-200">
<div className="flex items-center mb-3">
<div className="w-2 h-2 bg-blue-500 rounded-full mr-2"></div>
<span className="text-sm text-gray-500">
{relatedArticle.category || '技术'}
</span>
</div>
<h4 className="font-bold text-gray-900 mb-2 group-hover:text-blue-600 transition-colors line-clamp-2">
{relatedArticle.title}
</h4>
<p className="text-gray-600 text-sm mb-3 line-clamp-3">
{relatedArticle.summary}
</p>
<div className="flex items-center text-xs text-gray-500">
<svg className="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z" clipRule="evenodd" />
</svg>
{relatedArticle.date}
</div>
</div>
</Link>
))}
</div>
</div>
</div>
)}
{/* 文章操作区域 */}
<div className="mt-12">
<div className="bg-white rounded-3xl shadow-lg p-8">
<div className="flex flex-col md:flex-row items-center justify-between space-y-4 md:space-y-0">
<div className="flex items-center space-x-4">
<div className="flex items-center space-x-2">
<svg className="w-5 h-5 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path d="M15 8a3 3 0 10-2.977-2.63l-4.94 2.47a3 3 0 100 4.319l4.94 2.47a3 3 0 10.895-1.789l-4.94-2.47a3.027 3.027 0 000-.74l4.94-2.47C13.456 7.68 14.19 8 15 8z" />
</svg>
<span className="text-gray-600 font-medium">
{params.locale === 'en' ? 'Share Article' : params.locale === 'zh-TW' ? '分享文章' : '分享文章'}
</span>
</div>
<div className="flex space-x-2">
<button
onClick={() => shareArticle('wechat')}
className="px-3 py-1 bg-green-100 text-green-700 rounded-lg hover:bg-green-200 transition-colors text-sm flex items-center space-x-1"
>
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
<path d="M8.691 2.188C3.891 2.188 0 5.476 0 9.53c0 2.212 1.17 4.203 3.002 5.55a.59.59 0 0 1 .213.665l-.39 1.48c-.019.07-.048.141-.048.213 0 .163.13.295.29.295a.326.326 0 0 0 .167-.054l1.903-1.114a.864.864 0 0 1 .717-.098 10.16 10.16 0 0 0 2.837.403c.276 0 .543-.027.811-.05-.857-2.578.157-4.972 1.932-6.446 1.703-1.415 3.882-1.98 5.853-1.838-.576-3.583-4.196-6.348-8.596-6.348z"/>
<path d="M24 14.388c0-3.14-2.956-5.689-6.594-5.689-3.638 0-6.594 2.549-6.594 5.689 0 3.14 2.956 5.689 6.594 5.689a7.84 7.84 0 0 0 2.276-.336c.237-.063.473-.018.675.078l1.485.87a.24.24 0 0 0 .13.04c.127 0 .23-.103.23-.23 0-.056-.022-.107-.037-.158l-.305-1.115a.463.463 0 0 1 .167-.513C23.05 17.898 24 16.213 24 14.388z"/>
</svg>
<span></span>
</button>
<button
onClick={() => shareArticle('facebook')}
className="px-3 py-1 bg-blue-100 text-blue-700 rounded-lg hover:bg-blue-200 transition-colors text-sm flex items-center space-x-1"
>
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
<path d="M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z"/>
</svg>
<span>Facebook</span>
</button>
<button
onClick={() => shareArticle('twitter')}
className="px-3 py-1 bg-blue-100 text-blue-700 rounded-lg hover:bg-blue-200 transition-colors text-sm flex items-center space-x-1"
>
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 24 24">
<path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/>
</svg>
<span>Twitter</span>
</button>
</div>
</div>
<Link
href={`/${params.locale}/news`}
className="inline-flex items-center px-6 py-3 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-lg hover:shadow-lg transition-all duration-200"
>
<svg className="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M12.707 5.293a1 1 0 010 1.414L9.414 10l3.293 3.293a1 1 0 01-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
{params.locale === 'en' ? 'View More News' : params.locale === 'zh-TW' ? '查看更多新聞' : '查看更多新闻'}
</Link>
</div>
</div>
</div>
</div>
</div>
</>
);
}