Browse Source

改成可以多商品下单

gcz 2 months ago
parent
commit
dcf31a073b
4 changed files with 543 additions and 200 deletions
  1. 3 2
      center/orderdetails.vue
  2. 25 0
      common/apiurl.js
  3. 256 181
      pages/bookticket.vue
  4. 259 17
      pages/ticketlist.vue

+ 3 - 2
center/orderdetails.vue

@@ -77,9 +77,10 @@
 			<view class="box viewers-list">
 				<view class="title">观影人员</view>
 				<view class="item u-flex u-row-around" v-for="(item,index) in orderDetails.viewersList" :key="index">
-					<text>观影人员{{index + 1}}</text>
+					<!-- <text>观影人员{{index + 1}}</text> -->
 					<text style="width: 96rpx;">{{item.name}}</text>
-					<text style="width: 130rpx;">{{orderDetails.status>2?item.seatName:"-"}}</text>
+					 <!-- style="width: 130rpx;" -->
+					<text style="flex: 1;">{{orderDetails.status>2?item.seatName:"-"}}</text>
 					<!-- <text>{{item.idcard|maskID}}</text> -->
 					<text v-if="orderDetails.status==3||orderDetails.status==4||orderDetails.status==6||orderDetails.status==7" @click="singleQR(item,index)" class="qrbtn" :style="{opacity:item.qrcodeStatus===0?'1':'0.5'}">查看二维码</text>
 				</view>

+ 25 - 0
common/apiurl.js

@@ -122,11 +122,21 @@ const apiurl = {
 		url: '/merchant/merchantTheatreAuditorium/selectRegion',
 		type: 'post'
 	},
+	// 选择区域V2
+	selectRegionV2: {
+		url: '/merchant/merchantTheatreAuditorium/selectRegionV2',
+		type: 'post'
+	},
 	// 订单结算信息
 	getSettlement: {
 		url: '/order/orderInfo/getSettlement',
 		type: 'post'
 	},
+	// 订单结算信息V2
+	getSettlementV2: {
+		url: '/order/orderInfo/getSettlementV2',
+		type: 'post'
+	},
 	// 观影人历史列表
 	selectMemberAll: {
 		url: '/member/memberInfoFamily/selectMemberAll',
@@ -147,16 +157,31 @@ const apiurl = {
 		url: '/order/orderInfo/submit',
 		type: 'post'
 	},
+	// 订单提交V2
+	submitOrderV2: {
+		url: '/order/orderInfo/submitV2',
+		type: 'post'
+	},
 	// 订单支付
 	gotoPay: {
 		url: '/order/orderInfo/gotoPay',
 		type: 'post'
 	},
+	// 订单支付V2
+	gotoPayV2: {
+		url: '/order/orderInfo/gotoPayV2',
+		type: 'post'
+	},
 	// 支付查询
 	payQuery: {
 		url: '/order/orderInfo/payQuery',
 		type: 'post'
 	},
+	// 支付查询V2
+	payQueryV2: {
+		url: '/order/orderInfo/payQueryV2',
+		type: 'post'
+	},
 	// 订单列表
 	orderList: {
 		url: '/order/orderInfo/pageList',

+ 256 - 181
pages/bookticket.vue

@@ -8,13 +8,13 @@
 		<view class="page-wrap">
 			<view class="base-info">
 				<view class="up u-flex">
-					<image class="img" :src="pageContent.performImg" mode="aspectFill" alt="">
+					<image class="img" :src="pageContent.performImg" mode="aspectFill" alt="" />
 						<view class="text">
-							<view class="name text-item">{{pageContent.performName}}-{{pageContent.goodsName}}</view>
+							<view class="name text-item">{{pageContent.performName}}</view>
 							<view class="time text-item">日期 {{pageContent.timeDate}}({{pageContent.timeWeek}})</view>
 							<view class="time text-item">场次 {{pageContent.performTimeStart}} -
 								{{pageContent.performTimeEnd}}</view>
-							<view class="num text-item">{{pageContent.goodsName}} / {{pageContent.seatTypeName}}</view>
+							<!-- <view class="num text-item">{{pageContent.goodsName}} / {{pageContent.seatTypeName}}</view> -->
 							<view class=" text-item">{{pageContent.auditoriumName}}</view>
 							<!-- <view class="position text-item">{{performInfo.name}}</view> -->
 							<!-- 	<view class="addr u-flex u-row-between">
@@ -44,34 +44,36 @@
 					请先<text style="color: #ED0000;">实名认证</text>
 				</view>
 			</view>
-			<view class="block-wrap visitors">
+			<view class="block-wrap visitors" v-for="(goods, goodsIndex) in pageContent.goodsList" :key="goodsIndex">
 				<view class="block-title u-flex">
-					<view class="name">观影人信息</view>
-					<text v-if="needIdcardNumber">需要{{needIdcardNumber}}张身份证</text>
-					<!-- <text>用于入园身份验证</text> -->
+					<view class="name">{{goods.goodsName}}</view>
+					<text v-if="goods.realList && goods.realList[0]">需要{{goods.realList[0].realNum}}张身份证</text>
 				</view>
+				
 				<view class="people-list u-flex u-flex-wrap">
-					<view class="people btn" v-if="visitors.length>0" v-for="(visitor,index) in visitors" :key="index">
+					<!-- 已选择的观影人 -->
+					<view class="people btn" v-for="(visitor,index) in goods.visitors" :key="index">
 						{{visitor.name}}
 					</view>
-					<view class="btn u-flex u-row-center"
-						@click="visitorShow = true">
+					
+					<!-- 添加观影人按钮 -->
+					<view class="btn u-flex u-row-center" @click="showVisitorSelect(goodsIndex)">
 						<u-icon name="plus-circle" color="#2D2D2D" size="32rpx"></u-icon>
 						<text class="text">新增/更换</text>
 					</view>
 				</view>
-				<!-- <view class="no-people u-flex" v-if="!visitors.length>0">
-					<text>游客</text>
-					<text class="right" @click="visitorShow = true">点击选择游客</text>
-				</view> -->
-				<view class="peoples" v-if="visitors.length>0">
-					<view class="peoples-item u-flex u-row-between" v-for="(visitor,index) in visitors" :key="index">
-						<view class="left u-flex">
-							<u-icon @click="delVisitor(visitor)" name="close-circle" color="#2D2D2D"
-								size="32rpx"></u-icon>
-							<text style="margin-left: 8rpx;">观影人{{ index + 1 }}</text>
-							<text class="name">{{visitor.name}}</text>
-							<!-- <text class="people-id">{{ visitor.idcard | maskID }}</text> -->
+
+				<!-- 观影人列表 -->
+				<view class="peoples" v-if="goods.visitors && goods.visitors.length>0">
+					<view class="peoples-item u-flex u-row-between" v-for="(visitor,index) in goods.visitors" :key="index">
+						<view class="left u-flex u-row-center u-flex-col">
+							<view style="margin-bottom: 5px;">观影人{{ index + 1 }}</view>
+							<u-icon @click="delVisitor(goodsIndex, visitor)" name="close-circle" color="#2D2D2D" size="32rpx"></u-icon>
+						</view>
+						<view class="center">
+							<view class="center-item name">{{visitor.name}}</view>
+							<view class="center-item mobile">手机号 {{visitor.mobile}}</view>
+							<view class="center-item idcard">身份证 {{visitor.idcard}}</view>
 						</view>
 						<view class="right" @click="editVisitor(visitor)">编辑</view>
 					</view>
@@ -101,7 +103,7 @@
 					2.由于影院系统不稳定等因素,存在出票失败的情况,会进行退款
 				</view>
 				<view class="notice-item">
-					3.取票码可以在“我的-订单页”中查看
+					3.取票码可以在"我的-订单页"中查看
 				</view> -->
 			</view>
 		</view>
@@ -116,11 +118,23 @@
 					添加观影人信息
 				</view>
 				<view class="list">
-					<u-checkbox-group v-model="selectedVisitor" iconPlacement="left">
-						<view class="people u-flex u-row-between" v-for="(item,index) in visitorList" :key="index">
-							<u-checkbox activeColor="#ED0303" :label="item.name" :name="item.idcard"></u-checkbox>
-							<u-icon name="edit-pen-fill" color="#7F7F7F" size="32rpx"
-								@click="editVisitor(item)"></u-icon>
+					<u-checkbox-group v-model="selectedVisitor[currentGoodsIndex]" iconPlacement="left">
+						<view class="people u-flex u-row-between" 
+							v-for="(item,index) in visitorList" 
+							:key="index"
+							:class="{'disabled': selectedVisitors.includes(item.idcard) && !selectedVisitor[currentGoodsIndex].includes(item.idcard)}">
+							<u-checkbox 
+								activeColor="#ED0303" 
+								:label="item.name" 
+								:name="item.idcard"
+								:disabled="selectedVisitors.includes(item.idcard) && !selectedVisitor[currentGoodsIndex].includes(item.idcard)"
+							></u-checkbox>
+							<u-icon 
+								name="edit-pen-fill" 
+								color="#7F7F7F" 
+								size="32rpx"
+								@click="editVisitor(item)"
+							></u-icon>
 						</view>
 					</u-checkbox-group>
 				</view>
@@ -132,14 +146,17 @@
 				<view class="left u-flex u-col-top">
 					<text>实付金额:</text>
 					<view class="total-price">
-						¥{{finalPrice}}
-						<view class="bottom-coupon" v-if="coupon.quota">
-							优惠券共减¥{{couponAmount}}
+						¥{{pageContent.salePrice}}
+						<view class="original-price">
+							原价¥{{pageContent.originalPrice}}
 						</view>
+						<!-- <view class="bottom-coupon" v-if="coupon.quota">
+							优惠券共减¥{{couponAmount}}
+						</view> -->
 					</view>
 				</view>
 				<view class="right">
-					<view class="btn active" v-if="totalPrice>=0&&cansubmit&&vuex_member_info.isAuth"
+					<view class="btn active" v-if="pageContent.salePrice>=0&&cansubmit&&vuex_member_info.isAuth"
 						@click="setTemplate">立即支付</view>
 					<!-- <u-button @click="setTemplate" class="btn active"  v-if="totalPrice>0&&cansubmit&&vuex_member_info.isAuth">立即支付</u-button> -->
 					<view class="btn" v-else>立即支付</view>
@@ -186,14 +203,15 @@ import {
 					},
 					ticketNotice: '',
 					viewerList: [],
+					goodsList:[],
 				}, //页面信息
 				ticketNotice: '',
 				cansubmit: true,
 				staticUrl: this.$commonConfig.staticUrl,
-				visitors: [], //确认的游客
+				visitors: [], // 改为数组对象,每个对象包含goodsId和对应的游客列表
 				visitorShow: false, //游客弹层
 				visitorList: [], //游客列表
-				selectedVisitor: [], //选中的游客(id)
+				selectedVisitor: {}, // 改为对象,key为goodsId
 				params: {}, //要提交的数据
 				orderId: '', //订单提交获取
 				payResult: {}, //gotoPay结果
@@ -210,8 +228,10 @@ import {
 				retailId:null,
 				couponList:[],
 				latitude:null,
-				longitude:null
-
+				longitude:null,
+				selectGoodsList:[],
+				currentGoodsIndex: '', // 当前选择的商品ID
+				selectedVisitors: [], // 所有已选择的观影人idcard列表
 			}
 		},
 		watch: {
@@ -224,51 +244,35 @@ import {
 		  }
 		},
 		computed: {
-			finalPrice(){
-				let discount = 0;
-				let price = this.totalPrice||0;
-				if(this.coupon.type==1){
-					discount = this.coupon.quota || 0; // 如果没有优惠券,默认为0 
-					price = Math.max(this.totalPrice - discount,0);
+			// finalPrice(){
+			// 	let discount = 0;
+			// 	let price = this.totalPrice||0;
+			// 	if(this.coupon.type==1){
+			// 		discount = this.coupon.quota || 0; // 如果没有优惠券,默认为0 
+			// 		price = Math.max(this.totalPrice - discount,0);
 					
-				}else if(this.coupon.type==2){
-					price = this.totalPrice*(this.coupon.quota/100);
-				}
-				// console.log('price',price);
-				// console.log('price',typeof price);
-				if(typeof price=='string'){
-					price = Number(price)
-				}
-				return price.toFixed(2)
-			},
-			totalPrice() {
-				let that = this;
-				return this.visitors.reduce((total, item) => {
-					let price = Number(that.pageData.salePrice);
-					if(this.pageContent.personnelNum!==0){
-						total = price
-					}else{
-						total += price;
-					}
-					return total;
-				}, 0).toFixed(2);
-			},
+			// 	}else if(this.coupon.type==2){
+			// 		price = this.totalPrice*(this.coupon.quota/100);
+			// 	}
+			// 	// console.log('price',price);
+			// 	// console.log('price',typeof price);
+			// 	if(typeof price=='string'){
+			// 		price = Number(price)
+			// 	}
+			// 	return price.toFixed(2)
+			// },
+			// totalPrice() {
+			// 	// 计算所有商品的总价
+			// 	return this.pageContent.goodsList.reduce((total, goods) => {
+			// 		return total + (Number(goods.salePrice) * goods.saleNum);
+			// 	}, 0).toFixed(2);
+			// },
 			totalVisitor() {
-				let that = this;
-				return this.visitors.reduce((total, item) => {
-					total += 1;
-					return total;
+				// 计算所需的总游客数
+				return this.pageContent.goodsList.reduce((total, goods) => {
+					return total +  goods.saleNum;
 				}, 0);
 			},
-			needIdcardNumber(){
-				if(this.pageContent.oneMany===1&&this.pageContent.personnelNum!==0){
-					return this.pageContent.personnelNum
-				}else if(this.pageContent.oneMany===1&&this.pageContent.personnelNum===0){
-					return null
-				}else if(this.pageContent.oneMany===2){
-					return 1
-				}
-			},
 			couponAmount() {
 			    let amount = 0;
 			    if (this.coupon.type == 1) {
@@ -300,9 +304,17 @@ import {
 			this.pageData = page;
 			this.performId = page.performId;
 			this.pageData.performTimeId = page.performTimeId;
-			this.pageData.seatTypeId = page.seatTypeId
+			this.pageData.seatTypeId = page.seatTypeId;
+			const selectGoodsList = uni.getStorageSync('selectGoodsList');
+			if(selectGoodsList.length>0){
+				this.selectGoodsList = JSON.parse(selectGoodsList);
+				uni.removeStorage({
+					key:'selectGoodsList'
+				})
+			}
+			console.log('selectGoodsList',this.selectGoodsList);
 			this.getSystemInfo();
-			this.getSettlement();
+			// this.getSettlement();
 
 			this.getTemplateIdList(); //获取模板列表
 
@@ -320,7 +332,8 @@ import {
 				};
 			},
 			getSettlement() {
-				this.$u.api.getSettlement(this.pageData).then(res => {
+				this.pageData.goodsList = this.selectGoodsList;
+				this.$u.api.getSettlementV2(this.pageData).then(res => {
 					// console.log('getSettlement',res.data);
 					this.pageContent = res.data;
 					this.getMemberAll();
@@ -353,34 +366,55 @@ import {
 					console.log('getMemberAll', err);
 				})
 			},
-			confirmVisitor() {
-				let that = this;
+			showVisitorSelect(goodsIndex) {
+				this.currentGoodsIndex = goodsIndex;
+				this.visitorShow = true;
 				
-				if(this.needIdcardNumber&&this.needIdcardNumber!==this.selectedVisitor.length){
-					uni.showToast({
-						title:`身份证数量应该为${this.needIdcardNumber}张`,
-						icon:'none'
-					})
-					return false
+				// 初始化当前商品的已选游客
+				if(!this.selectedVisitor[goodsIndex]) {
+					this.selectedVisitor[goodsIndex] = [];
 				}
-				// console.log('selectedVisitor',this.selectedVisitor);
-				this.visitorShow = false;
-				this.visitors = this.visitorList.filter(obj => that.selectedVisitor.includes(obj.idcard)).map(item => {
-					return {
-						name: item.name,
-						mobile: item.mobile,
-						idcard: item.idcard
+				
+				// 设置checkbox的选中状态
+				const currentGoods = this.pageContent.goodsList[goodsIndex];
+				if(currentGoods.visitors) {
+					this.selectedVisitor[goodsIndex] = currentGoods.visitors.map(v => v.idcard);
+				}
+
+				// 获取其他商品已选的观影人列表(排除当前商品)
+				this.selectedVisitors = this.pageContent.goodsList.reduce((acc, goods, index) => {
+					if(index !== goodsIndex && goods.visitors) {
+						acc.push(...goods.visitors.map(v => v.idcard));
 					}
-				});
-				// console.log('this.visitors',this.visitors);
+					return acc;
+				}, []);
 			},
-			delVisitor(visitor) {
-				// console.log('delVisitor',visitor);
-				// console.log('this.visitors',this.visitors);
-				this.visitors = this.visitors.filter(obj => obj.idcard != visitor.idcard);
-				this.selectedVisitor = this.selectedVisitor.filter(item => item != visitor.idcard);
-				// console.log('this.visitors',this.visitors);
-				// console.log('this.selectedVisitor',this.selectedVisitor);
+			delVisitor(goodsIndex, visitor) {
+				console.log('goodsIndex', goodsIndex);
+				console.log('visitor', visitor);
+				
+				// 从对应商品的游客列表中删除
+				const goods = this.pageContent.goodsList[goodsIndex];
+				if(goods) {
+					// 使用 Vue.set 或 this.$set 来确保视图更新
+					this.$set(goods, 'visitors', (goods.visitors || []).filter(v => v.idcard !== visitor.idcard));
+				}
+				
+				// 从选中列表中删除
+				if(this.selectedVisitor[goodsIndex]) {
+					// 同样使用 this.$set 来更新数组
+					this.$set(this.selectedVisitor, goodsIndex, 
+						this.selectedVisitor[goodsIndex].filter(id => id !== visitor.idcard)
+					);
+				}
+
+				// 更新其他商品的 selectedVisitors
+				this.selectedVisitors = this.pageContent.goodsList.reduce((acc, goods, index) => {
+					if(index !== goodsIndex && goods.visitors) {
+						acc.push(...goods.visitors.map(v => v.idcard));
+					}
+					return acc;
+				}, []);
 			},
 			addVisitor() {
 				uni.$u.route('/center/people', {
@@ -436,26 +470,23 @@ import {
 				    }
 					this.retailId = retailId;
 				console.log('submitorder retailId',this.retailId);
-				if(this.pageContent.oneMany === 2 && this.pageContent.personnelNum!==0){//一证多票,补齐观影人信息
-					let dontNeedCardsNumber = this.pageContent.personnelNum - this.visitors.length;
-					if(dontNeedCardsNumber>0){//
-						for (let i = 0;i<dontNeedCardsNumber;i++) {
-							console.log('dontNeedCardsNumber',dontNeedCardsNumber);
-							this.visitors.push({
-								name: this.visitors[0].name,
-								idcard: this.visitors[0].idcard,
-								mobile: this.visitors[0].mobile,
-							});
-						}
-					}
-				}
+				// 构建订单参数
 				let params = {
 					performId: this.pageContent.performId,
-					goodsList: [{
-						goodsId: this.pageContent.goodsId,
-						salePeice: this.pageContent.salePrice,
-						saleNum: 1
-					}],
+					goodsList: this.pageContent.goodsList.map(goods => {
+						return {
+							goodsId: goods.goodsId,
+							saleNum: goods.saleNum,
+							tourists: goods.visitors.map(visitor => {
+								return {
+									realType: goods.realList[0].realType,
+									name: visitor.name,
+									mobile: visitor.mobile, 
+									idcard: visitor.idcard
+								}
+							})
+						}
+					}),
 					auditoriumId: this.pageContent.auditoriumId,
 					performTimeId: this.pageContent.performTimeId,
 					seatTypeId: this.pageContent.seatTypeId,
@@ -464,54 +495,29 @@ import {
 						mobile: this.pageContent.purchaser.mobile,
 						idcard: this.pageContent.purchaser.idcard
 					},
-					viewerList: this.visitors,
-					label:this.label,
-					retailId:this.retailId,
-					coupon:{
-						memberCouponId:null
+					label: this.label,
+					retailId: this.retailId,
+					coupon: {
+						memberCouponId: this.coupon.quota ? this.coupon.id : null
 					},
-					longitude:this.longitude,
-					latitude:this.latitude,
+					longitude: this.longitude,
+					latitude: this.latitude
 				}
-				if(this.coupon.quota){
-					// console.log('this.coupon',this.coupon);
-					params.coupon.memberCouponId = this.coupon.id;
-					// return
-				}
-				console.log('submitorder params',params);
-				// return
-				// #ifdef MP
-				params['source'] = 1
-				// #endif 
-				// #ifdef H5
-				params['source'] = 4
-				// #endif
-				this.cansubmit = false;
-				// console.log('pageData',this.pageData);
-				// console.log('visitors',this.visitors);
-				// console.log('params',params);
-				this.$u.api.submitOrder(params,{custom:{toast:false}}).then(res => {
-					// console.log('submitOrder',res.data);
-					this.orderId = res.data.orderId;
-					// this.templateEven()
-					this.gotoPay();
-
-				}).catch(err => {
-					this.cansubmit = true;
-					uni.hideLoading();
-					// uni.$u.toast(err.msg);
-					// if (err.msg.includes('限购')) {
+				
+				// 发起提交订单请求
+				this.$u.api.submitOrderV2(params, {custom:{toast:false}})
+					.then(res => {
+						this.orderId = res.data.orderId;
+						this.gotoPay();
+					})
+					.catch(err => {
+						this.cansubmit = true;
 						this.umodal.title = '';
 						this.umodal.content = err.msg;
 						this.umodal.show = true;
-						// setTimeout(() => {
-						// 	uni.$u.route('/center/order', {
-						// 		status: 0
-						// 	});
-						// }, 2000)
-					// }
-					console.log('submitOrder', err);
-				})
+					}).finally(()=>{
+						uni.hideLoading()
+					})
 			},
 			modalConfirm(){
 				this.umodal.show = false;
@@ -638,7 +644,7 @@ import {
 				// #endif
 			},
 			gotoPay() {
-				this.$u.api.gotoPay({
+				this.$u.api.gotoPayV2({
 					orderId: this.orderId,
 					openid: ''
 				}).then(res => {
@@ -721,25 +727,27 @@ import {
 						uni.hideLoading();
 						console.log("支付查询超时或达到最大重试次数");
 						// 在这里添加超时或达到最大重试次数的处理逻辑
-						uni.$u.route('/center/order');
+						uni.redirectTo({
+							url: '/center/order'
+						});
 					} else {
 						console.log("第" + retryCount + "次查询");
 						// 调用查询支付状态的方法
 						// 如果支付状态为成功,则清除定时器并处理成功的逻辑
 						// 如果支付状态为失败,则清除定时器并处理失败的逻辑
-						this.$u.api.payQuery({
+						this.$u.api.payQueryV2({
 							orderId: this.orderId
 						}).then(res => {
 							// 0-未支付 1-已支付 2-支付中 3-支付失败 4-支付退款
 							let payStatus = res.data.payStatus;
 							if (payStatus === 1) {
-								uni.$u.route('/center/orderdetails', {
-									type:'redirectTo',
-									id: res.data.orderId
-								});
-								// uni.$u.route('/center/order', {
-								// 	status: 3
+								// uni.$u.route('/center/orderdetails', {
+								// 	type:'redirectTo',
+								// 	id: res.data.orderId
 								// });
+								uni.redirectTo({
+									url: '/center/order?status=3'
+								});
 								// uni.$u.route('/center/paysuccess');
 							} else if (payStatus === 0 || payStatus === 2) {
 								this.payQuery()
@@ -826,6 +834,49 @@ import {
 			            // console.log('findOrderCoupons',err.data);
 			        });
 			},
+			// 确认选择观影人
+			confirmVisitor() {
+				const goods = this.pageContent.goodsList[this.currentGoodsIndex];
+				
+				// 验证身份证数量是否符合要求
+				if(goods.realList[0] && this.selectedVisitor[this.currentGoodsIndex].length !== goods.realList[0].realNum) {
+					uni.showToast({
+						title: `请选择${goods.realList[0].realNum}位观影人`,
+						icon: 'none'
+					})
+					return false;
+				}
+
+				// 更新选中的观影人
+				goods.visitors = this.visitorList
+					.filter(obj => this.selectedVisitor[this.currentGoodsIndex].includes(obj.idcard))
+					.map(item => ({
+						name: item.name,
+						mobile: item.mobile,
+						idcard: item.idcard
+					}));
+
+				this.visitorShow = false;
+			},
+			// 修改支付按钮点击方法
+			setTemplate() {
+				// 检查是否所有商品都选择了足够的观影人
+				const isValid = this.pageContent.goodsList.every(goods => {
+					if(!goods.visitors || !goods.realList[0]) return false;
+					return goods.visitors.length === goods.realList[0].realNum;
+				});
+
+				if(!isValid) {
+					uni.showToast({
+						title: '请为每个票种选择足够的观影人',
+						icon: 'none'
+					});
+					return;
+				}
+
+				// 继续原有的支付逻辑
+				this.submitorder();
+			}
 		}
 	}
 </script>
@@ -957,18 +1008,29 @@ import {
 			font-weight: 400;
 			color: #7F7F7F;
 			margin-bottom: 24rpx;
+			
+			.left{
+				text-align: center;
+			}
+			.center{
+				.center-item{
+					& ~ .center-item{
+						margin-top: 3px;
+					}
+				}
+			}
 
 			.peoples-item {
 				margin-bottom: 10rpx;
 			}
 
-			.name {
-				margin-left: 20rpx;
-				margin-right: 28rpx;
-				font-size: 28rpx;
-				font-weight: bold;
-				color: #363636;
-			}
+			// .name {
+			// 	margin-left: 20rpx;
+			// 	margin-right: 28rpx;
+			// 	font-size: 28rpx;
+			// 	font-weight: bold;
+			// 	color: #363636;
+			// }
 
 			.people-id {
 				font-size: 20rpx;
@@ -1140,6 +1202,11 @@ import {
 				.bottom-coupon{
 					font-size: 24rpx;
 				}
+				.original-price{
+					font-size: 24rpx;
+					color: #7F7F7F;
+					text-decoration: line-through;
+				}
 			}
 
 			.btn {
@@ -1175,4 +1242,12 @@ import {
 			border-radius: 8rpx;
 		}
 	}
+
+	.list {
+		.people {
+			&.disabled {
+				opacity: 0.5;
+			}
+		}
+	}
 </style>

+ 259 - 17
pages/ticketlist.vue

@@ -119,13 +119,14 @@
 					</view>
 				</view> -->
 				<view class="date-block ticket-type generic-block" v-if="sessionList.length>=1">
-					<view class="title">票务信息</view>
+					<view class="title">票</view>
 					<view class="empty" v-if="ticketTypeList.length<1">
 						暂无门票
 					</view>
 					<view class="date-list u-flex u-flex-wrap">
-						<view class="date-item" :class="{active:ticketTypeIndex==index}" @click="ticketTypeClick(index)" v-for="(date,index) in ticketTypeList" :key="index">
-							<view class="name">{{ date.goodsName }}</view>
+						<view class="date-item" :class="{active:ticketTypeIndex==index,'disabled':date.stockNum<1&&date.stockNum!==-1}" @click="ticketTypeClick(index,date)" v-for="(date,index) in ticketTypeList" :key="index">
+							<view class="name">{{ date.name }}</view>
+							<view class="sold-out" v-if="date.stockNum<1&&date.stockNum!==-1">售罄</view>
 						</view>
 					</view>
 					<view class="goodsSnapshot" v-if="ticketTypeList.length>0&&goodsSnapshot">
@@ -146,21 +147,60 @@
 				</view>
 				<view class="date-block position-select generic-block"  v-if="sessionList.length>=1">
 					<view class="title u-flex u-row-between">
-						<view class="">类型选择</view>
+						<view class="">票种</view>
 						<view v-if="positionData.seatImg" class="" style="font-size: 24rpx;color: #aaa" @click="showSeatImg">票区图</view>
 					</view>
 					<view class="empty" v-if="positionArr.length<1">
 						没有座位信息
 					</view>
-					<view class="date-list u-flex u-flex-wrap">
+					<!-- <view class="date-list u-flex u-flex-wrap">
 						<view class="date-item" :class="{active:positionIndex==index,'stock-over':date.stockNum<1&&date.stockNum!==-1}" @click="positionClick(index,date)" v-for="(date,index) in positionArr" :key="index">
 							<view class="name">{{ date.seatTypeName }} <br />¥ {{date.salePrice}}</view>
 							<view class="sold-out" v-if="date.stockNum<1&&date.stockNum!==-1">售罄</view>
 						</view>
+					</view> -->
+					<view class="ticket-type-list">
+						<view class="ticket-type-item" v-for="(item, index) in positionArr" :key="index">
+							<view class="type-info u-flex u-row-between">
+								<view class="type-name">{{item.goodsName}}</view>
+								<view class="type-price">
+									<text class="salePrice-price">¥{{item.salePrice}}</text>
+									<text class="original-price" v-if="item.originalPrice">¥{{item.originalPrice}}</text>
+								</view>
+							</view>
+							<view class="type-desc" v-if="item.goodsSnapshot">{{item.goodsSnapshot}}</view>
+							<view class="ticket-control u-flex u-row-right">
+								<view class="minus" :class="{disabled: !item.saleNum}" 
+									@click.stop="changeTicketNum(item, 'minus')">-</view>
+								<!-- <view class="num">{{item.saleNum || 0}}</view> -->
+								<input
+								    border="none"
+									type="number"
+									style="width: 40px;text-align: center;"
+								    v-model="item.saleNum"
+								  ></input>
+								<view class="plus" :class="{disabled: item.stockNum === item.saleNum}"
+									@click.stop="changeTicketNum(item, 'plus')">+</view>
+							</view>
+						</view>
+					</view>
+				</view>
+				<!-- <view class="book-btn full-btn" v-if="positionArr.length>=1&&sessionList.length>=1&&(positionIndex||positionIndex===0)" @click="book()">确定</view>
+				<view class="full-btn gray" v-else>确定</view> -->
+				<!-- 底部浮动条 -->
+				<view class="bottom-bar">
+					<view class="inner">
+						<view class="price-info">
+							<view class="total-price">
+								<text>¥{{totalPrice}}</text>
+								<text class="original">¥{{originalPrice}}</text>
+							</view>
+						</view>
+						<view class="submit-btn" :class="{active: hasSelectedTicket}" @click="book">
+							{{hasSelectedTicket ? '去支付' : '请选择票'}}
+						</view>
 					</view>
 				</view>
-				<view class="book-btn full-btn" v-if="positionArr.length>=1&&sessionList.length>=1&&(positionIndex||positionIndex===0)" @click="book()">确定</view>
-				<view class="full-btn gray" v-else>确定</view>
 			</view>
 			<!-- ticket end -->
 			<view class="details" v-if="tabsIndex==1">
@@ -583,6 +623,9 @@
 				this.getPositionData();
 				this.ticketTypeIndex = 0;
 				this.positionIndex = 0;
+				
+				let sellableTicketTypeIndex = this.findFirstSellableTicketTypeIndex();
+				this.ticketTypeIndex = sellableTicketTypeIndex;
 				// console.log('sessionClick',this.sessionList[this.sessionIndex]);
 			},
 			async getPositionData(){
@@ -629,15 +672,28 @@
 					retailId:this.retailId
 				}
 				// console.log('getPositionData params',params);
-				this.$u.api.selectRegion(params).then(res=>{
+				this.$u.api.selectRegionV2(params).then(res=>{
 					// console.log('getPositionData',res.data);
 					this.positionData =  res.data;
+					
 					// this.positionArr = res.data.regionPriceList||[];
-					this.ticketTypeList = res.data.goodsList||[];
+					this.ticketTypeList = res.data.seatList||[];
+					let sellableTicketTypeIndex = this.findFirstSellableTicketTypeIndex();
+					this.ticketTypeIndex = sellableTicketTypeIndex;
+					
 					this.goodsSnapshot = this.ticketTypeList[this.ticketTypeIndex]?.goodsSnapshot;
-					this.positionArr =  this.ticketTypeList[this.ticketTypeIndex]?.regionList||[];
-					let sellableProductIndex = this.findFirstSellableProductIndex();
-					this.positionIndex = sellableProductIndex;
+					this.positionArr =  this.ticketTypeList[this.ticketTypeIndex]?.goodsList||[];
+					this.positionArr = this.positionArr.map(obj => {
+						return {
+							...obj, // 保留原有的字段
+							saleNum: 0 // 新增的字段
+						};
+					});
+					// let sellableProductIndex = this.findFirstSellableProductIndex();
+					// this.positionIndex = sellableProductIndex;
+					
+					
+					
 					// console.log('this.positionIndex',this.positionIndex);
 					uni.hideLoading();
 				}).catch(err=>{
@@ -645,16 +701,45 @@
 					console.log('getPositionData',err);
 				})
 			},
-			ticketTypeClick(index){
+			// 改变票数
+			changeTicketNum(item, type) {
+				// console.log('changeTicketNum===',item, type);
+				const currentNum = item.saleNum;
+				if(type === 'minus' && currentNum > 0) {
+					item.saleNum=item.saleNum-1
+					// 直接修改数值
+					// console.log('item.saleNum',item.saleNum)
+				}
+				if(type === 'plus') {
+					 // && currentNum < item.stockNum
+					 // console.log('item.saleNum',item.saleNum)
+					item.saleNum=item.saleNum+1
+					// console.log('item.saleNum',item.saleNum)
+				}
+				console.log('this.positionArr',this.positionArr)
+			},
+			ticketTypeClick(index,date){
 				if(this.ticketTypeIndex==index){
 					// console.log('不变');
 					return
+				}else if(date.stockNum<1&&date.stockNum!==-1){
+					uni.showToast({
+						title:'已售罄',
+						icon:'none'
+					})
+					return
 				}
 				this.ticketTypeIndex = index;
 				this.goodsSnapshot = this.ticketTypeList[this.ticketTypeIndex]?.goodsSnapshot;
-				this.positionArr =  this.ticketTypeList[this.ticketTypeIndex]?.regionList||[];
-				let sellableProductIndex = this.findFirstSellableProductIndex();
-				this.positionIndex = sellableProductIndex;
+				this.positionArr =  this.ticketTypeList[this.ticketTypeIndex]?.goodsList||[];
+				this.positionArr = this.positionArr.map(obj => {
+					return {
+						...obj, // 保留原有的字段
+						saleNum: 0 // 新增的字段
+					};
+				});
+				// let sellableProductIndex = this.findFirstSellableProductIndex();
+				// this.positionIndex = sellableProductIndex;
 				// this.getPositionData()
 			},
 			positionClick(index,date){
@@ -671,6 +756,7 @@
 				this.positionIndex = index;
 			},
 			findFirstSellableProductIndex(){
+				console.log('this.positionArr',this.positionArr);
 				// 检查数组是否为空
 				if (!Array.isArray(this.positionArr) || this.positionArr.length === 0) {
 					return null; 
@@ -684,7 +770,25 @@
 				}
 				return null; 
 			},
+			findFirstSellableTicketTypeIndex(){
+				console.log('this.ticketTypeList',this.ticketTypeList);
+				// 检查数组是否为空
+				if (!Array.isArray(this.ticketTypeList) || this.ticketTypeList.length === 0) {
+					return null; 
+				}
+			
+				// 遍历数组,找到第一个可售的产品
+				for (let i = 0; i < this.ticketTypeList.length; i++) {
+					if (this.ticketTypeList[i].stockNum > 0 || this.ticketTypeList[i].stockNum === -1) {
+						return i; // 返回第一个可售产品的索引
+					}
+				}
+				return null; 
+			},
 			book(item){
+				if(!this.hasSelectedTicket){
+					return
+				}
 				// console.log('book',item);
 				// console.log('performInfo',this.performInfo);
 				let session = this.sessionList[this.sessionIndex];
@@ -693,7 +797,9 @@
 				let performTimeEnd = session.performTimeEnd;
 				let dateOBJ = this.dateList[this.dateIndex];
 				let ticketType = this.ticketTypeList[this.ticketTypeIndex];
-				let seatType = this.positionArr[this.positionIndex];
+				console.log('this.positionArr',this.positionArr);
+				console.log('this.positionIndex',this.positionIndex);
+				let seatType = this.ticketTypeList[this.ticketTypeIndex];
 				// console.log('ticketType',ticketType);
 				
 				let params ={
@@ -704,6 +810,22 @@
 					seatTypeId:seatType.seatTypeId,
 					salePrice:seatType.salePrice
 				}
+				
+				let selectGoodsList = this.positionArr.reduce((acc, item) => {
+					if (item.saleNum > 0) {
+						acc.push({
+							goodsId: item.goodsId,
+							saleNum: item.saleNum
+						});
+					}
+					return acc;
+				}, []);
+				// console.log('selectGoodsList',selectGoodsList)
+				uni.setStorage({
+					key:'selectGoodsList',
+					data:JSON.stringify(selectGoodsList)
+				});
+				
 				// console.log('book params',params);
 				uni.$u.route('pages/bookticket',params)
 				
@@ -920,6 +1042,26 @@
 				})
 			}
 
+		},
+		computed: {
+			// 是否已选择票
+			hasSelectedTicket() {
+				return this.positionArr.some(item => item.saleNum > 0);
+			},
+			// 总价
+			totalPrice() {
+				return this.positionArr.reduce((total, item) => {
+					return total + (item.saleNum * item.salePrice);
+				}, 0).toFixed(2);
+			},
+			// 原价
+			originalPrice() {
+				return this.positionArr.reduce((total, item) => {
+					// 如果有原价就用原价,没有就用售价
+					const price = item.originalPrice || item.salePrice;
+					return total + (item.saleNum * price);
+				}, 0).toFixed(2);
+			}
 		}
 	}
 </script>
@@ -1171,4 +1313,104 @@
 	position: fixed;
 	left: -99999px;
 }
+.ticket-type-list{
+	.ticket-type-item{
+		border-radius: 10rpx;
+		border:1px solid #7F7F7F;
+		padding: 20rpx;
+		margin-bottom: 24rpx;
+		box-shadow: 0rpx 2rpx 20rpx 0rpx rgba(211,211,211,0.5);
+	}
+	.type-info{
+		margin-bottom: 40rpx;
+		.type-name{
+			font-size: 28rpx;
+		}
+		.salePrice-price{
+			color: #ED0000;
+		}
+		.original-price{
+			color: #7F7F7F;
+			text-decoration: line-through;
+			margin-left: 10rpx;
+		}
+	}
+	.type-desc{
+		color: #7F7F7F;
+		font-size: 26rpx;
+	}
+	.ticket-control {
+		display: flex;
+		align-items: center;
+		
+		.minus, .plus {
+			width: 50rpx;
+			height: 50rpx;
+			line-height: 46rpx;
+			text-align: center;
+			border: 1px solid #ddd;
+			border-radius: 50%;
+			
+			&.disabled {
+				color: #ccc;
+				background: #f5f5f5;
+			}
+		}
+		
+		.num {
+			min-width: 60rpx;
+			text-align: center;
+			margin: 0 20rpx;
+		}
+	}
+}
+/* 底部浮动条样式 */
+.bottom-bar {
+	height: 100rpx;
+	.inner{
+		position: fixed;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		height: 100rpx;
+		background: #fff;
+		display: flex;
+		align-items: center;
+		padding: 0 30rpx;
+		box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.05);
+		z-index: 100;
+	}
+	.price-info {
+		flex: 1;
+		
+		.total-price {
+			font-size: 36rpx;
+			color: #f00;
+			font-weight: bold;
+			
+			.original {
+				font-size: 24rpx;
+				color: #999;
+				text-decoration: line-through;
+				margin-left: 10rpx;
+				font-weight: normal;
+			}
+		}
+	}
+	
+	.submit-btn {
+		width: 200rpx;
+		height: 80rpx;
+		line-height: 80rpx;
+		text-align: center;
+		background: #eee;
+		color: #999;
+		border-radius: 40rpx;
+		
+		&.active {
+			background: #f00;
+			color: #fff;
+		}
+	}
+}
 </style>