微信小程序仿猫眼电影在线选座实现

tech2024-10-03  19

 select-seat.js

const api = require('../../../utils/api.js') const app = getApp(); let that; Page({ /** * 页面的初始数据 */ data: { height: "", seats: [], lineTop: 0, lineHeight: "", lineArray: [], reset: false, columnNumber: 0, selectX: 0, selectY: 0, selectedSeat: [], price:'', rowSize: '', seatTypeList: [], seatArea: '', timer: null, maxY:'', maxX:'', loadComplete:false, hidden:true }, /** * 生命周期函数--监听页面加载 */ onLoad: function (options) { that = this; let movie = JSON.parse(options.paramsStr); let price = movie.sellPrice; this.setData({ price: price, movie: movie, seatArea: app.globalData.screenHeight - app.globalData.statusBarHeight - (500 * app.globalData.screenWidth / 750), rpxToPx: app.globalData.screenWidth / 750 }) }, /** * 生命周期函数--监听页面初次渲染完成 */ onReady: function () { let seats = []; api.post('/api/cinema/trade_seat', { showId: this.data.movie.showId }).then(function (res) { seats =that.prosessSeats(res.seatList.rows); that.setData({ seatTypeList: res.seatTypeList, seats: seats, columnNumber: res.seatList.rowSize }) that.prosessMaxSeat(res.seatList); const query = wx.createSelectorQuery() query.select('#seatView').boundingClientRect() query.exec((r) => { let height = r[0].heighrt; let newHeight =that.data.seatScaleHeight; let top = r[0].top; that.setData({ lineHeight: newHeight, lineTop: top-80 }) }) }) }, prosessSeats:function(data){ let seatData=data; seatData.forEach(e => { e.columns.forEach(j=>{ if(j.status=="CAN_SELL"){ j.icon="/assets/icon/seatPre.png"; j.flag=0; }else if(j.status=="EMPTY"){ j.icon=""; }else{ j.flag=1; j.icon="/assets/icon/seatDone.png"; } }) }); return seatData; }, //计算最大座位数,生成影厅图大小 prosessMaxSeat: function(value) { let seatList = value let maxY = seatList.rowSize; let maxX = seatList.rows[0]['columns'].length; let seatRealWidth = parseInt(maxX) * 70 * this.data.rpxToPx let seatRealheight = parseInt(maxY) * 70 * this.data.rpxToPx let seatScale = 1; let seatScaleX = 1; let seatScaleY = 1; let seatAreaWidth = 630 * this.data.rpxToPx let seatAreaHeight = this.data.seatArea - 200 * this.data.rpxToPx if (seatRealWidth > seatAreaWidth) { seatScaleX = seatAreaWidth / seatRealWidth } if (seatRealheight > seatAreaHeight) { seatScaleY = seatAreaHeight / seatRealheight } if (seatScaleX < 1 || seatScaleY < 1) { seatScale = seatScaleX < seatScaleY ? seatScaleX : seatScaleY } this.setData({ maxY: parseInt(maxY), maxX: parseInt(maxX), seatScale: seatScale, seatScaleHeight: seatScale * 70 * this.data.rpxToPx, loadComplete:true }); }, //解决官方bug handleScale: function (e) { if (this.data.timer) { clearTimeout(this.data.timer) } let timer = setTimeout(() => { this.setData({ seatArea: this.data.seatArea }); }, 200) }, onScale(e) { const query = wx.createSelectorQuery() query.select('#seatView').boundingClientRect() query.exec((res) => { let height = res[0].height; let top = res[0].top; let newHeight = that.data.seatScaleHeight; this.setData({ lineHeight: newHeight, lineTop: top }) }) }, selectSeat(e) { let rowid = e.currentTarget.dataset.rowid; let columnid = e.currentTarget.dataset.columnid; let flag = e.currentTarget.dataset.flag; let seats=that.data.seats if(flag==1){ for (var a = 0; a < that.data.selectedSeat.length; a++) { if (that.data.selectedSeat[a].rowid == rowid&&that.data.selectedSeat[a].columnid==columnid) { that.data.selectedSeat.splice(a, 1); break; } } }else{ if(that.data.selectedSeat.length==4){ wx.showToast({ title: '最多只能选4个座哦~~', icon:'none' }) return; } let seat = { rowid:rowid, columnid:columnid }; that.data.selectedSeat.push(seat); } seats.forEach(e=>{ e.columns.forEach(c=>{ if(rowid==e.rowId&&c.columnId==columnid){ if(c.flag==1){ c.flag=0; c.icon="/assets/icon/seatPre.png"; }else if(c.flag==0){ c.flag=1; c.icon="/assets/icon/selectIcon.png"; } } }) }) let hidden=true; if(that.data.selectedSeat.length!=0){ hidden=false; } that.setData({ hidden:hidden, seats:seats, selectedSeat: that.data.selectedSeat, totalPrice: that.data.selectedSeat.length*that.data.price }) }, cancelSeat(e) { let rowid = e.currentTarget.dataset.rowid; let columnid = e.currentTarget.dataset.columnid; let seats=that.data.seats for (var a = 0; a < that.data.selectedSeat.length; a++) { if (that.data.selectedSeat[a].rowid == rowid&&that.data.selectedSeat[a].columnid==columnid) { that.data.selectedSeat.splice(a, 1); break; } } seats.forEach(e=>{ e.columns.forEach(c=>{ if(rowid==e.rowId&&c.columnId==columnid){ c.flag=0; c.icon="/assets/icon/seatPre.png"; } }) }) let hidden=false; if(that.data.selectedSeat.length==0){ hidden=true; } that.setData({ hidden:hidden, selectedSeat: that.data.selectedSeat, totalPrice: that.data.selectedSeat.length*that.data.price, seats:seats }) }, confirmHandle() { if(that.data.selectedSeat.length==0){ wx.showToast({ title: '至少得选1个座位哦~~', icon:'none' }) return; } } })

  select-seat.wxml

<!--电影信息--> <view class='info'> <view class='movieName'>{{movie.filmName}}</view> <view class='planDetail'>{{movie.startDate}} {{movie.startTime}}</view> <!-- 使用时注释dom view class='about' 即可 .about css 在app.wxss中 --> </view> <!--座位示例图 --> <view class="seatDemosBack" wx:if="{{loadComplete}}"> <view class="seatDemos"> <block wx:for="{{seatTypeList}}" wx:for-index="index" wx:for-item="seatTypeItem" wx:key="index"> <view class="seatDemo"> <image class="seatDemoItem" mode="widthFix" src="{{seatTypeItem.icon}}"></image> <view class="seatDemoItem"> {{seatTypeItem.name}}</view> </view> </block> </view> </view> <movable-area scale-area="true" class="defaultArea" style="height:{{seatArea}}px; width: 750rpx;"> <movable-view class='movableOne' bindscale="handleScale" style="height:{{seatArea}}px; width: 750rpx;" scale="true" direction="all" scale-max="2" scale-value="1" out-of-bounds="true"> <view class='seatArea' id="seatView" style='width:{{seatScaleHeight * maxX}}px;height:{{seatScaleHeight * maxY}}px'> <view class='hallName'> <text>{{movie.hallName}}</text> </view> <view class='x' wx:for="{{seats}}" wx:key="keys" wx:for-item="item" wx:for-index="idx"> <view class='y' wx:for="{{item.columns}}" wx:key="items" wx:for-index="idy" wx:for-item="items" style="left:{{(items.columnId-1)* seatScaleHeight}}px;top:{{(item.rowNum-1) * seatScaleHeight}}px;width: {{seatScaleHeight}}px;height: {{seatScaleHeight}}px"> <image wx:if="{{items.status!='EMPTY'&&items.status!='CAN_SELL'}}" class="img" src='{{items.icon}}' data-rowid='{{item.rowId}}' data-flag="{{items.flag}}" data-index="{{idy}}" data-columnid='{{items.columnId}}'></image> <image wx:elif="{{items.status=='EMPTY'}}" class="img" src=''></image> <image wx:elif="{{items.status=='CAN_SELL'}}" class="img" bind:tap='selectSeat' src='{{items.icon}}' data-rowid='{{item.rowId}}' data-flag="{{items.flag}}" data-index="{{idy}}" data-columnid='{{items.columnId}}'></image> </view> </view> </view> </movable-view> </movable-area> <!--下部分座位示例图 --> <!-- 用户选中的座位详情 --> <view class='selectSeatInfo' hidden='{{hidden}}'> <scroll-view class="scrollSeat" scroll-x style="width: 100%"> <!-- 普通座位 --> <block wx:for="{{selectedSeat}}" wx:key="id" wx:for-item="selectedSeatItem"> <view class='scrollItem' bindtap='cancelSeat' data-rowid="{{selectedSeatItem.rowid}}" data-columnid="{{selectedSeatItem.columnid}}"> <view class='scrollTextTop'> {{selectedSeatItem.rowid}}排{{selectedSeatItem.columnid}}座 </view> <view class='scrollTextBottom'> ¥{{price}} </view> <image src='/assets/images/close.png'></image> </view> </block> </scroll-view> </view> <!-- 快速选座 --> <view class='selectSeatInfo' hidden='{{!hidden}}'> <scroll-view class="scrollSeat" scroll-x style="width: 100%"> <view class='quickItem' bindtap='quickSeat' data-num='1'> 1人座 </view> <view class='quickItem' bindtap='quickSeat' data-num='2'> 2人座 </view> <view class='quickItem' bindtap='quickSeat' data-num='3'> 3人座 </view> <view class='quickItem' bindtap='quickSeat' data-num='4'> 4人座 </view> </scroll-view> </view> <!-- 以下是确认选座 --> <view class='orderComfirm' style="flex-direction:row;"> <view class='comfirm' bindtap='confirmHandle'>¥ <text>{{totalPrice}}</text> 元 确认选座</view> </view>

select-seat.wxss

/* *@zenghao 2018-06-12 */ page { background: #eee; } /* *上方影片名称样式 */ @import "/style/main.wxss"; @import "/style/icon.wxss"; .movieName { font-size: 35rpx; font-weight: 600; margin-bottom: 10rpx; } /* *上方排期信息样式 */ .planDetail { color: #aaa; font-size: 27rpx; } /* *上方影片,排期信息的父级 */ .info { width: 100%; height: 80rpx; background: #fff; border-top: 1rpx solid #eee; border-bottom: 1rpx solid #eee; padding: 30rpx 30rpx; position: relative; } .seatDemosBack { background: #fff; } /* *座位样式的父级 */ .seatDemos { color: #aaa; background: #fff; position: relative; margin: 0 auto; width: 80%; box-sizing: border-box; font-size: 25rpx; height: 70rpx; display: flex; flex-direction: row; justify-content: space-between; align-items: center; } .areaSeatDemos { width: 100%; height: 80rpx; position: fixed; bottom: 100rpx; } .seatDemosScroll { color: #aaa; background: #fff; width: 100%; height: 100%; font-size: 25rpx; white-space: nowrap; } .seatDemosScrollItem { height: 90rpx; white-space: nowrap; overflow: hidden; margin-left: 15px; display: inline-block; align-items: center; margin-top: 25rpx; } .seatDemosScrollItem.Itemnormal image { margin-right: 10rpx; width: 40rpx; height: 40rpx; vertical-align: -30%; } /* *情侣座位的图片样式 */ .seatDemosScrollItem.Itemlove image { margin: 0; width: 40rpx; height: 40rpx; vertical-align: -30%; } /* *情侣座位的字体样式 */ .seatDemosScrollItem.Itemlove text { margin-left: 10rpx; } .seatDemo image { width: 45rpx; height: 45rpx; } /* *普通座位的图片样式 */ .seatDemoItem { white-space: nowrap; width: 45rpx; display: block; } /* *情侣座位的图片样式 */ .seatDemo { display: flex; align-items: center; } /* *情侣座位的字体样式 */ .loveSeatDemo text { margin-left: 10rpx; } /* *影厅图上方显示影厅名字区域 */ .hallName { width: 200rpx; height: 0; border-top: 40rpx solid #ccc; border-right: 20rpx solid transparent; border-left: 20rpx solid transparent; line-height: 30rpx; color: white; position: absolute; top: -100rpx; z-index: 2; left: 50%; transform: translateX(-50%); white-space: nowrap; } /* *影厅图上方显示影厅名字区域字体样式 */ .hallName text { font-size: 20rpx; position: absolute; left: 50%; transform: translateX(-50%); top: -35rpx; } /* *所有座位的区域 */ .seatArea { margin: 0 auto; font-size: 10rpx; position: relative; } /* *中轴线 */ /* .alignLine { position: absolute; left: 50%; height: 100%; border-left: 1px dashed #aaa; transform: translateX(-100%); } */ /* *选座区域普通座位的图片样式 */ .normal { position: relative; /* margin: 10rpx; */ width: 100%; height: 100%; } /* *选座区域情侣座位的图片样式 */ .LoveSeat { position: relative; /* margin: 10rpx 0; */ width: 70rpx; height: 70rpx; } /* *所有座位的图片样式下方透明可点击区域 */ .seatTap { position: absolute; } /* *情侣座位的图片样式下方透明可点击区域 */ .LoveSeatTap { position: absolute; top: 0; width: 70rpx; height: 70rpx; } /* * 座位图限制区域 */ movable-area { background: #eee; overflow: hidden; } /* * 座位图可移动区域(座位图) */ .movableOne { box-sizing: border-box; padding: 100rpx 60rpx; color: #fff; } /* * 座位图可移动区域(左边座位排号栏) */ .movableTwo { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 1400rpx; width: 30rpx; padding: 100rpx 0; color: #fff; } /* * (左边座位排号栏整体) */ .seatTool { width: 30rpx; padding: 100rpx 0; opacity: 0.5; } .seatToolArea { background: rgba(0, 0, 0, 0.2); border-radius: 50rpx; } /* * (左边座位排号栏每一个块) */ .seatTag { text-align: center; color: rgba(0, 0, 0, 0.5); } /* *页面最下方确认选座区域 */ .orderComfirm { background: #fff; position: fixed; display: flex; bottom: 0rpx; width: 100%; line-height: 100rpx; z-index: 3; } /* *页面最下方价格区域 */ .orderPrice { text-indent: 30rpx; color: black; height: 100rpx; width: 60%; } /* *页面最下方价格字体样式 */ .orderPrice text { color: red; } /* *页面最下方确认选座区域渐变色 */ .comfirm { font-weight: 900; text-align: center; color: white; width: 100%; background: linear-gradient(to right, #C26DFE, #6F50F5); height: 100rpx; } /* *用户选中的座位区域 */ .selectSeatInfo { background: #fff; position: fixed; bottom: 100rpx; height: 80rpx; width: 100%; padding: 10rpx 0; } /* *用户选中的座位详情滑块 */ .scrollSeat { height: 90rpx; white-space: nowrap; } /* *每块用户选中的座位详情 */ .scrollItem { border: 1rpx solid #bbb; border-radius: 10rpx; width: 180rpx; display: inline-block; margin-left: 20rpx; position: relative; } /* * 快速选座模块 */ .quickItem { border: 1rpx solid #bbb; color: #aaa; font-size: 28rpx; border-radius: 10rpx; width: 160rpx; height: 75rpx; line-height: 75rpx; text-align: center; display: inline-block; margin-left: 20rpx; position: relative; } /* *每块用户选中的座位详情上方文字 */ .scrollTextTop { color: #555; text-indent: 30rpx; line-height: 25rpx; font-size: 26rpx; height: 25rpx; margin-top: 10rpx; } /* *每块用户选中的座位详情下方文字 */ .scrollTextBottom { font-weight: 600; font-size: 26rpx; color: #ff005a; text-indent: 40rpx; height: 25rpx; line-height: 25rpx; margin: 10rpx 0; } /* *每块用户选中的座位详情关闭按钮 */ .scrollItem image { position: absolute; z-index: 2; width: 30rpx; height: 30rpx; right: 10rpx; top: 50%; transform: translateY(-50%); } .seatAreaTip { position: relative; width: 750rpx; height: 50rpx; line-height: 50rpx; text-align: center; font-size: 16rpx; color: rgba(0, 0, 0, 0.1); border-top: 1rpx dashed rgba(0, 0, 0, 0.1); } .x { display: flex; flex-direction: row; } .y { display: flex; flex-direction: column; } .img { margin: 5rpx; height: 60rpx; width: 60rpx; } .imgSelect { margin: 5rpx; height: 60rpx; width: 60rpx; background: #fd4f68; } .line { opacity: 0.5; display: flex; flex-direction: column; justify-content: center; position: fixed; left: 5rpx; background: #585656; } .text { opacity: 0.5; margin: 5rpx; text-align: center; width: 60rpx; font-size: auto; } .hallTitle { display: flex; flex-direction: row; align-items: center; justify-content: center; position: fixed; width: 460rpx; background: #e2e2e2; border-bottom-left-radius: 90rpx; border-bottom-right-radius: 90rpx; } .hallTitle text { padding: 10rpx; font-size: 28rpx; color: #666; text-align: center; }

 select-seat.json

{ "disableScroll": true, "navigationBarTitleText": "选座购票" }

app.js

//app.js const QQMapWX = require('./assets/libs/qqmap-wx-jssdk.min.js'); let qqmapsdk; qqmapsdk = new QQMapWX({ key: '********************************' }); App({ onLaunch: function () { this.initPage() }, initPage(){ // 获取用户授权信息信息,防止重复出现授权弹框 wx.getSetting({ success: res => { //已有权限直接获得信息,否则出现授权弹框 if (res.authSetting['scope.userLocation']) { this.getUserLocation() } else { this.getUserLocation() } } }) wx.getSystemInfo({ success: res => { this.globalData.screenHeight = res.screenHeight; this.globalData.screenWidth = res.screenWidth; this.globalData.statusBarHeight = res.statusBarHeight } }) }, //获取用户的位置信息 getUserLocation() { wx.getLocation({ //成功授权 success: (res) => { const latitude = res.latitude; const longitude = res.longitude; // 使用腾讯地图接口将位置坐标转出成名称(为什么弹框出出现两次?) qqmapsdk.reverseGeocoder({ location: { //文档说location默认为当前位置可以省略,但是还是要手动加上,否则弹框会出现两次,手机端则出现问题 latitude, longitude }, success: (res) => { const cityFullname = res.result.address_component.city; const location_id=res.result.ad_info.city_code; const cityInfo = { latitude, longitude, cityName: cityFullname.substring(0, cityFullname.length - 1), status:1, location_id:location_id.substr(3) } this.globalData.userLocation = { ...cityInfo} //浅拷贝对象 this.globalData.selectCity = { ...cityInfo } //浅拷贝对象 // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回,所以此处加入 callback 以防止这种情况 if (this.userLocationReadyCallback) { this.userLocationReadyCallback() } } }) }, fail:()=>{ this.globalData.userLocation = {status:0} this.globalData.selectCity.location_id=false //防止当弹框出现后,用户长时间不选择, if (this.userLocationReadyCallback) { this.userLocationReadyCallback() } } }) }, globalData: { userLocation: null, //用户的位置信息 selectCity: {location_id:150100}, //用户切换的城市 host:"https://movie.menggouyi.com", screenHeight:null, screenWidth:null, statusBarHeight:null } })

 

最新回复(0)