import React, { useEffect, useState } from 'react'; import { useParams, useNavigate, useSearchParams } from 'react-router-dom'; import { Typography, Button, Spin, Empty, Descriptions, Tag, Row, Col, Modal, Form, Input, message } from 'antd'; import { ArrowLeftOutlined, ClockCircleOutlined, UserOutlined, BookOutlined, FormOutlined, CalendarOutlined, PlayCircleOutlined, LockOutlined } from '@ant-design/icons'; import { getVCCourseDetail, createOrder, nativePay, queryOrderStatus } from '../api'; import { useAuth } from '../context/AuthContext'; import { QRCodeSVG } from 'qrcode.react'; import ReactMarkdown from 'react-markdown'; import remarkMath from 'remark-math'; import rehypeKatex from 'rehype-katex'; import remarkGfm from 'remark-gfm'; import rehypeRaw from 'rehype-raw'; import 'katex/dist/katex.min.css'; import styles from './VCCourseDetail.module.less'; import CodeBlock from '../components/CodeBlock'; const { Title, Paragraph } = Typography; const VCCourseDetail = () => { const { id } = useParams(); const navigate = useNavigate(); const [searchParams] = useSearchParams(); const { user } = useAuth(); const [course, setCourse] = useState(null); const [loading, setLoading] = useState(true); const [isModalOpen, setIsModalOpen] = useState(false); const [submitting, setSubmitting] = useState(false); const [form] = Form.useForm(); // Payment states const [payMode, setPayMode] = useState(false); const [qrCodeUrl, setQrCodeUrl] = useState(null); const [currentOrderId, setCurrentOrderId] = useState(null); const [paySuccess, setPaySuccess] = useState(false); // 优先从 URL 获取,如果没有则从 localStorage 获取 const refCode = searchParams.get('ref') || localStorage.getItem('ref_code'); const markdownComponents = { // eslint-disable-next-line no-unused-vars code({node, inline, className, children, ...props}) { const match = /language-(\w+)/.exec(className || '') return !inline && match ? ( {String(children).replace(/\n$/, '')} ) : ( {children} ) }, // eslint-disable-next-line no-unused-vars img({node, ...props}) { return ( ); } }; useEffect(() => { const fetchDetail = async () => { try { const response = await getVCCourseDetail(id); setCourse(response.data); } catch (error) { console.error("Failed to fetch course detail:", error); } finally { setLoading(false); } }; fetchDetail(); }, [id]); useEffect(() => { if (isModalOpen) { // Reset payment state when modal opens setPayMode(false); setQrCodeUrl(null); setCurrentOrderId(null); setPaySuccess(false); if (user && user.phone_number) { form.setFieldsValue({ phone_number: user.phone_number }); } } }, [isModalOpen, user, form]); // Polling for payment status useEffect(() => { let timer; if (payMode && !paySuccess && currentOrderId) { timer = setInterval(async () => { try { const response = await queryOrderStatus(currentOrderId); if (response.data.status === 'paid') { setPaySuccess(true); message.success('支付成功!报名已完成。'); setTimeout(() => { setIsModalOpen(false); // 刷新课程详情以获取解锁后的视频URL const fetchDetail = async () => { try { const res = await getVCCourseDetail(id); setCourse(res.data); } catch (error) { console.error("Failed to refresh course detail:", error); } }; fetchDetail(); }, 2000); // Wait 2 seconds before closing clearInterval(timer); } } catch (error) { console.error('Check payment status failed:', error); } }, 3000); } return () => clearInterval(timer); }, [payMode, paySuccess, currentOrderId, id]); const handleEnroll = async (values) => { setSubmitting(true); try { const isFree = course.price === 0 || parseFloat(course.price) === 0; if (isFree) { const orderData = { course: course.id, customer_name: values.customer_name, phone_number: values.phone_number, ref_code: refCode || "", quantity: 1, // 将其他信息放入收货地址字段中 shipping_address: `[课程报名] 微信号: ${values.wechat_id || '无'}, 邮箱: ${values.email || '无'}, 备注: ${values.message || '无'}` }; await createOrder(orderData); message.success('报名成功!您已成功加入课程。'); setIsModalOpen(false); } else { // Paid course - use nativePay to generate QR code const orderData = { goodid: course.id, type: 'course', quantity: 1, customer_name: values.customer_name, phone_number: values.phone_number, ref_code: refCode || "", shipping_address: `[课程报名] 微信号: ${values.wechat_id || '无'}, 邮箱: ${values.email || '无'}, 备注: ${values.message || '无'}` }; const response = await nativePay(orderData); if (response.data && response.data.code_url) { setQrCodeUrl(response.data.code_url); setCurrentOrderId(response.data.order_id); setPayMode(true); message.success('订单创建成功,请扫码支付'); } else { throw new Error('Failed to generate payment QR code'); } } } catch (error) { console.error(error); message.error('提交失败,请重试'); } finally { setSubmitting(false); } }; if (loading) { return (
Loading...
); } if (!course) { return (
); } return (
{course.tag && {course.tag}} {course.course_type_display || (course.course_type === 'hardware' ? '硬件课程' : '软件课程')}
{course.title} {course.description} {/* 视频课程播放区域 */} {course.is_video_course && (
{course.video_url ? ( ) : (
课程视频内容已锁定

请购买或报名该课程以解锁完整视频内容

)}
)}
<div style={{ width: 4, height: 18, background: '#00f0ff', marginRight: 10, borderRadius: 2 }} /> 课程信息 讲师}>
{course.instructor_avatar_url && ( avatar )} {course.instructor} {course.instructor_title && ( {course.instructor_title} )}
时长}> {course.duration} 课时}> {course.lesson_count} 课时 {course.is_fixed_schedule && (course.start_time || course.end_time) && ( 开课时间}>
{course.start_time && (
开始时间 {new Date(course.start_time).toLocaleString('zh-CN', {year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit'})}
)} {course.start_time && course.end_time && (
)} {course.end_time && (
结束时间 {new Date(course.end_time).toLocaleString('zh-CN', {year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit'})}
)}
)} {/* 讲师简介 */} {course.instructor_desc && (
讲师简介: {course.instructor_desc}
)}
{/* 课程详细内容区域 */} {course.content && (
课程大纲与详情
{course.content}
)}
{course.display_detail_image ? (
{course.title}
) : null}
{course.is_video_course ? '购买课程' : '报名咨询'}
{parseFloat(course.price) > 0 ? ( <> ¥{course.price} ) : ( 免费咨询 )}

{course.is_purchased ? '* 您已拥有该课程,可直接观看视频' : (course.is_video_course ? '* 支付成功后自动解锁视频内容' : '* 提交后我们的顾问将尽快与您联系确认')}

{/* Enroll Modal */} setIsModalOpen(false)} footer={null} destroyOnHidden width={payMode ? 400 : 520} > {payMode ? (
{paySuccess ? (
🎉

支付成功!

正在跳转...

) : ( <>
{qrCodeUrl ? ( ) : ( )}

¥{course.price}

请使用微信扫一扫支付

支付完成后将自动{course.is_video_course ? '解锁视频' : '完成报名'}
)}
) : ( <>

{course.is_video_course ? '请确认您的联系方式,以便我们记录您的购买信息。' : '请填写您的联系方式,我们将为您安排课程顾问。'}

)}
); }; export default VCCourseDetail;