feat: 新增预约系统三页面 + 订单/使用记录优化
- 新增 pages/reserve/reserve-index 景区预约页(日期选择/场次/库存/实名信息) - 新增 pages/reserve/reserve-success 预约成功页(二维码/规则/返回拦截) - 新增 pages/reserve/reserve-list 预约使用记录页(Tab筛选/取消/详情弹窗) - 预约日期仅展示明天起3天,支持已约满/无场次状态 - 使用详情增加卡种/卡号/核销照片/时间字段,照片可放大预览 - 订单列表/详情全部激活改绑定,卡号CJ格式,Tab均分布局 - 景区详情立即预约跳转预约页,使用说明跳转独立页面 - 统一景区信息区字号间距规范 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -102,6 +102,24 @@
|
|||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "订单详情"
|
"navigationBarTitleText": "订单详情"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/reserve/reserve-index",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "景区预约"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/reserve/reserve-success",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "预约成功"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "pages/reserve/reserve-list",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "预约/使用记录"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"subPackages": [
|
"subPackages": [
|
||||||
|
|||||||
@@ -234,6 +234,10 @@ function onFeatureClick(item: { label: string }) {
|
|||||||
uni.navigateTo({ url: '/pages/usage-guide/usage-guide' })
|
uni.navigateTo({ url: '/pages/usage-guide/usage-guide' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (item.label === '使用记录') {
|
||||||
|
uni.navigateTo({ url: '/pages/reserve/reserve-list' })
|
||||||
|
return
|
||||||
|
}
|
||||||
uni.showToast({ title: `${item.label}功能开发中`, icon: 'none' })
|
uni.showToast({ title: `${item.label}功能开发中`, icon: 'none' })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,663 @@
|
|||||||
|
<template>
|
||||||
|
<view class="page">
|
||||||
|
<!-- 景区基础信息卡片 -->
|
||||||
|
<view class="scenic-card">
|
||||||
|
<image class="scenic-img" :src="scenic.image" mode="aspectFill" />
|
||||||
|
<view class="scenic-info">
|
||||||
|
<text class="scenic-name">{{ scenic.name }}</text>
|
||||||
|
<view class="scenic-row">
|
||||||
|
<view class="scenic-icon scenic-icon--pin" />
|
||||||
|
<text class="scenic-text">{{ scenic.address }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="scenic-row">
|
||||||
|
<view class="scenic-icon scenic-icon--clock" />
|
||||||
|
<text class="scenic-text">{{ scenic.hours }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="scenic-tip">持赣湘全域旅游年卡免费入园,单人单日仅可预约1次</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 日期选择模块 -->
|
||||||
|
<view class="date-card">
|
||||||
|
<text class="section-title">选择预约日期</text>
|
||||||
|
<scroll-view scroll-x class="date-scroll">
|
||||||
|
<view class="date-list">
|
||||||
|
<view
|
||||||
|
v-for="d in dateOptions"
|
||||||
|
:key="d.key"
|
||||||
|
class="date-item"
|
||||||
|
:class="{
|
||||||
|
'date-item--active': selectedDate === d.key && d.stock > 0,
|
||||||
|
'date-item--disabled': d.stock <= 0
|
||||||
|
}"
|
||||||
|
@tap="d.stock > 0 && (selectedDate = d.key)"
|
||||||
|
>
|
||||||
|
<text class="date-week">{{ d.week }}</text>
|
||||||
|
<text class="date-day">{{ d.label }}</text>
|
||||||
|
<text v-if="d.stock > 0" class="date-stock">余{{ d.stock }}位</text>
|
||||||
|
<text v-else class="date-stock date-stock--empty">已约满</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
<!-- 场次选择 -->
|
||||||
|
<view v-if="selectedSessions.length > 0" class="session-section">
|
||||||
|
<text class="session-title">选择入园场次</text>
|
||||||
|
<view class="session-list">
|
||||||
|
<view
|
||||||
|
v-for="s in selectedSessions"
|
||||||
|
:key="s.id"
|
||||||
|
class="session-item"
|
||||||
|
:class="{
|
||||||
|
'session-item--active': selectedSession === s.id && s.stock > 0,
|
||||||
|
'session-item--disabled': s.stock <= 0
|
||||||
|
}"
|
||||||
|
@tap="s.stock > 0 && (selectedSession = s.id)"
|
||||||
|
>
|
||||||
|
<text class="session-time">{{ s.time }}</text>
|
||||||
|
<text v-if="s.stock > 0" class="session-stock">余{{ s.stock }}位</text>
|
||||||
|
<text v-else class="session-stock session-stock--empty">已约满</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-else-if="selectedDate && selectedDateStock <= 0" class="no-session-tip">
|
||||||
|
<text class="no-session-tip__text">该日期暂无可预约场次</text>
|
||||||
|
</view>
|
||||||
|
<view v-else-if="selectedDate" class="no-session-tip">
|
||||||
|
<text class="no-session-tip__text">该日期无需选择场次,直接预约即可</text>
|
||||||
|
</view>
|
||||||
|
<text class="date-tip">仅可预约近3日内时段,当日预约需提前1天取消</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 预约人实名信息 -->
|
||||||
|
<view class="user-card">
|
||||||
|
<text class="section-title">预约人信息</text>
|
||||||
|
<view class="user-row">
|
||||||
|
<text class="user-label">姓名</text>
|
||||||
|
<text class="user-value">{{ userInfo.name }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="user-row">
|
||||||
|
<text class="user-label">身份证号</text>
|
||||||
|
<text class="user-value">{{ maskedId }}</text>
|
||||||
|
</view>
|
||||||
|
<text class="user-warn">预约信息必须与持卡实名一致,不可更换他人</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 预约须知 -->
|
||||||
|
<view class="notice-card">
|
||||||
|
<text class="notice-title">预约须知</text>
|
||||||
|
<view class="notice-list">
|
||||||
|
<view class="notice-item">
|
||||||
|
<view class="notice-dot" />
|
||||||
|
<text class="notice-text">赣湘全域旅游年卡单人每日仅可预约该景区1次</text>
|
||||||
|
</view>
|
||||||
|
<view class="notice-item">
|
||||||
|
<view class="notice-dot" />
|
||||||
|
<text class="notice-text">需至少提前1天线上取消预约,预约当日无法取消</text>
|
||||||
|
</view>
|
||||||
|
<view class="notice-item">
|
||||||
|
<view class="notice-dot" />
|
||||||
|
<text class="notice-text">未提前取消且未入园,将扣除1次年卡使用次数,权益不可恢复</text>
|
||||||
|
</view>
|
||||||
|
<view class="notice-item">
|
||||||
|
<view class="notice-dot" />
|
||||||
|
<text class="notice-text">入园需携带本人身份证+年卡入园二维码,人证合一核验</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="notice-link" @tap="goFaq">
|
||||||
|
<text class="notice-link__text">查看完整FAQ</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部安全区占位 -->
|
||||||
|
<view class="safe-bottom" />
|
||||||
|
|
||||||
|
<!-- 底部固定预约按钮 -->
|
||||||
|
<view class="bottom-bar">
|
||||||
|
<button
|
||||||
|
class="btn-submit"
|
||||||
|
:class="{ 'btn-submit--disabled': !selectedDate }"
|
||||||
|
:disabled="!selectedDate"
|
||||||
|
@tap="handleSubmit"
|
||||||
|
>
|
||||||
|
<text class="btn-submit__text">提交预约</text>
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
import { onLoad } from '@dcloudio/uni-app'
|
||||||
|
|
||||||
|
const scenic = ref({
|
||||||
|
name: '武功山金顶景区',
|
||||||
|
address: '江西省萍乡市芦溪县武功山风景名胜区',
|
||||||
|
hours: '08:00-18:00',
|
||||||
|
image: 'https://images.unsplash.com/photo-1513415564515-763d91423bdd?fm=jpg&q=80&w=800&h=400&fit=crop'
|
||||||
|
})
|
||||||
|
|
||||||
|
const userInfo = ref({
|
||||||
|
name: '张三',
|
||||||
|
idCard: '360302199001011234'
|
||||||
|
})
|
||||||
|
|
||||||
|
const maskedId = computed(() => {
|
||||||
|
const id = userInfo.value.idCard
|
||||||
|
return id.slice(0, 6) + '****' + id.slice(-4)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 生成近3天日期选项(明天起,含库存,第3天已约满)
|
||||||
|
const weekNames = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
|
||||||
|
const mockStocks = [0, 200, 88]
|
||||||
|
const dateOptions = Array.from({ length: 3 }, (_, i) => {
|
||||||
|
const d = new Date()
|
||||||
|
d.setDate(d.getDate() + i + 1) // 从明天开始
|
||||||
|
const month = d.getMonth() + 1
|
||||||
|
const day = d.getDate()
|
||||||
|
return {
|
||||||
|
key: `${d.getFullYear()}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
|
||||||
|
week: i === 0 ? '明天' : weekNames[d.getDay()],
|
||||||
|
label: `${month}月${day}日`,
|
||||||
|
stock: mockStocks[i]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 默认选中第一个有库存的日期
|
||||||
|
const firstAvailable = dateOptions.find(d => d.stock > 0)
|
||||||
|
const selectedDate = ref(firstAvailable ? firstAvailable.key : '')
|
||||||
|
|
||||||
|
// 场次数据(根据选中日期动态展示)
|
||||||
|
interface Session {
|
||||||
|
id: string
|
||||||
|
time: string
|
||||||
|
stock: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const sessionMap: Record<string, Session[]> = {
|
||||||
|
[dateOptions[0].key]: [
|
||||||
|
{ id: 's1', time: '08:00-10:00', stock: 56 },
|
||||||
|
{ id: 's2', time: '10:00-12:00', stock: 42 },
|
||||||
|
{ id: 's3', time: '14:00-16:00', stock: 58 },
|
||||||
|
],
|
||||||
|
[dateOptions[1].key]: [
|
||||||
|
{ id: 's4', time: '08:00-12:00', stock: 100 },
|
||||||
|
{ id: 's5', time: '12:00-18:00', stock: 100 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedSession = ref('')
|
||||||
|
|
||||||
|
const selectedSessions = computed(() => {
|
||||||
|
if (!selectedDate.value) return []
|
||||||
|
const sessions = sessionMap[selectedDate.value] || []
|
||||||
|
// 切换日期时自动选中第一个有库存的场次
|
||||||
|
if (sessions.length > 0 && !sessions.find(s => s.id === selectedSession.value)) {
|
||||||
|
const available = sessions.find(s => s.stock > 0)
|
||||||
|
selectedSession.value = available ? available.id : ''
|
||||||
|
}
|
||||||
|
return sessions
|
||||||
|
})
|
||||||
|
|
||||||
|
const selectedDateStock = computed(() => {
|
||||||
|
if (!selectedDate.value) return 0
|
||||||
|
const d = dateOptions.find(o => o.key === selectedDate.value)
|
||||||
|
return d ? d.stock : 0
|
||||||
|
})
|
||||||
|
|
||||||
|
onLoad((query: any) => {
|
||||||
|
if (query?.scenicId) {
|
||||||
|
// 实际项目中根据 scenicId 请求景区详情
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
function handleSubmit() {
|
||||||
|
if (!selectedDate.value) return
|
||||||
|
|
||||||
|
// Mock 预约成功
|
||||||
|
const reserveId = 'YP' + Date.now().toString().slice(-8)
|
||||||
|
uni.navigateTo({
|
||||||
|
url: `/pages/reserve/reserve-success?reserveId=${reserveId}&date=${selectedDate.value}&scenic=${scenic.value.name}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function goFaq() {
|
||||||
|
uni.navigateTo({ url: '/pages/faq/faq' })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
$font: 'SourceHanSerifCN', '思源宋体', -apple-system, 'PingFang SC', 'Helvetica Neue', sans-serif;
|
||||||
|
$primary: #3c9cff;
|
||||||
|
$text-main: #111111;
|
||||||
|
$text-label: #222222;
|
||||||
|
$text-body: #333333;
|
||||||
|
$text-hint: #666666;
|
||||||
|
$text-weak: #999999;
|
||||||
|
$divider: #eeeeee;
|
||||||
|
$card-radius: 24rpx;
|
||||||
|
$bar-height: 120rpx;
|
||||||
|
|
||||||
|
.page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f7f8fa;
|
||||||
|
padding-bottom: calc(#{$bar-height} + env(safe-area-inset-bottom));
|
||||||
|
font-family: $font;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== 景区信息卡片 ===== */
|
||||||
|
.scenic-card {
|
||||||
|
margin: 24rpx 24rpx 0;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: $card-radius;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenic-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 280rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenic-info {
|
||||||
|
padding: 28rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenic-name {
|
||||||
|
display: block;
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $text-main;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenic-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenic-icon {
|
||||||
|
width: 24rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
margin-right: 8rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&--pin {
|
||||||
|
border: 3rpx solid $text-hint;
|
||||||
|
border-radius: 50% 50% 50% 0;
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
position: relative;
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
width: 10rpx;
|
||||||
|
height: 10rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2rpx solid $text-hint;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--clock {
|
||||||
|
border: 3rpx solid $text-hint;
|
||||||
|
border-radius: 50%;
|
||||||
|
position: relative;
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
width: 2rpx;
|
||||||
|
height: 10rpx;
|
||||||
|
background: $text-hint;
|
||||||
|
position: absolute;
|
||||||
|
top: 4rpx;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
}
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
width: 8rpx;
|
||||||
|
height: 2rpx;
|
||||||
|
background: $text-hint;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenic-text {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: $text-hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scenic-tip {
|
||||||
|
display: block;
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: $text-weak;
|
||||||
|
margin-top: 16rpx;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== 日期选择 ===== */
|
||||||
|
.date-card {
|
||||||
|
margin: 24rpx 32rpx 0;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: $card-radius;
|
||||||
|
padding: 32rpx;
|
||||||
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
display: block;
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: $text-label;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-scroll {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-list {
|
||||||
|
display: flex;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-item {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16rpx 12rpx;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
border: 2rpx solid $divider;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
background: $primary;
|
||||||
|
border-color: $primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-week {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 18rpx;
|
||||||
|
color: $text-hint;
|
||||||
|
margin-bottom: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-item--active .date-week {
|
||||||
|
color: rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-day {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 20rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $text-main;
|
||||||
|
margin-bottom: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-item--active .date-day {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-stock {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 18rpx;
|
||||||
|
color: $text-weak;
|
||||||
|
|
||||||
|
&--empty {
|
||||||
|
color: #f53f3f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-item--active .date-stock {
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-item--disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
|
||||||
|
.date-week,
|
||||||
|
.date-day {
|
||||||
|
color: $text-weak;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== 场次选择 ===== */
|
||||||
|
.session-section {
|
||||||
|
margin-top: 24rpx;
|
||||||
|
padding-top: 24rpx;
|
||||||
|
border-top: 1rpx solid $divider;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-title {
|
||||||
|
display: block;
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: $text-label;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16rpx 28rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
border: 2rpx solid $divider;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
|
||||||
|
&--active {
|
||||||
|
background: rgba(60, 156, 255, 0.08);
|
||||||
|
border-color: $primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-time {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 20rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: $text-main;
|
||||||
|
margin-bottom: 4rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-item--active .session-time {
|
||||||
|
color: $primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.session-stock {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 18rpx;
|
||||||
|
color: $text-weak;
|
||||||
|
|
||||||
|
&--empty {
|
||||||
|
color: #f53f3f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-session-tip {
|
||||||
|
margin-top: 24rpx;
|
||||||
|
padding-top: 24rpx;
|
||||||
|
border-top: 1rpx solid $divider;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-session-tip__text {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 18rpx;
|
||||||
|
color: $text-weak;
|
||||||
|
}
|
||||||
|
|
||||||
|
.date-tip {
|
||||||
|
display: block;
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 18rpx;
|
||||||
|
color: $text-weak;
|
||||||
|
margin-top: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== 预约人信息 ===== */
|
||||||
|
.user-card {
|
||||||
|
margin: 24rpx 32rpx 0;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: $card-radius;
|
||||||
|
padding: 32rpx;
|
||||||
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-label {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: $text-label;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-value {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: $text-body;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-warn {
|
||||||
|
display: block;
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #e53935;
|
||||||
|
margin-top: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== 预约须知 ===== */
|
||||||
|
.notice-card {
|
||||||
|
margin: 24rpx 32rpx 0;
|
||||||
|
background: #eef4ff;
|
||||||
|
border-radius: $card-radius;
|
||||||
|
padding: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-title {
|
||||||
|
display: block;
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $text-main;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-list {
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-dot {
|
||||||
|
width: 8rpx;
|
||||||
|
height: 8rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: $primary;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 14rpx;
|
||||||
|
margin-right: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-text {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: $text-hint;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-link {
|
||||||
|
&:active { opacity: 0.7; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice-link__text {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: $primary;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== 底部 ===== */
|
||||||
|
.safe-bottom {
|
||||||
|
height: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom-bar {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: $bar-height;
|
||||||
|
background: #ffffff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0 48rpx;
|
||||||
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
|
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.06);
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-submit {
|
||||||
|
width: 100%;
|
||||||
|
height: 80rpx;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
background: $primary;
|
||||||
|
border: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
|
||||||
|
&::after { display: none; }
|
||||||
|
|
||||||
|
&:active:not(.btn-submit--disabled) {
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--disabled {
|
||||||
|
background: #e5e7eb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-submit__text {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 30rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-submit--disabled .btn-submit__text {
|
||||||
|
color: $text-weak;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,612 @@
|
|||||||
|
<template>
|
||||||
|
<view class="page">
|
||||||
|
<!-- Tab 筛选栏 -->
|
||||||
|
<scroll-view scroll-x class="tab-bar">
|
||||||
|
<view class="tab-inner">
|
||||||
|
<view
|
||||||
|
v-for="tab in tabs"
|
||||||
|
:key="tab.value"
|
||||||
|
class="tab-item"
|
||||||
|
:class="{ 'tab-item--active': activeTab === tab.value }"
|
||||||
|
@tap="activeTab = tab.value"
|
||||||
|
>
|
||||||
|
<text class="tab-text">{{ tab.label }}</text>
|
||||||
|
<view v-if="activeTab === tab.value" class="tab-underline" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
<!-- 列表区域 -->
|
||||||
|
<scroll-view
|
||||||
|
scroll-y
|
||||||
|
class="list-area"
|
||||||
|
refresher-enabled
|
||||||
|
:refresher-triggered="refreshing"
|
||||||
|
@refresherrefresh="onRefresh"
|
||||||
|
@scrolltolower="onLoadMore"
|
||||||
|
>
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<view v-if="filteredRecords.length === 0" class="empty-state">
|
||||||
|
<view class="empty-icon">
|
||||||
|
<view class="empty-cal" />
|
||||||
|
<view class="empty-cal__line" />
|
||||||
|
</view>
|
||||||
|
<text class="empty-text">暂无预约记录,快去预约景区</text>
|
||||||
|
<view class="empty-btn" @tap="goReserve">
|
||||||
|
<text class="empty-btn__text">去预约</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 记录列表 -->
|
||||||
|
<view v-else class="record-list">
|
||||||
|
<view
|
||||||
|
v-for="r in filteredRecords"
|
||||||
|
:key="r.id"
|
||||||
|
class="record-card"
|
||||||
|
>
|
||||||
|
<!-- 头部 -->
|
||||||
|
<view class="record-header">
|
||||||
|
<text class="record-name">{{ r.scenicName }}</text>
|
||||||
|
<view class="status-tag" :class="`status-tag--${r.status}`">
|
||||||
|
<text class="status-tag__text">{{ statusLabel(r.status) }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 卡种+卡号 -->
|
||||||
|
<view class="record-card-info">
|
||||||
|
<text class="record-card-type">{{ r.cardType }}</text>
|
||||||
|
<text class="record-card-no">卡号:{{ r.cardNo }}</text>
|
||||||
|
</view>
|
||||||
|
<!-- 预约入园日期 -->
|
||||||
|
<view class="record-info">
|
||||||
|
<text class="record-date">预约入园日期:{{ r.date }}</text>
|
||||||
|
</view>
|
||||||
|
<!-- 操作 -->
|
||||||
|
<view class="record-actions">
|
||||||
|
<view
|
||||||
|
v-if="r.status === 'pending' && canCancel(r.date)"
|
||||||
|
class="btn-sm btn-sm--outline"
|
||||||
|
@tap="showCancelModal(r)"
|
||||||
|
>
|
||||||
|
<text class="btn-sm__text--outline">取消预约</text>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
v-if="r.status === 'pending'"
|
||||||
|
class="btn-sm btn-sm--outline"
|
||||||
|
@tap="showCode(r)"
|
||||||
|
>
|
||||||
|
<text class="btn-sm__text--outline">查看入园码</text>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="btn-sm btn-sm--outline"
|
||||||
|
@tap="viewDetail(r)"
|
||||||
|
>
|
||||||
|
<text class="btn-sm__text--outline">查看详情</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 加载更多 -->
|
||||||
|
<view v-if="filteredRecords.length > 0" class="load-more">
|
||||||
|
<text v-if="loading" class="load-more__text">加载中...</text>
|
||||||
|
<text v-else-if="noMore" class="load-more__text">没有更多记录了</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
|
||||||
|
<!-- 入园码弹窗 -->
|
||||||
|
<uni-popup ref="codePopup" type="center" :mask-click="true">
|
||||||
|
<view class="code-modal">
|
||||||
|
<text class="code-modal__title">入园码</text>
|
||||||
|
<view class="qr-box">
|
||||||
|
<image class="qr-img" src="/static/qr-demo.png" mode="aspectFit" />
|
||||||
|
</view>
|
||||||
|
<text class="code-modal__desc">入园时请出示此二维码+本人身份证</text>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
|
|
||||||
|
<!-- 取消预约弹窗 -->
|
||||||
|
<uni-popup ref="cancelPopup" type="center" :mask-click="true">
|
||||||
|
<view class="cancel-modal">
|
||||||
|
<text class="cancel-modal__title">取消预约</text>
|
||||||
|
<view class="cancel-modal__body">
|
||||||
|
<text class="cancel-modal__text">确定取消该预约吗?未提前取消且未入园将扣除1次年卡使用次数,权益不可恢复。</text>
|
||||||
|
</view>
|
||||||
|
<view class="cancel-modal__actions">
|
||||||
|
<view class="btn-modal btn-modal--outline" @tap="cancelPopup?.close()">
|
||||||
|
<text class="btn-modal__text--outline">再想想</text>
|
||||||
|
</view>
|
||||||
|
<view class="btn-modal btn-modal--primary" @tap="confirmCancel">
|
||||||
|
<text class="btn-modal__text--primary">确认取消</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
|
|
||||||
|
<!-- 详情弹窗 -->
|
||||||
|
<uni-popup ref="detailPopup" type="bottom" :mask-click="true">
|
||||||
|
<view class="detail-sheet">
|
||||||
|
<view class="sheet-header">
|
||||||
|
<text class="sheet-title">使用详情</text>
|
||||||
|
<view class="sheet-close" @tap="detailPopup?.close()">
|
||||||
|
<view class="sheet-close__icon" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<scroll-view scroll-y class="sheet-body">
|
||||||
|
<view class="sheet-row">
|
||||||
|
<text class="sheet-label">预约景区</text>
|
||||||
|
<text class="sheet-value">{{ detailRecord?.scenicName }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="sheet-row">
|
||||||
|
<text class="sheet-label">卡种</text>
|
||||||
|
<text class="sheet-value">{{ detailRecord?.cardType }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="sheet-row">
|
||||||
|
<text class="sheet-label">卡号</text>
|
||||||
|
<text class="sheet-value">{{ detailRecord?.cardNo }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="sheet-row">
|
||||||
|
<text class="sheet-label">预约入园日期</text>
|
||||||
|
<text class="sheet-value">{{ detailRecord?.date }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="sheet-row">
|
||||||
|
<text class="sheet-label">预约人</text>
|
||||||
|
<text class="sheet-value">{{ detailRecord?.userName }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="sheet-row">
|
||||||
|
<text class="sheet-label">证件号</text>
|
||||||
|
<text class="sheet-value">{{ detailRecord?.maskedId }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="sheet-row">
|
||||||
|
<text class="sheet-label">预约状态</text>
|
||||||
|
<text class="sheet-value">{{ statusLabel(detailRecord?.status || '') }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="sheet-row">
|
||||||
|
<text class="sheet-label">预约申请时间</text>
|
||||||
|
<text class="sheet-value">{{ detailRecord?.applyTime }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="detailRecord?.verifyTime" class="sheet-row">
|
||||||
|
<text class="sheet-label">核销时间</text>
|
||||||
|
<text class="sheet-value">{{ detailRecord.verifyTime }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="detailRecord?.cancelTime" class="sheet-row">
|
||||||
|
<text class="sheet-label">取消时间</text>
|
||||||
|
<text class="sheet-value">{{ detailRecord.cancelTime }}</text>
|
||||||
|
</view>
|
||||||
|
<!-- 核销人脸照片 -->
|
||||||
|
<view v-if="detailRecord?.verifyPhoto && detailRecord?.status !== 'auto_used'" class="sheet-photo">
|
||||||
|
<text class="sheet-label">核销照片</text>
|
||||||
|
<image class="sheet-photo-avatar" :src="detailRecord.verifyPhoto" mode="aspectFill" @tap="previewPhoto(detailRecord.verifyPhoto)" />
|
||||||
|
</view>
|
||||||
|
<view v-if="detailRecord?.status !== 'used'" class="sheet-tip">
|
||||||
|
<text class="sheet-tip__text">未提前取消且未入园将扣除1次年卡使用次数</text>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
|
||||||
|
type RecordStatus = 'pending' | 'used' | 'cancelled' | 'auto_used'
|
||||||
|
|
||||||
|
interface ReserveRecord {
|
||||||
|
id: string
|
||||||
|
scenicName: string
|
||||||
|
date: string
|
||||||
|
status: RecordStatus
|
||||||
|
userName: string
|
||||||
|
maskedId: string
|
||||||
|
cardType: string
|
||||||
|
cardNo: string
|
||||||
|
applyTime: string
|
||||||
|
cancelTime?: string
|
||||||
|
verifyTime?: string
|
||||||
|
verifyPhoto?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{ label: '全部', value: '' },
|
||||||
|
{ label: '待入园', value: 'pending' },
|
||||||
|
{ label: '已入园', value: 'used' },
|
||||||
|
{ label: '已取消', value: 'cancelled' },
|
||||||
|
{ label: '自动核销', value: 'auto_used' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const activeTab = ref('')
|
||||||
|
const loading = ref(false)
|
||||||
|
const refreshing = ref(false)
|
||||||
|
const noMore = ref(false)
|
||||||
|
const codePopup = ref<any>(null)
|
||||||
|
const cancelPopup = ref<any>(null)
|
||||||
|
const detailPopup = ref<any>(null)
|
||||||
|
const popupCode = ref('')
|
||||||
|
const detailRecord = ref<ReserveRecord | null>(null)
|
||||||
|
const cancelRecord = ref<ReserveRecord | null>(null)
|
||||||
|
|
||||||
|
const records = ref<ReserveRecord[]>([
|
||||||
|
{ id: 'YP26062601', scenicName: '武功山金顶景区', date: '2026-06-27', status: 'pending', userName: '张三', maskedId: '360302****1234', cardType: '超级玩家卡', cardNo: 'CJ0000012398', applyTime: '2026-06-25 14:30' },
|
||||||
|
{ id: 'YP26062502', scenicName: '杨岐山孽龙洞景区', date: '2026-06-26', status: 'pending', userName: '张三', maskedId: '360302****1234', cardType: '赣湘全域旅游年卡', cardNo: 'CJ0000012399', applyTime: '2026-06-24 10:15' },
|
||||||
|
{ id: 'YP26062003', scenicName: '仙凤三宝景区', date: '2026-06-20', status: 'used', userName: '张三', maskedId: '360302****1234', cardType: '超级玩家卡', cardNo: 'CJ0000012400', applyTime: '2026-06-18 09:20', verifyTime: '2026-06-20 09:45', verifyPhoto: 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?fm=jpg&q=80&w=200&h=200&fit=crop' },
|
||||||
|
{ id: 'YP26061804', scenicName: '武功山君澜温泉', date: '2026-06-18', status: 'cancelled', userName: '张三', maskedId: '360302****1234', cardType: '赣湘全域旅游年卡', cardNo: 'CJ0000012401', applyTime: '2026-06-16 16:40', cancelTime: '2026-06-17 20:30' },
|
||||||
|
{ id: 'YP26061505', scenicName: '凯光新天地生态旅游区', date: '2026-06-15', status: 'auto_used', userName: '张三', maskedId: '360302****1234', cardType: '超级玩家卡', cardNo: 'CJ0000012402', applyTime: '2026-06-13 11:05', verifyTime: '2026-06-16 00:00', verifyPhoto: 'https://images.unsplash.com/photo-1527980965255-d3b416303d12?fm=jpg&q=80&w=200&h=200&fit=crop' },
|
||||||
|
{ id: 'YP26061006', scenicName: '和平农场·哎马乐园', date: '2026-06-10', status: 'used', userName: '张三', maskedId: '360302****1234', cardType: '赣湘全域旅游年卡', cardNo: 'CJ0000012403', applyTime: '2026-06-08 08:50', verifyTime: '2026-06-10 10:22', verifyPhoto: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?fm=jpg&q=80&w=200&h=200&fit=crop' },
|
||||||
|
])
|
||||||
|
|
||||||
|
const filteredRecords = computed(() => {
|
||||||
|
if (!activeTab.value) return records.value
|
||||||
|
return records.value.filter(r => r.status === activeTab.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
function statusLabel(status: string) {
|
||||||
|
const map: Record<string, string> = { pending: '待入园', used: '已入园', cancelled: '已取消', auto_used: '自动核销' }
|
||||||
|
return map[status] || status
|
||||||
|
}
|
||||||
|
|
||||||
|
function canCancel(date: string) {
|
||||||
|
const today = new Date()
|
||||||
|
today.setHours(0, 0, 0, 0)
|
||||||
|
const d = new Date(date)
|
||||||
|
return (d.getTime() - today.getTime()) / (1000 * 60 * 60 * 24) >= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function showCancelModal(r: ReserveRecord) {
|
||||||
|
cancelRecord.value = r
|
||||||
|
cancelPopup.value?.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmCancel() {
|
||||||
|
cancelPopup.value?.close()
|
||||||
|
if (cancelRecord.value) {
|
||||||
|
const idx = records.value.findIndex(o => o.id === cancelRecord.value!.id)
|
||||||
|
if (idx !== -1) {
|
||||||
|
records.value[idx].status = 'cancelled'
|
||||||
|
records.value[idx].cancelTime = new Date().toISOString().slice(0, 16).replace('T', ' ')
|
||||||
|
}
|
||||||
|
uni.showToast({ title: '已取消预约', icon: 'success' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showCode(r: ReserveRecord) {
|
||||||
|
popupCode.value = r.id
|
||||||
|
codePopup.value?.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
function viewDetail(r: ReserveRecord) {
|
||||||
|
detailRecord.value = r
|
||||||
|
detailPopup.value?.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRefresh() {
|
||||||
|
refreshing.value = true
|
||||||
|
setTimeout(() => { refreshing.value = false }, 800)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onLoadMore() {
|
||||||
|
if (loading.value || noMore.value) return
|
||||||
|
loading.value = true
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value = false
|
||||||
|
noMore.value = true
|
||||||
|
}, 800)
|
||||||
|
}
|
||||||
|
|
||||||
|
function goReserve() {
|
||||||
|
uni.navigateTo({ url: '/pages/reserve/reserve-index' })
|
||||||
|
}
|
||||||
|
|
||||||
|
function previewPhoto(url: string) {
|
||||||
|
uni.previewImage({ urls: [url] })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
$font: 'SourceHanSerifCN', '思源宋体', -apple-system, 'PingFang SC', 'Helvetica Neue', sans-serif;
|
||||||
|
$primary: #3c9cff;
|
||||||
|
$text-main: #111111;
|
||||||
|
$text-label: #222222;
|
||||||
|
$text-body: #333333;
|
||||||
|
$text-hint: #666666;
|
||||||
|
$text-weak: #999999;
|
||||||
|
$divider: #eeeeee;
|
||||||
|
$card-radius: 24rpx;
|
||||||
|
|
||||||
|
.page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f7f8fa;
|
||||||
|
font-family: $font;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Tab ===== */
|
||||||
|
.tab-bar {
|
||||||
|
white-space: nowrap;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-inner {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20rpx 16rpx;
|
||||||
|
position: relative;
|
||||||
|
min-width: 120rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-text {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 24rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: $text-hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-item--active .tab-text {
|
||||||
|
color: $primary;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-underline {
|
||||||
|
width: 32rpx;
|
||||||
|
height: 4rpx;
|
||||||
|
border-radius: 2rpx;
|
||||||
|
background: $primary;
|
||||||
|
margin-top: 6rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== List ===== */
|
||||||
|
.list-area {
|
||||||
|
height: calc(100vh - 88rpx);
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-list {
|
||||||
|
padding: 0 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Record Card ===== */
|
||||||
|
.record-card {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: $card-radius;
|
||||||
|
padding: 28rpx;
|
||||||
|
margin-top: 20rpx;
|
||||||
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-name {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: $text-main;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-tag {
|
||||||
|
padding: 4rpx 14rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
|
||||||
|
&--pending { background: #e8f3ff; }
|
||||||
|
&--used { background: #f0fff4; }
|
||||||
|
&--cancelled { background: #f5f5f5; }
|
||||||
|
&--auto_used { background: #fff8e8; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-tag__text {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 22rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-tag--pending .status-tag__text { color: $primary; }
|
||||||
|
.status-tag--used .status-tag__text { color: #00b42a; }
|
||||||
|
.status-tag--cancelled .status-tag__text { color: #86909c; }
|
||||||
|
.status-tag--auto_used .status-tag__text { color: #ff9500; }
|
||||||
|
|
||||||
|
.record-card-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16rpx;
|
||||||
|
margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-card-type {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: $primary;
|
||||||
|
background: rgba(60, 156, 255, 0.06);
|
||||||
|
padding: 2rpx 12rpx;
|
||||||
|
border-radius: 6rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-card-no {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: $text-weak;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-info {
|
||||||
|
padding-bottom: 12rpx;
|
||||||
|
border-bottom: 1rpx solid $divider;
|
||||||
|
margin-bottom: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-date {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: $text-hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sm {
|
||||||
|
height: 52rpx;
|
||||||
|
padding: 0 24rpx;
|
||||||
|
border-radius: 26rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&--outline {
|
||||||
|
border: 2rpx solid $divider;
|
||||||
|
&:active { transform: scale(0.97); background: #f5f5f5; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sm__text--outline {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 22rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: $text-hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Empty ===== */
|
||||||
|
.empty-state {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 160rpx 48rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-icon { width: 120rpx; height: 120rpx; position: relative; margin-bottom: 32rpx; }
|
||||||
|
.empty-cal { width: 80rpx; height: 72rpx; border: 4rpx solid #cccccc; border-radius: 8rpx; position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); }
|
||||||
|
.empty-cal__line { width: 80rpx; height: 4rpx; background: #cccccc; position: absolute; bottom: 48rpx; left: 50%; transform: translateX(-50%); }
|
||||||
|
|
||||||
|
.empty-text { font-family: $font; font-size: 26rpx; color: $text-weak; margin-bottom: 32rpx; }
|
||||||
|
|
||||||
|
.empty-btn {
|
||||||
|
height: 72rpx; padding: 0 56rpx; border-radius: 36rpx; background: $primary;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
&:active { transform: scale(0.97); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-btn__text { font-family: $font; font-size: 26rpx; font-weight: 700; color: #ffffff; }
|
||||||
|
|
||||||
|
/* ===== Load More ===== */
|
||||||
|
.load-more { text-align: center; padding: 16rpx 0 120rpx; }
|
||||||
|
.load-more__text { font-family: $font; font-size: 22rpx; color: $text-weak; }
|
||||||
|
|
||||||
|
/* ===== Code Modal ===== */
|
||||||
|
.code-modal {
|
||||||
|
width: 560rpx; background: #ffffff; border-radius: $card-radius; padding: 40rpx;
|
||||||
|
display: flex; flex-direction: column; align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-modal__title { font-family: $font; font-size: 32rpx; font-weight: 700; color: $text-main; margin-bottom: 32rpx; }
|
||||||
|
|
||||||
|
.qr-box {
|
||||||
|
width: 360rpx; height: 360rpx; background: #F8FAFC;
|
||||||
|
border-radius: 16rpx; display: flex; align-items: center;
|
||||||
|
justify-content: center; border: 1rpx solid #E2E8F0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-img { width: 300rpx; height: 300rpx; }
|
||||||
|
|
||||||
|
.code-modal__desc { font-family: $font; font-size: 26rpx; color: $text-hint; margin-top: 24rpx; }
|
||||||
|
|
||||||
|
/* ===== Cancel Modal ===== */
|
||||||
|
.cancel-modal {
|
||||||
|
width: 600rpx; background: #ffffff; border-radius: $card-radius; padding: 40rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-modal__title {
|
||||||
|
display: block; font-family: $font; font-size: 32rpx; font-weight: 700;
|
||||||
|
color: $text-main; text-align: center; margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-modal__body { margin-bottom: 32rpx; }
|
||||||
|
|
||||||
|
.cancel-modal__text {
|
||||||
|
font-family: $font; font-size: 26rpx; color: $text-body; line-height: 1.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cancel-modal__actions { display: flex; gap: 24rpx; }
|
||||||
|
|
||||||
|
.btn-modal {
|
||||||
|
flex: 1; height: 72rpx; border-radius: 36rpx; display: flex;
|
||||||
|
align-items: center; justify-content: center; transition: all 0.15s ease;
|
||||||
|
|
||||||
|
&--outline {
|
||||||
|
border: 2rpx solid $divider;
|
||||||
|
&:active { transform: scale(0.97); background: #f5f5f5; }
|
||||||
|
}
|
||||||
|
&--primary {
|
||||||
|
background: $primary;
|
||||||
|
&:active { transform: scale(0.97); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-modal__text--outline { font-family: $font; font-size: 26rpx; font-weight: 500; color: $text-hint; }
|
||||||
|
.btn-modal__text--primary { font-family: $font; font-size: 26rpx; font-weight: 700; color: #ffffff; }
|
||||||
|
|
||||||
|
/* ===== Detail Sheet ===== */
|
||||||
|
.detail-sheet {
|
||||||
|
background: #ffffff; border-radius: $card-radius $card-radius 0 0; max-height: 75vh;
|
||||||
|
display: flex; flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-header {
|
||||||
|
display: flex; align-items: center; justify-content: space-between;
|
||||||
|
padding: 32rpx; border-bottom: 1rpx solid $divider; flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-title { font-family: $font; font-size: 30rpx; font-weight: 700; color: $text-main; }
|
||||||
|
|
||||||
|
.sheet-close {
|
||||||
|
width: 48rpx; height: 48rpx; display: flex; align-items: center; justify-content: center;
|
||||||
|
&:active { opacity: 0.6; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-close__icon {
|
||||||
|
width: 20rpx; height: 20rpx; position: relative;
|
||||||
|
&::before, &::after {
|
||||||
|
content: ''; position: absolute; top: 50%; left: 50%;
|
||||||
|
width: 20rpx; height: 3rpx; background: $text-hint; border-radius: 2rpx;
|
||||||
|
}
|
||||||
|
&::before { transform: translate(-50%, -50%) rotate(45deg); }
|
||||||
|
&::after { transform: translate(-50%, -50%) rotate(-45deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-body { padding: 32rpx; flex: 1; }
|
||||||
|
|
||||||
|
.sheet-row {
|
||||||
|
display: flex; justify-content: space-between; align-items: center;
|
||||||
|
padding: 14rpx 0; border-bottom: 1rpx solid $divider;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-label { font-family: $font; font-size: 26rpx; color: $text-label; }
|
||||||
|
.sheet-value { font-family: $font; font-size: 26rpx; color: $text-body; text-align: right; }
|
||||||
|
|
||||||
|
.sheet-photo {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 14rpx 0;
|
||||||
|
border-bottom: 1rpx solid $divider;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-photo-avatar {
|
||||||
|
width: 100rpx;
|
||||||
|
height: 100rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-tip {
|
||||||
|
margin-top: 24rpx; padding: 20rpx; background: #fff2f2; border-radius: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sheet-tip__text { font-family: $font; font-size: 24rpx; color: #f53f3f; line-height: 1.5; }
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,381 @@
|
|||||||
|
<template>
|
||||||
|
<view class="page">
|
||||||
|
<!-- 成功图标区域 -->
|
||||||
|
<view class="success-header">
|
||||||
|
<view class="success-icon">
|
||||||
|
<view class="success-circle" />
|
||||||
|
<view class="success-check" />
|
||||||
|
</view>
|
||||||
|
<text class="success-title">预约成功</text>
|
||||||
|
<text class="success-desc">请携带本人身份证+入园码按时入园</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 预约信息卡片 -->
|
||||||
|
<view class="info-card">
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">预约景区</text>
|
||||||
|
<text class="info-value">{{ scenicName }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">预约日期</text>
|
||||||
|
<text class="info-value">{{ reserveDate }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">预约人</text>
|
||||||
|
<text class="info-value">{{ userName }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">证件号</text>
|
||||||
|
<text class="info-value">{{ maskedId }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row">
|
||||||
|
<text class="info-label">预约编号</text>
|
||||||
|
<text class="info-value">{{ reserveId }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="info-row info-row--last">
|
||||||
|
<text class="info-label">入园凭证</text>
|
||||||
|
<view class="code-btn" @tap="showCode">
|
||||||
|
<text class="code-btn__text">查看入园码</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 重要提醒 -->
|
||||||
|
<view class="warn-card">
|
||||||
|
<text class="warn-title">重要预约规则</text>
|
||||||
|
<view class="warn-list">
|
||||||
|
<view class="warn-item">
|
||||||
|
<view class="warn-dot" />
|
||||||
|
<text class="warn-text">预约当日无法取消,如需改期请至少提前1天操作</text>
|
||||||
|
</view>
|
||||||
|
<view class="warn-item">
|
||||||
|
<view class="warn-dot" />
|
||||||
|
<text class="warn-text">爽约未取消将扣除年卡使用次数,无法恢复</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 底部按钮 -->
|
||||||
|
<view class="bottom-btns">
|
||||||
|
<view class="btn-half btn-half--outline" @tap="goRecord">
|
||||||
|
<text class="btn-half__text--outline">查看预约记录</text>
|
||||||
|
</view>
|
||||||
|
<view class="btn-half btn-half--primary" @tap="goHome">
|
||||||
|
<text class="btn-half__text--primary">返回首页</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 入园码弹窗 -->
|
||||||
|
<uni-popup ref="codePopup" type="center" :mask-click="true">
|
||||||
|
<view class="code-modal">
|
||||||
|
<text class="code-modal__title">入园码</text>
|
||||||
|
<view class="qr-box">
|
||||||
|
<image class="qr-img" src="/static/qr-demo.png" mode="aspectFit" />
|
||||||
|
</view>
|
||||||
|
<text class="code-modal__desc">入园时请出示此二维码+本人身份证</text>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { onLoad, onBackPress } from '@dcloudio/uni-app'
|
||||||
|
|
||||||
|
const reserveId = ref('YP26062601')
|
||||||
|
const scenicName = ref('武功山金顶景区')
|
||||||
|
const reserveDate = ref('2026-06-27(周六)')
|
||||||
|
const userName = ref('张三')
|
||||||
|
const maskedId = ref('360302****1234')
|
||||||
|
const codePopup = ref<any>(null)
|
||||||
|
|
||||||
|
onLoad((query: any) => {
|
||||||
|
if (query?.reserveId) reserveId.value = query.reserveId
|
||||||
|
if (query?.date) {
|
||||||
|
const d = new Date(query.date)
|
||||||
|
const weekNames = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
|
||||||
|
reserveDate.value = `${query.date}(${weekNames[d.getDay()]})`
|
||||||
|
}
|
||||||
|
if (query?.scenic) scenicName.value = decodeURIComponent(query.scenic)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 拦截返回,跳转预约记录页
|
||||||
|
onBackPress(() => {
|
||||||
|
uni.redirectTo({ url: '/pages/reserve/reserve-list' })
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
function showCode() {
|
||||||
|
codePopup.value?.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
function goRecord() {
|
||||||
|
uni.redirectTo({ url: '/pages/reserve/reserve-list' })
|
||||||
|
}
|
||||||
|
|
||||||
|
function goHome() {
|
||||||
|
uni.switchTab({ url: '/pages/index/index' })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
$font: 'SourceHanSerifCN', '思源宋体', -apple-system, 'PingFang SC', 'Helvetica Neue', sans-serif;
|
||||||
|
$primary: #3c9cff;
|
||||||
|
$text-main: #111111;
|
||||||
|
$text-label: #222222;
|
||||||
|
$text-body: #333333;
|
||||||
|
$text-hint: #666666;
|
||||||
|
$text-weak: #999999;
|
||||||
|
$divider: #eeeeee;
|
||||||
|
$card-radius: 24rpx;
|
||||||
|
|
||||||
|
.page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f7f8fa;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
|
font-family: $font;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== 成功图标 ===== */
|
||||||
|
.success-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 64rpx 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-icon {
|
||||||
|
width: 120rpx;
|
||||||
|
height: 120rpx;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-circle {
|
||||||
|
width: 120rpx;
|
||||||
|
height: 120rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(60, 156, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-check {
|
||||||
|
width: 48rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
border-left: 6rpx solid $primary;
|
||||||
|
border-bottom: 6rpx solid $primary;
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
position: absolute;
|
||||||
|
top: 40rpx;
|
||||||
|
left: 38rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-title {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 38rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $text-main;
|
||||||
|
margin-top: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success-desc {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: $text-hint;
|
||||||
|
margin-top: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== 预约信息卡片 ===== */
|
||||||
|
.info-card {
|
||||||
|
margin: 48rpx 32rpx 0;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: $card-radius;
|
||||||
|
padding: 32rpx;
|
||||||
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16rpx 0;
|
||||||
|
border-bottom: 1rpx solid $divider;
|
||||||
|
|
||||||
|
&--last {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-label {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: $text-label;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-value {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: $text-body;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-btn {
|
||||||
|
padding: 6rpx 20rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
background: rgba(60, 156, 255, 0.08);
|
||||||
|
|
||||||
|
&:active { transform: scale(0.95); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-btn__text {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: $primary;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== 重要提醒 ===== */
|
||||||
|
.warn-card {
|
||||||
|
margin: 24rpx 32rpx 0;
|
||||||
|
background: #fff2f2;
|
||||||
|
border-radius: $card-radius;
|
||||||
|
padding: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warn-title {
|
||||||
|
display: block;
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #f53f3f;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warn-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warn-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warn-dot {
|
||||||
|
width: 8rpx;
|
||||||
|
height: 8rpx;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #f53f3f;
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 14rpx;
|
||||||
|
margin-right: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warn-text {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: $text-hint;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== 底部按钮 ===== */
|
||||||
|
.bottom-btns {
|
||||||
|
display: flex;
|
||||||
|
gap: 32rpx;
|
||||||
|
padding: 48rpx 32rpx 64rpx;
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-half {
|
||||||
|
flex: 1;
|
||||||
|
height: 80rpx;
|
||||||
|
border-radius: 40rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: all 0.15s ease;
|
||||||
|
|
||||||
|
&--outline {
|
||||||
|
border: 2rpx solid $divider;
|
||||||
|
&:active { transform: scale(0.97); background: #f5f5f5; }
|
||||||
|
}
|
||||||
|
|
||||||
|
&--primary {
|
||||||
|
background: $primary;
|
||||||
|
&:active { transform: scale(0.97); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-half__text--outline {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: $text-hint;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-half__text--primary {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 26rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== 入园码弹窗 ===== */
|
||||||
|
.code-modal {
|
||||||
|
width: 560rpx;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: $card-radius;
|
||||||
|
padding: 40rpx;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-modal__title {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $text-main;
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-modal__desc {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: $text-hint;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-box {
|
||||||
|
width: 360rpx;
|
||||||
|
height: 360rpx;
|
||||||
|
background: #F8FAFC;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: 1rpx solid #E2E8F0;
|
||||||
|
margin-bottom: 32rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-img {
|
||||||
|
width: 300rpx;
|
||||||
|
height: 300rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-btn {
|
||||||
|
padding: 16rpx 48rpx;
|
||||||
|
border-radius: 32rpx;
|
||||||
|
background: rgba(60, 156, 255, 0.08);
|
||||||
|
|
||||||
|
&:active { transform: scale(0.95); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-btn__text {
|
||||||
|
font-family: $font;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: $primary;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -207,8 +207,7 @@ function onCopyAddress() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
function onBookClick(cardType: string) {
|
function onBookClick(cardType: string) {
|
||||||
const label = cardType === 'super' ? '超级玩家卡' : '赣湘全域旅游年卡'
|
uni.navigateTo({ url: '/pages/reserve/reserve-index' })
|
||||||
uni.showToast({ title: `${label}预约功能开发中`, icon: 'none' })
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user