Files
wangxiaowei 97759cdbad 完善功能
2026-05-04 16:54:47 +08:00

483 lines
18 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>
<view>
<navbar fixed layoutLeft hideLeftIcon>
<template #left>
<view class="h-48rpx flex items-center" @click="Room.handleBack">
<view class="mt-4rpx">
<wd-icon name="thin-arrow-left" size="30rpx" color="#121212" ></wd-icon>
</view>
<view class="text-[#303133] text-36rpx ml-24rpx leading-48rpx">预约茶室</view>
</view>
</template>
<template #right>
<view class="flex items-center ml-114rpx right-slot">
<!-- <view class="mr-16rpx flex items-center" @click="Room.handleCollect">
<template v-if="teaRoom.collect > 0">
<wd-img width="64rpx" height="64rpx" :src="`${OSS}icon/icon_sc_s.png`"></wd-img>
</template>
<template v-else>
<wd-img width="64rpx" height="64rpx" :src="`${OSS}icon/icon_sc.png`"></wd-img>
</template>
</view> -->
<!-- <view @click="showServicePopup = true" class="flex items-center">
<wd-img width="64rpx" height="64rpx" :src="`${OSS}icon/icon_kefu.png`"></wd-img>
</view> -->
</view>
</template>
</navbar>
</view>
<view class="mt-20rpx mx-30rpx swiper">
<view>
<wd-swiper value-key="image" height="320rpx"
:indicator="{ type: 'dots-bar' }" :list="swiperList" v-model:current="current" mode="aspectFit" @click="Room.handlePreviewImage">
</wd-swiper>
</view>
<view class="mt-38rpx flex justify-between">
<view>
<view class="text-34rpx text-[#303133] leading-48rpx font-bold line-1">{{ teaRoom.name }}</view>
<view class="relative mt-18rpx h-34rpx">
<view class="flex items-center">
<wd-rate v-model="teaRoom.star" readonly active-color="#FF5951" allow-half active-icon="star-filled" icon="star" space="4rpx"/>
<view class="text-26rpx text-[#606266] leading-34rpx ml-8rpx">{{ teaRoom.star }} 推荐</view>
</view>
</view>
</view>
<view class="flex flex-col items-end">
<!-- TODO 后续改为门店充值 -->
<view @click="Room.handleToRecharge">
<recharge-btn name="充值"></recharge-btn>
</view>
<!-- <view class="text-24rpx text-[#818CA9] mt-18rpx">{{ teaRoom.rechange_times }} 分钟前有人充值</view> -->
</view>
</view>
<view class="mt-26rpx">
<wd-gap bg-color="#F6F7F9" height="2rpx"></wd-gap>
</view>
<view class="flex justify-between items-center mt-30rpx mb-34rpx">
<view class="text-26rpx text-[#606266]">营业时间:{{ teaRoom.day_time }} {{ teaRoom.start_time }}-{{ teaRoom.end_time }}</view>
<view class="flex items-center" @click="router.navigateTo(`/bundle/tea-room/license?id=${teaRoomId}`)">
<view class="text-24rpx text-[#92928C]">资质信息</view>
<wd-icon name="chevron-right" size="32rpx" color="#C7C7C7"></wd-icon>
</view>
</view>
<view class="mt-26rpx">
<wd-gap bg-color="#F6F7F9" height="2rpx"></wd-gap>
</view>
<view class="mt-22rpx flex items-center justify-between">
<view class="">
<view class="flex items-center">
<view class="w-36rpx h-36rpx">
<wd-img width="36rpx" height="36rpx" :src="`${OSS}icon/icon_location2.png`"/>
</view>
<view class="ml-2rpx text-26rpx text-[#606266] line-2">{{ teaRoom.address }}</view>
</view>
<view class="text-[#92928C] text-24rpx ml-38rpx mt-14rpx">距您{{ teaRoom.distance }}km</view>
</view>
<view class="flex items-center mr-32rpx">
<view class="text-center mr-20rpx" @click="Room.handleLocation">
<wd-img width="64rpx" height="64rpx" :src="`${OSS}icon/icon_nav.png`"/>
<view class="text-[#606266] text-24rpx leading-32rpx mt-8rpx">导航</view>
</view>
<view class="text-center" @click="Room.handleCallPhone">
<wd-img width="64rpx" height="64rpx" :src="`${OSS}icon/icon_phone.png`"/>
<view class="text-[#606266] text-24rpx leading-32rpx mt-8rpx">电话</view>
</view>
</view>
</view>
</view>
<view class="mt-26rpx">
<wd-gap bg-color="#F6F7F9" height="20rpx"></wd-gap>
</view>
<view class="tabs">
<wd-tabs ref="tabsRef" v-model="tab" swipeable slidable="always" @change="Room.handleChangeTab" :lazy="false" :duration="0">
<wd-tab title="茶室预定" v-if="storeType == StoreType.Direct"></wd-tab>
<wd-tab title="团购套餐"></wd-tab>
<wd-tab title="抖音兑换" v-if="storeType == StoreType.Direct"></wd-tab>
</wd-tabs>
<view class="mx-30rpx mt-34rpx">
<view class="text-24rpx flex items-center justify-end mb-20rpx" v-if="storeType == StoreType.Direct && tab == 0">
<view class="flex items-center mr-12rpx">
<view class="bg-[#C9C9C9] w-20rpx h-20rpx rounded-10rpx mr-10rpx"></view>
<view>过期</view>
</view>
<view class="flex items-center mr-12rpx">
<view class="bg-[#4C9F44] w-20rpx h-20rpx rounded-10rpx mr-10rpx"></view>
<view>可预约</view>
</view>
<view class="flex items-center mr-12rpx">
<view class="bg-[#F55726] w-20rpx h-20rpx rounded-10rpx mr-10rpx"></view>
<view>已预约</view>
</view>
</view>
<view v-if="tab < 2">
<mescroll-body @init="mescrollInit" @down="downCallback" :down="downOption" :up="upOption" @up="Room.upCallback">
<room-list :is-reserve="tabIndexs === 0" :is-group-buying="tabIndexs === 1" :list="list"></room-list>
</mescroll-body>
</view>
<!-- 抖音兑换 -->
<view v-if="storeType == StoreType.Direct && tab === 2">
<dou-yin-excharge :store-id="teaRoomId" @success=""/>
</view>
</view>
</view>
<!-- 客服弹窗 -->
<wd-popup v-model="showServicePopup" lock-scroll custom-style="border-radius:30rpx;" @close="showServicePopup = false">
<view class="text-center w-440rpx h-560rpx flex flex-col justify-center items-center">
<view class="w-240rpx h-240rpx" @click="Room.handleOpenServiceSheet">
<wd-img width='100%' height='100%' :src="teaRoom.customer_service"></wd-img>
</view>
<view class="text-36rpx text-[#303133] leading-50rpx mt-54rpx">门店客服</view>
<view class="text-28rpx text-[#818CA9] leading-50rpx mt-22rpx">点击二维码添加客服</view>
</view>
</wd-popup>
<wd-action-sheet v-model="showAction" :actions="sheetMenu" cancel-text="取消" @close="showAction = false" @select="Room.handleSelectMenu" />
</view>
</template>
<script lang="ts" setup>
import { onPageScroll, onReachBottom } from '@dcloudio/uni-app'
import useMescroll from "@/uni_modules/mescroll-uni/hooks/useMescroll.js";
import RechargeBtn from '@/components/RechargeBtn.vue'
import RoomList from '@/components/reserve/RoomList.vue'
import {toast} from '@/utils/toast'
import { getTeaRoomDetail, collectTeaRoom, getStoreTeaRoomList, getTeaRoomPackage } from '@/api/tea-room'
import type { ITeaRoomDetailResult } from '@/api/types/tea-room'
import type {IUserInfoVo } from '@/api/types/login'
import { useUserStore } from '@/store'
import { router, previewImage, strToParams } from '@/utils/tools'
import { StoreType } from '@/utils/tea'
import DouYinExcharge from '@/bundle/components/DouYinExcharge.vue'
const rightPadding = inject('capsuleOffset')
const OSS = inject('OSS')
const swiperList = ref<string[]>([])
const current = ref<number>(0)
// 茶室ID
const teaRoomId = ref<number>(0)
const teaRoom = ref<any>({})
// 用户信息
const userInfo = ref<IUserInfoVo>(null)
// tab
const tab = ref<number>(0)
const tabsRef = ref()
// 弹窗
const showAction = ref<boolean>(false)
const sheetMenu = ref([])
const showServicePopup = ref<boolean>(false)
const storeType = ref<number>(StoreType.Direct)
// 分页
const { mescrollInit, downCallback, getMescroll } = useMescroll(onPageScroll, onReachBottom) // 调用mescroll的hook
const downOption = {
auto: true
}
const upOption = {
auto: true,
textNoMore: '~ 已经到底啦 ~', //无更多数据的提示
}
const list = ref<Array<any>>([])
const tabIndexs = ref<number>(0)
// 随机颜色列表
const tagColors = ['#40AE36', '#F55726']
onLoad((args) => {
console.log("🚀 ~ args:", args)
// 处理小程序码扫码进入时的 scene 参数
if (!args.id && args.scene) {
const scene = decodeURIComponent(args.scene)
const params = strToParams(scene)
Object.assign(args, params)
}
if (args.id) {
teaRoomId.value = Number(args.id)
storeType.value = Number(args.type) || StoreType.Direct
// 如果是加盟店铺则显示团购套餐TAB
if (storeType.value === StoreType.Franchise) {
tabIndexs.value = 1
}
Room.handleInit()
}
// 如果是从分享进入的未登录的情况下
uni.$on('refreshShareTeaRoomLists', (params) => {
uni.$off('refreshShareTeaRoomLists')
Room.handleInit()
list.value = []
getMescroll().resetUpScroll()
})
uni.$on('refreshTeaRoomDetail', () => {
list.value = []
getMescroll().resetUpScroll()
})
})
onUnload(() => {
uni.$off('refreshShareTeaRoomLists')
uni.$off('refreshTeaRoomDetail')
})
// 茶室分享(仅页面右上角分享,无需按钮分享)
onShareAppMessage(() => {
return {
title: teaRoom.value.name || '茶室预约',
path: `/bundle/tea-room/room?id=${teaRoomId.value}&type=${storeType.value}`,
}
})
const Room = {
sheetMenuType: '', // 记录菜单类型
/**
* 获取茶室预定和团购套餐列表
* @param mescroll
*/
upCallback: (mescroll) => {
const filter = {
page: mescroll.num,
size: mescroll.size,
id: teaRoomId.value
}
// 如果是直营店铺且是茶室预定
if (tabIndexs.value === 0 && storeType.value === StoreType.Direct) {
getStoreTeaRoomList(filter).then( res => {
console.log("🚀 ~ res:", res)
const curPageData = res.list || [] // 当前页数据
if(mescroll.num == 1) list.value = [] // 第一页需手动制空列表
list.value = list.value.concat(curPageData) //追加新数据
mescroll.endSuccess(curPageData.length, Boolean(res.more))
}).catch(() => {
mescroll.endErr() // 请求失败, 结束加载
})
} else {
// 处理团购套餐列表
getTeaRoomPackage(filter).then( res => {
const curPageData = res.list || [] // 当前页数据
if(mescroll.num == 1) list.value = [] // 第一页需手动制空列表
list.value = list.value.concat(curPageData) //追加新数据
mescroll.endSuccess(curPageData.length, Boolean(res.more))
}).catch(() => {
mescroll.endErr() // 请求失败, 结束加载
})
}
},
/**
* 初始化茶室详情
*/
handleInit: async () => {
const userStore = useUserStore()
userInfo.value = userStore.userInfo
console.log("🚀 ~ uni.getStorageSync('latitude'):", uni.getStorageSync('latitude'))
console.log("🚀 ~ uni.getStorageSync('longitude'):", uni.getStorageSync('longitude'))
const res = await getTeaRoomDetail({
id: teaRoomId.value,
latitude: uni.getStorageSync('latitude') || import.meta.env.VITE_DEFAULT_LATITUDE,
longitude: uni.getStorageSync('longitude') || import.meta.env.VITE_DEFAULT_LONGITUDE,
user_id: userInfo.value.id || 0
})
if (res && res.status == 0) {
router.switchTab('/pages/index/index')
return
}
teaRoom.value = res.details
storeType.value = teaRoom.value.operation_type
swiperList.value = teaRoom.value.image_arr
},
/**
* 处理收藏
*/
handleCollect: async () => {
let status = teaRoom.value.collect == 0 ? 1 : 0
await collectTeaRoom({
id: teaRoom.value.id,
status
})
teaRoom.value.collect = teaRoom.value.collect == 0 ? 1 : 0
},
/**
* 打开客服二维码弹窗
*/
handleOpenServiceSheet: () => {
Room.sheetMenuType = 'service'
showAction.value = true
sheetMenu.value = [
{
name: '保存图片',
value: 'saveImage'
},
{
name: '添加门店微信',
value: 'addWeChat'
}
]
},
/**
* 处理菜单选择
*/
handleSelectMenu: (item: any) => {
if (Room.sheetMenuType == 'service') {
// 处理客服相关的菜单项
if (item.value === 'saveImage') {
// 处理保存图片逻辑
toast.success('图片已保存')
} else if (item.value === 'addWeChat') {
// 处理添加微信逻辑
toast.success('已添加门店微信')
}
} else if (Room.sheetMenuType == 'call') {
uni.makePhoneCall({
phoneNumber: teaRoom.value.contact_phone
})
}
showAction.value = false // 关闭菜单
},
/**
* 处理导航逻辑
*/
handleLocation: () => {
uni.openLocation({
latitude: Number(teaRoom.value.latitude),
longitude: Number(teaRoom.value.longitude),
name: teaRoom.value.name,
address: teaRoom.value.address
})
},
/**
* 处理拨打电话逻辑
*/
handleCallPhone: () => {
Room.sheetMenuType = 'call'
showAction.value = true
sheetMenu.value = [
{
name: teaRoom.value.contact_phone || '暂无联系人',
value: ''
},
{
name: '呼叫',
value: teaRoom.value.contact_phone || ''
}
]
},
/**
* tab切换获取index
*/
handleChangeTab: (item: { index: number }) => {
tabIndexs.value = item.index
list.value = []
getMescroll().resetUpScroll()
},
/**
* 跳转到门店充值页面
*/
handleToRecharge: () => {
// router.navigateTo(`/bundle/store-recharge/store-recharge?id=${teaRoom.value.id}&storeName=${teaRoom.value.name}`)
router.navigateTo('/bundle/wallet/recharge-store?storeId=' + teaRoom.value.id)
},
/**
* 因为会从付款成功提示页跳转但是返回时页面没有栈所以需要switch方法跳转
*/
handleBack: () => {
router.navigateBack().catch(err => {
router.switchTab('/pages/index/index')
})
},
/**
* 预览图片
*/
handlePreviewImage: (e: {index: number, item: any}) => {
previewImage(swiperList.value[e.index], swiperList.value)
}
}
</script>
<style lang="scss">
page {
background-color: #fff;
}
.right-slot {
padding-right: v-bind(rightPadding);
}
.swiper {
:deep() {
.wd-swiper-nav__item--dots-bar {
width: 56rpx !important;
height: 6rpx !important;
border-radius: 3rpx !important;
}
.is-active {
background-color: #2B9F93 !important;
}
}
}
.tabs {
:deep() {
.wd-tabs__nav-item {
font-size: 32rpx !important;
color: #303133 !important;
line-height: 42rpx !important;
padding: 0 30rpx !important;
}
.wd-tabs__nav-item.is-active {
font-weight: 500 !important;
}
.wd-tabs__line {
bottom: 0 !important;
width: 60rpx !important;
height: 16rpx !important;
background-color: transparent !important;
background-image: url(#{$OSS}images/reserve_room/reserve_room_image1.png) !important;
background-size: cover !important;
}
}
}
</style>