video curcse
All checks were successful
Deploy to Server / deploy (push) Successful in 36s

This commit is contained in:
jeremygan2021
2026-02-27 13:54:22 +08:00
parent b58dc38a2b
commit f9c104452b
3 changed files with 129 additions and 2 deletions

View File

@@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useParams, useNavigate, useSearchParams } from 'react-router-dom'; import { useParams, useNavigate, useSearchParams } from 'react-router-dom';
import { Typography, Button, Spin, Empty, Descriptions, Tag, Row, Col, Modal, Form, Input, message } from 'antd'; import { Typography, Button, Spin, Empty, Descriptions, Tag, Row, Col, Modal, Form, Input, message } from 'antd';
import { ArrowLeftOutlined, ClockCircleOutlined, UserOutlined, BookOutlined, FormOutlined, CalendarOutlined } from '@ant-design/icons'; import { ArrowLeftOutlined, ClockCircleOutlined, UserOutlined, BookOutlined, FormOutlined, CalendarOutlined, PlayCircleOutlined, LockOutlined } from '@ant-design/icons';
import { getVCCourseDetail, createOrder, nativePay, queryOrderStatus } from '../api'; import { getVCCourseDetail, createOrder, nativePay, queryOrderStatus } from '../api';
import { useAuth } from '../context/AuthContext'; import { useAuth } from '../context/AuthContext';
import { QRCodeSVG } from 'qrcode.react'; import { QRCodeSVG } from 'qrcode.react';
@@ -214,6 +214,62 @@ const VCCourseDetail = () => {
{course.description} {course.description}
</Paragraph> </Paragraph>
{/* 视频课程播放区域 */}
{course.is_video_course && (
<div style={{
margin: '30px 0',
background: '#000',
borderRadius: 16,
overflow: 'hidden',
border: '1px solid rgba(0, 240, 255, 0.2)',
boxShadow: '0 8px 32px rgba(0,0,0,0.3)',
position: 'relative',
aspectRatio: '16/9'
}}>
{course.video_url ? (
<video
src={course.video_url}
controls
style={{ width: '100%', height: '100%', objectFit: 'contain' }}
poster={course.cover_image_url}
>
您的浏览器不支持视频播放
</video>
) : (
<div style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
height: '100%',
color: '#fff',
background: `linear-gradient(rgba(0,0,0,0.7), rgba(0,0,0,0.7)), url(${course.cover_image_url}) no-repeat center/cover`
}}>
<LockOutlined style={{ fontSize: 48, color: '#00f0ff', marginBottom: 20 }} />
<Title level={4} style={{ color: '#fff', marginBottom: 10 }}>课程视频内容已锁定</Title>
<p style={{ color: '#ccc', fontSize: 16 }}>
请购买或报名该课程以解锁完整视频内容
</p>
<Button
type="primary"
icon={<PlayCircleOutlined />}
size="large"
style={{
marginTop: 20,
background: '#00f0ff',
borderColor: '#00f0ff',
color: '#000',
fontWeight: 'bold'
}}
onClick={() => setIsModalOpen(true)}
>
立即解锁观看
</Button>
</div>
)}
</div>
)}
<div style={{ <div style={{
marginTop: 30, marginTop: 30,
background: 'rgba(255,255,255,0.03)', background: 'rgba(255,255,255,0.03)',

View File

@@ -65,6 +65,55 @@
} }
} }
.video-section {
.course-video {
width: 100%;
height: 420px;
border-radius: 16px;
background-color: #000;
}
.video-locked {
width: 100%;
height: 420px;
background-color: #111;
border-radius: 16px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border: 1px solid rgba(255, 255, 255, 0.1);
.lock-icon {
font-size: 60px;
margin-bottom: 20px;
}
.lock-text {
color: #fff;
font-size: 32px;
margin-bottom: 30px;
font-weight: bold;
}
.btn-unlock {
background: linear-gradient(90deg, #00f0ff, #0099ff);
color: #000;
font-size: 28px;
padding: 0 40px;
height: 80px;
line-height: 80px;
border-radius: 40px;
font-weight: bold;
border: none;
&::after {
border: none;
}
}
}
}
.section { .section {
margin-bottom: 50px; margin-bottom: 50px;

View File

@@ -1,4 +1,4 @@
import { View, Text, Button, Image, ScrollView } from '@tarojs/components' import { View, Text, Button, Image, ScrollView, Video } from '@tarojs/components'
import Taro, { useLoad, useShareAppMessage, useShareTimeline } from '@tarojs/taro' import Taro, { useLoad, useShareAppMessage, useShareTimeline } from '@tarojs/taro'
import { useState } from 'react' import { useState } from 'react'
import { getVBCourseDetail } from '../../api' import { getVBCourseDetail } from '../../api'
@@ -86,6 +86,28 @@ export default function CourseDetail() {
<Text className='price'>¥{detail.price}</Text> <Text className='price'>¥{detail.price}</Text>
</View> </View>
{/* 视频播放区域 */}
{detail.is_video_course && (
<View className='section video-section'>
<Text className='section-title'></Text>
{detail.video_url ? (
<Video
src={detail.video_url}
className='course-video'
poster={detail.cover_image_url}
controls
autoplay={false}
/>
) : (
<View className='video-locked' onClick={handleLaunch}>
<View className='lock-icon'>🔒</View>
<Text className='lock-text'></Text>
<Button className='btn-unlock'></Button>
</View>
)}
</View>
)}
{/* 讲师信息 */} {/* 讲师信息 */}
<View className='section instructor-section'> <View className='section instructor-section'>
<Text className='section-title'></Text> <Text className='section-title'></Text>