zhwl-miniapp/pages/scenic/seat.vue

539 lines
20 KiB
Vue
Raw Normal View History

2025-06-26 12:38:35 +08:00
<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>