Explorar el Código

点播跟直播代码更新

Signed-off-by: 李妹妹 <1639016684@qq.com>
李妹妹 hace 7 horas
padre
commit
c0056aad10

+ 2 - 5
api/courseLook.js

@@ -138,17 +138,14 @@ export function addCommentWithMedia(data) {
 	const videoPath = _isLocalMediaFilePath(videoArg) ? videoArg : ''
 	if (imagePath || videoPath) {
 		return new Promise((resolve, reject) => {
-			const token =
-				uni.getStorageSync('TOKEN_WEXIN') ||
-				uni.getStorageSync('AppTokenmini_AutoCourse') ||
-				''
+			const token =uni.getStorageSync('AppToken')
 			// 与 common/request.js 中 uni.request 一致;multipart 的 boundary 由客户端自动带,勿手写 Content-Type
 			const header = {
 				AppToken: token
 			}
 			try {
 				if (typeof wx !== 'undefined' && wx.getAccountInfoSync) {
-					header['appid'] = wx.getAccountInfoSync().miniProgram.appId
+					header['appid'] = 'wxecdaff01852ca309'
 				}
 			} catch (e) {}
 			const filePath = imagePath || videoPath

+ 2 - 1
api/storeAfterSales.js

@@ -12,7 +12,8 @@ export function getMyStoreOrderItemByOrderId(data) {
  }
  
  export function getStoreAfterSalesList(data) {
- 	 return request('/store/app/storeAfterSales/getStoreAfterSalesList',data,'GET');
+ 	 //return request('/store/app/storeAfterSales/getStoreAfterSalesList',data,'GET');
+	 return request('/app/order/getMergedAfterSalesList',data,'GET');
  } 
  
  export function getStoreAfterSalesById(data) {

+ 3 - 3
api/storeOrder.js

@@ -3,8 +3,8 @@ let request = new Request().http
 
  
  export function getMyStoreOrderList(data) {
- 	 return request('/store/app/storeOrder/getMyStoreOrderList',data,'GET');//旧版
-	 //return request('/app/order/getMyMergedOrderList',data,'GET')
+ 	 //return request('/store/app/storeOrder/getMyStoreOrderList',data,'GET');//旧版
+	 return request('/app/order/getMyMergedOrderList',data,'GET')
  } 
  export function getCompanyStoreOrderList(data) {
  	 return request('/store/app/storeOrder/getCompanyStoreOrderList',data,'GET');
@@ -89,7 +89,7 @@ export function clearPayType(data) {
  
 // 删除订单
 export function deleteOrder(data) {
-	return request('/store/app/order/deleteOrder', data, 'POST', 'application/json;charset=UTF-8');
+	return request('/app/order/deleteOrder', data, 'POST', 'application/json;charset=UTF-8');
 }
  
  

+ 3 - 0
common/request.js

@@ -43,6 +43,9 @@ export default class Request {
 		    path ='https://userapp.klbycp.com';
 			router = router.replace('/course_uniapp','')
 		}
+		if(router.indexOf("/course_auto") != -1 ) {
+			router = router.replace('/course_auto','')
+		}
 		// 腕表模块
 		// if (router.indexOf("/watch-api") != -1) {
 		// 	router = router.replace('/watch-api', '')

+ 94 - 61
components/likeProduct.vue

@@ -1,37 +1,41 @@
 <template>
    <view>
 		<view class="like-title" v-show="list &&list.length > 0">
-			<image src="https://zkzh-2025.oss-cn-beijing.aliyuncs.com/shop/images/like.png" mode=""></image>
+			<!-- <image src="https://jnlzjk-1323137866.cos.ap-chongqing.myqcloud.com/shop/images/like.png" mode=""></image> -->
 			<text class="text">猜你喜欢</text>
 		</view>
 		<view class="like-list">
 			<view class="item" v-for="(item,index) in list" :key="index" @click="showProduct(item)">
 				<view class="img-box">
-					<image :src="item.image" mode=""></image>
+					<image class="img" :src="item.image" mode="aspectFill"></image>
+					<view class="tag-row" v-if="item.tagList && item.tagList.length > 0">
+						<text class="tag-chip" v-for="(t, i) in item.tagList" :key="i">{{ t }}</text>
+					</view>
 				</view>
 				<view class="info-box">
 					<view class="title ellipsis2">{{ item.productName }}</view>
 					<view class="price-box">
 						<view class="now">
 							<text class="unit">¥</text>
-							<text class="num"  v-if="userinfoa.isShow==1&&isuser==false">{{item.price.toFixed(2)}}</text>
-							<text class="num" v-else>{{item.otPrice.toFixed(2)}}</text>
+							<text class="num">{{item.price.toFixed(2)}}</text>
+							<text class="numOld" v-if="item.otPrice != null">原价¥{{item.otPrice.toFixed(2)}}</text>
 						</view>
-						<view class="old" v-if="userinfoa.isShow==1&&isuser==false">¥{{item.otPrice.toFixed(2)}}</view>
 					</view>
 				</view>
 			</view>
 		</view>
-		<Loading :loaded="loaded" :loading="loading"></Loading>
+		<view class="load-status" v-if="list.length > 0 || loading" style="padding-bottom: 150rpx;">
+			<text v-if="loading">加载中...</text>
+			<text v-else-if="!hasMore">没有更多了</text>
+			<text v-else>上拉加载更多</text>
+		</view>
    </view>
 </template>
 
 <script>
   import {getGoodsProducts} from '@/api/product'
   import {getUserInfo} from '@/api/user'
-  import Loading from "@/components/Loading";
   export default {
-	components: {Loading },
     name: "likeProduct",
 	data() {
 		return {
@@ -41,7 +45,7 @@
 			},
 			total:0,
 			list:[],
-			loaded: false,
+			hasMore: true,
 			loading: false,
 			userinfoa:[],
 			isuser:false,
@@ -71,7 +75,7 @@
 							// 	this.tuiModalControl=true
 							// }
 							this.userinfoa=res.user
-							console.log(this.userinfoa.isShow,78787)
+							//console.log(this.userinfoa.isShow,78787)
 						}
 					}else{
 						uni.showToast({
@@ -84,33 +88,24 @@
 			);
 		},
 		getGoodsProducts(){
-			console.log(1)
-			var that=this;
-			if (that.loaded == true || that.loading == true) return;
-			that.loading = true;
-			uni.showLoading({
-				title:"加载中..."
-			})
-			getGoodsProducts(that.page).then(
-				res => {
-					if(res.code==200){
-						that.total=res.data.total;
-						that.list.push.apply(that.list, res.data.list);
-						that.loading = false;
-						that.loaded = that.list.length<that.total?false:true;
-						that.page.page = that.page.page + 1;
-						uni.hideLoading()
-					}
-				},
-				err => {
-					uni.hideLoading()
-					uni.showToast({
-						title: err.msg ,
-						icon: 'none',
-						duration: 2000
-					});
+			if (!this.hasMore || this.loading) return
+			this.loading = true
+			getGoodsProducts(this.page).then(res => {
+				if (res.code == 200) {
+					this.total = res.data.total
+					this.list.push.apply(this.list, res.data.list)
+					this.page.page++
+					this.hasMore = this.list.length < this.total
 				}
-			);
+			}).catch(err => {
+				uni.showToast({
+					title: err.msg || '加载失败',
+					icon: 'none',
+					duration: 2000
+				})
+			}).finally(() => {
+				this.loading = false
+			})
 		},
 		showProduct(item){
 			uni.navigateTo({
@@ -125,78 +120,108 @@
 	.like-title{
 		display: flex;
 		align-items: center;
-		justify-content: center;
-		padding: 30upx 0;
+		justify-content: left;
+		padding-bottom:24rpx;
 		image{
 			width: 37upx;
 			height: 37upx;
 			margin-right: 20upx;
 		}
 		.text{
-			font-size: 36upx;
-			font-family: PingFang SC;
-			font-weight: bold;
+			font-family: PingFangSC, PingFang SC;
+			font-weight: 600;
+			font-size: 40rpx;
+			color: #222222;
+			line-height: 56rpx;
 			color: #111111;
-			line-height: 1;
 		}
 	}
 	.like-list{
 		display: flex;
-		flex-wrap: wrap;
+		flex-direction: column;
+		//flex-wrap: wrap;
 		.item{
-			margin-right: 20rpx;
+			//margin-right: 20rpx;
 			margin-bottom: 20rpx;
-			width: 345rpx;
+			//width: 345rpx;
 			background: #FFFFFF;
 			box-shadow: 0px 0px 10rpx 4rpx rgba(199, 199, 199, 0.22);
 			border-radius: 20rpx;
 			overflow: hidden;
-			&:nth-child(2n) {
-				margin-right: 0;
-			}
+			// &:nth-child(2n) {
+			// 	margin-right: 0;
+			// }
 			.img-box{
 				width: 100%;
-				height: 334upx;
+				height: 394rpx;
+				position: relative;
 				image{
 					width: 100%;
 					height: 100%;
 				}
+				.tag-row {
+					position: absolute;
+					bottom:0;
+					display: flex;
+					flex-wrap: wrap;
+					gap: 12rpx;
+					padding: 0 12rpx;
+					margin-bottom: 12rpx;
+				}
+				.tag-chip {
+					font-family: PingFangSC, PingFang SC;
+					font-weight: 400;
+					font-size: 30rpx;
+					color: #AA4726;
+					line-height: 42rpx;
+					background: #FFF4F1;
+					padding: 4rpx 12rpx;
+					border-radius: 6rpx;
+				}
 			}
+			
 			.info-box{
 				box-sizing: border-box;
-				height: 182upx;
-				padding: 20upx 20upx 30upx;
+				//height: 182upx;
+				padding: 20rpx;
 				display: flex;
 				flex-direction: column;
 				justify-content: space-between;
 				.title{
-					font-size: 26upx;
-					font-family: PingFang SC;
+					font-family: PingFangSC, PingFang SC;
 					font-weight: 500;
+					font-size: 36rpx;
 					color: #111111;
-					line-height: 40upx;
+					line-height: 50rpx;
 				}
 				.price-box{
+					margin-top: 8rpx;
 					display: flex;
 					align-items: flex-end;
 					.now{
 						display: flex;
-						align-items: flex-end;
+						align-items: baseline;
 						margin-right: 20upx;
 						.unit{
-							font-size: 24upx;
+							font-size: 28rpx;
 							font-family: PingFang SC;
 							font-weight: 500;
-							color: #FF6633;
-							line-height: 1.2;
+							color: #FF233C;
+							
 							margin-right: 4upx;
 						}
 						.num{
-							font-size: 36upx;
+							font-size: 44rpx;
 							font-family: PingFang SC;
 							font-weight: bold;
-							color: #FF6633;
-							line-height: 1;
+							color: #FF233C;
+							
+						}
+						.numOld{
+							margin-left: 12rpx;
+							font-size: 28rpx;
+							color: #999;
+							text-decoration: line-through;
 						}
 					}
 					.old{
@@ -211,4 +236,12 @@
 			}
 		}
 	}
+	.load-status {
+		padding: 32rpx 0;
+		text-align: center;
+		text {
+			font-size: 28rpx;
+			color: #999;
+		}
+	}
 </style>

+ 1 - 1
components/popupBottom/popupBottom.vue

@@ -79,7 +79,7 @@
 			},
 			zindex: {
 				type: [String, Number],
-				default: 1000
+				default: 9999
 			},
 			maskZindex: {
 				type: [String, Number],

+ 2 - 0
debug.log

@@ -1,2 +1,4 @@
 [0525/154157.733:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: 系统找不到指定的文件。 (0x2)
 [0525/154637.334:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: 系统找不到指定的文件。 (0x2)
+[0604/181518.188:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: 系统找不到指定的文件。 (0x2)
+[0605/131242.950:ERROR:third_party\crashpad\crashpad\util\win\registration_protocol_win.cc:108] CreateFile: 系统找不到指定的文件。 (0x2)

+ 24 - 24
pages.json

@@ -3622,13 +3622,13 @@
 					"path": "video",
 					"style": {
 						"navigationBarTitleText": "课程",
-						"navigationStyle": "custom",
-						"usingComponents": {
-							"uni-popup": "/uni_modules/uni-popup/components/uni-popup/uni-popup"
-						},
-						"componentPlaceholder": {
-							"uni-popup": "view"
-						}
+						"enablePullDownRefresh": false
+						// "usingComponents": {
+						// 	"uni-popup": "/uni_modules/uni-popup/components/uni-popup/uni-popup"
+						// },
+						// "componentPlaceholder": {
+						// 	"uni-popup": "view"
+						// }
 					}
 				},
 				{
@@ -3670,16 +3670,16 @@
 					"path": "videovip",
 					"style": {
 						"navigationBarTitleText": "看课详情",
-						"enablePullDownRefresh": false,
-						"app-plus": {
-							"screenOrientation": [
-								//可选,字符串数组类型,应用支持的横竖屏
-								"portrait-primary", //可选,字符串类型,支持竖屏
-								"portrait-secondary", //可选,字符串类型,支持反向竖屏
-								"landscape-primary", //可选,字符串类型,支持横屏
-								"landscape-secondary" //可选,字符串类型,支持反向横屏
-							]
-							}
+						"enablePullDownRefresh": false
+						// "app-plus": {
+						// 	"screenOrientation": [
+						// 		//可选,字符串数组类型,应用支持的横竖屏
+						// 		"portrait-primary", //可选,字符串类型,支持竖屏
+						// 		"portrait-secondary", //可选,字符串类型,支持反向竖屏
+						// 		"landscape-primary", //可选,字符串类型,支持横屏
+						// 		"landscape-secondary" //可选,字符串类型,支持反向横屏
+						// 	]
+						// 	}
 					}
 				},
 				{
@@ -4382,14 +4382,14 @@
 						"navigationBarTitleText": "支付成功",
 						"enablePullDownRefresh": false
 					}
-				},
-				{
-					"path": "productList",
-					"style": {
-						"navigationBarTitleText": "药品列表",
-						"enablePullDownRefresh": false
-					}
 				}
+				// {
+				// 	"path": "productList",
+				// 	"style": {
+				// 		"navigationBarTitleText": "药品列表",
+				// 		"enablePullDownRefresh": false
+				// 	}
+				// }
 			]
 		},
 		{

+ 5 - 2
pages/common/launch.vue

@@ -12,8 +12,10 @@
 				 
 			};
 		},
+		onLoad() {
+			this.getDicts()
+		},
 		onShow() {
-			//this.getDicts()
 			this.navigatHandler();
 		},
 		methods: {
@@ -47,7 +49,8 @@
 				getDicts().then(
 					res => {
 						if(res.code==200){
-							uni.setStorageSync('dicts',JSON.stringify(res.data));
+							uni.setStorageSync('dicts',JSON.stringify(res));
+							console.log('res',res)
 						}
 					},
 					rej => {}

+ 1 - 1
pages/course/video/living-app.nvue

@@ -494,7 +494,7 @@
 			}
 			//this.getAd()
 			//updateMsgDot()
-			this.createLogs(this.user.userId)
+			//this.createLogs(this.user.userId)
 			this.animate = true
 			this.changeAnimate()
 			const systemInfo = uni.getSystemInfoSync();

+ 254 - 28
pages/user/index.vue

@@ -36,8 +36,19 @@
 								@tap="toSelfQr()" mode="aspectFill"></image> -->
 						</view>
 						<view class="flexBetween es-mt-20" @tap="toSelfQr()" v-if="isOpen">
-							<view class="qyh es-c-75">
+							<!-- <view class="qyh es-c-75">
 								ID:U{{user.userId}}
+							</view> -->
+							<view class="u-f-ajc" @tap.stop.prevent="copyFun(companyUserId?`C${companyUserId}`:`U${user.userId}`)">
+								<view class="qyh es-c-75" v-if="companyUserId">
+									销售号:C{{companyUserId}}
+								</view>
+								<view class="qyh es-c-75" v-if="!companyUserId && user.userId">
+									用户号:U{{user.userId}}
+								</view>
+								<view class="es-ml-12 u-f-ajc">
+									<image class="es-icon-32 u-f-ajc" src="@/static/image/my/copy.png" mode=""></image>
+								</view>
 							</view>
 							<!-- <image class="arrow_black" src="@/static/image/my/right_arrow_black_icon.png"
 								mode="aspectFill"></image> -->
@@ -123,7 +134,19 @@
 							</view>
 						</view>
 					</view>
-					<view class="mybox-menu" v-for="(item, index) in menuToolList" :key="`th_${index}`"
+					<view v-if="toolMenu.length>0" class="order-section">
+						<view class="sec-header">
+							<text class="sec-title">常用工具</text>
+						</view>
+						<view class="order-grid" style="padding: 0;">
+							<view class="order-item" style="width: 25%;" v-for="(item, index) in toolMenu" :key="index"
+								@click="navToToolPages(item)">
+								<image :src="item.icon" mode="aspectFit" class="order-icon" style="width: 52rpx;height: 52rpx;"></image>
+								<text>{{item.name}}</text>
+							</view>
+						</view>
+					</view>
+					<!-- <view class="mybox-menu" v-for="(item, index) in menuToolList" :key="`th_${index}`"
 						style="margin-top: 30rpx;">
 						<view class="u-f-jsb u-f" style="margin-bottom: 36rpx;">
 							<view class="mybox-menu-title">{{item.menuTitle}}</view>
@@ -138,11 +161,14 @@
 								<u-icon name="arrow-right" size="28rpx" color="#000000A6"></u-icon>
 							</view>
 						</view>
-					</view>
+					</view> -->
 				</view>
 				<view class="btns es-mt-40" v-if="isLogin">
 					<view class="login-btn" @click="showLogout">退出登录</view>
 				</view>
+				<view class="like-product">
+					<likeProduct  ref="product" />
+				</view>
 			</view>
 		</view>
 		<u-popup :show="inviteShow" @close="inviteShow=false" round="32rpx">
@@ -185,6 +211,10 @@
 		bindSales,
 		getMyCouponCount
 	} from '@/api/user'
+	import likeProduct from '@/components/likeProduct.vue'
+	import {
+		getGoodsProducts
+	} from '@/api/product'
 	// #ifdef APP-PLUS
 	import permision1 from "@/utils/permission.js"
 	// #endif
@@ -260,10 +290,15 @@
 						}
 					]
 				}],
+				toolMenu: [],
 				isIos: false,
-				showIOSPay: 0
+				showIOSPay: 0,
+				ompanyUserId:'',
 			}
 		},
+		components: {
+			likeProduct
+		},
 		onLoad() {
 			let that = this;
 			uni.$on('refreshUserInfo', function() {
@@ -274,15 +309,16 @@
 					that.isLogin = that.$isLogin()
 				}
 			});
-			if (!this.$qconfig.isAppStore) { //应用市场版本隐藏福币商城
-				let integralMenus = (this.menuList[2]).menus;
-				let integralSubMenu = {
-					name: '福币商城',
-					icon: "../../static/image/my/my_points_icon.png",
-					pageUrl: "/pages/user/integral/integralGoodsList"
-				};
-				integralMenus.splice(2, 0, integralSubMenu);
-			}
+			
+			// if (!this.$qconfig.isAppStore) { //应用市场版本隐藏福币商城
+			// 	let integralMenus = (this.menuList[2]).menus;
+			// 	let integralSubMenu = {
+			// 		name: '福币商城',
+			// 		icon: "../../static/image/my/my_points_icon.png",
+			// 		pageUrl: "/pages/user/integral/integralGoodsList"
+			// 	};
+			// 	integralMenus.splice(2, 0, integralSubMenu);
+			// }
 			// #ifdef APP-PLUS
 			permision1.checkPush();
 			this.isIos = (plus.os.name == "iOS")
@@ -295,7 +331,6 @@
 			uni.$off('refreshUserInfo');
 		},
 		onShow() {
-			uni.showTabBar();
 			this.isLogin = this.$isLogin()
 			let token = uni.getStorageSync('AppToken');
 			if (this.$isLogin()) {
@@ -309,12 +344,85 @@
 				})
 				return;
 			}
-
+			this.companyUserId = uni.getStorageSync('companyUserId')
+		},
+		onReachBottom() {
+			if (this.$refs.product) {
+				this.$refs.product.getGoodsProducts()
+			}
+		},
+		onPageScroll(e) {
+			if (e.scrollTop >= 50) {
+				this.fixedTop = true
+			} else {
+				this.fixedTop = false
+			}
+			this.top = e.scrollTop
 		},
 		created() {
 			this.initMenu();
+			this.initToolMenu();
 		},
 		methods: {
+			copyFun(e) {
+				uni.setClipboardData({
+				  data: e,
+				  success: () => {
+					uni.showToast({ title: '复制成功', icon: 'none' })
+				  },
+				  fail: () => {
+					uni.showToast({ title: '复制失败', icon: 'none' })
+				  }
+				})
+			},
+			initToolMenu() {
+				const menus = [{
+						name: '收货地址',
+						icon: '/static/image/my/address_management_icon.png',
+						url: '/pages/user/address'
+					},
+					{
+						name: '积分商城',
+						icon: '/static/image/my/points_mall.png',
+						url: '/pages_points/integralGoodsList'
+					},
+					{
+						name: '优惠券',
+						icon: '/static/image/my/coupon_collection.png',
+						url: '/pages_shopping/shopping/myCoupon'
+					},
+					{
+						name: '我的足迹',
+						icon: '/static/image/my/my_footprints.png',
+						url: '/pages_user/user/storeProductRelation'
+					},
+					{
+						name: '专属客服',
+						icon: '/static/image/my/customer_service.png',
+						isService: true,
+						url: ''
+					}
+				]
+				// if (this.user && this.user.isCompanyUser) {
+					menus.push({
+						name: '制单管理',
+						icon: '/static/image/my/document_management.png',
+						documentPreparation: true,
+						url: ''
+					}, {
+						name: '销售管理',
+						icon: '/static/image/my/sales_management.png',
+						salesManagement: true,
+						url: ''
+					})
+				// }
+				menus.push({
+					name: '关于我们',
+					icon: '/static/image/my/feedback_icon.png',
+					url: '/pages/user/about'
+				})
+				this.toolMenu = menus
+			},
 			async initMenu() {
 				this.menuList = [{
 						menuTitle: "我的订单",
@@ -591,7 +699,23 @@
 					}
 				}
 			},
-
+           navToToolPages(e) {
+           	if (e.isService && !e.url) {
+           		this.toCompany()
+           		return
+           	}
+           	if (e.documentPreparation && !e.url) {
+           		this.documentPreparationFun()
+           		return
+           	}
+           	if (e.salesManagement && !e.url) {
+           		this.toManagerCourse()
+           		return
+           	}
+           	uni.navigateTo({
+           		url: e.url
+           	});
+           },
 			scanCodeFun() {
 				let that = this
 				uni.scanCode({
@@ -778,6 +902,17 @@
 					})
 				}
 			},
+			toManagerCourse() {
+				if (uni.getStorageSync('ManageToken')) {
+					uni.navigateTo({
+						url: '/pages_manage/index'
+					})
+				} else {
+					uni.navigateTo({
+						url: '/pages_manage/login'
+					})
+				}
+			},
 			myOrderFun(item) {
 				const arr = ['待付款', '待发货', '待收货', '已完成']
 				let status = ''
@@ -853,6 +988,7 @@
 								uni.setStorageSync('userInfo', JSON.stringify(res.user));
 								this.user = res.user;
 								this.initMenu()
+								this.initToolMenu()
 							} else {
 								uni.showToast({
 									icon: 'none',
@@ -1025,7 +1161,39 @@
 					}
 				});
 			},
-
+            getGoodsProducts() {
+            	var that = this;
+            	if (that.likeGoodsHasMore == true || that.likeGoodsLoading == true) return;
+            	that.likeGoodsLoading = true;
+            	uni.showLoading({
+            		title: "加载中..."
+            	})
+            	getGoodsProducts(that.page).then(
+            		res => {
+            			if (res.code == 200) {
+            				that.total = res.data.total;
+            				that.likeGoodsList.push.apply(that.likeGoodsList, res.data.list);
+            				that.likeGoodsLoading = false;
+            				that.likeGoodsHasMore = that.likeGoodsList.length < that.total ? false : true;
+            				that.page.page = that.page.page + 1;
+            				uni.hideLoading()
+            			}
+            		},
+            		err => {
+            			uni.hideLoading()
+            			uni.showToast({
+            				title: err.msg,
+            				icon: 'none',
+            				duration: 2000
+            			});
+            		}
+            	);
+            },
+            showProduct(item) {
+            	uni.navigateTo({
+            		url: '/pages/shopping/productDetails?productId=' + item.productId
+            	})
+            },
 		},
 		computed: {
 			// 计算属性的 getter
@@ -1046,9 +1214,6 @@
 	}
 
 	.top-cont {
-		height: 100vh;
-		// overflow: hidden;
-		// height: 370rpx;
 		position: relative;
 		display: flex;
 		flex-direction: column;
@@ -1170,11 +1335,8 @@
 	}
 
 	.mybox {
-		flex: 1;
-		overflow-y: auto;
-		overflow-x: hidden;
 		padding: 12rpx 24rpx;
-
+        padding-bottom: 88rpx;
 		&-info {
 			padding: 0 16rpx;
 			@include u-flex(row, flex-start, flex-start);
@@ -1234,7 +1396,7 @@
 			background: #FFFFFF;
 			border-radius: 16rpx;
 			padding: 30rpx 30rpx 40rpx 24rpx;
-			margin: 20rpx 0;
+			margin: 30rpx 0;
 
 			&-title {
 				font-weight: 600;
@@ -1318,7 +1480,61 @@
 			}
 		}
 	}
-
+    .order-section {
+    	background: #fff;
+    	border-radius: 24rpx;
+    	padding: 30rpx 24rpx 40rpx 24rpx;
+    	margin-bottom: 30rpx;
+    
+    	.sec-header {
+    		display: flex;
+    		justify-content: space-between;
+    		align-items: center;
+    
+    		.sec-title {
+    			font-weight: bold;
+    			font-size: 40rpx;
+    			color: rgba(0, 0, 0, 0.85);
+    		}
+    
+    		.more {
+    			display: flex;
+    			align-items: center;
+    
+    			text {
+    				font-size: 32rpx;
+    				color: rgba(0, 0, 0, 0.65);
+    				margin-right: 10rpx;
+    			}
+    		}
+    	}
+    
+    	.order-grid {
+    		display: flex;
+    		// justify-content: space-around;
+    		flex-wrap: wrap;
+    		padding: 0 10rpx;
+    
+    		.order-item {
+    			width: 33.3%;
+    			display: flex;
+    			flex-direction: column;
+    			align-items: center;
+    			margin-top: 30rpx;
+    
+    			.order-icon {
+    				width: 60rpx;
+    				height: 60rpx;
+    				margin-bottom: 10rpx;
+    			}
+    
+    			text {
+    				font-size: 36rpx;
+    				color: rgba(0, 0, 0, 0.85);
+    			}
+    		}
+    	}
+    }
 	.vip-card {
 		height: 172rpx;
 		margin-top: 28rpx;
@@ -1372,10 +1588,20 @@
 		}
 	}
 
-
+   .like-product {
+   	width: 100%;
+   	margin-top: 40rpx;
+   }
+   
+   .likeTitleTextClass {
+   	font-weight: bold;
+   	font-size: 40rpx;
+   	color: #222222;
+   	margin-bottom: 30rpx;
+   }
 	.btns {
 		// height: 110rpx;
-		padding-bottom: 88rpx;
+		//padding-bottom: 88rpx;
 	}
 
 	.login-btn {

+ 249 - 0
pages_course/components/FeaturedCommentXgPlayer.vue

@@ -0,0 +1,249 @@
+<template>
+	<view class="fc-xg-player-host rating-msg-video">
+		<!-- #ifdef MP-WEIXIN -->
+		<video
+			:id="playerId"
+			class="fc-xg-player-mp-video"
+			:src="videoUrl"
+			:poster="poster"
+			:controls="false"
+			object-fit="fill"
+			:show-play-btn="false"
+			:show-center-play-btn="false"
+			:enable-progress-gesture="false"
+		></video>
+		<!-- #endif -->
+		<!-- #ifndef MP-WEIXIN -->
+		<view
+			class="fc-xg-player-render"
+			:fcXgPlayerConfig="fcXgPlayerConfig"
+			:change:fcXgPlayerConfig="fcXgRender.onConfig"
+			:fcXgPauseCmd="fcXgPauseCmd"
+			:change:fcXgPauseCmd="fcXgRender.onPause"
+		>
+			<view :id="playerId" class="fc-xg-player-inner"></view>
+		</view>
+		<!-- #endif -->
+	</view>
+</template>
+
+<script>
+	export default {
+		name: 'FeaturedCommentXgPlayer',
+		props: {
+			playerId: {
+				type: String,
+				required: true
+			},
+			videoUrl: {
+				type: String,
+				default: ''
+			},
+			poster: {
+				type: String,
+				default: ''
+			}
+		},
+		data() {
+			return {
+				fcXgPauseCmd: 0
+			}
+		},
+		computed: {
+			fcXgPlayerConfig() {
+				return {
+					playerId: this.playerId,
+					url: this.videoUrl || '',
+					poster: this.poster || ''
+				}
+			}
+		},
+		methods: {
+			pause() {
+				// #ifdef MP-WEIXIN
+				try {
+					const c = uni.createVideoContext(this.playerId, this)
+					if (c && c.pause) c.pause()
+				} catch (e) {}
+				// #endif
+				// #ifndef MP-WEIXIN
+				this.fcXgPauseCmd = Date.now()
+				// #endif
+			}
+		}
+	}
+</script>
+
+<script module="fcXgRender" lang="renderjs">
+	const XG_SCRIPT = 'https://unpkg.byted-static.com/xgplayer/3.0.20/dist/index.min.js';
+	const XG_CSS = 'https://unpkg.byted-static.com/xgplayer/3.0.20/dist/index.min.css';
+
+	export default {
+		data() {
+			return {
+				player: null,
+				lastConfig: null
+			};
+		},
+		beforeDestroy() {
+			this._destroyPlayer();
+		},
+		methods: {
+			_ensureXgAssets(cb) {
+				const run = () => {
+					this._ensureStyles();
+					cb();
+				};
+				if (typeof window.Player === 'function') {
+					run();
+					return;
+				}
+				if (window.__fcXgScriptLoading) {
+					window.__fcXgScriptLoading.then(run);
+					return;
+				}
+				window.__fcXgScriptLoading = new Promise((resolve) => {
+					if (!document.querySelector('link[data-fc-xg-css]')) {
+						const link = document.createElement('link');
+						link.rel = 'stylesheet';
+						link.href = XG_CSS;
+						link.setAttribute('data-fc-xg-css', '1');
+						document.head.appendChild(link);
+					}
+					const script = document.createElement('script');
+					script.src = XG_SCRIPT;
+					script.onload = () => resolve();
+					script.onerror = () => resolve();
+					document.head.appendChild(script);
+				});
+				window.__fcXgScriptLoading.then(run);
+			},
+			_ensureStyles() {
+				if (document.querySelector('style[data-fc-xg-inline]')) return;
+				const style = document.createElement('style');
+				style.setAttribute('data-fc-xg-inline', '1');
+				style.textContent = [
+					'.fc-xg-player-host, .fc-xg-player-render, .fc-xg-player-inner {',
+					'  width: 100% !important;',
+					'  height: 100% !important;',
+					'  min-height: 100% !important;',
+					'}',
+					'.fc-xg-player-host .xgplayer {',
+					'  width: 100% !important;',
+					'  height: 100% !important;',
+					'  min-height: 100% !important;',
+					'}',
+					'.fc-xg-player-host .xgplayer video {',
+					'  width: 100% !important;',
+					'  height: 100% !important;',
+					'  object-fit: fill !important;',
+					'}'
+				].join('\n');
+				document.head.appendChild(style);
+			},
+			_destroyPlayer() {
+				if (!this.player) return;
+				try {
+					this.player.destroy();
+				} catch (e) {}
+				this.player = null;
+			},
+			_shouldReinit(newValue, oldValue) {
+				if (!this.player || !oldValue || !newValue) return true;
+				return newValue.url !== oldValue.url || newValue.playerId !== oldValue.playerId;
+			},
+			_waitDom(playerId, cb, attempt = 0) {
+				const el = document.getElementById(playerId);
+				if (el) {
+					cb(el);
+					return;
+				}
+				if (attempt >= 30) return;
+				setTimeout(() => this._waitDom(playerId, cb, attempt + 1), 80);
+			},
+			_initPlayer(config) {
+				const Player = window.Player;
+				if (!Player || !config || !config.url || !config.playerId) return;
+				const playerId = config.playerId;
+				if (this.player && !this._shouldReinit(config, this.lastConfig)) {
+					this.lastConfig = config;
+					return;
+				}
+				this._destroyPlayer();
+				this.lastConfig = config;
+				const option = {
+					lang: 'zh',
+					url: config.url,
+					id: playerId,
+					autoplay: false,
+					loop: false,
+					height: '100%',
+					width: '100%',
+					poster: config.poster || '',
+					playsinline: true,
+					videoFillMode: 'fill',
+					controls: false,
+					ignores: [
+						'progress',
+						'volume',
+						'time',
+						'play',
+						'start',
+						'definition',
+						'fullscreen',
+						'miniplayer',
+						'miniscreen',
+						'keyboard',
+						'playbackrate'
+					],
+					disableGesture: true,
+					closeVideoTouch: true,
+					closeVideoDblclick: true,
+					closeVideoClick: true
+				};
+				this.player = new Player(option);
+			},
+			onConfig(newValue, oldValue) {
+				if (!newValue || !newValue.url) {
+					this._destroyPlayer();
+					this.lastConfig = null;
+					return;
+				}
+				this._ensureXgAssets(() => {
+					this._waitDom(newValue.playerId, () => {
+						this._initPlayer(newValue);
+					});
+				});
+			},
+			onPause() {
+				if (this.player && this.player.pause) {
+					try {
+						this.player.pause();
+					} catch (e) {}
+				}
+			}
+		}
+	};
+</script>
+
+<style scoped lang="scss">
+	.fc-xg-player-host {
+		width: 100%;
+		height: 100%;
+		min-height: 180rpx;
+		display: block;
+		overflow: hidden;
+	}
+	.fc-xg-player-render,
+	.fc-xg-player-inner {
+		width: 100%;
+		height: 100%;
+		min-height: 180rpx;
+	}
+	.fc-xg-player-mp-video {
+		width: 100%;
+		height: 100%;
+		min-height: 180rpx;
+		display: block;
+	}
+</style>

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 379 - 332
pages_course/video.vue


+ 248 - 124
pages_course/videovip.vue

@@ -128,7 +128,7 @@
 								v-if="currentCardItem && currentCardItem.images"
 								style="width:150px;height:150px;object-fit:cover;"
 								:src="currentCardItem.images"
-								mode="aspectFit"
+								mode="aspectFill"
 							></image>
 						</view>
 						<view class="goods-cover-info">
@@ -264,11 +264,11 @@
 				<view class="rating-ques-block" v-for="(item,index) in quesList" :key="index">
 					<view class="title">{{item.title}}</view>
 					<view class="sat-box">
-						<view class="sat" v-for="(option,idx) in item.questionOption" :key="idx"  :class="aindex==option.indexId?'sat--active':''" @click="handleAnswer(item,option)">
-							<view class="sat-img-wrap" :class="rateBounceIndex==option.indexId?'sat-img-bounce':''">
-								<image :src="aindex==option.indexId?rateList[option.indexId].selIcon:rateList[option.indexId].icon" mode="aspectFit"></image>
+						<view class="sat" v-for="(option,idx) in item.questionOption" :key="idx"  :class="isRateOptionSelected(item, option)?'sat--active':''" @click="handleAnswer(item,option,index)">
+							<view class="sat-img-wrap" :class="rateBounceIndex===(index + '_' + option.indexId)?'sat-img-bounce':''">
+								<image :src="getRateOptionIcon(option, isRateOptionSelected(item, option), idx)" mode="aspectFit"></image>
 							</view>
-							<text :class="aindex==option.indexId?'active':''">{{option.name}}</text>
+							<text :class="isRateOptionSelected(item, option)?'active':''">{{option.name}}</text>
 						</view>
 					</view>
 				</view>
@@ -308,17 +308,13 @@
 								<view class="rating-msg-text">{{ getCommentTextOnly(it) }}</view>
 								<view v-if="getCommentVideoUrl(it)" class="rating-msg-media rating-msg-media--video">
 									<view class="rating-msg-video-wrap">
-										<video
-											:id="getFeaturedCommentVideoId(it, idx)"
-											class="rating-msg-video"
-											:src="getCommentVideoUrl(it)"
+										<featured-comment-xg-player
+											ref="featuredCommentXg"
+											:ref-in-for="true"
+											:player-id="getFeaturedCommentVideoId(it, idx)"
+											:video-url="getCommentVideoUrl(it)"
 											:poster="getCommentVideoPoster(it)"
-											:controls="false"
-											object-fit="fill"
-											:show-play-btn="false"
-											:show-center-play-btn="false"
-											:enable-progress-gesture="false"
-										></video>
+										/>
 										<view
 											class="rating-msg-video-play-cover"
 											@tap.stop="openFeaturedCommentMediaPreview(it, idx, 'video')"
@@ -467,11 +463,11 @@
 					<view class="action-label">我的订单</view>
 				</view>
 				<view class="more-action-item"
-					@click="navgetTo('/pages_user/user/integralLogsList')">
+					@click="navgetTo('/pages_points/integralLogsList')">
 					<u-icon name="calendar" color="#FF233C" size="40"></u-icon>
 					<view class="action-label">积分记录</view>
 				</view>
-				<view class="more-action-item" @click="navgetTo('/pages_user/user/integralGoodsList')">
+				<view class="more-action-item" @click="navgetTo('/pages_points/integralGoodsList')">
 					<u-icon name="gift" color="#FF233C" size="40"></u-icon>
 					<view class="action-label">兑换好礼</view>
 				</view>
@@ -539,7 +535,7 @@
 							<image src="https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/video/upimg.png"></image>
 						</view>
 					</view>
-				<view class="author-cart" v-show="!(tabIndex === 0 && showTabFeaturedComments && isLogin && isAddKf == 1 && !isquestion && !isShu)">
+				<view class="author-cart" v-show="isLogin && isAddKf==1">
 					<view class="more-box">
 						<image src="/static/images/course/more24.png" @click="showMore"></image>
 						<view class="tips">更多</view>
@@ -698,6 +694,7 @@
 	} from 'vuex';
 	import ykscreenRecord from "@/components/yk-screenRecord/yk-screenRecord.vue"
 	import courseExpiration from './components/courseExpiration.vue'
+	import FeaturedCommentXgPlayer from './components/FeaturedCommentXgPlayer.vue'
 	import {
 		getErrMsg,
 		getH5CourseByVideoId,
@@ -719,7 +716,8 @@
 		components: {
 			ykscreenRecord,
 			courseExpiration,
-			goodsList
+			goodsList,
+			FeaturedCommentXgPlayer
 		},
 		data() {
 			return {
@@ -727,7 +725,6 @@
 				isMore:false,//更多
 				isCart:false,//购物车
 				isShu:false,//竖屏默认
-				aindex:null,
 				rateBounceIndex: null,
 				_rateBounceTimer: null,
 				rateList:[{
@@ -839,7 +836,7 @@
 				/** 当前是否处于播放中 */
 				videoPlaying: false,
 				// 是否允许拖动进度条
-				linkType: 0,
+				// linkType: 0,
 				ip: null,
 				checked: true,
 				isFinish: 0, // 是否完课
@@ -1020,6 +1017,7 @@
 				return this.$store.state.imgpath
 			},
 			appid() {
+				console.log('this.$store.state.appid',this.$store.state.appid)
 				return this.$store.state.appid
 			},
 			// 格式化后的倒计时
@@ -1095,7 +1093,7 @@
 			hasVisibleRatingTab() {
 				return this.visibleRatingTabCount > 0
 			},
-			...mapGetters(['coureLogin']),
+			//...mapGetters(['coureLogin']),
 			/** 小黄车商品中「最早上架」时间点(秒),用于跑马灯起始展示 */
 			firstShelfSecondsAmongProducts() {
 				const list = this.treatmentPackage || []
@@ -1127,7 +1125,7 @@
 				return !!(this.isShu || this.isFull)
 			},
 			videovipVideoConfig() {
-				const allowSeek = this.linkType == 1 || this.isFinish == 1 || this.isEnded
+				const allowSeek = this.isFinish == 1 || this.isEnded
 				return {
 					url: this.videoUrl || '',
 					poster: this.poster || '',
@@ -1135,23 +1133,24 @@
 					isFast: allowSeek ? 1 : 0,
 					videoReloadKey: this.videoReloadKey,
 					displayType: this.displayType || 'landscape',
+					layoutShuKey: this.isShu ? 1 : 0,
 					time: Date.now()
 				}
 			},
 		},
 		watch: {
-			coureLogin: {
-				immediate: true, // 页面一进入就检查一次
-				handler(val) {
-					console.log(val, '----')
-					if (val == 2 && this.isLogin) {
-						console.log("看课AppToken失效,请重新登录")
-						this.isLogin = false
-						this.isAddKf = 0
-						this.$showLoginPage()
-					}
-				}
-			},
+			// coureLogin: {
+			// 	immediate: true, // 页面一进入就检查一次
+			// 	handler(val) {
+			// 		console.log(val, '----')
+			// 		if (val == 2 && this.isLogin) {
+			// 			console.log("看课AppToken失效,请重新登录")
+			// 			this.isLogin = false
+			// 			this.isAddKf = 0
+			// 			this.$showLoginPage()
+			// 		}
+			// 	}
+			// },
 			isTripleMarqueeMode(now, was) {
 				if (!now || was) return
 				if (!this.fakeMarqueeEligible || !this._fakeMarqueeStarted) return
@@ -1211,7 +1210,7 @@
 			})
 		    this.getVideoContainerHeight()
 			this._lockAppPortrait()
-			this.code = option.code
+			// this.code = option.code
 			this.userInfo = this.$getUserInfo()
 			this.appToken = uni.getStorageSync('companyUserInfo')
 			console.log('option', option)
@@ -1309,6 +1308,7 @@
 			// 页面隐藏时停止完课积分倒计时
 			this.stopCountdown()
 			this.stopProductHotTimer()
+			this._flushWatchProgress()
 			// if (this.interval != null) {
 			// 	clearInterval(this.interval)
 			// 	this.interval = null
@@ -1340,6 +1340,7 @@
 			this.clearIntegral()
 			this.stopFakeMarqueeLoop()
 			this.fakeOrderPool = []
+			this._flushWatchProgress()
 			// #ifndef H5
 			if (this._featuredCommentKbHandler) {
 				uni.offKeyboardHeightChange(this._featuredCommentKbHandler)
@@ -1369,6 +1370,7 @@
 			this.stopProductHotTimer()
 			this.clearIntegral()
 			this.stopFakeMarqueeLoop()
+			this._flushWatchProgress()
 			// #ifndef H5
 			if (this._featuredCommentKbHandler) {
 				uni.offKeyboardHeightChange(this._featuredCommentKbHandler)
@@ -1836,6 +1838,15 @@
 			getFeaturedCommentVideoId(it, idx) {
 				return 'feat-comment-video-' + this.getFeaturedCommentRowKey(it, idx)
 			},
+			/** 暂停精选留言列表内所有西瓜/原生内联播放器(弹窗预览仍用独立 video) */
+			_pauseFeaturedCommentListInlinePlayers() {
+				const refs = this.$refs.featuredCommentXg
+				if (!refs) return
+				const arr = Array.isArray(refs) ? refs : [refs]
+				arr.forEach((r) => {
+					if (r && typeof r.pause === 'function') r.pause()
+				})
+			},
 			openFeaturedCommentMediaPreview(it, listIdx, mode, imageIndex) {
 				const m = mode === 'video' ? 'video' : 'image'
 				if (m === 'image') {
@@ -1846,13 +1857,7 @@
 						urls.length - 1
 					)
 					this._pauseCourseVideo()
-					try {
-						if (this.getCommentVideoUrl(it)) {
-							const id = this.getFeaturedCommentVideoId(it, listIdx)
-							const c = uni.createVideoContext(id, this)
-							if (c && c.pause) c.pause()
-						}
-					} catch (e) {}
+					this._pauseFeaturedCommentListInlinePlayers()
 					this.featuredCommentMediaPreviewMode = 'image'
 					this.featuredCommentMediaPreviewUrls = urls
 					this.featuredCommentMediaPreviewIndex = i
@@ -1864,11 +1869,7 @@
 				const vurl = this.getCommentVideoUrl(it)
 				if (!vurl) return
 				this._pauseCourseVideo()
-				try {
-					const id = this.getFeaturedCommentVideoId(it, listIdx)
-					const c = uni.createVideoContext(id, this)
-					if (c && c.pause) c.pause()
-				} catch (e) {}
+				this._pauseFeaturedCommentListInlinePlayers()
 				this.featuredCommentMediaPreviewMode = 'video'
 				this.featuredCommentMediaPreviewUrls = []
 				this.featuredCommentMediaPreviewIndex = 0
@@ -1919,16 +1920,7 @@
 			},
 			onFeaturedMediaPreviewModalVideoPlay() {
 				this._pauseCourseVideo()
-				const list = this.featuredCommentDisplayList || []
-				for (let i = 0; i < list.length; i++) {
-					const row = list[i]
-					if (!this.getCommentVideoUrl(row)) continue
-					const vid = this.getFeaturedCommentVideoId(row, i)
-					try {
-						const c = uni.createVideoContext(vid, this)
-						if (c && c.pause) c.pause()
-					} catch (e) {}
-				}
+				this._pauseFeaturedCommentListInlinePlayers()
 			},
 			_featuredMediaMaxBytes() {
 				return 100 * 1024 * 1024
@@ -2063,6 +2055,44 @@
 			closeFeaturedMediaActionSheet() {
 				this.featuredMediaActionSheetShow = false
 			},
+			/** 精选留言选相册视频:小程序用 chooseMedia,App/H5 用 chooseVideo */
+			_pickFeaturedAlbumVideo(clearIfStillArmed) {
+				const onFail = () => {
+					// 勿在此处清 suppress:须由 onShow 消费,避免先于 onShow 清标志导致停跑马灯
+					this._resumeCourseVideoAfterFeaturedJob()
+				}
+				const onSuccess = (path, size, thumbTempPath) => {
+					if (!path) return
+					const sz = size != null ? size : 0
+					this._applyFeaturedMediaIfSizeOk('video', path, sz, thumbTempPath || '')
+				}
+				// #ifdef MP-WEIXIN
+				wx.chooseMedia({
+					count: 1,
+					mediaType: ['video'],
+					sourceType: ['album'],
+					maxDuration: 30,
+					success: (res) => {
+						const f0 = res.tempFiles && res.tempFiles[0]
+						if (!f0) return
+						onSuccess(f0.tempFilePath, f0.size, f0.thumbTempFilePath)
+					},
+					fail: onFail,
+					complete: clearIfStillArmed
+				})
+				// #endif
+				// #ifndef MP-WEIXIN
+				uni.chooseVideo({
+					sourceType: ['album'],
+					maxDuration: 30,
+					success: (res) => {
+						onSuccess(res.tempFilePath, res.size, res.thumbTempFilePath)
+					},
+					fail: onFail,
+					complete: clearIfStillArmed
+				})
+				// #endif
+			},
 			onFeaturedMediaSheetPick(tapIndex) {
 				this.closeFeaturedMediaActionSheet()
 				// 立即调起相册/视频会与 u-popup 遮罩关闭叠加,小程序端易残留半透明层;对齐关闭动画后再打开系统选择器
@@ -2100,24 +2130,7 @@
 							(this.videoPlaying || this._courseWasPlayingBeforeFeaturedCommentOpen)
 						this._pauseCourseVideo()
 						const clearIfStillArmed = this._armFeaturedMediaPickerOnShowSuppress()
-						wx.chooseMedia({
-							count: 1,
-							mediaType: ['video'],
-							sourceType: ['album'],
-							maxDuration: 30,
-							success: (res) => {
-								const path = res.tempFiles[0].tempFilePath
-								if (!path) return
-								const size = res.tempFiles[0].size != null ? res.tempFiles[0].size : 0
-								this._applyFeaturedMediaIfSizeOk('video', path, size, res.tempFiles[0].thumbTempFilePath || '')
-							},
-							fail: (err) => {
-								//console.log('huoqu122221212122222222', err)
-								// 勿在此处清 suppress:须由 onShow 消费,避免先于 onShow 清标志导致停跑马灯
-								this._resumeCourseVideoAfterFeaturedJob()
-							},
-							complete: clearIfStillArmed
-						})
+						this._pickFeaturedAlbumVideo(clearIfStillArmed)
 					}
 				}, afterSheetCloseMs)
 			},
@@ -2562,15 +2575,41 @@
 				this.marqueeTripleSwitching = false
 				this._stopSelfMarqueeClock()
 			},
-			goRate(index){
-				this.aindex = index
+			getRateByIndexId(indexId, optionIdx) {
+				const list = this.rateList || []
+				if (!list.length) return null
+				const n = Number(indexId)
+				if (Number.isFinite(n)) {
+					const byId = list.find((r) => r && Number(r.id) === n)
+					if (byId) return byId
+					if (list[n]) return list[n]
+				}
+				const i = Number(optionIdx)
+				if (Number.isFinite(i) && list[i]) return list[i]
+				return list[0]
+			},
+			getRateOptionIcon(option, active, optionIdx) {
+				const rate = this.getRateByIndexId(option && option.indexId, optionIdx)
+				if (!rate) return ''
+				return active ? (rate.selIcon || rate.icon || '') : (rate.icon || '')
+			},
+			isRateOptionSelected(item, option) {
+				if (!item || !option) return false
+				if (Number(item.type) === 2) {
+					const answer = item.answer ? item.answer.split(',') : []
+					return answer.indexOf(option.name) !== -1
+				}
+				return item.answer === option.name
+			},
+			goRate(questionIndex, optionIndexId) {
+				const bounceKey = `${questionIndex}_${optionIndexId}`
 				if (this._rateBounceTimer) {
 					clearTimeout(this._rateBounceTimer)
 					this._rateBounceTimer = null
 				}
 				this.rateBounceIndex = null
 				this.$nextTick(() => {
-					this.rateBounceIndex = index
+					this.rateBounceIndex = bounceKey
 					this._rateBounceTimer = setTimeout(() => {
 						this.rateBounceIndex = null
 						this._rateBounceTimer = null
@@ -2608,16 +2647,16 @@
 					// 只在“进入全屏”时切换,避免 exitFullScreen 触发的 false 事件二次反转
 					if (!isFullScreen || this.fullscreenToggleLock) return;
 					this.fullscreenToggleLock = true;
-					this.isShu = !this.isShu
-					setTimeout(() => {
+					this.isShu = !this.isShu;
+					this.$nextTick(() => {
 						this._xgExitFullscreen();
 						this.isFull = false;
 						this._lockAppPortrait();
 						this.fullscreenToggleTimer = setTimeout(() => {
 							this.fullscreenToggleLock = false;
 							this.fullscreenToggleTimer = null;
-						}, 220);
-					}, 40);
+						}, 280);
+					});
 					return;
 				}
 				// 横屏课程:全屏锁横屏,退出全屏锁竖屏并恢复顶部小窗布局
@@ -2791,7 +2830,7 @@
 				} else {
 					// console.log(this.isFinish)
 					const suppressAntiSeek = Date.now() < (this._antiSeekSuppressExpireAt || 0)
-					if (!suppressAntiSeek && this.linkType != 1 && (currentTime - this.playTime > 3 || currentTime - this
+					if (!suppressAntiSeek && (currentTime - this.playTime > 3 || currentTime - this
 						.playTime < -3) && this
 						.isFinish != 1) {
 						uni.showToast({
@@ -2990,15 +3029,24 @@
 			// 商品卡片跳转
 			goBuy(item) {
 				if (!item || !item.productId) return
-				uni.navigateTo({
-					url: '/pages/shopping/productDetails?productId=' + item.productId +
-						'&companyId=' + (this.urlOption.companyId || '') +
-						'&companyUserId=' + (this.urlOption.companyUserId || '') +
-						'&courseId=' + (this.urlOption.courseId || '') +
-						'&videoId=' + (this.urlOption.videoId || '') +
-						'&projectId=' + (this.urlOption.projectId || '') +
-						'&periodId=' + (this.urlOption.periodId || '')
-				})
+				const url = '/pages/shopping/productDetails?productId=' + item.productId +
+					'&companyId=' + (this.urlOption.companyId || '') +
+					'&companyUserId=' + (this.urlOption.companyUserId || '') +
+					'&courseId=' + (this.urlOption.courseId || '') +
+					'&videoId=' + (this.urlOption.videoId || '') +
+					'&projectId=' + (this.urlOption.projectId || '') +
+					'&periodId=' + (this.urlOption.periodId || '')
+				// 横屏全屏下先退出全屏再跳转,避免视频层遮挡商品详情页
+				if (this.isFull) {
+					this._xgExitFullscreen()
+					this._lockAppPortrait()
+					this.isFull = false
+					setTimeout(() => {
+						uni.navigateTo({ url })
+					}, 100)
+					return
+				}
+				uni.navigateTo({ url })
 			},
 			// 当开始/继续播放时触发play事件
 			getPlay() {
@@ -3227,7 +3275,16 @@
 					rej => {}
 				)
 			},
-			handleAnswer(item, option, idx) {
+			handleAnswer(itemOrPayload, option, questionIndex) {
+				let item = itemOrPayload
+				let opt = option
+				if (itemOrPayload && itemOrPayload.item && itemOrPayload.option) {
+					item = itemOrPayload.item
+					opt = itemOrPayload.option
+					questionIndex = this.quesList.indexOf(item)
+				} else if (questionIndex == null || questionIndex < 0) {
+					questionIndex = this.quesList.indexOf(item)
+				}
 				// 以“完课积分倒计时”结束作为答题资格
 				// if (!this.remainTimeReady) {
 				// 	uni.showToast({
@@ -3236,7 +3293,9 @@
 				// 	})
 				// 	return
 				// }
-				this.goRate(option.indexId)
+				if (questionIndex >= 0 && opt) {
+					this.goRate(questionIndex, opt.indexId)
+				}
 				if (Number(this.remainTime || 0) > 0) {
 					uni.showToast({
 						title: "完课倒计时结束再评分",
@@ -3247,15 +3306,15 @@
 
 				if (item.type == 1) {
 					// 单选option
-					item.answer = option.name
+					item.answer = opt.name
 				} else if (item.type == 2) {
 					// 多选
 					let answer = item.answer ? item.answer.split(',') : []
-					if (answer.indexOf(option.name) === -1) {
-						answer.push(option.name)
+					if (answer.indexOf(opt.name) === -1) {
+						answer.push(opt.name)
 						item.answer = answer.join(',')
 					} else {
-						answer.splice(answer.indexOf(option.name), 1)
+						answer.splice(answer.indexOf(opt.name), 1)
 						item.answer = answer.join(',')
 					}
 				}
@@ -3626,13 +3685,22 @@
 				// this.$refs.kfPopup.close()
 				// this.kfPopup=!this.kfPopup
 			},
+			/** 离开页面前补上报,避免 60s 定时未到导致再次进入从头播放 */
+			_flushWatchProgress() {
+				if (!this.playTime || !this.isLogin || this.isAddKf != 1) return
+				const baseDuration = this.playDuration >= this.duration ? 0 : (Number(this.playDuration) || 0)
+				if (this.playTime <= baseDuration && this.isFinish != 1) return
+				this.getFinishCourseVideo()
+				this.getInternetTraffic()
+			},
 			getFinishCourseVideo() {
-				if (!this.playTime) return
-				// {videoId: this.videoId,duration:this.playTime}
+				if (!this.playTime || !this.isLogin || this.isAddKf != 1) return
+				const userId = this.userInfo && this.userInfo.userId
+				if (!userId) return
 				const param = {
 					duration: this.playTime,
 					videoId: this.videoId,
-					userId: this.userInfo.userId,
+					userId,
 					companyUserId: this.companyUserId
 				}
 				getFinishCourseVideo(param)
@@ -3770,25 +3838,40 @@
 				return document.querySelector('.videovip-video-container') || document.body;
 			},
 			_ensureXgVideoStyles() {
-				if (document.querySelector('style[data-xg-videovip-fix]')) return;
+				const attr = 'data-xg-videovip-fix-v2';
+				const old = document.querySelector('style[data-xg-videovip-fix]');
+				if (old) old.remove();
+				if (document.querySelector('style[' + attr + ']')) return;
 				const style = document.createElement('style');
-				style.setAttribute('data-xg-videovip-fix', '1');
+				style.setAttribute(attr, '1');
 				style.textContent = [
 					'.videovip-xg-player, .videovip-xg-player .xgplayer {',
 					'  width: 100% !important;',
 					'  height: 100% !important;',
 					'}',
-					'.videovip-xg-player video {',
-					'  position: absolute !important;',
-					'  top: 0 !important;',
-					'  left: 0 !important;',
+					'.videovip-xg-player video, .videovip-xg-player .xgplayer-video video {',
 					'  width: 100% !important;',
 					'  height: 100% !important;',
+					'  object-fit: contain !important;',
 					'  background: #000;',
+					'}',
+					'.xgplayer-is-fullscreen video,',
+					'.xgplayer-is-cssfullscreen video,',
+					'.xgplayer.xgplayer-is-fullscreen video,',
+					'.xgplayer.xgplayer-is-cssfullscreen video {',
+					'  object-fit: contain !important;',
 					'}'
 				].join('\n');
 				document.head.appendChild(style);
 			},
+			_applyVideoFillMode(isFullscreen) {
+				if (!this.player) return;
+				const mode = isFullscreen ? 'contain' : 'cover';
+				try {
+					if (this.player.config) this.player.config.videoFillMode = mode;
+					if (typeof this.player.resize === 'function') this.player.resize();
+				} catch (e) {}
+			},
 			_destroyPlayer() {
 				if (!this.player) return;
 				if (this.isFullscreen) {
@@ -3803,11 +3886,39 @@
 				} catch (e) {}
 				this.player = null;
 			},
+			_resizePlayerLayout() {
+				const run = () => {
+					if (!this.player) return;
+					try {
+						this._applyVideoFillMode(!!this.isFullscreen);
+						if (typeof this.player.resize === 'function') this.player.resize();
+					} catch (e) {}
+				};
+				setTimeout(run, 0);
+				setTimeout(run, 80);
+				setTimeout(run, 220);
+			},
+			_notifyFullscreenToVue(isFullscreen) {
+				if (!this.ownerInstance) return;
+				const portrait = this.lastConfig && this.lastConfig.displayType === 'portrait';
+				if (portrait) {
+					const now = Date.now();
+					if (this._fsNotifyVal === isFullscreen && now - (this._fsNotifyAt || 0) < 150) return;
+					this._fsNotifyVal = isFullscreen;
+					this._fsNotifyAt = now;
+				}
+				this.ownerInstance.callMethod('onXgFullscreenChange', {
+					isFullscreen
+				});
+			},
 			initJs(newValue, oldValue, ownerInstance) {
 				if (!newValue || !newValue.url) return;
 				if (!this._shouldReinitPlayer(newValue, oldValue)) {
 					this.lastConfig = newValue;
 					this._syncProgressLock(!!newValue.isFast);
+					if (oldValue && newValue.layoutShuKey !== oldValue.layoutShuKey) {
+						this._resizePlayerLayout();
+					}
 					return;
 				}
 				const bootstrap = () => {
@@ -3845,10 +3956,17 @@
 				if (!this.player) return;
 				const progress = this.player.getPlugin && this.player.getPlugin('progress');
 				if (!progress) return;
-				if (allowSeek) {
-					progress.show && progress.show();
-				} else {
-					progress.hide && progress.hide();
+				progress.show && progress.show();
+				const lockSeek = !allowSeek;
+				if (progress.config) {
+					progress.config.isCloseClickSeek = lockSeek;
+					progress.config.closeMoveSeek = lockSeek;
+					progress.config.isDraggingSeek = false;
+				}
+				if (this.player.config && this.player.config.progress) {
+					this.player.config.progress.isCloseClickSeek = lockSeek;
+					this.player.config.progress.closeMoveSeek = lockSeek;
+					this.player.config.progress.isDraggingSeek = false;
 				}
 			},
 			initPlayer(newValue, oldValue, ownerInstance) {
@@ -3878,7 +3996,7 @@
 					poster: newValue.poster || '',
 					'x5-video-player-type': 'h5',
 					playsinline: true,
-					videoFillMode: 'fill',
+					videoFillMode: 'cover',
 					ignores: ['volume', 'miniscreen', 'keyboard', 'playbackrate'],
 					disableGesture: true,
 					closeVideoTouch: true,
@@ -3935,20 +4053,23 @@
 				});
 				this.player.on(Events.FULLSCREEN_CHANGE, (isFullscreen) => {
 					if (this.isFullscreen == isFullscreen) return;
+					const portrait = this.lastConfig && this.lastConfig.displayType === 'portrait';
+					if (portrait) return;
 					this.isFullscreen = isFullscreen;
+					this._applyVideoFillMode(isFullscreen);
 					this._syncAppOrientationForFullscreen(isFullscreen);
-					owner.callMethod('onXgFullscreenChange', {
-						isFullscreen
-					});
+					this._notifyFullscreenToVue(isFullscreen);
 				});
 				this.player.on(Events.CSS_FULLSCREEN_CHANGE, (isFullscreen) => {
 					if (this.isFullscreen == isFullscreen) return;
 					this.isCSSFull = true;
 					this.isFullscreen = isFullscreen;
-					this._syncAppOrientationForFullscreen(isFullscreen);
-					owner.callMethod('onXgFullscreenChange', {
-						isFullscreen
-					});
+					const portrait = this.lastConfig && this.lastConfig.displayType === 'portrait';
+					if (!portrait) {
+						this._applyVideoFillMode(isFullscreen);
+						this._syncAppOrientationForFullscreen(isFullscreen);
+					}
+					this._notifyFullscreenToVue(isFullscreen);
 				});
 			},
 			_syncAppOrientationForFullscreen(isFullscreen) {
@@ -4430,14 +4551,17 @@
 		}
 		.rating-msg-video-wrap {
 			position: relative;
-			max-width: 320rpx;
-			// background: #000;
+			width: 320rpx;
+			max-width: 100%;
+			height: 180rpx;
+			min-height: 180rpx;
+			background: #000;
+			overflow: hidden;
 		}
 		.rating-msg-video {
 			width: 100%;
+			height: 100%;
 			display: block;
-			max-height: 240rpx;
-			// background: #000;
 		}
 		/* 小程序列表内用封面图代替 video,固定比例避免布局抖动 */
 		.rating-msg-video--poster {

+ 6 - 0
pages_im/pages/conversation/chating1/components/MessageItem/CustomMessageRender.vue

@@ -313,6 +313,7 @@ export default {
 			}
 			if (couseItem.appRealLink) {
 				const match = couseItem.appRealLink.match(/appcourse\/(.*)/);
+				const linkValue = couseItem.appRealLink.split('link=')[1];
 				 if (match && match[1]) {
 				    // 截取到的页面路径 + 参数
 				    const realPath = "/" + match[1];
@@ -322,6 +323,11 @@ export default {
 				      url: realPath
 				    });
 				    return;
+				  }else if(linkValue){
+					  uni.navigateTo({
+					    url: "/pages_course/video?link=" + linkValue
+					  });
+					  return;
 				  }else{
 					  uni.showToast({
 					  	icon: 'none',

+ 111 - 143
pages_live/components/like.vue

@@ -1,16 +1,12 @@
 <template>
-	<view class="like-container">
-		<image class="image" @click="handleLike" src="/static/images/live/zan1.png" ref="likeBtn"></image>
-		<view v-for="(icon, index) in icons" :key="icon.id" class="animated-icon" :style="{
-        top: icon.top + 'px',
-        left: icon.left + 'px',
-        fontSize: icon.size + 'px',
-        color: icon.color,
-        opacity: icon.opacity,
-        transform: 'translate(-50%, calc(-50% + ' + icon.y + 'px)) rotate(' + icon.rotate + 'deg)',
-        transition: 'all ' + icon.duration + 'ms ' + icon.delay + 'ms ease-out'
-      }">
-			{{ icon.symbol }}
+	<view class="like-wrap" :style="wrapStyle" @tap.stop="handleLike">
+		<image class="like-btn-img" :style="imgStyle" mode="aspectFit" src="/static/images/live/zan1.png" />
+		<view class="like-burst-host">
+			<text
+				v-for="icon in icons"
+				:key="icon.id"
+				class="burst-particle"
+				:style="particleStyle(icon)">{{ icon.symbol }}</text>
 		</view>
 	</view>
 </template>
@@ -19,17 +15,18 @@
 	export default {
 		name: 'DouyinLike',
 		props: {
-			// 初始点赞数
-			initialCount: {
+			btnRpx: {
 				type: Number,
-				default: 0
+				default: 80
 			},
-			// 单次点击生成的图标数量
 			iconAmount: {
 				type: Number,
 				default: 6
 			},
-			// 图标颜色集合
+			heartsPerClick: {
+				type: Number,
+				default: 0
+			},
 			iconColors: {
 				type: Array,
 				default: () => [
@@ -38,141 +35,121 @@
 					'#bb7df5', '#eeffc5'
 				]
 			},
-			// 图标大小范围
 			sizeRange: {
 				type: Array,
-				default: () => [40, 78]
+				default: () => [28, 44]
 			},
-			// 动画持续时间范围(ms)
 			durationRange: {
 				type: Array,
-				default: () => [700, 1300]
+				default: () => [700, 1100]
 			},
-			// 上升距离范围(px)
 			riseRange: {
 				type: Array,
-				default: () => [200, 420]
+				default: () => [120, 260]
 			},
-			// 左右飘动范围(px)
 			floatRange: {
 				type: Array,
-				default: () => [-50, 50]
+				default: () => [-36, 36]
 			}
 		},
 		data() {
 			return {
-				// 当前点赞数
-				count: this.initialCount,
-				// 是否已点赞
-				isLiked: false,
-				// 动画图标数组
 				icons: [],
-				// 可使用的图标集合
 				iconSymbols: ['❤', '★', '🎁', '🔥', '👍', '✨', '💖', '🌟'],
-				// 用于生成唯一ID
 				iconId: 0
 			};
 		},
+		computed: {
+			btnPx() {
+				return uni.upx2px(this.btnRpx);
+			},
+			wrapStyle() {
+				const p = this.btnPx;
+				return {
+					width: p + 'px',
+					height: p + 'px',
+					minWidth: p + 'px',
+					minHeight: p + 'px',
+					maxWidth: p + 'px',
+					maxHeight: p + 'px'
+				};
+			},
+			imgStyle() {
+				const p = this.btnPx;
+				return {
+					width: p + 'px',
+					height: p + 'px'
+				};
+			},
+			rpxPx() {
+				return uni.upx2px(1);
+			}
+		},
 		methods: {
-			/**
-			 * 处理点击事件
-			 */
 			handleLike() {
-				// 更新点赞状态
-				this.isLiked = !this.isLiked;
-				this.count += this.isLiked ? 1 : -1;
-				this.$emit('change', {
-					isLiked: this.isLiked,
-					count: this.count
-				});
-
-				// 生成图标动画
-				this.createIcons();
+				this.$emit('like');
+				this.spawnBurst();
 			},
-
-			/**
-			 * 创建多个图标动画(以点赞按钮中心为起点,避免真机触摸坐标偏差)
-			 */
-			createIcons() {
-				const query = uni.createSelectorQuery().in(this);
-				query.select('.like-container').boundingClientRect();
-				query.select('.image').boundingClientRect();
-				query.exec((res) => {
-					const containerRect = res[0];
-					const imageRect = res[1];
-					if (!containerRect || !imageRect) return;
-
-					const x = imageRect.left - containerRect.left + imageRect.width / 2;
-					const y = imageRect.top - containerRect.top + imageRect.height / 2;
-
-					for (let i = 0; i < this.iconAmount; i++) {
-						this.createIcon(x, y, i * 40);
-					}
-				});
+			getBurstCount() {
+				const n = Number(this.heartsPerClick);
+				return n > 0 ? n : this.iconAmount;
 			},
-
-			/**
-			 * 创建单个图标动画
-			 * @param {Number} x 初始X坐标
-			 * @param {Number} y 初始Y坐标
-			 * @param {Number} delay 延迟时间(ms)
-			 */
-			createIcon(x, y, delay) {
-				// 随机属性(sizeRange 为 rpx,转为 px 与定位单位一致)
-				const pxRatio = uni.getSystemInfoSync().windowWidth / 750;
-				const size = this.getRandom(...this.sizeRange) * pxRatio;
+			spawnBurst() {
+				const count = this.getBurstCount();
+				for (let i = 0; i < count; i++) {
+					this.addParticle(i * 36);
+				}
+			},
+			particleStyle(icon) {
+				return {
+					fontSize: icon.size + 'px',
+					color: icon.color,
+					opacity: icon.opacity,
+					transform: `translate3d(calc(-50% + ${icon.dx}px), calc(-50% + ${icon.dy}px), 0) rotate(${icon.rotate}deg)`,
+					transition: `transform ${icon.duration}ms ${icon.delay}ms ease-out, opacity ${icon.duration}ms ${icon.delay}ms ease-out`
+				};
+			},
+			addParticle(delay) {
+				const size = this.getRandom(...this.sizeRange) * this.rpxPx;
 				const color = this.iconColors[Math.floor(Math.random() * this.iconColors.length)];
 				const duration = this.getRandom(...this.durationRange);
 				const riseDistance = -this.getRandom(...this.riseRange);
 				const floatOffset = this.getRandom(...this.floatRange);
-				const rotate = this.getRandom(-30, 30);
-				// 随机选择一个图标
+				const rotate = this.getRandom(-24, 24);
 				const symbol = this.iconSymbols[Math.floor(Math.random() * this.iconSymbols.length)];
+				const id = this.iconId++;
 
-				// 生成唯一ID
-				const iconId = this.iconId++;
-
-				// 创建图标对象
-				const icon = {
-					id: iconId,
-					left: x,
-					top: y,
+				this.icons.push({
+					id,
+					dx: 0,
+					dy: 0,
 					size,
 					color,
 					opacity: 1,
-					y: 0,
 					rotate,
 					duration,
 					delay,
 					symbol
-				};
-
-				// 添加到数组
-				this.icons.push(icon);
+				});
 
-				// 触发动画
 				setTimeout(() => {
-					// 使用Vue的$set方法确保响应式更新
-					this.$set(this.icons, this.icons.findIndex(item => item.id === iconId), {
-						...this.icons.find(item => item.id === iconId),
-						y: riseDistance,
-						left: x + floatOffset,
+					const idx = this.icons.findIndex(item => item.id === id);
+					if (idx === -1) return;
+					this.$set(this.icons, idx, {
+						...this.icons[idx],
+						dx: floatOffset,
+						dy: riseDistance,
 						opacity: 0
 					});
-				}, delay);
+				}, delay + 20);
 
-				// 动画结束后移除
 				setTimeout(() => {
-					const index = this.icons.findIndex(item => item.id === iconId);
-					if (index !== -1) {
-						this.icons.splice(index, 1);
+					const idx = this.icons.findIndex(item => item.id === id);
+					if (idx !== -1) {
+						this.icons.splice(idx, 1);
 					}
-				}, duration + delay);
+				}, delay + duration + 40);
 			},
-
-			/**
-			 * 生成范围内的随机数
-			 */
 			getRandom(min, max) {
 				return min + Math.random() * (max - min);
 			}
@@ -181,44 +158,35 @@
 </script>
 
 <style scoped lang="scss">
-
-	.like-icon {
-		font-size: 28rpx;
-		color: #999;
-		transition: all 0.3s ease;
-	}
-
-	/* 动画容器 */
-	.like-container {
+	.like-wrap {
 		position: relative;
-		
-		.image {
-			width: 80rpx;
-			height: 80rpx;
-		}
-
-		/* 动画图标样式 */
-		.animated-icon {
-			position: absolute;
-			will-change: transform, opacity, left;
-			text-shadow: 0 1rpx 2rpx rgba(0, 0, 0, 0.2);
-			z-index: 10;
-			animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
-		}
+		flex-shrink: 0;
+		overflow: visible;
+	}
 
-		/* 点赞按钮动画 */
-		@keyframes pulse {
-			0% {
-				transform: scale(1);
-			}
+	.like-btn-img {
+		display: block;
+		flex-shrink: 0;
+	}
 
-			50% {
-				transform: scale(1.4);
-			}
+	.like-burst-host {
+		position: absolute;
+		left: 0;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		overflow: visible;
+		pointer-events: none;
+		z-index: 2;
+	}
 
-			100% {
-				transform: scale(1);
-			}
-		}
+	.burst-particle {
+		position: absolute;
+		left: 50%;
+		top: 50%;
+		line-height: 1;
+		white-space: nowrap;
+		pointer-events: none;
+		will-change: transform, opacity;
 	}
-</style>
+</style>

+ 25 - 3
pages_live/components/liveCommentInput.vue

@@ -43,7 +43,8 @@
 				<view
 					class="live-comment-input__send"
 					:class="{ 'live-comment-input__send--horizontal': showType === 1 }"
-					@click.stop="handleSend">
+					@touchstart.stop.prevent="handleSend"
+					@tap.stop="handleSend">
 					发送
 				</view>
 			</view>
@@ -92,6 +93,11 @@
 				default: true
 			}
 		},
+		data() {
+			return {
+				ignoreBlur: false
+			};
+		},
 		computed: {
 			theme() {
 				return this.showType === 1 ? 'light' : 'dark';
@@ -135,11 +141,27 @@
 				this.$emit('focus');
 			},
 			onBlur() {
+				if (this.ignoreBlur) return;
 				this.$emit('blur');
 			},
 			handleSend() {
 				if (this.disabled) return;
+				const text = (this.value || '').trim();
+				if (!text) {
+					uni.showToast({
+						title: '不能发送空消息',
+						icon: 'none'
+					});
+					return;
+				}
+				this.ignoreBlur = true;
 				this.$emit('send');
+				this.$nextTick(() => {
+					uni.hideKeyboard();
+					setTimeout(() => {
+						this.ignoreBlur = false;
+					}, 200);
+				});
 			},
 			clearInput() {
 				this.$emit('input', '');
@@ -277,8 +299,8 @@
 	}
 
 	.live-comment-input--dark .live-comment-input__send {
-		background-color: transparent;
-		color: #02b176;
+		background-color: #FF233C;
+		color: #ffffff;
 		padding: 14rpx 8rpx;
 	}
 </style>

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 454 - 291
pages_live/living.vue


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 380 - 493
pages_live/living333.vue


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 551 - 467
pages_live/shopping/confirmCreateOrder.vue


+ 287 - 108
pages_live/shopping/goods.vue

@@ -1,6 +1,6 @@
 <template>
 	<view class="">
-		<view class="content" v-cloak>
+		<view class="content">
 			<view class="shop-banner">
 				<swiper class="swiper" :indicator-dots="false" :circular="true" :autoplay="true" :interval="3000"
 					:duration="1000" indicator-color="rgba(255, 255, 255, 0.6)" indicator-active-color="#ffffff"
@@ -24,10 +24,13 @@
 						<text class="fs24 color-text2">零售价</text>
 						<text class="old">¥{{goosDetail.otPrice||0}}</text>
 					</view>
-					<text class="fs24 color-text2">月售{{goosDetail.sales||0}}件</text>
+					<!-- <text class="fs24 color-text2">月售{{goosDetail.sales||0}}件</text> -->
 				</view>
 				<view class="name-box">
 					{{goosDetail.productName||0}}
+					<view v-if="purchaseLimit > 0" class="limit-info">
+						限购{{purchaseLimit}}件<text v-if="remainingPurchaseLimit !== null && remainingPurchaseLimit >= 0">,已购{{purchaseLimit - remainingPurchaseLimit}}件</text>
+					</view> 
 				</view>
 
 			</view>
@@ -46,7 +49,7 @@
 			</view>
 		</view>
 		<!-- 购买人数、库存 -->
-		<view class="det-box" v-if="goosDetail">
+		<view class="det-box">
 			<view class="title">图文详情</view>
 			<view class="inner">
 				<view v-html="goosDetail.description" style="font-size:0"></view>
@@ -62,26 +65,30 @@
 		</view>
 
 		<!-- 选择产品规格弹窗 -->
-		<popupBottom ref="popup" :visible.sync="specVisible" title=" " radius="32" maxHeight="800">
-			<view class="product-spec" v-if="goosDetail">
+		<popupBottom ref="popup" :visible.sync="specVisible" title=" " radius="32" maxHeight="1024">
+			<view class="product-spec">
 				<view class="pro-info">
 					<view class="img-box">
-							<image
-								:src="productValueSelect ? productValueSelect.image : (goosDetail.image ||'https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/userapp/images/img.png')"
-								mode="aspectFill"></image>
-						</view>
+						<!-- 这里应该显示当前选择规格的图片 -->
+						<image
+							:src="(productValueSelect && productValueSelect.image) || goosDetail.image || 'https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com/userapp/images/img.png'"
+							mode="aspectFill"></image>
+					</view>
 					<view class="info-text">
-								<view class="info-title">{{goosDetail.productName}}</view>
-								<view class="price">
-									<view class="label">会员价</view>
-									<text class="unit">¥</text>
-									<text class="num">{{ productValueSelect ? productValueSelect.price : goosDetail.price}}</text>
-								</view>
-								<view class="desc-box">
-									<text class="text">月售{{goosDetail.sales}}件</text>
-									<text class="text">库存:{{productValueSelect ? productValueSelect.stock : goosDetail.stock}}件</text>
-								</view>
-							</view>
+						<!-- 商品名称不变 -->
+						<view class="info-title">{{goosDetail.productName}}</view>
+						<!-- 这里应该显示当前选择规格的价格 -->
+						<view class="price">
+							<view class="label">会员价</view>
+							<text class="unit">¥</text>
+							<text class="num">{{ productValueSelect ? productValueSelect.price : goosDetail.price}}</text>
+						</view>
+						<view class="desc-box">
+							<!-- 这里可以显示当前选择规格的库存 -->
+							<text class="text" v-if="productValueSelect">库存:{{productValueSelect.stock}}件</text>
+							<text class="text" v-else>月售{{goosDetail.sales}}件</text>
+						</view>
+					</view>
 				</view>
 
 
@@ -100,28 +107,50 @@
 
 
 
-				<view class="price-num">
+				<!-- <view class="price-num">
 					<view class="label">数量</view>
 					<u-number-box bgColor="#f3f3f3" v-model="totalNum" @change="goodsNumChange"></u-number-box>
+				</view> -->
+				<view class="price-num">
+                    <view class="label">数量</view>
+					<view class="num-box">
+						<view class="img-box" @click="lessNum()">
+							<image v-if="totalNum <= 1"
+								src="https://jnlzjk-1323137866.cos.ap-chongqing.myqcloud.com/shop/images/jian.png"
+								mode=""></image>
+							<image v-else
+								src="https://jnlzjk-1323137866.cos.ap-chongqing.myqcloud.com/shop/images/jian2.png"
+								mode=""></image>
+						</view>
+						<input type="number" @change="changeNum" v-model="totalNum" />
+						<view class="img-box" @click="addNum()">
+							<image src="https://jnlzjk-1323137866.cos.ap-chongqing.myqcloud.com/shop/images/add.png"
+								mode=""></image>
+						</view>
+					</view>
 				</view>
 				<view class="sub-btn" @click="submit">确定</view>
 			</view>
 		</popupBottom>
+   <CustomToast ref="customToast">
+   </CustomToast>
 	</view>
 </template>
 
 <script>
 	import {
 		liveGoodsDetail
-	} from '@/api/living'
+	} from '@/api/living';
 	import {
 		liveOrderKey, // 生成订单key
-	} from "@/api/liveOrder.js"
-	import popupBottom from '@/components/px-popup-bottom/px-popup-bottom.vue'
+	} from "@/api/liveOrder.js";
+   import popupBottom from '@/components/px-popup-bottom/px-popup-bottom.vue';
+  import CustomToast from '@/components/custom-toast.vue';
 
 	export default {
 		components: {
-			popupBottom
+			popupBottom,
+			CustomToast
 		},
 		data() {
 			return {
@@ -133,6 +162,8 @@
 				type: null,
 				liveId: null,
 				storeId: null,
+				selectVal: '', // 当前选择的规格值
+				productValueSelect: null, // 当前选择的规格对象
 				serviceList: ['品质保障', '隐私保护'],
 				productId: null,
 				goosDetail: null, //商品详情
@@ -142,14 +173,14 @@
 				activeBanner: 1,
 				// 规格弹窗
 				specVisible: false,
-				// 规格相关
-				selectVal: '',
-				values: [],
-				productValueSelect: null,
+				remainingPurchaseLimit:null,
+				purchaseLimit:0, // 总限购数量
+				isSubmitting: false, // 是否正在提交订单(防重复点击)
+				isGettingKey: false // 是否正在获取订单key(防重复调用接口)
 			};
 		},
 		onLoad(options) {
-			console.log("商品详情options", options)
+			// console.log("商品详情options", options)
 			if (options.productId) {
 				this.productId = options.productId;
 			}
@@ -167,36 +198,89 @@
 			}
 			this.getliveGoods()
 		},
+		onShow() {
+			// 页面显示时重置标志位,允许从确认订单页面返回后再次购买
+			this.isSubmitting = false;
+			this.isGettingKey = false;
+		},
 		methods: {
 			// swiper变化事件
 			swiperChange(event) {
 				this.activeBanner = event.detail.current + 1
 			},
 			doAddCart(type) {
+				// 防止重复提交:如果正在处理中,直接返回
+				if (this.isSubmitting) {
+					uni.showToast({
+						icon: 'none',
+						title: '订单正在处理中,请勿重复点击'
+					});
+					return;
+				}
+				
+				// 检查限购数量
+				if (this.remainingPurchaseLimit !== null && typeof this.remainingPurchaseLimit === 'number') {
+					// 如果限购数量为0,提示库存不足
+					if (this.remainingPurchaseLimit === 0) {
+			    this.$refs.customToast.show({
+			      title:`该商品限购:${this.purchaseLimit}件\n已达购买上限`,
+			      duration: 2000
+			    });
+						return;
+					}
+					// 如果购买数量超过限购数量,提示
+					if (this.totalNum > this.remainingPurchaseLimit) {
+			    this.$refs.customToast.show({
+			      title: "购买数量不能超过限购数量:" + this.remainingPurchaseLimit,
+			      duration: 2000
+			    });
+						return;
+					}
+				}
 				if (this.totalNum == 0) {
 					uni.showToast({
 						icon: 'none',
-						title: "库存不足",
+						title: "购买商品数量必须大于0",
 					});
 					return;
 				}
+				
 				var isBuy = type == "buy" ? 1 : 0;
 				if (type == "buy") {
-					this.getKey()
+					// 标记正在提交
+					this.isSubmitting = true;
+					this.getKey();
 				} else {
-					uni.showToast({
-						icon: 'success',
-						title: "添加成功",
-					});
+					// 标记正在提交
+					this.isSubmitting = true;
+					setTimeout(() => {
+						uni.showToast({
+							icon: 'success',
+							title: "添加成功",
+						});
+						// 延迟清除标志位
+						this.isSubmitting = false;
+					}, 300);
 				}
 			},
 
 			// 获得key
 			getKey() {
+				// 防止重复调用:如果正在获取key,直接返回
+				if (this.isGettingKey) {
+					console.log('正在获取订单key,请勿重复点击');
+					return;
+				}
+				
+				// 标记正在获取key
+				this.isGettingKey = true;
 				liveOrderKey().then(res => {
 						if (res.code == 200) {
 							// console.log("下订单的key>>>>", res)
 							this.orderKey = res.orderKey
+							// 跳转前清除标志位
+							this.isSubmitting = false;
+							this.isGettingKey = false;
 							uni.navigateTo({
 								url: '/pages_live/shopping/confirmCreateOrder?&orderKey=' + this.orderKey +
 									'&liveId=' + this.liveId + '&goodsId=' + this.goodsId +
@@ -204,26 +288,41 @@
 									"&productValueSelect=" + this.productValueSelect.id
 							})
 						} else {
+							// 错误时清除标志位
+							this.isSubmitting = false;
+							this.isGettingKey = false;
 							uni.showToast({
 								title: res.msg,
 								icon: 'none'
 							});
 						}
 					},
-					rej => {}
+					rej => {
+						// 请求失败时清除标志位
+						this.isSubmitting = false;
+						this.isGettingKey = false;
+						console.error('获取订单key失败:', rej);
+					}
 				);
 			},
-			// 选择商品数量
-			goodsNumChange(e) {
-				console.log('当前选择商品数量为: ' + e.value)
-				this.totalNum = e.value
-				if (this.totalNum < 1) {
-					this.totalNum = 1
-				}
-				if (this.totalNum >= this.productValueSelect.stock) {
-					this.totalNum = this.productValueSelect.stock
-				}
-			},
+			// // 选择商品数量
+			// goodsNumChange(e) {
+			// 	console.log('当前选择商品数量为: ' + e.value)
+			// 	this.totalNum = e.value
+			// 	if (this.totalNum < 1) {
+			// 		this.totalNum = 1
+			// 	}
+			// 	if (this.totalNum >= this.productValueSelect.stock) {
+			// 		this.totalNum = this.productValueSelect.stock
+			// 	}
+			// 	if(this.totalNum > this.remainingPurchaseLimit){
+			// 		this.totalNum = this.remainingPurchaseLimit
+			// 		uni.showToast({
+			// 			icon: 'none',
+			// 			title: "该商品限购:"+this.remainingPurchaseLimit+"次"+",已达购买上限",
+			// 		});
+			// 	}
+			// },
 			// 提交
 			submit() {
 				this.specVisible = false
@@ -231,28 +330,23 @@
 			},
 			// 加入购物车
 			addCart(type) {
-					if (type == 'buy') {
-						let userInfoStr = uni.getStorageSync('userData');
-						if(Object.prototype.toString.call(userInfoStr) == '[object String]'){
-							if (userInfoStr) {
-								userInfoStr = JSON.parse(userInfoStr)
-							} else {
-								userInfoStr = null;
-							}
-						}
-						// if (!userInfoStr  && !userInfoStr.maOpenId) {
-						// 	uni.navigateTo({
-						// 		url: '/pages/auth/login'
-						// 	});
-						// 	return;
-						// }
-					}
+				if (type == 'buy') {
+					// let userInfoStr = uni.getStorageSync('userInfo');
+					// if(Object.prototype.toString.call(userInfoStr) == '[object String]'){
+					// 	userInfoStr = JSON.parse(userInfoStr)
+					// }
+					// if (!userInfoStr || userInfoStr && !userInfoStr.maOpenId) {
+					// 	uni.navigateTo({
+					// 		url: '/pages/auth/login'
+					// 	});
+					// 	return;
+					// }
+				}
 				this.type = type;
 				this.specVisible = true
 			},
 			//商品详情
 			getliveGoods() {
-				console.log("dddd")
 				if (!this.productId) return;
 				uni.showLoading({
 					title: '加载中'
@@ -260,10 +354,13 @@
 				liveGoodsDetail(this.productId).then(res => {
 						uni.hideLoading()
 						if (res.code == 200) {
-							console.log(res)
 							this.goosDetail = res.product
 							this.banner = res.product.sliderImage.split(',');
-
+                            this.remainingPurchaseLimit=res.remainingPurchaseLimit
+							// 如果接口返回了总限购数量,也保存
+							if (res.product.purchaseLimit !== null) {
+								this.purchaseLimit = res.product.purchaseLimit;
+							}
 							this.attrs = res.productAttr;
 							this.attrs.forEach((item, index, arr) => {
 								item.values = item.attrValues.split(',');
@@ -302,6 +399,9 @@
 				}
 				let selectVal = values.sort().join(","); //返回值:默认
 				this.selectVal = selectVal;
+				// var valueSelect=this.values.filter((item)=>{
+				//      return item.sku==selectVal;
+				// });
 				var valueSelect = this.getValueSelect();
 				console.log("qxj valueSelect:" + valueSelect);
 				if (valueSelect != null && valueSelect.length > 0) {
@@ -318,6 +418,54 @@
 				} else {
 					this.totalNum = 1;
 				}
+			},
+			changeNum(e) {
+				this.totalNum = e.detail.value.replace(/\D/g, '')
+				if (this.totalNum < 1) {
+					this.totalNum = 1
+				}
+				if (this.totalNum >= this.productValueSelect.stock) {
+					this.totalNum = this.productValueSelect.stock
+				}
+				if(this.totalNum > this.remainingPurchaseLimit && this.remainingPurchaseLimit!==null){
+					this.totalNum = this.remainingPurchaseLimit;
+          this.$refs.customToast.show({
+            title: `该商品限购:${this.remainingPurchaseLimit}件\n已达购买上限`,
+            duration: 2000
+          });
+					// uni.showToast({
+					// 	icon: 'none',
+					// 	title: "该商品限购:"+this.remainingPurchaseLimit+"次"+",已达购买上限",
+					// });
+				}
+			},
+			// 数量减法
+			lessNum() {
+				this.totalNum--
+				if (this.totalNum < 1) {
+					this.totalNum = 1
+				}
+				if (this.totalNum >= this.productValueSelect.stock) {
+					this.totalNum = this.productValueSelect.stock
+				}
+			},
+			// 数量加法
+			addNum() {
+				this.totalNum++
+				if (this.totalNum >= this.productValueSelect.stock) {
+					this.totalNum = this.productValueSelect.stock
+				}
+				if(this.totalNum > this.remainingPurchaseLimit && this.remainingPurchaseLimit!==null){
+					this.totalNum = this.remainingPurchaseLimit;
+          this.$refs.customToast.show({
+            title: `该商品限购:${this.remainingPurchaseLimit}件\n已达购买上限`,
+            duration: 2000
+          });
+					// uni.showToast({
+					// 	icon: 'none',
+					// 	title: "该商品限购:"+this.remainingPurchaseLimit+"次"+",已达购买上限",
+					// });
+				}
 			}
 		}
 	}
@@ -497,6 +645,13 @@
 				float: left;
 				margin-top: 7rpx;
 			}
+			.limit-info {
+				font-size: 32rpx;
+				font-weight: 500;
+				color: #FF6633;
+				margin-top: 16rpx;
+				line-height: 1;
+			}
 		}
 	}
 
@@ -561,26 +716,24 @@
 				}
 
 				&.buy {
-					background: #FF5C03;
+					background: #FF233C;
 				}
 			}
 		}
 	}
 
 	.product-spec {
-		padding-bottom: 30rpx;
-
 		.pro-info {
 			display: flex;
 			align-items: center;
 
 			.img-box {
-				width: 200rpx;
-				height: 200rpx;
+				width: 200upx;
+				height: 200upx;
 				background: #FFFFFF;
-				border-radius: 16rpx;
+				border-radius: 16upx;
 				overflow: hidden;
-				margin-right: 30rpx;
+				margin-right: 30upx;
 
 				image {
 					width: 100%;
@@ -589,41 +742,27 @@
 			}
 
 			.info-text {
-				height: 200rpx;
+				height: 200upx;
 				display: flex;
 				flex-direction: column;
 				justify-content: space-between;
 
-				.info-title {
-					font-family: PingFang SC;
-					font-weight: 600;
-					font-size: 28rpx;
-					color: #222426;
-					text-align: left;
-				}
-
 				.price {
 					display: flex;
 					align-items: flex-end;
 
-					.label {
-						font-weight: 500;
-						font-size: 24rpx;
-						color: #FF5030;
-						line-height: 1.3;
-						margin-right: 10rpx;
-					}
-
 					.unit {
-						font-size: 32rpx;
+						font-size: 32upx;
+						font-family: PingFang SC;
 						font-weight: bold;
 						color: #FF6633;
 						line-height: 1.2;
-						margin-right: 10rpx;
+						margin-right: 10upx;
 					}
 
 					.num {
-						font-size: 50rpx;
+						font-size: 50upx;
+						font-family: PingFang SC;
 						font-weight: bold;
 						color: #FF6633;
 						line-height: 1;
@@ -633,13 +772,14 @@
 				.desc-box {
 					display: flex;
 					flex-direction: column;
-					padding-bottom: 9rpx;
+					padding-bottom: 9upx;
 
 					.text {
-						font-size: 26rpx;
+						font-size: 26upx;
+						font-family: PingFang SC;
 						font-weight: 500;
 						color: #999999;
-						margin-top: 27rpx;
+						margin-top: 27upx;
 						line-height: 1;
 
 						&:first-child {
@@ -682,9 +822,9 @@
 					margin-bottom: 30upx;
 
 					&.active {
-						background: #F1FFFE;
-						border: 1px solid #8AD5CE;
-						color: #FF5C03;
+						background: #fff;
+						border: 1px solid #FF233C;
+						color: #FF233C;
 					}
 				}
 			}
@@ -694,26 +834,65 @@
 			display: flex;
 			align-items: center;
 			justify-content: space-between;
-			margin-top: 30rpx;
+			margin-top: 14upx;
 
 			.label {
-				font-size: 36rpx;
+				font-size: 34upx;
+				font-family: PingFang SC;
 				font-weight: bold;
 				color: #111111;
 			}
+
+			.num-box {
+				display: flex;
+				align-items: center;
+
+				.img-box {
+					width: 60upx;
+					height: 60upx;
+					// border-radius: 4upx;
+					border: 1px solid #dddddd;
+					display: flex;
+					align-items: center;
+					justify-content: center;
+
+					image {
+						width: 25rpx;
+						height: 25rpx;
+					}
+				}
+
+				input {
+					width: 60upx;
+					height: 60upx;
+					line-height: 60upx;
+					font-size: 28upx;
+					font-family: PingFang SC;
+					font-weight: 500;
+					color: #111111;
+					// border-radius: 4upx;
+					border-top: 1px solid #dddddd;
+					border-bottom: 1px solid #dddddd;
+					text-align: center;
+					// margin: 0 16upx;
+				}
+			}
 		}
 
 		.sub-btn {
 			width: 100%;
-			height: 88rpx;
-			line-height: 88rpx;
+			height: 88upx;
+			line-height: 88upx;
 			text-align: center;
-			font-size: 32rpx;
+			font-size: 30upx;
+			font-family: PingFang SC;
 			font-weight: bold;
 			color: #FFFFFF;
-			background: #FF5C03;
-			border-radius: 44rpx;
-			margin-top: 30rpx;
+			background: #FF233C;
+			border-radius: 44upx;
+			margin-top: 30upx;
+			// margin-bottom: 30upx;
+
 		}
 	}
 </style>

+ 1 - 1
pages_mall/components/RecommendSection.vue

@@ -120,7 +120,7 @@ export default {
 		// 	title: '暂无课程',
 		// 	icon: 'none'
 		// })
-		uni.navigateTo({ url: '/pages_live/living?liveId=1884' })
+	   uni.navigateTo({ url: '/pages_live/livingList' })
 		// const courseData = {
 		//   companyId: 6,
 		//   companyUserId: 80,

+ 163 - 0
pages_mall/index - 副本.vue

@@ -0,0 +1,163 @@
+<template>
+	<view class="mall-index">
+		<!-- 内容区域 -->
+		<view class="content-area">
+			<index-page v-if="currentTab === 0" ref="indexPage" @updateMallIndexCartCount="getMallIndexCartCount"></index-page>
+			<!-- <category-page v-if="currentTab === 1" ref="categoryPage"></category-page> -->
+			<cart-page v-if="currentTab === 1" ref="cartPage"></cart-page>
+			<my-page v-if="currentTab === 2" ref="myPage"></my-page>
+		</view>
+
+		<!-- 底部导航栏 -->
+		<view class="tab-bar">
+			<view class="tab-item" :class="{active: currentTab === 0}" @click="switchTab(0)">
+				<u-icon :name="currentTab === 0 ? 'home-fill' : 'home'" size="28"
+					:color="currentTab === 0 ? '#FF233C' : '#999'"></u-icon>
+				<text>首页</text>
+			</view>
+			<!-- <view class="tab-item" :class="{active: currentTab === 1}" @click="switchTab(1)">
+				<u-icon :name="currentTab === 1 ? 'grid-fill' : 'grid'" size="26"
+					:color="currentTab === 1 ? '#FF233C' : '#999'"></u-icon>
+				<text>分类</text>
+			</view> -->
+			<view v-if="false" class="tab-item" :class="{active: currentTab === 1}" @click="switchTab(1)">
+				<view class="icon-wrap">
+					<u-icon :name="currentTab === 1 ? 'shopping-cart-fill' : 'shopping-cart'" size="28"
+						:color="currentTab === 1 ? '#FF233C' : '#999'"></u-icon>
+					<view class="badge">{{ cartMallCount }}</view>
+				</view>
+				<text>购物车</text>
+			</view>
+			<view class="tab-item" :class="{active: currentTab === 2}" @click="switchTab(2)">
+				<u-icon :name="currentTab === 2 ? 'account-fill' : 'account'" size="28"
+					:color="currentTab === 2 ? '#FF233C' : '#999'"></u-icon>
+				<text>我的</text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import CategoryPage from './category.vue'
+	import CartPage from './cart.vue'
+	import MyPage from './my.vue'
+	import IndexPage from './homeIndex.vue'
+	export default {
+		components: {
+			CategoryPage,
+			CartPage,
+			MyPage,
+			IndexPage
+		},
+		data() {
+			return {
+				currentTab: 0,
+				isFirstLoad: true, // 标记是否第一次进入页面
+				cartMallCount: 0
+			}
+		},
+		onLoad() {
+			this.isFirstLoad = true
+			this.currentTab = 0
+		},
+		onShow() {
+			// 关键判断:不是第一次加载 = 从返回进入
+			if (!this.isFirstLoad) {
+				const tabCart = uni.getStorageSync('tabCart')
+				if (tabCart) {
+					this.currentTab = 2
+					uni.removeStorageSync('tabCart')
+				}
+			}
+			// 第一次显示后,标记为非首次
+			this.isFirstLoad = false
+			uni.hideTabBar();
+		},
+		onUnload() {
+			uni.showTabBar();
+		},
+		onBackPress() {
+			uni.showTabBar();
+			uni.switchTab({
+				url: '/pages_im/pages/conversation/conversationList/index'
+				//url: '/pages/index/index'
+			})
+		},
+		methods: {
+			switchTab(index) {
+				this.currentTab = index;
+			},
+			getMallIndexCartCount(count) {
+				this.cartMallCount = count
+			}
+		}
+	}
+</script>
+
+<style scoped lang="scss">
+	.mall-index {
+		display: flex;
+		flex-direction: column;
+		height: 100vh;
+		background-color: #f8f8f8;
+	}
+
+	.content-area {
+		flex: 1;
+		height: 0; // Allow flex item to scroll internally
+		overflow: hidden;
+		position: relative;
+	}
+
+	.tab-bar {
+		height: 100rpx;
+		background-color: #fff;
+		display: flex;
+		align-items: center;
+		justify-content: space-around;
+		border-top: 1rpx solid #eee;
+		padding-bottom: constant(safe-area-inset-bottom);
+		padding-bottom: env(safe-area-inset-bottom);
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		right: 0;
+		z-index: 9999;
+
+		.tab-item {
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+
+			text {
+				font-size: 24rpx;
+				margin-top: 6rpx;
+				color: #999999;
+			}
+
+			&.active text {
+				color: #FF233C;
+			}
+
+			.icon-wrap {
+				position: relative;
+
+				.badge {
+					position: absolute;
+					top: -10rpx;
+					right: -10rpx;
+					background: #fa436a;
+					color: #fff;
+					font-size: 18rpx;
+					width: 30rpx;
+					height: 30rpx;
+					border-radius: 50%;
+					display: flex;
+					align-items: center;
+					justify-content: center;
+				}
+			}
+		}
+	}
+</style>

+ 514 - 96
pages_mall/index.vue

@@ -1,114 +1,404 @@
 <template>
-	<view class="mall-index">
-		<!-- 内容区域 -->
-		<view class="content-area">
-			<index-page v-if="currentTab === 0" ref="indexPage" @updateMallIndexCartCount="getMallIndexCartCount"></index-page>
-			<!-- <category-page v-if="currentTab === 1" ref="categoryPage"></category-page> -->
-			<cart-page v-if="currentTab === 1" ref="cartPage"></cart-page>
-			<my-page v-if="currentTab === 2" ref="myPage"></my-page>
+	<view class="content">
+		<image class="bg" src="@/static/images/bg.png" mode="aspectFill"></image>
+
+		<view class="header-wrap">
+			<SearchBar
+				:margin-top="'24rpx'"
+				:bg-color="bgColor"
+				:status-bar-height="statusBarHeight"
+				:cart-count="cartCount"
+				:search-width="'100%'"
+				placeholder="搜索商品"
+				@searchClick="toSearch"
+				@cartClick="goAuthUrl()"
+			/>
 		</view>
 
-		<!-- 底部导航栏 -->
-		<view class="tab-bar">
-			<view class="tab-item" :class="{active: currentTab === 0}" @click="switchTab(0)">
-				<u-icon :name="currentTab === 0 ? 'home-fill' : 'home'" size="28"
-					:color="currentTab === 0 ? '#FF233C' : '#999'"></u-icon>
-				<text>首页</text>
+		<scroll-view scroll-y class="scroll-content" @scroll="onScroll" @scrolltolower="loadMoreGoods">
+			<!-- 顶部占位:避免固定搜索栏遮挡 -->
+			<view style="padding-bottom:110rpx">
+				<view class="status_bar" :style="{height: statusBarHeight}"></view>
 			</view>
-			<!-- <view class="tab-item" :class="{active: currentTab === 1}" @click="switchTab(1)">
-				<u-icon :name="currentTab === 1 ? 'grid-fill' : 'grid'" size="26"
-					:color="currentTab === 1 ? '#FF233C' : '#999'"></u-icon>
-				<text>分类</text>
-			</view> -->
-			<view v-if="false" class="tab-item" :class="{active: currentTab === 1}" @click="switchTab(1)">
-				<view class="icon-wrap">
-					<u-icon :name="currentTab === 1 ? 'shopping-cart-fill' : 'shopping-cart'" size="28"
-						:color="currentTab === 1 ? '#FF233C' : '#999'"></u-icon>
-					<view class="badge">{{ cartMallCount }}</view>
-				</view>
-				<text>购物车</text>
+
+			<!-- 推荐频道 -->
+			<ChannelEntry :list="channelList" :per-row="4" :rows="2" @click="onChannelClick" />
+
+			<!-- 金刚区:分类图标 2 行 4 列,仅展示 -->
+			<CategoryTags :tags="categoryTagsData" @selectClick="onCategoryTagsSelect" />
+
+			<!-- 推荐区块:左侧直播卡(直播中/回放)+ 右上绿色有机 + 右下上新推荐 -->
+			<RecommendSection
+				:live="recommendData.live"
+				:green="recommendData.green"
+				:hot="recommendData.hot"
+				@liveClick="onLiveClick"
+				@more="onRecommendMore"
+				@itemClick="onRecommendItemClick"
+			/>
+
+			<!-- 瀑布流:全部 + 分类标签横向滚动 -->
+			<GoodsNav :nav-list="goodsNavList" :active-id="activeGoodsNavId" @select="onGoodsNavSelect" />
+
+			<GoodsList
+				:list="goodsList"
+				:loading="goodsLoading"
+				:has-more="goodsHasMore"
+				@itemClick="showProduct"
+				@loadMore="loadMoreGoods"
+			/>
+
+			<!--#ifdef MP-WEIXIN-->
+			<view class="official-account">
+				<official-account @load="bindload" @error="binderror"></official-account>
 			</view>
-			<view class="tab-item" :class="{active: currentTab === 2}" @click="switchTab(2)">
-				<u-icon :name="currentTab === 2 ? 'account-fill' : 'account'" size="28"
-					:color="currentTab === 2 ? '#FF233C' : '#999'"></u-icon>
-				<text>我的</text>
+			<!--#endif-->
+
+			<view class="popup-box" v-if="activityShow">
+				<view class="info-mask" @tap="closeActivity()"></view>
+				<view class="info-form">
+					<image :src="activity.logoUrl" @tap="showActivity()" />
+				</view>
 			</view>
-		</view>
+
+			<!-- <Server /> -->
+		</scroll-view>
 	</view>
 </template>
 
 <script>
-	import CategoryPage from './category.vue'
-	import CartPage from './cart.vue'
-	import MyPage from './my.vue'
-	import IndexPage from './homeIndex.vue'
-	export default {
-		components: {
-			CategoryPage,
-			CartPage,
-			MyPage,
-			IndexPage
-		},
-		data() {
-			return {
-				currentTab: 0,
-				isFirstLoad: true, // 标记是否第一次进入页面
-				cartMallCount: 0
-			}
-		},
-		onLoad() {
-			this.isFirstLoad = true
-			this.currentTab = 0
-		},
-		onShow() {
-			// 关键判断:不是第一次加载 = 从返回进入
-			if (!this.isFirstLoad) {
-				const tabCart = uni.getStorageSync('tabCart')
-				if (tabCart) {
-					this.currentTab = 2
-					uni.removeStorageSync('tabCart')
+// import zModal from '@/components/z-modal/z-modal.vue'
+import { getMenu, getIndexData, getCartCount } from '@/api/index'
+import { getStoreConfig } from '@/api/common'
+import { getHomeInit, getHomeRecommend, getHomeGoods } from '@/api/home.js'
+import HotProduct from './components/HotProduct.vue'
+//import TuiProduct from '@/components/tuiProduct.vue'
+import SearchBar from './components/SearchBar.vue'
+import CategoryTags from './components/CategoryTags.vue'
+import ChannelEntry from './components/ChannelEntry.vue'
+import RecommendSection from './components/RecommendSection.vue'
+import GoodsNav from './components/GoodsNav.vue'
+import GoodsList from './components/GoodsList.vue'
+import { getUserInfo, bindPromoter } from '@/api/user'
+
+export default {
+	components: {
+		// zModal,
+		HotProduct,
+		//TuiProduct,
+		SearchBar,
+		CategoryTags,
+		ChannelEntry,
+		RecommendSection,
+		GoodsNav,
+		GoodsList
+	},
+	data() {
+		return {
+			btnGroup: [
+				{ text: '取消', color: '#FFFFFF', bgColor: '#999999', width: '150rpx', height: '80rpx', shape: 'fillet', eventName: 'cancle' },
+				{ text: '确定', color: '#FFFFFF', bgColor: '#FF233C', width: '150rpx', height: '80rpx', shape: 'fillet', eventName: 'sure' }
+			],
+			menuButtonInfo: {
+				top: '0px',
+				height: '0px',
+				centerY: '0px',
+				right: '0px'
+			}, // 胶囊按钮布局信息
+			tuiModalControl: false,
+			activity: null,
+			activityShow: false,
+			hotProductList: [],
+			menus: [],
+			channelList: [],
+			categoryTagsData: [],
+			goodsNav: [],
+			recommendData: { live: [], green: [], hot: [] },
+			activeGoodsNavId: 'all',
+			goodsList: [],
+			goodsLoading: false,
+			goodsHasMore: true,
+			goodsPageNum: 1,
+			cartCount: 0,
+			advList: [],
+			top: 0,
+			statusBarHeight: uni.getSystemInfoSync().statusBarHeight + 'px',
+			userinfoa: [],
+			isuser: false,
+			currentTab: 0
+		}
+	},
+	computed: {
+		bgColor() {
+			const t = this.top / 30
+			if(t>0){
+				return '/static/images/bg.png'
+			}else{
+				return ''
+			}
+		},
+		// 瀑布流标签:后端 categoryTags(不包含“全部”)
+		goodsNavList() {
+			const tags = (this.goodsNav || []).map(t => ({
+				id: t.id || t.cateId,
+				name: t.categoryName || t.cateName || t.name,
+				categoryName: t.categoryName || t.cateName || t.name
+			}))
+			return tags
+		}
+	},
+	onLoad(option) {
+		this.getMenuButtonInfo()
+		if (option.userCode != null) {
+			uni.setStorageSync('userCode', option.userCode)
+			if (this.$isLogin()) this.getUserInfo()
+		}
+		if (option.hasOwnProperty('q') && option.q) {
+			const url = decodeURIComponent(option.q)
+			const obj = this.$urlToObj(url)
+			uni.setStorageSync('userCode', obj.userCode)
+			if (this.$isLogin()) this.getUserInfo()
+		}
+	},
+	onUnload() {
+		uni.$emit('stop')
+	},
+	onHide() {
+		uni.$emit('stop')
+	},
+	onShow() {
+		this.initIndexData()
+		if (this.$isLogin()) this.getCartCount()
+	},
+	onShareAppMessage() {
+		return { title: '岚财优选-您的专属解决方案', path: '/pages/common/launch', imageUrl: '/static/logo.jpg' }
+	},
+	onShareTimeline() {
+		return { title: '岚财优选-您的专属解决方案', query: '', imageUrl: '/static/logo.jpg' }
+	},
+	methods: {
+		onScroll(e) {
+			// scroll-view 的滚动事件
+			this.top = e?.detail?.scrollTop ?? e?.scrollTop ?? 0
+		},
+		switchTab(index) {
+			this.currentTab = index;
+		},
+		initIndexData() {
+			this.getHomeInit()
+			this.getHomeRecommend()
+			this.getMenu()
+			if (uni.getStorageSync('AppToken')) {
+				this.getUserInfo()
+			} else {
+				this.isuser = true
+			}
+			this.getStoreConfig()
+		},
+		// 获取胶囊按钮布局参数
+		getMenuButtonInfo() {
+			// 微信小程序API(Uniapp可直接用uni.getMenuButtonBoundingClientRect)
+			const menuBtn = uni.getMenuButtonBoundingClientRect();
+			if (menuBtn) {
+		
+				this.menuButtonInfo = {
+					top: menuBtn.top + 'px', // 胶囊顶部距离
+					height: menuBtn.height + 'px', // 胶囊高度
+					centerY: (menuBtn.top + menuBtn.height / 2) + 'px', // 胶囊垂直居中Y坐标
+					right: menuBtn.right + 'px' // 胶囊右侧距离
+				};
+			}
+		},
+		getHomeInit() {
+			getHomeInit().then(res => {
+				if (res.code == 200 && res.data) {
+					this.channelList = res.data.channelList || []
+					this.categoryTagsData = res.data.categoryTags || []
+					this.goodsNav = res.data.goodsNav || []
+					if (this.activeGoodsNavId === null || this.activeGoodsNavId === undefined) {
+						this.activeGoodsNavId = 'all'
+					}
+					this.fetchGoodsList(true)
+				}
+			}).catch(() => {})
+		},
+		getHomeRecommend() {
+			getHomeRecommend().then(res => {
+				if (res.code != 200) return
+				this.recommendData = {
+					live: res.live && Array.isArray(res.live) ? res.live.slice(0, 3) : [],
+					green: res.green && Array.isArray(res.green) ? res.green : [],
+					hot: res.hot && Array.isArray(res.hot) ? res.hot : []
+				}
+			}).catch(() => {})
+		},
+		fetchGoodsList(reset) {
+			if (reset) {
+				this.goodsPageNum = 1
+				this.goodsList = []
+				this.goodsHasMore = true
+			}
+			if (this.goodsLoading || !this.goodsHasMore) return
+			this.goodsLoading = true
+			const id = this.activeGoodsNavId === 'all' || this.activeGoodsNavId === '' ? 0 : this.activeGoodsNavId
+			getHomeGoods({ id, position:2, pageNum: this.goodsPageNum, pageSize: 10 }).then(res => {
+				this.goodsLoading = false
+				if (res.code == 200 && res.data) {
+					const list = Array.isArray(res.data.list) ? res.data.list : []
+					const total = res.data.total != null ? Number(res.data.total) : 0
+					if (reset) this.goodsList = list
+					else this.goodsList = this.goodsList.concat(list)
+					this.goodsHasMore = list.length >= 20 && (this.goodsList.length < total || total === 0)
+					this.goodsPageNum++
+				}
+			}).catch(() => { this.goodsLoading = false })
+		},
+		loadMoreGoods() {
+			this.fetchGoodsList(false)
+		},
+		onGoodsNavSelect(item) {
+      //console.log("2222")
+      //console.log(item.id)
+			this.activeGoodsNavId = item.id
+			this.fetchGoodsList(true)
+		},
+		onCategoryTagsSelect(item) {
+			//console.log(this.categoryTagsData,'item')
+			// 金刚区点击:带分类 id 跳转商品列表页筛选(防护 item 为空)
+			if (!item || typeof item !== 'object') {
+				uni.navigateTo({ url: '/pages_mall/productList' })
+				return
+			}
+			const id = item.id ?? item.cateId ?? item.categoryId
+			if (id != null && id !== '') {
+				uni.navigateTo({ url: '/pages_mall/productList?id=' + id })
+			} else {
+				uni.navigateTo({ url: '/pages_mall/productList' })
+			}
+		},
+		onChannelClick(item) {
+			// 推荐频道点击,可跳转或筛选,按需扩展
+		},
+		onRecommendMore(section) {
+			if (section && section.moreUrl) {
+				uni.navigateTo({ url: '/pages_mall/recommendList' })
+				return
+			}
+			if (section && (section.type === 'green' || section.type === 'hot')) {
+				uni.navigateTo({ url: '/pages_mall/recommendList?type='+section.type})
+			}
+		},
+		onLiveClick(item) {
+			//console.log(item)
+			if (item && item.liveId) uni.navigateTo({ url: '/pages_course/living?liveId=' + item.liveId })
+		},
+		onRecommendItemClick(item,type) {
+			if (item.productId) this.showProduct(item,type)
+		},
+		getMenu() {
+			// getMenu().then(res => {
+			// 	if (res.code == 200) this.menus = res.data || []
+			// })
+		},
+		getIndexData() {
+			getIndexData({}).then(res => {
+				if (res.code == 200) {
+					this.advList = res.data.advList || []
+					this.hotProductList = res.data.hotProductList || []
+				} else {
+					uni.showToast({ icon: 'none', title: '请求失败' })
 				}
+			})
+		},
+		getUserInfo() {
+			getUserInfo().then(res => {
+				if (res.code == 200 && res.user != null) this.userinfoa = res.user
+				else uni.showToast({ icon: 'none', title: '请求失败' })
+			})
+		},
+		getCartCount() {
+			if (this.$isLogin()){
+				getCartCount().then(cartRes => {
+					if (cartRes.code == 200) this.cartCount = cartRes.data
+				})
 			}
-			// 第一次显示后,标记为非首次
-			this.isFirstLoad = false
-			uni.hideTabBar();
 		},
-		onUnload() {
-			uni.showTabBar();
+		getStoreConfig() {
+			getStoreConfig().then(res => {
+				if (res.code == 200) uni.setStorageSync('config', JSON.stringify(res.data))
+			})
+		},
+		cancleTui() {
+			this.tuiModalControl = false
 		},
-		onBackPress() {
-			uni.showTabBar();
-			uni.switchTab({
-				url: '/pages_im/pages/conversation/conversationList/index'
-				//url: '/pages/index/index'
+		submitTui(e) {
+			if (!e.inputText) {
+				uni.showToast({ icon: 'none', title: '请输入邀请码' })
+				return
+			}
+			bindPromoter({ userCode: e.inputText }).then(res => {
+				uni.showToast({ icon: 'none', title: res.msg })
+				if (res.code == 200) this.tuiModalControl = false
 			})
 		},
-		methods: {
-			switchTab(index) {
-				this.currentTab = index;
-			},
-			getMallIndexCartCount(count) {
-				this.cartMallCount = count
+		bindload() {},
+		binderror() {},
+		closeActivity() {
+			this.activityShow = false
+		},
+		showActivity() {
+			this.activityShow = false
+			if (this.activity && this.activity.activityId) {
+				uni.navigateTo({ url: '/pages_shopping/shopping/activityDetails?activityId=' + this.activity.activityId })
+			}
+		},
+		menuClick(item) {
+			const linkUrl = item.linkUrl || item.url
+			const linkType = item.linkType != null ? item.linkType : (linkUrl ? 1 : 0)
+			if (linkType == 1 && linkUrl) {
+				if (linkUrl == '/pages/shopping/index' || linkUrl == '/pages/healthy/index') {
+					uni.switchTab({ url: linkUrl })
+				} else {
+					uni.navigateTo({ url: linkUrl })
+				}
+			} else if (linkType == 0) {
+				uni.showToast({ icon: 'none', title: '开发中...' })
+			}
+		},
+		handleAdvClick(item) {
+			if (item.showType == 1) {
+				uni.setStorageSync('url', item.advUrl)
+				uni.navigateTo({ url: 'h5' })
+			} else if (item.showType == 2) {
+				uni.navigateTo({ url: item.advUrl })
+			} else if (item.showType == 3) {
+				uni.setStorageSync('content', item.content)
+				uni.navigateTo({ url: 'content' })
+			}
+		},
+		goAuthUrl(url) {
+			if (this.$isLogin()) {
+				uni.switchTab({ url })
 			}
+		},
+		navTo(url) {
+			uni.navigateTo({ url })
+		},
+		toSearch() {
+			
+			//uni.navigateTo({ url: './productSearch' })
+			uni.navigateTo({ url: '/pages_mall/productList' })
+		},
+		showProduct(item,type) {
+			//console.log('item',type)
+			//uni.navigateTo({ url: '../shopping/productDetails?productId=' + item.productId })
+			uni.navigateTo({ url: '/pages_mall/recommendList?type='+type})
 		}
 	}
+}
 </script>
 
-<style scoped lang="scss">
-	.mall-index {
-		display: flex;
-		flex-direction: column;
-		height: 100vh;
-		background-color: #f8f8f8;
-	}
-
-	.content-area {
-		flex: 1;
-		height: 0; // Allow flex item to scroll internally
-		overflow: hidden;
-		position: relative;
-	}
-
+<style lang="scss" scoped>
 	.tab-bar {
 		height: 100rpx;
 		background-color: #fff;
@@ -123,26 +413,26 @@
 		left: 0;
 		right: 0;
 		z-index: 9999;
-
+	
 		.tab-item {
 			display: flex;
 			flex-direction: column;
 			align-items: center;
 			justify-content: center;
-
+	
 			text {
 				font-size: 24rpx;
 				margin-top: 6rpx;
 				color: #999999;
 			}
-
+	
 			&.active text {
 				color: #FF233C;
 			}
-
+	
 			.icon-wrap {
 				position: relative;
-
+	
 				.badge {
 					position: absolute;
 					top: -10rpx;
@@ -160,4 +450,132 @@
 			}
 		}
 	}
-</style>
+.content {
+	width: 100%;
+	position: relative;
+	min-height: 0;
+	padding-top: 1rpx;
+	display: flex;
+	flex-direction: column;
+	height: 100%;
+}
+.scroll-content {
+	flex: 1;
+	height: 0;
+}
+.header-wrap {
+	position: relative;
+	z-index: 10;
+}
+.banner-row {
+	position: relative;
+	display: flex;
+	gap: 20rpx;
+	padding: 20rpx 24rpx;
+	// background: #fff;
+}
+.banner-item {
+	flex: 1;
+	height: 220rpx;
+	border-radius: 16rpx;
+	overflow: hidden;
+	background: #f5f5f5;
+}
+.banner-img {
+	width: 100%;
+	height: 100%;
+}
+.bg {
+	width: 100%;
+	height: 380rpx;
+	position: absolute;
+	top: 0;
+	left: 0;
+	z-index: 0;
+}
+.status_bar {
+	width: 100%;
+}
+.banner-box {
+	padding: 0 20upx;
+}
+.banner-box .inner {
+	width: 100%;
+	height: 236upx;
+	border-radius: 10upx;
+	overflow: hidden;
+}
+.banner-box .swiper,
+.banner-box .swiper-item,
+.banner-box .swiper-item image {
+	width: 100%;
+	height: 100%;
+}
+.menu-content {
+	background-color: #fff;
+	overflow: hidden;
+	padding: 20upx 20upx 0;
+}
+.menu-box {
+	display: flex;
+	align-items: center;
+	background-color: #fff;
+}
+.online-inquiry {
+	box-sizing: border-box;
+	width: 100%;
+	height: 300upx;
+	padding: 20upx;
+	background: linear-gradient(180deg, rgba(255, 255, 255, 0.38) 62%, rgba(255, 255, 255, 0) 100%);
+}
+.online-inquiry .item {
+	width: 100%;
+	height: 100%;
+	position: relative;
+}
+.online-inquiry .bg-img {
+	width: 100%;
+	height: 100%;
+	position: absolute;
+	top: 0;
+	left: 0;
+	border-radius: 15rpx;
+}
+.index-cont {
+	box-sizing: border-box;
+	padding: 0 20upx 120rpx;
+}
+.official-account {
+	width: 100%;
+}
+.popup-box {
+	position: fixed;
+	top: 0;
+	right: 0;
+	left: 0;
+	bottom: 0;
+	z-index: 999;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+}
+.popup-box .info-mask {
+	position: fixed;
+	top: 0;
+	right: 0;
+	bottom: 0;
+	left: 0;
+	background-color: rgba(0, 0, 0, 0.5);
+	z-index: 999;
+}
+.popup-box .info-form {
+	z-index: 1000;
+	width: 450rpx;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+}
+.popup-box .info-form image {
+	width: 100%;
+}
+</style>

+ 39 - 16
pages_user/user/refundOrderDetail.vue

@@ -1,11 +1,16 @@
 <template>
 	<view>
+		<view class="fixed-top-box">
+			<view class="status_bar" :style="{height: statusBarHeight}"></view>
+			<view class="back-box" @click="back">
+				<image src="../../static/images/live/back_white.png" mode=""></image>
+				<text class="title">售后详情</text>
+				<text></text>
+			</view>
+		</view>
 		<view class="top-cont">
 			<!-- 背景图片 -->
-			<view class="bgViewClass"></view>
-			<view class="top-inner">			
-				<u-navbar bgColor="transparent"  :titleStyle="{color:'#FFFFFF'}" leftIconColor="#FFFFFF" title="售后详情" :autoBack="true"> </u-navbar>
-				
+			<view class="top-inner">		
 				<!-- 顶部固定后站位元素 -->
 				<view style="padding-bottom: 88upx;">
 					<view :style="{height: statusBarHeight}"></view>
@@ -266,26 +271,44 @@
 <style lang="scss">
 	.fixed-top-box{
 		width: 100%;
-		background: linear-gradient(135deg, #66b2ef 0%, #FF233C 100%);
+		//background: linear-gradient(135deg, #66b2ef 0%, #FF233C 100%);
 		position: fixed;
-		top: 0;
+		top: 52rpx;
 		left: 0;
 		z-index: 1000;
-	}
-	.top-cont{
-		width: 100%;
-		height: 500upx;
-		position: relative;
-		.bgViewClass{
+		overflow: hidden;
+		.bg{
 			width: 100%;
-			height: 100%;
+			height: auto;
 			position: absolute;
 			top: 0;
 			left: 0;
-			z-index: 1;
-			background: linear-gradient(to bottom, #FF233C, #FFF5F5);;
-			border-radius: 0 0 40rpx 40rpx;
+			z-index: -1;
 		}
+		.back-box{
+			height: 88upx;
+			padding-left: 22upx;
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			padding: 0 20upx;
+			image{
+				width: 40upx;
+				height: 40upx;
+			}
+			.title{
+				font-size: 36upx;
+				font-family: PingFang SC;
+				font-weight: 500;
+				color: #FFFFFF;
+			}
+		}
+	}
+	.top-cont{
+		width: 100%;
+		height: 476upx;
+		position: relative;
+		background: linear-gradient(to bottom, #FF233C, #FFF5F5);
 		.top-inner{
 			width: 100%;
 			height: 100%;

+ 11 - 4
pages_user/user/refundOrderList.vue

@@ -188,9 +188,16 @@
 			},
 			// 查看订单详情
 			showDetail(item) {
-				uni.navigateTo({
-					url: './refundOrderDetail?salesId=' + item.id
-				})
+				if(item.afterSalesType==2){
+					uni.navigateTo({
+						url: '/pages_live/shopping/refundOrderDetail?id=' + item.id
+					})
+				}else{
+					uni.navigateTo({
+						url: './refundOrderDetail?salesId=' + item.id
+					})
+				}
+				
 			},
 
 		}
@@ -349,7 +356,7 @@
 								display: inline-block;
 								padding: 0 6upx;
 								height: 30upx;
-								background: linear-gradient(90deg, #FF233C 0%, #2BC7A4 100%);
+								background: linear-gradient(90deg, #FF233C 0%, #FF233C 100%);
 								border-radius: 4upx;
 								margin-right: 10upx;
 								font-size: 22upx;

+ 273 - 48
pages_user/user/storeOrder.vue

@@ -92,11 +92,19 @@
 									<view class="num" >{{item.payPrice.toFixed(2)}}</view>
 								</view>
 							</view>
-							<view class="btn-box">
+							<!-- <view class="btn-box">
 								<view v-if="item.status == 0" class="btn cancel" @click="cancel(item)">取消订单</view>
 								<view v-if="item.status == 0" class="btn pay" @click="pay(item)">支付</view>
 								<view v-if="item.isAfterSales==1" class="btn cancel" @click="refund(item)">申请售后</view>
 								<view v-if="item.status >=2 &&item.deliveryId!=null" class="btn pay" @click.stop="showDelivery(item)">查看物流</view>
+							</view> -->
+							<view class="btn-box">
+								<view v-if="item.status == 0" class="btn cancel" @click="cancel(item)">取消订单</view>
+								<view v-if="item.status == 0" class="btn pay" @click="pay(item)">支付</view>
+								<view v-if="item.isAfterSales==1" class="btn cancel" @click="refund(item)">申请售后</view>
+								<view v-if="item.status >=2 &&item.deliveryId!=null" class="btn pay"
+									@click.stop="showDelivery(item)">查看物流</view>
+								<view v-if="item.status == -3" class="btn cancel" @click="deleteOrder(item)">删除订单</view>
 								<!-- <view v-if="item.status==4" class="btn pay">再次购买</view> -->
 							</view>
 						</view>
@@ -105,17 +113,26 @@
 			</view>
 		</mescroll-body>
 		<!-- <ykscreenRecord></ykscreenRecord> -->
+		<CustomToast ref="customToast">
+		</CustomToast>
 	</view>
 </template>
 
 <script>
-	import {getMyStoreOrderList,cancelOrder} from '@/api/storeOrder'
+	import {getMyStoreOrderList,cancelOrder,deleteOrder as deleteOrderApi,cancelLiveOrder} from '@/api/storeOrder'
+	import {
+		checkPurchaseLimit,
+		checkOrderPurchaseLimit
+	} from '@/api/product';
 	import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
+	import {
+		CustomToast
+	} from '@/components/custom-toast.vue';
 	// import ykscreenRecord from "@/components/yk-screenRecord/yk-screenRecord.vue"
 	export default {
 		components: {
-			// ykscreenRecord
-		},	
+			CustomToast
+		},
 		mixins: [MescrollMixin], 
 		data() {
 			return {
@@ -148,6 +165,7 @@
 				},
 				// 列表数据
 				dataList: [],
+				payingMap: {},
 			};
 		},
 		onLoad(option) {
@@ -157,15 +175,25 @@
 				that.mescroll.resetUpScroll()
 			});
 		},
+		onShow() {
+			// 页面显示时清除所有支付标志位,确保从支付页返回后可以再次支付
+			this.payingMap = {};
+		},
 		methods: {
 			goSearch(e) {
 				this.searchKey=e.detail.value;
 				this.mescroll.resetUpScroll()
 			},
 			refund(item) {
-				uni.navigateTo({
-					url: './refundOrderProduct?orderId='+item.id
-				})	
+				if (item.orderType == 2) {
+					uni.navigateTo({
+						url: '/pages_live/shopping/refundOrderProduct?orderId=' + item.orderId
+					})
+				} else {
+					uni.navigateTo({
+						url: './refundOrderProduct?orderId=' + item.id
+					})
+				}
 			},
 			// tab切换
 			orderStatusChange(item) {
@@ -213,63 +241,260 @@
 			// 查看订单详情
 			showDetail(item) {
 				console.log(item)
-				uni.navigateTo({
-					url: './storeOrderDetail?id=' + item.id
-				})
+				if (item.orderType == 2) {
+					uni.navigateTo({
+						url: '/pages_live/shopping/storeOrderDetail?orderId=' + item.orderId
+					})
+				} else {
+					uni.navigateTo({
+						url: './storeOrderDetail?id=' + item.id
+					})
+				}
 			},
-			cancel(item){
-				var that=this;
-				uni.showModal({
-				    title: '提示',
-				    content: '确定取消订单吗',
-				    success: function (res) {
-				        if (res.confirm) {
-							var data = {
-								orderId:item.id
-							};
+		cancel(item) {
+			var that = this;
+			uni.showModal({
+				title: '提示',
+				content: '确定取消订单吗',
+				success: function(res) {
+					if (res.confirm) {
+						var data = {
+							orderId: item.orderType == 2 ? item.orderId : item.id
+						};
+						if (item.orderType == 2) {
+							cancelLiveOrder(data).then(res => {
+								if (res.code == 200) {
+									uni.showToast({
+										icon: 'success',
+										title: "操作成功",
+									});
+									that.mescroll.resetUpScroll()
+								} else {
+									uni.showToast({
+										icon: 'none',
+										title: res.msg,
+									});
+								}
+							});
+						} else {
 							cancelOrder(data).then(res => {
-								if(res.code==200){
+								if (res.code == 200) {
 									uni.showToast({
-										icon:'success',
+										icon: 'success',
 										title: "操作成功",
 									});
-									 that.mescroll.resetUpScroll()
-								}else{
+									that.mescroll.resetUpScroll()
+								} else {
 									uni.showToast({
-										icon:'none',
+										icon: 'none',
 										title: res.msg,
 									});
 								}
 							});
-				        } 
-						else if (res.cancel) {
-							
-				        }
-				    }
-				});
-			},
-			pay(item) {
-				if(item.isPrescribe==1 && item.prescribeId==null){
-					uni.navigateTo({
-						url:"/pages/shopping/prescribe?orderId="+item.id
+						}
+		
+					} else if (res.cancel) {
+		
+					}
+				}
+			});
+		},
+			async pay(item) {
+				console.log('item',item)
+				// 获取订单唯一标识
+				const orderKey = item.id || item.orderId || item.orderCode;
+				if (!orderKey) {
+					console.error('订单ID不存在,无法支付');
+					return;
+				}
+			
+				// 防止重复点击
+				if (this.payingMap[orderKey]) {
+					console.log(`订单 ${orderKey} 正在支付中,请勿重复点击`);
+					uni.showToast({
+						icon: 'none',
+						title: '支付处理中,请勿重复点击',
+						duration: 1500
 					});
+					return;
 				}
-				else{
-					uni.navigateTo({
-						// url: '/pages_mall/newArrival?orderId='+item.id
-						url: '/pages/shopping/paymentOrder?orderId='+item.id
-					})
-					// uni.navigateTo({
-					// 	// url: '/pages/store/packageOrderPay?orderId='+item.id
-					// 	url: '/pages_mall/paymentOrder?orderId='+item.id
-					// })
+				// 立即设置标志,防止重复点击
+				this.$set(this.payingMap, orderKey, true);
+				try {
+					if (item.isPrescribe == 1 && item.prescribeId == null) {
+						uni.navigateTo({
+							url: "/pages/shopping/prescribe?orderId=" + item.id,
+							success: () => {
+								// 跳转成功,延迟清除标志位
+								setTimeout(() => {
+									this.$set(this.payingMap, orderKey, false);
+								}, 500);
+							},
+							fail: () => {
+								// 跳转失败,清除标志位
+								this.$set(this.payingMap, orderKey, false);
+							}
+						});
+						return;
+					}
+			
+					if (item.orderType == 2) {
+						// 直播订单,限购逻辑不变,使用原有逻辑
+						let productId = null;
+						let num = item.totalNum || 1; // 获取订单数量
+						if (item.productId) {
+							productId = item.productId;
+						} else if (item.itemJson) {
+							try {
+								const itemJson = JSON.parse(item.itemJson);
+								productId = itemJson.productId;
+							} catch (e) {
+								console.error('解析 itemJson 失败:', e);
+							}
+						}
+			
+						// 如果获取到 productId,检查限购
+						if (productId) {
+							try {
+								const res = await checkPurchaseLimit({
+									productId: productId,
+									num: num
+								});
+			
+								if (res.code !== 200) {
+									this.$set(this.payingMap, orderKey, false); // 限购检查失败,清除标志
+									this.$refs.customToast.show({
+										title: res.msg || '该商品限购,目前剩余可购买数量不足',
+										duration: 2000
+									});
+									return;
+								}
+							} catch (error) {
+								this.$set(this.payingMap, orderKey, false); // 检查失败,清除标志
+								console.error('检查限购失败:', error);
+								uni.showToast({
+									icon: 'none',
+									title: '检查限购失败,请稍后重试'
+								});
+								return;
+							}
+						}
+			
+						// 支付
+						console.log("去支付", item)
+						uni.navigateTo({
+							url: `/pages_live/shopping/paymentOrder?orderList=${encodeURIComponent(JSON.stringify(item))}`,
+							success: () => {
+								// 跳转成功,延迟清除标志位(页面会跳转走)
+								setTimeout(() => {
+									this.$set(this.payingMap, orderKey, false);
+								}, 500);
+							},
+							fail: () => {
+								// 跳转失败,清除标志位
+								this.$set(this.payingMap, orderKey, false);
+								uni.showToast({
+									icon: 'none',
+									title: '跳转失败,请重试'
+								});
+							}
+						})
+					} else {
+						// 商城订单,使用新接口根据orderCode校验
+						if (item.orderCode) {
+							try {
+								const res = await checkOrderPurchaseLimit({
+									orderCode: item.orderCode
+								});
+								if (res.code !== 200) {
+									this.$set(this.payingMap, orderKey, false); // 限购检查失败,清除标志
+									this.$refs.customToast.show({
+										title: res.msg || '订单商品限购校验失败',
+										duration: 2000
+									});
+									return;
+								}
+							} catch (error) {
+								this.$set(this.payingMap, orderKey, false); // 检查失败,清除标志
+								console.error('检查限购失败:', error);
+								uni.showToast({
+									icon: 'none',
+									title: '检查限购失败,请稍后重试'
+								});
+								return;
+							}
+						}
+			
+						// 限购检查通过,进行支付跳转
+						uni.navigateTo({
+							url: '/pages/shopping/paymentOrder?orderId=' + item.id,
+							success: () => {
+								// 跳转成功,延迟清除标志位(页面会跳转走)
+								setTimeout(() => {
+									this.$set(this.payingMap, orderKey, false);
+								}, 500);
+							},
+							fail: () => {
+								// 跳转失败,清除标志位
+								this.$set(this.payingMap, orderKey, false);
+								uni.showToast({
+									icon: 'none',
+									title: '跳转失败,请重试'
+								});
+							}
+						})
+					}
+				} catch (error) {
+					// 异常情况,清除标志位
+					this.$set(this.payingMap, orderKey, false);
+					console.error('支付处理异常:', error);
+					uni.showToast({
+						icon: 'none',
+						title: '支付处理异常,请重试'
+					});
 				}
 			},
 			// 查看物流
 			showDelivery(item) {
-				uni.navigateTo({
-					url: './storeOrderDelivery?orderId='+item.id
-				})
+				if (item.orderType == 2) {
+					uni.navigateTo({
+						url: `/pages_live/shopping/storeOrderDelivery?orderId=${item.orderId}`
+					})
+				} else {
+					uni.navigateTo({
+						url: './storeOrderDelivery?orderId=' + item.id
+					})
+				}
+			},
+			// 删除订单
+			deleteOrder(item) {
+				var that = this;
+				uni.showModal({
+					title: '提示',
+					content: '确定删除订单吗?删除后无法恢复',
+					success: function(res) {
+						if (res.confirm) {
+							var data = {
+								orderId: item.orderType == 2 ? item.orderId : item.id,
+								orderType: item.orderType
+							};
+							deleteOrderApi(data).then(res => {
+								if (res.code == 200) {
+									uni.showToast({
+										icon: 'success',
+										title: "删除成功",
+									});
+									that.mescroll.resetUpScroll()
+								} else {
+									uni.showToast({
+										icon: 'none',
+										title: res.msg,
+									});
+								}
+							});
+						}
+					}
+				});
 			}
 			
 		}

BIN
static/image/my/copy.png


BIN
static/images/live/return_black.png


+ 4 - 1
store/index.js

@@ -17,9 +17,12 @@ const store = new Vuex.Store({
     conversation,
     message,
 	newMsg,
-	timStore
+	timStore,
   },
   getters,
+  state: {
+      appid: 'wxecdaff01852ca309'
+    }
 });
 
 export default store

+ 3 - 3
store/modules/newMsg.js

@@ -1,8 +1,8 @@
 const state = {
   updateTime: '',
-  imgpath:"https://hsyy-1348049832.cos.ap-chongqing.myqcloud.com",
-  logoname:"河山恒康",
-  appid: '',
+  imgpath:"https://bjzmky-1323137866.cos.ap-chongqing.myqcloud.com",
+  logoname:"乐享韶华",
+  appid: 'wxecdaff01852ca309',
 };
 
 export default {

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio