343 lines
15 KiB
TypeScript
343 lines
15 KiB
TypeScript
|
|
'use client';
|
||
|
|
|
||
|
|
import { useState, useEffect } from 'react';
|
||
|
|
import { Navigation } from '../../components/Navigation';
|
||
|
|
import { Footer } from '../../components/Footer';
|
||
|
|
import { Head } from '../../components/Head';
|
||
|
|
import { Calendar, User, Tag, ArrowRight, Clock } from 'lucide-react';
|
||
|
|
import Link from 'next/link';
|
||
|
|
|
||
|
|
interface NewsArticle {
|
||
|
|
id: string;
|
||
|
|
category: string;
|
||
|
|
title: string;
|
||
|
|
excerpt: string;
|
||
|
|
author: string;
|
||
|
|
date: string;
|
||
|
|
readTime: number;
|
||
|
|
image?: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export default function NewsPage() {
|
||
|
|
const [currentLang, setCurrentLang] = useState<'zh' | 'zh-tw' | 'en'>('zh');
|
||
|
|
const [selectedCategory, setSelectedCategory] = useState('all');
|
||
|
|
const [articles, setArticles] = useState<NewsArticle[]>([]);
|
||
|
|
const [loading, setLoading] = useState(true);
|
||
|
|
|
||
|
|
const translations = {
|
||
|
|
zh: {
|
||
|
|
title: '资讯中心 - CloudPro 云服务器代理商',
|
||
|
|
description:
|
||
|
|
'获取最新的云计算资讯、技术文章、产品更新和行业动态,了解云服务器和云计算的最新发展趋势。',
|
||
|
|
keywords: '云计算资讯,技术文章,产品更新,行业动态,云服务器新闻',
|
||
|
|
nav: {
|
||
|
|
home: '首页',
|
||
|
|
products: '产品',
|
||
|
|
solutions: '解决方案',
|
||
|
|
support: '支持',
|
||
|
|
news: '资讯',
|
||
|
|
contact: '联系我们',
|
||
|
|
},
|
||
|
|
hero: {
|
||
|
|
title: '资讯中心',
|
||
|
|
subtitle: '了解云计算最新动态',
|
||
|
|
description: '获取最新的技术资讯、产品更新和行业洞察',
|
||
|
|
},
|
||
|
|
categories: {
|
||
|
|
all: '全部',
|
||
|
|
tech: '技术文章',
|
||
|
|
product: '产品更新',
|
||
|
|
industry: '行业动态',
|
||
|
|
tutorial: '教程指南',
|
||
|
|
},
|
||
|
|
readMore: '阅读更多',
|
||
|
|
readTime: '阅读时间',
|
||
|
|
minutes: '分钟',
|
||
|
|
loading: '加载中...',
|
||
|
|
noArticles: '暂无文章',
|
||
|
|
},
|
||
|
|
'zh-tw': {
|
||
|
|
title: '資訊中心 - CloudPro 雲伺服器代理商',
|
||
|
|
description:
|
||
|
|
'獲取最新的雲端運算資訊、技術文章、產品更新和行業動態,了解雲伺服器和雲端運算的最新發展趨勢。',
|
||
|
|
keywords: '雲端運算資訊,技術文章,產品更新,行業動態,雲伺服器新聞',
|
||
|
|
nav: {
|
||
|
|
home: '首頁',
|
||
|
|
products: '產品',
|
||
|
|
solutions: '解決方案',
|
||
|
|
support: '支援',
|
||
|
|
news: '資訊',
|
||
|
|
contact: '聯絡我們',
|
||
|
|
},
|
||
|
|
hero: {
|
||
|
|
title: '資訊中心',
|
||
|
|
subtitle: '了解雲端運算最新動態',
|
||
|
|
description: '獲取最新的技術資訊、產品更新和行業洞察',
|
||
|
|
},
|
||
|
|
categories: {
|
||
|
|
all: '全部',
|
||
|
|
tech: '技術文章',
|
||
|
|
product: '產品更新',
|
||
|
|
industry: '行業動態',
|
||
|
|
tutorial: '教學指南',
|
||
|
|
},
|
||
|
|
readMore: '閱讀更多',
|
||
|
|
readTime: '閱讀時間',
|
||
|
|
minutes: '分鐘',
|
||
|
|
loading: '載入中...',
|
||
|
|
noArticles: '暫無文章',
|
||
|
|
},
|
||
|
|
en: {
|
||
|
|
title: 'News Center - CloudPro Cloud Server Reseller',
|
||
|
|
description:
|
||
|
|
'Get the latest cloud computing news, technical articles, product updates and industry insights about cloud servers and cloud computing trends.',
|
||
|
|
keywords:
|
||
|
|
'cloud computing news,technical articles,product updates,industry insights,cloud server news',
|
||
|
|
nav: {
|
||
|
|
home: 'Home',
|
||
|
|
products: 'Products',
|
||
|
|
solutions: 'Solutions',
|
||
|
|
support: 'Support',
|
||
|
|
news: 'News',
|
||
|
|
contact: 'Contact',
|
||
|
|
},
|
||
|
|
hero: {
|
||
|
|
title: 'News Center',
|
||
|
|
subtitle: 'Stay Updated with Cloud Computing',
|
||
|
|
description: 'Get the latest technical insights, product updates and industry news',
|
||
|
|
},
|
||
|
|
categories: {
|
||
|
|
all: 'All',
|
||
|
|
tech: 'Technical Articles',
|
||
|
|
product: 'Product Updates',
|
||
|
|
industry: 'Industry News',
|
||
|
|
tutorial: 'Tutorials',
|
||
|
|
},
|
||
|
|
readMore: 'Read More',
|
||
|
|
readTime: 'Read Time',
|
||
|
|
minutes: 'min',
|
||
|
|
loading: 'Loading...',
|
||
|
|
noArticles: 'No articles found',
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
const t = translations[currentLang];
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const browserLang = navigator.language.toLowerCase();
|
||
|
|
if (browserLang.includes('en')) {
|
||
|
|
setCurrentLang('en');
|
||
|
|
} else if (browserLang.includes('zh-tw') || browserLang.includes('zh-hk')) {
|
||
|
|
setCurrentLang('zh-tw');
|
||
|
|
}
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const loadArticles = async () => {
|
||
|
|
try {
|
||
|
|
setLoading(true);
|
||
|
|
|
||
|
|
// 获取所有文章目录
|
||
|
|
const response = await fetch('/api/articles');
|
||
|
|
if (response.ok) {
|
||
|
|
const articlesData = await response.json();
|
||
|
|
setArticles(articlesData);
|
||
|
|
} else {
|
||
|
|
console.error('Failed to load articles');
|
||
|
|
setArticles([]);
|
||
|
|
}
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Error loading articles:', error);
|
||
|
|
setArticles([]);
|
||
|
|
} finally {
|
||
|
|
setLoading(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
loadArticles();
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const filteredArticles =
|
||
|
|
selectedCategory === 'all'
|
||
|
|
? articles
|
||
|
|
: articles.filter((article) => article.category === selectedCategory);
|
||
|
|
|
||
|
|
const getCategoryColor = (category: string) => {
|
||
|
|
const colors = {
|
||
|
|
tech: 'bg-blue-100 text-blue-800',
|
||
|
|
product: 'bg-green-100 text-green-800',
|
||
|
|
industry: 'bg-purple-100 text-purple-800',
|
||
|
|
tutorial: 'bg-orange-100 text-orange-800',
|
||
|
|
};
|
||
|
|
return colors[category as keyof typeof colors] || 'bg-gray-100 text-gray-800';
|
||
|
|
};
|
||
|
|
|
||
|
|
if (loading) {
|
||
|
|
return (
|
||
|
|
<div className="min-h-screen bg-gradient-to-br from-amber-50 to-yellow-50 flex items-center justify-center">
|
||
|
|
<div className="text-center">
|
||
|
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-amber-600 mx-auto mb-4"></div>
|
||
|
|
<p className="text-amber-700">{t.loading}</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div
|
||
|
|
className="min-h-screen bg-gradient-to-br from-amber-50 to-yellow-50"
|
||
|
|
data-oid="boqb.7r"
|
||
|
|
>
|
||
|
|
<Head
|
||
|
|
title={t.title}
|
||
|
|
description={t.description}
|
||
|
|
keywords={t.keywords}
|
||
|
|
currentLang={currentLang}
|
||
|
|
data-oid="bfx9fff"
|
||
|
|
/>
|
||
|
|
|
||
|
|
<Navigation
|
||
|
|
currentLang={currentLang}
|
||
|
|
onLanguageChange={setCurrentLang}
|
||
|
|
translations={translations}
|
||
|
|
data-oid="hg2jxud"
|
||
|
|
/>
|
||
|
|
|
||
|
|
{/* Hero Section */}
|
||
|
|
<section
|
||
|
|
className="bg-gradient-to-r from-amber-900 to-yellow-800 text-white py-20"
|
||
|
|
data-oid="2hx59ni"
|
||
|
|
>
|
||
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" data-oid="_qad75g">
|
||
|
|
<div className="text-center" data-oid="25h11wr">
|
||
|
|
<h1 className="text-4xl md:text-5xl font-bold mb-6" data-oid="sm1glbj">
|
||
|
|
{t.hero.title}
|
||
|
|
</h1>
|
||
|
|
<p className="text-xl md:text-2xl text-yellow-100 mb-8" data-oid="302vwh6">
|
||
|
|
{t.hero.subtitle}
|
||
|
|
</p>
|
||
|
|
<p className="text-lg text-yellow-200 max-w-2xl mx-auto" data-oid="og47v15">
|
||
|
|
{t.hero.description}
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
{/* Category Filter */}
|
||
|
|
<section className="py-8 bg-white border-b border-amber-200" data-oid="tz-k6ln">
|
||
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" data-oid="9bs7jyn">
|
||
|
|
<div className="flex flex-wrap justify-center gap-4" data-oid="vh_g87_">
|
||
|
|
{Object.entries(t.categories).map(([key, label]) => (
|
||
|
|
<button
|
||
|
|
key={key}
|
||
|
|
onClick={() => setSelectedCategory(key)}
|
||
|
|
className={`px-6 py-3 rounded-full font-medium transition-all duration-200 ${
|
||
|
|
selectedCategory === key
|
||
|
|
? 'bg-gradient-to-r from-amber-500 to-yellow-500 text-white shadow-lg'
|
||
|
|
: 'bg-amber-100 text-amber-700 hover:bg-amber-200'
|
||
|
|
}`}
|
||
|
|
data-oid="0eygd-d"
|
||
|
|
>
|
||
|
|
{label}
|
||
|
|
</button>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
{/* News Grid */}
|
||
|
|
<section className="py-20" data-oid="xw94g35">
|
||
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" data-oid="wwtx4ho">
|
||
|
|
{filteredArticles.length === 0 ? (
|
||
|
|
<div className="text-center py-12">
|
||
|
|
<p className="text-amber-700 text-lg">{t.noArticles}</p>
|
||
|
|
</div>
|
||
|
|
) : (
|
||
|
|
<div
|
||
|
|
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8"
|
||
|
|
data-oid="4xt-f7q"
|
||
|
|
>
|
||
|
|
{filteredArticles.map((article) => (
|
||
|
|
<article
|
||
|
|
key={article.id}
|
||
|
|
className="bg-white rounded-xl shadow-lg hover:shadow-xl transition-all duration-300 overflow-hidden border border-amber-200 group"
|
||
|
|
data-oid="jciu2kd"
|
||
|
|
>
|
||
|
|
<div
|
||
|
|
className="aspect-video bg-gradient-to-br from-amber-100 to-yellow-100 flex items-center justify-center"
|
||
|
|
data-oid="x7t1wke"
|
||
|
|
>
|
||
|
|
<div className="text-amber-600 text-6xl" data-oid="zve:qud">
|
||
|
|
📰
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="p-6" data-oid="_yz_608">
|
||
|
|
<div
|
||
|
|
className="flex items-center justify-between mb-4"
|
||
|
|
data-oid="ybf-qhj"
|
||
|
|
>
|
||
|
|
<span
|
||
|
|
className={`px-3 py-1 rounded-full text-xs font-medium ${getCategoryColor(article.category)}`}
|
||
|
|
data-oid="or3hmoo"
|
||
|
|
>
|
||
|
|
{t.categories[article.category as keyof typeof t.categories] || article.category}
|
||
|
|
</span>
|
||
|
|
<div
|
||
|
|
className="flex items-center text-amber-600 text-sm"
|
||
|
|
data-oid="220a3nu"
|
||
|
|
>
|
||
|
|
<Clock className="w-4 h-4 mr-1" data-oid="kizescv" />
|
||
|
|
{article.readTime} {t.minutes}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<h2
|
||
|
|
className="text-xl font-bold text-amber-900 mb-3 group-hover:text-amber-700 transition-colors"
|
||
|
|
data-oid="4ulljgk"
|
||
|
|
>
|
||
|
|
{article.title}
|
||
|
|
</h2>
|
||
|
|
|
||
|
|
<p
|
||
|
|
className="text-amber-700 mb-4 leading-relaxed"
|
||
|
|
data-oid="8y50obt"
|
||
|
|
>
|
||
|
|
{article.excerpt}
|
||
|
|
</p>
|
||
|
|
|
||
|
|
<div
|
||
|
|
className="flex items-center justify-between text-sm text-amber-600 mb-4"
|
||
|
|
data-oid="71yedyp"
|
||
|
|
>
|
||
|
|
<div className="flex items-center" data-oid="azrio3x">
|
||
|
|
<User className="w-4 h-4 mr-1" data-oid="ymx:c55" />
|
||
|
|
{article.author}
|
||
|
|
</div>
|
||
|
|
<div className="flex items-center" data-oid="dkyta:_">
|
||
|
|
<Calendar className="w-4 h-4 mr-1" data-oid="kb8.f0-" />
|
||
|
|
{article.date}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<Link
|
||
|
|
href={`/news/${article.id}`}
|
||
|
|
className="inline-flex items-center text-amber-600 hover:text-amber-700 font-medium group-hover:translate-x-1 transition-all duration-200"
|
||
|
|
data-oid="7_fsq:6"
|
||
|
|
>
|
||
|
|
{t.readMore}
|
||
|
|
<ArrowRight className="w-4 h-4 ml-2" data-oid="hmhay_i" />
|
||
|
|
</Link>
|
||
|
|
</div>
|
||
|
|
</article>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</section>
|
||
|
|
|
||
|
|
<Footer currentLang={currentLang} translations={translations} data-oid="ot09pr8" />
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|