- 设计令牌层:mm-* CSS 变量与工具类、uView 换肤、优设标题黑字体 - 九个页面全部重写为新风格(列表/详情/下单/出行人/订单/支付成功/登录) - 补齐 H5 运行时缺失的基础样式(uni-view/scroll-view/toast/swiper 等,App.vue 全局) - 修复:详情头图轮播、预订须知弹层滚动、出行人数量按购买数限制、订单详情去箭头 - 新增 gmy-back-touch 返回悬浮钮,除首页外全页接入,点击返回上一页 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
988 lines
23 KiB
Vue
988 lines
23 KiB
Vue
<template>
|
||
<view v-if="skuObj" class="page mm-page">
|
||
<gmy-back-touch></gmy-back-touch>
|
||
<!-- xuchao: 商品与日期/时段/数量选择卡 -->
|
||
<view class="sec mm-card mm-fade-up">
|
||
<view class="sku__name">{{skuObj.sku.skuName}}</view>
|
||
<view class="sku__age">适用年龄:{{skuObj.sku.typeInfo}}</view>
|
||
|
||
<view class="divider"></view>
|
||
|
||
<view class="label">使用日期</view>
|
||
<view class="dates">
|
||
<view v-for="(item,index) in cList" :key="index" v-if="index<3"
|
||
:class="['date', item.isSelect?'date--on':'', (!item.isEnabled&&!item.isSelect)?'date--off':'']"
|
||
@click="selectDate(item)">
|
||
{{getDayName(item.enableDate)}}<br />
|
||
{{formatToMonthDay(item.enableDate)}}
|
||
<view class="date__tag">{{item.isSelect?'√':item.isEnabled?'可订':'不可订'}}</view>
|
||
</view>
|
||
<view class="date date--more" @tap="show=true">更多<br />日期
|
||
<u-icon name="arrow-right" color="#64748B" size="22"></u-icon>
|
||
</view>
|
||
</view>
|
||
|
||
<template v-if="!skuObj.sku.stockDateType">
|
||
<view class="label">适用时段</view>
|
||
<view class="slots">
|
||
<view :class="['slot', item.isSelect?'slot--on':'']" v-for="(item,index) in timeSlotList"
|
||
:key="index" @click="selectSlot(index)">{{item.name}}
|
||
<view class="slot__tag">余{{item.stock}}张</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<view class="divider"></view>
|
||
|
||
<!-- xuchao: 购买数量 —— 自定义步进器:数字居中,加减号点击即加减,上限取当前日期/时段库存 -->
|
||
<view class="qty">
|
||
<text class="qty__label">购买数量</text>
|
||
<view class="stepper">
|
||
<view :class="['stepper__btn', value<=1?'stepper__btn--off':'']" @click="minusNum">
|
||
<u-icon name="minus" :color="value<=1?'#CBD5E1':'#0052FF'" size="26"></u-icon>
|
||
</view>
|
||
<text class="stepper__val mm-display">{{value}}</text>
|
||
<view :class="['stepper__btn', value>=getMaxStock()?'stepper__btn--off':'']" @click="plusNum">
|
||
<u-icon name="plus" :color="value>=getMaxStock()?'#CBD5E1':'#0052FF'" size="26"></u-icon>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- xuchao: 游客信息卡(需实名入园时) -->
|
||
<view class="sec mm-card mm-fade-up" v-if="skuObj.sku.enterDemand=='2'">
|
||
<view class="sec__head">
|
||
<view class="mm-sec-title">游客信息</view>
|
||
<view class="sec__hint">需要 <text class="sec__hint-num">{{value}}位</text> 出行游客信息,用于入园身份验证</view>
|
||
</view>
|
||
|
||
<view class="persons">
|
||
<view class="p_on mm-grad" v-for="(item,index) in personList" :key="index">{{item.name}}</view>
|
||
<view class="p_add" @tap="addPerson">新增/更换
|
||
<u-icon name="arrow-right" color="#64748B" size="20"></u-icon>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="p_row" v-for="(item,index) in personList" :key="index">
|
||
<view class="p_row__del" @click="deletePerson(index)">
|
||
<u-icon name="close-circle" color="#CBD5E1" size="36"></u-icon>
|
||
</view>
|
||
<text class="p_row__label">游客{{index+1}}</text>
|
||
<view class="p_row__info">
|
||
<view class="p_row__name">{{item.name}}</view>
|
||
<view class="p_row__id">{{item.identityNo}}</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="c_row">
|
||
<text class="c_row__label">联系手机</text>
|
||
<text class="c_row__val">{{createInfo.mobile}}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- xuchao: 联系人信息卡(无需实名时) -->
|
||
<view class="sec mm-card mm-fade-up" v-else>
|
||
<view class="mm-sec-title">联系人信息</view>
|
||
<view class="c_row">
|
||
<text class="c_row__label">联系人</text>
|
||
<input class="c_row__input" :adjust-position="false" type="text" v-model="createInfo.contactName"
|
||
placeholder="请输入联系人姓名" placeholder-class="mm-ph" />
|
||
</view>
|
||
<view class="c_row">
|
||
<text class="c_row__label">手机号</text>
|
||
<text class="c_row__val">{{createInfo.mobile}}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- xuchao: 更多日期日历(选中色随主题) -->
|
||
<u-calendar v-model="show" :mode="mode" activeBgColor="#0052FF" :calendarList="calendarList"
|
||
@change="change"></u-calendar>
|
||
|
||
<!-- xuchao: 底部结算栏 —— 总价 + 明细 + 立即购买 -->
|
||
<view class="bar">
|
||
<view class="bar__sum">
|
||
<view class="bar__label">总价</view>
|
||
<view class="bar__price" @tap="show1=true">
|
||
<text class="bar__cur">¥</text>
|
||
<text class="bar__num mm-display mm-grad-text">{{salesPriceSum}}</text>
|
||
<view class="bar__detail">明细
|
||
<u-icon name="arrow-down-fill" color="#0052FF" size="20"></u-icon>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="bar__buy mm-grad" @tap="buy">立即购买</view>
|
||
</view>
|
||
|
||
<!-- xuchao: 费用明细弹层 -->
|
||
<u-popup v-model="show1" border-radius="20" mode="bottom">
|
||
<view class="pp mm-safe-bottom">
|
||
<view class="mm-sec-title">费用明细</view>
|
||
<view class="pp__sub">景区/场馆</view>
|
||
<view class="pp__row">
|
||
<text class="pp__row-label">门票</text>
|
||
<text class="pp__row-val">¥{{salesPrice}} x{{value}}</text>
|
||
</view>
|
||
<view class="pp__total">
|
||
<text class="pp__total-label">总价</text>
|
||
<view class="pp__total-price">
|
||
<text class="pp__total-cur">¥</text>
|
||
<text class="pp__total-num mm-display mm-grad-text">{{salesPriceSum}}</text>
|
||
</view>
|
||
</view>
|
||
<view class="mm-btn" @click="show1=false">确定</view>
|
||
</view>
|
||
</u-popup>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
// xuchao: 确认订单页 —— 支付统一走 common/pay.js 支付层
|
||
import pay from '@/common/pay.js'
|
||
|
||
var that = ''
|
||
import gmyBackTouch from "@/components/gmy-back-touch/gmy-back-touch.vue"
|
||
export default {
|
||
components: {
|
||
'gmy-back-touch': gmyBackTouch
|
||
},
|
||
data() {
|
||
return {
|
||
show1: false,
|
||
show: false,
|
||
mode: 'date',
|
||
sf: `**** **** **** *1918`,
|
||
mz: `李富贵`,
|
||
value: 1,
|
||
hday: '',
|
||
whday: '',
|
||
skuObj: '',
|
||
calendarList: [],
|
||
mobile: '',
|
||
personList: [],
|
||
objInfo: '',
|
||
createInfo: {
|
||
"ticketsId": 0,
|
||
"skuId": 0,
|
||
"buyNum": 0,
|
||
"calendarId": 0,
|
||
"timeSlot": 0,
|
||
"touristIdList": [],
|
||
"contactMobile": "",
|
||
"contactName": "",
|
||
"price": "",
|
||
"orderSource": "Channel_XGH",
|
||
"mobile": ""
|
||
}
|
||
}
|
||
},
|
||
computed: {
|
||
|
||
salesPrice() {
|
||
let price = 0
|
||
if (this.skuObj) {
|
||
if (this.skuObj.sku.stockDateType) {
|
||
this.calendarList.forEach(item => {
|
||
if (item.isSelect) {
|
||
price = (item.salePrice / 100).toFixed(2)
|
||
}
|
||
})
|
||
} else {
|
||
this.timeSlotList.forEach(item => {
|
||
if (item.isSelect) {
|
||
price = (item.salePrice / 100).toFixed(2)
|
||
}
|
||
})
|
||
}
|
||
|
||
}
|
||
return price
|
||
},
|
||
salesPriceSum() {
|
||
let priceSum = 0
|
||
if (this.skuObj) {
|
||
if (this.skuObj.sku.stockDateType) {
|
||
this.calendarList.forEach(item => {
|
||
if (item.isSelect) {
|
||
priceSum = (item.salePrice * this.value / 100).toFixed(2)
|
||
}
|
||
})
|
||
} else {
|
||
this.timeSlotList.forEach(item => {
|
||
if (item.isSelect) {
|
||
priceSum = (item.salePrice * this.value / 100).toFixed(2)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
return priceSum
|
||
},
|
||
|
||
cList() {
|
||
let arr = []
|
||
|
||
console.log(this.calendarList,12312);
|
||
if (this.calendarList.length != 0) {
|
||
this.calendarList.forEach((item, index) => {
|
||
|
||
if (item.isSelect && index < 3) {
|
||
if(this.calendarList.length==1){
|
||
arr.push(this.calendarList[0])
|
||
}else if(this.calendarList.length==2){
|
||
arr.push(this.calendarList[0])
|
||
arr.push(this.calendarList[1])
|
||
}else if(this.calendarList.length>=3){
|
||
arr.push(this.calendarList[0])
|
||
arr.push(this.calendarList[1])
|
||
arr.push(this.calendarList[2])
|
||
}
|
||
|
||
} else if (item.isSelect && index >= 3) {
|
||
arr.push(this.calendarList[0])
|
||
arr.push(this.calendarList[1])
|
||
arr.push(item)
|
||
}
|
||
})
|
||
}
|
||
console.log(arr);
|
||
return arr
|
||
},
|
||
timeSlotList() {
|
||
let arr = []
|
||
if (this.calendarList.length != 0) {
|
||
this.calendarList.forEach((item, index) => {
|
||
if (item.isSelect) {
|
||
arr = item.timeSlotList
|
||
}
|
||
})
|
||
if (arr) {
|
||
arr.forEach((item1, index1) => {
|
||
if (index1 == 0) {
|
||
this.$set(item1, 'isSelect', true)
|
||
} else {
|
||
this.$set(item1, 'isSelect', false)
|
||
}
|
||
|
||
})
|
||
}
|
||
|
||
}
|
||
return arr
|
||
}
|
||
},
|
||
onLoad() {
|
||
that = this
|
||
this.createInfo.mobile = uni.getStorageSync('phone')
|
||
this.createInfo.contactMobile = uni.getStorageSync('phone')
|
||
this.skuObj = uni.getStorageSync('scenicDetail')
|
||
this.createInfo.ticketsId = this.skuObj.tickets.id
|
||
this.createInfo.skuId = this.skuObj.sku.id
|
||
if (this.skuObj.sku.stockDateType) {
|
||
for (var i = 0; i < this.skuObj.calendarList.length; i++) {
|
||
this.skuObj.calendarList[i].enableDate = this.formatDateTime(this.skuObj.calendarList[i].enableDate)
|
||
if (this.skuObj.sku.bookType == 1) {
|
||
if (i == 0) {
|
||
this.$set(this.skuObj.calendarList[i], 'isSelect', true)
|
||
} else {
|
||
this.$set(this.skuObj.calendarList[i], 'isSelect', false)
|
||
|
||
}
|
||
} else {
|
||
if (i == 1) {
|
||
this.$set(this.skuObj.calendarList[i], 'isSelect', true)
|
||
this.skuObj.calendarList[0].isEnabled = false
|
||
} else {
|
||
this.$set(this.skuObj.calendarList[i], 'isSelect', false)
|
||
}
|
||
}
|
||
|
||
this.calendarList.push(this.skuObj.calendarList[i])
|
||
}
|
||
} else {
|
||
for (var i = 0; i < this.skuObj.timeSlotData.length; i++) {
|
||
this.$set(this.skuObj.timeSlotData[i], 'isEnabled', true)
|
||
this.$set(this.skuObj.timeSlotData[i], 'enableDate', this.formatDateTime(this.skuObj.timeSlotData[i]
|
||
.date))
|
||
if (this.skuObj.sku.bookType == 1) {
|
||
if (i == 0) {
|
||
this.$set(this.skuObj.timeSlotData[i], 'isSelect', true)
|
||
|
||
} else {
|
||
this.$set(this.skuObj.timeSlotData[i], 'isSelect', false)
|
||
|
||
}
|
||
} else {
|
||
if (i == 1) {
|
||
this.$set(this.skuObj.timeSlotData[i], 'isSelect', true)
|
||
this.skuObj.timeSlotData[0].isEnabled = false
|
||
} else {
|
||
this.$set(this.skuObj.timeSlotData[i], 'isSelect', false)
|
||
}
|
||
}
|
||
this.calendarList.push(this.skuObj.timeSlotData[i])
|
||
}
|
||
}
|
||
console.log(this.calendarList);
|
||
},
|
||
onShow() {
|
||
var pages = getCurrentPages();
|
||
var prevPage = pages[pages.length - 1];
|
||
if (prevPage.objitem != undefined) {
|
||
that.isshow = true
|
||
that.personList = JSON.parse(prevPage.objitem);
|
||
}
|
||
},
|
||
methods: {
|
||
formatDateTime(isoString) {
|
||
// const date = new Date(isoString);
|
||
|
||
// const year = date.getFullYear();
|
||
// const month = String(date.getMonth() + 1).padStart(2, '0');
|
||
// const day = String(date.getDate()).padStart(2, '0');
|
||
// const hours = String(date.getHours()).padStart(2, '0');
|
||
// const minutes = String(date.getMinutes()).padStart(2, '0');
|
||
// const seconds = String(date.getSeconds()).padStart(2, '0');
|
||
|
||
return isoString.replace(/-/g, "/");;
|
||
},
|
||
getMaxStock() {
|
||
let stock = 0
|
||
if (this.skuObj.sku.stockNumType) {
|
||
stock = 99999
|
||
} else {
|
||
if (this.skuObj.sku.stockDateType) {
|
||
this.calendarList.forEach(item => {
|
||
if (item.isSelect) {
|
||
stock = item.stock
|
||
}
|
||
})
|
||
} else {
|
||
this.timeSlotList.forEach(item => {
|
||
if (item.isSelect) {
|
||
stock = item.stock
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
|
||
return stock
|
||
},
|
||
deletePerson(e) {
|
||
this.personList.splice(e, 1)
|
||
},
|
||
selectDate(e) {
|
||
if (e.isEnabled) {
|
||
this.cList.forEach(item => {
|
||
if (item.enableDate == e.enableDate) {
|
||
item.isSelect = true
|
||
} else {
|
||
item.isSelect = false
|
||
}
|
||
})
|
||
}
|
||
this.value = 1
|
||
},
|
||
selectSlot(e) {
|
||
this.timeSlotList.forEach((item, index) => {
|
||
if (e == index) {
|
||
this.timeSlotList[index].isSelect = true
|
||
} else {
|
||
this.timeSlotList[index].isSelect = false
|
||
}
|
||
})
|
||
this.value = 1
|
||
},
|
||
formatToMonthDay(dateStr) {
|
||
const date = new Date(dateStr)
|
||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||
const day = date.getDate().toString().padStart(2, '0');
|
||
return `${month}月${day}日`;
|
||
},
|
||
// xuchao: 数量步进 —— 减到下限 1 为止,加到当前库存上限并提示
|
||
minusNum() {
|
||
if (this.value > 1) this.value--
|
||
},
|
||
plusNum() {
|
||
if (this.value < this.getMaxStock()) {
|
||
this.value++
|
||
} else {
|
||
uni.showToast({
|
||
icon: 'none',
|
||
title: '库存不足了',
|
||
duration: 1500
|
||
})
|
||
}
|
||
},
|
||
change(e) {
|
||
this.calendarList.forEach((item, index) => {
|
||
if (new Date(item.enableDate).getTime() == new Date(e.result + ' 00:00:00').getTime()) {
|
||
item.isSelect = true
|
||
} else {
|
||
item.isSelect = false
|
||
}
|
||
})
|
||
this.value = 1
|
||
},
|
||
formatDateToMMDD(dateStr) {
|
||
const [year, month, day] = dateStr.split('-');
|
||
return `${month.padStart(2, '0')}月${day.padStart(2, '0')}日`;
|
||
},
|
||
buy() {
|
||
// uni.showModal({
|
||
// title: '提示',
|
||
// content: '预订人同一证件号1天内,最多可购买1份,当前超限制的证件号尾号为0514',
|
||
// showCancel: false
|
||
// })
|
||
if (this.skuObj.sku.enterDemand == '2') {
|
||
if (this.value > this.personList.length) {
|
||
uni.showToast({
|
||
icon: 'none',
|
||
title: '还需填写' + (this.value - this.personList.length) + '位游客信息',
|
||
duration: 2000
|
||
});
|
||
return
|
||
}
|
||
// xuchao: 出行人多于购买数量也拦截(如先选多人后减量),提示移除多余位
|
||
if (this.personList.length > this.value) {
|
||
uni.showToast({
|
||
icon: 'none',
|
||
title: '只需' + this.value + '位出行人,请移除多余' + (this.personList.length - this.value) + '位',
|
||
duration: 2000
|
||
});
|
||
return
|
||
}
|
||
} else {
|
||
if (this.createInfo.contactName == '') {
|
||
uni.showToast({
|
||
icon: 'none',
|
||
title: '请填写联系人姓名',
|
||
duration: 2000
|
||
});
|
||
return
|
||
}
|
||
}
|
||
this.createInfo.buyNum = this.value
|
||
if (this.skuObj.sku.stockDateType) {
|
||
this.createInfo.calendarId = this.cList.filter(item => {
|
||
return item.isSelect
|
||
})[0].id
|
||
} else {
|
||
this.createInfo.timeSlot = this.timeSlotList.filter(item => {
|
||
return item.isSelect
|
||
})[0].id
|
||
}
|
||
this.createInfo.price = this.salesPriceSum
|
||
this.createInfo.touristIdList = this.personList.map(item => {
|
||
return item.id
|
||
})
|
||
console.log(this.createInfo);
|
||
this.$u.api.createTicketsOrder(this.createInfo).then(res => {
|
||
if (res.errcode == 0) {
|
||
// xuchao: 支付层 —— 开发模式模拟成功,正式模式唤起微信支付
|
||
pay.pay(this, {
|
||
orderId: res.data,
|
||
onSuccess: () => {
|
||
uni.redirectTo({
|
||
url: 'paysuccess?id=' + res.data
|
||
})
|
||
},
|
||
onFail: () => {
|
||
uni.showToast({
|
||
icon: 'none',
|
||
title: '支付未完成,可在订单中继续支付',
|
||
duration: 2000
|
||
})
|
||
setTimeout(() => {
|
||
uni.redirectTo({
|
||
url: 'orderlist'
|
||
})
|
||
}, 1500)
|
||
}
|
||
})
|
||
}
|
||
})
|
||
},
|
||
addPerson() {
|
||
uni.navigateTo({
|
||
url: 'select?value=' + this.value
|
||
})
|
||
},
|
||
getFormattedDate(offset = 0) {
|
||
const date = new Date();
|
||
date.setDate(date.getDate() + offset);
|
||
|
||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||
const day = date.getDate().toString().padStart(2, '0');
|
||
|
||
return `${month}月${day}日`;
|
||
},
|
||
getDayName(date) {
|
||
const today = new Date();
|
||
today.setHours(0, 0, 0, 0);
|
||
|
||
const inputDate = new Date(date);
|
||
inputDate.setHours(0, 0, 0, 0);
|
||
|
||
const diffDays = Math.round((inputDate - today) / (1000 * 60 * 60 * 24));
|
||
|
||
// 处理今天/明天/后天
|
||
if (diffDays === 0) return "今天";
|
||
if (diffDays === 1) return "明天";
|
||
if (diffDays === 2) return "后天";
|
||
|
||
// 其他日期返回星期几
|
||
const weekDays = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"];
|
||
return weekDays[inputDate.getDay()];
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="less">
|
||
/* xuchao: 底部预留 200rpx,避免内容被固定结算栏遮挡 */
|
||
.page {
|
||
padding: 32rpx 32rpx 200rpx;
|
||
}
|
||
|
||
.sec {
|
||
margin-top: 24rpx;
|
||
padding: 32rpx 24rpx;
|
||
|
||
&:first-child {
|
||
margin-top: 0;
|
||
}
|
||
}
|
||
|
||
.divider {
|
||
margin: 28rpx 0;
|
||
border-top: 2rpx dashed var(--mm-border);
|
||
}
|
||
|
||
.label {
|
||
font-size: 24rpx;
|
||
color: var(--mm-muted-fg);
|
||
margin-top: 4rpx;
|
||
}
|
||
|
||
/* ---------- 商品摘要 ---------- */
|
||
.sku__name {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
line-height: 1.4;
|
||
color: var(--mm-fg);
|
||
}
|
||
|
||
.sku__age {
|
||
margin-top: 8rpx;
|
||
font-size: 22rpx;
|
||
color: var(--mm-muted-fg);
|
||
}
|
||
|
||
/* ---------- 日期 chips ---------- */
|
||
.dates {
|
||
display: flex;
|
||
gap: 8rpx;
|
||
margin-top: 16rpx;
|
||
}
|
||
|
||
.date {
|
||
flex: 1;
|
||
height: 170rpx;
|
||
border: 2rpx solid var(--mm-border);
|
||
border-radius: 16rpx;
|
||
background: var(--mm-card);
|
||
color: var(--mm-muted-fg);
|
||
position: relative;
|
||
font-size: 26rpx;
|
||
line-height: 1.5;
|
||
padding: 40rpx 0 0 24rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.date--off {
|
||
background: var(--mm-muted);
|
||
color: #CBD5E1;
|
||
}
|
||
|
||
.date--on {
|
||
border-color: var(--mm-accent);
|
||
background: rgba(0, 82, 255, 0.04);
|
||
color: var(--mm-accent);
|
||
}
|
||
|
||
.date__tag {
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
height: 34rpx;
|
||
padding: 0 12rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 18rpx;
|
||
background: var(--mm-muted);
|
||
color: var(--mm-muted-fg);
|
||
border-radius: 0 14rpx 0 14rpx;
|
||
}
|
||
|
||
.date--on .date__tag {
|
||
background: var(--mm-grad);
|
||
color: #fff;
|
||
}
|
||
|
||
.date--more {
|
||
flex: none;
|
||
width: 96rpx;
|
||
padding: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 22rpx;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
/* ---------- 时段 chips ---------- */
|
||
.slots {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 12rpx;
|
||
margin-top: 16rpx;
|
||
}
|
||
|
||
.slot {
|
||
width: 210rpx;
|
||
height: 110rpx;
|
||
border: 2rpx solid var(--mm-border);
|
||
border-radius: 16rpx;
|
||
color: var(--mm-muted-fg);
|
||
font-size: 26rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
position: relative;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.slot--on {
|
||
border-color: var(--mm-accent);
|
||
background: rgba(0, 82, 255, 0.04);
|
||
color: var(--mm-accent);
|
||
}
|
||
|
||
.slot__tag {
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
height: 34rpx;
|
||
padding: 0 12rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 18rpx;
|
||
background: var(--mm-muted);
|
||
color: var(--mm-muted-fg);
|
||
border-radius: 0 14rpx 0 14rpx;
|
||
}
|
||
|
||
.slot--on .slot__tag {
|
||
background: var(--mm-grad);
|
||
color: #fff;
|
||
}
|
||
|
||
/* ---------- 购买数量 ---------- */
|
||
.qty {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.qty__label {
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
color: var(--mm-fg);
|
||
}
|
||
|
||
/* xuchao: 数量步进器 —— 两端描边按钮、中间数字居中 */
|
||
.stepper {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.stepper__btn {
|
||
width: 64rpx;
|
||
height: 64rpx;
|
||
border-radius: 16rpx;
|
||
border: 2rpx solid var(--mm-border);
|
||
background: var(--mm-card);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
transition: transform 0.15s ease;
|
||
}
|
||
|
||
.stepper__btn:active {
|
||
transform: scale(0.92);
|
||
background: var(--mm-muted);
|
||
}
|
||
|
||
.stepper__btn--off {
|
||
border-color: var(--mm-muted);
|
||
background: var(--mm-muted);
|
||
}
|
||
|
||
.stepper__val {
|
||
width: 110rpx;
|
||
text-align: center;
|
||
font-size: 36rpx;
|
||
color: var(--mm-fg);
|
||
}
|
||
|
||
/* ---------- 游客/联系人 ---------- */
|
||
.sec__head {
|
||
display: flex;
|
||
align-items: flex-end;
|
||
}
|
||
|
||
.sec__hint {
|
||
margin-left: 16rpx;
|
||
font-size: 20rpx;
|
||
color: var(--mm-muted-fg);
|
||
}
|
||
|
||
.sec__hint-num {
|
||
font-size: 24rpx;
|
||
font-weight: 600;
|
||
color: var(--mm-fg);
|
||
}
|
||
|
||
.persons {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 16rpx;
|
||
margin-top: 24rpx;
|
||
}
|
||
|
||
/* xuchao: 已选游客徽章 —— 渐变胶囊(保持原有识别度) */
|
||
.p_on {
|
||
height: 60rpx;
|
||
padding: 0 24rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
border-radius: 999rpx;
|
||
color: #fff;
|
||
font-size: 26rpx;
|
||
box-shadow: var(--mm-shadow-accent);
|
||
}
|
||
|
||
.p_add {
|
||
height: 60rpx;
|
||
padding: 0 24rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4rpx;
|
||
border-radius: 999rpx;
|
||
border: 2rpx dashed var(--mm-border);
|
||
color: var(--mm-muted-fg);
|
||
font-size: 26rpx;
|
||
}
|
||
|
||
.p_row {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 24rpx 0;
|
||
border-bottom: 2rpx solid var(--mm-border);
|
||
}
|
||
|
||
.p_row__del {
|
||
flex: none;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.p_row__label {
|
||
flex: none;
|
||
margin-left: 20rpx;
|
||
font-size: 26rpx;
|
||
color: var(--mm-muted-fg);
|
||
}
|
||
|
||
.p_row__info {
|
||
flex: 1;
|
||
min-width: 0;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-end;
|
||
}
|
||
|
||
.p_row__name {
|
||
font-size: 30rpx;
|
||
font-weight: 600;
|
||
color: var(--mm-fg);
|
||
}
|
||
|
||
.p_row__id {
|
||
margin-top: 4rpx;
|
||
font-size: 22rpx;
|
||
letter-spacing: 0.04em;
|
||
color: var(--mm-muted-fg);
|
||
}
|
||
|
||
.c_row {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 24rpx 0 0;
|
||
}
|
||
|
||
.c_row__label {
|
||
flex: none;
|
||
width: 140rpx;
|
||
font-size: 28rpx;
|
||
color: var(--mm-muted-fg);
|
||
}
|
||
|
||
.c_row__val {
|
||
font-size: 28rpx;
|
||
color: var(--mm-fg);
|
||
}
|
||
|
||
.c_row__input {
|
||
flex: 1;
|
||
font-size: 28rpx;
|
||
color: var(--mm-fg);
|
||
}
|
||
|
||
/* ---------- 底部结算栏 ---------- */
|
||
.bar {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
margin: 0 auto;
|
||
width: 750rpx;
|
||
bottom: 0;
|
||
z-index: 99;
|
||
display: flex;
|
||
align-items: center;
|
||
background: var(--mm-card);
|
||
border-top: 2rpx solid var(--mm-border);
|
||
box-shadow: 0 -8rpx 24rpx rgba(15, 23, 42, 0.05);
|
||
padding: 16rpx 32rpx;
|
||
/* xuchao: 全面屏底部安全区 */
|
||
padding-bottom: calc(16rpx + constant(safe-area-inset-bottom));
|
||
padding-bottom: calc(16rpx + env(safe-area-inset-bottom));
|
||
}
|
||
|
||
.bar__sum {
|
||
flex: 1;
|
||
min-width: 0;
|
||
}
|
||
|
||
.bar__label {
|
||
font-size: 20rpx;
|
||
color: var(--mm-muted-fg);
|
||
}
|
||
|
||
.bar__price {
|
||
display: flex;
|
||
align-items: baseline;
|
||
gap: 16rpx;
|
||
}
|
||
|
||
.bar__cur {
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: var(--mm-accent);
|
||
}
|
||
|
||
.bar__num {
|
||
font-size: 52rpx;
|
||
line-height: 1.1;
|
||
}
|
||
|
||
.bar__detail {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4rpx;
|
||
font-size: 22rpx;
|
||
color: var(--mm-accent);
|
||
}
|
||
|
||
.bar__buy {
|
||
flex: none;
|
||
width: 288rpx;
|
||
height: 96rpx;
|
||
border-radius: 999rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
color: #fff;
|
||
font-size: 32rpx;
|
||
font-weight: 600;
|
||
box-shadow: var(--mm-shadow-accent);
|
||
transition: transform 0.15s ease;
|
||
}
|
||
|
||
.bar__buy:active {
|
||
transform: scale(0.96);
|
||
}
|
||
|
||
/* ---------- 费用明细弹层 ---------- */
|
||
.pp {
|
||
padding: 48rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.pp__sub {
|
||
margin-top: 24rpx;
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: var(--mm-fg);
|
||
}
|
||
|
||
.pp__row {
|
||
height: 88rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
border-bottom: 2rpx dashed var(--mm-border);
|
||
}
|
||
|
||
.pp__row-label {
|
||
font-size: 26rpx;
|
||
color: var(--mm-muted-fg);
|
||
}
|
||
|
||
.pp__row-val {
|
||
font-size: 26rpx;
|
||
color: var(--mm-fg);
|
||
}
|
||
|
||
.pp__total {
|
||
display: flex;
|
||
align-items: baseline;
|
||
justify-content: flex-end;
|
||
gap: 12rpx;
|
||
margin: 24rpx 0 40rpx;
|
||
}
|
||
|
||
.pp__total-label {
|
||
font-size: 22rpx;
|
||
color: var(--mm-muted-fg);
|
||
}
|
||
|
||
.pp__total-price {
|
||
display: flex;
|
||
align-items: baseline;
|
||
}
|
||
|
||
.pp__total-cur {
|
||
font-size: 26rpx;
|
||
font-weight: 600;
|
||
color: var(--mm-accent);
|
||
}
|
||
|
||
.pp__total-num {
|
||
font-size: 44rpx;
|
||
line-height: 1;
|
||
}
|
||
</style>
|