Vue实现美团app的影院推荐选座功能【推荐】

经常用美团app买电影票,不禁对它的推荐选座功能产生了好奇,于是打算自己实现一个类似的算法,美团app的推荐选座界面如下

Vue实现美团app的影院推荐选座功能【推荐】

 

最多可以选5个座位,本demo的选座界面如下图

Vue实现美团app的影院推荐选座功能【推荐】

 

上图是点击 推荐选座5人

后选出的座位(绿色),这个demo和美团app不同的地方在于可以连续进行推荐选座,美团app点击了推荐选座就必须买票才能继续选择。

本demo采用Vue-cli搭建,github地址点此 ,clone后直接npm start即可进行具体操作

算法思考过程

对于这个推荐座位算法,我尝试了不同场次的电影进行推荐选座,总结出以下几点

(1)推荐算法首先从影院中间排数的后一排的正中间开始搜索如下图

Vue实现美团app的影院推荐选座功能【推荐】

 

可以确定是这个逻辑,试了其他几个场次也是一样

(2)优先向后排方向进行搜索,后排搜索完成后再从中间起始位置向前排搜索这个大多数情况是对的,如下图,偶尔会出现不同

Vue实现美团app的影院推荐选座功能【推荐】

 

(3)后排搜索完成后,每一行都会有一个结果(每一行的结果是最靠近中轴线的那一组座位),取这些结果中距离中轴线最小的那个结果作为最终结果,而不是距离屏幕越近的

这一点也是大多数情况是对的,有些情况不对,很奇怪

(4)只考虑并排且连续的座位,不能不在一排或者一排中间有分隔,比如过道之类的这一条可以理解,毕竟坐一排肯定观影体验好得多

影院座位数据结构

可以肯定的是用一个二维数组 seatArray 代表影院座位,注意到影院座位分布是不规则的,因此需要确定一个 seatRow 和 seatCol 来确定影院座位的数组尺寸,分别代表行列数,对于那些没有座位的地方, seatArray 对应的位置填-1,下面是座位具体的值和代表的含义

-1 非座位
0  可选座位   (白色)
1  已选座位   (绿色)
2  已购票座位 (红色)

然后在mounted中初始化座位,初始值都为0(可选座位),如下代码

//初始座位数组 initSeatArray: function(){ let seatArray = Array(this.seatRow).fill(0).map(()=>Array(this.seatCol).fill(0)); this.seatArray = seatArray; //均分父容器宽度作为座位的宽度 this.seatSize = this.$refs.innerSeatWrapper ? parseInt(parseInt(window.getComputedStyle(this.$refs.innerSeatWrapper).width,10) / this.seatCol,10) :0; //初始化不是座位的地方 this.initNonSeatPlace(); }, //初始化不是座位的地方 initNonSeatPlace: function(){ for(let i=0;i<9;i++){ this.seatArray[i][0]=-1; } for(let i=0;i<8;i++){ this.seatArray[i][this.seatArray[0].length-1]=-1; this.seatArray[i][this.seatArray[0].length-2]=-1; } for(let i=0;i<9;i++){ this.seatArray[i][this.seatArray[0].length-3]=-1; } for(let i=0;i<this.seatArray[0].length;i++){ this.seatArray[2][i]=-1; } }

初始化好之后用一个二重循环来构建html结构,2个v-for嵌套循环出整个结构,如下代码

<div ref="innerSeatWrapper" > <div v-for="row in seatRow"> <!--这里的v-if很重要,如果没有则会导致报错,因为seatArray初始状态为空--> <div v-for="col in seatCol" v-if="seatArray.length>0" :style="{width:seatSize+'px',height:seatSize+'px'}"> <div @click="handleChooseSeat(row-1,col-1)" v-if="seatArray[row-1][col-1]!==-1" :class="seatArray[row-1][col-1]===2?'bought-seat':(seatArray[row-1][col-1]===1?'selected-seat':'unselected-seat')"> </div> </div> </div> </div>

上述的inner-seat类的div就是具体的座位div,v-if说明了如果是-1也就是是过道之类的就不渲染,然后:class一句控制了该座位对应状态的类的值,一个嵌套三目运算符来控制,对于每个座位绑定点击事件 handleChooseSeat(row-1,col-1) 进行状态切换

//处理座位选择逻辑 handleChooseSeat: function(row,col){ let seatValue = this.seatArray[row][col]; let newArray = this.seatArray; //如果是已购座位,直接返回 if(seatValue===2) return //如果是已选座位点击后变未选 if(seatValue === 1){ newArray[row][col]=0 }else if(seatValue === 0){ newArray[row][col]=1 } //必须整体更新二维数组,Vue无法检测到数组某一项更新,必须slice复制一个数组才行 this.seatArray = newArray.slice(); },

这里注意vue中改变data中的二维数组必须先缓存二维数组,修改后,最终将二维数组重新赋值,否则修改不生效,因为Vue无法侦测到数组内的变动。

推荐座位的具体代码

首先给每个推荐座位的按钮绑定事件smartChoose

 

代码如下

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/2af084c17b29f640ef7d406947c59ae9.html