code
import React, { useState } from 'react';
import { Moon, Sun } from 'lucide-react';
const TimelineComponent = () => {
const [isDark, setIsDark] = useState(false);
// 示例数据
const timelineData = [
{
year: '2026',
items: ['云原生架构升级', '智能运维平台', '全球化部署'],
status: 'future'
},
{
year: '2025',
items: ['AI测试应用', '影子化验证', '测试工具链'],
status: 'current'
},
{
year: '2020',
items: ['精准测试', '安全测试', '超自动化'],
status: 'past'
},
{
year: '2018',
items: ['测试能力系统化', 'C2C业样'],
status: 'past'
}
];
return (
<div className={`min-h-screen transition-colors duration-300 ${isDark ? 'bg-gray-900' : 'bg-gray-50'}`}>
{/* 主题切换按钮 */}
<div className="fixed top-6 right-6 z-10">
<button
onClick={() => setIsDark(!isDark)}
className={`p-2.5 rounded-full shadow-lg transition-all duration-300 ${
isDark
? 'bg-gray-800 text-yellow-400 hover:bg-gray-700'
: 'bg-white text-gray-600 hover:bg-gray-100'
}`}
>
{isDark ? <Sun size={18} /> : <Moon size={18} />}
</button>
</div>
<div className="container mx-auto px-4 py-20">
<div className="max-w-6xl mx-auto">
<div className="flex justify-between relative">
{/* 水平线条 - 在圆点位置 */}
<div
className={`absolute left-0 right-0 h-0.5 transition-colors duration-300 ${
isDark ? 'bg-gray-700' : 'bg-gray-300'
}`}
style={{ top: '56px' }}
/>
{timelineData.map((item, index) => (
<div
key={index}
className="flex flex-col items-center flex-1"
style={{ animation: `fadeInUp 0.5s ease-out ${index * 0.1}s both` }}
>
{/* 年份 */}
<div className={`text-xl font-semibold mb-6 transition-colors ${
item.status === 'current'
? isDark ? 'text-blue-400' : 'text-blue-600'
: isDark ? 'text-gray-400' : 'text-gray-600'
}`}>
{item.year}
</div>
{/* 圆点 */}
<div className="relative z-10 mb-6">
<div className={`w-3.5 h-3.5 rounded-full transition-all duration-300 ${
item.status === 'current'
? isDark
? 'bg-blue-500 shadow-lg shadow-blue-500/50 ring-4 ring-blue-500/20'
: 'bg-blue-500 shadow-lg shadow-blue-500/30 ring-4 ring-blue-500/20'
: item.status === 'future'
? isDark
? 'bg-orange-500 ring-2 ring-gray-900'
: 'bg-orange-400 ring-2 ring-gray-50'
: isDark
? 'bg-blue-600 ring-2 ring-gray-900'
: 'bg-blue-500 ring-2 ring-gray-50'
}`}>
{item.status === 'current' && (
<>
<div className="absolute inset-0 rounded-full bg-blue-400 animate-ping opacity-75" />
<div className="absolute inset-0 rounded-full bg-blue-400 animate-pulse" />
</>
)}
</div>
</div>
{/* 内容卡片 */}
<div className={`rounded-lg p-4 w-full max-w-[200px] transition-all duration-300 ${
isDark
? 'bg-gray-800/50 backdrop-blur-sm border border-gray-700/50'
: 'bg-white/80 backdrop-blur-sm border border-gray-200'
} hover:shadow-lg ${
item.status === 'current'
? isDark
? 'shadow-md shadow-blue-500/10'
: 'shadow-md shadow-blue-500/5'
: ''
}`}>
<div className="space-y-1.5">
{item.items.map((text, idx) => (
<div
key={idx}
className={`text-sm leading-relaxed transition-colors ${
isDark
? 'text-gray-300'
: 'text-gray-700'
}`}
>
{text}
</div>
))}
</div>
</div>
</div>
))}
</div>
</div>
</div>
<style jsx>{`
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(15px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
`}</style>
</div>
);
};
export default TimelineComponent;