liujiaxin 2 달 전
부모
커밋
651a72660d
7개의 변경된 파일445개의 추가작업 그리고 151개의 파일을 삭제
  1. 5 1
      api/courseLook.js
  2. 13 1
      api/living.js
  3. 275 128
      pages_course/living.vue
  4. 12 1
      pages_course/videovip.vue
  5. 118 10
      pages_shopping/live/confirmCreateOrder.vue
  6. 2 2
      pages_shopping/live/goods.vue
  7. 20 8
      pages_shopping/live/paymentOrder.vue

+ 5 - 1
api/courseLook.js

@@ -74,4 +74,8 @@ export function getRealLink(data) {
  //h5授权登录
  export function handleFsUserWx(data) {
  	 return request('/app/wx/h5/mp/handleFsUserWx',data,'POST','application/json;charset=UTF-8','https://h5api.his.cdwjyyh.com');
- }
+ }
+ //看课获取用户信息
+  export function getUserInfoLook(data) {
+  	 return request('/course_uniapp/app/user/getUserInfo',data,'GET','application/json;charset=UTF-8','https://h5api.his.cdwjyyh.com');
+  }

+ 13 - 1
api/living.js

@@ -31,7 +31,9 @@ const api = {
 	liveList: '/app/live/liveList', //直播列表
 	liveShareList: (companyId, pageSize, pageNum) =>
 		`/app/live/liveList/${companyId}?pageSize=${pageSize}&pageNum=${pageNum}`, //直播列表
-	subNotifyLive: '/app/live/subNotifyLive' //订阅消息
+	subNotifyLive: '/app/live/subNotifyLive' ,//订阅消息
+	coupon: '/app/live/coupon/claim' ,//领取优惠券
+	curCoupon: '/app/live/coupon/curCoupon' ,//优惠券列表
 }
 // 点赞
 export function liveDataLike(liveId, data = {}) {
@@ -152,4 +154,14 @@ export function liveShareList(companyId, pageSize, pageNum, data = {}) {
 //订阅消息
 export function subNotifyLive(data) {
 	return request(api.subNotifyLive, data, 'POST', 'application/json;charset=UTF-8');
+}
+
+//领取优惠券
+export function coupon(data) {
+	return request(api.coupon, data, 'POST', 'application/json;charset=UTF-8');
+}
+
+//优惠券列表
+export function curCoupon(data) {
+	return request(api.curCoupon, data, 'POST', 'application/json;charset=UTF-8');
 }

+ 275 - 128
pages_course/living.vue

@@ -15,13 +15,13 @@
 					</view>
 					<view v-if="Array.isArray(filteredViewers)" class=" align-center" @click="toggleViewerList"
 						style="margin-top: 120rpx;">
-						<view v-for="(item,viewerIndex) in filteredViewers"
-							:key="viewerIndex">
-						<image  v-if="item.avatar" class="w52 h52 mr4"  style="border-radius: 26rpx;" :src="item.avatar" />
-						<view v-else class="w52 h52"
-							:style="{backgroundColor: getUserRandomColor(item.userId), borderRadius:'50%'}">
-							<text class="text-white text-xs">{{getNicknameInitial(item.nickName)}}</text>
-						</view>
+						<view v-for="(item,viewerIndex) in filteredViewers" :key="viewerIndex">
+							<image v-if="item.avatar" class="w52 h52 mr4" style="border-radius: 26rpx;"
+								:src="item.avatar" />
+							<view v-else class="w52 h52"
+								:style="{backgroundColor: getUserRandomColor(item.userId), borderRadius:'50%'}">
+								<text class="text-white text-xs">{{getNicknameInitial(item.nickName)}}</text>
+							</view>
 						</view>
 						<view class="sum">{{liveUserTotal||0}}</view>
 					</view>
@@ -59,7 +59,8 @@
 						</view>
 					</view>
 
-					<!-- <view class="item-box" @click="onLottery()">
+					<!-- v-if="isShowCoupon" -->
+					<!-- <view class="item-box" @click="isShowCoupon=true">
 						<view class="x-f">
 							<view class="tip">优惠券</view>
 							<view class="item">
@@ -83,8 +84,8 @@
 							class="item"></live-player>
 						<!-- 录播 -->
 						<video v-if="liveItem.videoUrl&&liveItem.liveType==2" :id="`myVideo_${liveId}`" class="item"
-							:src="liveItem.videoUrl" :autoplay="true" :controls="false" object-fit="contain"
-							:custom-cache="false" :enable-progress-gesture="false" vslide-gesture-in-fullscreen="true"
+							:src="liveItem.videoUrl" :controls="false" object-fit="contain" :custom-cache="false"
+							:enable-progress-gesture="false" vslide-gesture-in-fullscreen="true"
 							:show-center-play-btn="false" :http-cache="false" loop @error="videoError"
 							@timeupdate="onVideoTimeUpdate" @loadeddata="onVideoLoaded"
 							@loadedmetadata="onVideoMetaLoaded" @pause="onVideoPause" @play="onVideoPlay">
@@ -92,10 +93,10 @@
 
 						<!-- 直播回放 -->
 						<video v-if="liveItem.videoUrl&&liveItem.liveType==3" :id="`myVideo_${liveId}`" class="item"
-							:src="liveItem.videoUrl" :autoplay="true" :controls="true" object-fit="contain"
-							:custom-cache="false" :enable-progress-gesture="liveItem.isSpeedAllowed"
-							vslide-gesture-in-fullscreen="true" :show-center-play-btn="true" :http-cache="false" loop
-							@error="videoError" @timeupdate="onVideoTimeUpdate" @loadeddata="onVideoLoaded"
+							:src="liveItem.videoUrl" :controls="true" object-fit="contain" :custom-cache="false"
+							:enable-progress-gesture="liveItem.isSpeedAllowed" vslide-gesture-in-fullscreen="true"
+							:show-center-play-btn="true" :http-cache="false" loop @error="videoError"
+							@timeupdate="onVideoTimeUpdate" @loadeddata="onVideoLoaded"
 							@loadedmetadata="onVideoMetaLoaded" @pause="onVideoPause" @play="onVideoPlay"
 							@seek="onVideoSeek">
 						</video>
@@ -362,24 +363,25 @@
 			</u-popup>
 
 			<!-- 优惠券弹窗 -->
-			<!-- <view class="coupon-pop" v-if="isShowCoupon">
+			<view class="coupon-pop" v-if="isShowCoupon">
 				<view class="coupon-block">
 					<image class="bg" src="/static/images/coupon_bg.png" />
 					<image class="nav" src="/static/images/coupon_top.png" />
 					<image @click="isShowCoupon=false" class="w40 h40 close" src="/static/images/close1.png" />
 
 					<view class="item">
-						<view class="title">鸡蛋专属优惠券</view>
-						<view class="price">¥<text class="bold">999</text></view>
-						<view class="txt">无门槛</view>
+						<view class="title">{{couponInfo.couponName}}</view>
+						<view class="price">¥<text class="bold">{{couponInfo.couponPrice}}</text></view>
+						<!-- <view class="txt">无门槛</view> -->
+						<view class="txt">满{{couponInfo.useMinPrice}}元可用</view>
 						<view class="txt" style="margin-top: 26rpx;">指定商品可用</view>
-						<view class="txt">自领取起2天内有效</view>
-						<view class="button" @click="">
+						<view class="txt">自领取起{{couponInfo.couponTime}}天内有效</view>
+						<view class="button" @click="onCoupon()">
 							立即领券
 						</view>
 					</view>
 				</view>
-			</view> -->
+			</view>
 
 
 			<!-- <u-popup :show="true" @close="closeShop" round='20rpx' bgColor='#f3f5f9' zIndex='10075'>
@@ -470,6 +472,7 @@
 	import ThreeItemSwiper from '@/pages_course/components/ThreeItemSwiper.vue'
 	import CryptoJS from 'crypto-js'
 	import {
+		coupon, //领取优惠券
 		liveLottery, // 抽奖查询
 		claim, //抽奖
 		liveRed, // 点击领红包
@@ -490,6 +493,9 @@
 	import {
 		getUserInfo
 	} from '@/api/user'
+	import {
+		nextTick
+	} from "vue"
 
 	// var wsUrl = "wss://live.test.ylrztop.com/ws/live-api/app/webSocket"; //余红奇
 	// var pingpangTimes = null;
@@ -504,6 +510,12 @@
 		},
 		data() {
 			return {
+				isConnecting: false, // 是否正在连接中
+				previousToken: null,
+				pageInBackground: false,
+				hasInitialized: false,
+
+
 				couponsList: [{
 					status: 0,
 					couponPrice: 59,
@@ -511,8 +523,8 @@
 					couponTitle: '包装袋',
 					limitTime: 2
 				}],
-				liveViewersAvatar: [],
-				liveUserCalled: false, //调用过watchUserList没
+				liveViewersData: [],
+				// liveUserCalled: false, //调用过watchUserList没
 				liveViewDataTimer: null,
 				userRandomColors: {}, // 缓存用户ID -> 随机色的映射
 				randomColor: '#8978e2',
@@ -542,6 +554,7 @@
 				// wsNewUrl: 'ws://192.168.10.126:7014/app/webSocket',
 				// wsNewUrl: 'wss://api.fhhx.runtzh.com/app/webSocket',
 				wsNewUrl: 'wss://api.fhhx.runtzh.com/ws/app/webSocket',
+				// wsNewUrl: 'ws://192.168.10.166:7114/ws/app/webSocket',
 				// wsNewUrl:'ws://192.168.10.166:7014/app/webSocket',
 				qrFrom: null,
 				scene: '',
@@ -582,6 +595,7 @@
 				integral: {},
 				lotteryInfo: {},
 				goodsCard: {},
+				couponInfo: {},
 				redInfo: {},
 				storeId: null,
 				isFocus: false,
@@ -640,13 +654,15 @@
 				showadd: false,
 				videoContext: '',
 				livedata: {}, //直播间点赞、关注、在线人数数据
-				userData: {}
-
+				userData: {},
 			};
 		},
 
 
 		onLoad(options) {
+			this.userinfo = JSON.parse(uni.getStorageSync("userInfo"))
+			this.userData = uni.getStorageSync("userData")
+			this.previousToken = uni.getStorageSync('AppToken')
 			console.log("全部参数", options)
 			//获取键盘高度
 			this.keyboardListener = uni.onKeyboardHeightChange(res => {
@@ -680,8 +696,7 @@
 				console.log("这个", this.qrFrom)
 			}
 
-			this.userinfo = JSON.parse(uni.getStorageSync("userInfo"))
-			this.userData = uni.getStorageSync("userData")
+
 			// 初始化直播间列表
 			this.$nextTick(() => {
 				if (this.liveId) {
@@ -693,19 +708,49 @@
 			})
 
 		},
+		// onShow() {
+		// 	this.randomColor = this.getRandomHexColor()
+		// 	if (uni.getStorageSync('AppToken')) {
+		// 		this.getUserInfo();
+		// 	}
+
+		// 	this.setVideoCurrentTime(this.liveItem.totalTime)
+		// 	// this.setVideoCurrentTime(this.liveItem.totalSeconds);
+		// 	// this.getliveUser()
+		// 	// 页面显示时,若连接未打开则重新初始化
+		// 	if (!this.isSocketAvailable()) {
+		// 		this.initSocket()
+		// 	}
+		// },
 		onShow() {
+			const currentToken = uni.getStorageSync('AppToken')
+
+			// 首次加载或登录状态变化时重新初始化
+			if (!this.hasInitialized || (!this.previousToken && currentToken)) {
+				this.initializePageData()
+				this.hasInitialized = true
+			}
+
+			this.previousToken = currentToken
+			this.pageInBackground = false
+
+			// 恢复播放和连接
+			this.resumePageActivity()
+
+
 			this.randomColor = this.getRandomHexColor()
-			if (uni.getStorageSync('AppToken')) {
-				this.getUserInfo();
+
+
+			if (currentToken) {
+				this.getUserInfo()
 			}
 
 			this.setVideoCurrentTime(this.liveItem.totalTime)
-			// this.setVideoCurrentTime(this.liveItem.totalSeconds);
-			this.getliveUser()
+
 			// 页面显示时,若连接未打开则重新初始化
-			if (!this.isSocketAvailable()) {
-				this.initSocket()
-			}
+			// if (!this.isSocketAvailable()) {
+			// 	this.initSocket()
+			// }
 		},
 
 		//分享给好友
@@ -725,8 +770,7 @@
 		computed: {
 			filteredViewers() {
 				// 1. 强制兜底:若 liveViewers 不是数组(如 null/undefined),直接返回空数组
-				const safeLiveViewers = Array.isArray(this.liveViewersAvatar) ? this.liveViewersAvatar : [];
-				console.log("有头像吗",safeLiveViewers)
+				const safeLiveViewers = Array.isArray(this.liveViewersData) ? this.liveViewersData : [];
 				// 2. 截取前3项(空数组 slice 不会报错)
 				return safeLiveViewers.slice(0, 3);
 			},
@@ -741,13 +785,12 @@
 					return String(item.userId) === String(this.userData.userId);
 				});
 			}
-			// payLimitTime() {
-			// 	return this.order?.updateTime ?
-			// 		dayjs(this.order.updateTime).add(2, 'day').format('YYYY-MM-DD HH:mm:ss') :
-			// 		'';
-			// }
 		},
 		onHide() {
+			this.pageInBackground = true
+			this.suspendPageActivity()
+
+
 			console.log('页面隐藏,暂停视频和定时器');
 			this.stopHeartBeat();
 
@@ -762,29 +805,14 @@
 		},
 
 		onUnload() {
-			  this.loadUserColorsFromStorage();
+			this.loadUserColorsFromStorage();
 			if (this.liveViewDataTimer) {
 				clearInterval(this.liveViewDataTimer);
 				this.liveViewDataTimer = null;
 			}
-			console.log('页面卸载,清理资源');
+			// console.log('页面卸载,清理资源');
 			this.isPageUnloading = true; // 标记为主动离开
 
-			// (可选)手动向服务端发送离开事件,确保服务端正确推送out
-			if (this.isSocketAvailable()) {
-				const outMsg = JSON.stringify({
-					cmd: "out",
-					userId: this.userData.userId || '',
-					liveId: this.liveId,
-					msg: "离开"
-				});
-				this.socket.send({
-					data: outMsg,
-					success: () => console.log("手动发送离开事件成功"),
-					fail: (err) => console.error("手动发送离开事件失败:", err)
-				});
-			}
-
 			// 1. 先关闭WebSocket
 			this.closeWebSocket(true);
 
@@ -859,23 +887,103 @@
 			}
 		},
 		methods: {
+			onCoupon() {
+				if (!this.couponInfo.couponIssueId) return;
+
+				let data = {
+					goodsId: this.couponInfo.goodsId,
+					couponIssueId: this.couponInfo.couponIssueId,
+					liveId: this.liveId
+				};
+				coupon(data)
+					.then(res => {
+						if (res.code == 200) {
+							uni.showToast({
+								title: res.msg,
+								icon: 'none'
+							});
+							this.isShowCoupon = false
+							// const couponData = res.data || {};
+						} else {
+							uni.showToast({
+								title: res.msg,
+								icon: 'none'
+							});
+
+						}
+					})
+					.catch(rej => {});
+			},
+			async initializePageData() {
+				if (uni.getStorageSync('AppToken')) {
+					await this.getUserInfo()
+				}
+
+				if (this.liveId) {
+					await this.getliving(this.liveId)
+					this.getCurrentActivities()
+					this.getliveOrder()
+				}
+
+				this.initSocket()
+			},
+
+			// 恢复页面活动
+			resumePageActivity() {
+				if (this.liveItem) {
+					this.playVideo(this.liveItem)
+					this.startTimeTimer(this.liveItem)
+				}
+
+				if (!this.isSocketAvailable()) {
+					this.initSocket()
+				}
+			},
+
+			// 暂停页面活动
+			suspendPageActivity() {
+				this.stopHeartBeat()
+				this.saveVideoProgress()
+
+				if (this.liveItem) {
+					this.pauseVideo(this.liveItem)
+					this.clearTimeTimer(this.liveItem)
+				}
+			},
+			// reinitializePage() {
+			// 	// 重新获取用户信息
+			// 	this.getUserInfo()
+
+			// 	// 重新获取直播间信息
+			// 	if (this.liveId) {
+			// 		this.getliving(this.liveId)
+			// 	}
+
+			// 	// 重新获取活动信息
+			// 	this.getCurrentActivities()
+
+			// 	// 重新获取订单信息
+			// 	this.getliveOrder()
+
+			// 	// 重置WebSocket连接
+			// 	this.closeWebSocket(true)
+			// 	setTimeout(() => {
+			// 		this.initSocket()
+			// 	}, 1000)
+			// },
 			// 获取用户专属随机色(缓存机制:同一用户始终用同一颜色)
 			getUserRandomColor(userId) {
-				// 1. 参数校验和兜底
 				if (!userId) {
 					return '#8978e2'; // 默认颜色
 				}
-
-				// 2. 如果缓存中已有该用户的颜色,直接返回
+				//如果缓存中已有该用户的颜色,直接返回
 				if (this.userRandomColors[userId]) {
 					return this.userRandomColors[userId];
 				}
-
-				// 3. 为新用户生成固定颜色(基于用户ID生成,不是完全随机)
+				//为新用户生成固定颜色(基于用户ID生成,不是完全随机)
 				const color = this.generateStableColor(userId);
 				this.userRandomColors[userId] = color;
-
-				// 4. 存储到本地缓存,确保页面刷新后颜色不变
+				// 存储到本地缓存,确保页面刷新后颜色不变
 				this.saveUserColorsToStorage();
 
 				return color;
@@ -1150,7 +1258,6 @@
 				this.isFocus = false
 			},
 			getTimeDifferenceInSeconds(createTimeStr) {
-				console.log('设置视频播放位置qq', createTimeStr)
 				const createTime = new Date(createTimeStr.replace(/-/g, '/'));
 				const now = new Date();
 				const timeDiffMs = now - createTime;
@@ -1161,24 +1268,11 @@
 			onVideoMetaLoaded(e) {
 				// 设置存储key
 				this.videoProgressKey = `videoProgress_${this.liveId}`;
-				// 尝试从存储中获取保存的播放进度
-				// uni.getStorage({
-				// 	key: this.videoProgressKey,
-				// 	success: (res) => {
-				// 		this.videoCurrentTime = res.data;
-				// 		console.log('设置视频播放位置', this.videoCurrentTime)
-				// 		// 设置视频播放位置
-				// 		this.setVideoCurrentTime(this.videoCurrentTime);
-				// 	},
-				// 	fail: (err) => {
-				// 		console.log('没有找到保存的播放进度或获取失败', err);
-				// 		this.videoCurrentTime = 0;
-				// 	}
-				// });
 				const diff = this.getTimeDifferenceInSeconds(this.liveItem.startTime);
-				console.log('设置视频播放位置', diff)
-				this.setVideoCurrentTime(diff);
-				// this.videoCurrentTime = res.data;
+				const realDiff = diff % this.liveItem.duration
+				setTimeout(() => {
+					this.setVideoCurrentTime(realDiff);
+				}, 100); 
 			},
 
 
@@ -1211,12 +1305,13 @@
 			},
 			// 设置视频当前时间
 			setVideoCurrentTime(time) {
+				if (!time) return;
 				const videoId = `myVideo_${this.liveId}`;
 				const videoContext = uni.createVideoContext(videoId, this);
 
 				if (videoContext) {
 					videoContext.seek(time);
-					// console.log(`设置视频播放位置: ${time}秒`);
+					console.log(`设置视频播放位置: ${time}秒`);
 				}
 			},
 
@@ -1495,7 +1590,6 @@
 			// 播放视频
 			playVideo(liveItem) {
 				if (!liveItem) return;
-				console.log("直播的状态", liveItem)
 				try {
 					// 直播流使用live-player
 					if (liveItem.liveType === 1 && liveItem.livingUrl && liveItem.status == 2) {
@@ -1505,7 +1599,6 @@
 							livePlayerContext.play();
 						}
 					} else if (liveItem.status == 1 && liveItem.previewUrl) {
-						console.log("直播预告")
 						const videoId = `myVideo_${this.liveId}`;
 						const videoContext = uni.createVideoContext(videoId, this);
 						if (videoContext) {
@@ -1589,9 +1682,13 @@
 				try {
 					const res = await watchUserList(this.liveId, this.viewPageSize, this.viewPageNum, false);
 					if (res.code === 200) {
-						const userRows = Array.isArray(res.row) ? res.row : [];
-						this.liveViewersAvatar = userRows.map(item => item.avatar || '');
-
+						const userRows = Array.isArray(res.rows) ? res.rows : [];
+						// this.liveViewersData = userRows.map(item => item.avatar || '');
+						this.liveViewersData = userRows.map(item => ({
+							avatar: item.avatar || '',
+							userId: item.userId || '',
+							nickName: item.nickName || '未命名'
+						}));
 
 						this.liveUserTotal = res.total || 0;
 						// 兜底 newRows:接口返回的 rows 必须是数组,否则为空数组
@@ -1771,7 +1868,11 @@
 						// 直播预告
 						this.liveStartTimer = setInterval(() => {
 							this.liveCountdown = this.handleTime(res.data.startTime, 0)
+
 						}, 1000);
+						if (res.data.liveType !== 1 && this.liveCountdown) {
+							this.getliving(this.liveId); // 需用 await 确保数据更新完成
+						}
 						this.$set(this.liveItem, 'previewUrl', res.data.previewUrl);
 						this.$set(this.liveItem, 'livingUrl', ''); // 清空直播流
 						this.$set(this.liveItem, 'videoUrl', ''); // 清空回放视频
@@ -1831,7 +1932,6 @@
 						if (livePlayerContext) {
 							livePlayerContext.stop(); // 先停止旧流
 							livePlayerContext.play(); // 播放新流
-							console.log("live-player 重新拉流:", livingUrl);
 						}
 					}
 
@@ -1843,7 +1943,6 @@
 							videoContext.stop(); // 停止旧视频
 							videoContext.seek(0); // 重置到开头
 							videoContext.play(); // 播放新视频
-							console.log("video 重新加载:", videoUrl);
 						}
 					}
 				});
@@ -1865,8 +1964,6 @@
 
 			// 返回上一个页面并关闭WebSocket
 			goBack() {
-				console.log('返回上一页,清理资源');
-
 				// 暂停当前视频
 				const currentLive = this.liveItem;
 				if (currentLive) {
@@ -1993,7 +2090,7 @@
 					return;
 				}
 
-				console.log("websocket 连接关闭: 1000 normal closure");
+				console.log("websocket 连接关闭");
 				this.isManualClose = isManual;
 				this.stopHeartBeat();
 				this.resetReconnectState();
@@ -2025,6 +2122,14 @@
 				}, this.heartBeatInterval);
 			},
 			initSocket() {
+				if (this.isConnecting) {
+					return;
+				}
+
+				// 如果已经存在连接且状态为open,则退出
+				if (this.socket && this.socket.readyState === 1) {
+					return;
+				}
 				if (this.socket && (this.socket.readyState === 0 || this.socket.readyState === 1)) {
 					console.log('关闭现有WebSocket连接,创建新连接');
 					this.closeWebSocket(true);
@@ -2051,12 +2156,12 @@
 
 				try {
 					const baseWsUrl = 'wss://api.fhhx.runtzh.com/ws/app/webSocket';
-					// const baseWsUrl = 'wss://api.fhhx.runtzh.com/app/webSocket';
+					// const baseWsUrl = 'ws://192.168.10.166:7114/ws/app/webSocket';
 					let wsUrl =
 						`${baseWsUrl}?userId=${this.userData.userId}&liveId=${this.liveId}&userType=${this.userType}&timestamp=${this.timestamp}&signature=${signature}`;
 					if (this.qrFrom) wsUrl += this.qrFrom;
 
-					console.log("创建新的WebSocket连接:", wsUrl);
+					// console.log("创建新的WebSocket连接:", wsUrl);
 
 					const socketTask = uni.connectSocket({
 						url: wsUrl,
@@ -2071,6 +2176,7 @@
 					socketTask.onOpen((res) => {
 						console.log("WebSocket连接已打开");
 						this.socket = socketTask;
+						this.isConnecting = false;
 						this.isSocketOpen = true;
 						this.reconnectCount = 0;
 						this.resetReconnectState();
@@ -2081,12 +2187,10 @@
 
 					// 消息接收事件
 					socketTask.onMessage((res) => {
-						console.log(res, 'res')
 						try {
 							const data = JSON.parse(res.data);
 							// 处理服务端心跳响应(根据服务端协议调整cmd字段)
 							if (data.cmd === "heartBeatAck") {
-								console.log("收到心跳响应");
 								this.stopHeartBeat(); // 清除当前超时检测
 								this.startHeartBeat(); // 重新启动心跳周期
 								return;
@@ -2101,18 +2205,18 @@
 						console.error("WebSocket连接错误:", err);
 						this.isSocketOpen = false;
 						this.stopHeartBeat();
+						this.isConnecting = false;
 						this.handleReconnect(); // 错误直接触发重连
 					});
 					// 连接关闭事件
 					socketTask.onClose((res) => {
 						console.log("WebSocket连接关闭:", res);
 						this.isSocketOpen = false;
+						this.isConnecting = false;
 						this.stopHeartBeat(); // 清除心跳定时器(复用之前的停止方法)
 						if (!this.isManualClose && this.reconnectCount < this.maxReconnectAttempts) {
-							// this.handleReconnect();
-						} else {
-							console.log("无需重连(手动关闭或正常关闭)");
-						}
+							this.handleReconnect();
+						} else {}
 					});
 				} catch (e) {
 					console.error("创建WebSocket异常:", e);
@@ -2185,7 +2289,6 @@
 							this.handleReconnect(); // 主动触发重连
 							return;
 						}
-						console.log("聊天", messageData)
 						const oldList = Array.isArray(this.talklist) ? this.talklist : [];
 						const newList = [...oldList, messageData];
 						this.talklist = newList;
@@ -2215,6 +2318,19 @@
 						this.goodsCard = JSON.parse(socketMessage.data);
 						this.isShowGoods = socketMessage.status == 1;
 
+					} else if (socketMessage.cmd == 'coupon') {
+						this.couponInfo = JSON.parse(socketMessage.data);
+						this.isShowCoupon = socketMessage.status === 1;
+						if (this.isShowCoupon) {
+							// this.couponTimer = setInterval(() => {
+							// 	const couponCountdown = this.handleTime(this.couponInfo.updateTime, this.couponInfo.duration)
+							// 	if (!couponCountdown) {
+							// 		this.isShowCoupon = false
+							// 		clearInterval(this.couponTimer)
+							// 	}
+							// }, 1000);
+						}
+
 					} else if (socketMessage.cmd == 'lottery') {
 
 						const parsedData = JSON.parse(socketMessage.data || '{}'); // 默认为空对象
@@ -2246,17 +2362,22 @@
 						try {
 							if (!this.liveUserCalled) {
 								this.getliveUser(false);
+								this.liveUserTotal++;
 								this.liveUserCalled = true;
 							}
 							if (socketMessage.avatar) {
-								this.liveViewersAvatar.push(socketMessage.avatar)
+								const liveViewers = {
+									userId: socketMessage.userId,
+									nickName: socketMessage.nickName,
+									avatar: socketMessage.avatar
+								}
+								this.liveViewersData.push(liveViewers)
 							}
-							console.log("人数数组为", this.liveViewersAvatar)
 							// 解析用户ID(根据实际接口字段调整,此处假设data含userId)
 							const userData = JSON.parse(socketMessage.data || '{}');
 							const userId = userData.userId || socketMessage.userId; // 兼容不同字段
 							if (!userId) return; // 无用户ID不处理
-							this.liveUserTotal++;
+
 							// 仅新用户(未显示过)才触发提示
 							if (!this.shownEntryUsers.has(userId)) {
 								this.inAndOut = socketMessage;
@@ -2274,31 +2395,55 @@
 						} catch (err) {
 							console.error("解析entry用户数据失败:", err);
 						}
-					} else if (socketMessage.cmd ==
-						'out') {
-						if (this.liveUserTotal > 0) {
-							this.liveUserTotal--;
-						}
-
-
-						if (this.isPageUnloading) {
-							this.inAndOut = socketMessage;
-							this.showWelcomeMessage = true;
+					}
 
-							// 3秒后隐藏提示
-							if (this.welcomeTimer) clearTimeout(this.welcomeTimer);
-							this.welcomeTimer = setTimeout(() => {
-								this.showWelcomeMessage = false;
-							}, 3000);
-							const index = this.liveViewersAvatar.indexOf(socketMessage.avatar);
+					//  else if (socketMessage.cmd ==
+					// 	'out') {
+					// 	
+					// 	if (this.liveUserTotal > 0) {
+					// 		this.liveUserTotal--;
+					// 	}
+
+
+					// 	if (this.isPageUnloading) {
+					// 		this.inAndOut = socketMessage;
+					// 		this.showWelcomeMessage = true;
+
+					// 		// 3秒后隐藏提示
+					// 		if (this.welcomeTimer) clearTimeout(this.welcomeTimer);
+					// 		this.welcomeTimer = setTimeout(() => {
+					// 			this.showWelcomeMessage = false;
+					// 		}, 3000);
+					// 		const index = this.liveViewersData.indexOf(socketMessage.avatar);
+					// 		if (index !== -1) {
+					// 			this.liveViewersData.splice(index, 1); // 从索引位置删除1个元素
+					// 		}
+					// 	} else {
+					// 		// 非主动离开(如重连导致的假out),直接忽略
+					// 		console.log("忽略重连导致的out事件:", socketMessage);
+					// 	}
+
+					// } 
+					else if (socketMessage.cmd == 'out') {
+						console.log("用户离开");
+						if (this.liveUserTotal > 0) {
+							this.liveUserTotal--; // 根据userId删除对应的用户数据
+							const userIdToRemove = socketMessage.userId;
+							const index = this.liveViewersData.findIndex(item => item.userId === userIdToRemove);
 							if (index !== -1) {
-								this.liveViewersAvatar.splice(index, 1); // 从索引位置删除1个元素
+								this.liveViewersData.splice(index, 1);
 							}
-						} else {
-							// 非主动离开(如重连导致的假out),直接忽略
-							console.log("忽略重连导致的out事件:", socketMessage);
 						}
 
+
+						this.inAndOut = socketMessage;
+						this.showWelcomeMessage = true;
+
+						// 3秒后隐藏提示
+						if (this.welcomeTimer) clearTimeout(this.welcomeTimer);
+						this.welcomeTimer = setTimeout(() => {
+							this.showWelcomeMessage = false;
+						}, 3000);
 					} else if (socketMessage.cmd == 'live_start' || socketMessage.cmd ==
 						'live_end') {
 						// this.getliving(this.liveId);
@@ -2419,7 +2564,7 @@
 							// 2. 发送失败重试:使用定义后的 retries
 							if (retries > 0) {
 								uni.showToast({
-									title: `发送失败,正在重试(${retries}次)...`,
+									title: `发送失败,正在重试(${retries}次)`,
 									icon: 'none'
 								});
 								setTimeout(() => this.sendMsg(retries - 1), 500);
@@ -2481,6 +2626,7 @@
 			box-sizing: border-box;
 
 			.item {
+				display: flex;
 				width: 100%;
 				height: 1.7 * 100rpx;
 				margin-bottom: 0.16 * 100rpx;
@@ -2695,6 +2841,7 @@
 				height: 100rpx;
 				margin-right: 20rpx;
 				position: relative;
+				z-index: 999;
 
 				.tip {
 					position: absolute;
@@ -2892,7 +3039,7 @@
 		flex-direction: column;
 		border-radius: 20rpx;
 		align-items: center;
-		background: linear-gradient(180deg, #f76446 0%, #ffd7d5 27%, #ffffff 100%);
+		background: linear-gradient(180deg, #f7823f 0%, #ffd4be 27%, #ffffff 100%);
 		position: relative;
 
 		.nav-img {

+ 12 - 1
pages_course/videovip.vue

@@ -287,7 +287,8 @@
 		sendReward,
 		getRealLink,
 		loginByMiniApp,
-		handleFsUserWx
+		handleFsUserWx,
+		getUserInfoLook
 	} from "@/api/courseLook.js"
 	import {
 		getConfigByKey
@@ -534,6 +535,7 @@
 			console.log(AppToken)
 			if(AppToken){
 				// this.isLogin=true
+				this.getUserInfoLooks()
 				if(this.isAddKf == 1&&this.userinfo.userId){
 					console.log(1233)
 					this.getH5CourseVideoDetails()
@@ -587,6 +589,15 @@
 			this.clearIntegral()
 		},
 		methods: {
+			getUserInfoLooks(){
+				getUserInfoLook().then(res=>{
+					if(res.code==200){
+						this.userinfos.nickname=res.user.nickname
+						this.userInfo=res.user;
+					}
+					console.log(res)
+				})
+			},
 			getWebviewUrl() {
 				var data = {
 					key: 'course.config'

+ 118 - 10
pages_shopping/live/confirmCreateOrder.vue

@@ -111,7 +111,8 @@
 							</div>
 							<div class="data acea-row row-between-wrapper">
 								<div>{{ item.limitTime }}到期</div>
-								<div class="bnt bg-color-red" @click="couponSelect(item)">选择</div>
+								<div v-if=" item.status==0" class="bnt bg-color-red" @click="couponSelect(item)">选择</div>
+								<div v-else class="bnt " style="background-color: #878787;" >已失效</div>
 							</div>
 						</div>
 					</div>
@@ -121,7 +122,6 @@
 					<view class="empty-title">暂无数据</view>
 				</view>
 			</view>
-
 		</popupBottom>
 	</view>
 </template>
@@ -134,6 +134,9 @@
 		computed
 		// checked //获取购物车选中商品
 	} from "@/api/order.js"
+	import {
+		curCoupon
+	} from "@/api/living.js"
 	import popupBottom from '@/components/px-popup-bottom/px-popup-bottom.vue'
 	export default {
 		components: {
@@ -150,6 +153,7 @@
 				totalNum: null,
 				orderKey: null,
 				// price: null,
+				goodsId:null,
 				liveId: null,
 				orderList: [],
 				userInfo: null,
@@ -172,6 +176,7 @@
 			console.log("确认订单", options)
 			this.orderKey = options.orderKey;
 			this.liveId = options.liveId
+			this.goodsId = options.goodsId
 			this.productId = options.productId
 			this.totalNum = Number(options.totalNum) || 0
 		},
@@ -193,6 +198,21 @@
 			uni.$off('updateAddress');
 		},
 		methods: {
+			
+			// getCurCoupon() {
+			// 	curCoupon().then(res => {
+			// 			if (res.code == 200) {
+							
+			// 			} else {
+			// 				uni.showToast({
+			// 					title: res.msg,
+			// 					icon: 'none'
+			// 				});
+			// 			}
+			// 		},
+			// 		rej => {}
+			// 	);
+			// },
 			handleAddressUpdate(item) {
 				console.log('接收到地址数据:', item);
 				// 在这里处理地址数据
@@ -230,7 +250,9 @@
 					totalNum: this.totalNum,
 					productId: this.productId,
 					orderKey: this.orderKey,
+					couponUserId:this.couponUserId||''
 				}
+				
 				computed(data).then(res => {
 						if (res.code == 200) {
 							console.log("查询创建订单信息>>>>", res.data)
@@ -262,6 +284,7 @@
 					cartId: "5",
 					productId: this.productId,
 					totalNum: this.totalNum,
+					couponUserId:this.couponUserId
 
 				}
 
@@ -294,17 +317,18 @@
 				this.couponText = "-¥" + (item.couponPrice || 0).toFixed(2);
 				this.couponUserId = item.id;
 				this.couponVisible = false;
-				this.computed();
+				// this.computed();
+				this.computedOrder()
 			},
 			openCoupon() {
-				let that = this;
-				var data = {
-					couponType: 2,
-					useMinPrice: this.orderData.payPrice
+				console.log("点了优惠券列表")
+								const data = {
+					liveId: this.liveId,
+					goodsId: this.goodsId
 				};
-				getMyEnableCouponList(data).then(res => {
+				curCoupon(data).then(res => {
 					this.couponVisible = true;
-					that.couponsList = res.data
+					this.couponsList = res.data
 				})
 			},
 			integralChange(e) {
@@ -338,7 +362,7 @@
 					console.log("orderList>>", orderList)
 					const orderListStr = encodeURIComponent(JSON.stringify(orderList));
 					uni.navigateTo({
-						url: `/pages_shopping/live/paymentOrder?orderList=${orderListStr}`
+						url: `/pages_shopping/live/paymentOrder?orderList=${orderListStr}&couponUserId=${this.couponUserId}`
 					});
 				} catch (error) {
 					uni.showToast({
@@ -742,4 +766,88 @@
 			}
 		}
 	}
+	
+	.coupon {
+	  height: 100%;
+	}
+	/*优惠券列表公共*/
+	.coupon-list {
+	}
+	.coupon-list .item {
+	  display: flex;
+	  flex-direction: column;
+	  justify-content: center;
+	  align-items: center;
+	  width: 100%;
+	  height: 1.7 * 100rpx;
+	  margin-bottom: 0.16 * 100rpx;
+	}
+	
+	.coupon-list .item .money {
+	  background-size: 100% 100%;
+	  width: 2.4 * 100rpx;
+	  height: 100%;
+	  color: #fff;
+	  font-size: 0.36 * 100rpx;
+	  font-weight: bold;
+	  text-align: center;
+	  display: flex;
+	  flex-direction: column;
+	  align-items: center;
+	  justify-content: center;
+	  position: relative;
+	  
+	}
+	.coupon-list .item .money .img{
+	  position: absolute;
+	  width: 2.4 * 100rpx;
+	  height: 100%;
+	  color: #fff;
+	  
+	}
+	
+	.coupon-list .item .money .num {
+	  font-size: 0.6 * 100rpx;
+	}
+	.coupon-list .item .money .pic-num {
+	  font-size: 20rpx;
+	  z-index: 99;
+	}
+	
+	
+	.coupon-list .item .text {
+	  width: 4.5 * 100rpx;
+	  padding: 0 0.17 * 100rpx 0 0.24 * 100rpx;
+	  background-color: #fff;
+	  box-sizing: border-box;
+	}
+	
+	.coupon-list .item .text .condition {
+	  font-size: 0.3 * 100rpx;
+	  color: #282828;
+	  height: 0.93 * 100rpx;
+	  line-height: 0.93 * 100rpx;
+	  border-bottom: 1px solid #f0f0f0;
+	}
+	
+	.coupon-list .item .text .data {
+	  font-size: 0.2 * 100rpx;
+	  color: #999;
+	  height: 0.76 * 100rpx;
+	}
+	
+	.coupon-list .item .text .data .bnt {
+	  width: 1.36 * 100rpx;
+	  height: 0.44 * 100rpx;
+	  border-radius: 0.22 * 100rpx;
+	  font-size: 0.22 * 100rpx;
+	  color: #fff;
+	  text-align: center;
+	  line-height: 0.44 * 100rpx;
+	  background-color: red;
+	}
+	
+	.coupon-list .item .text .data .bnt.gray {
+	  background-color: #ccc;
+	}
 </style>

+ 2 - 2
pages_shopping/live/goods.vue

@@ -321,8 +321,8 @@
 							console.log("key>>>>", this.orderKey)
 							uni.navigateTo({
 								url: '/pages_shopping/live/confirmCreateOrder?&orderKey=' + this.orderKey +
-									'&liveId=' +
-									this.liveId + '&productId=' + this.productId + '&totalNum=' + this
+									'&liveId=' +this.liveId+'&goodsId='+this.goodsId
+									 + '&productId=' + this.productId + '&totalNum=' + this
 									.totalNum 
 							})
 						} else {

+ 20 - 8
pages_shopping/live/paymentOrder.vue

@@ -6,7 +6,7 @@
 				<text class="time">待支付</text>
 				<view class="price-box">
 					<text class="unit">¥</text>
-					<text class="num">{{order ? (Number(order.totalPrice) || 0).toFixed(2) : "0.00"}}</text>
+					<text class="num">{{order ? (Number(order.payMoney) || 0).toFixed(2) : "0.00"}}</text>
 				</view>
 
 			</view>
@@ -56,6 +56,10 @@
 					<text class="text"
 						v-if="order!=null">{{order ? (Number(order.totalPrice) || 0).toFixed(2) : "0.00"}}</text>
 				</view>
+				<view class="item">
+					<text class="label">优惠券</text>
+					<text class="text">-¥{{ order.discountMoney.toFixed(2)}} </text>
+				</view>
 			</view>
 
 		</view>
@@ -77,7 +81,8 @@
 	export default {
 		data() {
 			return {
-				newOrder:{},
+				payPrice: null,
+				newOrder: {},
 				payType: 2,
 				order: null,
 				orderId: null,
@@ -86,6 +91,7 @@
 				config: null,
 				payType: 1,
 				user: null,
+				couponUserId: null
 			}
 		},
 		computed: {
@@ -99,6 +105,12 @@
 			}
 		},
 		onLoad(options) {
+			// if (options.payPrice) {
+			// 	this.payPrice = options.payPrice;
+			// }
+			if (options.couponUserId) {
+				this.couponUserId = options.couponUserId;
+			}
 			console.log("支付订单是>>", options)
 			// this.orderKey = options.orderKey;
 			// this.liveId = options.liveId
@@ -133,14 +145,14 @@
 					payType: 1
 					// payType: this.order.payType
 				};
-				
+
 				var that = this;
 				uni.showLoading();
 				weChatPayment(data).then(
 					res => {
 						if (res.code == 200) {
 							console.log(res);
-								
+
 							if (res.payType == 1 || res.payType == 2) {
 								var result = JSON.parse(res.result);
 								uni.requestPayment({
@@ -152,12 +164,12 @@
 									paySign: result.paySign,
 									success: function(res) {
 										uni.hideLoading();
-										
-											uni.redirectTo({
+
+										uni.redirectTo({
 											url: "./success?order=" + JSON.stringify(that.newOrder)
 										})
-										
-										
+
+
 									},
 									fail: function(err) {
 										uni.showToast({