first commit

This commit is contained in:
“dongming”
2026-01-21 16:05:30 +08:00
commit faa74086fe
69 changed files with 14801 additions and 0 deletions

View File

@@ -0,0 +1,183 @@
import { motion } from 'framer-motion';
import { Calendar, ArrowRight } from 'lucide-react';
import { Link } from 'react-router-dom';
import { formatDate } from '../../lib/utils';
/**
* NewsSection 组件 - 最新动态区域
*/
// 模拟新闻数据
const newsItems = [
{
id: 1,
category: 'company',
title: '示例集团荣获"2025年度优秀企业"称号',
excerpt: '在近日举办的年度企业评选活动中,示例集团凭借其卓越的经营业绩和社会责任表现,荣获"2025年度优秀企业"称号。',
date: '2025-12-20',
image: '/images/news-award.jpg',
},
{
id: 2,
category: 'industry',
title: '金融科技创新论坛圆满落幕,示例集团分享行业洞察',
excerpt: '示例集团受邀参加金融科技创新论坛,与行业专家共同探讨金融科技发展趋势,分享公司在数字化转型方面的实践经验。',
date: '2025-12-15',
image: '/images/news-tech.jpg',
},
{
id: 3,
category: 'achievement',
title: '示例集团完成新一轮战略融资,估值突破百亿',
excerpt: '示例集团宣布完成新一轮战略融资,本轮融资由知名投资机构领投,估值突破百亿元人民币,标志着公司发展进入新阶段。',
date: '2025-12-10',
image: '/images/news-company.jpg',
},
];
// 新闻分类映射
const categoryMap: Record<string, { label: string; color: string }> = {
company: { label: '公司动态', color: 'bg-primary/10 text-primary' },
industry: { label: '行业资讯', color: 'bg-accent/20 text-accent-dark' },
achievement: { label: '荣誉资质', color: 'bg-green-100 text-green-700' },
};
/**
* 新闻卡片组件
*/
const NewsCard: React.FC<{
news: typeof newsItems[0];
index: number;
}> = ({ news, index }) => {
const category = categoryMap[news.category] || categoryMap.company;
return (
<motion.article
className="group bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden hover:shadow-lg transition-shadow duration-300"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ delay: index * 0.1, duration: 0.5 }}
whileHover={{ y: -5 }}
>
{/* 图片区域 */}
<div className="aspect-[16/9] bg-gradient-to-br from-primary/5 to-primary-light/10 relative overflow-hidden">
{news.image ? (
<img
src={news.image}
alt=""
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
loading="lazy"
/>
) : (
<div className="text-primary/20">
<svg
className="w-16 h-16 absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1}
d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"
/>
</svg>
</div>
)}
{/* 分类标签 */}
<span
className={`absolute top-4 left-4 px-3 py-1 text-xs font-medium rounded-full ${category.color}`}
>
{category.label}
</span>
{/* 图片遮罩 */}
<div className="absolute inset-0 bg-gradient-to-t from-black/30 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
</div>
{/* 内容区域 */}
<div className="p-6">
{/* 日期 */}
<div className="flex items-center gap-2 text-sm text-gray-500 mb-3">
<Calendar size={14} />
<time dateTime={news.date}>{formatDate(news.date, 'YYYY年MM月DD日')}</time>
</div>
{/* 标题 */}
<h3 className="text-lg font-semibold text-primary-dark mb-3 line-clamp-2 group-hover:text-primary transition-colors">
{news.title}
</h3>
{/* 摘要 */}
<p className="text-gray-600 text-sm leading-relaxed line-clamp-3 mb-4">
{news.excerpt}
</p>
{/* 了解更多链接 */}
<Link
to={`/news/${news.id}`}
className="inline-flex items-center gap-2 text-sm font-medium text-primary group-hover:text-primary-light transition-colors"
>
<motion.span
initial={{ x: 0 }}
whileHover={{ x: 5 }}
transition={{ duration: 0.2 }}
>
<ArrowRight size={16} />
</motion.span>
</Link>
</div>
</motion.article>
);
};
export const NewsSection: React.FC = () => {
return (
<section
id="news"
className="py-20 lg:py-28 bg-background"
aria-labelledby="news-heading"
>
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
{/* 标题区域 */}
<motion.div
className="flex flex-col md:flex-row md:items-end md:justify-between gap-4 mb-12"
initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.6 }}
>
<div>
<h2
id="news-heading"
className="text-3xl md:text-4xl font-bold text-primary-dark"
>
</h2>
<p className="mt-2 text-lg text-gray-600">
</p>
</div>
<Link
to="/news"
className="inline-flex items-center gap-2 text-primary font-medium hover:text-primary-light transition-colors"
>
<ArrowRight size={18} />
</Link>
</motion.div>
{/* 新闻卡片网格 */}
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
{newsItems.map((news, index) => (
<NewsCard key={news.id} news={news} index={index} />
))}
</div>
</div>
</section>
);
};
export default NewsSection;