H5-ThreeDoorder/pages/film/filmSeat.vue

1043 lines
26 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="w-100">
<view class="bg-f1 h-100vh">
<view class="film-header">
<view class="film-title">
<view class="title">
{{filmData.movieName}}
</view>
<view class="type">
{{filmData.versionType}}
</view>
</view>
<view class="film-time">
{{filmList.showTime}}
</view>
</view>
<!-- 荧幕 -->
<view class="pt-f left-0 w-100 screen-bg">
<image src="https://img.agrimedia.cn/chwl%2Fv2%2F20220921-113344.png"></image>
</view>
<!-- 循环的座位 -->
<movable-area :style="'height:'+(seatRow * 40 + 550)+'rpx;width: 100vw;top:'+(rpxNum * 280)+'px'"
class="pt-f left-0 seat-wrap">
<movable-view :style="'width: 100vw;height:'+(seatRow*40+350)+'rpx;'" :inertia="true" :scale="true"
:scale-min="0.95" :scale-max="2" direction="all" @change="onMove" @scale="onScale">
<view v-for="(item,index) in seatArray" :key="index" class="dp-f mt-20"
:style="'width:'+boxWidth+'px;height:'+seatSize+'px'">
<view v-for="(seat,col) in item" :key="col" class="dp-ib"
:style="'width:'+seatSize+'px;height:'+seatSize+'px'" @click="handleChooseSeat(index,col)">
<!-- 0为可选空座(白) 1为当前选择(红) 2为不可售或者禁用(灰)-->
<image v-if="seat.type===0" class="w-100 h-100"
src="https://img.agrimedia.cn/chwl%2Fv2%2Fseat-0.png" mode="aspectFit"></image>
<image v-else-if="seat.type===1" class="w-100 h-100"
src="https://img.agrimedia.cn/chwl%2Fv2%2Fseat-2.png" mode="aspectFit"></image>
<image v-else-if="seat.type===2" class="w-100 h-100"
src="https://img.agrimedia.cn/chwl%2Fv2%2Fseat-1.png" mode="aspectFit"></image>
</view>
</view>
<!-- 座位列表左轴 -->
<view class="pt-f bg-line br-15 over-h"
:style="'left: '+(10-moveX/scale)+'px;top:0rpx;width:30rpx;'">
<view class="w-100 dp-f ai-c jc-c fz-22 color-fff" :style="'height:'+seatSize+'px'"
v-for="(m,mindex) in mArr" :key="mindex">{{m}}</view>
<view :style="'height: 20rpx;'"></view>
</view>
<!-- 座位列表右轴 -->
<view class="pt-f bg-line br-15 over-h"
:style="'right: '+(10-moveX/scale)+'px;top:0rpx;width:30rpx;'">
<view class="w-100 dp-f ai-c jc-c fz-22 color-fff" :style="'height:'+seatSize+'px'"
v-for="(m,mindex) in mArr" :key="mindex">{{m}}</view>
<view :style="'height: 20rpx;'"></view>
</view>
</movable-view>
</movable-area>
<!-- 底部工具栏 -->
<view class="pt-f bottom-bar left-0 dp-f fd-cr mrg-0 z1000">
<!-- 按钮 -->
<view class="btn-wrap">
<view class="btn-seat" @click="buySeat">
{{SelectNum > 0 ? ('¥' + aPrice + '确认座位'):'请选座位'}}
</view>
</view>
<!-- 自动调座 -->
<view class="auto-seat">
<view class="auto-left">
<view class="auto-title">自动调座</view>
<view class="auto-desc">所选座位已售时,系统自动调至最佳观影座位</view>
</view>
<u-switch v-model="autoSeatFlag" activeColor="#FC554F" @change="autoSeat"></u-switch>
</view>
<!-- 已选 -->
<view class="seat-selected" v-if="SelectNum > 0">
<u-scroll-list :indicator="false">
<view class="seat-selected-item" v-for="(optItem,optindex) in optArr" :key="optindex">
{{optItem.seatNo}}
<text @click="deleteSeat(optItem)">×</text>
</view>
</u-scroll-list>
</view>
</view>
<!-- 自动购票提示框 -->
<view class="tipModel">
<u-modal :show="showFlag" title=" " borderRadius="20">
<template v-slot:default>
<view class="RX-slot-content">
<view class="text">
为节省您的购票时间若您所选座位被占时系统自动将座位调至剩余座位中最佳观影区
</view>
</view>
</template>
<template v-slot:confirmButton>
<view class="confirmButton">
<view class="btn replace" @tap="showFlag = false">不同意(无座自动退款)</view>
<view class="btn confirm" @tap="agreen">同意</view>
</view>
</template>
</u-modal>
</view>
<!-- 场次取消提示框 -->
<view class="tipModel">
<u-modal :show="cancelFlag" title=" " borderRadius="20">
<template v-slot:default>
<view class="RX-slot-content">
<view class="text">{{contentTips}}</view>
</view>
</template>
<template v-slot:confirmButton>
<view class="confirmButton">
<view class="btn confirm btn-one" @tap="cancelFlag = false">确认</view>
</view>
</template>
</u-modal>
</view>
<!-- 加载状态 -->
<b-load :show="loading" width="79" height="80" background="rgba(0,0,0,0.3)" type="load"></b-load>
</view>
</view>
</view>
</template>
<script>
import {
getFilmTimeSeat
} from '@/request/film/index.js';
import {
mapState,
mapMutations,
mapActions,
mapGetters
} from 'vuex';
export default {
data() {
return {
scaleMin: 1, //h5端为解决1无法缩小问题设为0.95
boxWidth: 450, //屏幕宽度px
space: ' ', //空格
seatArray: [], //影院座位的二维数组,-1为非座位0为未购座位1为已选座位(绿色),2为已购座位(红色),一维行,二维列
seatRow: 0, //影院座位行数
seatCol: 0, //影院座位列数
seatSize: 0, //座位尺寸
SelectNum: 0, //选择座位数
moveX: 0, //水平移动偏移量
scale: 1, //放大倍数
minRow: 0, //从第几行开始排座位
minCol: 0, //从第几列开始排座位
showTis: true, //显示选座提示
seatList: [], //接口获取的原始位置
mArr: [], //排数提示
optArr: [], //选中的座位数组。
isWXAPP: false,
loading: true,
// 后增
showFlag: false, // 自动选座提示框
cancelFlag: false, // 提示框
optArr: [], //选中的座位数组。
autoSeatFlag: false, // 自动调座
filmData: {}, // 电影详情
seatData: [], // 座位数据
contentTips: "", // 提示内容
cinemaId: "", // 场次ID
// 订单
showInfor: [] //选票数据
};
},
watch: {
// 选择的座位数量
SelectNum: {
deep: true,
immediate: true,
handler(val) {
this.showInfor = [];
for (let i = 0; i < this.optArr.length; i++) {
let obj = {}
obj.seatId = this.optArr[i].seatId;
obj.seatName = this.optArr[i].seatNo;
this.showInfor.push(obj)
}
}
},
optArr: {
deep: true,
immediate: true,
handler(val) {
}
}
},
computed: {
// 总价格
aPrice() {
return Math.round((parseFloat(this.SelectNum) * parseFloat(this.filmList.pay_price)) * 100) / 100
},
rpxNum() {
return this.boxWidth / 750;
},
pxNum() {
return 750 / this.boxWidth;
},
},
onLoad(option) {
//获取宽度
uni.getSystemInfo({
success: function(e) {
this.boxWidth = e.screenWidth
//#ifdef H5
this.scaleMin = 0.95
//#endif
}
})
// this.filmData = JSON.parse(decodeURIComponent(option.filmData)); // 电影详情
// this.filmList = JSON.parse(decodeURIComponent(option.filmList)); // 场次详情
// this.cinemaData = JSON.parse(decodeURIComponent(option.cinemaData)); // 影院详情
// this.contentTips = '该场次已取消'
// this.cancelFlag = true;
this.getData()
},
methods: {
...mapMutations("film", {
SetFileData: 'SetFileData'
}),
// 初始化请求数据
getData() {
const params = {
showId: this.filmList.showId,
addFlag: 0,
}
getFilmTimeSeat(params).then((res) => {
this.seatData = res['data']['seats'];
this.seatData.map(item => {
// 字符串转坐标
// item.YCoord = item.seatNo.substring(0, item.seatNo.indexOf("排"));
// item.XCoord = item.seatNo.substring(item.YCoord.length + 1, item.seatNo.length - 1);
item.YCoord = item.rowNo;
item.XCoord = item.columnNo;
});
console.table(this.seatData)
this.initData()
});
},
initData() {
let arr = this.seatData;
//假数据说明seatNo座位编号rowNo-行号columnNo-纵号YCoord-Y坐标XCoord-X坐标Status-状态
let row = 0
let col = 0
let minCol = parseInt(arr[0].XCoord)
let minRow = parseInt(arr[0].YCoord)
console.log()
for (let i of arr) {
console.group(`${i['seatNo']}次循环`);
console.log(`XCoord:${i.XCoord}`);
console.log(`YCoord:${i.YCoord}`);
console.log(i);
console.groupEnd(`${i['seatNo']}次循环`);
minRow = parseInt(i.YCoord) < minRow ? parseInt(i.YCoord) : minRow;
minCol = parseInt(i.XCoord) < minCol ? parseInt(i.XCoord) : minCol;
row = parseInt(i.YCoord) > row ? parseInt(i.YCoord) : row;
col = parseInt(i.XCoord) > col ? parseInt(i.XCoord) : col;
};
// console.log(row,minRow)
this.seatList = arr;
this.seatRow = row - minRow + 1;
this.seatCol = col - minCol + 3;
this.minRow = minRow;
this.minCol = minCol - 1;
this.initSeatArray()
},
//初始座位数组
initSeatArray: function() {
let seatArray = Array(this.seatRow).fill(0).map(() => Array(this.seatCol).fill({
type: -1,
seatNo: '',
rowNo: '',
columnNo: ''
}));
this.seatArray = seatArray
this.seatSize = this.boxWidth > 0 ?
parseInt(parseInt(this.boxWidth, 10) / (this.seatCol + 1), 10) :
parseInt(parseInt(414, 10) / (this.seatCol + 1), 10)
this.initNonSeatPlace();
},
//初始化是座位的地方
initNonSeatPlace: function() {
let seat = this.seatList.slice()
let arr = this.seatArray.slice()
for (let num in seat) {
// 0为可选空座(白) 1为当前选择(红) 2为不可售或者禁用(灰)
// N 可售 LK 不可售
let type
if (seat[num].status === 'N') {
type = 0
} else if (seat[num].status === 'LK') {
type = 2
} else {
type = 2
}
// 映射对应的座位数据
arr[parseInt(seat[num].YCoord) - this.minRow][parseInt(seat[num].XCoord) - this.minCol] = {
type,
XCoord: seat[num].XCoord,
YCoord: seat[num].YCoord,
areaId: seat[num].areaId,
seatId: seat[num].seatId,
seatNo: seat[num].seatNo,
rowNo: seat[num].rowNo,
columnNo: seat[num].columnNo,
status: seat[num].status
}
}
this.seatArray = arr.slice()
let mArr = []
for (let i = 0; i < arr.length; i++) {
mArr.push(i + 1)
}
this.mArr = mArr
this.loading = false; // 关闭加载
this.showFlag = true; // 选座提示
},
// 自动选座(单人)
autoSeat(e) {
this.autoSeatFlag = e;
if (this.autoSeatFlag) {
this.smartChoose(1);
} else {
this.autoSeatFlag = e;
this.resetSeat();
}
},
//放大缩小事件
onScale: function(e) {
this.showTis = false
// this.moveX=-e.detail.x
let w = this.boxWidth * 0.5
let s = 1 - e.detail.scale
this.moveX = w * s
this.scale = e.detail.scale
if (s > 0 || s === 0) {
this.showTis = true
}
},
//移动事件
onMove: function(e) {
this.showTis = false
this.moveX = e.detail.x
},
//重置座位
resetSeat: function() {
this.SelectNum = 0
this.optArr = []
//将所有已选座位的值变为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].type === 1) {
oldArray[i][j].type = 0
}
}
}
this.seatArray = oldArray;
},
//选定且购买座位提交订单
buySeat() {
if (this.SelectNum === 0) {
this.cancelFlag = true;
this.contentTips = '请选择座位再购买';
return
}
console.log(this.cinemaData, '影片数据')
console.log(this.filmList, '放映厅列表')
const params = {
amount: this.SelectNum, // 购票数
filmImg: this.filmData.posterUrl, // 海报图
movieId: this.filmList.movieId, // 影片ID
cinemaId: this.cinemaData.cinemaId, // 影院ID
cinemaName: this.cinemaData.cinemaName, // 影院名称
cinemaAddress: this.cinemaData.cinemaAddress, // 影院地址
lng: this.cinemaData.longitude, // 纬
lat: this.cinemaData.latitude, // 经
movieName: this.filmData.movieName, // 影片名称
showTime: this.filmList.showTime, // 上映时间
autoSeatFlag: this.filmList.autoSeatFlag, // 自动选座
pay_price: this.filmList.pay_price * this.SelectNum, // 价格
price: this.filmList.showPrice * this.SelectNum, // 原价
deduction_price: this.filmList.deduction_price * this.SelectNum, // 抵扣
commission_total: this.filmList.commission_price * this.SelectNum, // 佣金
showId: this.filmList.showId, // 影院ID
showInfor: this.showInfor, // 票详情
hallName: this.filmList.hallName, // 厅型
versionType: this.filmData.versionType, // 电影型号
movieType: this.filmData.movieType, // 电影类型
stopSellTime: this.filmList.stopSellTime, // 出票时间
addFlag: 0
}
console.log(params, '传过去的参数')
this.SetFileData(params)
uni.navigateTo({
url: './filmSubmit'
})
},
//处理座位选择逻辑
handleChooseSeat: function(row, col) {
if (this.optArr.length <= 3) {
let seatValue = this.seatArray[row][col].type;
let newArray = this.seatArray;
//如果是已购座位,直接返回
if (seatValue === 2 || seatValue === -1) return
//如果是已选座位点击后变未选
if (seatValue === 1) {
newArray[row][col].type = 0
this.SelectNum--
this.getOptArr(newArray[row][col], 0)
} else if (seatValue === 0) {
newArray[row][col].type = 1
this.SelectNum++
this.getOptArr(newArray[row][col], 1)
}
//必须整体更新二维数组Vue无法检测到数组某一项更新,必须slice复制一个数组才行
this.seatArray = newArray.slice();
} else {
this.contentTips = "最多选择四个座位!"
this.cancelFlag = true;
}
},
//处理已选座位数组
getOptArr: function(item, type) {
let optArr = this.optArr
if (type === 1) {
optArr.push(item)
} else if (type === 0) {
let arr = []
optArr.forEach(v => {
if (v.seatNo !== item.seatNo) {
arr.push(v)
}
})
optArr = arr
}
this.optArr = optArr.slice()
},
//推荐选座,参数是推荐座位数目,
smartChoose: function(num) {
// 先重置
this.resetSeat()
//找到影院座位水平垂直中间位置的后一排
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);
this.SelectNum += num
return
}
//再从中间排往前排搜索
let forwardResult = this.searchSeatByDirection(rowStart - 1, 0, num);
if (forwardResult.length > 0) {
this.chooseSeat(forwardResult);
this.SelectNum += num
return
}
//提示用户无合法位置可选
alert('无合法位置可选!')
},
//搜索函数,参数:fromRow起始行toRow终止行,num推荐座位数
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
},
/*辅助函数判断每一行座位从i列到j列是否全部空余且连续
*
*/
checkRowSeatContinusAndEmpty: function(rowNo, startPos, endPos) {
let isValid = true;
for (let i = startPos; i <= endPos; i++) {
if (this.seatArray[rowNo][i].type !== 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 opt = this.optArr
let oldArray = this.seatArray.slice();
for (let i = 0; i < result.length; i++) {
//选定座位
oldArray[result[i][0]][result[i][1]].type = 1
this.optArr.push(oldArray[result[i][0]][result[i][1]])
}
this.seatArray = oldArray;
},
// 删除座位tag
deleteSeat(optItem) {
let index = this.optArr.findIndex(item => item.seatId == optItem.seatId)
this.optArr.splice(index, 1)
this.SelectNum--
for (let i = 0; i < this.seatArray.length; i++) {
for (let j = 0; j < this.seatArray[i].length; j++) {
if (this.seatArray[i][j].seatId == optItem.seatId) {
this.seatArray[i][j].type = 0;
}
}
}
},
// 同意自动选票
agreen() {
this.showFlag = false;
this.autoSeatFlag = true; // 调座开关打开
this.smartChoose(1); // 自动选座
}
}
}
</script>
<style lang="scss" scoped>
.film-header {
width: 100%;
padding: 20rpx 24rpx;
box-sizing: border-box;
background-color: #FFFFFF;
.film-title {
display: flex;
align-items: center;
.title {
font-size: 40rpx;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 600;
color: #333333;
line-height: 56rpx;
padding-right: 10rpx;
margin-right: 10rpx;
}
.type {
display: block;
font-size: 22rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
line-height: 28rpx;
border: 1rpx solid #999999;
padding: 6rpx;
}
}
.film-time {
font-size: 24rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
line-height: 34rpx;
}
}
.seat-selected {
display: flex;
justify-content: center;
padding: 10rpx 24rpx;
.seat-selected-item {
height: 60rpx;
border-radius: 30rpx;
border: 2rpx solid #DEDEDE;
padding: 10rpx 20rpx;
box-sizing: border-box;
color: #333333;
font-size: 24rpx;
display: flex;
align-items: center;
margin-left: 32rpx;
text {
display: inline-block;
font-size: 28rpx;
color: #E55757;
padding-left: 8rpx;
}
}
.seat-selected-item:nth-child(1) {
margin-left: 0rpx;
}
}
.screen-bg {
width: 100%;
height: 480rpx;
position: fixed;
top: -10rpx;
image {
width: 100%;
height: 480rpx;
}
}
.auto-seat {
display: flex;
justify-content: space-between;
align-items: center;
width: 700rpx;
height: 120rpx;
background: #FFFFFF;
border-radius: 20rpx;
padding: 20rpx;
box-sizing: border-box;
margin: 0rpx auto;
.auto-left {
.auto-title {
font-size: 26rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
line-height: 40rpx;
}
.auto-desc {
font-size: 24rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #EB2727;
}
}
}
.btn-wrap {
width: 100%;
display: flex;
justify-content: center;
.btn-seat {
width: 700rpx;
height: 84rpx;
background: linear-gradient(132deg, #FC5159 0%, #FF7700 100%);
border-radius: 40rpx;
line-height: 84rpx;
text-align: center;
color: #FFFFFF;
margin: 20rpx 0rpx;
font-size: 26rpx;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 600;
}
}
// 就餐方式弹框
.tipModel {
/deep/ .u-modal__button-group--confirm-button {
padding: 0rpx !important;
}
/deep/ .u-modal__content {
padding: 34rpx !important;
border-bottom: 2rpx solid #EDEDED;
}
/deep/ .u-modal__title {
display: none !important;
padding-bottom: 20rpx !important;
}
}
.slot-content-Tips {
font-size: 30rpx;
line-height: 40rpx;
.text1 {
color: #DB9F5E;
}
}
.slot-content-Tips2 {
width: 100%;
background-color: #FFF9F3;
margin-top: 20rpx;
color: #FF7910;
font-size: 26rpx;
line-height: 49rpx;
padding: 15rpx 32rpx;
}
.confirmButton {
font-size: 24rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
line-height: 86rpx;
display: flex;
align-items: center;
.replace {
width: 50%;
height: 86rpx;
text-align: center;
font-size: 24rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
line-height: 86rpx;
border-right: 2rpx solid #EDEDED;
}
.confirm {
width: 50%;
height: 86rpx;
text-align: center;
font-size: 24rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
line-height: 86rpx;
}
.btn-one {
width: 100% !important;
}
}
.RX-slot-content {
.text {
font-size: 28rpx;
font-family: PingFangSC-Semibold, PingFang SC;
font-weight: 600;
color: #333333;
line-height: 40rpx;
}
/deep/ .u-modal__button-group--confirm-button {
border: 2rpx solid #EDEDED;
}
}
.mrg-0 {
width: 100%;
}
.p-all-10 {
padding: 10rpx;
}
.ml-10 {
margin-left: 10rpx;
}
.m-0-10 {
margin: 0 10rpx;
}
.bg-unbtn {
background-color: #f9abb3;
}
.bg-red-1 {
background-color: #F45664;
}
.br-10 {
border-radius: 10rpx;
}
.ml-20 {
margin-left: 20rpx;
}
.mb-20 {
margin-bottom: 20rpx;
}
.p-all-32 {
padding: 32rpx;
}
.fd-cr {
flex-direction: column-reverse;
/* 主轴方向从下到上,默认从左到右 */
}
.bottom-bar {
bottom: var(--window-bottom);
}
.color-fff {
color: #000
}
.br-15 {
border-radius: 15rpx;
}
.over-h {
overflow: hidden;
}
.dp-ib {
display: inline-block;
}
.mt-20 {
margin-top: 20rpx;
}
.pa-v-2 {
/* 定位垂直对齐 */
left: 50%;
transform: translateX(-50%)
}
.b-d-1 {
border: 1px dashed #e5e5e5;
}
.w-100 {
width: 100%;
}
.h-100 {
height: 100%;
}
.bg-f1 {
background-color: #f1f1f1;
}
.h-100vh {
height: 100vh;
}
.pt-f {
position: fixed;
}
.seat-wrap {
margin-top: 20rpx;
}
.left-0 {
left: 0;
}
.p-0-32 {
padding: 0 32rpx;
}
.pt-20 {
padding-top: 20rpx;
}
.bg-white {
background-color: #fff;
}
.z1000 {
z-index: 1000;
}
.fz-34 {
font-size: 34rpx;
}
.fw-b {
font-weight: bold;
}
.mt-10 {
margin-top: 10rpx;
}
.fz-28 {
font-size: 28rpx;
}
.color-666 {
color: #666666
}
.dp-f {
display: flex;
margin: 12rpx auto;
}
.jc-c {
justify-content: center;
}
.ai-c {
align-items: center;
}
.fz-22 {
font-size: 22rpx;
}
.color-333 {
color: #333333
}
.m-0-a {
margin: 0 auto;
}
.mt-48 {
margin-top: 48rpx;
}
.fz-20 {
font-size: 20rpx;
}
.color-999 {
color: #999999
}
.b-1 {
border: 1px solid #CCCCCC;
}
.br-5 {
border-radius: 5rpx;
}
.Stage {
background-color: #dddddd;
width: 380rpx;
height: 34rpx;
transform: perspective(34rpx) rotateX(-10deg);
margin: 0 auto;
}
</style>