zhwl-miniapp/pages/scenic/seat.vue
2025-06-26 12:38:35 +08:00

539 lines
20 KiB
Vue
Raw 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>
<u-navbar :autoBack="true" :bgColor="'white'" :leftIconColor="'#303133'">
<view slot='center' class="navbar_title">座位预定</view>
</u-navbar>
<view class="wrap" :style="{marginTop: paddingTop + 'px'}">
<view class="scroll-box">
<scroll-view scroll-x="true">
<view class="scroll-x">
<view class="tab">
<view class="box-show" style="border-color: #03AE80; background: #03AE80;">
<!-- <image src="../../static/image/seat/bought.png" mode="aspectFit"></image> -->
</view>
已售出
</view>
<view class="tab" v-for="(item, index) in detail.zoneList" :key="item.zoneId">
<view class="box-show" :class="['border-color-' + index % 9]"></view>
{{item.zoneName}} {{ item.price ? '¥' + item.price.price : '' }}
</view>
</view>
</scroll-view>
</view>
<view class="sm-title">{{pageData.hallName}}</view>
<view class="site-box" v-if="detail">
<scroll-view scroll-x="true">
<view class="row">
<view class="column" v-for="(i, index) in seatStateHeader" :key="index">
<view class="sm-icon unselect xuhao">{{i}}</view>
</view>
</view>
<view class="row" v-for="(value, key) in detail.venueSeats" :key="key">
<view class="column" v-for="(i, index) in value" :key="index" @click="selectItem(key, i[2], index)">
<!-- 未配置价格 -->
<view class="sm-icon disable" v-if="detail.saleList.length == 0"></view>
<template v-else>
<!-- 未选中 -->
<view class="sm-icon unselect" v-if="i[3] === 0 && i[1] === 1 && isSale(key, i[2], 1) && isLock(key, i[2])" :class="[borderColorFun(i), isPartition(key, index) ? 'gray' : '']"></view>
<!-- 选中 -->
<view class="sm-icon selected" v-if="isSale(key, i[2], 3)"></view>
<!-- 已售出 -->
<image v-if="i[3] === 1 && i[1] === 1" class="sm-icon" src="../../static/image/seat/bought.png" mode="aspectFit"></image>
<!-- 无座或者占座 -->
<view class="sm-icon disable" v-if="i[1] === 0 || isSale(key, i[2], 2) || !isLock(key, i[2])"></view>
</template>
</view>
</view>
</scroll-view>
<view class="sm-line-index">
<view class="text" v-for="(value, key) in detail.venueSeats" :key="key">
{{key}}
</view>
</view>
</view>
<view class="info-box">
<view class="info">
<view class="info-title">{{pageData.ticketName}}</view>
<view class="info-sub-title">{{pageData.hallTime}}</view>
</view>
<view class="info" v-if="chooseTicket.length">
<view class="info-site-box">
<view class="info-site" v-for="(item, index) in chooseTicket" :key="item.id">
<view class="info-site-left">
<view>{{item.rowNum}}排{{item.seatNum}}座</view>
<view>¥{{item.sumPrice.toFixed(2)}}</view>
</view>
<view class="info-site-right" @click="delChoose(index)">
<u-icon name="close" color="#E1E1E1" size="24rpx"></u-icon>
</view>
</view>
</view>
</view>
<view class="price" v-if="chooseTicket.length">
<span>¥</span>{{totalPrice}}<span>/{{chooseTicket.length}}张</span>
</view>
</view>
<view class="btn-box" :style="{paddingBottom: windowBottom + 'px'}">
<view class="btn" @click="confirm">确认选座</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
paddingTop: 0,
windowBottom: 0,
detail: null,
chooseTicket: [],
pageData: null,
seatStateHeader: []
};
},
computed: {
totalPrice() {
let total = 0
if (this.chooseTicket.length) {
this.chooseTicket.forEach(item => {
total += item.sumPrice
})
}
return total;
}
},
async onLoad(options) {
this.pageData = JSON.parse(decodeURIComponent(options.pageData));
this.paddingTop = this.$paddingTop;
this.windowBottom = this.$safeAreaBottom;
let info = await this.$http.getSalelnfo({
itemId: options.itemId,
hallId: options.hallId,
saleTime: this.pageData.saleTime
})
info.data.venueSeats = info.data.venueSeats ? JSON.parse(info.data.venueSeats) : {};
for (let i in info.data.venueSeats) {
for (var j = 0; j < info.data.venueSeats[i].length; j++) {
info.data.venueSeats[i][j][3] = 0;
}
}
this.getSeatStateHeader(info.data);
this.detail = info.data
},
methods: {
sortArrayByOddEven(seatStateHeader) {
let arr1 = [];
let arr2 = [];
seatStateHeader.forEach((item) => {
if (item % 2 == 0) {
arr2.push(item)
} else arr1.push(item)
})
arr2.sort((a, b) => {
return b - a;
})
arr1 = arr1.reverse();
arr2 = arr2.reverse();
arr1 = arr1.concat(arr2)
return arr1;
},
getSeatStateHeader (data) {
let seatStateHeader = [];
for (var i = 1; i <= data.seatNumsRow; i ++) {
seatStateHeader.push(i);
}
if (data.rowStart == 2) {
seatStateHeader = this.sortArrayByOddEven(seatStateHeader)
}
this.seatStateHeader = JSON.parse(JSON.stringify(seatStateHeader));
},
confirm() {
if (this.chooseTicket.length == 0) {
uni.showToast({
title: `请选择座位`,
icon: 'none',
mask: true
})
return
}
let obj = JSON.parse(JSON.stringify(this.pageData));
obj.chooseTicket = this.chooseTicket;
obj.totalPrice = this.totalPrice;
uni.redirectTo({
url: "/pages/scenic/ticktOrderConfirm?pageData=" + encodeURIComponent(JSON.stringify(obj))
})
},
selectItem(key, num, index) {
let data = this.detail.saleList.find((info) => {
return info.rowNum == key && info.seatNum == num;
})
if (!data) return false;
// 判断 是否是可买的状态& 是否被占座& 是否设置分区
if (data.seatState == 1 && data.lockSeat == 0 && !this.isPartition(key, index)) {
this.detail.zoneList.map((item) => {
if (item.zoneId == data.zoneId) {
data.sumPrice = item.price.price
data.zoneName = item.zoneName
}
})
let currentInd = this.chooseTicket.findIndex(ele => ele.id == data.id)
if (currentInd == -1) {
if (this.pageData.limit != null) {
// 是否小于限制数
if (this.chooseTicket.length >= this.pageData.limit) {
uni.showToast({
title: `最多可买${this.pageData.limit}张`,
icon: 'none',
mask: true
})
} else {
this.detail.venueSeats[key][index][3] = 1
this.chooseTicket.push(data)
}
} else {
this.detail.venueSeats[key][index][3] = 1
this.chooseTicket.push(data)
}
} else {
this.detail.venueSeats[key][index][3] = 0
this.chooseTicket.splice(currentInd, 1)
}
this.$forceUpdate()
}
},
borderColorFun(i) {
let ind = this.detail.zoneList.findIndex(item => item.zoneId == i[0])
return 'border-color-' + ind
},
// 是否售出
isSale(key, index, status) {
/** seatState 所有座位的状态0不可选1表示可售2表示占座3表示售出在场次表中体现 */
let data = this.detail.saleList.find((info) => {
return info.rowNum == key && info.seatNum == index;
})
return data.seatState == status
},
// 是否分区
isPartition(key, index) {
let data = this.detail.saleList.find((info) => {
return info.rowNum == key && info.seatNum == (index + 1);
})
// 未设置分区的 禁止买
let arr = this.detail.zoneList.filter((item) => item.zoneId == data.zoneId)
// 未分区 true 分区 false
return arr.length ? false : true
},
// 是否锁座
isLock(key, index) {
/** lockSeat 付款锁定0未锁定1,已锁定(待付款) */
let data = this.detail.saleList.find((info) => {
return info.rowNum == key && info.seatNum == index;
})
// 未锁座 true 锁座 false
return data.lockSeat == 0 ? true : false
},
delChoose(index) {
let item = this.chooseTicket[index]
this.detail.venueSeats[item.rowNum][item.seatNum - 1][3] = 0
this.chooseTicket.splice(index, 1)
}
}
}
</script>
<style lang="scss">
page {
background-color: #FBFBFB;
}
</style>
<style lang="scss" scoped>
.wrap {
overflow-x: hidden;
box-sizing: border-box;
.scroll-box {
margin: 0 32rpx;
box-sizing: border-box;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
.scroll-x {
display: flex;
align-items: center;
height: 92rpx;
.tab {
margin-right: 30rpx;
white-space: nowrap;
font-size: 26rpx;
color: rgba(0, 0, 0, 0.45);
line-height: 30rpx;
display: flex;
align-items: center;
.box-show {
width: 38rpx;
height: 38rpx;
border-radius: 6rpx;
border-style: solid;
border-width: 2rpx;
margin-right: 12rpx;
box-sizing: border-box;
display: inline-block;
&>image {
width: 100%;
height: 100%;
}
}
}
}
}
.sm-title {
width: 100%;
text-align: center;
font-size: 30rpx;
color: rgba(0, 0, 0, 0.45);
margin-top: 30rpx;
margin-bottom: 60rpx;
}
.site-box {
position: relative;
.row {
white-space: nowrap;
text-align: center;
.column {
display: inline-block; margin: 8rpx; position: relative; width: 48rpx; height: 48rpx;
.sm-icon {
position: absolute; left: 0; top: 0; z-index: 3;
// margin: 8rpx;
width: 100%; height: 100%;
display: block;
box-sizing: border-box;
}
.unselect {
border-style: solid;
border-width: 1rpx;
border-radius: 8rpx;
background: white;
}
.gray {
background: #F4F4F4;
border-color: #F4F4F4;
}
.selected {
border-radius: 8rpx;
background: #03AE80;
border: 1rpx solid #03AE80;
z-index: 5;
}
.disable {
background: #F4F4F4;
border-radius: 8rpx;
border: 1rpx solid rgba(0, 0, 0, 0.2);
z-index: 4;
}
.xuhao{ color: #666; font-size: 28rpx; border: none; background: transparent; text-align: center; line-height: 48rpx; }
}
}
.sm-line-index {
position: absolute;
top: 52rpx;
left: 32rpx;
border-radius: 24rpx;
background-color: rgba(0, 0, 0, 0.4);
overflow: hidden;
z-index: 3;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 10rpx 0;
.text {
min-width: 30rpx;
height: 48rpx;
line-height: 48rpx;
font-size: 24rpx;
color: #ffffff;
text-align: center;
margin-top: 20rpx;
margin: 8rpx 0 16rpx;
box-sizing: border-box;
}
}
}
.info-box {
margin: 0 32rpx;
padding: 12rpx 24rpx 32rpx;
box-sizing: border-box;
background: #FFFFFF;
border-radius: 10rpx;
margin-top: 70rpx;
margin-bottom: 30rpx;
.info {
border-top: 1rpx solid rgba(0, 0, 0, 0.1);
box-sizing: border-box;
padding: 24rpx 0 0;
&:first-child {
border-top: none;
}
.info-title {
height: 42rpx;
font-weight: 600;
font-size: 30rpx;
color: rgba(0, 0, 0, 0.85);
line-height: 35rpx;
margin-bottom: 12rpx;
}
.info-sub-title {
height: 36rpx;
font-size: 26rpx;
color: rgba(0, 0, 0, 0.45);
line-height: 30rpx;
margin-bottom: 24rpx;
}
.info-site-box {
display: flex;
align-items: center;
flex-wrap: wrap;
}
.info-site {
width: 156rpx;
height: 91rpx;
background: rgba(0, 0, 0, 0.03);
border-radius: 8rpx;
margin-right: 20rpx;
margin-bottom: 24rpx;
display: flex;
align-items: center;
justify-content: space-between;
padding: 10rpx 16rpx;
.info-site-left {
box-sizing: border-box;
flex: 1;
padding-right: 20rpx;
border-right: 1rpx solid rgba(0, 0, 0, 0.05);
margin-right: 13rpx;
&>view:nth-child(1) {
height: 34rpx;
font-weight: 600;
font-size: 24rpx;
color: rgba(0, 0, 0, 0.85);
line-height: 28rpx;
margin-bottom: 6rpx;
}
&>view:nth-child(2) {
height: 31rpx;
font-size: 22rpx;
color: rgba(0, 0, 0, 0.65);
line-height: 26rpx;
}
}
.info-site-right {
height: 100%;
::v-deep .u-icon--right {
height: 100%;
}
}
}
}
.price {
height: 102rpx;
box-sizing: border-box;
padding-top: 24rpx;
text-align: right;
font-size: 64rpx;
color: #FF5833;
border-top: 1rpx solid rgba(0, 0, 0, 0.1);
&>span:nth-child(1) {
font-size: 36rpx;
}
&>span:last-child {
font-size: 24rpx;
color: rgba(0, 0, 0, 0.45);
line-height: 28rpx;
}
}
}
.btn {
margin: 0 32rpx;
background: #03AE80;
height: 100rpx;
border-radius: 50rpx;
text-align: center;
line-height: 100rpx;
font-weight: 600;
font-size: 32rpx;
color: #FFFFFF;
}
}
.unselect {
border-color: rgba(0, 0, 0, 0.2);
}
.border-color-0 {
border-color: #FF3B44;
}
.border-color-1 {
border-color: #FF8319;
}
.border-color-2 {
border-color: #FFC53B;
}
.border-color-3 {
border-color: #FF67D6;
}
.border-color-4 {
border-color: #6795FE;
}
.border-color-5 {
border-color: #53CBBA;
}
.border-color-6 {
border-color: #8800FF;
}
.border-color-7 {
border-color: #97D5FF;
}
.border-color-8 {
border-color: #A70C13;
}
.border-color-9 {
border-color: #79FF19;
}
</style>