<template>
<div class="wrapper">
<div class="cinema-wrapper">
<h1 class="title">電影院推薦座位功能</h1>
<div class="btn-wrapper">
<div class="btn-buy" @click="buySeat">
選定座位
</div>
<div class="btn-buy" @click="resetSeat">
重置座位
</div>
<!--智能選擇-->
<template v-for="(item,index) in smartChooseMaxNum">
<div class="btn-buy smart" @click="smartChoose(index+1)">
推薦選座{{index+1}}人
</div>
</template>
</div>
<div class="seat-wrapper">
<div class="illustration">
<div class="illustration-img-wrapper unselected-seat">
</div>
<span class="illustration-text">可選</span>
<div class="illustration-img-wrapper selected-seat">
</div>
<span class="illustration-text">已選</span>
<div class="illustration-img-wrapper bought-seat">
</div>
<span class="illustration-text">不可選</span>
</div>
<div class="screen">
3號激光廳銀幕
</div>
<div class="screen-center">
銀幕中央
<div class="mid-line"></div>
</div>
<div class="inner-seat-wrapper" ref="innerSeatWrapper" >
<div v-for="row in seatRow">
<!--這裏的v-if很重要,若是沒有則會致使報錯,由於seatArray初始狀態爲空-->
<div v-for="col in seatCol"
v-if="seatArray.length>0"
class="seat"
:style="{width:seatSize+'px',height:seatSize+'px'}">
<div class="inner-seat"
@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>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'cinemaSeatChoose',
data () {
return {
//影院座位的二維數組,-1爲非座位,0爲未購座位,1爲已選座位(綠色),2爲已購座位(紅色)
seatArray:[],
//影院座位行數
seatRow:10,
//影院座位列數
seatCol:20,
//座位尺寸
seatSize:'',
//推薦選座最大數量
smartChooseMaxNum:5
}
},
computed:{
},
methods:{
//向先後某個方向進行搜索的函數,參數是起始行,終止行,推薦座位個數
searchSeatByDirection: function(fromRow,toRow,num){
/*
* 推薦座位規則
* (1)初始狀態從座位行數的一半處的後一排的中間開始向左右分別搜索,取離中間最近的,若是知足條件,
* 記錄下該結果離座位中軸線的距離,後排搜索完成後取距離最小的那個結果座位最終結果,優先向後排進行搜索,
* 後排都沒有才往前排搜,前排邏輯同上
*
* (2)只考慮並排且連續的座位,不能不在一排或者一排中間有分隔
*
* */
/*
* 保存當前方向搜索結果的數組,元素是對象,result是結果數組,offset表明與中軸線的偏移距離
* {
* result:Array([x,y])
* offset:Number
* }
*
*/
let currentDirectionSearchResult = [];
let largeRow = fromRow>toRow?fromRow:toRow,
smallRow = fromRow>toRow?toRow:fromRow;
for(let i=smallRow;i<=largeRow;i++){
//每一排的搜索,找出該排裏中軸線最近的一組座位
let tempRowResult = [],
minDistanceToMidLine=Infinity;
for(let j=0;j<=this.seatCol - num;j++){
//若是有合法位置
if(this.checkRowSeatContinusAndEmpty(i,j,j+num-1)){
//計算該組位置距離中軸線的距離:該組位置的中間位置到中軸線的距離
let resultMidPos = parseInt((j+num/2),10);
let distance = Math.abs(parseInt(this.seatCol/2) - resultMidPos);
//若是距離較短則更新
if(distance<minDistanceToMidLine){
minDistanceToMidLine = distance;
//該行的最終結果
tempRowResult = this.generateRowResult(i,j,j+num-1)
}
}
}
//保存該行的最終結果
currentDirectionSearchResult.push({
result:tempRowResult,
offset:minDistanceToMidLine
})
}
//處理後排的搜索結果:找到距離中軸線最短的一個
//注意這裏的邏輯須要區分先後排,對於後排是從前日後,前排則是從後往前找
let isBackDir = fromRow < toRow;
let finalReuslt = [],minDistanceToMid = Infinity;
if(isBackDir){
//後排狀況,從前日後
currentDirectionSearchResult.forEach((item)=>{
if(item.offset < minDistanceToMid){
finalReuslt = item.result;
minDistanceToMid = item.offset;
}
});
}else{
//前排狀況,從後往前找
currentDirectionSearchResult.reverse().forEach((item)=>{
if(item.offset < minDistanceToMid){
finalReuslt = item.result;
minDistanceToMid = item.offset;
}
})
}
//直接返回結果
return finalReuslt
},
//推薦選座,參數是推薦座位數目
smartChoose: function(num){
//找到影院座位水平垂直中間位置的後一排
let rowStart = parseInt((this.seatRow-1)/2,10)+1;
//先從中間排日後排搜索
let backResult = this.searchSeatByDirection(rowStart,this.seatRow-1,num);
if(backResult.length>0){
this.chooseSeat(backResult);
return
}
//再從中間排往前排搜索
let forwardResult = this.searchSeatByDirection(rowStart-1,0,num);
if(forwardResult.length>0) {
this.chooseSeat(forwardResult);
return
}
//提示用戶無合法位置可選
alert('無合法位置可選!')
},
/*輔助函數,判斷每一行座位從i列到j列是否所有空餘且連續
*
*/
checkRowSeatContinusAndEmpty: function(rowNum,startPos,endPos){
let isValid = true;
for(let i=startPos;i<=endPos;i++){
if(this.seatArray[rowNum][i]!==0){
isValid=false;
break;
}
}
return isValid
},
//輔助函數:返回每一行的某個合理位置的座位數組
generateRowResult: function(row,startPos,endPos){
let result = [];
for(let i=startPos;i<=endPos;i++){
result.push([row,i])
}
return result
},
//輔助函數:智能推薦的選座操做
chooseSeat: function(result){
let oldArray = this.seatArray.slice();
for(let i=0;i<result.length;i++){
//選定座位
oldArray[result[i][0]][result[i][1]] = 1
}
this.seatArray = oldArray;
},
//重置座位
resetSeat: function(){
//將全部座位的值變爲0
let oldArray = this.seatArray.slice();
for(let i=0;i<this.seatRow;i++){
for(let j=0;j<this.seatCol;j++){
if(oldArray[i][j]!==-1){
oldArray[i][j]=0
}
}
}
this.seatArray = oldArray;
},
//選定且購買座位
buySeat: function(){
//遍歷seatArray,將值爲1的座位變爲2
let oldArray = this.seatArray.slice();
for(let i=0;i<this.seatRow;i++){
for(let j=0;j<this.seatCol;j++){
if(oldArray[i][j]===1){
oldArray[i][j]=2
}
}
}
this.seatArray = oldArray;
},
//處理座位選擇邏輯
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();
},
//初始座位數組
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;
}
}
},
mounted:function(){
this.initSeatArray(10,20)
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.wrapper{
height:100%;
padding: 40px;
box-sizing: border-box;
}
.cinema-wrapper{
height:100%;
}
.title{
text-align: center;
}
.seat-wrapper{
height:700px;
width:1000px;
border:1px dotted #c5c5c5;
margin: 0 auto;
position: relative;
overflow: hidden;
}
.screen{
margin: 0 auto;
height:30px;
width:300px;
background-color: #e3e3e3;
border-radius: 0 0 30px 30px;
color: #585858;
line-height: 30px;
text-align: center;
}
.inner-seat-wrapper{
position: absolute;
top:120px;
bottom:0;
width:100%;
box-sizing: border-box;
}
.seat{
float:left;
display: flex;
justify-content: center;
align-items: center;
}
.inner-seat{
width:80%;
height:80%;
cursor: pointer;
}
.selected-seat{
background: url('./../assets/selected.png') center center no-repeat;
background-size: 100% 100%;
}
.unselected-seat{
background: url('./../assets/unselected.png') center center no-repeat;
background-size: 100% 100%;
}
.bought-seat{
background: url('./../assets/bought.png') center center no-repeat;
background-size: 100% 100%;
}
.screen-center{
position: absolute;
left:50%;
transform: translateX(-50%);
padding:5px;
font-size: 13px;
border-radius: 5px;
top:50px;
background-color: #f6f6f6;
color: #636363;
border:1px solid #b1b1b1;
}
.mid-line{
position: absolute;
left:50%;
transform: translateX(-50%);
top:100%;
width:1px;
height:800px;
border-left:1px dashed #919191;
}
.btn-wrapper{
margin: 20px auto;
width:1000px;
height:30px;
text-align: center;
}
.btn-buy{
height:100%;
line-height: 30px;
font-size: 14px;
border-radius: 5px;
padding:0 5px;
background-color: #ffa349;
color: #ffffff;
display: inline-block;
cursor: pointer;
margin-right: 10px;
}
.smart{
background-color: #39ac6a;
}
.illustration{
position: absolute;
left:0;
top:0;
height:35px;
width:300px;
}
.illustration-img-wrapper{
width:25px;
height:25px;
position: relative;
top:50%;
display: inline-block;
transform: translateY(-50%);
margin-left: 10px;
}
.illustration-text{
display: inline-block;
height:100%;
line-height: 35px;
font-size: 14px;
position: relative;
top:-2px;
}
</style>
複製代碼