'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.date}
    {article.author && (
    {article.author}
    )} {article.category && (
    {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 && (

    {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}

    {relatedArticle.date}
    ))}
    )} {/* 文章操作区域 */}
    {params.locale === 'en' ? 'Share Article' : params.locale === 'zh-TW' ? '分享文章' : '分享文章'}
    {params.locale === 'en' ? 'View More News' : params.locale === 'zh-TW' ? '查看更多新聞' : '查看更多新闻'}
    ); }