功能展示:
1. 默认界面
2.场次的下拉表单
3.放映影厅的下拉表单
4.选择座位的功能
5.最多选择十个座位,座位的盒子用flex布局
.box{
display: flex;
width: 465px;
flex-flow: wrap;
}
6.取消座位
7.切换场次,显示不同的座位表
8.切换影厅,显示不同的座位表
9.包场按钮,只有全场都是空座的时候才能选择
10.点击包场按钮
实现思路:
如何实现两个下拉表单的改变从而改变座位表:
// 下拉表单设置一个点击事件 改变便利的数组下标 数组下标可以放在data里
//n表示第n天,m是第m厅
n:1,
m:1,
seat:[
[//第一天
[//一号厅
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,2,2,0,0,0,0,
0,0,0,2,2,0,2,2,0,0,
-1,0,0,0,0,0,0,0,0,-1,
-1,-1,0,0,0,0,0,0,-1,-1,
],
[//二号厅
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,2,2,0,0,0,0,
0,0,0,2,2,0,2,2,0,0,
-1,0,0,0,0,0,0,0,0,-1,
-1,-1,0,0,0,0,0,0,-1,-1,
]
],
[//第二天
[//一号厅
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,2,2,0,0,0,0,
0,0,0,2,2,0,2,2,0,0,
-1,0,0,0,0,0,0,0,0,-1,
-1,-1,0,0,0,0,0,0,-1,-1,
],
[//二号厅
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,2,2,0,0,0,0,
0,0,0,2,2,0,2,2,0,0,
-1,0,0,0,0,0,0,0,0,-1,
-1,-1,0,0,0,0,0,0,-1,-1,
]
]
]
time:["2020.5.18","2020.5.19","2020.5.20"]
room:["一号影厅","二号影厅"]
//seat[第n天][第m厅][座位号]
首先用一个三维数组表示不同的场次,影厅,座位
用n代表场次的下标,m是代表影厅的下表,那么调用不同的座位表就是seat[n][m],选择不同的作为就是seat[n][m][index](n和m是响应式变量,要传到html中进行控制)
然后如何将下拉表单的改变同步座位表的改变呢?首先我想到把select->option中加一个@click的事件,后来发现,option没有点击事件,所以不行,但是section有一个change事件,只要value改变就会触发,所以再声明两个变量timeString和roomString,使用v-model和select绑定,目的是为了选择不同的下拉选项从而改变seat[n][m]的n和m从而该表座位表
<p>放映影厅:
<select v-model="roomString" @change="changeM()">
<option v-for = "(roomItem,roomIndex) in filmInfo.room" >{{roomItem}}
</option>
</select>
</p>
如果选择不同的下拉表单,roomString改变,因为改变,出发change事件,再将room数组里存储的信息和roomString比对,从而获得改变之后的下标,再赋值给m。
const changeM = ()=>{
state.filmInfo.room.forEach((item,index)=>{
if(state.roomString === item){
state.m = index;
}
})
这里会产生一些问题,比如在第一场选择后座位,切换到第二场选择座位,回来再看第一场发现,那些座位还是被选中的状态,这是不希望出现的,所以需要每次切换影厅或者场次的时候进行初始化操作,把所有被选中的取消。
const initSeat = ()=>{
for(let i = 0;i<state.seat[state.n][state.m].length;i++){
if(state.seat[state.n][state.m][i]==1)
state.seat[state.n][state.m][i]=0;
}
}
如何实现包场按钮
包场按钮如果只是点击之后全选座位的话比较好实现,但是因为增添了要只有空座才能点击,并且要在座位的盒子里添加”包场“所以需要很多初始化的操作。
包场的具体业务逻辑就是,先检测座位表内可以选择座位的个数,如果等于座位的总数(seat[n][m].length)那么包场按钮的disabled就是false
需要一个函数判断是否可以包场:
const ifFlag = ()=>{
state.counter=0;
for(let i = 0;i<state.seat[state.n][state.m].length;i++){
if(state.seat[state.n][state.m][i]===0)
state.counter++;
}
if(state.counter==state.seat[state.n][state.m].length){
state.btnFlag = false;
}
else {
state.btnFlag = true;
}
}//判断能不能包场
包场按钮的点击事件:
const choseAll = (()=>{
for(let i = 0;i<state.seat[state.n][state.m].length;i++){
if(state.seat[state.n][state.m][i]==1||state.seat[state.n][state.m][i]==0)
state.seat[state.n][state.m][i]=state.seat[state.n][state.m][i]==1?0:1;
}
if(state.flag==1){
state.curSeat.push("包场");
state.flag=0;
state.count = 110;
totalPrice = 110*38;
}
else {
state.curSeat = [];
state.flag=1;
state.count = 0;
totalPrice = 0;
}
})
但其实包场按钮还是有一些bug的,还需要完善
总体的代码
<template>
<div class="film">
<div class="filmLeft">
<h3>屏幕</h3>
<ul>
<li v-for="(item,index) in seat[n][m]" class = "seat"
:class ="{'noSeat':seat[n][m][index]==-1,
'seatSpace':seat[n][m][index]==0,
'seatActive':seat[n][m][index]==1,
'seatNoUse':seat[n][m][index]==2}"
@click = "choseSeat(index)">
</li>
</ul>
<button :disabled = "btnFlag" @click = "choseAll" >包场</button>
</div>
<div class="filmRight">
<div class="rightTop">
<div class="rightTopLeft">
<a href="#">
<img :src="filmInfo.filmImg" width = "200" height="200" >
</a>
</div>
<div div class="rightTopRight">
<p>影片中文名:<strong>{{filmInfo.name}}</strong></p>
<p>影片英文名:{{filmInfo.nameEnglish}}</p>
<p>影片类型:{{filmInfo.storyType}}</p>
<p>版本:{{filmInfo.copyRight}}</p>
<p>{{filmInfo.place}}/{{filmInfo.timeLength}}</p>
<p>{{filmInfo.timeShow}}</p>
</div>
</div>
<div class="rightBottom">
<p>电影院:{{filmInfo.cinema}}</p>
<p>场次:
<select v-model="timeString" @change="changeN()">
<option v-for = "(timeItem,timeIndex) in filmInfo.time" >{{timeItem}}</option>
</select>
</p>
<p>放映影厅:
<select v-model="roomString" @change="changeM()">
<option v-for = "(roomItem,roomIndex) in filmInfo.room" >{{roomItem}}</option>
</select>
</p>
<p>座位:
<div class="box">
<span class="smSeat" v-for="item in curSeat">{{item}}</span>
</div>
</p>
<p>已选择<strong style="color:red;">{{count}}</strong>个座位,
<strong style="color:red;">再次点击座位可取消
<span v-show="maxFlag">你最多选择十个座位</span>
</strong>
</p>
<hr>
<p>单价:<strong>{{filmInfo.price}}</strong></p>
<p>总价:<strong>{{totalPrice}}</strong></p>
</div>
</div>
</div>
</template>
<script>
import { reactive, toRefs, computed ,watch,onMounted} from "vue";
export default {
setup(){
const state = new reactive({
count:0, //选择座位的数量
counter:0,//可选座位
maxFlag:false,
btnFlag:true,
flag : 1,
n:0,
m:0,
timeString:"2020.5.18",
roomString:"一号影厅",
filmInfo: {
name:"囧妈",
nameEnglish: "Lost in Russia",
copyRight:'中文2D',
filmImg:require("@/assets/film.jpg"),
storyType:'喜剧',
place:"中国内地",
timeLength:"126分钟",
timeShow:"2020-2-2",
cinema:"万达影城",
// room:["一号影厅","二号影厅","三号影厅","四号影厅"]
price:38,
room:["一号影厅","二号影厅","三号影厅"],
// time:[],
time:["2020.5.18","2020.5.19","2020.5.20"]
},
seatIndex:[],//按顺序存放座位的在seatFlag的下标
curSeat:[],//存放对应的座位号 是字符串
seat:[
[//第一天
[//一号厅
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,2,0,0,0,0,0,
0,0,0,0,2,0,0,0,0,0,
0,0,0,0,2,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,2,2,0,0,0,0,
0,0,0,2,2,0,2,2,0,0,
-1,0,0,0,0,0,0,0,0,-1,
-1,-1,0,0,0,0,0,0,-1,-1,
],
[//二号厅
-1,-1,-1,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,2,2,0,0,0,0,
0,0,0,2,2,0,2,2,0,0,
-1,0,0,0,0,0,0,0,0,-1,
-1,-1,0,0,0,0,0,0,-1,-1,
],
[//三号厅
0,0,0,0,0,0,0,0,0,-1,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,2,2,0,0,0,0,0,0,
0,0,2,2,2,2,2,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,2,2,0,0,0,0,
0,0,0,2,2,0,2,2,0,0,
-1,0,0,0,0,0,0,0,0,-1,
-1,-1,0,0,0,0,0,0,-1,-1,
],
],
[//第二天
[//一号厅
-1,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,2,2,0,0,0,0,0,0,
0,0,2,2,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,2,2,0,0,0,0,
0,0,0,2,2,0,2,2,0,0,
-1,0,0,0,0,0,0,0,0,-1,
-1,-1,0,0,0,0,0,0,-1,-1,
],
[//二号厅
2,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,2,2,2,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,2,0,0,
0,0,0,0,0,0,0,2,0,0,
0,0,0,0,2,2,0,0,0,0,
0,0,0,2,2,0,2,2,0,0,
-1,0,0,0,0,0,0,0,0,-1,
-1,-1,0,0,0,0,0,0,-1,-1,
],
[//三号厅
2,2,0,0,0,0,0,0,0,-1,
2,2,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,2,2,0,0,0,0,0,0,
0,0,2,2,2,2,2,0,0,0,
0,0,0,0,0,2,0,0,0,0,
0,0,0,0,0,2,0,0,0,0,
0,0,0,0,2,2,0,0,0,0,
0,0,0,2,2,0,2,2,0,0,
-1,0,0,0,0,0,0,0,0,-1,
-1,-1,0,0,0,0,0,0,-1,-1,
],
],
[//第三天
[//一号厅
-1,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,2,2,0,0,0,0,
0,0,0,0,2,2,0,0,0,0,
0,0,0,0,2,2,0,0,0,0,
0,0,0,0,2,2,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,2,2,0,0,0,0,
0,0,0,2,2,0,2,2,0,0,
-1,0,0,0,0,0,0,0,0,-1,
-1,-1,0,0,0,0,0,0,-1,-1,
],
[//二号厅
2,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,2,2,0,
0,0,0,0,0,0,2,2,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,2,2,0,0,0,0,
0,0,0,2,2,0,2,2,0,0,
-1,0,0,0,0,0,0,0,0,-1,
-1,-1,0,0,0,0,0,0,-1,-1,
],
[//三号厅
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,
],
]
]
})
const ifFlag = ()=>{
state.counter=0;
for(let i = 0;i<state.seat[state.n][state.m].length;i++){
if(state.seat[state.n][state.m][i]===0)
state.counter++;
}
if(state.counter==state.seat[state.n][state.m].length){
state.btnFlag = false;
}
else {
state.btnFlag = true;
}
}//判断能不能包场
const initSeat = ()=>{
for(let i = 0;i<state.seat[state.n][state.m].length;i++){
if(state.seat[state.n][state.m][i]==1)
state.seat[state.n][state.m][i]=0;
}
}
const choseSeat = (index)=>{
// console.log(state.curSeat);
// console.log(`选择了${state.count}个座位`);
if(state.seat[state.n][state.m][index]===1){
state.seat[state.n][state.m][index] = 0;
state.seatIndex.splice(state.seatIndex.findIndex((item)=>item==index),1);//把当前座位删掉
}
else{
if(state.seat[state.n][state.m][index]===0&&state.count<10){
state.seat[state.n][state.m][index] =1;
state.seatIndex.push(index);
}
}
state.curSeat = []
for(let data of state.seatIndex){
state.curSeat.push((Math.floor(data/10)+1)+'行'+(data%10+1)+'列');
}
//计算当前选择了多少个座位
state.count = state.seatIndex.length;
if(state.count>=10){
state.maxFlag = true;
}
else {
state.maxFlag = false;
}
ifFlag();
}
const choseAll = (()=>{
for(let i = 0;i<state.seat[state.n][state.m].length;i++){
if(state.seat[state.n][state.m][i]==1||state.seat[state.n][state.m][i]==0)
state.seat[state.n][state.m][i]=state.seat[state.n][state.m][i]==1?0:1;
}
if(state.flag==1){
state.curSeat.push("包场");
state.flag=0;
state.count = 110;
totalPrice = 110*38;
}
else {
state.curSeat = [];
state.flag=1;
state.count = 0;
totalPrice = 0;
}
})
const changeN = ()=>{
state.count=0;
state.seatIndex = [];
state.curSeat = [];
console.log(state.roomString);
state.filmInfo.time.forEach((item,index)=>{
if(state.timeString == item){
state.n = index;
}
})
initSeat();//更换场次就把之前选择的座位复原,不能用数组遍历方法,因为遍历方法不能改变数组的值
ifFlag();
console.log(state.counter,state.seat[state.n][state.m].length)
}
const changeM = ()=>{
state.count=0;
state.seatIndex = [];
state.curSeat = [];
// console.log(state.roomString);
state.filmInfo.room.forEach((item,index)=>{
if(state.roomString === item){
state.m = index;
}
})
initSeat();
ifFlag();
console.log(state.counter,state.seat[state.n][state.m].length)
}
let totalPrice = computed(()=>{
let total = 0;
total = state.count*state.filmInfo.price;
return total
})
return {
...toRefs(state),
choseSeat,
choseAll,
changeN,
changeM,
totalPrice
}
}
}
</script>
<style>
.film{
margin: 0 auto;
width: 1050px;
height: 550px;
border: 1px solid black;
}
.film h3{
text-align: center;
}
.filmLeft ul{
list-style: none;
}
.seat{
float:left;
width: 30px;
height: 30px;
margin:5px 10px;
cursor:pointer;
}
.noSeat{
background: url("../assets/空座位.png") no-repeat center center;
/* background-color: aquamarine; */
background-size: contain;
}
.seatSpace{
background: url("../assets/可选座位.png") no-repeat ;
background-size: contain;
/* background-color: pink; */
}
.seatActive{
background: url("../assets/选中座位.png") no-repeat ;
background-size: contain;
/* background-color: black; */
}
.seatNoUse{
background: url("../assets/售出座位 .png") center center;
background-size: contain;
/* background-color: green; */
}
.filmLeft{
width: 550px;
height: 500px;
float: left;
}
.filmRight{
width: 500px;
height: 550px;
float: left;
background-color: bisque;
}
.rightTopLeft{
float: left;
margin: 20px 15px 5px 10px;
}
.rightTopRight{
float: left;
margin: 0px 0px 5px 5px ;
}
.rightBottom{
clear:both;
margin: 0px 10px;
}
.box{
display: flex;
width: 465px;
flex-flow: wrap;
}
.smSeat{
margin-right: 5px;
border: 1px solid black;
}
</style>