This commit is contained in:
@@ -176,6 +176,7 @@ class VCCourseSerializer(serializers.ModelSerializer):
|
|||||||
display_detail_image = serializers.SerializerMethodField()
|
display_detail_image = serializers.SerializerMethodField()
|
||||||
course_type_display = serializers.CharField(source='get_course_type_display', read_only=True)
|
course_type_display = serializers.CharField(source='get_course_type_display', read_only=True)
|
||||||
video_url = serializers.SerializerMethodField()
|
video_url = serializers.SerializerMethodField()
|
||||||
|
is_purchased = serializers.SerializerMethodField()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = VCCourse
|
model = VCCourse
|
||||||
@@ -195,6 +196,30 @@ class VCCourseSerializer(serializers.ModelSerializer):
|
|||||||
return obj.detail_image.url
|
return obj.detail_image.url
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _check_purchased(self, obj):
|
||||||
|
request = self.context.get('request')
|
||||||
|
if not request:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 尝试获取当前用户
|
||||||
|
user = get_current_wechat_user(request)
|
||||||
|
if not user:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# 如果是管理员,视为已购买
|
||||||
|
if user.user and user.user.is_staff:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# 检查是否已购买/报名 (通过已支付的订单)
|
||||||
|
return Order.objects.filter(
|
||||||
|
wechat_user=user,
|
||||||
|
course=obj,
|
||||||
|
status__in=['paid', 'shipped', 'completed']
|
||||||
|
).exists()
|
||||||
|
|
||||||
|
def get_is_purchased(self, obj):
|
||||||
|
return self._check_purchased(obj)
|
||||||
|
|
||||||
def get_video_url(self, obj):
|
def get_video_url(self, obj):
|
||||||
"""
|
"""
|
||||||
仅当用户已付费/报名时返回视频URL
|
仅当用户已付费/报名时返回视频URL
|
||||||
@@ -202,27 +227,7 @@ class VCCourseSerializer(serializers.ModelSerializer):
|
|||||||
if not obj.is_video_course:
|
if not obj.is_video_course:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
request = self.context.get('request')
|
if self._check_purchased(obj):
|
||||||
if not request:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# 尝试获取当前用户
|
|
||||||
user = get_current_wechat_user(request)
|
|
||||||
if not user:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# 如果是管理员,直接返回
|
|
||||||
if user.user and user.user.is_staff:
|
|
||||||
return obj.video_url
|
|
||||||
|
|
||||||
# 检查是否已购买/报名 (通过已支付的订单)
|
|
||||||
has_paid = Order.objects.filter(
|
|
||||||
wechat_user=user,
|
|
||||||
course=obj,
|
|
||||||
status__in=['paid', 'shipped', 'completed'] # 包含所有已完成状态
|
|
||||||
).exists()
|
|
||||||
|
|
||||||
if has_paid:
|
|
||||||
return obj.video_url
|
return obj.video_url
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -425,22 +425,26 @@ const VCCourseDetail = () => {
|
|||||||
size="large"
|
size="large"
|
||||||
block
|
block
|
||||||
icon={course.is_video_course ? <PlayCircleOutlined /> : <FormOutlined />}
|
icon={course.is_video_course ? <PlayCircleOutlined /> : <FormOutlined />}
|
||||||
|
disabled={course.is_purchased}
|
||||||
style={{
|
style={{
|
||||||
height: 50,
|
height: 50,
|
||||||
background: '#00f0ff',
|
background: course.is_purchased ? '#333' : '#00f0ff',
|
||||||
borderColor: '#00f0ff',
|
borderColor: course.is_purchased ? '#444' : '#00f0ff',
|
||||||
color: '#000',
|
color: course.is_purchased ? '#888' : '#000',
|
||||||
fontWeight: 'bold',
|
fontWeight: 'bold',
|
||||||
fontSize: '16px'
|
fontSize: '16px',
|
||||||
|
cursor: course.is_purchased ? 'not-allowed' : 'pointer'
|
||||||
}}
|
}}
|
||||||
onClick={() => setIsModalOpen(true)}
|
onClick={() => !course.is_purchased && setIsModalOpen(true)}
|
||||||
>
|
>
|
||||||
{course.is_video_course ? '购买视频课程' : '立即报名 / 咨询'}
|
{course.is_purchased ? '已购买' : (course.is_video_course ? '购买视频课程' : '立即报名 / 咨询')}
|
||||||
</Button>
|
</Button>
|
||||||
<p style={{ color: '#666', marginTop: 15, fontSize: 12, textAlign: 'center' }}>
|
<p style={{ color: '#666', marginTop: 15, fontSize: 12, textAlign: 'center' }}>
|
||||||
{course.is_video_course
|
{course.is_purchased
|
||||||
? '* 支付成功后自动解锁视频内容'
|
? '* 您已拥有该课程,可直接观看视频'
|
||||||
: '* 提交后我们的顾问将尽快与您联系确认'}
|
: (course.is_video_course
|
||||||
|
? '* 支付成功后自动解锁视频内容'
|
||||||
|
: '* 提交后我们的顾问将尽快与您联系确认')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -352,6 +352,11 @@
|
|||||||
border: none;
|
border: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
background: #333;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
&::after {
|
&::after {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,8 +196,12 @@ export default function CourseDetail() {
|
|||||||
<Text className='label'>总价:</Text>
|
<Text className='label'>总价:</Text>
|
||||||
<Text className='amount'>¥{detail.price}</Text>
|
<Text className='amount'>¥{detail.price}</Text>
|
||||||
</View>
|
</View>
|
||||||
<Button className='btn-buy' onClick={handleLaunch}>
|
<Button
|
||||||
{detail.is_video_course ? '立即购买' : '立即报名'}
|
className={`btn-buy ${detail.is_purchased ? 'disabled' : ''}`}
|
||||||
|
onClick={() => !detail.is_purchased && handleLaunch()}
|
||||||
|
disabled={detail.is_purchased}
|
||||||
|
>
|
||||||
|
{detail.is_purchased ? '已购买' : (detail.is_video_course ? '立即购买' : '立即报名')}
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -377,3 +377,38 @@
|
|||||||
0% { transform: scale(1); opacity: 0.5; }
|
0% { transform: scale(1); opacity: 0.5; }
|
||||||
100% { transform: scale(1.15); opacity: 0; }
|
100% { transform: scale(1.15); opacity: 0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ai-badge {
|
||||||
|
background: rgba(0, 185, 107, 0.1);
|
||||||
|
border: 1px solid rgba(0, 185, 107, 0.3);
|
||||||
|
padding: 8px 20px;
|
||||||
|
border-radius: 30px;
|
||||||
|
margin: 15px auto;
|
||||||
|
display: inline-block;
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
box-shadow: 0 0 10px rgba(0, 185, 107, 0.1);
|
||||||
|
|
||||||
|
text {
|
||||||
|
color: #00b96b;
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: bold;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
text-shadow: 0 0 5px rgba(0, 185, 107, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.compliance-footer {
|
||||||
|
text-align: center;
|
||||||
|
padding: 30px 20px 50px;
|
||||||
|
margin-top: 40px;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.05);
|
||||||
|
background: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.5));
|
||||||
|
|
||||||
|
.compliance-text {
|
||||||
|
color: #444;
|
||||||
|
font-size: 22px;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -48,6 +48,10 @@ export default function ServicesIndex() {
|
|||||||
<View className='page-container'>
|
<View className='page-container'>
|
||||||
<View className='header'>
|
<View className='header'>
|
||||||
<Text className='title'>AI 全栈<Text className='highlight'>解决方案</Text></Text>
|
<Text className='title'>AI 全栈<Text className='highlight'>解决方案</Text></Text>
|
||||||
|
<View className='ai-badge'>
|
||||||
|
<Text>生成式AI生成内容</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
<Text className='subtitle'>从数据处理到模型部署,我们为您提供一站式 AI 基础设施服务。</Text>
|
<Text className='subtitle'>从数据处理到模型部署,我们为您提供一站式 AI 基础设施服务。</Text>
|
||||||
|
|
||||||
<View className='vc-promo-container'>
|
<View className='vc-promo-container'>
|
||||||
@@ -130,6 +134,10 @@ export default function ServicesIndex() {
|
|||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
<View className='compliance-footer'>
|
||||||
|
<Text className='compliance-text'>深度合成-AI问答类目</Text>
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user