|
|
@@ -0,0 +1,690 @@
|
|
|
+<script setup lang="ts">
|
|
|
+// 1. 导入 Vue3 核心 API 和 uni-app 生命周期
|
|
|
+import { computed, nextTick, ref } from 'vue'
|
|
|
+import { onLoad } from '@dcloudio/uni-app'
|
|
|
+
|
|
|
+// 2. 导入原组件和模拟数据
|
|
|
+import { getArrayFieldMax, isWithin45Minutes } from '../utils'
|
|
|
+import canvasSeatmap from './components/pages/CanvasSeatmap.vue'
|
|
|
+import { StaticUrl } from '@/config'
|
|
|
+import router from '@/router'
|
|
|
+
|
|
|
+definePage({
|
|
|
+ name: 'film-choose-seat',
|
|
|
+ islogin: false,
|
|
|
+ style: {
|
|
|
+ navigationBarTitleText: '座次选择',
|
|
|
+ backgroundColorBottom: '#fff',
|
|
|
+ },
|
|
|
+})
|
|
|
+
|
|
|
+// ************************ 核心:定义所有缺失的接口(解决类型报错的关键) ************************
|
|
|
+// /**
|
|
|
+// * 影院信息接口
|
|
|
+// */
|
|
|
+// interface CinemaInfo {
|
|
|
+// cinema_name: string
|
|
|
+// [key: string]: any // 兼容其他未知属性,避免后续扩展报错
|
|
|
+// }
|
|
|
+
|
|
|
+/**
|
|
|
+ * 区域列表项接口
|
|
|
+ */
|
|
|
+interface AreaItem {
|
|
|
+ area: string | number
|
|
|
+ areaId: string | number
|
|
|
+ market_price: number | string
|
|
|
+ strokeStyle: string
|
|
|
+ [key: string]: any // 兼容组件所需的其他未知属性
|
|
|
+}
|
|
|
+
|
|
|
+// /**
|
|
|
+// * 场次信息接口
|
|
|
+// */
|
|
|
+// interface ScheduleInfo {
|
|
|
+// schedule_id: string | number
|
|
|
+// hall_name: string
|
|
|
+// cinema: CinemaInfo | null
|
|
|
+// schedule_area: AreaItem[]
|
|
|
+// [key: string]: any // 兼容其他未知属性
|
|
|
+// }
|
|
|
+
|
|
|
+/**
|
|
|
+ * 座位信息接口
|
|
|
+ */
|
|
|
+interface SeatInfo {
|
|
|
+ seatName: string
|
|
|
+ marketPrice: number | string
|
|
|
+ [key: string]: any // 兼容组件所需的其他未知属性
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 座位图配置项接口
|
|
|
+ */
|
|
|
+interface SeatmapOptions {
|
|
|
+ title: string
|
|
|
+ hallName: string
|
|
|
+ areaList: AreaItem[]
|
|
|
+ footerHeight: number
|
|
|
+ maxSelectNum?: number // 可选属性,对应注释的配置
|
|
|
+ isolateSeats?: boolean // 可选属性,对应注释的配置
|
|
|
+ [key: string]: any // 兼容组件所需的其他未知配置
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 座位点击回调事件接口
|
|
|
+ */
|
|
|
+interface SeatTapEvent {
|
|
|
+ chooseSeatList: SeatInfo[]
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 座位图组件实例接口(定义组件的公开方法,解决 ref 调用方法的类型报错)
|
|
|
+ */
|
|
|
+interface CanvasSeatmapInstance {
|
|
|
+ init: (params: { seatList: SeatInfo[], options: SeatmapOptions }) => void
|
|
|
+ validateIsolates: () => boolean
|
|
|
+ cancelSeat: (seat: SeatInfo) => void
|
|
|
+}
|
|
|
+
|
|
|
+const isLoging = ref(false)
|
|
|
+const loading = ref<boolean>(false)
|
|
|
+const seatInfo = ref<Api.filmMovieSeat>({})
|
|
|
+const areaList = ref<AreaItem[]>([])
|
|
|
+const seatList = ref<SeatInfo[]>([])
|
|
|
+const chooseSeatList = ref<SeatInfo[]>([])
|
|
|
+const options = ref<SeatmapOptions>({
|
|
|
+ title: '',
|
|
|
+ hallName: '',
|
|
|
+ areaList: [],
|
|
|
+ footerHeight: 0,
|
|
|
+})
|
|
|
+const areaArr = ref(['贵宾区', '普通区', '特价区1', '特价区2', '特价区3', '特价区4', '特价区5', '特价区6'])
|
|
|
+const { userInfo } = storeToRefs(useUserStore())
|
|
|
+const phone = ref('')
|
|
|
+const query = ref({
|
|
|
+ memberId: unref(userInfo).id,
|
|
|
+ channelId: unref(userInfo).channelId,
|
|
|
+ shopId: '1',
|
|
|
+ channelName: unref(userInfo).channelName,
|
|
|
+ cinemaName: '',
|
|
|
+ cinemaCode: '',
|
|
|
+ movieCode: '',
|
|
|
+ hallName: '',
|
|
|
+ orderPayMode: '1',
|
|
|
+ originPrice: 0,
|
|
|
+ seatNames: '',
|
|
|
+ sessionBeginTime: '',
|
|
|
+ switchSeat: false,
|
|
|
+ movieName: '',
|
|
|
+ postImageUrl: '',
|
|
|
+ planType: '',
|
|
|
+
|
|
|
+})
|
|
|
+const active = ref('')
|
|
|
+const showTime = ref(false)
|
|
|
+const screenList = ref()
|
|
|
+// ************************ 非响应式常量(类型自动推断,无需额外注解) ************************
|
|
|
+const colorList = [
|
|
|
+ '#449DFE', // 浅黄色
|
|
|
+ '#FFB639', // 浅蓝色
|
|
|
+ '#FF4D3A', // 干枯玫瑰
|
|
|
+ '#b6e67f', // 浅绿色
|
|
|
+ '#ffb0bc', // 浅粉色
|
|
|
+] as const // as const 锁定数组元素,禁止修改,类型更严格
|
|
|
+
|
|
|
+const totalPrice = computed(() => {
|
|
|
+ let total = 0
|
|
|
+ const scale = 10000
|
|
|
+ if (!seatInfo.value.showTime)
|
|
|
+ return 0
|
|
|
+ const key = isWithin45Minutes(new Date(), seatInfo.value.showTime) ? 'fastPrice' : 'ticketPrice'
|
|
|
+ const price = getArrayFieldMax(chooseSeatList.value, key) || 0
|
|
|
+ total = (price * scale) * chooseSeatList.value.length / scale
|
|
|
+ return total
|
|
|
+})
|
|
|
+
|
|
|
+// ************************ 组件 ref(添加明确实例类型,解决调用方法的类型报错) ************************
|
|
|
+const seatmapRef = ref<CanvasSeatmapInstance | null>(null)
|
|
|
+
|
|
|
+// ************************ 所有方法(添加参数/返回值类型注解,消除隐式 any) ************************
|
|
|
+// /**
|
|
|
+// * 金额转换(分转元,保留 2 位小数)
|
|
|
+// * @param total 分单位金额
|
|
|
+// * @returns 元单位金额(字符串,保留 2 位小数)
|
|
|
+// */
|
|
|
+// function priceConversion(total: number | string): string {
|
|
|
+// const numTotal = Number(total)
|
|
|
+// // 处理 NaN 情况,避免返回 NaN.toFixed(2) 报错
|
|
|
+// if (Number.isNaN(numTotal))
|
|
|
+// return '0.00'
|
|
|
+// return (numTotal / 100).toFixed(2)
|
|
|
+// }
|
|
|
+
|
|
|
+async function getScreenList(cinemaId: string, movieId: string) {
|
|
|
+ const res = await Apis.film.getFilmMovieList({ data: { cinemaId, movieId } })
|
|
|
+ res.data.data.movieShows?.forEach((i: any) => {
|
|
|
+ if (i.shows.length) {
|
|
|
+ i.shows.forEach((it: any, idx: number) => {
|
|
|
+ if (active.value.includes(it.showDate)) {
|
|
|
+ screenList.value = i.shows[idx].sessions
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+ console.log(2222222, screenList.value)
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 获取座位详情
|
|
|
+ */
|
|
|
+async function getData(sessionId: string, movieId: string, cinemaId: string): Promise<void> {
|
|
|
+ uni.showLoading({ title: '加载中...' })
|
|
|
+ const res = await Apis.film.getFilmMovieSeat({ data: { sessionId, movieId, cinemaId } })
|
|
|
+ uni.hideLoading()
|
|
|
+ getScreenList(res.data.cinemaId, res.data.movieId)
|
|
|
+ active.value = res.data.showTime
|
|
|
+ seatInfo.value = res.data
|
|
|
+ // seatInfo.value.seatSection = []
|
|
|
+ query.value.hallName = res.data.hall
|
|
|
+ query.value.cinemaCode = res.data.cinemaCode
|
|
|
+ query.value.movieCode = res.data.movieCode
|
|
|
+ query.value.sessionBeginTime = res.data.showTime
|
|
|
+ query.value.cinemaName = res.data.cinemaName
|
|
|
+ query.value.movieName = res.data.movieName
|
|
|
+ query.value.postImageUrl = res.data.postImageUrl
|
|
|
+ areaList.value = res.data.areaPriceList
|
|
|
+ areaList.value.sort((a, b) => Number(b.originPrice) - Number(a.originPrice))
|
|
|
+ areaList.value.forEach((item: AreaItem, index: number) => {
|
|
|
+ item.strokeStyle = colorList[index % colorList.length] || '#ccc'
|
|
|
+ item.areaName = areaArr.value[index]
|
|
|
+ })
|
|
|
+ for (let index = 0; index < res.data.seatSection.seatRows.length; index++) {
|
|
|
+ const item = res.data.seatSection.seatRows[index]
|
|
|
+ for (let i = 0; i < item.columns.length; i++) {
|
|
|
+ const seat = item.columns[i]
|
|
|
+ let price = 0
|
|
|
+ let marketPrice = 0
|
|
|
+ let fastPrice = 0
|
|
|
+ for (let d = 0; d < res.data.areaPriceList.length; d++) {
|
|
|
+ const el = res.data.areaPriceList[d]
|
|
|
+ if (el.areaId === seat.areaId) {
|
|
|
+ // 价格
|
|
|
+ price = el.price
|
|
|
+ marketPrice = el.originPrice
|
|
|
+ fastPrice = el.fastPrice
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 桌椅状态
|
|
|
+
|
|
|
+ let status = 0
|
|
|
+ if (seat.state === 0 || seat.state === 4) {
|
|
|
+ status = -2
|
|
|
+ }
|
|
|
+ else if (seat.state === 1) {
|
|
|
+ status = 1
|
|
|
+ }
|
|
|
+ else if (seat.state === 2) {
|
|
|
+ status = 0
|
|
|
+ }
|
|
|
+ else if (seat.state === 3) {
|
|
|
+ status = 10
|
|
|
+ }
|
|
|
+ else if (seat.state === 10) {
|
|
|
+ status = 1
|
|
|
+ }
|
|
|
+ else if (seat.state === 11) {
|
|
|
+ status = 1
|
|
|
+ }
|
|
|
+ // 座位类型
|
|
|
+ let seatType = 0
|
|
|
+ if (seat.seatType === 2) {
|
|
|
+ seatType = 1
|
|
|
+ }
|
|
|
+ else if (seat.seatType === 3) {
|
|
|
+ seatType = 3
|
|
|
+ }
|
|
|
+
|
|
|
+ seatList.value.push({
|
|
|
+ status,
|
|
|
+ seatType,
|
|
|
+ seatName: seat.seatName,
|
|
|
+ seatId: seat.originSeatID,
|
|
|
+ columnId: seat.colId,
|
|
|
+ rowId: item.rowsId,
|
|
|
+ areaId: seat.areaId,
|
|
|
+ marketPrice,
|
|
|
+ ticketPrice: price,
|
|
|
+ fastPrice,
|
|
|
+ serviceFee: 0,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(2222, areaList.value)
|
|
|
+
|
|
|
+ // 赋值座位图配置项(严格符合 SeatmapOptions 接口)
|
|
|
+ options.value = {
|
|
|
+ title: '',
|
|
|
+ hallName: seatInfo.value.hall as string,
|
|
|
+ areaList: areaList.value,
|
|
|
+ footerHeight: 470,
|
|
|
+ // maxSelectNum: 4, // 可选属性,开启后无类型报错
|
|
|
+ // isolateSeats: true, // 可选属性,开启后无类型报错
|
|
|
+ }
|
|
|
+
|
|
|
+ // 可选链访问,避免 cinema 为 null 时报错
|
|
|
+ // const cinemaName = schedule.value.cinema?.cinema_name || '默认影院'
|
|
|
+ // uni.setNavigationBarTitle({ title: cinemaName })
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 选定并购买座位
|
|
|
+ */
|
|
|
+async function buySeat() {
|
|
|
+ if (loading.value)
|
|
|
+ return false
|
|
|
+ if (chooseSeatList.value.length === 0) {
|
|
|
+ return uni.showToast({
|
|
|
+ title: '请选择座位',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 2000,
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // 可选链调用,避免组件实例为 null 时报错
|
|
|
+ if (seatmapRef.value?.validateIsolates()) {
|
|
|
+ uni.showToast({
|
|
|
+ title: '左右座位请不要留空!',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 2000,
|
|
|
+ })
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ query.value.seatNames = chooseSeatList.value.map(item => item.seatName).join(',')
|
|
|
+ query.value.planType = seatInfo.value?.language as string + seatInfo.value?.planType
|
|
|
+ uni.showToast({
|
|
|
+ title: '开始锁座',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 2000,
|
|
|
+ })
|
|
|
+ uni.setStorageSync('film-info', { showTime: active.value, endTime: seatInfo.value.endTime, cinemaName: seatInfo.value.cinemaName, chooseSeatList: chooseSeatList.value, language: seatInfo.value.language, planType: seatInfo.value.planType, totalPrice: totalPrice.value, phone: phone.value })
|
|
|
+ router.push({ name: 'film-submit-order', params: { query: JSON.stringify(query.value) } })
|
|
|
+ console.log(query.value)
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 取消选定座位
|
|
|
+ * @param seat 要取消的座位信息
|
|
|
+ */
|
|
|
+function cancelSeat(seat: SeatInfo): void {
|
|
|
+ seatmapRef.value?.cancelSeat(seat)
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 座位图错误回调
|
|
|
+ * @param e 错误事件对象
|
|
|
+ */
|
|
|
+function onError(e: { message: string }): void {
|
|
|
+ uni.showToast({
|
|
|
+ title: e.message || '座位图加载失败',
|
|
|
+ icon: 'none',
|
|
|
+ duration: 2000,
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 座位点击回调
|
|
|
+ * @param event 座位点击事件对象
|
|
|
+ */
|
|
|
+function onSeatTap(event: SeatTapEvent): void {
|
|
|
+ chooseSeatList.value = event.chooseSeatList
|
|
|
+ console.log('zuoshsjd', chooseSeatList.value)
|
|
|
+ // 遍历已选座位列表,累加价格(严格类型访问)
|
|
|
+ let price = 0
|
|
|
+ for (const item of chooseSeatList.value) {
|
|
|
+ if (item.marketPrice as number > price) {
|
|
|
+ price = item.marketPrice as number
|
|
|
+ }
|
|
|
+ }
|
|
|
+ query.value.originPrice = price
|
|
|
+}
|
|
|
+
|
|
|
+// ************************ 页面生命周期(严格类型,无报错) ************************
|
|
|
+onLoad(async (opt): Promise<void> => {
|
|
|
+ phone.value = opt?.phone
|
|
|
+ await getData(opt?.sessionId, opt?.movieId, opt?.cinemaId)
|
|
|
+
|
|
|
+ // 等待 DOM 渲染完成后初始化座位图
|
|
|
+ await nextTick()
|
|
|
+
|
|
|
+ // 可选链 + 类型守卫,确保调用安全无报错
|
|
|
+ if (seatmapRef.value) {
|
|
|
+ seatmapRef.value.init({
|
|
|
+ seatList: seatList.value,
|
|
|
+ options: options.value,
|
|
|
+ })
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+function handleTime(time: string, sessionId: string) {
|
|
|
+ if (time === active.value) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ active.value = time
|
|
|
+ router.push({ name: 'film-choose-seat', params: { sessionId, movieId: seatInfo.value.movieId as string, cinemaId: seatInfo.value.cinemaId } })
|
|
|
+}
|
|
|
+function handleSwitch() {
|
|
|
+ console.log(11111)
|
|
|
+
|
|
|
+ showTime.value = !showTime.value
|
|
|
+}
|
|
|
+
|
|
|
+// 获取时间
|
|
|
+function judgeNearDaysSimple(time: any) {
|
|
|
+ const today = new Date(new Date().setHours(0, 0, 0, 0)).getTime()
|
|
|
+ const target = new Date(new Date(time).setHours(0, 0, 0, 0)).getTime()
|
|
|
+ const dayDiff = Math.floor((target - today) / 86400000)
|
|
|
+
|
|
|
+ return { 0: '今天', 1: '明天', 2: '后天' }[dayDiff] || ''
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<template>
|
|
|
+ <view class="view-page">
|
|
|
+ <!-- 顶部价格区域 -->
|
|
|
+ <view v-if="seatInfo.cinemaId" class="areaList">
|
|
|
+ <view class="area-item">
|
|
|
+ <view class="area" style="background-color:#AAAAAA" />
|
|
|
+ <view class="area-price">
|
|
|
+ 已售
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view v-for="(item, index) in areaList" :key="index" class="area-item">
|
|
|
+ <view class="area" :style="`border:1px solid ${item.strokeStyle}`" />
|
|
|
+ <view class="area-price">
|
|
|
+ {{ `${item.areaName} ${isWithin45Minutes(new Date(), seatInfo.showTime) ? item.fastPrice : item.price}` }}
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <canvasSeatmap ref="seatmapRef" @error="onError" @on-seat-tap="onSeatTap" />
|
|
|
+
|
|
|
+ <!-- 底部信息 -->
|
|
|
+ <view v-if="seatInfo.cinemaId" class="footer">
|
|
|
+ <view class="schedule bg-white">
|
|
|
+ <view class="name-box">
|
|
|
+ <view class="film-name">
|
|
|
+ {{ seatInfo.movieName }}
|
|
|
+ </view>
|
|
|
+ <view class="change-box" @click="handleSwitch">
|
|
|
+ <view>切换场次</view>
|
|
|
+ <image class="arrow" :src="`${StaticUrl}/film-black-arrow.png`" mode="scaleToFill" />
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="film-info">
|
|
|
+ <text style="color:red">
|
|
|
+ {{ judgeNearDaysSimple(seatInfo.showTime) || seatInfo.showTime?.split('T')[0] }}
|
|
|
+ </text>
|
|
|
+ {{ seatInfo.showTime?.split('T')[1] }} -
|
|
|
+ {{ seatInfo.endTime?.split('T')[1] }}
|
|
|
+ {{ seatInfo.planType }} {{ seatInfo.language }}
|
|
|
+ </view>
|
|
|
+ <!-- 切换场次 -->
|
|
|
+ <view v-if="showTime" class="time-list">
|
|
|
+ <view
|
|
|
+ v-for="(item, index) in screenList" :key="index" class="time-item"
|
|
|
+ :class="[active == item.showTime ? 'active' : '']" @click="handleTime(item.showTime, item.sessionId)"
|
|
|
+ >
|
|
|
+ <view class="time">
|
|
|
+ {{ item.showTime.split('T')[1] }}
|
|
|
+ </view>
|
|
|
+ <view class="type">
|
|
|
+ {{ item.language }} {{ item.planType }}
|
|
|
+ </view>
|
|
|
+ <view class="price">
|
|
|
+ ¥{{ item.sellPrice }}起
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view v-if="chooseSeatList.length" class="seat-list">
|
|
|
+ <view v-for="(seat, optindex) in chooseSeatList" :key="optindex" class="seat-item">
|
|
|
+ <view class="seat">
|
|
|
+ <view class="seat-name">
|
|
|
+ {{ seat.seatName }}
|
|
|
+ </view>
|
|
|
+ <view class="seat-price">
|
|
|
+ <!-- ¥{{ priceConversion(seat.marketPrice) }} -->
|
|
|
+ ¥{{ isWithin45Minutes(new Date(), seatInfo.showTime) ? seat.fastPrice : seat.ticketPrice }}
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="delete-seat" @click="cancelSeat(seat)">
|
|
|
+ ×
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="notice bg-white">
|
|
|
+ 请尽快选座,座位可能被抢!
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <wd-button
|
|
|
+ custom-class="confirm" block :loading="isLoging" :class="{ unbtn: !chooseSeatList.length || loading }"
|
|
|
+ @click="buySeat"
|
|
|
+ >
|
|
|
+ {{ chooseSeatList.length ? (`¥${totalPrice} 确认座位`) : '请选座位' }}
|
|
|
+ </wd-button>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.view-page {
|
|
|
+ background-color: #f7f9fc;
|
|
|
+}
|
|
|
+
|
|
|
+.bg-white {
|
|
|
+ background-color: #fff;
|
|
|
+}
|
|
|
+
|
|
|
+/* 核心样式 */
|
|
|
+.areaList {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ /* 上下居中 */
|
|
|
+ /* 左右居中(可选) */
|
|
|
+ height: 40px;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow-x: auto;
|
|
|
+ scrollbar-width: none;
|
|
|
+ /* Firefox */
|
|
|
+ -ms-overflow-style: none;
|
|
|
+ /* IE/Edge */
|
|
|
+}
|
|
|
+
|
|
|
+.areaList::-webkit-scrollbar {
|
|
|
+ display: none;
|
|
|
+ /* Chrome、Safari、微信小程序-web-view */
|
|
|
+}
|
|
|
+
|
|
|
+.area-item {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ white-space: nowrap;
|
|
|
+ margin: 0 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.area {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.area-price {
|
|
|
+ margin-left: 10rpx;
|
|
|
+ font-size: 20rpx;
|
|
|
+ line-height: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.footer {
|
|
|
+ position: fixed;
|
|
|
+ left: 0;
|
|
|
+ bottom: var(--window-bottom);
|
|
|
+ padding: 0 15px 15px 15px;
|
|
|
+ width: 100vw;
|
|
|
+ box-sizing: border-box;
|
|
|
+ border-radius: 5px 5px 0% 0%;
|
|
|
+ z-index: 999999999;
|
|
|
+ background: #FFFFFF;
|
|
|
+ border-radius: 16rpx 16rpx 0rpx 0rpx;
|
|
|
+ padding: 28rpx 24rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+ .notice{
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #FF4A39;
|
|
|
+ margin-top: 24rpx;
|
|
|
+ margin-bottom: 24rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .schedule {
|
|
|
+
|
|
|
+ .name-box {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ .film-name {
|
|
|
+ font-weight: bold;
|
|
|
+ font-size: 32rpx;
|
|
|
+ color: #222222;
|
|
|
+ }
|
|
|
+ .change-box {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #222222;
|
|
|
+ .arrow{
|
|
|
+ width: 32rpx;
|
|
|
+ height: 32rpx;
|
|
|
+ transform: rotate(90deg);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .film-info {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #AAAAAA;
|
|
|
+ margin-top: 20rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+}
|
|
|
+.time-list{
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: nowrap;
|
|
|
+ scrollbar-width: none;
|
|
|
+ margin-top: 24rpx;
|
|
|
+ overflow-x: auto;
|
|
|
+ white-space: nowrap;
|
|
|
+ /* Firefox */
|
|
|
+ -ms-overflow-style: none;
|
|
|
+ /* IE/Edge */
|
|
|
+ .time-item{
|
|
|
+ flex-shrink: 0;
|
|
|
+ width: 160rpx;
|
|
|
+ height: 114rpx;
|
|
|
+ background: #F9F9F9;
|
|
|
+ border-radius: 16rpx 16rpx 16rpx 16rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+ padding: 10rpx 0;
|
|
|
+ text-align: center;
|
|
|
+ margin-right: 20rpx;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ .time{
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #222222;
|
|
|
+ }
|
|
|
+ .type{
|
|
|
+ font-size: 22rpx;
|
|
|
+ color: #AAAAAA;
|
|
|
+ }
|
|
|
+ .price{
|
|
|
+ font-size: 22rpx;
|
|
|
+ color: #222222;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .time-item.active {
|
|
|
+ background: #FFE7E5;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+.seat-list {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: flex-start;
|
|
|
+ overflow-x: auto;
|
|
|
+ white-space: nowrap;
|
|
|
+ margin-top: 5px;
|
|
|
+ scrollbar-width: none;
|
|
|
+ margin-top: 24rpx;
|
|
|
+ /* Firefox */
|
|
|
+ -ms-overflow-style: none;
|
|
|
+ /* IE/Edge */
|
|
|
+}
|
|
|
+
|
|
|
+.seat-list::-webkit-scrollbar {
|
|
|
+ display: none;
|
|
|
+ /* Chrome、Safari、微信小程序-web-view */
|
|
|
+}
|
|
|
+
|
|
|
+.seat-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ height: 80rpx;
|
|
|
+ background: #F9F9F9;
|
|
|
+ border-radius: 16rpx 16rpx 16rpx 16rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+ padding: 10rpx 16rpx;
|
|
|
+ margin-right: 20rpx;
|
|
|
+ .seat-name {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #222222;
|
|
|
+ }
|
|
|
+
|
|
|
+ .seat-price {
|
|
|
+ font-size: 22rpx;
|
|
|
+ color: #222222;
|
|
|
+ margin-top: 10rpx;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ .delete-seat {
|
|
|
+ width: 20px;
|
|
|
+ height: 20px;
|
|
|
+ line-height: 20px;
|
|
|
+ color: #727272;
|
|
|
+ text-align: center;
|
|
|
+ margin-left: 16rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+.confirm {
|
|
|
+ width: 100%;
|
|
|
+ height: 80rpx;
|
|
|
+ line-height: 80rpx;
|
|
|
+ text-align: center;
|
|
|
+ border-radius: 10px;
|
|
|
+ font-size: 14px;
|
|
|
+ color: #fff;
|
|
|
+ background-color: #e54f4f;
|
|
|
+}
|
|
|
+
|
|
|
+.unbtn {
|
|
|
+ background-color: #f69c9c;
|
|
|
+}
|
|
|
+</style>
|