完善服务模块缺少付款页的bug
This commit is contained in:
@@ -37,7 +37,8 @@
|
||||
</view>
|
||||
</ServicePanel>
|
||||
|
||||
<ServicePanel title="Step3 上门时间" subtitle="可直接选择推荐时段,也支持手动输入。">
|
||||
<ServicePanel title="Step3 预约上门时间" subtitle="请选择服务人员预计到达的日期和时间段,提交前系统将重新校验可预约状态。">
|
||||
<text class="booking-hint">请至少提前30分钟预约,以便安排服务人员到达现场。</text>
|
||||
<scroll-view class="booking-day-scroll" direction="horizontal" :show-scrollbar="false">
|
||||
<view class="booking-day-row">
|
||||
<view
|
||||
@@ -48,6 +49,7 @@
|
||||
>
|
||||
<text :class="['booking-day-label', selectedDayId == item.id ? 'booking-day-label-active' : '']">{{ item.label }}</text>
|
||||
<text :class="['booking-day-date', selectedDayId == item.id ? 'booking-day-date-active' : '']">{{ item.dateText }}</text>
|
||||
<text :class="['booking-day-weekday', selectedDayId == item.id ? 'booking-day-date-active' : '']">{{ item.weekday }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
@@ -61,9 +63,18 @@
|
||||
<text :class="['booking-slot-label', selectedSlotId == item.id ? 'booking-slot-label-active' : '']">{{ item.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-item form-item-last">
|
||||
<text class="label">期望时间</text>
|
||||
<input v-model="form.preferredTime" class="input" placeholder="例如 2026-05-14 上午" />
|
||||
<view class="custom-slot-section">
|
||||
<text class="custom-slot-title">其他上门时间</text>
|
||||
<picker
|
||||
mode="selector"
|
||||
:range="customSlotLabels"
|
||||
:value="customSlotIndex"
|
||||
@change="handleCustomSlotChange"
|
||||
>
|
||||
<view class="custom-slot-picker">
|
||||
<text class="custom-slot-picker-text">{{ customSlotDisplayText }}</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
</ServicePanel>
|
||||
|
||||
@@ -112,45 +123,88 @@ import { computed, reactive, ref } from 'vue'
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import ServicePageScaffold from '@/components/homeService/ServicePageScaffold.uvue'
|
||||
import ServicePanel from '@/components/homeService/ServicePanel.uvue'
|
||||
import { createHomeServiceApplication, fetchHomeServiceCatalog } from '@/services/homeServiceService.uts'
|
||||
import { createHomeServiceApplication, fetchHomeServiceCatalog, fetchHomeServicePackages } from '@/services/homeServiceService.uts'
|
||||
import { HomeServiceApplicationDraftType, HomeServiceCatalogType } from '@/types/home-service.uts'
|
||||
import { BookingDayOptionType, BookingTimeSlotType, getBookingDayOptions, getBookingTimeSlots } from '@/utils/homeServiceUiMock.uts'
|
||||
import {
|
||||
BookingDayOptionType,
|
||||
BookingTimeSlotType,
|
||||
buildBookingDays,
|
||||
buildCustomSlots,
|
||||
buildPresetSlots,
|
||||
formatStandardAppointmentTime,
|
||||
hasAnyAvailableSlots,
|
||||
validateSelectedAppointmentTime
|
||||
} from '@/utils/homeServiceBookingTime.uts'
|
||||
|
||||
const services = ref<Array<HomeServiceCatalogType>>([])
|
||||
const selectedServiceId = ref('svc-001')
|
||||
const ageText = ref('78')
|
||||
const bookingDays = ref<Array<BookingDayOptionType>>([])
|
||||
const bookingSlots = ref<Array<BookingTimeSlotType>>([])
|
||||
const selectedDayId = ref('day-1')
|
||||
const selectedSlotId = ref('slot-1')
|
||||
const customSlots = ref<Array<BookingTimeSlotType>>([])
|
||||
const selectedDayId = ref('')
|
||||
const selectedSlotId = ref('')
|
||||
const selectedCustomSlotId = ref('')
|
||||
const customSlotIndex = ref(-1)
|
||||
|
||||
const form = reactive({
|
||||
serviceId: 'svc-001',
|
||||
serviceName: '基础上门护理',
|
||||
selectedPackageId: '',
|
||||
selectedPackageName: '',
|
||||
applicantName: '李晓兰',
|
||||
elderName: '李奶奶',
|
||||
age: 78,
|
||||
gender: '女',
|
||||
phone: '13800138000',
|
||||
address: '梅州市梅江区学海路 18 号 2 栋 602',
|
||||
preferredTime: '2026-05-14 上午',
|
||||
preferredTime: '',
|
||||
appointmentStartAt: 0,
|
||||
appointmentEndAt: 0,
|
||||
appointmentDate: '',
|
||||
slotSource: '',
|
||||
minAdvanceMinutesSnapshot: 30,
|
||||
demandSummary: '老人需要基础照护、血压监测和跌倒风险提醒。'
|
||||
} as HomeServiceApplicationDraftType)
|
||||
|
||||
const selectedPackagePrice = ref('0')
|
||||
|
||||
const selectedPrice = computed((): string => {
|
||||
for (let i = 0; i < services.value.length; i++) {
|
||||
if (services.value[i].id == selectedServiceId.value) {
|
||||
return services.value[i].price.toString()
|
||||
}
|
||||
if (selectedPackagePrice.value != '0') {
|
||||
return selectedPackagePrice.value
|
||||
}
|
||||
return '0'
|
||||
})
|
||||
|
||||
function initDefaultDaySelection(now: Date) {
|
||||
let firstAvailableIndex = -1
|
||||
for (let i = 0; i < bookingDays.value.length; i++) {
|
||||
if (hasAnyAvailableSlots(bookingDays.value[i].dateKey, now)) {
|
||||
firstAvailableIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if (firstAvailableIndex >= 0) {
|
||||
selectedDayId.value = bookingDays.value[firstAvailableIndex].id
|
||||
bookingSlots.value = buildPresetSlots(bookingDays.value[firstAvailableIndex].dateKey, now)
|
||||
customSlots.value = buildCustomSlots(bookingDays.value[firstAvailableIndex].dateKey, now)
|
||||
} else {
|
||||
selectedDayId.value = ''
|
||||
bookingSlots.value = [] as Array<BookingTimeSlotType>
|
||||
customSlots.value = [] as Array<BookingTimeSlotType>
|
||||
}
|
||||
selectedSlotId.value = ''
|
||||
selectedCustomSlotId.value = ''
|
||||
customSlotIndex.value = -1
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
bookingDays.value = getBookingDayOptions()
|
||||
bookingSlots.value = getBookingTimeSlots()
|
||||
const now = new Date()
|
||||
bookingDays.value = buildBookingDays(now)
|
||||
initDefaultDaySelection(now)
|
||||
services.value = await fetchHomeServiceCatalog()
|
||||
if (services.value.length > 0) {
|
||||
selectService(services.value[0].id, services.value[0].name)
|
||||
await selectService(services.value[0].id, services.value[0].name)
|
||||
return
|
||||
}
|
||||
selectedServiceId.value = ''
|
||||
@@ -158,43 +212,113 @@ async function loadData() {
|
||||
form.serviceName = ''
|
||||
}
|
||||
|
||||
function selectService(serviceId: string, serviceName: string) {
|
||||
async function selectService(serviceId: string, serviceName: string) {
|
||||
selectedServiceId.value = serviceId
|
||||
form.serviceId = serviceId
|
||||
form.serviceName = serviceName
|
||||
form.selectedPackageId = ''
|
||||
form.selectedPackageName = ''
|
||||
selectedPackagePrice.value = '0'
|
||||
const packages = await fetchHomeServicePackages(serviceId)
|
||||
if (packages.length > 0) {
|
||||
form.selectedPackageId = packages[0].id
|
||||
form.selectedPackageName = packages[0].packageName
|
||||
selectedPackagePrice.value = packages[0].price.toString()
|
||||
}
|
||||
}
|
||||
|
||||
function syncPreferredTime() {
|
||||
let selectedDay = ''
|
||||
const selectedDay = computed((): BookingDayOptionType | null => {
|
||||
for (let i = 0; i < bookingDays.value.length; i++) {
|
||||
if (bookingDays.value[i].id == selectedDayId.value) {
|
||||
selectedDay = bookingDays.value[i].label + ' ' + bookingDays.value[i].dateText
|
||||
break
|
||||
return bookingDays.value[i]
|
||||
}
|
||||
}
|
||||
let selectedSlot = ''
|
||||
return null
|
||||
})
|
||||
|
||||
const selectedSlot = computed((): BookingTimeSlotType | null => {
|
||||
for (let i = 0; i < bookingSlots.value.length; i++) {
|
||||
if (bookingSlots.value[i].id == selectedSlotId.value) {
|
||||
selectedSlot = bookingSlots.value[i].label
|
||||
break
|
||||
return bookingSlots.value[i]
|
||||
}
|
||||
}
|
||||
if (selectedDay != '' && selectedSlot != '') {
|
||||
form.preferredTime = selectedDay + ' ' + selectedSlot
|
||||
return null
|
||||
})
|
||||
|
||||
const customSlotLabels = computed((): Array<string> => {
|
||||
const labels: Array<string> = []
|
||||
for (let i = 0; i < customSlots.value.length; i++) {
|
||||
labels.push(customSlots.value[i].label)
|
||||
}
|
||||
return labels
|
||||
})
|
||||
|
||||
const customSlotDisplayText = computed((): string => {
|
||||
if (selectedCustomSlotId.value == '') {
|
||||
return '请选择其他时间段'
|
||||
}
|
||||
for (let i = 0; i < customSlots.value.length; i++) {
|
||||
if (customSlots.value[i].id == selectedCustomSlotId.value) {
|
||||
return customSlots.value[i].label
|
||||
}
|
||||
}
|
||||
return '请选择其他时间段'
|
||||
})
|
||||
|
||||
function syncPreferredTime() {
|
||||
const day = selectedDay.value
|
||||
const slot = selectedSlot.value
|
||||
if (day != null && slot != null) {
|
||||
form.preferredTime = day.label + ' ' + day.dateText + ' ' + day.weekday + ' ' + slot.label
|
||||
form.appointmentStartAt = slot.startAt
|
||||
form.appointmentEndAt = slot.endAt
|
||||
form.appointmentDate = day.dateKey
|
||||
form.slotSource = slot.source
|
||||
}
|
||||
}
|
||||
|
||||
function selectDay(dayId: string) {
|
||||
selectedDayId.value = dayId
|
||||
selectedSlotId.value = ''
|
||||
selectedCustomSlotId.value = ''
|
||||
customSlotIndex.value = -1
|
||||
const day = selectedDay.value
|
||||
if (day != null) {
|
||||
const now = new Date()
|
||||
bookingSlots.value = buildPresetSlots(day.dateKey, now)
|
||||
customSlots.value = buildCustomSlots(day.dateKey, now)
|
||||
}
|
||||
syncPreferredTime()
|
||||
}
|
||||
|
||||
function selectSlot(slotId: string, available: boolean) {
|
||||
if (!available) {
|
||||
uni.showToast({ title: '该时段暂不可约', icon: 'none' })
|
||||
uni.showToast({ title: '该时间段已无法预约,请选择稍晚时间', icon: 'none' })
|
||||
return
|
||||
}
|
||||
selectedSlotId.value = slotId
|
||||
selectedCustomSlotId.value = ''
|
||||
customSlotIndex.value = -1
|
||||
syncPreferredTime()
|
||||
}
|
||||
|
||||
function handleCustomSlotChange(e: any) {
|
||||
const detail = e.detail
|
||||
const index = typeof detail.value == 'number' ? detail.value : parseInt(detail.value)
|
||||
if (isNaN(index) || index < 0 || index >= customSlots.value.length) {
|
||||
return
|
||||
}
|
||||
const slot = customSlots.value[index]
|
||||
if (slot == null) {
|
||||
return
|
||||
}
|
||||
if (!slot.available) {
|
||||
uni.showToast({ title: '该时间段已无法预约,请选择稍晚时间', icon: 'none' })
|
||||
return
|
||||
}
|
||||
selectedCustomSlotId.value = slot.id
|
||||
selectedSlotId.value = slot.id
|
||||
customSlotIndex.value = index
|
||||
syncPreferredTime()
|
||||
}
|
||||
|
||||
@@ -203,10 +327,29 @@ async function submitApplication() {
|
||||
uni.showToast({ title: '当前没有可预约的服务项目', icon: 'none' })
|
||||
return
|
||||
}
|
||||
if (form.applicantName == '' || form.elderName == '' || form.phone == '' || form.address == '' || form.preferredTime == '') {
|
||||
if (form.applicantName == '' || form.elderName == '' || form.phone == '' || form.address == '') {
|
||||
uni.showToast({ title: '请补全申请信息', icon: 'none' })
|
||||
return
|
||||
}
|
||||
const now = new Date()
|
||||
const day = selectedDay.value
|
||||
const slot = selectedSlot.value
|
||||
const validation = validateSelectedAppointmentTime(day, slot, now)
|
||||
if (!validation.valid) {
|
||||
uni.showToast({ title: validation.message, icon: 'none' })
|
||||
if (day != null && slot != null && !slot.available) {
|
||||
bookingSlots.value = buildPresetSlots(day.dateKey, now)
|
||||
customSlots.value = buildCustomSlots(day.dateKey, now)
|
||||
selectedSlotId.value = ''
|
||||
selectedCustomSlotId.value = ''
|
||||
customSlotIndex.value = -1
|
||||
}
|
||||
return
|
||||
}
|
||||
const standardTime = formatStandardAppointmentTime(day, slot)
|
||||
if (standardTime != '') {
|
||||
form.preferredTime = standardTime
|
||||
}
|
||||
|
||||
const parsedAge = parseInt(ageText.value)
|
||||
form.age = isNaN(parsedAge) ? 0 : parsedAge
|
||||
@@ -460,4 +603,39 @@ onLoad(() => {
|
||||
font-weight: 700;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.booking-hint {
|
||||
font-size: 24rpx;
|
||||
color: #64748b;
|
||||
margin-bottom: 18rpx;
|
||||
line-height: 34rpx;
|
||||
}
|
||||
|
||||
.custom-slot-section {
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
|
||||
.custom-slot-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #16324f;
|
||||
margin-bottom: 14rpx;
|
||||
}
|
||||
|
||||
.custom-slot-picker {
|
||||
height: 82rpx;
|
||||
border-radius: 22rpx;
|
||||
background: #f8fbfc;
|
||||
border-width: 1rpx;
|
||||
border-style: solid;
|
||||
border-color: #eef2f6;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.custom-slot-picker-text {
|
||||
font-size: 24rpx;
|
||||
color: #476072;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user