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

 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.xxx.com",
    screenHeight:null,
    screenWidth:null,
    statusBarHeight:null

  }
})

猜你喜欢

转载自blog.csdn.net/u013373006/article/details/108393371