1044 lines
23 KiB
Vue
1044 lines
23 KiB
Vue
<script setup>
|
||
/**
|
||
* 规划预约页面
|
||
*
|
||
* 功能:
|
||
* - 规划师信息展示
|
||
* - 日期时间选择
|
||
* - 表单填写:姓名、手机号、年级、成绩(根据年级动态显示)
|
||
* - 支付预约
|
||
* - 预约成功弹窗
|
||
*/
|
||
|
||
import { ref, computed, watch, onMounted } from 'vue'
|
||
import { onLoad } from '@dcloudio/uni-app'
|
||
import { useUserStore } from '@/store/user.js'
|
||
import { useAuth } from '@/composables/useAuth.js'
|
||
import { usePayment } from '@/composables/usePayment.js'
|
||
import { getPlannerDetail, getAvailableTime } from '@/api/planner.js'
|
||
import { isPhone } from '@/utils/validate.js'
|
||
|
||
const userStore = useUserStore()
|
||
const { checkLogin } = useAuth()
|
||
const { processPayment } = usePayment()
|
||
|
||
// 页面参数
|
||
const plannerId = ref(0)
|
||
const plannerName = ref('')
|
||
|
||
// 规划师信息
|
||
const plannerInfo = ref({
|
||
id: 0,
|
||
name: '',
|
||
avatar: '',
|
||
intro: '',
|
||
price: 0
|
||
})
|
||
|
||
// 日期选择
|
||
const selectedDate = ref('')
|
||
const dateList = ref([])
|
||
const currentDateIndex = ref(0)
|
||
|
||
// 时间选择
|
||
const selectedTime = ref('')
|
||
const timeList = ref([])
|
||
const timeLoading = ref(false)
|
||
|
||
// 表单数据
|
||
const formData = ref({
|
||
name: '',
|
||
phone: '',
|
||
grade: '',
|
||
score: ''
|
||
})
|
||
|
||
// 年级选项
|
||
const gradeOptions = [
|
||
'小学一年级', '小学二年级', '小学三年级', '小学四年级', '小学五年级', '小学六年级',
|
||
'初一', '初二', '初三',
|
||
'高一', '高二', '高三',
|
||
'大一', '大二', '大三', '大四',
|
||
'研究生', '其他'
|
||
]
|
||
const gradeIndex = ref(-1)
|
||
|
||
// 需要显示成绩的年级
|
||
const gradesWithScore = ['初一', '初二', '初三', '高一', '高二', '高三']
|
||
|
||
// 是否显示成绩字段
|
||
const showScoreField = computed(() => {
|
||
return gradesWithScore.includes(formData.value.grade)
|
||
})
|
||
|
||
// 页面加载状态
|
||
const pageLoading = ref(true)
|
||
const submitting = ref(false)
|
||
|
||
// 预约成功弹窗
|
||
const showSuccessPopup = ref(false)
|
||
const bookingInfo = ref({
|
||
date: '',
|
||
time: '',
|
||
plannerName: ''
|
||
})
|
||
|
||
/**
|
||
* 生成日期列表(未来7天)
|
||
*/
|
||
function generateDateList() {
|
||
const list = []
|
||
const today = new Date()
|
||
const weekDays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
|
||
|
||
for (let i = 0; i < 7; i++) {
|
||
const date = new Date(today)
|
||
date.setDate(today.getDate() + i)
|
||
|
||
const year = date.getFullYear()
|
||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||
const day = String(date.getDate()).padStart(2, '0')
|
||
const dateStr = `${year}-${month}-${day}`
|
||
|
||
let label = ''
|
||
if (i === 0) {
|
||
label = '今天'
|
||
} else if (i === 1) {
|
||
label = '明天'
|
||
} else {
|
||
label = weekDays[date.getDay()]
|
||
}
|
||
|
||
list.push({
|
||
date: dateStr,
|
||
label: label,
|
||
day: `${month}/${day}`
|
||
})
|
||
}
|
||
|
||
dateList.value = list
|
||
if (list.length > 0) {
|
||
selectedDate.value = list[0].date
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 加载规划师详情
|
||
*/
|
||
async function loadPlannerDetail() {
|
||
try {
|
||
const res = await getPlannerDetail(plannerId.value)
|
||
if (res && res.code === 0 && res.data) {
|
||
plannerInfo.value = {
|
||
id: res.data.id || plannerId.value,
|
||
name: res.data.name || plannerName.value || '规划师',
|
||
avatar: res.data.avatar || res.data.photo || '',
|
||
intro: res.data.intro || res.data.introduction || '',
|
||
price: res.data.price || 0
|
||
}
|
||
} else {
|
||
// 使用传入的参数
|
||
plannerInfo.value.name = plannerName.value || '规划师'
|
||
}
|
||
} catch (error) {
|
||
console.error('加载规划师详情失败:', error)
|
||
plannerInfo.value.name = plannerName.value || '规划师'
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 加载可预约时间
|
||
*/
|
||
async function loadAvailableTime() {
|
||
if (!selectedDate.value) return
|
||
|
||
timeLoading.value = true
|
||
timeList.value = []
|
||
selectedTime.value = ''
|
||
|
||
try {
|
||
const res = await getAvailableTime(plannerId.value, selectedDate.value)
|
||
if (res && res.code === 0 && res.data) {
|
||
// 支持多种数据格式
|
||
if (Array.isArray(res.data)) {
|
||
timeList.value = res.data.map(item => {
|
||
if (typeof item === 'string') {
|
||
return { time: item, available: true }
|
||
}
|
||
return {
|
||
time: item.time || item.timeSlot || '',
|
||
available: item.available !== false
|
||
}
|
||
})
|
||
} else if (res.data.list && Array.isArray(res.data.list)) {
|
||
timeList.value = res.data.list.map(item => ({
|
||
time: item.time || item.timeSlot || '',
|
||
available: item.available !== false
|
||
}))
|
||
}
|
||
}
|
||
|
||
// 如果没有数据,生成默认时间段
|
||
if (timeList.value.length === 0) {
|
||
timeList.value = generateDefaultTimeSlots()
|
||
}
|
||
} catch (error) {
|
||
console.error('加载可预约时间失败:', error)
|
||
// 使用默认时间段
|
||
timeList.value = generateDefaultTimeSlots()
|
||
} finally {
|
||
timeLoading.value = false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 生成默认时间段
|
||
*/
|
||
function generateDefaultTimeSlots() {
|
||
const slots = []
|
||
const times = [
|
||
'09:00', '10:00', '11:00',
|
||
'14:00', '15:00', '16:00', '17:00',
|
||
'19:00', '20:00'
|
||
]
|
||
|
||
times.forEach(time => {
|
||
slots.push({ time, available: true })
|
||
})
|
||
|
||
return slots
|
||
}
|
||
|
||
/**
|
||
* 选择日期
|
||
*/
|
||
function selectDate(index) {
|
||
currentDateIndex.value = index
|
||
selectedDate.value = dateList.value[index].date
|
||
}
|
||
|
||
/**
|
||
* 选择时间
|
||
*/
|
||
function selectTime(item) {
|
||
if (!item.available) return
|
||
selectedTime.value = item.time
|
||
}
|
||
|
||
/**
|
||
* 年级选择
|
||
*/
|
||
function onGradeChange(e) {
|
||
gradeIndex.value = e.detail.value
|
||
formData.value.grade = gradeOptions[e.detail.value]
|
||
// 切换年级时清空成绩
|
||
if (!showScoreField.value) {
|
||
formData.value.score = ''
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 表单是否填写完整
|
||
*/
|
||
const isFormComplete = computed(() => {
|
||
const baseComplete = (
|
||
selectedDate.value !== '' &&
|
||
selectedTime.value !== '' &&
|
||
formData.value.name.trim() !== '' &&
|
||
formData.value.phone.trim() !== '' &&
|
||
formData.value.grade !== ''
|
||
)
|
||
|
||
// 如果需要显示成绩字段,则成绩也必填
|
||
if (showScoreField.value) {
|
||
return baseComplete && formData.value.score.trim() !== ''
|
||
}
|
||
|
||
return baseComplete
|
||
})
|
||
|
||
/**
|
||
* 验证表单
|
||
*/
|
||
function validateForm() {
|
||
if (!selectedDate.value) {
|
||
uni.showToast({ title: '请选择预约日期', icon: 'none' })
|
||
return false
|
||
}
|
||
|
||
if (!selectedTime.value) {
|
||
uni.showToast({ title: '请选择预约时间', icon: 'none' })
|
||
return false
|
||
}
|
||
|
||
if (!formData.value.name.trim()) {
|
||
uni.showToast({ title: '请输入姓名', icon: 'none' })
|
||
return false
|
||
}
|
||
|
||
if (!isPhone(formData.value.phone)) {
|
||
uni.showToast({ title: '请输入正确的手机号', icon: 'none' })
|
||
return false
|
||
}
|
||
|
||
if (!formData.value.grade) {
|
||
uni.showToast({ title: '请选择年级', icon: 'none' })
|
||
return false
|
||
}
|
||
|
||
if (showScoreField.value && !formData.value.score.trim()) {
|
||
uni.showToast({ title: '请输入成绩', icon: 'none' })
|
||
return false
|
||
}
|
||
|
||
return true
|
||
}
|
||
|
||
/**
|
||
* 提交预约
|
||
*/
|
||
async function handleSubmit() {
|
||
if (!isFormComplete.value) return
|
||
|
||
// 检查登录
|
||
if (!checkLogin()) return
|
||
|
||
// 验证表单
|
||
if (!validateForm()) return
|
||
|
||
submitting.value = true
|
||
|
||
try {
|
||
uni.showLoading({ title: '创建订单中...' })
|
||
|
||
// 发起支付
|
||
const result = await processPayment({
|
||
productType: 2, // 规划预约类型
|
||
productId: plannerId.value,
|
||
userInfo: {
|
||
name: formData.value.name,
|
||
phone: formData.value.phone,
|
||
grade: formData.value.grade,
|
||
score: formData.value.score || '',
|
||
bookDate: selectedDate.value,
|
||
bookTime: selectedTime.value,
|
||
plannerId: plannerId.value,
|
||
plannerName: plannerInfo.value.name
|
||
}
|
||
})
|
||
|
||
uni.hideLoading()
|
||
|
||
if (result.success) {
|
||
// 支付成功,显示预约成功弹窗
|
||
bookingInfo.value = {
|
||
date: selectedDate.value,
|
||
time: selectedTime.value,
|
||
plannerName: plannerInfo.value.name
|
||
}
|
||
showSuccessPopup.value = true
|
||
} else if (result.error) {
|
||
uni.showToast({
|
||
title: result.error,
|
||
icon: 'none'
|
||
})
|
||
}
|
||
} catch (error) {
|
||
uni.hideLoading()
|
||
console.error('预约失败:', error)
|
||
uni.showToast({
|
||
title: '预约失败,请重试',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
submitting.value = false
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 关闭成功弹窗
|
||
*/
|
||
function closeSuccessPopup() {
|
||
showSuccessPopup.value = false
|
||
// 返回上一页或跳转到订单列表
|
||
uni.navigateBack()
|
||
}
|
||
|
||
/**
|
||
* 查看订单
|
||
*/
|
||
function goToOrderList() {
|
||
showSuccessPopup.value = false
|
||
uni.navigateTo({
|
||
url: '/pages/order/list/index'
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 格式化价格
|
||
*/
|
||
function formatPrice(price) {
|
||
if (price === undefined || price === null) return '0'
|
||
const num = Number(price)
|
||
if (isNaN(num)) return '0'
|
||
if (Number.isInteger(num)) return num.toString()
|
||
return num.toFixed(2)
|
||
}
|
||
|
||
// 监听日期变化,加载对应时间段
|
||
watch(selectedDate, () => {
|
||
loadAvailableTime()
|
||
})
|
||
|
||
/**
|
||
* 页面加载
|
||
*/
|
||
onLoad(async (options) => {
|
||
plannerId.value = Number(options.plannerId) || 0
|
||
plannerName.value = decodeURIComponent(options.plannerName || '')
|
||
|
||
// 恢复用户登录状态
|
||
userStore.restoreFromStorage()
|
||
|
||
// 如果已登录,预填手机号
|
||
if (userStore.isLoggedIn && userStore.phone) {
|
||
formData.value.phone = userStore.phone
|
||
}
|
||
|
||
// 生成日期列表
|
||
generateDateList()
|
||
|
||
// 加载规划师详情
|
||
await loadPlannerDetail()
|
||
|
||
pageLoading.value = false
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<view class="planner-book-page">
|
||
<!-- 页面内容 -->
|
||
<view class="page-content">
|
||
<!-- 规划师信息卡片 -->
|
||
<view class="planner-card">
|
||
<image
|
||
:src="plannerInfo.avatar || '/static/ic_empty.png'"
|
||
mode="aspectFill"
|
||
class="planner-avatar"
|
||
/>
|
||
<view class="planner-info">
|
||
<view class="planner-name">{{ plannerInfo.name }}</view>
|
||
<view class="planner-intro">{{ plannerInfo.intro || '专业学业规划师' }}</view>
|
||
</view>
|
||
<view class="planner-price">
|
||
<text class="price-symbol">¥</text>
|
||
<text class="price-value">{{ formatPrice(plannerInfo.price) }}</text>
|
||
<text class="price-unit">/次</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 日期选择 -->
|
||
<view class="section">
|
||
<view class="section-title">选择日期</view>
|
||
<scroll-view scroll-x class="date-scroll">
|
||
<view class="date-list">
|
||
<view
|
||
v-for="(item, index) in dateList"
|
||
:key="item.date"
|
||
class="date-item"
|
||
:class="{ 'active': currentDateIndex === index }"
|
||
@click="selectDate(index)"
|
||
>
|
||
<view class="date-label">{{ item.label }}</view>
|
||
<view class="date-day">{{ item.day }}</view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
|
||
<!-- 时间选择 -->
|
||
<view class="section">
|
||
<view class="section-title">选择时间</view>
|
||
<view v-if="timeLoading" class="time-loading">
|
||
<text>加载中...</text>
|
||
</view>
|
||
<view v-else-if="timeList.length === 0" class="time-empty">
|
||
<text>暂无可预约时间</text>
|
||
</view>
|
||
<view v-else class="time-grid">
|
||
<view
|
||
v-for="item in timeList"
|
||
:key="item.time"
|
||
class="time-item"
|
||
:class="{
|
||
'active': selectedTime === item.time,
|
||
'disabled': !item.available
|
||
}"
|
||
@click="selectTime(item)"
|
||
>
|
||
{{ item.time }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 表单区域 -->
|
||
<view class="section form-section">
|
||
<view class="section-title">填写信息</view>
|
||
|
||
<!-- 姓名 -->
|
||
<view class="form-item">
|
||
<view class="form-label">
|
||
<text class="required">*</text>
|
||
<text>姓名</text>
|
||
</view>
|
||
<input
|
||
class="form-input"
|
||
type="text"
|
||
placeholder="请输入姓名"
|
||
v-model="formData.name"
|
||
maxlength="20"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 手机号 -->
|
||
<view class="form-item">
|
||
<view class="form-label">
|
||
<text class="required">*</text>
|
||
<text>手机号</text>
|
||
</view>
|
||
<input
|
||
class="form-input"
|
||
type="number"
|
||
placeholder="请输入手机号"
|
||
v-model="formData.phone"
|
||
maxlength="11"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 年级 -->
|
||
<view class="form-item">
|
||
<view class="form-label">
|
||
<text class="required">*</text>
|
||
<text>年级</text>
|
||
</view>
|
||
<picker
|
||
mode="selector"
|
||
:range="gradeOptions"
|
||
@change="onGradeChange"
|
||
>
|
||
<view class="form-picker">
|
||
<text :class="{ 'placeholder': !formData.grade }">
|
||
{{ formData.grade || '请选择年级' }}
|
||
</text>
|
||
<view class="picker-arrow"></view>
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
|
||
<!-- 成绩(根据年级动态显示) -->
|
||
<view v-if="showScoreField" class="form-item">
|
||
<view class="form-label">
|
||
<text class="required">*</text>
|
||
<text>成绩</text>
|
||
</view>
|
||
<input
|
||
class="form-input"
|
||
type="text"
|
||
placeholder="请输入近期考试成绩/排名"
|
||
v-model="formData.score"
|
||
maxlength="50"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部占位 -->
|
||
<view class="bottom-placeholder"></view>
|
||
</view>
|
||
|
||
<!-- 底部按钮 -->
|
||
<view class="bottom-bar">
|
||
<view class="price-info">
|
||
<text class="price-label">合计:</text>
|
||
<text class="price-symbol">¥</text>
|
||
<text class="price-amount">{{ formatPrice(plannerInfo.price) }}</text>
|
||
</view>
|
||
<view
|
||
class="submit-btn"
|
||
:class="{ 'disabled': !isFormComplete || submitting }"
|
||
@click="handleSubmit"
|
||
>
|
||
{{ submitting ? '提交中...' : '立即预约' }}
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 预约成功弹窗 -->
|
||
<view v-if="showSuccessPopup" class="popup-mask" @click.stop>
|
||
<view class="success-popup">
|
||
<!-- 成功图标 -->
|
||
<view class="success-icon">
|
||
<view class="icon-circle">
|
||
<text class="icon-check">✓</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 成功标题 -->
|
||
<view class="success-title">预约成功</view>
|
||
|
||
<!-- 预约信息 -->
|
||
<view class="booking-info">
|
||
<view class="info-item">
|
||
<text class="info-label">规划师:</text>
|
||
<text class="info-value">{{ bookingInfo.plannerName }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="info-label">预约日期:</text>
|
||
<text class="info-value">{{ bookingInfo.date }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="info-label">预约时间:</text>
|
||
<text class="info-value">{{ bookingInfo.time }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 提示文字 -->
|
||
<view class="success-tip">
|
||
规划师将在预约时间与您联系,请保持手机畅通
|
||
</view>
|
||
|
||
<!-- 按钮 -->
|
||
<view class="popup-buttons">
|
||
<view class="popup-btn secondary" @click="closeSuccessPopup">返回</view>
|
||
<view class="popup-btn primary" @click="goToOrderList">查看订单</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
|
||
<style lang="scss" scoped>
|
||
@import '@/styles/variables.scss';
|
||
|
||
.planner-book-page {
|
||
min-height: 100vh;
|
||
background-color: $bg-color;
|
||
}
|
||
|
||
.page-content {
|
||
padding: $spacing-lg;
|
||
padding-bottom: 180rpx;
|
||
}
|
||
|
||
// 规划师信息卡片
|
||
.planner-card {
|
||
display: flex;
|
||
align-items: center;
|
||
background-color: $bg-white;
|
||
border-radius: $border-radius-lg;
|
||
padding: $spacing-lg;
|
||
margin-bottom: $spacing-lg;
|
||
box-shadow: $shadow-sm;
|
||
|
||
.planner-avatar {
|
||
width: 120rpx;
|
||
height: 120rpx;
|
||
border-radius: $border-radius-md;
|
||
flex-shrink: 0;
|
||
margin-right: $spacing-md;
|
||
}
|
||
|
||
.planner-info {
|
||
flex: 1;
|
||
min-width: 0;
|
||
|
||
.planner-name {
|
||
font-size: $font-size-lg;
|
||
font-weight: $font-weight-medium;
|
||
color: $text-color;
|
||
margin-bottom: $spacing-xs;
|
||
}
|
||
|
||
.planner-intro {
|
||
font-size: $font-size-sm;
|
||
color: $text-secondary;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
}
|
||
|
||
.planner-price {
|
||
display: flex;
|
||
align-items: baseline;
|
||
flex-shrink: 0;
|
||
|
||
.price-symbol {
|
||
font-size: $font-size-sm;
|
||
color: $error-color;
|
||
}
|
||
|
||
.price-value {
|
||
font-size: $font-size-xl;
|
||
font-weight: $font-weight-bold;
|
||
color: $error-color;
|
||
}
|
||
|
||
.price-unit {
|
||
font-size: $font-size-xs;
|
||
color: $text-placeholder;
|
||
margin-left: 4rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 区块样式
|
||
.section {
|
||
background-color: $bg-white;
|
||
border-radius: $border-radius-lg;
|
||
padding: $spacing-lg;
|
||
margin-bottom: $spacing-lg;
|
||
|
||
.section-title {
|
||
font-size: $font-size-lg;
|
||
font-weight: $font-weight-medium;
|
||
color: $text-color;
|
||
margin-bottom: $spacing-md;
|
||
}
|
||
}
|
||
|
||
// 日期选择
|
||
.date-scroll {
|
||
white-space: nowrap;
|
||
margin: 0 -#{$spacing-lg};
|
||
padding: 0 $spacing-lg;
|
||
}
|
||
|
||
.date-list {
|
||
display: inline-flex;
|
||
gap: $spacing-md;
|
||
}
|
||
|
||
.date-item {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 120rpx;
|
||
height: 120rpx;
|
||
background-color: $bg-gray;
|
||
border-radius: $border-radius-md;
|
||
transition: all $transition-fast;
|
||
|
||
.date-label {
|
||
font-size: $font-size-sm;
|
||
color: $text-secondary;
|
||
margin-bottom: 4rpx;
|
||
}
|
||
|
||
.date-day {
|
||
font-size: $font-size-md;
|
||
font-weight: $font-weight-medium;
|
||
color: $text-color;
|
||
}
|
||
|
||
&.active {
|
||
background-color: $primary-color;
|
||
|
||
.date-label,
|
||
.date-day {
|
||
color: $text-white;
|
||
}
|
||
}
|
||
|
||
&:active {
|
||
opacity: 0.8;
|
||
}
|
||
}
|
||
|
||
// 时间选择
|
||
.time-loading,
|
||
.time-empty {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 200rpx;
|
||
|
||
text {
|
||
font-size: $font-size-md;
|
||
color: $text-placeholder;
|
||
}
|
||
}
|
||
|
||
.time-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: $spacing-md;
|
||
}
|
||
|
||
.time-item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 72rpx;
|
||
background-color: $bg-gray;
|
||
border-radius: $border-radius-sm;
|
||
font-size: $font-size-md;
|
||
color: $text-color;
|
||
transition: all $transition-fast;
|
||
|
||
&.active {
|
||
background-color: $primary-color;
|
||
color: $text-white;
|
||
}
|
||
|
||
&.disabled {
|
||
background-color: $bg-gray;
|
||
color: $text-disabled;
|
||
pointer-events: none;
|
||
}
|
||
|
||
&:active {
|
||
opacity: 0.8;
|
||
}
|
||
}
|
||
|
||
// 表单区域
|
||
.form-section {
|
||
padding: 0 $spacing-lg;
|
||
}
|
||
|
||
.form-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: $spacing-lg 0;
|
||
border-bottom: 1rpx solid $border-light;
|
||
|
||
&:last-child {
|
||
border-bottom: none;
|
||
}
|
||
}
|
||
|
||
.form-label {
|
||
width: 140rpx;
|
||
flex-shrink: 0;
|
||
font-size: $font-size-md;
|
||
color: $text-color;
|
||
|
||
.required {
|
||
color: $error-color;
|
||
margin-right: 4rpx;
|
||
}
|
||
}
|
||
|
||
.form-input {
|
||
flex: 1;
|
||
height: 48rpx;
|
||
font-size: $font-size-md;
|
||
color: $text-color;
|
||
|
||
&::placeholder {
|
||
color: $text-placeholder;
|
||
}
|
||
}
|
||
|
||
.form-picker {
|
||
flex: 1;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
height: 48rpx;
|
||
font-size: $font-size-md;
|
||
color: $text-color;
|
||
|
||
.placeholder {
|
||
color: $text-placeholder;
|
||
}
|
||
|
||
.picker-arrow {
|
||
width: 16rpx;
|
||
height: 16rpx;
|
||
border-right: 3rpx solid $text-placeholder;
|
||
border-bottom: 3rpx solid $text-placeholder;
|
||
transform: rotate(-45deg);
|
||
margin-left: $spacing-sm;
|
||
}
|
||
}
|
||
|
||
// 底部占位
|
||
.bottom-placeholder {
|
||
height: 40rpx;
|
||
}
|
||
|
||
// 底部按钮栏
|
||
.bottom-bar {
|
||
position: fixed;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
background-color: $bg-white;
|
||
padding: $spacing-md $spacing-lg;
|
||
padding-bottom: calc(#{$spacing-md} + env(safe-area-inset-bottom));
|
||
box-shadow: 0 -4rpx 16rpx rgba(0, 0, 0, 0.05);
|
||
|
||
.price-info {
|
||
display: flex;
|
||
align-items: baseline;
|
||
|
||
.price-label {
|
||
font-size: $font-size-md;
|
||
color: $text-secondary;
|
||
}
|
||
|
||
.price-symbol {
|
||
font-size: $font-size-md;
|
||
color: $error-color;
|
||
font-weight: $font-weight-medium;
|
||
}
|
||
|
||
.price-amount {
|
||
font-size: $font-size-xxl;
|
||
color: $error-color;
|
||
font-weight: $font-weight-bold;
|
||
}
|
||
}
|
||
|
||
.submit-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 240rpx;
|
||
height: 88rpx;
|
||
background: linear-gradient(135deg, #FF6B6B 0%, #FF5252 100%);
|
||
border-radius: 44rpx;
|
||
font-size: $font-size-lg;
|
||
font-weight: $font-weight-medium;
|
||
color: $text-white;
|
||
|
||
&:active {
|
||
opacity: 0.8;
|
||
}
|
||
|
||
&.disabled {
|
||
background: #CCCCCC;
|
||
pointer-events: none;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 弹窗遮罩
|
||
.popup-mask {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: $bg-mask;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 1000;
|
||
}
|
||
|
||
// 成功弹窗
|
||
.success-popup {
|
||
width: 600rpx;
|
||
background-color: $bg-white;
|
||
border-radius: $border-radius-xl;
|
||
padding: $spacing-xl;
|
||
text-align: center;
|
||
}
|
||
|
||
.success-icon {
|
||
margin-bottom: $spacing-lg;
|
||
|
||
.icon-circle {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 120rpx;
|
||
height: 120rpx;
|
||
background: linear-gradient(135deg, #52C41A 0%, #73D13D 100%);
|
||
border-radius: 50%;
|
||
|
||
.icon-check {
|
||
font-size: 60rpx;
|
||
color: $text-white;
|
||
font-weight: $font-weight-bold;
|
||
}
|
||
}
|
||
}
|
||
|
||
.success-title {
|
||
font-size: $font-size-xl;
|
||
font-weight: $font-weight-bold;
|
||
color: $text-color;
|
||
margin-bottom: $spacing-lg;
|
||
}
|
||
|
||
.booking-info {
|
||
background-color: $bg-gray;
|
||
border-radius: $border-radius-md;
|
||
padding: $spacing-md $spacing-lg;
|
||
margin-bottom: $spacing-lg;
|
||
text-align: left;
|
||
|
||
.info-item {
|
||
display: flex;
|
||
padding: $spacing-xs 0;
|
||
|
||
.info-label {
|
||
font-size: $font-size-md;
|
||
color: $text-secondary;
|
||
width: 160rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.info-value {
|
||
font-size: $font-size-md;
|
||
color: $text-color;
|
||
flex: 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
.success-tip {
|
||
font-size: $font-size-sm;
|
||
color: $text-placeholder;
|
||
margin-bottom: $spacing-xl;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.popup-buttons {
|
||
display: flex;
|
||
gap: $spacing-md;
|
||
|
||
.popup-btn {
|
||
flex: 1;
|
||
height: 80rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border-radius: 40rpx;
|
||
font-size: $font-size-md;
|
||
font-weight: $font-weight-medium;
|
||
|
||
&.secondary {
|
||
background-color: $bg-gray;
|
||
color: $text-secondary;
|
||
}
|
||
|
||
&.primary {
|
||
background: linear-gradient(135deg, #FF6B6B 0%, #FF5252 100%);
|
||
color: $text-white;
|
||
}
|
||
|
||
&:active {
|
||
opacity: 0.8;
|
||
}
|
||
}
|
||
}
|
||
</style>
|