'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(null);
const [loading, setLoading] = useState(true);
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [readingProgress, setReadingProgress] = useState(0);
const [relatedArticles, setRelatedArticles] = useState([]);
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, '$1
')
.replace(/^## (.*$)/gim, '$1
')
.replace(/^# (.*$)/gim, '$1
')
// 粗体和斜体
.replace(/\*\*(.*?)\*\*/g, '$1')
.replace(/\*(.*?)\*/g, '$1')
// 列表处理
.replace(/^\s*\* (.*$)/gim, '• $1')
.replace(/^\s*- (.*$)/gim, '• $1')
.replace(/^\s*\d+\. (.*$)/gim, '$1')
// 链接处理
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1')
// 代码块处理
.replace(/```([^`]+)```/g, '$1
')
.replace(/`([^`]+)`/g, '$1')
// 段落处理
.replace(/\n\n/g, '
')
// 换行处理
.replace(/\n/g, '
');
};
if (loading) {
return (
);
}
if (!article) {
return (
{params.locale === 'en' ? 'Article Not Found' :
params.locale === 'zh-TW' ? '文章未找到' : '文章未找到'}
{params.locale === 'en' ? 'Sorry, the article you are looking for does not exist.' :
params.locale === 'zh-TW' ? '抱歉,您訪問的文章不存在。' : '抱歉,您访问的文章不存在。'}
{params.locale === 'en' ? 'Back to News' :
params.locale === 'zh-TW' ? '返回新聞列表' : '返回新闻列表'}
);
}
// 处理语言不可用的情况
if (article.error === 'language_not_available') {
return (
{params.locale === 'en' ? 'Article Not Available in English' :
params.locale === 'zh-TW' ? '文章暫無繁體中文版本' : '文章暂无简体中文版本'}
{params.locale === 'en' ? 'This article is available in other languages.' :
params.locale === 'zh-TW' ? '此文章有其他語言版本可供閱讀。' : '此文章有其他语言版本可供阅读。'}
{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' ? '英文' : '简体中文'}版本`}
{params.locale === 'en' ? 'Back to News' :
params.locale === 'zh-TW' ? '返回新聞列表' : '返回新闻列表'}
);
}
return (
<>
{/* 优雅的阅读进度条 */}
0 ? '0 0 10px rgba(139, 92, 246, 0.3)' : 'none'
}}
>
{/* 悬浮分享按钮 */}
{/* 返回顶部按钮 */}
{showBackToTop && (
)}
{/* Navigation */}
{/* Article Content */}
{/* Article Header */}
{params.locale === 'en' ? 'Back to News' : params.locale === 'zh-TW' ? '返回新聞列表' : '返回新闻列表'}
{article.title}
{article.author && (
)}
{article.category && (
)}
{params.locale === 'en' ? `About ${calculateReadingTime(article.content || '')} min read` :
params.locale === 'zh-TW' ? `約 ${calculateReadingTime(article.content || '')} 分鐘閱讀` :
`约 ${calculateReadingTime(article.content || '')} 分钟阅读`}
{article.tags && article.tags.length > 0 && (
{article.tags.map((tag: string, index: number) => (
#{tag}
))}
)}
{/* Article Body */}
{article.summary && (
)}
${renderMarkdown(article.content)}`
}}
/>
{/* 相关文章推荐 */}
{relatedArticles.length > 0 && (
{params.locale === 'en' ? 'Related Articles' : params.locale === 'zh-TW' ? '相關推薦' : '相关推荐'}
{relatedArticles.map((relatedArticle, index) => (
{relatedArticle.category || '技术'}
{relatedArticle.title}
{relatedArticle.summary}
))}
)}
{/* 文章操作区域 */}
{params.locale === 'en' ? 'Share Article' : params.locale === 'zh-TW' ? '分享文章' : '分享文章'}
{params.locale === 'en' ? 'View More News' : params.locale === 'zh-TW' ? '查看更多新聞' : '查看更多新闻'}
>
);
}