1122 lines
48 KiB
Vue
1122 lines
48 KiB
Vue
<!-- 使用 type="home" 属性设置首页,其他页面不需要设置,默认为page -->
|
||
<route lang="jsonc" type="page">{
|
||
"needLogin": true,
|
||
"layout": "tabbar",
|
||
"style": {
|
||
"navigationStyle": "custom"
|
||
}
|
||
}</route>
|
||
<template>
|
||
<view class="pb-180rpx">
|
||
<!-- 消息提示框 -->
|
||
<wd-message-box selector="wd-message-box-slot" />
|
||
|
||
<!-- 上门服务-预定时间 -->
|
||
<wd-popup v-model="showReservePopup" lock-scroll custom-style="border-radius: 32rpx 32rpx 0rpx 0rpx;" position="bottom">
|
||
<view class="relative">
|
||
<view class="absolute top-18rpx right-30rpx" @click="showReservePopup = false">
|
||
<wd-img width="60rpx" height='60rpx' :src="`${OSS}icon/icon_close.png`"></wd-img>
|
||
</view>
|
||
<view class="text-36rpx text-[#121212] leading-50rpx text-center pt-50rpx pb-40rpx">选择时间</view>
|
||
<view class="w-[100%] h-100rpx flex justify-between items-center">
|
||
<view
|
||
class="w-[50%] h-[100%] flex flex-col items-center justify-center rounded-l-[8rpx]"
|
||
:class="`${currentTimePicker == 'start' ? 'bg-[#4C9F44] text-[#fff]' : 'bg-[#F6F7F8] text-[#303133]'}`"
|
||
@click="currentTimePicker = 'start'">
|
||
<view class="text-28rpx leading-40rpx">开始时间</view>
|
||
<view class="text-26rpx leading-36rpx mt-2rpx">{{ startTimeLayout }}</view>
|
||
</view>
|
||
<view
|
||
class="w-[50%] h-[100%] flex flex-col items-center justify-center rounded-r-[8rpx]"
|
||
:class="`${currentTimePicker == 'end' ? 'bg-[#4C9F44] text-[#fff]' : 'bg-[#F6F7F8] text-[#303133]'}`"
|
||
@click="currentTimePicker = 'end'">
|
||
<view class="text-28rpx leading-40rpx">结束时间</view>
|
||
<view class="text-26rpx leading-36rpx mt-2rpx">{{ endTimeLayout }}</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="date-picker">
|
||
<view class="" v-if="currentTimePicker == 'start'">
|
||
<wd-datetime-picker-view
|
||
:minDate='minTimestamp'
|
||
:maxDate='maxTimestamp'
|
||
type="datetime"
|
||
v-model="startTimeValue"
|
||
:formatter="Door.handleFormatTime"
|
||
@change="Door.handleStartTimePicker"
|
||
/>
|
||
</view>
|
||
<view class="" v-if="currentTimePicker == 'end'">
|
||
<wd-datetime-picker-view
|
||
:minDate='futureTimestamp'
|
||
:maxDate='maxTimestamp'
|
||
type="datetime"
|
||
v-model="endTimeValue"
|
||
:formatter="Door.handleFormatTime"
|
||
@change="Door.handleEndTimePicker"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="pb-22rpx mt-40rpx mx-30rpx flex justify-between items-center text-[32rpx] text-center">
|
||
<view class='bg-[#F6F7F8] text-[#303133] rounded-8rpx h-90rpx leading-90rpx w-[50%] mr-28rpx' @click="Door.handleResetTime">重置</view>
|
||
<view class='bg-[#4C9F44] text-[#fff] rounded-8rpx h-90rpx leading-90rpx w-[50%]' @click="Door.handleConfirmHour">确定({{ totalHour }}小时)</view>
|
||
</view>
|
||
</view>
|
||
</wd-popup>
|
||
|
||
<!-- 费用明细 -->
|
||
<wd-popup v-model="showCostPopup" lock-scroll custom-style="border-radius: 32rpx 32rpx 0rpx 0rpx;" @close="showCostPopup = false" position="bottom">
|
||
<view class='bg-[#FBFBFB] py-40rpx realtive'>
|
||
<view class="absolute top-18rpx right-30rpx" @click="showCostPopup = false">
|
||
<wd-img width="60rpx" height='60rpx' :src="`${OSS}icon/icon_close.png`"></wd-img>
|
||
</view>
|
||
<view class="text-36rpx text-[#121212] leading-50rpx text-center">费用明细</view>
|
||
<view class="mx-30rpx bg-white rounded-16rpx px-30rpx pt-40rpx mt-40rpx pb-30rpx">
|
||
<!-- 服务费 -->
|
||
<view>
|
||
<view class="flex justify-between items-center text-30rpx text-[#303133] leading-42rpx">
|
||
<view>服务费</view>
|
||
<view>¥{{ costBill.server_all_price }}</view>
|
||
</view>
|
||
|
||
<view class="flex justify-between items-center text-24rpx text-[#909399] leading-34rpx mt-16rpx">
|
||
<view>服务费(¥{{ costBill.server_price }}元/小时)</view>
|
||
<view>x{{ costParams.hours }}</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 车马费 -->
|
||
<view>
|
||
<view class="flex justify-between items-center text-30rpx text-[#303133] leading-42rpx mt-52rpx">
|
||
<view>车马费</view>
|
||
<view>¥{{ costBill.mileage_server_price }}</view>
|
||
</view>
|
||
|
||
<view class="flex justify-between items-center text-24rpx text-[#909399] leading-34rpx mt-16rpx">
|
||
<view>车马费(¥{{ costBill.mileage_price }}元/小时)</view>
|
||
<view>{{ costBill.team_user_distance }}公里</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 茶艺服务 -->
|
||
<view>
|
||
<view class="flex justify-between items-center text-30rpx text-[#303133] leading-42rpx mt-52rpx">
|
||
<view>茶艺服务</view>
|
||
<view>¥{{ costBill.leaf_tea_all_price }}</view>
|
||
</view>
|
||
|
||
<view class="flex justify-between items-center text-24rpx text-[#909399] leading-34rpx mt-16rpx" v-if="selectedTeaTxt.length > 0">
|
||
<view class='w-400rpx'>{{ selectedTeaTxt.join('/') }}</view>
|
||
<view>¥{{ costBill.leaf_amount }}</view>
|
||
</view>
|
||
|
||
<view class="flex justify-between items-center text-24rpx text-[#909399] leading-34rpx mt-16rpx" v-if="teaUsageValue > 0">
|
||
<view class='w-400rpx'>茶具使用</view>
|
||
<view>{{ costBill.teacup_price }}</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="mt-52rpx">
|
||
<view class="flex justify-between items-center text-30rpx text-[#303133] leading-42rpx">
|
||
<view>优惠</view>
|
||
<view class="text-[#4C9F44]">-¥{{ costBill.coupon_price }}</view>
|
||
</view>
|
||
<view class="flex justify-between items-center text-24rpx text-[#909399] leading-34rpx mt-16rpx">
|
||
<view>优惠券</view>
|
||
<view>-¥{{ costBill.coupon_price }}</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="my-30rpx">
|
||
<wd-gap height="2rpx" bgColor='#F6F7F9'></wd-gap>
|
||
</view>
|
||
|
||
<view class="flex justify-between items-center text-30rpx text-[#303133] leading-42rpx">
|
||
<view>实付金额</view>
|
||
<view>¥{{ costBill.order_amount }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</wd-popup>
|
||
|
||
<!-- 茶艺服务 -->
|
||
<wd-popup v-model="showTeaServicePopup" lock-scroll custom-style="border-radius: 32rpx 32rpx 0rpx 0rpx;" position="bottom">
|
||
<view class="relative">
|
||
<view class="absolute top-18rpx right-30rpx" @click="showTeaServicePopup = false">
|
||
<wd-img width="60rpx" height='60rpx' :src="`${OSS}icon/icon_close.png`"></wd-img>
|
||
</view>
|
||
<view class="text-36rpx text-[#121212] leading-50rpx text-center pt-50rpx pb-40rpx">茶艺服务</view>
|
||
<scroll-view scroll-y class="h-900rpx">
|
||
<view class="">
|
||
<!-- 服务人数 -->
|
||
<view class="mx-60rpx mb-56rpx">
|
||
<view class="text-32rpx leading-44rpx text-#303133">服务人数</view>
|
||
<view class="flex items-center justify-between mt-28rpx">
|
||
<view class="text-28rpx leading-40rpx text-#303133">服务人数</view>
|
||
<view class="">
|
||
<wd-input-number v-model="servicePeople"/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 茶具使用 -->
|
||
<view class="mx-60rpx mb-70rpx">
|
||
<view class="text-32rpx leading-44rpx text-#303133">茶具使用</view>
|
||
<view class="mt-28rpx flex items-center justify-between w-full">
|
||
<view class="flex items-center justify-between w-full">
|
||
<view class="text-28rpx leading-40rpx text-#303133">茶具需求</view>
|
||
<view>
|
||
<wd-radio-group v-model="teaUsageValue" shape="dot" checked-color="#4C9F44" inline>
|
||
<block v-for="(item, index) in teaUsageList" :key="index">
|
||
<wd-radio :value="item.type">
|
||
<view class="text-[#303133] text-26rpx leading-36rpx mt-2rpx">{{item.name}}</view>
|
||
</wd-radio>
|
||
</block>
|
||
</wd-radio-group>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 预定茶叶 -->
|
||
<view class="mx-60rpx mb-50rpx">
|
||
<view class="text-32rpx leading-44rpx text-#303133">
|
||
<text class="mr-20rpx">预定茶叶</text>
|
||
<text class="text-26rpx leading-36rpx text-#909399">支持多选</text>
|
||
</view>
|
||
<view class="mt-28rpx">
|
||
<view class="grid grid-cols-3 gap-x-16rpx gap-y-16rpx">
|
||
<view
|
||
v-for="(item, index) in teaList" :key="index"
|
||
class="text-28rpx leading-40rpx rounded-8rpx text-center py-14rpx"
|
||
:class="selectedTea.includes(item.id) ? 'bg-#4C9F44 text-#fff' : 'bg-#F7F7F7 text-#606266'"
|
||
@click="Reserve.handleToggleTea(item.id, item.name, item.price)">
|
||
<view>{{item.name}}</view>
|
||
<view>¥{{item.price}}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="h-2rpx bg-#EFF0F2"></view>
|
||
|
||
<!-- 按钮 -->
|
||
<view class="mx-60rpx flex items-center justify-between py-40rpx">
|
||
<view class="">
|
||
<view class="text-24rpx leading-34rpx text-#303133">已选 {{selectedTea.length}} 项</view>
|
||
<view class="">
|
||
<price-format color="#FF5951" :first-size="40" :second-size="40" :subscript-size="28" :price="costBill.order_amount"></price-format>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="flex items-center">
|
||
<view class="w-178rpx h-70rpx leading-70rpx text-center bg-#F6F7F8 text-#303133 rounded-8rpx mr-20rpx" @click="Reserve.handleResetTeaService">重置</view>
|
||
<view class="w-178rpx h-70rpx leading-70rpx text-center bg-#4C9F44 text-#fff rounded-8rpx" @click="Reserve.handleConfirmTeaService">确定</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
</wd-popup>
|
||
|
||
<!-- 选择预定时间 -->
|
||
<booking-time v-model="showBookTimePopup" :day="sevenDay" :auto-reserve-time="sevenDay.minimum_time" @selectedTime="Reserve.handleChooseReserveTime"></booking-time>
|
||
|
||
<view>
|
||
<navbar title="预约茶艺师" :leftArrow="false"></navbar>
|
||
</view>
|
||
|
||
<view>
|
||
<!-- 茶艺师信息 -->
|
||
<view class="flex items-center bg-white p-20rpx rounded-10rpx mb-20rpx mt-20rpx">
|
||
<view class="mr-28rpx relative">
|
||
<wd-img width="200rpx" height="200rpx" :src="info.image"></wd-img>
|
||
</view>
|
||
<view class="">
|
||
<view class="font-bold text-[#303133] text-30rpx leading-42rpx mr-14rpx">
|
||
{{ info.name }}
|
||
</view>
|
||
<view class="flex items-center mt-18rpx">
|
||
<view class="mr-12rpx">
|
||
<view class="bg-#FEF1F0 text-#FF5951 w-144rpx h-40rpx rounded-6rpx text-center text-22rpx leading-40rpx">{{ info.age_range }}后茶艺师</view>
|
||
</view>
|
||
<tea-specialist-level :level="info.level"></tea-specialist-level>
|
||
</view>
|
||
<view class="font-400 text-22rpx leading-32rpx text-#6A6363 mt-30rpx">已预约 {{ Number(info.sold) > 10 ? Number(info.sold) + '+' : Number(info.sold) }}</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 服务方式 -->
|
||
<view class="mt-20rpx bg-white px-30rpx py-34rpx">
|
||
<view class="flex items-center justify-between">
|
||
<view class="font-bold text-32rpx leading-44rpx">服务方式</view>
|
||
<view class="bg-[#F0F6EF] h-60rpx rounded-20rpx flex items-center justify-between py-14rpx px-30rpx relative w-304rpx font-400 text-26rpx text-[#333]">
|
||
<view
|
||
class="absolute left-30rpx top-1/2 -translate-y-1/2 z-2"
|
||
:class="serviceTypeValue == 1 ? 'text-[#fff]' : ''"
|
||
@click="Reserve.handleChooseService(1)">
|
||
到店服务
|
||
</view>
|
||
<view
|
||
class="absolute right-30rpx top-1/2 -translate-y-1/2 z-2"
|
||
:class="serviceTypeValue == 2 ? 'text-[#fff]' : ''"
|
||
@click="Reserve.handleChooseService(2)">
|
||
上门服务
|
||
</view>
|
||
<view class="swiper-service" :style="{ transform: serviceTypeValue == 1 ? 'translateY(-50%) translateX(0)' : 'translateY(-50%) translateX(76px)' }"></view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 预定门店 -->
|
||
<view class="flex items-center justify-between mt-48rpx" v-if="serviceTypeValue == 1" @click="Reserve.handleToChooseStore">
|
||
<view class="text-28rpx leading-40rpx">预定店</view>
|
||
<view class="flex items-center">
|
||
<view class="text-28rpx leading-40rpx text-#303133">{{ teaHouse.name || '请选择茶馆' }}</view>
|
||
<view>
|
||
<wd-icon name="chevron-right" size="32rpx" color="#909399"></wd-icon>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 上门服务地址 -->
|
||
<view class="flex items-center justify-between mt-48rpx" v-if="serviceTypeValue == 2" @click="Reserve.handleToAddress">
|
||
<view class="text-28rpx leading-40rpx">地址</view>
|
||
<view class="flex items-center">
|
||
<view class="text-28rpx leading-40rpx text-#303133 w-430rpx line-1 text-right">
|
||
<template v-if="address && address.id > 0">
|
||
{{address.contact}} {{ address.telephone }} {{ address.address }}
|
||
</template>
|
||
<template v-else>
|
||
请选择地址
|
||
</template>
|
||
</view>
|
||
<view>
|
||
<wd-icon name="chevron-right" size="32rpx" color="#909399"></wd-icon>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- 预定时间 -->
|
||
<view class="bg-white py-26rpx px-30rpx mt-20rpx" @click="Reserve.handleChooseTime">
|
||
<view class="text-[#303133] text-32rpx leading-44rpx font-bold mb-24rpx">预定时间</view>
|
||
<view class="flex items-center justify-between">
|
||
<view class="text-[26rpx] text-[#606266] leading-36rpx">{{ sevenDay.minimum_time }}小时起订</view>
|
||
<view class="flex items-center">
|
||
<view class="text-[28rpx] text-[#909399] leading-40rpx w-430rpx line-1 text-right">
|
||
<template v-if="startTimeLayout && endTimeLayout">
|
||
{{ startTimeLayout }} {{ endTimeLayout }}
|
||
</template>
|
||
<template v-else>
|
||
请选择
|
||
</template>
|
||
</view>
|
||
<view>
|
||
<wd-icon name="chevron-right" size="32rpx" color="#909399"></wd-icon>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 茶艺服务 -->
|
||
<view class="bg-white py-26rpx px-30rpx mt-20rpx" @click="showTeaServicePopup = true">
|
||
<view class="text-[#303133] text-32rpx leading-44rpx font-bold mb-24rpx">茶艺服务</view>
|
||
<view class="flex items-center justify-between">
|
||
<view class="text-[26rpx] text-[#606266] leading-36rpx">茶艺服务</view>
|
||
<view class="flex items-center">
|
||
<view class="text-[28rpx] text-[#909399] leading-40rpx w-430rpx line-1 text-right">
|
||
<template v-if="selectedTea.length > 0">
|
||
{{ servicePeople }}人/{{ selectedTeaTxt.join(',') }}
|
||
</template>
|
||
<template v-else>
|
||
请选择
|
||
</template>
|
||
</view>
|
||
<view>
|
||
<wd-icon name="chevron-right" size="32rpx" color="#909399"></wd-icon>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 优惠券 -->
|
||
<view class="bg-white py-26rpx px-30rpx mt-20rpx" @click="Reserve.handleToCoupon(CouponType.Discount)">
|
||
<view class="text-[#303133] text-32rpx leading-44rpx font-bold mb-24rpx">优惠券</view>
|
||
<view class="flex items-center justify-between">
|
||
<view class="text-[26rpx] text-[#606266] leading-36rpx">优惠券</view>
|
||
<view class="flex items-center">
|
||
<view class="text-[28rpx] text-[#909399] leading-40rpx w-430rpx line-1 text-right">
|
||
<template v-if="selectedCoupon?.id > 0">
|
||
{{ selectedCoupon.name }}
|
||
</template>
|
||
<template v-else>
|
||
请选择
|
||
</template>
|
||
</view>
|
||
<view>
|
||
<wd-icon name="chevron-right" size="32rpx" color="#909399"></wd-icon>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 订单备注 -->
|
||
<view class="bg-white py-26rpx px-30rpx mt-20rpx">
|
||
<view class="text-32rpx leading-44rpx text-#303133 mb-28rpx">
|
||
<text class="mr-20rpx">订单备注 </text>
|
||
<text class="text-26rpx leading-36rpx text-#909399">(选填)</text>
|
||
</view>
|
||
<wd-textarea v-model="orderRemarks"
|
||
:auto-height="true"
|
||
no-border
|
||
placeholder="有想说的可以在这里写哦!"
|
||
custom-class='!rounded-18rpx !border-2rpx !border-[#EFF0EF] !bg-[#F8F9FA]'
|
||
custom-textarea-class='!bg-[#F8F9FA] !min-h-[100rpx] !px-24rpx' />
|
||
</view>
|
||
|
||
|
||
|
||
<!-- 支付方式 -->
|
||
<!-- <view class="bg-white py-26rpx px-30rpx mt-20rpx">
|
||
<pay hide-store-balance @pay="Reserve.handleGetPayValue"></pay>
|
||
</view> -->
|
||
|
||
<view class="fixed left-0 right-0 bottom-0 z-2 bg-[#fff]"
|
||
:style="{ height: '140rpx' }">
|
||
<view class="mt-22rpx flex justify-between items-center">
|
||
<view class="flex items-center ml-40rpx mr-20rpx" @click="showCostPopup = true">
|
||
<view class="text-24rpx text-[#303133] leading-34rpx w-50rpx whitespace-nowrap mr-10rpx">合计:</view>
|
||
<view class="flex items-center h-56rpx mr-16rpx">
|
||
<price-format color="#FF5951" :first-size="40" :second-size="40" :subscript-size="28" :price="costBill.order_amount"></price-format>
|
||
</view>
|
||
<view class="flex items-center text-[#4C9F44] w-100rpx whitespace-nowrap">
|
||
<view class="text-24rpx mr-10rpx">费用明细</view>
|
||
<wd-icon :name="showCostPopup ? 'arrow-up' : 'arrow-down'" size="24rpx" color="#4C9F44"></wd-icon>
|
||
</view>
|
||
</view>
|
||
<view class="mr-30rpx">
|
||
<wd-button custom-class='!bg-[#4C9F44] !rounded-8rpx !h-70rpx' :disabled="isSubmitting" @click="Reserve.handleSubmitOrder">立即预定</wd-button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { useMessage, useToast } from 'wot-design-uni'
|
||
import { digits } from '@/utils/test'
|
||
import { router, toTimes, toPlus, getCurrentDate } from '@/utils/tools'
|
||
import PriceFormat from '@/components/PriceFormat.vue'
|
||
import { ReserveServiceCategory, OrderType } from '@/utils/order'
|
||
import { getTeaSpecialistDetails, getTeaTypeList, createTeaSpecialistOrder } from '@/api/tea'
|
||
import { getNext7Days} from '@/api/tea-room'
|
||
import type { ITeaSpecialistDetailsFields, ITeaSpecialistFuture7DaysResult, ITeaTypeListResult } from '@/api/types/tea'
|
||
import { TeaSpecialistLevelValue } from '@/utils/teaSpecialist'
|
||
import type { IUserAddressListResult } from '@/api/types/user'
|
||
import BookingTime from '@/components/BookingTime.vue'
|
||
import { CouponType } from '@/utils/coupon'
|
||
import { getTeaSpecialistDetail, getTeaSpecialistFeeDetails } from '@/api/tea-specialist'
|
||
|
||
const OSS = inject('OSS')
|
||
const toast = useToast()
|
||
const message = useMessage('wd-message-box-slot')
|
||
|
||
// 防止重复提交
|
||
const isSubmitting = ref(false)
|
||
|
||
// 服务方式
|
||
const serviceType = ref<Array<any>>([
|
||
{type: 1, name: '到店服务'},
|
||
{type: 2, name: '上门服务'},
|
||
])
|
||
const serviceTypeValue = ref<number>(1)
|
||
|
||
// 选择茶馆
|
||
const teaHouse = ref<{id: number, name: string}>({id: 0, name: ''})
|
||
|
||
// 选择预定时间
|
||
const MINIMUMTIME = 2 // TODO 暂时写死
|
||
const showBookTimePopup = ref<boolean>(false)
|
||
const sevenDay = reactive<ITeaSpecialistFuture7DaysResult>({
|
||
minimum_time: MINIMUMTIME,
|
||
time: []
|
||
})
|
||
const reserveTime = ref<Array<any>>([])
|
||
const timeSlots = ref<Array<string>>([]) // 连续选择的预约时间
|
||
const dayTitle = ref<string>('') // 周三03/18
|
||
const dayTime = ref<string>('') // 2024-03-18
|
||
const dayHours = ref<string>('') // 预定时长00:00,00:30
|
||
const startTime = ref<number>(0) // 开始时间
|
||
const endTime = ref<number>(0) // 结束时间
|
||
const countHours = ref<number>(0) // 预定了几个小时
|
||
|
||
// 上门服务-预定时间相关
|
||
const showReservePopup = ref<boolean>(false) // 预定时间popup
|
||
const currentTimePicker = ref<string>('start') // 当前选择的时间类型
|
||
const startTimeValue = ref<string>('') // 开始时间
|
||
const endTimeValue = ref<string>('') // 结束时间
|
||
const now = new Date()
|
||
const minTimestamp = Date.now() + 0.5 * 60 * 60 * 1000
|
||
// 允许选择今天及未来六天的日期
|
||
const futureTimestamp = minTimestamp + 2 * 60 * 60 * 1000
|
||
const maxTimestamp = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 6, 23, 59, 59, 999).getTime()
|
||
const startTimeLayout = ref<string>('')
|
||
const endTimeLayout = ref<string>('')
|
||
const totalHour = ref<number>(0)
|
||
|
||
// 上门服务选择的地址
|
||
const address = ref({
|
||
id: 0,
|
||
contact: '',
|
||
telephone: '',
|
||
longitude: 0,
|
||
latitude: 0,
|
||
address: '',
|
||
is_default: 0,
|
||
})
|
||
|
||
// 茶艺服务
|
||
const showTeaServicePopup = ref<boolean>(false) // 显示门店列表弹窗
|
||
const servicePeople = ref<number>(1) // 服务人数
|
||
const teaList = ref<ITeaTypeListResult[]>([]) // 茶叶列表
|
||
const selectedTea = ref<Array<any>>([]) // 选择的茶叶
|
||
const selectedTeaTxt = ref<Array<any>>([]) // 选择的茶叶文本
|
||
const selectedTeaPrice = ref<Array<any>>([]) // 选择的茶叶价格
|
||
const totalSelectedTeaPrice = ref<string>('') // 选择的茶叶总价
|
||
|
||
// 茶具使用
|
||
const teaUsageList = ref<Array<any>>([
|
||
{type: 0, name: '客户自备'},
|
||
{type: 1, name: '茶艺师提供'},
|
||
])
|
||
const teaUsageValue = ref<number>(0)
|
||
|
||
// 订单备注
|
||
const orderRemarks = ref<string>('')
|
||
|
||
// 费用明细相关
|
||
const showCostPopup = ref<boolean>(false) // 费用明细popup
|
||
|
||
// 茶艺师
|
||
const id = ref<number>(0)
|
||
const info = ref({
|
||
id: 0,
|
||
image: '',
|
||
image_arr: [], // 轮播图
|
||
name: '', // 茶艺师名字
|
||
star: 0, // 茶艺师评分
|
||
sold: 0, // 已预约
|
||
age_range: '00', // 茶艺师年龄段
|
||
level: 1, // 茶艺师等级
|
||
distance: 0, // 距离
|
||
is_time: '', // 预计到达
|
||
sex: 0, // 性别
|
||
both: 0, // 年龄
|
||
weight: 0, // 体重
|
||
height: 0, // 身高
|
||
hobby_introduce: '', // 兴趣爱好介绍
|
||
price: 0, // 服务费
|
||
mileage_price: 0, // 车马费
|
||
state: 0, // 茶艺师状态哦
|
||
server_type: 0, // 服务类型 1到店 2上门 3两者皆有
|
||
})
|
||
|
||
// 选择的优惠券
|
||
const selectedCoupon = ref<{id: number, name: string}>({id: 0, name: ''})
|
||
const selectCouponId = ref<number>(0)
|
||
|
||
// 计算费用明细 service(服务费) travel(车马费) teaServiceFee(茶艺服务) coupon(优惠券)
|
||
const bill = ref<{service: any, travel: any, teaService: any, coupon: number, total: number}>({
|
||
service: {
|
||
total: 0,
|
||
unitPrice: 0,
|
||
num: 0,
|
||
startTime: 0,
|
||
endTime: 0
|
||
},
|
||
travel: {
|
||
total: 0,
|
||
unitPrice: 0,
|
||
num: 0
|
||
},
|
||
teaService: {
|
||
total: 0,
|
||
tea: '',
|
||
teaTotal: 0,
|
||
cup: 0,
|
||
peopleNum: 0
|
||
},
|
||
coupon: 0,
|
||
total: 0
|
||
})
|
||
|
||
// 费用明细
|
||
const costBill = ref<any>({
|
||
order_amount: 0, // 订单实付价格
|
||
server_price: 0, // 服务费单价
|
||
server_all_price: 0, // 服务费总价
|
||
mileage_price: 0, // 车马费单价
|
||
team_user_distance: 0, // 车马费公里数
|
||
mileage_server_price: 0, //车马费总价
|
||
leaf_tea_all_price: 0, // 茶艺服务费
|
||
leaf_amount: 0, // 茶叶金额
|
||
teacup_price: 0, // 茶具金额
|
||
coupon_price: 0, // 优惠券金额
|
||
})
|
||
|
||
// 计算费用明细参数
|
||
const costParams = ref({
|
||
hours: 0, // 预定小时数
|
||
tea_id: '', // 茶叶id
|
||
longitude: 0, // 经度
|
||
latitude: 0, // 纬度
|
||
user_coupon_id: 0, // 优惠券id
|
||
is_teacup: 0, // 是否需要茶具 0客户自备 1茶艺师提供
|
||
team_user_id: 0, // 茶艺师id
|
||
})
|
||
|
||
onLoad(async (args) => {
|
||
if (args.id) {
|
||
id.value = Number(args.id)
|
||
costParams.value.team_user_id = Number(args.id)
|
||
|
||
// 获取茶艺师详情
|
||
const res = await getTeaSpecialistDetail({
|
||
team_user_id: args.id,
|
||
latitude: uni.getStorageSync('latitude') || import.meta.env.VITE_DEFAULT_LATITUDE,
|
||
longitude: uni.getStorageSync('longitude') || import.meta.env.VITE_DEFAULT_LONGITUDE,
|
||
})
|
||
info.value = res.teamaster
|
||
|
||
// 根据茶艺师支持的服务类型初始化服务方式
|
||
if (info.value.server_type === 1 || info.value.server_type === 3) {
|
||
serviceTypeValue.value = 1
|
||
} else if (info.value.server_type === 2 || info.value.server_type === 3) {
|
||
serviceTypeValue.value = 2
|
||
}
|
||
}
|
||
|
||
// 初始化数据
|
||
Reserve.handleInit()
|
||
})
|
||
|
||
const Reserve = {
|
||
/**
|
||
* 初始化-获取茶叶列表
|
||
*/
|
||
handleInit: async () => {
|
||
const tea = await getTeaTypeList()
|
||
teaList.value = tea as ITeaTypeListResult[]
|
||
},
|
||
|
||
/**
|
||
* 选择服务方式
|
||
* @param type 服务类型 1到店服务 2上门服务
|
||
*/
|
||
handleChooseService: (type: number) => {
|
||
if (type === 1 && (info.value.server_type === 2)) {
|
||
toast.show('该茶艺师仅支持上门服务')
|
||
return
|
||
}
|
||
|
||
if (type === 2 && (info.value.server_type === 1)) {
|
||
toast.show('该茶艺师仅支持到店服务')
|
||
return
|
||
}
|
||
|
||
serviceTypeValue.value = type
|
||
|
||
if (type === 1) {
|
||
Door.handleResetTime()
|
||
address.value = {
|
||
id: 0,
|
||
contact: '',
|
||
telephone: '',
|
||
longitude: 0,
|
||
latitude: 0,
|
||
address: '',
|
||
is_default: 0,
|
||
}
|
||
}
|
||
|
||
if (type === 2) {
|
||
// 重置事件选择
|
||
reserveTime.value = []
|
||
dayTitle.value = ''
|
||
dayTime.value = ''
|
||
dayHours.value = ''
|
||
sevenDay.minimum_time = MINIMUMTIME
|
||
sevenDay.time = []
|
||
}
|
||
|
||
teaHouse.value = {id: 0, name: ''}
|
||
},
|
||
|
||
/**
|
||
* 选择预订时间
|
||
*/
|
||
handleChooseTime: async () => {
|
||
if (info.value.state === 1) {
|
||
toast.show('茶艺师工作中,暂不可预约')
|
||
return
|
||
}
|
||
|
||
if (info.value.state === 2) {
|
||
toast.show('茶艺师已离线,暂不可预约')
|
||
return
|
||
}
|
||
|
||
if (serviceTypeValue.value === 1 && teaHouse.value.id === 0) {
|
||
// 到店服务必须要选择门店
|
||
toast.show('请先选择门店')
|
||
return false
|
||
}
|
||
|
||
showReservePopup.value = true
|
||
},
|
||
|
||
/**
|
||
* 切换预定茶叶选择
|
||
*/
|
||
handleToggleTea: (id: number, name: string, price: number) => {
|
||
console.log("🚀 ~ id:", id)
|
||
const index = selectedTea.value.indexOf(id)
|
||
console.log("🚀 ~ index:", index)
|
||
if (index >= 0) {
|
||
// 已选择,取消选择
|
||
selectedTea.value.splice(index, 1)
|
||
selectedTeaTxt.value.splice(index, 1)
|
||
selectedTeaPrice.value.splice(index, 1)
|
||
} else {
|
||
// 未选择,添加选择
|
||
selectedTea.value.push(id)
|
||
selectedTeaTxt.value.push(name)
|
||
selectedTeaPrice.value.push(price)
|
||
}
|
||
console.log("🚀 ~ selectedTea.value:", selectedTea.value)
|
||
|
||
totalSelectedTeaPrice.value = toPlus(selectedTeaPrice.value)
|
||
bill.value.teaService.total = Number(totalSelectedTeaPrice.value) // 更新茶艺服务费用
|
||
|
||
// 计算费用明细
|
||
costParams.value.tea_id = selectedTea.value.join(',')
|
||
Reserve.handleCost()
|
||
},
|
||
|
||
/**
|
||
* 选择门店
|
||
*/
|
||
handleToChooseStore: () => {
|
||
uni.$on('chooseTeaHouse', params => {
|
||
console.log("🚀 ~ params:", params)
|
||
|
||
uni.$off('chooseTeaHouse')
|
||
teaHouse.value = params
|
||
|
||
// 计算费用明细
|
||
costParams.value.longitude = params.longitude
|
||
costParams.value.latitude = params.latitude
|
||
Reserve.handleCost()
|
||
})
|
||
router.navigateTo('/bundle_b/pages/tea-specialist/store?from=reserve')
|
||
},
|
||
|
||
/**
|
||
* 选择地址
|
||
*/
|
||
handleToAddress: () => {
|
||
uni.$on('chooseAddress', params => {
|
||
console.log("🚀 ~ params:", params)
|
||
uni.$off('chooseAddress')
|
||
address.value = params
|
||
|
||
costParams.value.longitude = params.longitude
|
||
costParams.value.latitude = params.latitude
|
||
Reserve.handleCost()
|
||
})
|
||
router.navigateTo('/bundle_b/pages/tea-specialist/address/list?from=reserve')
|
||
},
|
||
|
||
/**
|
||
* 选中预定时间
|
||
*/
|
||
handleChooseReserveTime: (params: any) => {
|
||
reserveTime.value = params
|
||
// if (!digits(params.countSelectedTime)) {
|
||
// message.alert({
|
||
// title: '提示',
|
||
// msg: '选择时间段不满一小时按一小时算请合理选择时间',
|
||
// confirmButtonText: '确定',
|
||
// cancelButtonProps: {
|
||
// customClass: '!bg-[#F6F7F8] !text-[#303133] !text-32rpx !leading-44rpx !rounded-8rpx',
|
||
// },
|
||
// confirmButtonProps: {
|
||
// customClass: '!bg-[#4C9F44] !text-[#fff] !text-32rpx !leading-44rpx !rounded-8rpx',
|
||
// },
|
||
// })
|
||
// }
|
||
|
||
timeSlots.value = params.selectedTimestamps
|
||
dayTitle.value = params.dayTitle
|
||
dayTime.value = params.dayTime
|
||
const times = params.selectedTime.map(item => {
|
||
return item.time
|
||
}).join(',')
|
||
dayHours.value = times
|
||
startTime.value = params.selectedTimestamps[0]
|
||
endTime.value = params.selectedTimestamps[params.selectedTimestamps.length - 1]
|
||
|
||
// 预定了几个小时
|
||
countHours.value = params.countSelectedTime
|
||
|
||
bill.value.service = {
|
||
total: 0,
|
||
unitPrice: 0,
|
||
num: params.countSelectedTime,
|
||
startHour: params.selectedTime[0].time,
|
||
endHour: params.selectedTime[params.selectedTime.length - 1].time
|
||
}
|
||
|
||
// 计算费用明细
|
||
costParams.value.hours = params.countSelectedTime
|
||
Reserve.handleCost()
|
||
},
|
||
|
||
/**
|
||
* 跳转优惠券页面
|
||
*/
|
||
handleToCoupon(type) {
|
||
if (totalHour.value == 0) {
|
||
toast.info('请选择预定时间')
|
||
return
|
||
}
|
||
|
||
uni.$off('chooseCoupon');
|
||
uni.$on('chooseCoupon', params => {
|
||
console.log("🚀 ~ params:", params)
|
||
uni.$off('chooseCoupon')
|
||
if (params.coupon) {
|
||
selectedCoupon.value = {id: params.coupon.id, name: params.coupon.name}
|
||
bill.value.coupon = params.coupon.coupon_price
|
||
selectCouponId.value = params.coupon.id // 这里的ID是在数据表自增的ID,保存下来是为了回显列表的,没有其他作用
|
||
costParams.value.user_coupon_id = params.coupon.id
|
||
} else {
|
||
selectedCoupon.value = {id: 0, name: '' }
|
||
bill.value.coupon = 0
|
||
selectCouponId.value = 0
|
||
costParams.value.user_coupon_id = 0
|
||
}
|
||
|
||
// 计算费用明细
|
||
Reserve.handleCost()
|
||
})
|
||
|
||
// 获取预定了几个小时
|
||
router.navigateTo(`/bundle_b/pages/tea-specialist/coupon?couponId=${selectCouponId.value}&price=${costBill.value.order_amount}`)
|
||
},
|
||
|
||
/**
|
||
* 重置差茶艺服务
|
||
*/
|
||
handleResetTeaService: () => {
|
||
servicePeople.value = 1
|
||
selectedTea.value = []
|
||
teaUsageValue.value = 0
|
||
},
|
||
|
||
/**
|
||
* 确认茶艺服务
|
||
*/
|
||
handleConfirmTeaService: () => {
|
||
if (selectedTea.value.length == 0) {
|
||
toast.info('请选择预定茶叶')
|
||
return
|
||
}
|
||
showTeaServicePopup.value = false
|
||
|
||
// 计算费用明细
|
||
costParams.value.is_teacup = teaUsageValue.value
|
||
Reserve.handleCost()
|
||
},
|
||
|
||
/**
|
||
* 提交订单数据
|
||
*/
|
||
handleSubmitOrder: async () => {
|
||
if (isSubmitting.value) return
|
||
|
||
if (costParams.value.longitude == 0 && costParams.value.latitude == 0) {
|
||
toast.info('请选择地址')
|
||
return false
|
||
}
|
||
|
||
if (info.value.state === 1) {
|
||
toast.show('茶艺师工作中,暂不可预约')
|
||
return
|
||
}
|
||
|
||
if (info.value.state === 2) {
|
||
toast.show('茶艺师已离线,暂不可预约')
|
||
return
|
||
}
|
||
|
||
if (totalHour.value == 0) {
|
||
toast.info('请选择预定时间')
|
||
return false
|
||
}
|
||
|
||
if (selectedTea.value.length == 0) {
|
||
toast.info('请选择茶叶')
|
||
return false
|
||
}
|
||
|
||
// if (teaUsageValue.value == -1) {
|
||
// toast.info('请选择茶具需求')
|
||
// showTeaServicePopup.value = true
|
||
// return false
|
||
// }
|
||
|
||
isSubmitting.value = true
|
||
// uni.showLoading({
|
||
// title: '提交中...'
|
||
// })
|
||
toast.loading('提交中...')
|
||
|
||
// 提交的订单数据
|
||
const params = {
|
||
team_user_id: id.value, // 茶艺师ID
|
||
server_type: serviceTypeValue.value, // 服务方式 1到店服务 2上门服务
|
||
longitude: costParams.value.longitude, // 经度
|
||
latitude: costParams.value.latitude, // 纬度
|
||
store_id: serviceTypeValue.value == 1 ? teaHouse.value.id : 0, // 茶室Id如果是上门服务则传0
|
||
address: serviceTypeValue.value == 1 ? teaHouse.value.name : address.value.address, // 地址: 到店传茶室名称 上门则传地址
|
||
start_time: Math.floor(Door.startTimeTimestamp / 1000), // 预约开始时间
|
||
end_time: Math.floor(Door.endTimeTimestamp / 1000), // 预约结束时间
|
||
hours: totalHour.value, // 预约小时数
|
||
remrk: orderRemarks.value, // 订单备注
|
||
is_teacup: teaUsageValue.value, // 是否需要茶具 0客户自备 1茶艺师提供
|
||
tea_id: selectedTea.value.join(','), // 茶叶商品ids 多个逗号隔开
|
||
user_coupon_id: selectedCoupon.value.id, // 优惠券id默认传0
|
||
server_number: servicePeople.value
|
||
}
|
||
|
||
try {
|
||
const res = await createTeaSpecialistOrder(params)
|
||
toast.close()
|
||
|
||
uni.$on('payment', params => {
|
||
console.log("🚀 ~ params:", params)
|
||
setTimeout(() => {
|
||
uni.$off("payment")
|
||
isSubmitting.value = false
|
||
|
||
if (params.result) {
|
||
uni.redirectTo({
|
||
url: `/pages/notice/reserve?type=${OrderType.TeaSpecialist}&orderId=${params.orderId}`
|
||
})
|
||
} else {
|
||
uni.redirectTo({
|
||
url: '/bundle/order/tea-specialist/order-list'
|
||
})
|
||
}
|
||
}, 1000)
|
||
})
|
||
|
||
setTimeout(() => {
|
||
router.navigateTo(`/pages/cashier/cashier?from=${OrderType.TeaSpecialist}&orderId=${res.order_id}&teaSpecialistName=${info.value.name}&teaSpecialistOrderAmount=${res.order_amount}`)
|
||
}, 800)
|
||
} catch (error) {
|
||
toast.close()
|
||
isSubmitting.value = false
|
||
return
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 计算费用明细
|
||
*/
|
||
handleCost: async () => {
|
||
toast.loading('计费中...')
|
||
const res = await getTeaSpecialistFeeDetails(costParams.value)
|
||
toast.close()
|
||
costBill.value = res
|
||
}
|
||
}
|
||
|
||
// 上门服务
|
||
const Door = {
|
||
startTimeTimestamp: 0, // 记录开始时间戳
|
||
endTimeTimestamp: 0, // 记录结束时间戳
|
||
|
||
handleStartTimePicker: (e: {value: number}) => {
|
||
console.log("🚀 ~ e:", e)
|
||
Door.startTimeTimestamp = e.value
|
||
startTimeLayout.value = Door.handleFormatDate(e.value)
|
||
endTimeLayout.value = Door.handleFormatDate(Number(e.value) + Number(2 * 60 * 60 * 1000)) // 初始化结束时间是未来2个小时
|
||
Door.endTimeTimestamp = Number(e.value) + Number(2 * 60 * 60 * 1000) // 初始化结束时间是未来2个小时
|
||
endTimeValue.value = String(Door.endTimeTimestamp)
|
||
Door.handleTotalTimestamp()
|
||
|
||
costParams.value.hours = totalHour.value
|
||
Reserve.handleCost()
|
||
},
|
||
|
||
handleEndTimePicker: (e: {value: number}) => {
|
||
Door.endTimeTimestamp = e.value
|
||
endTimeLayout.value = Door.handleFormatDate(e.value)
|
||
Door.handleTotalTimestamp()
|
||
},
|
||
|
||
handleFormatTime: (type: string, values: string) => {
|
||
if (type === 'year') {
|
||
return `${values}年`
|
||
}
|
||
if (type === 'month') {
|
||
return `${values}月`
|
||
}
|
||
if (type === 'date') {
|
||
return `${values}日`
|
||
}
|
||
if (type === 'hour') {
|
||
return `${values}时`;
|
||
}
|
||
if (type === 'minute') {
|
||
return `${values}分`;
|
||
}
|
||
return values
|
||
},
|
||
|
||
handleTotalTimestamp: () => {
|
||
if (Door.startTimeTimestamp && Door.endTimeTimestamp) {
|
||
const diffMs = Door.endTimeTimestamp - Door.startTimeTimestamp
|
||
// 计算小时,保留一位小数
|
||
const hours = (diffMs / 1000 / 60 / 60)
|
||
const result = Math.round(hours * 10) / 10 // 保留一位小数
|
||
if (result >= 0) {
|
||
totalHour.value = result
|
||
}
|
||
return result
|
||
}
|
||
totalHour.value = 0
|
||
return 0
|
||
},
|
||
|
||
/**
|
||
* 格式化时间
|
||
*/
|
||
handleFormatDate: (timestamp: number) => {
|
||
const date = new Date(timestamp)
|
||
const year = date.getFullYear()
|
||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||
const day = String(date.getDate()).padStart(2, '0')
|
||
const hour = String(date.getHours()).padStart(2, '0')
|
||
const minute = String(date.getMinutes()).padStart(2, '0')
|
||
|
||
return `${year}-${month}-${day} ${hour}:${minute}`
|
||
},
|
||
|
||
/**
|
||
* 重置预定时间
|
||
*/
|
||
handleResetTime: () => {
|
||
startTimeValue.value = ''
|
||
endTimeValue.value = ''
|
||
startTimeLayout.value = ''
|
||
endTimeLayout.value = ''
|
||
Door.startTimeTimestamp = 0
|
||
Door.endTimeTimestamp = 0
|
||
totalHour.value = 0
|
||
currentTimePicker.value = 'start'
|
||
},
|
||
|
||
/**
|
||
* 确定时间
|
||
*/
|
||
handleConfirmHour: () => {
|
||
if (totalHour.value < sevenDay.minimum_time) {
|
||
toast.info(`起订时间${sevenDay.minimum_time}小时`)
|
||
return
|
||
}
|
||
|
||
// if (!digits(totalHour.value)) {
|
||
// message.alert({
|
||
// title: '提示',
|
||
// msg: '选择时间段不满一小时按一小时算请合理选择时间',
|
||
// confirmButtonText: '确定',
|
||
// cancelButtonProps: {
|
||
// customClass: '!bg-[#F6F7F8] !text-[#303133] !text-32rpx !leading-44rpx !rounded-8rpx',
|
||
// },
|
||
// confirmButtonProps: {
|
||
// customClass: '!bg-[#4C9F44] !text-[#fff] !text-32rpx !leading-44rpx !rounded-8rpx',
|
||
// },
|
||
// })
|
||
// }
|
||
showReservePopup.value = false
|
||
|
||
// 计算费用明细
|
||
costParams.value.hours = totalHour.value
|
||
Reserve.handleCost()
|
||
},
|
||
}
|
||
|
||
const billTotal = computed(() => {
|
||
const s = Number(bill.value.service.total) || 0
|
||
const t = Number(bill.value.travel.total) || 0
|
||
const ts = Number(bill.value.teaService.total) || 0
|
||
|
||
let total = Number(toPlus(s, t, ts))
|
||
if (bill.value.coupon > 0 ) {
|
||
total + bill.value.coupon
|
||
return total - bill.value.coupon
|
||
}
|
||
return total
|
||
})
|
||
|
||
watch(billTotal, (val) => {
|
||
bill.value.total = val
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
page {
|
||
background-color: $cz-page-background;
|
||
}
|
||
|
||
.swiper {
|
||
:deep() {
|
||
.wd-swiper-nav__item--dots-bar {
|
||
width: 56rpx !important;
|
||
height: 6rpx !important;
|
||
border-radius: 3rpx !important;
|
||
}
|
||
|
||
.is-active {
|
||
background-color: #2B9F93 !important;
|
||
}
|
||
}
|
||
}
|
||
|
||
.pay {
|
||
:deep() {
|
||
.wd-radio {
|
||
margin-top: 0 !important;
|
||
}
|
||
}
|
||
}
|
||
|
||
.swiper-service {
|
||
content: " ";
|
||
display: block;
|
||
background-color: #4C9F44;
|
||
width: 152rpx;
|
||
height: 56rpx;
|
||
position: absolute;
|
||
left: 0;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
border-radius: 20rpx;
|
||
transition: all 0.3s ease;
|
||
z-index: 0;
|
||
}
|
||
</style> |