소스 검색

积分弹窗和记录时长

xw 1 주 전
부모
커밋
b2c4636624
1개의 변경된 파일160개의 추가작업 그리고 5개의 파일을 삭제
  1. 160 5
      pages_course/living.vue

+ 160 - 5
pages_course/living.vue

@@ -676,6 +676,24 @@
 					</view>
 				</view>
 			</view>
+
+			<!-- 完课积分弹窗 -->
+			<u-popup :show="showCompletionPoints" round="20rpx" mode="center" bgColor="#ffffff" zIndex="10076">
+				<view class="completion-points-popup">
+					<view class="popup-icon">🎉</view>
+					<view class="popup-title">恭喜完成观看任务!</view>
+					<view class="popup-content">
+						<view class="content-item">观看时长达标</view>
+						<view class="content-item highlight">奖励积分:{{ completionPointsData?.pointsAwarded || 0 }} 分</view>
+						<view class="content-item">连续完课:第 {{ completionPointsData?.continuousDays || 1 }} 天</view>
+					</view>
+					<view class="popup-buttons">
+						<view class="popup-button secondary" @click="closeCompletionPoints">稍后再说</view>
+						<view class="popup-button primary" @click="receiveCompletionPoints">立即领取</view>
+					</view>
+				</view>
+			</u-popup>
+
 			<view v-if="liveItem.status == 1" class="ash_bg"></view>
 		</view>
 	</view>
@@ -802,6 +820,10 @@
 
 				stayTime: 0,
 				startTime: 0,
+				watchStartTime: 0, // 观看开始时间(毫秒)
+				totalWatchTime: 0, // 总观看时长(秒)
+				showCompletionPoints: false, // 是否显示完课积分弹窗
+				completionPointsData: null, // 完课积分数据
 
 				scrollTop: 0,
 				currentScrollTop: 0, // 当前实际滚动位置
@@ -2756,14 +2778,18 @@
 				}
 				this.lastHeartBeatTime = Date.now();
 
+				// 计算观看时长(秒)
+				const watchDuration = this.watchStartTime > 0 ? Math.floor((this.lastHeartBeatTime - this.watchStartTime) / 1000) : 0;
+
 				try {
 					const heartBeatMsg = JSON.stringify({
 						cmd: 'heartbeat',
 						msg: 'ping',
-						userId: this.userInfo.userId || '',
-						liveId: this.liveId,
 						timestamp: this.lastHeartBeatTime,
-						networkType: this.networkType
+						networkType: this.networkType,
+						liveId: this.liveId,
+						userId: this.userInfo.userId || '',
+						data: String(watchDuration) // 观看时长(秒),字符串格式
 					});
 
 					this.socket.send({
@@ -3334,6 +3360,55 @@
 				this.havePrize = false;
 				uni.setStorageSync('havePrize', this.havePrize);
 			},
+			// 领取完课积分
+			async receiveCompletionPoints() {
+				if (!this.completionPointsData || !this.completionPointsData.id) {
+					uni.showToast({
+						title: '数据异常,请重试',
+						icon: 'none'
+					});
+					return;
+				}
+
+				try {
+					uni.showLoading({
+						title: '领取中...'
+					});
+
+					const res = await this.$u.post('/api/live/completion/receive', {
+						recordId: this.completionPointsData.id,
+						userId: this.userInfo.userId
+					});
+
+					uni.hideLoading();
+
+					if (res.code === 0 || res.code === 200) {
+						uni.showToast({
+							title: '积分领取成功!',
+							icon: 'success'
+						});
+						// 关闭弹窗
+						this.showCompletionPoints = false;
+						this.completionPointsData = null;
+					} else {
+						uni.showToast({
+							title: res.msg || '领取失败',
+							icon: 'none'
+						});
+					}
+				} catch (error) {
+					uni.hideLoading();
+					console.error('领取积分失败:', error);
+					uni.showToast({
+						title: '网络异常,请重试',
+						icon: 'none'
+					});
+				}
+			},
+			// 稍后领取
+			closeCompletionPoints() {
+				this.showCompletionPoints = false;
+			},
 			// 商品收藏
 			onGoodsCollect(item) {
 				if (!item || item.length === 0 || !item.goodsId) {
@@ -4056,10 +4131,10 @@
 			},
 			startHeartBeat() {
 				this.stopHeartBeat(); // 先停止现有心跳,防止重复
-				// 使用自适应间隔循环发送心跳
+				// 使用自适应间隔循环发送心跳(改为30秒)
 				this.heartBeatTimer = setInterval(() => {
 					this.sendHeartBeat();
-				}, this.adaptiveHeartBeatInterval);
+				}, 30000); // 30秒间隔
 			},
 			initSocket() {
 				// 检查是否正在连接中
@@ -4168,6 +4243,9 @@
 						this.isConnecting = false;
 						this.isSocketOpen = true;
 
+						// 记录观看开始时间
+						this.watchStartTime = Date.now();
+
 						// 计算连接延迟(性能监控)
 						if (this.connectionStartTime > 0) {
 							this.connectionLatency = Date.now() - this.connectionStartTime;
@@ -4566,6 +4644,16 @@
 									});
 								}
 							});
+						} else if (socketMessage.cmd == 'completionPoints') {
+							// 完课积分弹窗
+							try {
+								const pointsData = socketMessage.data ? JSON.parse(socketMessage.data) : {};
+								this.completionPointsData = pointsData;
+								this.showCompletionPoints = true;
+								console.log('收到完课积分弹窗:', pointsData);
+							} catch (err) {
+								console.error('解析完课积分数据失败:', err);
+							}
 						}
 					} else {
 						uni.showToast({
@@ -6640,6 +6728,73 @@
 					}
 				}
 			}
+
+			// 完课积分弹窗
+			.completion-points-popup {
+				width: 600rpx;
+				padding: 60rpx 40rpx 40rpx;
+				text-align: center;
+				background: #ffffff;
+				border-radius: 20rpx;
+
+				.popup-icon {
+					font-size: 80rpx;
+					margin-bottom: 20rpx;
+				}
+
+				.popup-title {
+					font-size: 36rpx;
+					font-weight: 600;
+					color: #333333;
+					margin-bottom: 30rpx;
+				}
+
+				.popup-content {
+					padding: 30rpx 20rpx;
+					background: #f8f9fa;
+					border-radius: 12rpx;
+					margin-bottom: 30rpx;
+
+					.content-item {
+						font-size: 28rpx;
+						color: #666666;
+						margin: 8rpx 0;
+
+						&.highlight {
+							font-size: 32rpx;
+							font-weight: 600;
+							color: #ff5c03;
+							margin: 16rpx 0;
+						}
+					}
+				}
+
+				.popup-buttons {
+					display: flex;
+					gap: 20rpx;
+					justify-content: space-between;
+
+					.popup-button {
+						flex: 1;
+						height: 80rpx;
+						line-height: 80rpx;
+						border-radius: 40rpx;
+						font-size: 30rpx;
+						font-weight: 500;
+						text-align: center;
+
+						&.secondary {
+							background: #f5f7fa;
+							color: #666666;
+						}
+
+						&.primary {
+							background: linear-gradient(270deg, #ff5c03 0%, #ffac64 100%);
+							color: #ffffff;
+						}
+					}
+				}
+			}
 		}
 	}
 </style>