<template> <view class="pages"> <view class="navbar-box"> <u-navbar title=" " :safeAreaInsetTop="true" @leftClick="leftClick" :titleStyle="{color:'#fff'}" leftIconColor="#fff" bgColor="transparent"></u-navbar> </view> <view class="banner"> <!-- <image class="img" :src="staticUrl+'/img/bookticket-banner.png'" alt=""> --> <!-- <image class="img" :src="theatre.showImg" alt=""> --> <u-swiper :list="performInfo.photoList" height="554rpx" indicatorMode="dot" :indicatorStyle="{bottom:'100rpx'}" keyName="imageUrl" radius="0" @click="previewBanner" :indicator="false" circular ></u-swiper> <!-- <view class="content"> <view class="share" @click="openShare()"> <image class="icon" :src="staticUrl+'/img/share-ico.png'" ></image> </view> </view> --> </view> <view class="base-info"> <view class="inner"> <view class="name">{{performInfo.name}}</view> <view class="addr u-flex u-row-between"> <view class="text">演出地址:{{theatre.address||''}}</view> <view class="menu u-flex"> <view class="item" @click="goMap"> <image class="icon" :src="staticUrl+'/img/map-ico.png'" ></image> <text>导航</text> </view> <view class="item" @click="phoneCall"> <image class="icon" :src="staticUrl+'/img/phone-call-ico.png'" ></image> <text>电话</text> </view> <view class="item" @click="openShare()"> <image class="icon" :src="staticUrl+'/img/share-ico-v2.png'" ></image> <text>分享</text> </view> <!-- <u-icon name="arrow-right" color="#ffffff" size="36rpx" @click="goMap"></u-icon> <u-icon name="arrow-right" color="#ffffff" size="36rpx" @click="goMap"></u-icon> --> </view> </view> </view> </view> <view class="tabs-wrap"> <view class="inner"> <u-tabs :list="tabsArr" @click="tabsClick" lineColor="#ED0000" lineWidth="82rpx" :activeStyle="{color: '#2D2D2D',fontWeight: 'bold'}" itemStyle="width:33%; height: 46px;box-sizing:border-box" > </u-tabs> </view> </view> <view class="page-wrap"> <view class="ticket" v-if="tabsIndex==0"> <view class="date-block"> <view class="title">演出日期</view> <view class="date-list u-flex"> <view class="date-item" :class="{active:dateIndex==index,dot:performDateList.find(item => item.performDate === `${date.year}-${date.month}-${date.day}`) !== undefined}" @click="dateClick(index)" v-for="(date,index) in dateList" :key="index"> <!-- <view class="name">{{ date.name || ' ' }}</view> --> <!-- <view class="name"> {{ performDateList.find(item => item.performDate === date.fullDay ) !== undefined?'有演出':'无演出' }} </view> --> <view class="name"> {{date.fullDay|checkWeekDate}} </view> <view class="date">{{ date.month }} - {{ date.day }}</view> <image class="selected-img" :src="staticUrl+'/img/selected.png'"></image> </view> <view class="date-item more-date u-flex u-row-center" @click="calendarShow = true"> <view class="text"> <view class="">更多</view> <view class="">日期</view> </view> <u-icon name="arrow-right" color="#7F7F7F" size="24rpx"></u-icon> </view> </view> </view> <view class="date-block auditorium generic-block" v-show="false"> <view class="title">演出厅</view> <view class="date-list u-flex u-flex-wrap"> <view class="date-item" :class="{active:auditoriumIndex==index}" @click="auditoriumClick(index)" v-for="(date,index) in auditoriumList" :key="index"> <view class="name">{{ date.name }}</view> </view> </view> </view> <view class="date-block session-wrap generic-block"> <view class="title">演出场次</view> <view class="empty" v-if="auditoriumList.length>=1&&sessionList.length<1"> 当前日期暂无演出场次,请重新选择 </view> <view class="date-list u-flex u-flex-wrap" v-else> <view class="date-item" :class="{active:sessionIndex==index,disabled:!date.ifVaild}" @click="sessionClick(index,date)" v-for="(date,index) in sessionList" :key="index"> <view class="name">{{ date.performTimeStart}} - {{date.performTimeEnd}}</view> </view> </view> </view> <!-- <view class="date-block generic-block session-wrap"> <view class="title">演出场次</view> <view class="session"> <view class="session-item" :class="{active:sessionIndex==index}" @click="sessionClick(index)" v-for="(date,index) in sessionList" :key="index"> {{ date.performTimeStart}} - {{date.performTimeEnd}} </view> </view> <view class="empty" v-if="auditoriumList.length>=1&&sessionList.length<1"> 当前日期暂无演出场次,请重新选择 </view> </view> --> <view class="date-block ticket-type generic-block" v-if="sessionList.length>=1"> <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,'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"> 票务说明:{{goodsSnapshot}} </view> <!-- <view class="type-item" :class="{active:sessionIndex==index}" v-for="(item,index) in ticketTypeList" :key="index"> <view class="name-price u-flex u-row-between"> <view class="name">{{item.goodsName}}</view> <view class="prices">¥ {{item.salePrice}} 起</view> </view> <view class="bottom u-flex u-row-between"> <view class="left u-flex" v-html="item.goodsSnapshot||''"> </view> <view class="btn" @click="book(item)" v-if="item.quantity!==0&&sessionList.length>=1">预定</view> <view class="btn disabled" v-else>无票</view> </view> </view> --> </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 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-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||fromPage=='rebook'}" @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" :disabled="fromPage=='rebook'" > <view class="plus" :class="{disabled: item.stockNum === item.saleNum||fromPage=='rebook'}" @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 rebook" v-if="fromPage=='rebook'"> <view class="price-info"> <view class="total-price"> <text class="price-item">原订单总额:{{originOrderInfo.realPrice}}</text> <text class="price-item">改签总额:{{totalPrice}}</text> </view> <!-- 差价信息 --> <view class="difference"> <view class="difference-item" v-if="originOrderInfo.realPrice - totalPrice>0"> <text>待退款:{{originOrderInfo.realPrice - totalPrice}}</text> </view> <view class="difference-item" v-if="originOrderInfo.realPrice - totalPrice<=0"> <text>待支付:{{totalPrice - originOrderInfo.realPrice}}</text> </view> </view> </view> <view class="submit-btn" :class="{active: hasSelectedTicket}" @click="handelRebook"> {{hasSelectedTicket ? '确认改签' : '请选择票'}} </view> </view> <view class="inner" v-else> <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> <!-- ticket end --> <view class="details" v-if="tabsIndex==1"> <view class="details-block"> <view class="title">剧情简介</view> <view class="intro parse-content"> <u-parse :content="performInfo.performSnapshot"></u-parse> </view> </view> <view class="details-block actors"> <view class="title u-flex u-row-between"> 演职人员 <view class="right u-flex" @click="$u.route('pages/actors',{performId:performId})"> <text>更多</text> <u-icon name="arrow-right" color="#7F7F7F" size="24rpx"></u-icon> </view> </view> <view class="actor-list"> <u-scroll-list :indicator="false"> <view class="item" v-for="(item, index) in actorsArr" :key="index" @click="$u.route('/pages/actorsinfo',{id:item.id})"> <image class="img" :src="item.performerHead||staticUrl+'/img/actors.png'"></image> <view class="text"> <view class="name">{{item.performerName}}</view> <view class="role u-line-1">{{item.performerRole}}</view> </view> </view> </u-scroll-list> <u-empty text="暂无" v-if="actorsArr.length<1"></u-empty> </view> </view> </view> <view class="viewingTips parse-content" v-if="tabsIndex==2"> <u-parse :content="formerNotice"></u-parse> </view> </view> <!-- :maxDate="maxDate" --> <u-calendar ref="calendar" :formatter="formatter" :maxDate="maxDate" monthNum="12" :show="calendarShow" color="#EF1010" :closeOnClickOverlay="true" @close="closeCalendar" @confirm="confirmCalendar"> </u-calendar> <!-- 分享选择弹出内容 --> <view class="share-option" :class="{shareShow:shareShow}"> <view class="overlay" v-if="shareShow" @click="shareShow=false"></view> <!-- #ifdef MP-WEIXIN --> <button class="share-option-item wx-share" data-name="shareBtn" open-type="share"> 发送给朋友 </button> <!-- #endif --> <view class="share-option-item" @click="getPoster">生成海报</view> <view class="share-option-item" @click="shareShow=false">取消</view> </view> <u-popup :show="posterShow" @close="posterShow=false" ref="uni-popup"> <view class="poster-wrap u-flex u-col-center"> <view class="poster-inner"> <view class="close-wrap" @click="posterShow=false"> <u-icon name="close-circle" color="#fff" size="56rpx"></u-icon> </view> <view class="poster" id="poster" ref="poster" > <u--image :showLoading="false" :src="posterSrc" width="100%" height="65vh" mode="aspectFit"></u--image> </view> <!-- savePoster --> <view class="poster-btn" @click="saveImage">保存图片</view> </view> </view> </u-popup> <canvas canvas-id="canvas" class="canvas" style="width: 574px; height: 1042px;"></canvas> </view> </template> <script> import moment from 'moment'; import { systemInfo } from "@/mixin.js"; // 日历 const d = new Date() const year = d.getFullYear() let month = d.getMonth() + 1 month = month < 10 ? `0${month}` : month const date = d.getDate() // 日历 // 算最大日期 const maxd = new Date() maxd.setMonth(maxd.getMonth() + 12) // 将月份加3 const maxdyear = maxd.getFullYear() let maxdmonth = maxd.getMonth() + 1 maxdmonth = maxdmonth < 10 ? `0${maxdmonth}` : maxdmonth const maxddate = maxd.getDate() const theMaxDate = `${maxdyear}-${maxdmonth}-${maxddate}` // console.log('theMaxDate=============================',theMaxDate) //输出: //算最大日期 let parentThis= null; let sharePath = '/pages/index/index'; export default { onShareAppMessage(res) { if (res.from === 'button') {// 来自页面内分享按钮 console.log(res.target) } return { title: this.$isDevelop?'剧场票务':'伟大转折演艺', path: sharePath } }, onShareTimeline(res) { if (res.from === 'button') {// 来自页面内分享按钮 console.log(res.target) } return { title: this.$isDevelop?'剧场票务':'伟大转折演艺', path: '/pages/ticketlist' } }, mixins:[systemInfo], data() { return { performId:'', staticUrl:this.$commonConfig.staticUrl, tabsArr:[ {name:'票务购买'}, {name:'演出详情'}, {name:'观影须知'} ], theatre:{}, tabsIndex:0, dateList:[], dateIndex:0, calendarShow:false,//日历 // maxDate:`${year}-${month}-${date + 30}`, maxDate:theMaxDate, auditoriumList:[],//演艺厅 auditoriumIndex:0, sessionList:[],//演出场次 sessionIndex:0, ticketTypeList:[],//门票 actorsArr:[], shareShow:false, posterShow:false, posterSrc:'', posterSrcType:null, performInfo:{},//节目详情 formerNotice:{},//节目观影须知 performDateList:[],//有票的日期 // firstGet:0, ticketTypeIndex:0, positionData:{},//座位信息接口 positionArr:[], positionIndex:null, scene:null, retailId:null,//分销码 label:null,//scene解析出来的 goodsSnapshot:null,//票务说明 retailId:null, fromPage:null, payResult:{}, cansubmit: true, newOrderId:'', originOrderInfo:{}, resubmitInfo:{}, } }, onShow() { if(this.auditoriumList.length>=1){ this.getPositionData(); } }, onLoad(page) { console.log('ticketlist page',page); parentThis = this; const scene = decodeURIComponent(page.scene); if(scene&&scene!='undefined'){ uni.setStorage({ key:'scene', data:scene }); } // console.log('scene=========',scene); if(scene&&scene!='undefined'){ this.$u.api.uncompress({scene:scene}).then(res=>{ this.performId = res.data.performId; sharePath=`/pages/ticketlist?id=${res.data.performId}`; this.getPerformData(); // this.getSystemInfo(); let today = new Date(); this.setDate(today); this.getPerformInfo(); this.getPerformerNotice(); // this.label = res.data.label; // console.log('uncompress',res.data); }).catch(err=>{ console.log('uncompress',err); }) }else if(page.fromPage=='rebook'){ this.performId = page.performId; this.orderId = page.orderId; this.fromPage = page.fromPage; sharePath=`/pages/ticketlist?id=${page.id}`; this.getPerformData(); // this.getSystemInfo(); let today = new Date(); this.setDate(today); this.getPerformInfo(); this.getPerformerNotice(); //获取订单信息 this.getOrderInfo(); } else{ this.performId = page.id; sharePath=`/pages/ticketlist?id=${page.id}`; this.getPerformData(); // this.getSystemInfo(); let today = new Date(); this.setDate(today); this.getPerformInfo(); this.getPerformerNotice(); } }, onReady() { // 如果需要兼容微信小程序的话,需要用此写法 this.$refs.calendar.setFormatter(this.formatter); }, methods: { leftClick(e){ let pages = getCurrentPages(); if(pages.length==1){ uni.$u.route('/pages/index/index') }else{ uni.navigateBack() }; }, uncompress(scene){ this.$u.api.uncompress({scene:scene}).then(res=>{ this.performId = res.data.performId; // this.label = res.data.label; // console.log('uncompress',res.data); }).catch(err=>{ console.log('uncompress',err); }) }, getOrderInfo(){ this.$u.api.orderDetails({id:this.orderId}).then(res=>{ console.log('getOrderInfo',res.data); this.originOrderInfo = res.data||{}; this.resubmitInfo = res.data.resubmitInfo||{}; }).catch(err=>{ console.log('getOrderInfo',err); }) }, getPerformData(){ this.$u.api.performSell({performId:this.performId}).then(res=>{ // console.log('getPerformData',res.data); this.theatre = res.data.theatreList[0]; this.auditoriumList = res.data.theatreList[0].auditoriumList; // this.ticketTypeList = res.data.goodsList; // this.goodsSnapshot = this.ticketTypeList[this.ticketTypeIndex]?.goodsSnapshot; this.performDateList = this.auditoriumList[this.auditoriumIndex].performDateList; this.getDate() // console.log('auditoriumList',this.auditoriumList); }).catch(err=>{ console.log('getPoster',err); }) }, getPerformInfo(){ this.$u.api.performInfo({id:this.performId}).then(res=>{ // console.log('getPerformInfo',res.data); this.performInfo = res.data; this.actorsArr = res.data.performerList; }).catch(err=>{ console.log('getPerformInfo',err); }) }, getPerformerNotice(){ this.$u.api.performerNotice({performId:this.performId}).then(res=>{ // console.log('getPerformerNotice',res.data); this.formerNotice = res.data.performNotice; }).catch(err=>{ console.log('getPerformerNotice',err); }) }, getDate(){ let param = { auditoriumId:this.auditoriumList[this.auditoriumIndex]?.id,//演艺厅ID(演艺厅列表) performId:this.performId, }; this.$u.api.getAuditoriumDate(param).then(res=>{ // console.log('getDate',res.data); this.performDateList = res.data.list||[]; this.setDate(); // this.getTimes(); }).catch(err=>{ console.log('getDate',err); }) }, getTimes(){ // this.firstGet++; uni.showLoading(); let auditoriumId= this.auditoriumList[this.auditoriumIndex]?.id; if(!auditoriumId){return} // console.log('this.dateList',this.dateList); // console.log('this.dateIndex',this.dateIndex); let dateOBJ = this.dateList[this.dateIndex]; let param = { auditoriumId:auditoriumId,//演艺厅ID(演艺厅列表) performId:this.performId, date:`${dateOBJ.year}-${dateOBJ.month}-${dateOBJ.day}` }; // console.log('param',param); this.$u.api.getAuditoriumTimes(param).then(res=>{ // console.log('getTimes',res.data); this.sessionList = res.data.list.map(item=>{ item.performTimeStart = item.performTimeStart; item.performTimeEnd = item.performTimeEnd; return item }); console.log('this.sessionList',this.sessionList); // this.sessionIndex = 0; this.sessionIndex = this.sessionList.findIndex(item => item.ifVaild === 1); if (this.sessionIndex !== -1) { this.getPositionData(); } // let session = this.sessionList[this.sessionIndex]; }).catch(err=>{ console.log('getPoster',err); }) }, formatter: (day) => { if(!parentThis){ return } day.date = moment(day.date).format("YYYY-MM-DD HH:mm:ss") // console.log('day',day); // console.log('thisthisthisthisthisthisthisthisthis',parentThis); // console.log('==================================',day.date.toISOString().split('T')[0]); let ticket = parentThis.performDateList.find(item => item.performDate === day.date.split(' ')[0]); if(ticket){ day.dot = true } // if(ticket&&ticket.ishave){ // day.bottomInfo = '有票' // day.dot = true // }else if(ticket){ // day.dot = true // } return day }, setDate(firstDay,isSelect){ // console.log('this.performDateList',this.performDateList); if(this.performDateList[0]?.performDate&&!isSelect){ // firstDay = new Date(this.performDateList[0].performDate); const uniquePerformDateList = Array.from(new Set(this.performDateList.map(JSON.stringify))).map(JSON.parse);//去重 // console.log('uniquePerformDateList',uniquePerformDateList); let arr = uniquePerformDateList.slice(0, 3);//只要前三项 this.dateList = arr.map(item=>{ // console.log('uniquePerformDateList item',item); let day = item.performDate.split('-'); // console.log('setDate day',day); return { year:day[0],month:String(day[1]),day:String(day[2]), fullDay:`${day[0]}-${String(day[1])}-${String(day[2])}`} }); // console.log('this.dateList',this.dateList); this.dateIndex = 0; this.getTimes(); // return } // console.log('this.dateListthis.dateListthis.dateListthis.dateList',this.dateList); // 前端写演出日期(今天,明天,后天) let today = firstDay||new Date(); // console.log('today=========',today); let tomorrow = new Date(today.getTime()); tomorrow.setDate(today.getDate() + 1); let afterTomorrow = new Date(today.getTime()); afterTomorrow.setDate(today.getDate() + 2); this.dateList = [ { year: today.getFullYear(), month: String(today.getMonth() + 1).padStart(2, '0'), day: String(today.getDate()).padStart(2, '0'), name: '日期', fullDay:`${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${ String(today.getDate()).padStart(2, '0')}` }, { year: tomorrow.getFullYear(), month: String(tomorrow.getMonth() + 1).padStart(2, '0'), day: String(tomorrow.getDate()).padStart(2, '0'), name: '日期', fullDay:`${tomorrow.getFullYear()}-${String(tomorrow.getMonth() + 1).padStart(2, '0')}-${ String(tomorrow.getDate()).padStart(2, '0')}` }, { year: afterTomorrow.getFullYear(), month: String(afterTomorrow.getMonth() + 1).padStart(2, '0'), day: String(afterTomorrow.getDate()).padStart(2, '0'), name: '日期', fullDay:`${afterTomorrow.getFullYear()}-${String(afterTomorrow.getMonth() + 1).padStart(2, '0')}-${ String(afterTomorrow.getDate()).padStart(2, '0')}` } ]; this.getTimes() // console.log('this.dateList ',this.dateList); }, dateClick(index){ this.dateIndex = index; this.ticketTypeIndex = 0; this.positionIndex = 0; this.getTimes() }, auditoriumClick(index){ this.auditoriumIndex = index; this.getDate(); }, confirmCalendar(e){ // console.log('confirmCalendar',e); // this.getDate(e) this.dateIndex = 0; this.setDate( new Date(e),true ); // console.log('maxDate',this.maxDate); // console.log('dateList',this.dateList); this.calendarShow = false; }, closeCalendar(){ this.calendarShow = false; }, tabsClick(e){ // console.log('tabsClick',e); this.tabsIndex = e.index; }, sessionClick(index,data){ // console.log('sessionClick index',index); console.log('sessionClick data',data); if(!data.ifVaild){ uni.showToast({ title:'该场次不可用', icon:'none' }) return } if(this.sessionIndex==index){ // console.log('不变'); return } this.sessionIndex = index; let session = this.sessionList[this.sessionIndex]; this.getPositionData(); this.ticketTypeIndex = 0; this.positionIndex = 0; let sellableTicketTypeIndex = this.findFirstSellableTicketTypeIndex(); this.ticketTypeIndex = sellableTicketTypeIndex; // console.log('sessionClick',this.sessionList[this.sessionIndex]); }, async getPositionData() { try { uni.showLoading(); // 获取 retailId let retailId = null; try { const res = await uni.getStorage({ key: 'retailId' }); retailId = res.data; } catch (error) { console.log('获取 retailId 失败:', error); } this.retailId = retailId; // 构建请求参数 const session = this.sessionList[this.sessionIndex] || {}; const params = { performId: this.performId, auditoriumId: this.auditoriumList[this.auditoriumIndex].id, performTimeId: session.id, label: this.label, retailId: this.retailId, timeId: session.id, orderId: this.orderId }; // 根据不同场景调用不同接口 const api = this.fromPage === 'rebook' ? this.$u.api.selectRegionRebook : this.$u.api.selectRegionV2; const res = await api(params,{custom:{toast:false,noload:true}}); // 处理响应数据 this.positionData = res.data; this.ticketTypeList = res.data.seatList || []; // 设置默认选中的票档 const sellableTicketTypeIndex = this.findFirstSellableTicketTypeIndex(); this.ticketTypeIndex = sellableTicketTypeIndex; // 设置票务说明 this.goodsSnapshot = this.ticketTypeList[this.ticketTypeIndex]?.goodsSnapshot; // 初始化座位数据 this.positionArr = (this.ticketTypeList[this.ticketTypeIndex]?.goodsList || []) .map(obj => ({ ...obj, saleNum: obj.saleNum||0 // 初始化选择数量为0 })); } catch (error) { console.error('获取座位数据失败:', error); uni.showModal({ title: '提示', content: error.msg||'获取座位数据失败', showCancel:false, success: function (res) { console.log('获取座位数据失败 提示',res); } }) // uni.showToast({ // title: error.msg||'获取座位数据失败', // icon: 'none' // }); } finally { uni.hideLoading(); } }, // 改变票数 changeTicketNum(item, type) { // console.log('changeTicketNum===',item, type); if(this.fromPage=='rebook'){ return } 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]?.goodsList||[]; this.positionArr = this.positionArr.map(obj => { return { ...obj, // 保留原有的字段 saleNum: 0 // 新增的字段 }; }); // let sellableProductIndex = this.findFirstSellableProductIndex(); // this.positionIndex = sellableProductIndex; // this.getPositionData() }, positionClick(index,date){ if(this.positionIndex==index){ // console.log('不变'); return }else if(date.stockNum<1&&date.stockNum!==-1){ uni.showToast({ title:'已售罄', icon:'none' }) return } this.positionIndex = index; }, findFirstSellableProductIndex(){ console.log('this.positionArr',this.positionArr); // 检查数组是否为空 if (!Array.isArray(this.positionArr) || this.positionArr.length === 0) { return null; } // 遍历数组,找到第一个可售的产品 for (let i = 0; i < this.positionArr.length; i++) { if (this.positionArr[i].stockNum > 0 || this.positionArr[i].stockNum === -1) { return i; // 返回第一个可售产品的索引 } } 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]; // console.log('session',session); let performTimeStart = session.performTimeStart; let performTimeEnd = session.performTimeEnd; let dateOBJ = this.dateList[this.dateIndex]; let ticketType = this.ticketTypeList[this.ticketTypeIndex]; console.log('this.positionArr',this.positionArr); console.log('this.positionIndex',this.positionIndex); let seatType = this.ticketTypeList[this.ticketTypeIndex]; // console.log('ticketType',ticketType); let params ={ performId:this.performId, goodsId:ticketType.id, auditoriumId:this.auditoriumList[this.auditoriumIndex].id, performTimeId:session.id, 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) // 调整选择位置类型页面 // uni.$u.route('pages/chosenposition',{ // goodsId:item.id, // performId:this.performId, // performName:this.performInfo.name, // day:`${dateOBJ.year}-${dateOBJ.month}-${dateOBJ.day}`, // performTimeStart:session.performTimeStart, // performTimeEnd:session.performTimeEnd, // performTimeId:session.id, // auditoriumId:this.auditoriumList[this.auditoriumIndex].id // }) }, getUserLocation() { return new Promise((resolve, reject) => { uni.getLocation({ type: 'gcj02', success: (res) => { resolve(res); }, fail: (err) => { reject(err); } }); }); }, async handelRebook(){ if(!this.hasSelectedTicket){ return } const location = await this.getUserLocation(); let session = this.sessionList[this.sessionIndex]; let params = { orderId:this.orderId, performTimeId:session.id, longitude:location.longitude, latitude:location.latitude } console.log('handelRebook params',params); uni.showModal({ title: '温馨提示', content: '仅限改签一次,且改签后禁止退款,确定要继续改签吗?', success: res => { if (res.confirm) { this.cansubmit = false; this.$u.api.rebookApi(params) .then(res => { this.newOrderId = res.data.orderId; // if(!res.data.needPay){ // //this.payQuery(); // uni.$u.route('/center/orderdetails', { // type:'redirectTo', // id: this.orderId // }); // }else { // this.payResult = res.data.payInfo; // this.payResult.package = res.data.payInfo.packageValue; // this.wxPay() // } this.gotoPay(); // uni.redirectTo({ // url: '/center/order' // }); }) .catch(err => { uni.showToast({ title: err.msg||'改签失败', icon: 'none' }) }); } } }) }, gotoPay() { this.$u.api.gotoPayV2({ orderId: this.newOrderId, openid: '' }).then(res => { // this.payResult = res.data.payInfo; // this.payResult.package = res.data.payInfo.packageValue; // ygh if(!res.data.needPay){ //this.payQuery(); uni.$u.route('/center/orderdetails', { type:'redirectTo', id: this.newOrderId }); }else { this.payResult = res.data.payInfo; this.payResult.package = res.data.payInfo.packageValue; this.wxPay() } }).catch(err => { this.cansubmit = true; console.log('gotoPay', err); }) }, wxPay() { let that = this; uni.requestPayment({ ...this.payResult, "provider": "wxpay", "orderInfo": { // "appid": "wx499********7c70e", // 微信开放平台 - 应用 - AppId,注意和微信小程序、公众号 AppId 可能不一致 // "noncestr": "c5sEwbaNPiXAF3iv", // 随机字符串 // "package": "Sign=WXPay", // 固定值 // "partnerid": "148*****52", // 微信支付商户号 // "prepayid": "wx202254********************fbe90000", // 统一下单订单号 // "timestamp": 1597935292, // 时间戳(单位:秒) // "sign": "A842B45937F6EFF60DEC7A2EAA52D5A0" // 签名,这里用的 MD5/RSA 签名 }, success(res) { that.payQuery(); }, fail(e) { uni.$u.route('/center/order', { status: 0 }); console.log('wxPayfail', e); }, complete() { this.cansubmit = true; } }) }, payQuery() { let that = this; let retryCount = 0; let maxRetryCount = 5; // 设置最大重试次数 let interval = 2000; // 设置间隔时间为2秒 let timeout = 10000; // 设置超时时间为10秒 let timer; uni.showLoading({ title: '支付结果查询中' }) timer = setInterval(() => { retryCount++; if (retryCount > maxRetryCount || retryCount * interval > timeout) { clearInterval(timer); uni.hideLoading(); console.log("支付查询超时或达到最大重试次数"); // 在这里添加超时或达到最大重试次数的处理逻辑 uni.redirectTo({ url: '/center/order' }); } else { console.log("第" + retryCount + "次查询"); // 调用查询支付状态的方法 // 如果支付状态为成功,则清除定时器并处理成功的逻辑 // 如果支付状态为失败,则清除定时器并处理失败的逻辑 this.$u.api.payQueryV2({ orderId: this.newOrderId }).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.redirectTo({ url: '/center/order?status=3' }); // uni.$u.route('/center/paysuccess'); } else if (payStatus === 0 || payStatus === 2) { this.payQuery() } else if (payStatus === 3) { uni.toast('支付失败') } clearInterval(timer); }).catch(err => { console.log('payQuery', err); }).finally(() => { uni.hideLoading() this.cansubmit = true; }) } }, interval); }, openShare(){ if(!this.performInfo.posterImg){ uni.$u.toast('没有节目信息'); return false } this.shareShow = true; }, // 海报相关开始 getPoster(item){ this.posterShow = true; this.shareShow = false; // 后端生成海报 // this.$u.api.performQrcode({performId:this.performId}).then(res=>{ // this.posterSrc = res.data.imageUrl; // // console.log('getPoster',res.data); // }).catch(err=>{ // console.log('getPoster',err); // }) //前端生成海报 uni.showLoading({ title: '生成海报中' }); // 前端生成海报开始 let that = this; const ctx = uni.createCanvasContext('canvas', this); // 加载海报背景图 uni.getImageInfo({ src: this.performInfo.posterImg, success: res1 => { const img1 = res1.path; // 加载海报二维码 uni.getImageInfo({ src: this.performInfo.appletQrcode, success: res2 => { const img2 = res2.path; // 绘制海报背景图 ctx.drawImage(img1, 0, 0, 574, 1042); // 绘制海报二维码 ctx.drawImage(img2, 428, 888, 114, 114); // 绘制完成后导出图片并显示 ctx.draw(false, () => { uni.canvasToTempFilePath({ canvasId: 'canvas', success: res3 => { this.posterSrc = res3.tempFilePath; this.posterSrcType = 'local'; }, fail: err => { console.error(err); }, }, this); }); }, fail: err2 => { console.error(err2); }, }); }, fail: err1 => { console.error(err1); }, complete: () => { uni.hideLoading() } }); // 前端生成海报结束 }, saveImage() { let that = this; uni.showLoading({ title: '保存中' }); if(this.posterSrcType == 'local'){ let params = { tempFilePath:that.posterSrc } that.saveImageToPhotosAlbum(params); return } uni.downloadFile({ url: this.posterSrc, success(res) { if (res.statusCode === 200) { that.saveImageToPhotosAlbum(res) } else { uni.showToast({ title: '下载图片失败', icon: 'none' }); } }, fail(e) { console.log('下载图片失败', e); console.log('posterSrc',that.posterSrc); uni.showToast({ title: '下载图片失败', icon: 'none' }); } }); }, saveImageToPhotosAlbum(res){ let that = this; uni.saveImageToPhotosAlbum({ filePath: res.tempFilePath, success() { uni.showToast({ title: '保存相册成功!', icon: 'success' }); }, fail(err) { console.log('保存图片失败',err); if (err.errMsg === 'saveImageToPhotosAlbum:fail auth deny') { uni.getSetting({ success(res) { if (!res.authSetting['scope.writePhotosAlbum']) { uni.showModal({ title: '提示', content: '您还没有授权访问相册,请前往设置页面打开权限。', confirmText: '去设置', success(res) { if (res.confirm) { uni.openSetting(); } } }); } else { uni.showToast({ title: '保存图片失败', icon: 'none' }); } } }); } else { uni.showToast({ title: '保存图片失败', icon: 'none' }); } }, complete() { setTimeout(()=>{ uni.hideLoading(); that.posterShow = false; },1500) } }); }, // 海报相关结束 goMap(){ uni.openLocation({ latitude:Number(this.theatre.latitude), //维度 longitude: Number(this.theatre.longitude), //经度 name: this.theatre.name, //目的地定位名称 scale: 15, //缩放比例 address: this.theatre.address //导航详细地址 }) }, phoneCall(){ // console.log('this.theatre',this.theatre); uni.makePhoneCall({ phoneNumber: this.theatre.customerServiceMobile, success() { console.log('success'); }, fail() { console.log('fail'); } }); }, showSeatImg(){ if(!this.positionData.seatImg){ uni.$u.toast('没有图片') return } let urls = []; urls.push(this.positionData.seatImg) uni.previewImage({ urls: urls, }) }, previewBanner(index){ let urls = this.performInfo.photoList.map(item=>{ return item.imageUrl }); // console.log('urls',urls); uni.previewImage({ urls: urls, current:index }) } }, 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> <style lang="scss" scoped> .banner{ position: relative; .img{ width: 750rpx; height: 554rpx; display: block; } .content{ position: absolute; // width: 100%; box-sizing: border-box; padding: 0 32rpx; right: 0; bottom: 200rpx; } .share{ overflow: hidden; margin-bottom: 100rpx; float: right; .icon{ float: right; display: block; width: 32rpx; height: 32rpx; padding: 10rpx; border-radius: 50%; background-color: rgba(0,0,0,0.4); } } } .base-info{ position: relative; // margin-bottom: 54rpx; .inner{ position: relative; padding: 50rpx 38rpx 58rpx 40rpx; background-color: #FFFFFF; border-radius: 32rpx 32rpx 0rpx 0rpx; margin-top: -30px; border-bottom: 12rpx solid #F7F8F9; } .name{ font-size: 40rpx; font-weight: 800; color: #2D2D2D; margin-bottom: 26rpx; } .addr{ font-size: 26rpx; font-weight: 400; color: #606060; } .menu{ .item{ margin-left: 18rpx; font-size: 18rpx; font-weight: 400; color: #999999; text-align: center; } .icon{ width: 62rpx; height: 64rpx; display: block; margin-bottom: 4rpx; } } } .tabs-wrap{ position: relative; margin-bottom: 54rpx; &::after{ content: ''; width: 100%; height: 1px; background-color: #eee; position: absolute; left: 0; right: 0; bottom: -46rpx; } .inner{ position: relative; padding-top: 48rpx; background-color: #FFFFFF; // border-radius: 32rpx 32rpx 0rpx 0rpx; height: 46rpx; // margin-top: -30px; } } .date-block{ margin-bottom: 64rpx; } .title{ font-size: 32rpx; font-weight: bold; color: #2D2D2D; line-height: 48rpx; margin-bottom: 38rpx; } .empty{ background-color: #636363; color: #979797; border-radius: 8rpx; padding: 24rpx; text-align: center; } .session-wrap{ } .session{ display: grid; grid-template-columns: repeat(3, 1fr); gap: 20rpx; .session-item{ height: 80rpx; line-height: 80rpx; border-radius: 12rpx; border: 2rpx solid #636363; text-align: center; font-size: 24rpx; font-weight: 400; color: #636363; &.active{ color: #ED0000; border-color: #ED0000; } } } .ticket-type{ .type-item{ background: #FFFFFF; box-shadow: 0rpx 2rpx 12rpx 0rpx rgba(180,180,180,0.5); border-radius: 20rpx; margin-bottom: 20rpx; padding: 38rpx 40rpx; .name-price{ margin-bottom: 26rpx; } .name{ font-size: 28rpx; font-weight: 500; color: #363636; } .prices{ font-size: 36rpx; font-weight: bold; color: #ED0000; } .ishave{ margin-bottom: 26rpx; .text{ height: 30rpx; line-height: 30rpx; padding: 0 22rpx; background: #FFC8C8; border-radius: 20rpx; font-size: 20rpx; font-weight: 400; color: #ED0000; } } .bottom{ font-size: 22rpx; font-weight: 400; color: #7F7F7F; .btn{ height: 60rpx; line-height: 60rpx; background: linear-gradient(90deg, #FF7979 0%, #ED0000 100%); border-radius: 30rpx; padding: 0 36rpx; font-size: 28rpx; font-weight: 500; color: #FFFFFF; &.disabled{ background: #979797; } } } } .goodsSnapshot{ font-size: 24rpx; color: #750000; line-height: 1.5; margin-top: 24rpx; padding-bottom: 24rpx; // border-bottom: 1px solid #750000; } } .details{ .title{} .details-block{ background: #FFFFFF; box-shadow: 0rpx 2rpx 12rpx 2rpx rgba(221,221,221,0.5); border-radius: 20rpx; margin-bottom: 24rpx; padding: 36rpx; &.actors{ .title{ .right{ font-size: 24rpx; font-weight: 400; color: #7F7F7F; } } } } .intro{ font-size: 26rpx; font-weight: 400; color: #4E4E4E; line-height: 40rpx; } .actor-list{ .item{ flex-shrink: 0; width: 160rpx; text-align: center; margin-right: 24rpx; } .img{ display: block; width: 100%; height: 200rpx; margin-bottom: 16rpx; } .name{ font-size: 28rpx; font-weight: 500; color: #2D2D2D; line-height: 42rpx; margin-bottom: 4rpx; } .role{ font-size: 24rpx; font-weight: 400; color: #7F7F7F; line-height: 36rpx; } } } .canvas{ 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; &.rebook{ .price-info{ display: flex; align-items: center; justify-content: space-between; .price-item{ font-size: 24rpx; color: #999; display:block } } .difference{ padding:0 10rpx; .difference-item{ font-size: 26rpx; color: #f00; font-weight: bold; } } } } .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; padding: 0 24rpx; height: 80rpx; line-height: 80rpx; text-align: center; background: #eee; color: #999; border-radius: 40rpx; &.active { background: #f00; color: #fff; } } } </style>