126 lines
5.8 KiB
JavaScript
126 lines
5.8 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
||
import { motion } from 'framer-motion';
|
||
import { Button, Typography, Spin, Row, Col, Empty, Tag } from 'antd';
|
||
import { ReadOutlined, ClockCircleOutlined, UserOutlined, BookOutlined } from '@ant-design/icons';
|
||
import { getVBCourses } from '../api';
|
||
|
||
const { Title, Paragraph } = Typography;
|
||
|
||
const VBCourses = () => {
|
||
const [courses, setCourses] = useState([]);
|
||
const [loading, setLoading] = useState(true);
|
||
|
||
useEffect(() => {
|
||
const fetchCourses = async () => {
|
||
try {
|
||
const res = await getVBCourses();
|
||
setCourses(res.data);
|
||
} catch (error) {
|
||
console.error("Failed to fetch VB Courses:", error);
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}
|
||
fetchCourses();
|
||
}, []);
|
||
|
||
if (loading) return <div style={{ textAlign: 'center', padding: 100 }}><Spin size="large" /></div>;
|
||
|
||
return (
|
||
<div style={{ padding: '40px 0', minHeight: '80vh', position: 'relative' }}>
|
||
<div style={{ textAlign: 'center', marginBottom: 60, position: 'relative', zIndex: 2 }}>
|
||
<Title level={1} style={{ color: '#fff', letterSpacing: 4 }}>
|
||
VB <span style={{ color: '#00f0ff' }}>CODING COURSES</span>
|
||
</Title>
|
||
<Paragraph style={{ color: '#aaa', fontSize: 18, maxWidth: 600, margin: '0 auto' }}>
|
||
探索 Vibe Coding 软件与硬件课程,开启您的编程之旅。
|
||
</Paragraph>
|
||
</div>
|
||
|
||
{courses.length === 0 ? (
|
||
<div style={{ textAlign: 'center', marginTop: 100, zIndex: 2, position: 'relative' }}>
|
||
<Empty description={<span style={{ color: '#666' }}>暂无课程内容</span>} />
|
||
</div>
|
||
) : (
|
||
<Row gutter={[32, 32]} justify="center" style={{ padding: '0 20px', position: 'relative', zIndex: 2 }}>
|
||
{courses.map((item, index) => (
|
||
<Col xs={24} md={12} lg={8} key={item.id}>
|
||
<motion.div
|
||
initial={{ opacity: 0, y: 30 }}
|
||
animate={{ opacity: 1, y: 0 }}
|
||
transition={{ delay: index * 0.1 }}
|
||
whileHover={{ scale: 1.02 }}
|
||
>
|
||
<div style={{
|
||
background: 'rgba(255,255,255,0.05)',
|
||
border: '1px solid rgba(0,240,255,0.2)',
|
||
borderRadius: 12,
|
||
overflow: 'hidden',
|
||
height: '100%',
|
||
display: 'flex',
|
||
flexDirection: 'column'
|
||
}}>
|
||
<div style={{ height: 200, background: '#000', overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'relative' }}>
|
||
{item.display_cover_image ? (
|
||
<img src={item.display_cover_image} alt={item.title} style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
|
||
) : (
|
||
<ReadOutlined style={{ fontSize: 40, color: '#333' }} />
|
||
)}
|
||
<div style={{ position: 'absolute', top: 10, right: 10, display: 'flex', gap: '5px' }}>
|
||
{item.tag && (
|
||
<Tag color="volcano" style={{ marginRight: 0 }}>{item.tag}</Tag>
|
||
)}
|
||
<Tag color={item.course_type === 'hardware' ? 'purple' : 'cyan'}>
|
||
{item.course_type_display || (item.course_type === 'hardware' ? '硬件课程' : '软件课程')}
|
||
</Tag>
|
||
</div>
|
||
</div>
|
||
<div style={{ padding: 20, flex: 1, display: 'flex', flexDirection: 'column' }}>
|
||
<h3 style={{ color: '#fff', fontSize: 20, marginBottom: 10 }}>{item.title}</h3>
|
||
<div style={{ color: '#888', marginBottom: 15, fontSize: 14 }}>
|
||
<span style={{ marginRight: 15 }}><UserOutlined /> {item.instructor}</span>
|
||
<span style={{ marginRight: 15 }}><ClockCircleOutlined /> {item.duration}</span>
|
||
<span><BookOutlined /> {item.lesson_count} 课时</span>
|
||
</div>
|
||
<p style={{ color: '#aaa', marginBottom: 20, flex: 1 }}>{item.description}</p>
|
||
<Button type="primary" block ghost style={{ borderColor: '#00f0ff', color: '#00f0ff' }}>
|
||
开始学习
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</motion.div>
|
||
</Col>
|
||
))}
|
||
</Row>
|
||
)}
|
||
|
||
{/* 装饰性背景 */}
|
||
<div style={{
|
||
position: 'fixed',
|
||
top: 0,
|
||
left: 0,
|
||
width: '100%',
|
||
height: '100%',
|
||
background: `
|
||
radial-gradient(circle at 50% 50%, rgba(0, 240, 255, 0.05) 0%, transparent 50%)
|
||
`,
|
||
zIndex: 0,
|
||
pointerEvents: 'none'
|
||
}} />
|
||
|
||
<div style={{
|
||
position: 'fixed',
|
||
bottom: 0,
|
||
width: '100%',
|
||
height: '300px',
|
||
background: `linear-gradient(to top, rgba(0,0,0,0.8), transparent)`,
|
||
zIndex: 1,
|
||
pointerEvents: 'none'
|
||
}} />
|
||
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default VBCourses;
|