<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" :indicator="false" circular ></u-swiper> <view class="content"> <view class="share" @click="openShare(item)"> <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> <!-- <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"> <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"> <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"> <view class="title">门票</view> <view class="empty" v-if="ticketTypeList.length<1"> 暂无门票 </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="ishave"> <text class="text">{{(item.quantity>0&&sessionList.length>=1)?'有票':'无票'}}</text> </view> --> <view class="bottom u-flex u-row-between"> <view class="left u-flex" v-html="item.goodsSnapshot||''"> <!-- 购票须知 <u-icon name="arrow-right" color="#7F7F7F" size="24rpx"></u-icon> --> <!-- {{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> <!-- 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"> <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> <button class="share-option-item wx-share" data-name="shareBtn" open-type="share"> 发送给朋友 </button> <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="true" :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: 670px; height: 900px;"></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; export default { 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, } }, onShow() { }, onLoad(page) { parentThis = this; console.log('page',page); this.performId = 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() }; }, 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.performDateList = this.auditoriumList[this.auditoriumIndex].performDateList; this.getTimes() // 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); }) }, getTimes(){ this.firstGet++; uni.showLoading(); let auditoriumId= this.auditoriumList[this.auditoriumIndex]?.id; if(!auditoriumId){return} 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.performDateList = res.data.performDateList||[]; this.sessionList = res.data.list.map(item=>{ item.performTimeStart = item.performTimeStart; item.performTimeEnd = item.performTimeEnd; return item }); this.setDate(); uni.hideLoading(); }).catch(err=>{ console.log('getPoster',err); }) }, formatter: (day) => { 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){ 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.firstGet',this.firstGet); if(this.firstGet<=1){ this.getTimes(); } // return } // console.log('this.dateListthis.dateListthis.dateListthis.dateList',this.dateList); // 前端写演出日期(今天,明天,后天) let today = firstDay; let tomorrow = new Date(); tomorrow.setDate(today.getDate() + 1); let afterTomorrow = new Date(); 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')}` } ]; // console.log('this.dateList ',this.dateList); }, dateClick(index){ this.dateIndex = index; this.getTimes() }, auditoriumClick(index){ this.auditoriumIndex = index; this.getTimes(); }, confirmCalendar(e){ // console.log('confirmCalendar',e); this.setDate( new Date(e),true ); this.dateIndex = 0; // 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){ this.sessionIndex = index; }, book(item){ // 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]; 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 }) }, openShare(){ 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, 670, 900); // 绘制海报二维码 ctx.drawImage(img2, 468, 724, 188, 166); // 绘制完成后导出图片并显示 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() { uni.hideLoading(); that.posterShow = false; } }); }, // 海报相关结束 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'); } }); } } } </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; left: 0; bottom: 200rpx; } .share{ overflow: hidden; margin-bottom: 100rpx; .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; } } } } } .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: absolute; left: -99999px; } </style>