This commit is contained in:
jeremygan2021
2026-02-11 03:00:38 +08:00
parent c3b4373c94
commit 96d5598fb5
57 changed files with 2239 additions and 577 deletions

View File

@@ -6,7 +6,7 @@ import ProductDetail from './pages/ProductDetail';
import Payment from './pages/Payment';
import AIServices from './pages/AIServices';
import ServiceDetail from './pages/ServiceDetail';
import ARExperience from './pages/ARExperience';
import VBCourses from './pages/VBCourses';
import MyOrders from './pages/MyOrders';
import 'antd/dist/reset.css';
import './App.css';
@@ -19,7 +19,7 @@ function App() {
<Route path="/" element={<Home />} />
<Route path="/services" element={<AIServices />} />
<Route path="/services/:id" element={<ServiceDetail />} />
<Route path="/ar" element={<ARExperience />} />
<Route path="/courses" element={<VBCourses />} />
<Route path="/my-orders" element={<MyOrders />} />
<Route path="/product/:id" element={<ProductDetail />} />
<Route path="/payment/:orderId" element={<Payment />} />

View File

@@ -19,7 +19,7 @@ export const confirmPayment = (orderId) => api.post(`/orders/${orderId}/confirm_
export const getServices = () => api.get('/services/');
export const getServiceDetail = (id) => api.get(`/services/${id}/`);
export const createServiceOrder = (data) => api.post('/service-orders/', data);
export const getARServices = () => api.get('/ar/');
export const getVBCourses = () => api.get('/courses/');
export const sendSms = (data) => api.post('/auth/send-sms/', data);
export const queryMyOrders = (data) => api.post('/orders/my_orders/', data);

View File

@@ -34,9 +34,9 @@ const Layout = ({ children }) => {
label: 'AI 服务',
},
{
key: '/ar',
key: '/courses',
icon: <EyeOutlined />,
label: 'AR 体验',
label: 'VB 课程',
},
{
key: '/my-orders',

View File

@@ -1,28 +1,27 @@
import React, { useState, useEffect } from 'react';
import { motion } from 'framer-motion';
import { Button, Typography, Spin, Row, Col, Empty } from 'antd';
import { ScanOutlined } from '@ant-design/icons';
import { getARServices } from '../api';
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 ARExperience = () => {
const [scanning, setScanning] = useState(true);
const [arServices, setArServices] = useState([]);
const VBCourses = () => {
const [courses, setCourses] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchAR = async () => {
const fetchCourses = async () => {
try {
const res = await getARServices();
setArServices(res.data);
const res = await getVBCourses();
setCourses(res.data);
} catch (error) {
console.error("Failed to fetch AR services:", error);
console.error("Failed to fetch VB Courses:", error);
} finally {
setLoading(false);
}
}
fetchAR();
fetchCourses();
}, []);
if (loading) return <div style={{ textAlign: 'center', padding: 100 }}><Spin size="large" /></div>;
@@ -31,20 +30,20 @@ const ARExperience = () => {
<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 }}>
AR <span style={{ color: '#00f0ff' }}>UNIVERSE</span>
VB <span style={{ color: '#00f0ff' }}>CODING COURSES</span>
</Title>
<Paragraph style={{ color: '#aaa', fontSize: 18, maxWidth: 600, margin: '0 auto' }}>
探索全息增强现实体验请佩戴您的设备或使用移动端摄像头扫描空间
探索 Vibe Coding 软件与硬件课程开启您的编程之旅
</Paragraph>
</div>
{arServices.length === 0 ? (
{courses.length === 0 ? (
<div style={{ textAlign: 'center', marginTop: 100, zIndex: 2, position: 'relative' }}>
<Empty description={<span style={{ color: '#666' }}>暂无 AR 体验内容</span>} />
<Empty description={<span style={{ color: '#666' }}>暂无课程内容</span>} />
</div>
) : (
<Row gutter={[32, 32]} justify="center" style={{ padding: '0 20px', position: 'relative', zIndex: 2 }}>
{arServices.map((item, index) => (
{courses.map((item, index) => (
<Col xs={24} md={12} lg={8} key={item.id}>
<motion.div
initial={{ opacity: 0, y: 30 }}
@@ -57,20 +56,35 @@ const ARExperience = () => {
border: '1px solid rgba(0,240,255,0.2)',
borderRadius: 12,
overflow: 'hidden',
height: '100%'
height: '100%',
display: 'flex',
flexDirection: 'column'
}}>
<div style={{ height: 200, background: '#000', overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<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' }} />
) : (
<ScanOutlined style={{ fontSize: 40, color: '#333' }} />
<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 }}>
<h3 style={{ color: '#fff', fontSize: 20 }}>{item.title}</h3>
<p style={{ color: '#888', marginBottom: 20, minHeight: 44 }}>{item.description}</p>
<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>
@@ -108,4 +122,4 @@ const ARExperience = () => {
);
};
export default ARExperience;
export default VBCourses;