Files
wangxiaowei 079a66f287 修改代码
2026-02-02 02:36:40 +08:00

1122 lines
48 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- 使用 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>