feat: 初始化多Agent协作系统项目并添加直播回放功能

- 添加导航栏组件及直播回放按钮
- 实现视频播放模态框
- 配置赛博朋克风格主题
- 添加课程首页和课程页面

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
KQL
2025-11-03 14:36:16 +08:00
commit cc390fc756
107 changed files with 31444 additions and 0 deletions

View File

@@ -0,0 +1,359 @@
import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
// 网络节点组件 - 代表Agent
const NetworkNode: React.FC<{
id: number;
onConnect: (id: number) => void;
connections: number[];
}> = ({ id, onConnect, connections }) => {
const [position] = useState({
x: Math.random() * 90 + 5,
y: Math.random() * 90 + 5,
size: Math.random() * 8 + 4,
});
const [isActive, setIsActive] = useState(false);
useEffect(() => {
const interval = setInterval(() => {
if (Math.random() < 0.3) {
setIsActive(true);
onConnect(id);
setTimeout(() => setIsActive(false), 2000);
}
}, Math.random() * 8000 + 5000);
return () => clearInterval(interval);
}, [id, onConnect]);
return (
<motion.div
className="absolute"
style={{
left: `${position.x}%`,
top: `${position.y}%`,
width: `${position.size}px`,
height: `${position.size}px`,
}}
initial={{ opacity: 0, scale: 0 }}
animate={{
opacity: isActive ? 0.9 : 0.4,
scale: isActive ? 1.5 : 1,
}}
transition={{
duration: 0.5,
opacity: { duration: 0.3 }
}}
>
{/* 节点核心 */}
<div
className={`w-full h-full rounded-full transition-all duration-500 ${
isActive
? 'bg-gradient-to-r from-cyber-pink-400 to-neon-purple-400 shadow-lg'
: 'bg-gradient-to-r from-cyber-pink-500/50 to-neon-purple-500/50'
}`}
style={{
boxShadow: isActive
? `0 0 ${position.size * 2}px rgba(6, 182, 212, 0.6), 0 0 ${position.size * 4}px rgba(16, 185, 129, 0.3)`
: `0 0 ${position.size}px rgba(6, 182, 212, 0.2)`
}}
/>
{/* 活跃时的脉冲效果 */}
{isActive && (
<motion.div
className="absolute inset-0 rounded-full border-2 border-neon-cyan-300"
initial={{ scale: 1, opacity: 0.6 }}
animate={{ scale: 3, opacity: 0 }}
transition={{ duration: 1.5, ease: "easeOut" }}
/>
)}
</motion.div>
);
};
// 数据流粒子组件
const DataParticle: React.FC<{ path: { start: {x: number, y: number}, end: {x: number, y: number} } }> = ({ path }) => {
const [isVisible, setIsVisible] = useState(true);
useEffect(() => {
const timer = setTimeout(() => setIsVisible(false), 3000);
return () => clearTimeout(timer);
}, []);
if (!isVisible) return null;
const distance = Math.sqrt(
Math.pow(path.end.x - path.start.x, 2) + Math.pow(path.end.y - path.start.y, 2)
);
return (
<motion.div
className="absolute w-2 h-2 bg-neon-purple-400 rounded-full"
style={{
left: `${path.start.x}%`,
top: `${path.start.y}%`,
boxShadow: '0 0 8px rgba(16, 185, 129, 0.8)'
}}
initial={{
x: 0,
y: 0,
opacity: 0,
scale: 0.5
}}
animate={{
x: `${(path.end.x - path.start.x) * (100/100)}%`,
y: `${(path.end.y - path.start.y) * (100/100)}%`,
opacity: [0, 1, 1, 0],
scale: [0.5, 1, 1, 0.5]
}}
transition={{
duration: Math.max(distance / 20, 2),
ease: "easeInOut"
}}
/>
);
};
// 连接线组件
const ConnectionLine: React.FC<{
start: {x: number, y: number};
end: {x: number, y: number};
isActive: boolean;
}> = ({ start, end, isActive }) => {
const length = Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2));
const angle = Math.atan2(end.y - start.y, end.x - start.x) * 180 / Math.PI;
return (
<motion.div
className="absolute origin-left"
style={{
left: `${start.x}%`,
top: `${start.y}%`,
width: `${length}%`,
height: '1px',
transform: `rotate(${angle}deg)`,
background: isActive
? 'linear-gradient(90deg, rgba(6, 182, 212, 0.8), rgba(16, 185, 129, 0.8))'
: 'linear-gradient(90deg, rgba(6, 182, 212, 0.2), rgba(16, 185, 129, 0.2))',
boxShadow: isActive ? '0 0 4px rgba(6, 182, 212, 0.5)' : 'none'
}}
initial={{ scaleX: 0, opacity: 0 }}
animate={{
scaleX: 1,
opacity: isActive ? 0.8 : 0.3
}}
exit={{ scaleX: 0, opacity: 0 }}
transition={{ duration: 0.5 }}
/>
);
};
// 主背景组件
const AgentNetworkBackground: React.FC = () => {
const [activeConnections, setActiveConnections] = useState<{
start: {x: number, y: number};
end: {x: number, y: number};
id: string;
}[]>([]);
const [dataParticles, setDataParticles] = useState<{
path: { start: {x: number, y: number}, end: {x: number, y: number} };
id: string;
}[]>([]);
const nodePositions = React.useRef<{[key: number]: {x: number, y: number}}>({});
// 初始化节点位置
React.useEffect(() => {
for (let i = 0; i < 12; i++) {
nodePositions.current[i] = {
x: Math.random() * 90 + 5,
y: Math.random() * 90 + 5
};
}
}, []);
const handleNodeConnect = (nodeId: number) => {
// 随机选择其他节点连接
const otherNodes = Object.keys(nodePositions.current)
.map(Number)
.filter(id => id !== nodeId);
if (otherNodes.length === 0) return;
const targetId = otherNodes[Math.floor(Math.random() * otherNodes.length)];
const start = nodePositions.current[nodeId];
const end = nodePositions.current[targetId];
if (!start || !end) return;
const connectionId = `${nodeId}-${targetId}-${Date.now()}`;
// 添加连接线
const newConnection = { start, end, id: connectionId };
setActiveConnections(prev => [...prev, newConnection]);
// 添加数据粒子
const particleId = `particle-${Date.now()}`;
setDataParticles(prev => [...prev, { path: { start, end }, id: particleId }]);
// 清除连接线
setTimeout(() => {
setActiveConnections(prev => prev.filter(conn => conn.id !== connectionId));
}, 3000);
// 清除数据粒子
setTimeout(() => {
setDataParticles(prev => prev.filter(p => p.id !== particleId));
}, 3000);
};
return (
<div className="fixed inset-0 pointer-events-none overflow-hidden z-0">
{/* 基础深色技术背景 */}
<div
className="absolute inset-0"
style={{
background: `
radial-gradient(circle at 25% 25%, rgba(15, 23, 42, 0.8) 0%, transparent 50%),
radial-gradient(circle at 75% 75%, rgba(8, 145, 178, 0.1) 0%, transparent 50%),
linear-gradient(135deg,
#0f172a 0%,
#1e293b 25%,
#0f172a 50%,
#164e63 75%,
#0f172a 100%
)
`
}}
/>
{/* 网格背景 - 体现技术感 */}
<div
className="absolute inset-0 opacity-[0.08]"
style={{
backgroundImage: `
linear-gradient(rgba(6, 182, 212, 0.3) 1px, transparent 1px),
linear-gradient(90deg, rgba(6, 182, 212, 0.3) 1px, transparent 1px)
`,
backgroundSize: '50px 50px'
}}
/>
{/* 动态光晕效果 */}
<motion.div
className="absolute inset-0"
style={{
background: `
radial-gradient(600px circle at var(--mouse-x, 50%) var(--mouse-y, 50%),
rgba(6, 182, 212, 0.1) 0%,
rgba(16, 185, 129, 0.05) 40%,
transparent 100%
)
`
}}
animate={{
'--mouse-x': ['30%', '70%', '30%'],
'--mouse-y': ['40%', '60%', '40%']
} as any}
transition={{
duration: 20,
repeat: Infinity,
ease: "easeInOut"
}}
/>
{/* 数据流背景效果 */}
<motion.div
className="absolute inset-0"
style={{
background: `
linear-gradient(45deg,
transparent 0%,
rgba(6, 182, 212, 0.05) 25%,
transparent 50%,
rgba(16, 185, 129, 0.05) 75%,
transparent 100%
)
`,
transform: 'translateX(-100%)'
}}
animate={{
transform: ['translateX(-100%)', 'translateX(100%)']
}}
transition={{
duration: 15,
repeat: Infinity,
ease: "linear"
}}
/>
{/* 连接线层 */}
<div className="absolute inset-0">
<AnimatePresence>
{activeConnections.map(connection => (
<ConnectionLine
key={connection.id}
start={connection.start}
end={connection.end}
isActive={true}
/>
))}
</AnimatePresence>
</div>
{/* 网络节点 - Agent */}
<div className="absolute inset-0">
{Array.from({ length: 12 }, (_, i) => (
<NetworkNode
key={i}
id={i}
onConnect={handleNodeConnect}
connections={[]}
/>
))}
</div>
{/* 数据粒子层 */}
<div className="absolute inset-0">
<AnimatePresence>
{dataParticles.map(particle => (
<DataParticle
key={particle.id}
path={particle.path}
/>
))}
</AnimatePresence>
</div>
{/* 顶部渐变遮罩 - 确保文字可读性 */}
<div
className="absolute inset-0 pointer-events-none"
style={{
background: `
linear-gradient(180deg,
rgba(15, 23, 42, 0.3) 0%,
transparent 20%,
transparent 80%,
rgba(15, 23, 42, 0.2) 100%
)
`
}}
/>
{/* 性能优化 */}
<style>{`
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
`}</style>
</div>
);
};
export default AgentNetworkBackground;