8 Commits

Author SHA1 Message Date
2cedcf376e 添加百度的数据埋点 2026-04-16 16:44:17 +08:00
83f4ed1d4b 完善页面 2026-04-16 12:22:03 +08:00
ee209bdb7b 完善功能 2026-04-13 14:17:04 +08:00
6262eb10a2 个人展示改为个人照片及改为非必填 2026-04-13 14:16:32 +08:00
c0547289f8 完善茶艺师套餐功能优化 2026-04-03 15:02:49 +08:00
401b1f0d2f 完善茶艺师套餐功能 2026-03-31 21:59:42 +08:00
61d0ca2bd1 完善茶艺师套餐功能 2026-03-28 19:05:32 +08:00
4729dc5ae8 完善茶艺师套餐功能 2026-03-28 16:42:40 +08:00
44 changed files with 2156 additions and 1113 deletions

10
.vscode/mcp.json vendored Normal file
View File

@ -0,0 +1,10 @@
{
"servers": {
"pencil": {
"type": "stdio",
"command": "E:\\work\\Pencil\\resources\\app.asar.unpacked\\out\\mcp-server-windows-x64.exe",
"args": ["--app", "desktop"],
"env": {}
}
}
}

View File

@ -9,6 +9,8 @@ VITE_SHOW_SOURCEMAP = true
# 请求地址
VITE_SERVER_BASEURL = 'https://test1.stnav.com'
# VITE_SERVER_BASEURL = 'https://76458.com'
# 图片上传路径
VITE_UPLOAD_BASEURL = 'https://test1.stnav.com/upload'

View File

@ -5,15 +5,69 @@
import { useUserStore } from '@/store'
import { strToParams } from '@/utils/tools'
onLaunch((options) => {
// if (options?.path === 'bundle/order/tea-room/order-detail') {
// uni.setStorageSync('transferOrder', {
// orderId: options?.query?.orderId || '',
// shareSource: options?.query?.shareSource || ''
// })
// console.log("🚀 ~ onLaunch sync:", uni.getStorageSync('transferOrder'))
const showWeixinUpdateFailedModal = () => {
// #ifdef MP-WEIXIN
uni.showModal({
title: '更新失败',
content: '新版本下载失败,请关闭当前小程序后重新进入。',
showCancel: false,
confirmText: '关闭小程序',
success: () => {
if (typeof wx.exitMiniProgram === 'function') {
wx.exitMiniProgram()
return
}
showWeixinUpdateFailedModal()
},
})
// #endif
}
const setupWeixinForcedUpdate = () => {
// #ifdef MP-WEIXIN
if (typeof wx.getUpdateManager !== 'function') {
return
}
const updateManager = wx.getUpdateManager()
updateManager.onCheckForUpdate(({ hasUpdate }) => {
if (!hasUpdate) {
return
}
uni.showLoading({
title: '新版本下载中',
mask: true,
})
})
updateManager.onUpdateReady(() => {
uni.hideLoading()
uni.showModal({
title: '更新提示',
content: '检测到新版本,需重启小程序完成更新。',
showCancel: false,
confirmText: '立即更新',
success: ({ confirm }) => {
if (confirm) {
updateManager.applyUpdate()
}
},
})
})
updateManager.onUpdateFailed(() => {
uni.hideLoading()
showWeixinUpdateFailedModal()
})
// #endif
}
onLaunch((options) => {
setupWeixinForcedUpdate()
// }
// 处理直接进入页面路由的情况:如h5直接输入路由、微信小程序分享后进入等
// https://github.com/unibest-tech/unibest/issues/192
if (options?.path) {
@ -22,10 +76,6 @@
else {
navigateToInterceptor.invoke({ url: '/' })
}
// uni.setStorageSync(LOCATION_LAT_KEY, LOCATION_DEFAULT_LAT)
// uni.setStorageSync(LOCATION_LNG_KEY, LOCATION_DEFAULT_LNG)
// uni.setStorageSync(LOCATION_CITY_KEY, LOCATION_DEFAULT_CITY)
})
onShow((options) => {
if (options?.path === 'bundle/order/tea-room/order-detail') {

View File

@ -27,4 +27,4 @@ export function claimIndexCoupon() {
export function getWxacode() {
return http.Post('/api/wxCode/getWxacode', {})
}
}

View File

@ -94,7 +94,7 @@ export function getWxCode() {
* @param params 微信登录参数,包含code, reg_source(注册来源,可选)
* @returns Promise 包含登录结果
*/
export function wxLogin(data: { code: string, source?: number }) {
export function wxLogin(data: { code: string, source?: number, store_id?: number }) {
return http.Post<IUserLogin>('/api/login/mnpLogin',
data,
{

28
src/api/tea-package.ts Normal file
View File

@ -0,0 +1,28 @@
import { http } from '@/http/alova'
/**
* 获取套餐列表
*/
export function getTeaPackageList() {
return http.Post<any>('/api/Teamaster/teamasterGroupList', null,
{
meta: { ignoreAuth: true }
}
)
}
/**
* 获取茶艺师套餐详情
*/
export function getTeaPackageDetail(id: number) {
return http.Post<any>('/api/Teamaster/teamasterGroupDetails', { id }, {
meta: { ignoreAuth: true }
})
}
/**
* 获取套餐优惠券
*/
export function getTeaPackageCoupon(id: number, company_name: string, real_name: string, mobile: string ) {
return http.Post<any>('/api/userCoupon/submitYh', { id, company_name, real_name, mobile })
}

View File

@ -51,6 +51,7 @@ export interface IHomeTeaStoreListParams {
latitude: number
longitude: number
city_area_id?: number
level?: number
}
export function getHomeTeaStoreList(data: IHomeTeaStoreListParams) {

View File

@ -15,12 +15,13 @@ export interface ITeaSpecialistParams {
size: number
latitude: number
longitude: number
level: string
search: string
level?: string
search?: string
server_type?: number // 1到店服务 2上门服务
}
export function getTeaSpecialist(data: ITeaSpecialistParams) {
return http.Post('/api/Teamaster/teamasterList', data)
return http.Post<any>('/api/Teamaster/teamasterList', data)
}
/**
@ -46,6 +47,13 @@ export function getTeaSpecialistFeeDetails(data) {
return http.Post<any>('/api/Teamaster/countTeamPrice', data)
}
/**
* 套餐费用明细
*/
export function getPackageTeaSpecialistFeeDetails(data) {
return http.Post<any>('/api/Teamaster/countGroupTeamPrice', data)
}
/**
* 茶叶商品列表
*/
@ -98,3 +106,21 @@ export function createTeaSpecialistRenewOrder(data: ITeaSpecialistRenewOrder) {
export function applyTeaSpecialistRefund(id: number, order_type: number = 10) {
return http.Post('/api/Teamaster/TeamRefund', {id, order_type})
}
/**
* 获取专属圈子标签
*/
export function getExclusiveCirclesLabel() {
return http.Post<any>('/api/Teamaster/teamasterLabel', null,
{
meta: { ignoreAuth: true }
}
)
}
/**
* 团体预约茶艺师
*/
export function reserveGroupTeaSpecialist(data) {
return http.Post<any>('/api/user/reservation', data)
}

View File

@ -59,8 +59,8 @@ export function getNext7Days() {
/**
* 获取茶叶类型列表
*/
export function getTeaTypeList() {
return http.Post<any>('/api/Teamaster/LeafList')
export function getTeaTypeList(params: { team_user_id: number, type: number }) {
return http.Post<any>('/api/Teamaster/newLeafList', params)
}
/**
@ -104,12 +104,38 @@ export interface ICreateTeaSpecialistOrderParams {
tea_id: string
user_coupon_id: number
server_number: number
team_group_id?: number
}
export function createTeaSpecialistOrder(data: ICreateTeaSpecialistOrderParams) {
return http.Post<any>('/api/Teamaster/addTeamOrder', data)
}
/**
* 创建茶艺师套餐订单
*/
export interface ICreateTeaSpecialistPackageOrderParams {
team_user_id: number
server_type: number
longitude: number
latitude: number
store_id: number
start_time: number
end_time: number
hours: number
address: string
remrk: string
is_teacup: number
tea_id: string
user_coupon_id: number
server_number: number
team_group_id?: number
}
export function createTeaSpecialistPackageOrder(data: ICreateTeaSpecialistPackageOrderParams) {
return http.Post<any>('/api/Teamaster/addGroupTeamOrder', data)
}
/***
* 获取订单详情
*/
@ -144,25 +170,4 @@ export interface ITeaSpecialistPayParams {
export function teaSpecialistPay(data: ITeaSpecialistPayParams) {
return http.Post('/api/pay/yuePay', data)
}
/**
* 团体预约
*/
export interface ITeaSpecialistGroupReserveParams {
numbers: string
other_require: string
res_time: string
contact: string
phone: string
code: string
province: string
city: string
district: string
address: string
content: string
}
export function teaSpecialistGroupReserve(data: ITeaSpecialistGroupReserveParams) {
return http.Post('/api/Teamaster/groupReservation', data)
}

View File

@ -202,23 +202,8 @@ export function getVerificationCode(data: IGetStoreVerificationCodeParams) {
/**
* 添加申请茶艺师
*/
export interface IAddTeaSpecialistParams {
license_img: string // 营业执照
username: string // 茶艺师名字
gender: number // 茶艺师性别(1男 2女)
both: number | string // 出生年月
height: string // 身高
weight: string // 体重
hobby_introduce: string // 兴趣爱好介绍
city_id: number // 城市ID
avatar: string // 头像
certificate: string // 健康证、资格证书信息
mobile: string // 联系电话
code: string // 验证码
}
export function addTeaSpecialist(data: IAddTeaSpecialistParams) {
return http.Post('/api/submitForm/addTeamaster', data)
export function addTeaSpecialist(data) {
return http.Post<any>('/api/submitForm/addTeamaster', data)
}
/**
@ -251,4 +236,13 @@ export function addJoinCooperation(data: IAddJoinCooperationParams) {
*/
export function getJoinCooperationStatus() {
return http.Post<any>('/api/submitForm/StoreRealStatus')
}
/**
* 获取配置项
*/
export function getConfig() {
return http.Post<any>('/api/setting/get' , null , {
meta: { ignoreAuth: true }
})
}

View File

@ -169,7 +169,7 @@
* @param numbers 预定时长
*/
handleInitCoupon: async (price: number) => {
const res = await getCoupons({type_id: 2, price}) // 1茶艺师 2茶室 3 个人中心
const res = await getCoupons({type_id: 2, price}) // 1茶艺师 2茶室 3 个人中心 4 茶艺师套餐
// couponList.value = res
useCoupon.value = res.count.yes_use
unUseCoupon.value = res.count.no_use
@ -178,9 +178,6 @@
couponList.value.use = res.result.filter(item => item.is_use == 0)
couponList.value.no_use = res.result.filter(item => item.is_use == 1)
}
console.log("🚀 ~ couponList.value:", couponList.value)
console.log("🚀 ~ couponList.value:", useCoupon.value)
console.log("🚀 ~ couponList.value:", unUseCoupon.value)
},
/**

View File

@ -615,7 +615,10 @@
orderStatus.value = res.order_status
// 获取茶叶列表
const tea = await getTeaTypeList()
const tea = await getTeaTypeList({
team_user_id: order.value.team_user_id,
type: 0
})
teaList.value = tea
},

View File

@ -12,7 +12,7 @@
<wd-navbar safeAreaInsetTop custom-class='!bg-[#F6F7F8]' :bordered="false" placeholder>
<template #left>
<view class="h-48rpx flex items-center">
<view class="mt-4rpx" @click="router.navigateBack()">
<view class="mt-4rpx" @click="OrderList.handleBack()">
<wd-icon name="thin-arrow-left" size="30rpx"></wd-icon>
</view>
<view class="search-box">
@ -128,10 +128,10 @@
// 返回上一页
handleBack: () => {
uni.navigateBack({
delta: 1
router.navigateBack().catch(err => {
router.switchTab('/pages/my/my')
})
},
}
}
</script>

View File

@ -0,0 +1,108 @@
<route lang="jsonc" type="page">{
"needLogin": true,
"layout": "default",
"style": {
"navigationBarTitleText": "填写资料",
"navigationBarBackgroundColor": "#fff"
}
}</route>
<template>
<view>
<!-- <wd-navbar title="填写资料" placeholder safeAreaInsetTop :bordered="false">
<template #capsule>
<wd-navbar-capsule @back="router.navigateBack" @back-home="router.switchTab('/pages/index/index')" />
</template>
</wd-navbar> -->
<view class="bg-white rounded-16rpx px-30rpx pt-28rpx mt-20rpx">
<view class="text-32rpx leading-44rpx text-#303133 mb-32rpx flex items-center">
<view class="mr-10rpx">基本信息</view>
<view class="flex items-center">
<wd-img width="16rpx" height="16rpx" :src="`${OSS}icon/icon_validate.png`"></wd-img>
</view>
</view>
<view>
<view>
<view class="text-26rpx leading-32rpx text-#606266">公司名称</view>
<view class="mt-20rpx border-b border-b-solid border-b-[#F6F7F8] pb-20rpx">
<wd-input no-border placeholder="请填写公司名称" v-model="form.company_name"></wd-input>
</view>
</view>
<view class="mt-22rpx">
<view class="text-26rpx leading-32rpx text-#606266">姓名</view>
<view class="mt-20rpx border-b border-b-solid border-b-[#F6F7F8] pb-20rpx">
<wd-input no-border placeholder="请填写真实姓名" v-model="form.real_name"></wd-input>
</view>
</view>
<view class="mt-22rpx">
<view class="text-26rpx leading-32rpx text-#606266">手机号</view>
<view class="mt-20rpx border-b border-b-solid border-b-[#F6F7F8] pb-20rpx">
<wd-input no-border placeholder="请填写手机号" v-model="form.mobile" type="number"></wd-input>
</view>
</view>
</view>
</view>
<!-- 底部按钮 -->
<view class="fixed left-0 right-0 bottom-0 z-2 bg-[#fff] flex items-center justify-center"
:style="{ height: '140rpx' }">
<view class="text-32rpx flex items-center justify-center leading-90rpx text-center w-full px-60rpx">
<view class="flex-1 w-full h-90rpx bg-[#4C9F44] rounded-8rpx text-[#fff]" @click="hasToken ? ClaimCoupon.handleSubmit() : router.navigateTo('/pages/login/login')">{{ hasToken ? '提交' : '去登录' }}</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { mobile } from '@/utils/test';
import { router, removeImageUrlPrefix, strToParams } from '@/utils/tools'
import { getTeaPackageCoupon } from '@/api/tea-package'
import { useToast } from 'wot-design-uni'
const toast = useToast()
const OSS = inject('OSS')
const form = reactive({
company_name: '',
real_name: '',
mobile: '',
})
const id = ref<number>(0)
const hasToken = ref<boolean>(false)
onLoad((args) => {
const scene = args?.scene
if (scene) {
id.value = strToParams(decodeURIComponent(args.scene))?.id
console.log("🚀 ~ id.value:", id.value)
}
})
onShow(() => {
const token = uni.getStorageSync('token')
hasToken.value = !!token
})
const ClaimCoupon = {
handleSubmit: async () => {
if (!id.value) {
toast.show('请重新扫码')
return
}
if (!form.company_name || !form.real_name || !form.mobile) {
toast.show('请完善信息')
return
}
if (!mobile(form.mobile)) {
toast.show('请输入正确的手机号')
return
}
const res = await getTeaPackageCoupon(id.value, form.company_name, form.real_name, form.mobile)
router.switchTab('/pages/index/index', 1000)
}
}
</script>

View File

@ -1,6 +1,5 @@
<!-- 使用 type="home" 属性设置首页,其他页面不需要设置,默认为page -->
<route lang="jsonc" type="page">{
"needLogin": true,
"layout": "tabbar",
"style": {
"navigationStyle": "custom"
@ -15,7 +14,7 @@
<view>
<view class="mt-20rpx mx-30rpx swiper">
<wd-swiper value-key="image" height="320rpx" :indicator="{ type: 'dots-bar' }" :list="swiperList"
<wd-swiper value-key="image" height="320rpx" :indicator="{ type: 'dots-bar' }" :list="detail.img_arr"
v-model:current="current" mode="aspectFit" @click="Detail.handlePreviewImage">
</wd-swiper>
</view>
@ -25,29 +24,30 @@
<view class="flex items-center justify-between">
<view class="flex items-center">
<view>
<price-format color="#FF5951" :first-size="44" :second-size="44" :subscript-size="28" :price="298"></price-format>
<price-format color="#FF5951" :first-size="44" :second-size="44" :subscript-size="28" :price="detail.discount_price"></price-format>
</view>
<view class="ml-20rpx">
<price-format color="#909399" :first-size="24" :second-size="24" :subscript-size="24" :price="298" :line-through="true"></price-format>
<price-format color="#909399" :first-size="24" :second-size="24" :subscript-size="24" :price="detail.price" :line-through="true"></price-format>
</view>
<view
class="bg-[#FFEDED] text-[#FF5951] text-24rpx px-20rpx py-6rpx rounded-8rpx ml-28rpx flex items-center">
2.9折热销中</view>
{{ detail.discount }}折热销中</view>
</view>
<view class="text-[#909399] text-24rpx">已售500+</view>
<view class="text-[#909399] text-24rpx">已售{{ detail.sold }}+</view>
</view>
<!-- 标题 -->
<view class="text-[#303133] text-36rpx font-bold mt-20rpx">清风套餐-标准品质</view>
<view class="text-[#303133] text-36rpx font-bold mt-20rpx">{{ detail.title }}</view>
<!-- 标签 -->
<view class="flex items-center mt-20rpx">
<view
<!-- <view
class="text-[#C08A45] text-22rpx border-2rpx border-solid border-[#C3801C] rounded-8rpx px-12rpx py-6rpx mr-16rpx font-500">
回头客92</view>
回头客92
</view> -->
<view
class="text-[#C08A45] text-22rpx border-2rpx border-solid border-[#C3801C] rounded-8rpx px-12rpx py-6rpx font-500">
500+购买</view>
{{ detail.sold }}+购买</view>
</view>
<!-- 保障框 -->
@ -86,12 +86,12 @@
<!-- 套餐详情 -->
<view class="bg-white rounded-16rpx py-26rpx px-30rpx mt-24rpx mx-30rpx">
<view class="text-[#303133] text-32rpx leading-44rpx font-bold mb-24rpx">套餐详情</view>
<view class="mt-24rpx" v-if="teaRoomPackage.introduce">
<view class="mt-24rpx">
<view class="flex items-center mb-20rpx">
<wd-img width="36rpx" height="36rpx" :src="`${OSS}icon/icon_tcsm.png`"></wd-img>
<view class="font-bold text-28rpx leading-48rpx text-[#303133] ml-6rpx">套餐介绍</view>
</view>
<rich-text :nodes="teaRoomPackage.introduce"></rich-text>
<rich-text :nodes="detail.introduce_details"></rich-text>
</view>
<view class="mt-30rpx">
<view class="flex items-center mb-20rpx">
@ -100,7 +100,7 @@
</view>
<view class="flex items-center">
<view class="bg-[#6A6363] w-8rpx h-8rpx rounded-full mr-12rpx"></view>
<view class="font-500 text-26rpx text-[#303133]">超过一小时100元</view>
<view class="font-500 text-26rpx text-[#303133]">{{ detail.other_description }}</view>
</view>
</view>
</view>
@ -116,36 +116,36 @@
</view>
<view class="flex items-center">
<view class="bg-[#6A6363] w-8rpx h-8rpx rounded-full mr-12rpx"></view>
<view class="font-500 text-26rpx text-[#303133]">个人品鉴,小型聚会</view>
<view class="font-500 text-26rpx text-[#303133]">{{ detail.description }}</view>
</view>
</view>
<view class="mt-30rpx" v-if="teaRoomPackage.hour">
<view class="mt-30rpx">
<view class="flex items-center mb-20rpx">
<wd-img width="36rpx" height="36rpx" :src="`${OSS}icon/icon_sysc.png`"></wd-img>
<view class="font-bold text-28rpx leading-48rpx text-[#303133] ml-6rpx">适用时长</view>
</view>
<view class="">{{ teaRoomPackage.hour }}小时</view>
<view class="">{{ detail.hour }}小时</view>
</view>
<view class="mt-30rpx" v-if="teaRoomPackage.pl_number">
<view class="mt-30rpx">
<view class="flex items-center mb-20rpx">
<wd-img width="36rpx" height="36rpx" :src="`${OSS}icon/icon_syrs.png`"></wd-img>
<view class="font-bold text-28rpx leading-48rpx text-[#303133] ml-6rpx">使用人数</view>
</view>
<view class="">{{ teaRoomPackage.pl_number }}</view>
<view class="">{{ detail.pl_number }}</view>
</view>
<view class="mt-20rpx mb-24rpx">
<wd-gap height="2rpx" bgColor="#F6F7F9"></wd-gap>
</view>
<view class="mt-30rpx" v-if="teaRoomPackage.returd_details">
<view class="mt-30rpx">
<view class="flex items-center mb-20rpx">
<wd-img width="36rpx" height="36rpx" :src="`${OSS}icon/icon_tgsm.png`"></wd-img>
<view class="font-bold text-28rpx leading-48rpx text-[#303133] ml-6rpx">退改说明</view>
</view>
<rich-text :nodes="teaRoomPackage.returd_details"></rich-text>
<rich-text :nodes="detail.returd_details"></rich-text>
</view>
</view>
</view>
@ -160,17 +160,21 @@
</template>
<script lang="ts" setup>
import { toast } from '@/utils/toast'
import { useToast } from 'wot-design-uni'
import { router, previewImage, replaceNewLine } from '@/utils/tools'
import PriceFormat from '@/components/PriceFormat.vue'
import { getTeaPackageDetail } from '@/api/tea-package'
const OSS = inject('OSS')
const swiperList = ref<string[]>([])
const current = ref<number>(0)
const toast = useToast()
// 套餐ID
const teaPackageId = ref<number>(0)
// 套餐详情
const detail = ref<any>({})
const teaRoomPackage = reactive({
introduce: '',
@ -179,7 +183,18 @@ const teaRoomPackage = reactive({
returd_details: ''
})
onLoad((args) => {
Detail.handleInit(args)
if (!args.id) {
toast.show('缺少套餐ID')
router.navigateBack()
return
}
// 设置套餐ID
uni.setStorageSync('teaPackageId', args.id)
uni.setStorageSync('teaPackageType', args.type)
console.log('teaPackageId', uni.getStorageSync('teaPackageId'), uni.getStorageSync('teaPackageType'))
teaPackageId.value = args.id
Detail.handleInit()
})
@ -188,14 +203,16 @@ const Detail = {
/**
* 初始包间详情
*/
handleInit: async (args) => {
handleInit: async () => {
// 包间详情
const res = await getTeaPackageDetail(teaPackageId.value)
detail.value = res[0]
detail.value.introduce_details = replaceNewLine(detail.value.introduce_details)
detail.value.returd_details = replaceNewLine(detail.value.returd_details)
},
/**
* 提交订单
* 立即预约
*/
handleReserve: async () => {
router.navigateTo('/bundle/package/service')

View File

@ -69,12 +69,12 @@
<view>
<view class="flex justify-between items-center text-30rpx text-[#303133] leading-42rpx">
<view>套餐</view>
<view>¥{{ costBill.server_all_price }}</view>
<view>¥{{ costBill.server_price }}</view>
</view>
<view
class="flex justify-between items-center text-24rpx text-[#909399] leading-34rpx mt-16rpx">
<view>服务费(含两小时)</view>
<view>服务费(含{{ teaPackage.hour }}小时)</view>
<view>x1</view>
</view>
</view>
@ -133,6 +133,18 @@
</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>
@ -191,7 +203,7 @@
<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>
<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">
@ -244,7 +256,7 @@
<!-- 茶艺师信息 -->
<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" radius="10rpx"></wd-img>
<wd-img width="200rpx" height="200rpx" :src="info.image" radius="10rpx" @click="previewImage(info.image, [info.image])"></wd-img>
</view>
<view class="">
<view class="font-bold text-[#303133] text-30rpx leading-42rpx mr-14rpx">
@ -292,9 +304,11 @@
<view class="font-400 text-28rpx text-[#303133] line-1">{{ address.name }}</view>
<view class="flex items-start mt-10rpx">
<view class="w-28rpx h-28rpx flex items-center mr-8rpx mt-8rpx">
<wd-img :src="`${OSS}icon/icon_location4.png`" width="28rpx" height="28rpx"></wd-img>
<wd-img :src="`${OSS}icon/icon_location4.png`" width="28rpx"
height="28rpx"></wd-img>
</view>
<view class="font-400 text-28rpx text-[#303133] line-1 w-400rpx">{{ address.address }}
</view>
<view class="font-400 text-28rpx text-[#303133] line-1 w-400rpx">{{ address.address }}</view>
</view>
</view>
</view>
@ -354,6 +368,27 @@
</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">
@ -392,13 +427,15 @@
<script lang="ts" setup>
import { useMessage, useToast } from 'wot-design-uni'
import { router, toPlus } from '@/utils/tools'
import { router, toPlus, previewImage } from '@/utils/tools'
import PriceFormat from '@/components/PriceFormat.vue'
import { OrderType } from '@/utils/order'
import { getTeaTypeList, createTeaSpecialistOrder } from '@/api/tea'
import { getTeaTypeList, createTeaSpecialistPackageOrder } from '@/api/tea'
import type { ITeaSpecialistFuture7DaysResult, ITeaTypeListResult } from '@/api/types/tea'
import BookingTime from '@/components/BookingTime.vue'
import { getTeaSpecialistDetail, getTeaSpecialistFeeDetails } from '@/api/tea-specialist'
import { getTeaSpecialistDetail, getPackageTeaSpecialistFeeDetails } from '@/api/tea-specialist'
import { getTeaPackageDetail } from '@/api/tea-package'
import { CouponType } from '@/utils/coupon'
const OSS = inject('OSS')
const toast = useToast()
@ -506,6 +543,10 @@ const info = ref({
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: {
@ -555,9 +596,25 @@ const costParams = ref({
user_coupon_id: 0, // 优惠券id
is_teacup: 0, // 是否需要茶具 0客户自备 1茶艺师提供
team_user_id: 0, // 茶艺师id
team_group_id: 0, // 茶艺师套餐ID
})
// 选择的套餐
const teaPackage = ref<any>({})
const teaPackageId = ref<number>(0)
onLoad(async (args) => {
teaPackageId.value = Number(uni.getStorageSync('teaPackageId'))
if (teaPackageId.value === 0) {
toast.show('请选择套餐')
router.navigateBack(1, 500)
return
}
// 设置套餐ID
costParams.value.team_group_id = Number(teaPackageId.value)
if (args.id) {
id.value = Number(args.id)
costParams.value.team_user_id = Number(args.id)
@ -595,7 +652,10 @@ const Reserve = {
* 初始化-获取茶叶列表
*/
handleInit: async () => {
const tea = await getTeaTypeList()
const tea = await getTeaTypeList({
team_user_id: id.value,
type: uni.getStorageSync('teaPackageType')
})
teaList.value = tea as ITeaTypeListResult[]
},
@ -604,10 +664,19 @@ const Reserve = {
*/
handleSetServiceType: async () => {
// 设置默认服务方式
const serviceStorage = uni.getStorageSync('selectedPackageService')
if (serviceStorage) {
const serviceData = serviceStorage
const storage = uni.getStorageSync('selectedPackageService')
if (storage) {
const serviceData = storage
serviceTypeValue.value = serviceData.serviceType
// 到店服务
if (serviceTypeValue.value == 1) {
teaHouse.value = {
id: serviceData.id,
name: serviceData.name,
}
}
// 如果是到店服务则默认选择门店,如果是上门服务则默认选择地址
address.value = {
id: serviceData.id,
@ -627,6 +696,11 @@ const Reserve = {
router.navigateBack(1, 500)
return
}
const res = await getTeaPackageDetail(uni.getStorageSync('teaPackageId'))
teaPackage.value = res[0]
sevenDay.minimum_time = Number(teaPackage.value.hour)
// costBill.value.package = package.price
},
/**
@ -653,24 +727,21 @@ const Reserve = {
},
/**
* 切换预定茶叶选择
* 切换预定茶叶选择 (单选)
*/
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)
selectedTea.value = []
selectedTeaTxt.value = []
selectedTeaPrice.value = []
} else {
// 未选择,添加选择
selectedTea.value.push(id)
selectedTeaTxt.value.push(name)
selectedTeaPrice.value.push(price)
// 未选择,设置为当前选择 (排他单选)
selectedTea.value = [id]
selectedTeaTxt.value = [name]
selectedTeaPrice.value = [price]
}
console.log("🚀 ~ selectedTea.value:", selectedTea.value)
totalSelectedTeaPrice.value = toPlus(selectedTeaPrice.value)
bill.value.teaService.total = Number(totalSelectedTeaPrice.value) // 更新茶艺服务费用
@ -685,8 +756,6 @@ const Reserve = {
*/
handleToChooseStore: () => {
uni.$on('chooseTeaHouse', params => {
console.log("🚀 ~ params:", params)
uni.$off('chooseTeaHouse')
teaHouse.value = params
@ -759,6 +828,40 @@ const Reserve = {
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}&typeId=4`)
},
/**
* 重置差茶艺服务
*/
@ -809,6 +912,16 @@ const Reserve = {
return false
}
if (totalHour.value < sevenDay.minimum_time) {
toast.info(`起订时间${sevenDay.minimum_time}小时`)
return
}
if (totalHour.value > sevenDay.minimum_time) {
toast.info(`结束时间不能超过${sevenDay.minimum_time}小时`)
return
}
// 选择了上门服务则必须要茶艺师提供茶具
if (serviceTypeValue.value === 2 && teaUsageValue.value === 0) {
teaUsageValue.value = 1
@ -841,34 +954,32 @@ const Reserve = {
remrk: orderRemarks.value, // 订单备注
is_teacup: teaUsageValue.value, // 是否需要茶具 0客户自备 1茶艺师提供
tea_id: selectedTea.value.join(','), // 茶叶商品ids 多个逗号隔开
user_coupon_id: 0, // 优惠券id默认传0
server_number: servicePeople.value
user_coupon_id: selectedCoupon.value.id, // 优惠券id默认传0
server_number: servicePeople.value,
team_group_id: teaPackageId.value, // 茶艺师套餐ID
}
try {
const res = await createTeaSpecialistOrder(params)
const res = await createTeaSpecialistPackageOrder(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}`
})
// 结果通知改为TeaPackage
router.reLaunch(`/pages/notice/reserve?type=${OrderType.TeaPackage}&orderId=${params.orderId}&teaPackageTitle=${teaPackage.value.title}`)
} else {
uni.redirectTo({
url: '/bundle/order/tea-specialist/order-list'
})
toast.info('订单未支付')
router.reLaunch('/bundle/order/tea-specialist/order-list', 800)
}
}, 1000)
})
setTimeout(() => {
router.navigateTo(`/pages/cashier/cashier?from=${OrderType.TeaSpecialist}&orderId=${res.order_id}&teaSpecialistName=${info.value.name}&teaSpecialistOrderAmount=${res.order_amount}`)
router.navigateTo(`/pages/cashier/cashier?from=${OrderType.TeaSpecialist}&orderId=${res.order_id}&teaSpecialistName=${info.value.name}&teaSpecialistOrderAmount=${res.order_amount}&teaPackageTitle=${teaPackage.value.title}`)
}, 800)
} catch (error) {
toast.close()
@ -881,8 +992,11 @@ const Reserve = {
* 计算费用明细
*/
handleCost: async () => {
console.log("🚀 ~ costParams.value:", costParams.value)
toast.loading('计费中...')
const res = await getTeaSpecialistFeeDetails(costParams.value)
costParams
const res = await getPackageTeaSpecialistFeeDetails(costParams.value)
toast.close()
costBill.value = res
}
@ -898,8 +1012,10 @@ const Door = {
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个小时
// 动态最小预定时间
const minHour = sevenDay.minimum_time || 2
endTimeLayout.value = Door.handleFormatDate(Number(e.value) + Number(minHour * 60 * 60 * 1000))
Door.endTimeTimestamp = Number(e.value) + Number(minHour * 60 * 60 * 1000)
endTimeValue.value = String(Door.endTimeTimestamp)
Door.handleTotalTimestamp()
@ -909,7 +1025,13 @@ const Door = {
handleEndTimePicker: (e: { value: number }) => {
Door.endTimeTimestamp = e.value
endTimeLayout.value = Door.handleFormatDate(e.value)
// 保证选择的时长不能小于最小预定时间
const minHour = sevenDay.minimum_time || 2
if (Door.endTimeTimestamp - Door.startTimeTimestamp < minHour * 60 * 60 * 1000) {
Door.endTimeTimestamp = Door.startTimeTimestamp + minHour * 60 * 60 * 1000
endTimeValue.value = String(Door.endTimeTimestamp)
}
endTimeLayout.value = Door.handleFormatDate(Door.endTimeTimestamp)
Door.handleTotalTimestamp()
},
@ -984,6 +1106,11 @@ const Door = {
return
}
if (totalHour.value > sevenDay.minimum_time) {
toast.info(`结束时间不能超过${sevenDay.minimum_time}小时`)
return
}
// if (!digits(totalHour.value)) {
// message.alert({
// title: '提示',

View File

@ -38,7 +38,17 @@
<!-- 商家信息 -->
<template v-if="serviceType === 1">
<view class="text-34rpx font-bold leading-48rpx text-#303133">商家信息</view>
<view class="mt-32rpx">
<view class="service-search-box relative mt-20rpx">
<wd-search placeholder="门店名称" cancel-txt="搜索" placeholder-left hide-cancel custom-input-class="!h-72rpx" v-model="storeName" >
</wd-search>
<view
class="absolute top-1/2 -translate-y-1/2 right-2rpx w-142rpx h-64rpx leading-64rpx text-center rounded-32rpx bg-#4C9F44 text-#fff font-400 text-32rpx"
@click="Service.handleSearch">
搜索
</view>
</view>
<view class="mt-20rpx">
<mescroll-body @init="mescrollInit" @down="downCallback" @up="Service.upCallback"
:up="upOption">
<wd-radio-group v-model="storeIndex" size="large" shape="dot" checked-color="#4C9F44">
@ -199,6 +209,12 @@ const Service = {
addressList.value = Array.isArray(res) ? res : []
},
// 搜索
handleSearch: () => {
list.value = []
getMescroll().resetUpScroll()
},
/**
* 跳转地址添加/编辑页面
* @param id 地址ID,0表示添加,非0表示编辑
@ -273,7 +289,7 @@ const Service = {
}
uni.setStorageSync('selectedPackageService', address) // 将选择的服务信息存储到本地,供下一个页面使用
router.navigateTo('/bundle/package/tea-specialist')
router.navigateTo('/bundle/package/tea-specialist?serviceType=' + serviceType.value) // 跳转到下一个页面,并传递服务方式参数
}
}
</script>
@ -282,4 +298,12 @@ const Service = {
page {
background: $cz-page-background;
}
.service-search-box {
:deep() {
.wd-search {
padding: 10rpx 0 !important;
}
}
}
</style>

View File

@ -15,11 +15,11 @@
<view class="text-30rpx font-bold text-[#303133]">茶艺师</view>
<view class="text-24rpx text-[#818CA9] mt-12rpx mb-32rpx font-500">匹配心动茶艺师</view>
<view class="flex flex-wrap justify-between">
<view class="w-216rpx mb-30rpx relative" v-for="(item, index) in list" :key="index"
<view class="grid grid-cols-3 gap-y-20rpx gap-x-20rpx justify-items-center">
<view class="w-216rpx relative" v-for="(item, index) in list" :key="index"
@click="TeaSpecialist.handleSelect(index)">
<view class="relative w-216rpx h-216rpx rounded-16rpx overflow-hidden">
<wd-img :src="item.avatar" width="216rpx" height="216rpx" mode="aspectFill"></wd-img>
<wd-img :src="item.image" width="216rpx" height="216rpx" mode="aspectFill"></wd-img>
<view class="absolute bottom-0 left-0 z-10">
<!-- 选中的样式 -->
<view
@ -34,11 +34,13 @@
</view>
</view>
<view class="text-28rpx text-[#2A3B56] mt-12rpx line-1">{{ item.name }}</view>
<view class="text-24rpx text-[#F55B02] mt-6rpx">{{ item.level }}</view>
<view class="text-24rpx text-[#F55B02] mt-6rpx">
<tea-specialist-level :level="item.level"></tea-specialist-level>
</view>
</view>
</view>
<view class="flex justify-center mt-10rpx">
<view class="flex justify-center mt-40rpx">
<view class="flex items-center justify-center bg-[#F6F7F8] rounded-34rpx h-100rpx w-320rpx"
@click="TeaSpecialist.handleMore">
<wd-img :src="`${OSS}icon/icon_more.png`" width="36rpx" height="36rpx"></wd-img>
@ -50,23 +52,25 @@
</view>
<!-- 茶艺师详细信息模块 -->
<view class="mx-28rpx mt-20rpx mb-30rpx relative border-2rpx border-solid border-[#F6F4F4]" v-if="selectedSpecialist">
<view class="mx-28rpx mt-20rpx mb-30rpx relative border-2rpx border-solid border-[#F6F4F4]" v-if="info">
<view class="flex justify-between items-start bg-[#F5F7FF] rounded-t-16rpx p-30rpx">
<view>
<view class="text-36rpx font-bold text-[#303133] mb-12rpx">{{ selectedSpecialist.name }}</view>
<view class="text-28rpx text-[#120D26] mb-12rpx">{{ selectedSpecialist.title }}</view>
<view class="text-36rpx font-bold text-[#303133] mb-12rpx">{{ info.name }}</view>
<view class="text-28rpx text-[#120D26] mb-12rpx">
<tea-specialist-level :level="info.level"></tea-specialist-level>
</view>
<view class="flex items-center text-24rpx text-[#909399] mb-16rpx">
<wd-img :src="`${OSS}icon/icon_store_cert.png`" width="36rpx" height="36rpx"
class="mr-8rpx"></wd-img>
<text>商家认证的名称</text>
<text @click="TeaSpecialist.handlePreviewInfo">{{ info.company_abv }}</text>
</view>
<view class="flex items-center">
<wd-rate v-model="selectedSpecialist.rating" readonly active-color="#FF5951" allow-half
<wd-rate v-model="info.sold" readonly active-color="#FF5951" allow-half
active-icon="star-filled" icon="star" space="4rpx" />
<text class="text-24rpx text-[#606266] ml-10rpx">{{ selectedSpecialist.rating }} 推荐</text>
<text class="text-24rpx text-[#606266] ml-10rpx">{{ info.sold }} 推荐</text>
</view>
</view>
<view class="w-120rpx h-120rpx" @click="TeaSpecialist.handleChat">
<view class="w-120rpx h-120rpx">
<wd-img :src="`${OSS}icon/icon_msg.png`" width="120rpx" height="120rpx" placeholder="icon"></wd-img>
</view>
</view>
@ -77,10 +81,8 @@
<view class="bg-[#EBFBF7] h-64rpx rounded-12rpx p-14rpx flex items-center mt-22rpx text-[#35C1A0]">
<wd-img :src="`${OSS}icon/icon_heart.png`" width="36rpx" height="36rpx"></wd-img>
<text class="font-bold text-24rpx leading-34rpx ml-16rpx">服务方式</text>
<text class="ml-20rpx font-400 text-24rpx leading-34rpx">到店服务</text>
<text class="ml-20rpx font-400 text-24rpx leading-34rpx">上门服务</text>
<!-- <text class="ml-20rpx font-400 text-24rpx leading-34rpx" v-if="info.server_type == 1 || info.server_type == 3">到店服务</text>
<text class="ml-20rpx font-400 text-24rpx leading-34rpx" v-if="info.server_type == 2 || info.server_type == 3">上门服务</text> -->
<text class="ml-20rpx font-400 text-24rpx leading-34rpx" v-if="info.server_type == 1">到店服务</text>
<text class="ml-20rpx font-400 text-24rpx leading-34rpx" v-if="info.server_type == 2">上门服务</text>
</view>
<!-- 基本属性 -->
@ -89,25 +91,25 @@
<wd-img width="40rpx" height="40rpx" :src="`${OSS}icon/icon_gender.png`"
class="mr-16rpx"></wd-img>
<text class="mr-16rpx">性别:</text>
<text class="text-[#303133]">女</text>
<text class="text-[#303133]">{{ info.sex }}</text>
</view>
<view class="flex items-center text-28rpx text-[#606266]">
<wd-img width="40rpx" height="40rpx" :src="`${OSS}icon/icon_born.png`"
class="mr-16rpx"></wd-img>
<text class="mr-16rpx">年龄:</text>
<text class="text-[#303133]">21岁</text>
<text class="text-[#303133]">{{ info.both }}岁</text>
</view>
<view class="flex items-center text-28rpx text-[#606266]">
<wd-img width="40rpx" height="40rpx" :src="`${OSS}icon/icon_height.png`"
class="mr-16rpx"></wd-img>
<text class="mr-16rpx">身高:</text>
<text class="text-[#303133]">165cm</text>
<text class="text-[#303133]">{{ info.height }}cm</text>
</view>
<view class="flex items-center text-28rpx text-[#606266]">
<wd-img width="40rpx" height="40rpx" :src="`${OSS}icon/icon_weight.png`"
class="mr-16rpx"></wd-img>
<text class="mr-16rpx">体重:</text>
<text class="text-[#303133]">53kg</text>
<text class="text-[#303133]">{{ info.weight }}kg</text>
</view>
</view>
@ -115,7 +117,8 @@
<view class="mt-36rpx border border-solid border-[#F0F2F5] rounded-14rpx p-24rpx pb-8rpx bg-[#FFFFFF]">
<view class="flex flex-wrap">
<view class="flex flex-wrap items-center">
<view class="mr-8rpx bg-[#4C9F44] text-[#fff] px-10rpx leading-34rpx font-400 text-24rpx rounded-4rpx">123</view>
<view class="mr-8rpx bg-[#4C9F44] text-[#fff] px-10rpx leading-34rpx font-400 text-24rpx rounded-4rpx"
v-for="label in info.label" :key="label.id">{{ label.label_name }}</view>
</view>
</view>
</view>
@ -134,56 +137,129 @@
<script lang="ts" setup>
import { inject, ref, computed } from 'vue'
import { router } from '@/utils/tools'
import { router, previewImage } from '@/utils/tools'
import { getTeaSpecialist, getTeaSpecialistDetail } from '@/api/tea-specialist'
import type { ITeaSpecialistResult } from '@/api/types/tea-specialist'
import { toast } from '@/utils/toast'
const OSS = inject('OSS')
// 模拟数据列表
const list = ref([
{ name: '春风', level: '初级茶艺师', title: '清风茶艺师', avatar: 'https://picsum.photos/400/400?radom=1', rating: 4.5 },
{ name: '春风', level: '初级茶艺师', title: '清风茶艺师', avatar: 'https://picsum.photos/400/400?radom=2', rating: 4.8 },
{ name: '夏雪', level: '初级茶艺师', title: '清风茶艺师', avatar: 'https://picsum.photos/400/400?radom=3', rating: 5.0 },
{ name: '春风', level: '初级茶艺师', title: '清风茶艺师', avatar: 'https://picsum.photos/400/400?radom=4', rating: 4.2 },
{ name: '春风', level: '初级茶艺师', title: '清风茶艺师', avatar: 'https://picsum.photos/400/400?radom=5', rating: 4.5 },
{ name: '夏雪', level: '初级茶艺师', title: '清风茶艺师', avatar: 'https://picsum.photos/400/400?radom=6', rating: 4.5 }
])
// 茶艺师列表及分页参数
const list = ref<any[]>([])
const page = ref(1)
const size = 6
const hasMore = ref(true)
const selectedIndex = ref(0)
const selectedSpecialist = computed(() => list.value[selectedIndex.value])
const info = ref({
team_user_id: 0,
name: '', // 茶艺师名字
star: 0, // 茶艺师评分
level: 1, // 茶艺师等级
sold: 0, // 推荐
sex: 0, // 性别
both: 0, // 年龄
weight: 0, // 体重
height: 0, // 身高
mileage_price: 0, // 车马费
server_type: 0, // 服务方式 1到店 2上门 3两者都有
label: [], // 专属圈子标签
company_abv: '', // 公司简称
company_url: '', // 图片链接
company_status: 0, // 是否有公司信息
})
const serviceType = ref<number>(1) // 服务方式,默认为到店服务
onLoad((args) => {
serviceType.value = args.serviceType || 1 // 默认到店服务
TeaSpecialist.handleGetTeaSpecialistList()
})
const TeaSpecialist = {
/**
* 获取茶艺师列表,分页加载
*/
handleGetTeaSpecialistList: async () => {
if (!hasMore.value) return
console.log("🚀 ~ uni.getStorageSync('teaPackageType'):", uni.getStorageSync('teaPackageType'))
const storage = uni.getStorageSync('selectedPackageService')
const filter = {
page: page.value,
size,
latitude: storage.latitude || import.meta.env.VITE_DEFAULT_LATITUDE,
longitude: storage.longitude || import.meta.env.VITE_DEFAULT_LONGITUDE,
level: uni.getStorageSync('teaPackageType'),
server_type: serviceType.value, // 1到店服务 2上门服务
}
try {
const res = await getTeaSpecialist(filter)
const curPageData = res.list || []
if (page.value === 1) {
list.value = curPageData
if (curPageData.length > 0) {
selectedIndex.value = 0
TeaSpecialist.handleGetTeaSpecialistDetail(curPageData[0].team_user_id)
}
} else {
list.value = list.value.concat(curPageData)
}
hasMore.value = curPageData.length === size
if (hasMore.value) page.value += 1
} catch (e) {
uni.showToast({ title: '加载失败', icon: 'none' })
}
},
/**
* 选中某个茶艺师
*/
handleSelect: (index: number) => {
selectedIndex.value = index
const item = list.value[index]
if (item && item.team_user_id) {
TeaSpecialist.handleGetTeaSpecialistDetail(item.team_user_id)
}
},
/**
* 加载更多茶艺师
*/
handleMore: () => {
uni.showToast({
title: '加载更多',
icon: 'none'
})
TeaSpecialist.handleGetTeaSpecialistList()
},
/**
* 点击聊天
* 获取茶艺师详情
*/
handleChat: () => {
uni.showToast({
title: '打开聊天',
icon: 'none'
handleGetTeaSpecialistDetail: async (id: number) => {
const res = await getTeaSpecialistDetail({
team_user_id: 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
},
/**
* 下一步
*/
handleNext: () => {
router.navigateTo('/bundle/package/reserve?id=439')
console.log("🚀 ~ info.value:", info.value)
router.navigateTo(`/bundle/package/reserve?id=${info.value.team_user_id}`)
},
/**
* 查看商家信息
*/
handlePreviewInfo: () => {
if (info.value.company_status) {
previewImage(info.value.company_url, [info.value.company_url])
} else {
toast.info('暂无商家信息')
}
}
}

View File

@ -100,7 +100,7 @@
</view>
</view>
<view class="flex justify-between items-center">
<view class="flex justify-between items-center mb-54rpx">
<view class="font-400 text-[#303133] text-30rpx leading-42rpx">修改手机号</view>
<view class="flex justify-end">
<view class="flex items-center">
@ -113,6 +113,17 @@
</view>
</view>
</view>
<view class="flex justify-between items-center">
<view class="font-400 text-[#303133] text-30rpx leading-42rpx">版本号</view>
<view class="flex justify-end">
<view class="flex items-center">
<view class="font-400 text-[#303133] text-30rpx leading-42rpx">
2.14.9
</view>
</view>
</view>
</view>
</view>
<view class="bg-white rounded-16rpx mt-144rpx mx-30rpx px-30rpx py-32rpx text-center font-400 text-[#303133] text-30rpx leading-42rpx" @click="showLogoutPopup = true">

View File

@ -24,7 +24,7 @@
</view>
<view class="flex-1">
<wd-input no-border placeholder="请填写预约人数" custom-class="!px-28rpx !py-20rpx"></wd-input>
<wd-input no-border placeholder="请填写预约人数" custom-class="!px-28rpx !py-20rpx" v-model="form.numbers" inputmode="numeric"></wd-input>
</view>
</view>
</view>
@ -35,9 +35,9 @@
<view class="text-32rpx leading-44rpx text-#303133">其他要求</view>
<view class="text-24rpx text-#909399">可以多选</view>
</view>
<wd-checkbox-group v-model="form.other_requirements" shape="square" checked-color="#4C9F44">
<wd-checkbox-group v-model="otherRequirementsModel" shape="square" checked-color="#4C9F44">
<view class="flex flex-wrap">
<view class="w-1/2 mb-30rpx"><wd-checkbox modelValue="需要金牌茶艺师">需要金牌茶艺师</wd-checkbox></view>
<view class="w-1/2 mb-30rpx"><wd-checkbox modelValue="需要云华茶艺师">需要云华茶艺师</wd-checkbox></view>
<view class="w-1/2 mb-30rpx"><wd-checkbox modelValue="需要茶艺师导师">需要茶艺师导师</wd-checkbox></view>
<view class="w-1/2"><wd-checkbox modelValue="需要茶技">需要茶技</wd-checkbox></view>
</view>
@ -50,9 +50,9 @@
<view class="text-32rpx leading-44rpx text-#303133">
预定日期 <text class="text-[#FF3B30] ml-10rpx">*</text>
</view>
<wd-datetime-picker v-model="form.reserve_date" type="date" :min-date="minDate" :max-date="maxDate" use-default-slot @confirm="TeaRoom.handleConfirmDate">
<wd-datetime-picker v-model="form.res_time" type="date" :min-date="minDate" :max-date="maxDate" use-default-slot @confirm="TeaRoom.handleConfirmDate">
<view class="flex items-center">
<view class="text-30rpx mr-8rpx" :class="form.reserve_date ? 'text-#303133' : 'text-#C9C9C9'">
<view class="text-30rpx mr-8rpx" :class="form.res_time ? 'text-#303133' : 'text-#C9C9C9'">
{{ reserveDateStr || '请选择' }}
</view>
<wd-icon name="arrow-right" size="32rpx" color="#C9C9C9"></wd-icon>
@ -73,14 +73,14 @@
<view>
<view class="text-26rpx leading-32rpx text-#606266">联系人</view>
<view class="mt-20rpx border-b border-b-solid border-b-[#F6F7F8] pb-20rpx">
<wd-input no-border placeholder="请填写联系人" v-model="form.username"></wd-input>
<wd-input no-border placeholder="请填写联系人" v-model="form.contact"></wd-input>
</view>
</view>
<view class="mt-22rpx">
<view class="text-26rpx leading-32rpx text-#606266">手机号</view>
<view class="mt-20rpx border-b border-b-solid border-b-[#F6F7F8] pb-20rpx">
<wd-input no-border placeholder="请填写手机号" v-model="form.mobile"></wd-input>
<wd-input no-border placeholder="请填写手机号" v-model="form.phone" inputmode="numeric"></wd-input>
</view>
</view>
@ -140,7 +140,7 @@
订单信息
</view>
<view class="">
<wd-textarea placeholder="有想说的可以在这里写哦!" v-model="form.suggest"
<wd-textarea placeholder="有想说的可以在这里写哦!" v-model="form.content"
custom-class='!rounded-18rpx !border-2rpx !border-[#EFF0EF] !bg-[#F8F9FA]'
custom-textarea-class='!bg-[#F8F9FA]' />
</view>
@ -159,9 +159,11 @@ import { useColPickerData } from '@/hooks/useColPickerData'
import { router, removeImageUrlPrefix } from '@/utils/tools'
import { SMS_ENUM } from '@/enum/sms'
import { mobile } from '@/utils/test'
import { getVerificationCode, addJoinCooperation, getJoinCooperationStatus } from '@/api/user'
import { reserveGroupTeaSpecialist } from '@/api/tea-specialist'
import { getVerificationCode, getJoinCooperationStatus } from '@/api/user'
import { getOpenCityList } from '@/api/tea-room'
import { useToast } from 'wot-design-uni'
import { OrderType } from '@/utils/order'
const OSS = inject('OSS')
const token = ref<string>('') // 用户token
@ -175,49 +177,61 @@ const isSuccess = ref<boolean>(false)
const openCityList = ref<Array<any>>([])
const city = ref<string>('')
// 上传文件
const fileList = ref<any[]>([])
const action = import.meta.env.VITE_UPLOAD_BASEURL // 仅做测试使用,实际请换成真实上传接口
// 验证码倒计时
const countDownTime = ref<number>(1 * 60 * 1000) // 60s倒计时
const startCountDown = ref<boolean>(false) // 是否开始倒计时
const countDown = ref<any>(null) // 倒计时组件
// 表单
const form = ref<{
operation_type: number // 加盟类型
license_img: string // 营业执照
username: string // 联系人姓名
mobile: string // 联系电话
code: string // 验证码
city_id: number // 城市ID
address: string // 详细地址
suggest: string // 意见建议
other_requirements: string[] // 其他要求
reserve_date: number | string // 预定日期
const form = reactive<{
numbers: string
other_requirements: string | string[]
res_time: string | number
contact: string
phone: string
code: string
city_id: string
address: string
content: string
license_img: string
}>({
operation_type: 1,
license_img: '',
username: '',
mobile: '',
code: '',
city_id: 0,
address: '',
suggest: '',
other_requirements: [],
reserve_date: Date.now()
numbers: '', // 预约人数
other_requirements: '', // 其他要求
res_time: '', // 预定日期
contact: '', // 联系人
phone: '', // 联系电话
code: '', // 验证码
city_id: '', // 城市id
address: '', // 详细地址
content: '', // 留言建议
license_img: '' // 证照图片
})
// 审核状态
const status = ref<number>(-1) // 0待审核 1审核成功 2审核失败 3未提交数据
const formatDateToYMD = (value: string | number) => {
if (value === '' || value === null || value === undefined) return ''
const input = typeof value === 'string' && /^\d+$/.test(value) ? Number(value) : value
const d = new Date(input)
if (Number.isNaN(d.getTime())) return ''
return `${d.getFullYear()}-${d.getMonth() + 1}-${d.getDate()}`
}
// 预定日期格式化 M月D日
const reserveDateStr = computed(() => {
if (!form.value.reserve_date) return ''
const d = new Date(form.value.reserve_date)
return `${d.getFullYear()}年${d.getMonth() + 1}月${d.getDate()}日`
return formatDateToYMD(form.res_time)
})
const otherRequirementsModel = computed<string[]>({
get: () => Array.isArray(form.other_requirements)
? form.other_requirements
: (form.other_requirements ? [form.other_requirements] : []),
set: (val) => {
form.other_requirements = val
}
})
const minDate = ref<number>(new Date(new Date().setHours(0, 0, 0, 0)).getTime())
const maxDate = ref<number>(new Date(new Date().getFullYear(), 11, 31, 23, 59, 59).getTime())
@ -242,12 +256,12 @@ const TeaRoom = {
* 发送验证码
*/
handleCountDown: async () => {
if (!mobile(form.value.mobile)) {
if (!mobile(form.phone)) {
toast.show('请检查手机号码')
return false
}
await getVerificationCode({ scene: SMS_ENUM.BANK, mobile: String(form.value.mobile) })
await getVerificationCode({ scene: SMS_ENUM.BANK, mobile: String(form.phone) })
startCountDown.value = true
setTimeout(() => {
@ -273,39 +287,60 @@ const TeaRoom = {
* 确认选择预定日期
*/
handleConfirmDate: (e: any) => {
form.value.reserve_date = e.value
form.res_time = e.value
},
/**
* 提交申请
*/
handleSubmit: async () => {
if (!form.value.username) {
if (!form.contact) {
toast.show('请输入联系人')
return false
}
if (!mobile(form.value.mobile)) {
if (!form.res_time) {
toast.show('请选择预定日期')
return false
}
if (!mobile(form.phone)) {
toast.show('请检查手机号码')
return false
}
if (!form.value.code) {
if (!form.code) {
toast.show('请输入验证码')
return false
}
if (!form.value.city_id) {
if (!form.city_id) {
toast.show('请选择地区')
return false
}
if (!form.value.address) {
if (!form.address) {
toast.show('请输入详细地址')
return false
}
form.value.license_img = fileList.value.length > 0 ? removeImageUrlPrefix(fileList.value)[0] : ''
const submitResTime = reserveDateStr.value
const submitOtherRequirements = Array.isArray(form.other_requirements)
? form.other_requirements.join(',')
: form.other_requirements
const payload = {
numbers: form.numbers,
other_requirements: submitOtherRequirements,
res_time: submitResTime,
contact: form.contact,
phone: form.phone,
code: form.code,
city_id: Number(form.city_id),
address: form.address,
content: form.content
}
toast.loading({
loadingType: 'ring',
@ -313,17 +348,13 @@ const TeaRoom = {
msg: '提交中...'
})
try {
await reserveGroupTeaSpecialist(payload as any)
toast.close()
await addJoinCooperation(form.value)
status.value = 0 // 提交成功,变更状态为待审核
setTimeout(() => {
toast.success('提交成功')
}, 100)
router.redirectTo(`/pages/notice/reserve?type=${OrderType.ReserveGroup}`)
} catch (e) {
toast.close()
return
return false
}
}
}

View File

@ -52,10 +52,10 @@
<view class="flex items-center">
<wd-radio-group v-model="form.operation_type" size="large" shape="dot" checked-color="#4C9F44" inline>
<wd-radio :value="1" border>
<view class="leading-36rpx font-400 text-30rpx ml-12rpx mr-60rpx">加盟</view>
<view class="leading-36rpx font-400 text-30rpx ml-12rpx">直营</view>
</wd-radio>
<wd-radio :value="2" border>
<view class="leading-36rpx font-400 text-30rpx ml-12rpx">入驻</view>
<view class="leading-36rpx font-400 text-30rpx ml-12rpx mr-60rpx">加盟</view>
</wd-radio>
</wd-radio-group>
</view>

View File

@ -1,16 +1,14 @@
<route lang="jsonc" type="page">
{
<route lang="jsonc" type="page">{
"needLogin": true,
"layout": "default",
"style": {
"navigationStyle": "custom"
}
}
</route>
}</route>
<template>
<view class="">
<view class="">
<view>
<view>
<wd-navbar safeAreaInsetTop :bordered="false" custom-style="background-color: #F6F7F8 !important;">
<template #left>
<view class="flex items-center" @click="router.switchTab('/pages/my/my')">
@ -28,7 +26,7 @@
<!-- 待审核 -->
<view class="mt-84rpx" v-if="status === 0">
<view class="flex justify-center items-center">
<wd-img width="300rpx" height="278rpx" :src="`${OSS}images/settle_in/settle_in_image2.png`"/>
<wd-img width="300rpx" height="278rpx" :src="`${OSS}images/settle_in/settle_in_image2.png`" />
</view>
<view class="mt-60rpx text-center">
<view class="text-36rpx leading-50rpx text-#303133">您的信息已成功提交</view>
@ -39,36 +37,41 @@
<!-- 申请成功提示 -->
<view class="mt-84rpx" v-if="status === 1">
<view class="flex justify-center items-center">
<wd-img width="300rpx" height="278rpx" :src="`${OSS}images/settle_in/settle_in_image1.png`"/>
<wd-img width="300rpx" height="278rpx" :src="`${OSS}images/settle_in/settle_in_image1.png`" />
</view>
<view class="mt-60rpx text-center">
<view class="text-36rpx leading-50rpx text-#303133">申请茶艺师成功</view>
<view class="font-400 text-26rpx leading-42rpx text-#9CA3AF mt-28rpx mx-102rpx">请登录【茶址茶艺师】小程序查看信息进行操作,如有疑问请联系平台</view>
<view class="font-400 text-26rpx leading-42rpx text-#9CA3AF mt-28rpx mx-102rpx">
请登录【茶址茶艺师】小程序查看信息进行操作,如有疑问请联系平台
</view>
</view>
</view>
<template v-if="status >= 2">
<view class="mx-30rpx mt-38rpx">
<!-- 营业执照 -->
<!-- <view class="bg-white rounded-16rpx px-30rpx py-28rpx mt-20rpx">
<view class="text-32rpx leading-44rpx text-#303133 mb-32rpx">营业执照</view>
<view class="">
<wd-upload
:header="{'token': token}"
v-model:file-list="fileList"
:limit="1"
image-mode="scaleToFill"
:action="action">
<view class="border-2rpx border-dashed border-[#E5E5E5] w-184rpx h-184rpx flex flex-col items-center justify-center rounded-16rpx">
<!-- 照片信息 -->
<view class="bg-white rounded-16rpx px-30rpx py-28rpx mt-20rpx">
<view class="flex items-center">
<view class="text-32rpx leading-44rpx text-#303133 mr-10rpx">照片信息</view>
<view class="flex items-center">
<wd-img width="16rpx" height="16rpx" :src="`${OSS}icon/icon_validate.png`"></wd-img>
</view>
</view>
<view class="mt-32rpx">
<wd-upload :header="{ 'token': token }" v-model:file-list="fileList2" :limit="1"
image-mode="scaleToFill" :action="action">
<view
class="border-2rpx border-dashed border-[#E5E5E5] w-184rpx h-184rpx flex flex-col items-center justify-center rounded-16rpx">
<view class="">
<wd-img width="64rpx" height="64rpx" :src="`${OSS}icon/icon_upload.png`" mode="aspectFill" />
<wd-img width="64rpx" height="64rpx" :src="`${OSS}icon/icon_upload.png`"
mode="aspectFill" />
</view>
<view class="font-400 text-26rpx leading-36rpx text-#303133">营业执照</view>
<view class="font-400 text-26rpx leading-36rpx text-#303133">本人头像</view>
</view>
</wd-upload>
</view>
</view> -->
</view>
<!-- 基本信息 -->
<view class="bg-white rounded-16rpx px-30rpx pt-28rpx mt-20rpx">
<view class="text-32rpx leading-44rpx text-#303133 mb-32rpx flex items-center">
@ -87,19 +90,25 @@
<view class="mt-22rpx">
<view class="text-26rpx leading-32rpx text-#606266">性别</view>
<view class="mt-20rpx border-b border-b-solid border-b-[#F6F7F8] pb-20rpx tea-specialist-cell">
<wd-picker placeholder="请选择性别" :columns="genderColumns" v-model="form.gender" @confirm="TeaSpecialist.handleConfirmGender" />
<view
class="mt-20rpx border-b border-b-solid border-b-[#F6F7F8] pb-20rpx tea-specialist-cell">
<wd-picker placeholder="请选择性别" :columns="genderColumns" v-model="form.gender"
@confirm="TeaSpecialist.handleConfirmGender" />
</view>
</view>
<view class="mt-22rpx">
<view class="text-26rpx leading-32rpx text-#606266">出生年月</view>
<view class="mt-20rpx border-b border-b-solid border-b-[#F6F7F8] pb-20rpx tea-specialist-cell">
<wd-datetime-picker placeholder="请选择出生年月" v-model="form.both" :minDate="new Date(1970, 0, 1).getTime()" :maxDate="new Date().getTime()" type="date" @confirm="TeaSpecialist.handleConfirmBoth"></wd-datetime-picker>
<view
class="mt-20rpx border-b border-b-solid border-b-[#F6F7F8] pb-20rpx tea-specialist-cell">
<wd-datetime-picker placeholder="请选择出生年月" v-model="form.both"
:minDate="new Date(1970, 0, 1).getTime()" :maxDate="new Date().getTime()"
type="date" @confirm="TeaSpecialist.handleConfirmBoth"></wd-datetime-picker>
</view>
</view>
<view class="mt-22rpx flex items-center justify-between border-b border-b-solid border-b-[#F6F7F8] pb-20rpx">
<view
class="mt-22rpx flex items-center justify-between border-b border-b-solid border-b-[#F6F7F8] pb-20rpx">
<view class="mr-166rpx">
<view class="text-26rpx leading-32rpx text-#606266">身高</view>
<view class="mt-20rpx flex items-center">
@ -122,19 +131,17 @@
<!-- 专属圈子 -->
<view class="bg-white rounded-16rpx px-30rpx py-32rpx mt-20rpx">
<view class="text-32rpx leading-44rpx text-#303133 mb-32rpx flex items-center">
专属圈子 <text class="text-[#818CA9] ml-10rpx text-20rpx mt-4rpx">最多可选6个</text>
专属圈子
<view class="flex items-center">
<wd-img width="16rpx" height="16rpx" :src="`${OSS}icon/icon_validate.png`"></wd-img>
</view>
<text class="text-[#818CA9] ml-10rpx text-20rpx mt-4rpx">最多可选6个</text>
</view>
<view>
<view class="flex items-center flex-wrap">
<view
v-for="(item, idx) in tagList"
:key="item.id"
class="tea-tag"
:style="selectedTags.includes(item.id) ?'background:#4C9F44;color:#fff;' : 'background:#F3F3F3;color:#818CA9;'
"
@click="handleTagClick(item.id)"
>
{{ item.title }}
<view v-for="item in exclusiveCirclesLabel" :key="item.id" class="tea-tag" :style="selectedTags.includes(item.id) ? 'background:#4C9F44;color:#fff;' : 'background:#F3F3F3;color:#818CA9;'
" @click="TeaSpecialist.handleTagClick(item.id)">
{{ item.label_name }}
</view>
</view>
</view>
@ -149,9 +156,11 @@
</view>
</view>
<view class="flex-1 ml-80rpx">
<wd-picker :columns="openCityList" label-key="name" value-key="id" v-model="form.city_id" use-default-slot @confirm="TeaSpecialist.handleConfirmAddress">
<wd-picker :columns="openCityList" label-key="name" value-key="id" v-model="form.city_id"
use-default-slot @confirm="TeaSpecialist.handleConfirmAddress">
<view class="flex items-center">
<wd-input readonly v-model="city" size="large" placeholder="请选择地区" no-border placeholderStyle="font-size: 30rpx; line-height: 42rpx; color: #c9c9c9;"></wd-input>
<wd-input readonly v-model="city" size="large" placeholder="请选择地区" no-border
placeholderStyle="font-size: 30rpx; line-height: 42rpx; color: #c9c9c9;"></wd-input>
<wd-icon name="chevron-right" size="32rpx" color="#909399"></wd-icon>
</view>
</wd-picker>
@ -159,75 +168,113 @@
</view>
</view>
<!-- 照片信息 -->
<!-- 身份证 -->
<view class="bg-white rounded-16rpx px-30rpx py-28rpx mt-20rpx">
<view class="flex items-center">
<view class="text-32rpx leading-44rpx text-#303133 mr-10rpx">照片信息</view>
<view class="text-32rpx leading-44rpx text-#303133 mr-10rpx">身份证</view>
<view class="flex items-center">
<wd-img width="16rpx" height="16rpx" :src="`${OSS}icon/icon_validate.png`"></wd-img>
</view>
</view>
<view class="mt-32rpx">
<view class="mt-32rpx flex items-center gap-16rpx">
<wd-upload
:header="{'token': token}"
v-model:file-list="fileList2"
v-model:file-list="frontCard"
:limit="1"
image-mode="scaleToFill"
:action="action">
<view class="border-2rpx border-dashed border-[#E5E5E5] w-184rpx h-184rpx flex flex-col items-center justify-center rounded-16rpx">
<view class="">
<view>
<wd-img width="64rpx" height="64rpx" :src="`${OSS}icon/icon_upload.png`" mode="aspectFill" />
</view>
<view class="font-400 text-26rpx leading-36rpx text-#303133">本人照片</view>
<view class="font-400 text-26rpx leading-36rpx text-#303133">身份证正面</view>
</view>
</wd-upload>
<wd-upload
:header="{'token': token}"
v-model:file-list="backCard"
:limit="1"
image-mode="scaleToFill"
:action="action">
<view class="border-2rpx border-dashed border-[#E5E5E5] w-184rpx h-184rpx flex flex-col items-center justify-center rounded-16rpx">
<view>
<wd-img width="64rpx" height="64rpx" :src="`${OSS}icon/icon_upload.png`" mode="aspectFill" />
</view>
<view class="font-400 text-26rpx leading-36rpx text-#303133">身份证反面</view>
</view>
</wd-upload>
</view>
</view>
<!-- 健康证、资格证书信息 -->
<!-- 茶艺师资格证书 -->
<view class="bg-white rounded-16rpx px-30rpx py-28rpx mt-20rpx">
<view class="flex items-center">
<view class="text-32rpx leading-44rpx text-#303133 mr-10rpx">健康证、资格证书信息</view>
<!-- <view class="flex items-center">
<view class="text-32rpx leading-44rpx text-#303133 mr-10rpx">茶艺师资格证书</view>
<view class="flex items-center">
<wd-img width="16rpx" height="16rpx" :src="`${OSS}icon/icon_validate.png`"></wd-img>
</view> -->
</view>
</view>
<view class="mt-32rpx flex items-center">
<wd-upload
:header="{'token': token}"
v-model:file-list="fileList"
:limit="1"
image-mode="scaleToFill"
:action="action">
<view class="border-2rpx border-dashed border-[#E5E5E5] w-184rpx h-184rpx flex flex-col items-center justify-center rounded-16rpx">
<view>
<wd-img width="64rpx" height="64rpx" :src="`${OSS}icon/icon_upload.png`" mode="aspectFill" />
</view>
<view class="font-400 text-26rpx leading-36rpx text-#303133">资格证书</view>
</view>
</wd-upload>
</view>
</view>
<!-- 个人照片 -->
<view class="bg-white rounded-16rpx px-30rpx py-28rpx mt-20rpx">
<view class="flex items-center">
<view class="text-32rpx leading-44rpx text-#303133 mr-10rpx">个人照片</view>
</view>
<view class="mt-32rpx">
<wd-upload :header="{ 'token': token }" v-model:file-list="fileList4" :limit="9" multiple
image-mode="scaleToFill" :action="action">
<view
class="border-2rpx border-dashed border-[#E5E5E5] w-184rpx h-184rpx flex flex-col items-center justify-center rounded-16rpx">
<view class="">
<wd-img width="64rpx" height="64rpx" :src="`${OSS}icon/icon_upload.png`"
mode="aspectFill" />
</view>
<view class="flex items-center">
<view class="font-400 text-26rpx leading-36rpx text-#303133">个人照片</view>
</view>
</view>
</wd-upload>
</view>
</view>
<!-- 健康证书信息 -->
<view class="bg-white rounded-16rpx px-30rpx py-28rpx mt-20rpx">
<view class="flex items-center">
<view class="text-32rpx leading-44rpx text-#303133 mr-10rpx">健康证</view>
</view>
<view class="mt-32rpx flex items-center">
<view class="mr-16rpx">
<wd-upload
:header="{'token': token}"
v-model:file-list="fileList3"
:limit="1"
image-mode="scaleToFill"
:action="action">
<view class="border-2rpx border-dashed border-[#E5E5E5] w-184rpx h-184rpx flex flex-col items-center justify-center rounded-16rpx">
<wd-upload :header="{ 'token': token }" v-model:file-list="fileList3" :limit="1"
image-mode="scaleToFill" :action="action">
<view
class="border-2rpx border-dashed border-[#E5E5E5] w-184rpx h-184rpx flex flex-col items-center justify-center rounded-16rpx">
<view class="">
<wd-img width="64rpx" height="64rpx" :src="`${OSS}icon/icon_upload.png`" mode="aspectFill" />
<wd-img width="64rpx" height="64rpx" :src="`${OSS}icon/icon_upload.png`"
mode="aspectFill" />
</view>
<view class="font-400 text-26rpx leading-36rpx text-#303133">健康证</view>
</view>
</wd-upload>
</view>
<view>
<wd-upload
:header="{'token': token}"
v-model:file-list="fileList4"
:limit="1"
image-mode="scaleToFill"
:action="action">
<view class="border-2rpx border-dashed border-[#E5E5E5] w-184rpx h-184rpx flex flex-col items-center justify-center rounded-16rpx">
<view class="">
<wd-img width="64rpx" height="64rpx" :src="`${OSS}icon/icon_upload.png`" mode="aspectFill" />
</view>
<view class="flex items-center">
<view class="font-400 text-26rpx leading-36rpx text-#303133">茶艺师资格证</view>
<view class="flex items-center">
<wd-img width="16rpx" height="16rpx" :src="`${OSS}icon/icon_validate.png`"></wd-img>
</view>
</view>
</view>
</wd-upload>
</view>
</view>
</view>
@ -250,17 +297,25 @@
<view class="mt-22rpx">
<view class="text-26rpx leading-32rpx text-#606266">验证码</view>
<view class="mt-20rpx pb-20rpx">
<wd-input type="text" placeholder="请填写手机验证码" v-model="form.code" inputmode="numeric" no-border>
<template #suffix>
<wd-input type="text" placeholder="请填写手机验证码" v-model="form.code" inputmode="numeric"
no-border>
<template #suffix>
<view class="flex items-center mr-34rpx">
<view class="flex items-center">
<view class="text-[#4C9F44] text-28rpx font-400 leading-40rpx" v-if="!startCountDown" @click="TeaSpecialist.handleCountDown">发送验证码</view>
<view class="!text-[#C9C9C9] text-28rpx font-400 leading-40rpx flex items-center" v-if="startCountDown">
<wd-count-down ref="countDown" :time="countDownTime" millisecond :auto-start="false" format="ss" custom-class="!text-[#C9C9C9] !text-32rpx" @finish="TeaSpecialist.handleFinishCountDown"></wd-count-down>
<view class="text-[#4C9F44] text-28rpx font-400 leading-40rpx"
v-if="!startCountDown" @click="TeaSpecialist.handleCountDown">发送验证码
</view>
<view
class="!text-[#C9C9C9] text-28rpx font-400 leading-40rpx flex items-center"
v-if="startCountDown">
<wd-count-down ref="countDown" :time="countDownTime" millisecond
:auto-start="false" format="ss"
custom-class="!text-[#C9C9C9] !text-32rpx"
@finish="TeaSpecialist.handleFinishCountDown"></wd-count-down>
<view> S后重发</view>
</view>
</view>
</view>
</view>
</template>
</wd-input>
</view>
@ -272,9 +327,12 @@
*提交表单申请,工作人员将在3个工作日内与您电话联系,如需及时了解,请直接电话咨询,我们将在第一时间解答
</view>
<view class="text-32rpx leading-44rpx flex items-center justify-center leading-90rpx text-center text-[#303133] mt-112rpx pb-66rpx">
<view class="w-330rpx h-90rpx bg-[#FFFFFF] rounded-8rpx mr-30rpx" @click="router.navigateTo('/bundle/contact/contact')">联系我们</view>
<view class="w-330rpx h-90rpx bg-[#4C9F44] rounded-8rpx text-#fff" @click="TeaSpecialist.handleSubmit">提交申请</view>
<view
class="text-32rpx leading-44rpx flex items-center justify-center leading-90rpx text-center text-[#303133] mt-112rpx pb-66rpx">
<view class="w-330rpx h-90rpx bg-[#FFFFFF] rounded-8rpx mr-30rpx"
@click="router.navigateTo('/bundle/contact/contact')">联系我们</view>
<view class="w-330rpx h-90rpx bg-[#4C9F44] rounded-8rpx text-#fff" @click="TeaSpecialist.handleSubmit">
提交申请</view>
</view>
</template>
@ -282,55 +340,125 @@
</template>
<script lang="ts" setup>
import { router, removeImageUrlPrefix, strToParams } from '@/utils/tools'
import { getOpenCityList } from '@/api/tea-room'
import { useToast } from 'wot-design-uni'
import { getVerificationCode, addTeaSpecialist, getTeaSpecialistStatus } from '@/api/user'
import { SMS_ENUM } from '@/enum/sms'
import { mobile } from '@/utils/test'
import { router, removeImageUrlPrefix, strToParams } from '@/utils/tools'
import { getOpenCityList } from '@/api/tea-room'
import { useToast } from 'wot-design-uni'
import { getVerificationCode, addTeaSpecialist, getTeaSpecialistStatus } from '@/api/user'
import { getExclusiveCirclesLabel } from '@/api/tea-specialist'
import { SMS_ENUM } from '@/enum/sms'
import { mobile } from '@/utils/test'
const OSS = inject('OSS')
const token = ref<string>('') // 用户token
const toast = useToast()
const OSS = inject('OSS')
const token = ref<string>('') // 用户token
const toast = useToast()
// 已开通城市列表
const openCityList = ref<Array<any>>([])
const city = ref<string>('')
// 已开通城市列表
const openCityList = ref<Array<any>>([])
const city = ref<string>('')
// 审核提示
const tips = ref<boolean>(false)
// 性别
const genderColumns = ['女', '男']
// 性别
const genderColumns = ['女', '男']
// 上传文件
const fileList = ref<any[]>([]) // 资格证
const fileList2 = ref<any[]>([]) // 照片信息
const fileList3 = ref<any[]>([]) // 个人展示
const fileList4 = ref<any[]>([]) // 健康证
const frontCard = ref<any[]>([]) // 身份证正面
const backCard = ref<any[]>([]) // 身份证反面
const action = import.meta.env.VITE_UPLOAD_BASEURL
// 上传文件
const fileList = ref<any[]>([]) // 营业执照
const fileList2 = ref<any[]>([]) // 照片信息
const fileList3 = ref<any[]>([]) // 健康证
const fileList4 = ref<any[]>([]) // 资格证
const action = import.meta.env.VITE_UPLOAD_BASEURL
// 验证码倒计时
const countDownTime = ref<number>(1 * 60 * 1000) // 60s倒计时
const startCountDown = ref<boolean>(false) // 是否开始倒计时
const countDown = ref<any>(null) // 倒计时组件
// 验证码倒计时
const countDownTime = ref<number>(1 * 60 * 1000) // 60s倒计时
const startCountDown = ref<boolean>(false) // 是否开始倒计时
const countDown = ref<any>(null) // 倒计时组件
// 专属圈子tag颜色
const tagList = [
{title: '茶艺表演', id: 1},
{title: '茶艺培训', id: 2},
{title: '茶文化推广', id: 3},
{title: '茶艺比赛', id: 4},
{title: '茶艺研究', id: 5},
{title: '其他', id: 6},
{title: '关单', id: 7},
]
// 表单
const form = ref<{
license_img: string // 营业执照
username: string // 茶艺师名字
gender: number // 茶艺师性别(1男 2女)
both: number | string // 出生年月
height: string // 身高
weight: string // 体重
city_id: number // 城市ID
avatar: string // 头像
mobile: string // 联系电话
code: string // 验证码
health_certificate?: string // 健康证
information?: string // 个人展示
image?: string // 个人展示
front_card?: string // 身份证正面
back_card?: string // 身份证反面
broker_user_id?: number // 经纪人ID(可选,扫码绑定经纪人时携带)
label_id?: string // 专属圈子标签ID,逗号分隔
}>({
license_img: '', // 营业执照
username: '', // 茶艺师名字
gender: 2, // 茶艺师性别(1男 2女)
both: '', // 出生年月
height: '', // 身高
weight: '', // 体重
city_id: 0, // 城市ID
avatar: '', // 头像
mobile: '', // 联系电话
code: '', // 验证码
health_certificate: '', // 健康证
information: '', // 个人展示(2026-04-07暂时废弃)
image: '', // 本人照片
front_card: '', // 身份证正面
back_card: '', // 身份证反面
broker_user_id: 0, // 经纪人ID(可选,扫码绑定经纪人时携带)
label_id: '' // 专属圈子标签ID,逗号分隔
})
const both = ref<string>('1998-12-21')
// 选中的标签id数组
const selectedTags = ref<number[]>([])
// 审核状态
const status = ref<number>(3) // 0待审核 1审核成功 2审核失败 3未提交数据
// 专属圈子标签
const selectedTags = ref<number[]>([])
const exclusiveCirclesLabel = ref<Array<any>>([])
onLoad(async (args) => {
// 默认出生年月为1998年12月21日
form.value.both = new Date(1998, 11, 21).getTime()
// 处理扫码携带的参数(茶艺师绑定经纪人)
const scene = args?.scene
if (scene) {
form.value.broker_user_id = strToParams(decodeURIComponent(args.scene))?.brokerid
}
TeaSpecialist.handleInit()
})
onShow(async () => {
token.value = uni.getStorageSync('token')
if (token.value) {
const res = await getTeaSpecialistStatus()
status.value = res.status
} else {
console.log("🚀 ~ token.value:", token.value)
router.navigateTo('/pages/login/login')
}
})
const TeaSpecialist = {
/**
* 初始化
*/
handleInit: async () => {
const res = await getOpenCityList()
openCityList.value = res.list
const label = await getExclusiveCirclesLabel()
exclusiveCirclesLabel.value = label
},
// 标签点击事件
function handleTagClick(id: number) {
handleTagClick(id: number) {
const idx = selectedTags.value.indexOf(id)
if (idx > -1) {
selectedTags.value.splice(idx, 1)
@ -341,230 +469,193 @@
toast.show('最多只能选择6个圈子')
}
}
}
},
// 表单
const form = ref<{
license_img: string // 营业执照
username: string // 茶艺师名字
gender: number // 茶艺师性别(1男 2女)
both: number | string // 出生年月
height: string // 身高
weight: string // 体重
hobby_introduce: string // 兴趣爱好介绍
city_id: number // 城市ID
avatar: string // 头像
certificate: string // 健康证、资格证书信息
mobile: string // 联系电话
code: string // 验证码
broker_id?: number // 经纪人ID(可选,扫码绑定经纪人时携带)
}>({
license_img: '',
username: '',
gender: 2,
both: '',
height: '',
weight: '',
hobby_introduce: '',
city_id: 0,
avatar: '',
certificate: '',
mobile: '',
code: '',
broker_id: 0 // 经纪人ID(可选,扫码绑定经纪人时携带)
})
const both = ref<string>('1998-12-21')
// 选择性别
handleConfirmGender: (gender: string) => {
console.log("🚀 ~ gender:", gender)
},
// 审核状态
const status = ref<number>(3) // 0待审核 1审核成功 2审核失败 3未提交数据
onLoad(async (args) => {
// 默认出生年月为1998年12月21日
form.value.both = new Date(1998, 11, 21).getTime()
// 处理扫码携带的参数(茶艺师绑定经纪人)
const scene = args?.scene
if (scene) {
form.value.broker_id = strToParams(decodeURIComponent(args.scene))?.brokerid
// 发送验证码
handleCountDown: async () => {
if (!mobile(form.value.mobile)) {
toast.show('请检查手机号码')
return false
}
TeaSpecialist.handleInit()
})
await getVerificationCode({ scene: SMS_ENUM.BANK, mobile: String(form.value.mobile) })
onShow(async () => {
token.value = uni.getStorageSync('token')
if (token.value) {
const res = await getTeaSpecialistStatus()
// status.value = res.status
status.value = 3
startCountDown.value = true
setTimeout(() => {
countDown.value.start()
}, 100)
},
// 验证码倒计时结束
handleFinishCountDown: () => {
startCountDown.value = false
},
/**
* 确认选择地址
*/
handleConfirmAddress: (item) => {
city.value = item.selectedItems.name
},
/**
*
*/
handleConfirmBoth({ value }: any) {
const d = new Date(value)
const year = d.getFullYear()
const month = d.getMonth() + 1
const day = d.getDate()
const date = `${year}-${month < 10 ? '0' + month : month}-${day < 10 ? '0' + day : day}`
both.value = date
},
/**
* 提交申请
*/
handleSubmit: async () => {
if (fileList2.value.length == 0) {
toast.show('请上传本人头像')
return false
}
})
const TeaSpecialist = {
/**
* 初始化
*/
handleInit: async () => {
const res = await getOpenCityList()
openCityList.value = res.list
},
if (!form.value.username) {
toast.show('请输入姓名')
return false
}
if (!form.value.username) {
toast.show('请输入姓名')
return false
}
// 选择性别
handleConfirmGender: (gender: string) => {
console.log("🚀 ~ gender:", gender)
},
if (!form.value.both) {
toast.show('请选择出生年月')
return false
}
// 发送验证码
handleCountDown: async () => {
if (!mobile(form.value.mobile)) {
toast.show('请检查手机号码')
return false
}
if (!form.value.height) {
toast.show('请输入身高')
return false
}
await getVerificationCode({ scene: SMS_ENUM.BANK, mobile: String(form.value.mobile) })
if (!form.value.weight) {
toast.show('请输入体重')
return false
}
// 专属圈子校验
if (selectedTags.value.length === 0) {
toast.show('请至少选择一个专属圈子')
return false
}
if (selectedTags.value.length > 6) {
toast.show('最多只能选择6个专属圈子')
return false
}
// 身份证正面和反面校验
if (frontCard.value.length === 0) {
toast.show('请上传身份证正面照片')
return false
}
if (!form.value.city_id) {
toast.show('请选择服务区域')
return false
}
if (backCard.value.length === 0) {
toast.show('请上传身份证反面照片')
return false
}
if (fileList.value.length == 0) {
toast.show('请上传茶艺师资格证书')
return false
}
// if (fileList4.value.length == 0) {
// toast.show('请至少上传一张个人展示照片')
// return false
// }
if (!mobile(form.value.mobile)) {
toast.show('请检查手机号码')
return false
}
if (!form.value.code) {
toast.show('请输入验证码')
return false
}
form.value.label_id = selectedTags.value.join(',') // 专属圈子标签ID,逗号分隔
form.value.license_img = fileList.value.length > 0 ? removeImageUrlPrefix(fileList.value)[0] : '' // 茶艺师资格证书
form.value.avatar = fileList2.value.length > 0 ? removeImageUrlPrefix(fileList2.value)[0] : '' // 个人照片
form.value.health_certificate = fileList3.value.length > 0 ? removeImageUrlPrefix(fileList3.value)[0] : '' // 健康证
form.value.image = fileList4.value.length > 0 ? removeImageUrlPrefix(fileList4.value) : '' // 个人展示
form.value.image = Array.isArray(form.value.image) ? form.value.image.join(',') : form.value.image
form.value.front_card = frontCard.value.length > 0 ? removeImageUrlPrefix(frontCard.value)[0] : '' // 身份证正面
form.value.back_card = backCard.value.length > 0 ? removeImageUrlPrefix(backCard.value)[0] : '' // 身份证反面
let data = form.value
data.both = both.value
toast.loading({
loadingType: 'ring',
loadingColor: '#4C9F44',
msg: '提交中...'
})
try {
toast.close()
await addTeaSpecialist(data)
status.value = 0 // 提交成功,变更状态为待审核
startCountDown.value = true
setTimeout(() => {
countDown.value.start()
toast.success('提交成功')
}, 100)
},
// 验证码倒计时结束
handleFinishCountDown: () => {
startCountDown.value = false
},
/**
* 确认选择地址
*/
handleConfirmAddress: (item) => {
city.value = item.selectedItems.name
},
/**
*
*/
handleConfirmBoth({ value }: any) {
const d = new Date(value)
const year = d.getFullYear()
const month = d.getMonth() + 1
const day = d.getDate()
const date = `${year}-${month < 10 ? '0' + month : month}-${day < 10 ? '0' + day : day}`
both.value = date
},
/**
* 提交申请
*/
handleSubmit: async () => {
if (!form.value.username) {
toast.show('请输入姓名')
return false
}
if (!form.value.both) {
toast.show('请选择出生年月')
return false
}
if (!form.value.height) {
toast.show('请输入身高')
return false
}
if (!form.value.weight) {
toast.show('请输入体重')
return false
}
if (!form.value.city_id) {
toast.show('请选择服务区域')
return false
}
if (fileList2.value.length == 0) {
toast.show('请上传本人照片')
return false
}
if (fileList4.value.length == 0) {
toast.show('请上传茶艺师资格证书')
return false
}
if (!mobile(form.value.mobile)) {
toast.show('请检查手机号码')
return false
}
if (!form.value.code) {
toast.show('请输入验证码')
return false
}
form.value.license_img = fileList.value.length > 0 ? removeImageUrlPrefix(fileList.value)[0] : ''
form.value.avatar = fileList2.value.length > 0 ? removeImageUrlPrefix(fileList2.value)[0] : ''
if (fileList3.value.length > 0 && fileList4.value.length > 0) {
form.value.certificate = removeImageUrlPrefix(fileList3.value)[0] + ',' + removeImageUrlPrefix(fileList4.value)[0]
} else if (fileList3.value.length > 0) {
form.value.certificate = removeImageUrlPrefix(fileList3.value)[0] + "," + ""
} else if (fileList4.value.length > 0) {
form.value.certificate = "" + "," +removeImageUrlPrefix(fileList4.value)[0]
}
let data = form.value
data.both = both.value
toast.loading({
loadingType: 'ring',
loadingColor: '#4C9F44',
msg: '提交中...'
})
try {
toast.close()
await addTeaSpecialist(data)
status.value = 0 // 提交成功,变更状态为待审核
setTimeout(() => {
toast.success('提交成功')
}, 100)
} catch(e) {
toast.close()
return
}
} catch (e) {
toast.close()
return
}
}
}
</script>
<style lang="scss">
page {
background: $cz-page-background;
}
page {
background: $cz-page-background;
}
.tea-specialist-cell {
:deep() {
.wd-cell {
padding-left: 0 !important;
}
.tea-specialist-cell {
:deep() {
.wd-cell {
padding-left: 0 !important;
}
.wd-cell__wrapper {
padding-top: 0 !important;
padding-bottom: 0 !important;
}
.wd-cell__wrapper {
padding-top: 0 !important;
padding-bottom: 0 !important;
}
}
}
.tea-tag {
padding: 0 8rpx;
border-radius: 4rpx;
margin-right: 16rpx;
margin-bottom: 16rpx;
font-size: 24rpx;
line-height: 44rpx;
cursor: pointer;
transition: all 0.2s;
display: inline-block;
}
.tea-tag {
padding: 0 8rpx;
border-radius: 4rpx;
margin-right: 16rpx;
margin-bottom: 16rpx;
font-size: 24rpx;
line-height: 44rpx;
cursor: pointer;
transition: all 0.2s;
display: inline-block;
}
</style>

View File

@ -27,24 +27,24 @@
const levelMap = {
gold: {
icon: `${OSS}icon/icon_gold_medal.png`,
text: '金牌茶艺师'
text: '云华茶艺师'
},
senior: {
icon: `${OSS}icon/icon_senior_medal.png`,
text: '高级茶艺师'
text: '竹影茶艺师'
},
intermediate: {
icon: `${OSS}icon/icon_intermediate_medal.png`,
text: '中级茶艺师'
text: '明月茶艺师'
},
junior: {
icon: `${OSS}icon/icon_junior_medal.png`,
text: '初级茶艺师'
text: '清风茶艺师'
},
enthusiast: {
icon: `${OSS}icon/icon_enthusiast_medal.png`,
text: '茶艺爱好者'
}
// enthusiast: {
// icon: `${OSS}icon/icon_enthusiast_medal.png`,
// text: '茶艺爱好者'
// }
}
</script>

View File

@ -0,0 +1,56 @@
<route lang="jsonc" type="page">
{
"needLogin": true,
"layout": "default",
"style": {
"navigationStyle": "custom"
}
}
</route>
<template>
<view>
<navbar title="资质证书" custom-class='!bg-[#fff]' :leftArrow="false"></navbar>
<view class="mt-40rpx mx-30rpx">
<view class="mb-20rpx">
<view class="font-400 text-36rpx text-[#303133] leading-50rpx">资格证</view>
<view class="w-624rpx h-420rpx mt-34rpx mx-auto">
<wd-img width="100%" height="100%" :src="info.license_img" @click="previewImage(info.license_img, [info.license_img])" v-if="info.license_img"></wd-img>
<view class="text-center font-400 text-32rpx text-[#606266]" v-if="!info.license_img">暂未上传</view>
</view>
</view>
<view>
<view class="font-400 text-36rpx text-[#303133] leading-50rpx">健康证</view>
<view class="w-624rpx h-420rpx mt-34rpx mx-auto">
<wd-img width="624rpx" height="420rpx" :src="info.health_certificate" @click="previewImage(info.health_certificate, [info.health_certificate])" v-if="info.health_certificate"></wd-img>
<view class="text-center font-400 text-32rpx text-[#606266]" v-if="!info.health_certificate">暂未上传</view>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { previewImage } from '@/utils/tools'
const info = ref({
license_img: '', // 营业执照图片
health_certificate: '', // 健康证图片
})
onLoad((args) => {
if (args.info) {
info.value = JSON.parse(decodeURIComponent(args.info))
console.log("🚀 ~ info.value:", info.value)
}
})
</script>
<style lang="scss" scoped>
page {
background-color: #fff;
}
</style>

View File

@ -73,8 +73,13 @@
// 可用优惠券数量
const useCoupon = ref<number>(0)
// 优惠券类型
const typeId = ref<number>(1) // 1茶艺师 2茶室 3个人中心 4套餐优惠券,这个页面是普通的茶艺师列表和茶艺师套餐列表共用的,所以需要区分
onLoad(async (args) => {
typeId.value = args.typeId || 1
teaRoomId.value = args.id
checkedId.value = args.couponId || 0
@ -87,15 +92,7 @@
* 初始化优惠券列表
*/
handleInitCoupon: async (price) => {
// const res = await getTeaSpecialistCoupons()
// const coupon = res.map( item => {
// return {
// ...item,
// user_coupon_id: item.id,
// }
// })
// couponList.value.use = coupon
const res = await getCoupons({type_id: 1, price}) // 1茶艺师 2茶室 3 个人中心
const res = await getCoupons({type_id: typeId.value, price}) // 1茶艺师 2茶室 3个人中心 4套餐优惠券
useCoupon.value = res.count.yes_use
couponList.value.use = res.result.filter(item => item.is_use == 0)
console.log("🚀 ~ couponList.value.use:", couponList.value.use)

View File

@ -114,9 +114,14 @@
<view class="flex items-center mt-24rpx">
<view class="flex items-center">
<wd-img :src="`${OSS}icon/icon_store_cert.png`" width="36rpx" height="36rpx"></wd-img>
<wd-img :src="`${OSS}icon/icon_store_cert2.png`" width="30rpx" height="30rpx"></wd-img>
</view>
<text class="ml-8rpx font-400 text-24rpx leading-4rpx text-#303133">商家认证</text>
<view class="ml-8rpx font-400 text-24rpx leading-4rpx text-#303133" @click="Detail.handlePreviewInfo">
{{ info.company_status ? '查看资质' : '暂无认证信息' }}
</view>
<!-- <view class="flex items-center mt-4rpx">
<wd-icon name="chevron-right" size="32rpx"></wd-icon>
</view> -->
</view>
<view class="bg-[#EBFBF7] w-692rpx h-64rpx rounded-12rpx p-14rpx flex items-center mt-22rpx text-[#35C1A0]">
@ -151,8 +156,7 @@
<view class="flex justify-between items-center mx-88rpx text-center mt-30rpx">
<view>
<view class="font-400 text-28rpx leading-40rpx text-#606266">性别</view>
<view class="font-bold text-30rpx leading-42rpx text-#303133 mt-12rpx">{{ info.sex == 1 ? '男' : '女'
}}
<view class="font-bold text-30rpx leading-42rpx text-#303133 mt-12rpx">{{ info.sex }}
</view>
</view>
<view class="w-4rpx h-66rpx bg-#F6F7F9"></view>
@ -186,16 +190,26 @@
<view class="font-400 text-26rpx leading-36rpx text-#606266">专属圈子</view>
</view>
<view class="flex flex-wrap items-center mt-16rpx">
<view class="mr-8rpx bg-[#4C9F44] text-[#fff] px-10rpx leading-34rpx font-400 text-24rpx rounded-4rpx">123</view>
<view class="mr-8rpx bg-[#4C9F44] text-[#fff] px-10rpx leading-34rpx font-400 text-24rpx rounded-4rpx"
v-for="(label, index) in info.label" :key="index">{{ label.label_name }}</view>
</view>
</view>
</view>
<view class="h-20rpx bg-#F6F7F9 mt-42rpx mb-40rpx"></view>
<view class="content-tab">
<wd-tabs v-model="tab">
<wd-tab title="费用说明"></wd-tab>
<wd-tab title="项目介绍"></wd-tab>
<wd-tab title="禁忌说明"></wd-tab>
<wd-tab title="下单须知"></wd-tab>
</wd-tabs>
</view>
<view class="mx-30rpx mt-40rpx">
<!-- 费用说明 -->
<view>
<view v-if="tab === 0">
<view class="font-bold text-32rpx leading-44rpx text-#303133">
费用说明
</view>
@ -255,12 +269,82 @@
</view>
</view>
</view>
</view>
<view class="h-1rpx bg-[#F6F7F9] mt-40rpx mb-24rpx"></view>
<!-- 项目介绍 -->
<view v-if="tab === 1">
<view class="font-bold text-32rpx leading-44rpx text-#303133">
项目介绍
</view>
<view class="mt-24rpx">
<view>
<view class="font-bold text-28rpx leading-48rpx text-[#303133]">自饮七步</view>
<view class="mt-24rpx font-500 text-[#303133] text-26rpx leading-48rpx">
<view>
<text class="mr-20rpx">① 选茶</text>
<text class="mr-20rpx">② 择器</text>
<text class="mr-20rpx">③ 备水</text>
</view>
<view>
<text class="mr-20rpx">④ 入境</text>
<text class="mr-20rpx">⑤ 冲泡</text>
<text class="mr-20rpx">⑥ 品茗</text>
<text class="mr-20rpx">⑦ 回味</text>
</view>
</view>
</view>
<view class="mt-24rpx">
<view class="font-bold text-28rpx leading-48rpx text-[#303133]">茶品介绍</view>
<view class="mt-24rpx font-500 text-[#303133] text-26rpx leading-48rpx">
<view>
<text class="mr-20rpx">1. 产地介绍</text>
<text class="mr-20rpx">2. 基础属性</text>
<text class="mr-20rpx">3. 外形品相</text>
<text class="mr-20rpx">4. 色香表现</text>
</view>
<view>
<text class="mr-20rpx">5.工艺特点</text>
<text class="mr-20rpx">6. 滋味口感</text>
</view>
</view>
</view>
</view>
</view>
<view class="flex items-center">
<view class="w-8rpx h-8rpx bg-[#6A6363] rounded-full mr-12rpx"></view>
<view class="font-500 text-26rpx leading-48rpx text-[#F29747]">茶艺师需提前一小时预约,两小时起订</view>
<!-- 禁忌说明 -->
<view v-if="tab === 2">
<view class="font-bold text-32rpx leading-44rpx text-#303133">
禁忌说明
</view>
<view class="mt-24rpx font-500 text-26rpx leading-48rpx text-[#303133]">
<view>绿色正规茶艺师综合服务平台,</view>
<view>为确保平台营运的合法合规和避免不必要的纠纷禁止茶师私添加客户微信,</view>
<view>禁止茶师私自收款(现金、微信、支付宝红包或转帐等),</view>
<view>禁止茶师私下接受宴请,</view>
<view>以及从事除平台项目之外的其它内容和活动。</view>
</view>
</view>
<!-- 下单须知 -->
<view v-if="tab === 3">
<view class="font-bold text-32rpx leading-44rpx text-#303133">
下单须知
</view>
<view class="mt-24rpx font-500 text-26rpx leading-48rpx text-[#303133]">
茶艺师出发后,客户取消订单,车费不予返还,敬请谅解!
</view>
<view class="mt-24rpx">
<view class="flex items-center">
<view class="mr-14rpx">
<wd-icon name="info-circle" size="36rpx" color="#707070"></wd-icon>
</view>
<view class="font-bold text-28rpx leading-48rpx text-#303133">温馨提示</view>
</view>
<view class="flex items-center mt-24rpx">
<view class="w-8rpx h-8rpx rounded-8rpx bg-#6A6363 mr-12rpx"></view>
<view class="font-500 text-26rpx leading-48rpx text-#F29747">茶艺师需提前一小时预约,两小时起订</view>
</view>
</view>
</view>
</view>
@ -329,6 +413,13 @@ const info = ref({
price: 0, // 服务费
mileage_price: 0, // 车马费
server_type: 0, // 服务方式 1到店 2上门 3两者都有
label: [], // 专属圈子标签
company_abv: '', // 公司简称
company_url: '', // 图片链接
company_status: 0, // 是否有公司信息
license_img: '', // 营业执照图片
health_certificate: '', // 健康证图片
state: 0, // 茶艺师状态 0可约 1工作中 2不可约
})
const latitude = ref<number>(0) // 纬度
const longitude = ref<number>(0) // 经度
@ -490,8 +581,13 @@ const Detail = {
* 立即邀约
*/
handleReserveTeaspecialist: () => {
// id.value - 茶艺师的ID
router.navigateTo(`/bundle_b/pages/tea-specialist/reserve?id=${id.value}`)
if (info.value.state == 0) {
// id.value - 茶艺师的ID
router.navigateTo(`/bundle_b/pages/tea-specialist/reserve?id=${id.value}`)
} else {
toast.info('当前茶艺师不可预约')
return
}
},
/**
@ -508,6 +604,22 @@ const Detail = {
*/
handlePreviewImage: (e: { index: number, item: any }) => {
previewImage(info.value.image_arr[e.index], info.value.image_arr)
},
/**
* 查看商家信息
*/
handlePreviewInfo: () => {
if (info.value.company_status) {
const obj = {
license_img: info.value.license_img,
health_certificate: info.value.health_certificate,
}
const infoStr = encodeURIComponent(JSON.stringify(obj))
router.navigateTo(`/bundle_b/pages/tea-specialist/cert?info=${infoStr}`)
} else {
toast.info('暂无认证信息')
}
}
}
</script>

View File

@ -59,28 +59,27 @@
:up="upOption" fixed>
<view class="flex items-center bg-white p-20rpx rounded-10rpx mx-30rpx mb-20rpx relative"
v-for="(item, index) in list" :key="index">
<view
class="tea-specialist-time absolute z-3 top-6rpx left-0 bg-[#4C9F44] text-[#fff] font-400 text-18rpx flex items-center justify-center">
<text v-if="item.state == 0">可约</text>
<text v-if="item.state == 1">工作中</text>
<text v-if="item.state == 2">不可约</text>
</view>
<view class="mr-28rpx relative z-2" @click="List.handlePreviewImage(item.image)">
<wd-img width="200rpx" height="200rpx" :src="item.image" mode="aspectFill"></wd-img>
<wd-img width="260rpx" height="320rpx" :src="item.image" mode="aspectFill" radius="10rpx"></wd-img>
<view
class="tea-specialist-time absolute z-3 top-26rpx left-0 bg-[#4C9F44] text-[#fff] font-400 text-18rpx flex items-center justify-center">
<text v-if="item.state == 0">可约</text>
<text v-if="item.state == 1">工作中</text>
<text v-if="item.state == 2">不可约</text>
</view>
</view>
<view class="flex-1" @click="List.handleReserveTeaSpecialist(item.team_user_id, item.state)">
<view class="flex items-center">
<view class="font-bold text-[#303133] text-30rpx leading-42rpx mr-14rpx">
<view class="flex items-center mb-8rpx">
<view class="font-bold text-[#303133] text-30rpx leading-42rpx mr-12rpx">
{{ item.name }}
</view>
<view class="flex items-center">
<wd-icon name="star-on" size="22rpx" color="#FF5951"></wd-icon>
<view class="font-400 text-24rpx text-[#FF5951] leading-34rpx ml-6rpx">5.0</view>
<!-- <tea-specialist-level :level="item.level"></tea-specialist-level> -->
<view class="font-400 text-24rpx text-[#FF5951] leading-34rpx ml-6rpx">{{ item.star }}</view>
</view>
</view>
<view class="flex items-center">
<view class="flex items-center mb-14rpx">
<!-- 到店服务标签 -->
<view class="mr-12rpx" v-if="item.server_type == 1 || item.server_type == 3">
<wd-tag color="#F55726" bg-color="#F55726" plain
@ -102,20 +101,25 @@
</view>
</view>
<view class="flex flex-wrap items-center mt-14rpx">
<view class="flex flex-wrap items-center mt-14rpx mb-24rpx">
<view
class="mr-8rpx bg-[#4C9F44] text-[#fff] px-10rpx leading-34rpx font-400 text-24rpx rounded-4rpx">
123</view>
class="mr-8rpx px-10rpx leading-34rpx font-400 text-24rpx rounded-4rpx mb-14rpx"
:style="List.handleGetLabelStyle(Number(index))"
v-for="(label, index) in item.label" :key="index">
{{ label.label_name }}
</view>
</view>
<view class="flex items-center justify-between mt-18rpx">
<view class="flex items-center justify-between mb-8rpx">
<view class="mr-20rpx w-200rpx">
<view class="flex items-center">
<view class="flex items-center">
<wd-img :src="`${OSS}icon/icon_store_cert.png`" width="36rpx"
height="36rpx"></wd-img>
<wd-img :src="`${OSS}icon/icon_store_cert2.png`" width="30rpx"
height="30rpx"></wd-img>
</view>
<text class="ml-8rpx font-400 text-24rpx leading-4rpx text-#303133">商家认证</text>
<text class="ml-8rpx font-400 text-24rpx leading-4rpx" :class="item.company_status ? 'text-#4C9F44' : 'text-#303133'">
{{ item.company_abv }}
</text>
</view>
</view>
@ -153,8 +157,8 @@ const OSS = inject('OSS')
const navbarHeight = inject('navbarHeight')
const toast = useToast()
// 茶艺师等级
const selectedLevel = ref<Array<any>>([]) // 选择茶艺师的点击等级
// 茶艺师等级(单选)
const selectedLevel = ref<Array<any>>([]) // 当前选中的茶艺师等级
// mescroll
const { mescrollInit, downCallback, getMescroll } = useMescroll(onPageScroll, onReachBottom) // 调用mescroll的hook
@ -193,6 +197,16 @@ onShareAppMessage(() => {
})
const List = {
handleGetLabelStyle: (index: number) => {
const firstThreeStyles = [
{ backgroundColor: '#FAF1F1', color: '#CE295A' },
{ backgroundColor: '#F1F3FA', color: '#2934CE' },
{ backgroundColor: '#F1FAF9', color: '#29C9CE' },
]
return firstThreeStyles[index] || { backgroundColor: '#F3F3F3', color: '#818CA9' }
},
/**
* 筛选表
*/
@ -203,7 +217,8 @@ const List = {
size: mescroll.size,
latitude: uni.getStorageSync('latitude') || import.meta.env.VITE_DEFAULT_LATITUDE,
longitude: uni.getStorageSync('longitude') || import.meta.env.VITE_DEFAULT_LONGITUDE,
search: keywords.value
search: keywords.value,
server_type: 0
}
getTeaSpecialist(filter).then((res: ITeaSpecialistResult) => {
@ -232,11 +247,11 @@ const List = {
handleToggleTeaSpecialistLevel: (value: any) => {
const index = selectedLevel.value.indexOf(value);
if (index > -1) {
// 如果已经选择了该等级,则取消选择
selectedLevel.value.splice(index, 1);
// 已选中时再次点击,取消选择
selectedLevel.value = [];
} else {
// 如果未选择该等级,则添加选择
selectedLevel.value.push(value);
// 单选:只保留当前点击项
selectedLevel.value = [value];
}
List.handleSearch()

View File

@ -117,6 +117,7 @@
</view>
</view>
<!-- 优惠 -->
<view class="mt-52rpx">
<view class="flex justify-between items-center text-30rpx text-[#303133] leading-42rpx">
<view>优惠</view>
@ -231,7 +232,7 @@
<!-- 茶艺师信息 -->
<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" radius="10rpx" :src="info.image"></wd-img>
<wd-img width="200rpx" height="200rpx" radius="10rpx" :src="info.image" @click="previewImage(info.image, [info.image])"></wd-img>
</view>
<view class="">
<view class="font-bold text-[#303133] text-30rpx leading-42rpx mr-14rpx">
@ -405,15 +406,11 @@
<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 { router, toPlus, previewImage } 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 { OrderType } from '@/utils/order'
import { getTeaTypeList, createTeaSpecialistOrder } from '@/api/tea'
import type { ITeaSpecialistFuture7DaysResult, ITeaTypeListResult } from '@/api/types/tea'
import BookingTime from '@/components/BookingTime.vue'
import { CouponType } from '@/utils/coupon'
import { getTeaSpecialistDetail, getTeaSpecialistFeeDetails } from '@/api/tea-specialist'
@ -426,10 +423,6 @@
const isSubmitting = ref(false)
// 服务方式
const serviceType = ref<Array<any>>([
{type: 1, name: '到店服务'},
{type: 2, name: '上门服务'},
])
const serviceTypeValue = ref<number>(1)
// 选择茶馆
@ -597,6 +590,14 @@
}
}
// 初始化时间组件默认值,兼容 H5 端不自动触发 @change 的情况
startTimeValue.value = String(minTimestamp)
// #ifdef H5
// 仅在 H5 环境下手动触发,避免小程序端组件抛出 @change 造成执行两次
Door.handleStartTimePicker({ value: minTimestamp as number })
// #endif
// 初始化数据
Reserve.handleInit()
})
@ -606,7 +607,10 @@
* 初始化-获取茶叶列表
*/
handleInit: async () => {
const tea = await getTeaTypeList()
const tea = await getTeaTypeList({
team_user_id: id.value,
type: 0
})
teaList.value = tea as ITeaTypeListResult[]
},
@ -818,7 +822,7 @@
})
// 获取预定了几个小时
router.navigateTo(`/bundle_b/pages/tea-specialist/coupon?couponId=${selectCouponId.value}&price=${costBill.value.order_amount}`)
router.navigateTo(`/bundle_b/pages/tea-specialist/coupon?couponId=${selectCouponId.value}&price=${costBill.value.order_amount}&typeId=1`)
},
/**

View File

@ -15,7 +15,7 @@
</view>
<view class="search-box relative">
<wd-search placeholder="门店名称" cancel-txt="搜索" placeholder-left hide-cancel custom-input-class="!h-72rpx" v-model="storeName" >
<wd-search placeholder="门店名称" cancel-txt="搜索" placeholder-left hide-cancel custom-input-class="!h-72rpx" v-model="storeName" >
</wd-search>
<view
class="absolute top-1/2 -translate-y-1/2 right-34rpx w-142rpx h-64rpx leading-64rpx text-center rounded-32rpx bg-#4C9F44 text-#fff font-400 text-32rpx"
@ -81,7 +81,7 @@
})
const Store = {
// 搜索
// 搜索
handleSearch: () => {
list.value = []
getMescroll().resetUpScroll()

View File

@ -26,26 +26,26 @@
// 茶艺师等级对应的icon和文字(这个数组5-1就是对应的等级)
const levelMap = {
5: {
icon: `${OSS}icon/icon_gold_medal.png`,
text: '金牌茶艺师'
},
4: {
icon: `${OSS}icon/icon_senior_medal.png`,
text: '高级茶艺师'
icon: `${OSS}icon/icon_gold_medal.png`,
text: '云华茶艺师'
},
3: {
icon: `${OSS}icon/icon_intermediate_medal.png`,
text: '中级茶艺师'
icon: `${OSS}icon/icon_senior_medal.png`,
text: '竹影茶艺师'
},
2: {
icon: `${OSS}icon/icon_junior_medal.png`,
text: '初级茶艺师'
icon: `${OSS}icon/icon_intermediate_medal.png`,
text: '明月茶艺师'
},
1: {
icon: `${OSS}icon/icon_enthusiast_medal.png`,
text: '茶艺爱好者'
}
icon: `${OSS}icon/icon_junior_medal.png`,
text: '清风茶艺师'
},
// 1: {
// icon: `${OSS}icon/icon_enthusiast_medal.png`,
// text: '茶艺爱好者'
// }
}
</script>

View File

@ -5,6 +5,9 @@ import { requestInterceptor } from './http/interceptor'
import { routeInterceptor } from './router/interceptor'
import NavBar from '@/components/Navbar.vue'
import './utils/mtj-wx-sdk.config'
import './utils/mtj-wx-sdk'
import store from './store'
import '@/style/index.scss'
import 'virtual:uno.css'
@ -13,6 +16,7 @@ import { getNavBarHeight, getCapsuleOffset } from '@/utils/index'
export function createApp() {
const app = createSSRApp(App)
/* 注册全局组件 */
app.component('NavBar', NavBar) // 注册全局组件

View File

@ -243,9 +243,18 @@
}
},
{
"path": "package/detail",
"path": "package/claim-coupon",
"type": "page",
"needLogin": true,
"layout": "default",
"style": {
"navigationBarTitleText": "填写资料",
"navigationBarBackgroundColor": "#fff"
}
},
{
"path": "package/detail",
"type": "page",
"layout": "tabbar",
"style": {
"navigationStyle": "custom"
@ -650,6 +659,15 @@
{
"root": "bundle_b",
"pages": [
{
"path": "pages/tea-specialist/cert",
"type": "page",
"needLogin": true,
"layout": "default",
"style": {
"navigationStyle": "custom"
}
},
{
"path": "pages/tea-specialist/coupon",
"type": "page",

View File

@ -1,389 +1,408 @@
<route lang="jsonc" type="page">
{
<route lang="jsonc" type="page">{
"needLogin": true,
"layout": "default",
"style": {
"navigationStyle": "custom"
}
}
</route>
}</route>
<template>
<view>
<view>
<navbar title="收银台" custom-class='!bg-[#F6F7F8]' :leftArrow="false"></navbar>
</view>
<navbar title="收银台" custom-class='!bg-[#F6F7F8]' :leftArrow="false"></navbar>
</view>
<!-- 支付信息 -->
<view class="mt-56rpx text-center">
<view class="text-28rpx leading-40rpx text-#606266">{{ title }}</view>
<view class="text-28rpx leading-40rpx text-#606266" v-if="!teaPackageTitle">{{ title }}</view>
<view class="text-28rpx leading-40rpx text-#606266" v-if="teaPackageTitle">茶艺师套餐-{{ teaPackageTitle }}
</view>
<view class="mt-24rpx">
<template>
<price-format color="#303133" :first-size="44" :second-size="44" :subscript-size="28" :price="money"></price-format>
<price-format color="#303133" :first-size="44" :second-size="44" :subscript-size="28"
:price="money"></price-format>
</template>
</view>
<view class="mt-12rpx flex items-center justify-center">
<view class="text-24rpx leading-34rpx text-#606266">
支付剩余时间
</view>
支付剩余时间
</view>
<wd-count-down :time="time" custom-class="!text-[#606266] !text-24rpx !leading-34rpx" />
</view>
</view>
<!-- 支付方式 -->
<view class="bg-white rounded-16rpx px-30rpx py-34rpx mx-30rpx mt-84rpx">
<view class="font-400 text-32rpx leading-44rpx text-#303133 border-b border-b-solid border-b-#F6F7F8 pb-16rpx mb-32rpx">支付方式</view>
<view
class="font-400 text-32rpx leading-44rpx text-#303133 border-b border-b-solid border-b-#F6F7F8 pb-16rpx mb-32rpx">
支付方式</view>
<!-- pay 组件 -->
<pay @pay="Cashier.handleGetPayValue" :hidePlatformBalance="hidePlatformBalance" :hideStoreBalance="hideStoreBalance" :hideWechat="hideWechat" :storeMoney="storeMoney"></pay>
<pay @pay="Cashier.handleGetPayValue" :hidePlatformBalance="hidePlatformBalance"
:hideStoreBalance="hideStoreBalance" :hideWechat="hideWechat" :storeMoney="storeMoney"></pay>
</view>
<view class="fixed bottom-70rpx left-0 right-0 text-#fff font-bold text-30rpx leading-42rpx mx-60rpx text-center rounded-8rpx w-630rpx" >
<wd-button custom-class='!bg-[#4C9F44] !rounded-8rpx !h-90rpx !leading-90rpx !w-630rpx' :disabled="isSubmitting" @click='Cashier.handleToPay'>{{ isGroupBuying ? '立即购买' : '立即预定' }}</wd-button>
<view
class="fixed bottom-70rpx left-0 right-0 text-#fff font-bold text-30rpx leading-42rpx mx-60rpx text-center rounded-8rpx w-630rpx">
<wd-button custom-class='!bg-[#4C9F44] !rounded-8rpx !h-90rpx !leading-90rpx !w-630rpx'
:disabled="isSubmitting" @click='Cashier.handleToPay'>{{ isGroupBuying ? '立即购买' : '立即预定' }}</wd-button>
</view>
</view>
</template>
<script lang="ts" setup>
import { getTeaSpecialistDetails } from '@/api/tea'
import { ITeaSpecialistDetailsFields } from '@/api/types/tea'
import { prePay, balancePay, newPrePay } from '@/api/pay'
import type {IUserInfoVo } from '@/api/types/login'
import { getTeaRoomBalance, getTeaRoomOrderDetail, getTeaRoomPackageOrderDetail, getTeaRoomPackageDetail, getTeaRoomPayOrderAmount, getRenewTeaRoomPayOrderAmount } from '@/api/tea-room'
import Pay from '@/components/Pay.vue'
import { PayValue, PayValueMap, PayOrderType, PayOrderSource } from '@/utils/pay'
import { toast } from '@/utils/toast'
import { router } from '@/utils/tools'
import { OrderType } from '@/utils/order'
import { wechatPay } from '@/hooks/usePay'
import { useUserStore } from '@/store'
import { getTeaSpecialistOrderDetails } from '@/api/teaSpecialist-order'
import { getTeaSpecialistDetails } from '@/api/tea'
import { ITeaSpecialistDetailsFields } from '@/api/types/tea'
import { prePay, balancePay, newPrePay } from '@/api/pay'
import type { IUserInfoVo } from '@/api/types/login'
import { getTeaRoomBalance, getTeaRoomOrderDetail, getTeaRoomPackageOrderDetail, getTeaRoomPackageDetail, getTeaRoomPayOrderAmount, getRenewTeaRoomPayOrderAmount } from '@/api/tea-room'
import Pay from '@/components/Pay.vue'
import { PayValue, PayValueMap, PayOrderType, PayOrderSource } from '@/utils/pay'
import { toast } from '@/utils/toast'
import { router } from '@/utils/tools'
import { OrderType } from '@/utils/order'
import { wechatPay } from '@/hooks/usePay'
import { useUserStore } from '@/store'
import { getTeaSpecialistOrderDetails } from '@/api/teaSpecialist-order'
// 用户信息
const userInfo = ref<IUserInfoVo>(null)
// 用户信息
const userInfo = ref<IUserInfoVo>(null)
// 支付倒计时取消
const time = ref<number>(3 * 60 * 1000)
// 支付倒计时取消
const time = ref<number>(3 * 60 * 1000)
// 支付金额
const money = ref<number>(0)
// 茶艺师详情
const id = ref<number>(0)
const info = reactive<ITeaSpecialistDetailsFields>({
id: 0,
name: '',
star: 0,
image: '',
reservation_num: 0,
distance: 0,
speed: 0,
real: { gender: 1, both: 18, height: 165, weight: 53, interests: '爱好茶艺,喜欢旅游,把爱好当工作' },
teamasterlabel: [],
teamasterLevel: [],
price: 0,
fare_price: 0,
collect: 0,
up_status: 0,
textarea: []
})
// 支付金额
const money = ref<number>(0)
// 支付方式
const pay = ref<number>(0)
const hideWechat = ref<boolean>(false)
const hidePlatformBalance = ref<boolean>(false)
const hideStoreBalance = ref<boolean>(false)
// 茶艺师详情
const id = ref<number>(0)
const info = reactive<ITeaSpecialistDetailsFields>({
id: 0,
name: '',
star: 0,
image: '',
reservation_num: 0,
distance: 0,
speed: 0,
real: { gender: 1, both: 18, height: 165, weight: 53, interests: '爱好茶艺,喜欢旅游,把爱好当工作' },
teamasterlabel: [],
teamasterLevel: [],
price: 0,
fare_price: 0,
collect: 0,
up_status: 0,
textarea: []
})
// 支付方式
const pay = ref<number>(0)
const hideWechat = ref<boolean>(false)
const hidePlatformBalance = ref<boolean>(false)
const hideStoreBalance = ref<boolean>(false)
// 订单
const orderId = ref<number>(0)
const order = ref<{}>(null)
const result = ref<string>('')
const from = ref<string>('')
// 支付标题
const title = ref<string>('')
// 门店ID
const storeId = ref<number>(0)
const storeMoney = ref<number>(0)
const comboId = ref<number>(0) // 套餐ID
// 一键续订支付
const renew = ref<string>('')
// 购买套餐支付
const isGroupBuying = ref<number>(0)
const groupCouponId = ref<number>(0)
// 只有在续订茶艺师下才有这个renewOrderId
const renewOrderId = ref<number>(0)
// 预约茶艺师支付
const reserveTeaSpecialist = ref<boolean>(false)
const teaSpecialistOrderAmount = ref<number>(0) // 茶艺师订单金额
// 茶艺师套餐标题
const teaPackageTitle = ref<string>('')
// 防止重复提交
const isSubmitting = ref(false)
onLoad(async (args) => {
// 订单
const orderId = ref<number>(0)
const order = ref<{}>(null)
const result = ref<string>('')
const from = ref<string>('')
orderId.value = Number(args.orderId)
comboId.value = Number(args.cmoboId) || 0
// 支付标题
const title = ref<string>('')
// 茶艺师套餐
teaPackageTitle.value = args.teaPackageTitle || ''
// 门店ID
const storeId = ref<number>(0)
const storeMoney = ref<number>(0)
const comboId = ref<number>(0) // 套餐ID
// 套餐购买
groupCouponId.value = Number(args.groupCouponId) || 0
isGroupBuying.value = Number(args.isGroupBuying) || 0
console.log("🚀 ~ isGroupBuying.value:", isGroupBuying.value)
// 一键续订支付
const renew = ref<string>('')
// 一键续订
renew.value = args?.renew || ''
// 购买套餐支付
const isGroupBuying = ref<number>(0)
const groupCouponId = ref<number>(0)
// 只有在续订茶艺师下才有这个renewOrderId
const renewOrderId = ref<number>(0)
// 预约茶艺师支付
const reserveTeaSpecialist = ref<boolean>(false)
const teaSpecialistOrderAmount = ref<number>(0) // 茶艺师订单金额
// 防止重复提交
const isSubmitting = ref(false)
onLoad(async (args) => {
// 订单
orderId.value = Number(args.orderId)
comboId.value = Number(args.cmoboId) || 0
// 套餐购买
groupCouponId.value = Number(args.groupCouponId) || 0
isGroupBuying.value = Number(args.isGroupBuying) || 0
console.log("🚀 ~ isGroupBuying.value:", isGroupBuying.value)
// 一键续订
renew.value = args?.renew || ''
// 获取门店余额
if (args.storeId) {
storeId.value = Number(args.storeId)
const balance = await getTeaRoomBalance({ store_id: storeId.value })
storeMoney.value = Number(balance.data.money) || 0
}
// 设置支付来源标题
from.value = args.from || ''
if (from.value === OrderType.TeaRoomOrder) {
// 茶室订单:预定、套餐团购两种方式
if (args.name && isGroupBuying.value == 0) {
title.value = renew.value ? `茶室续订-${args.name}` : `茶室预定-${args.name}`
} else {
title.value = `茶室套餐购买-${args.name}`
hideStoreBalance.value = true // 隐藏门店余额支付
}
} else if (from.value === OrderType.TeaSpecialist) {
// 预约茶艺师
renewOrderId.value = args.renewOrderId
teaSpecialistOrderAmount.value = Number(args.teaSpecialistOrderAmount) || 0
reserveTeaSpecialist.value = true
if (renewOrderId.value > 0) {
title.value = `续订茶艺师-${args.teaSpecialistName || ''}`
} else {
title.value = `预订茶艺师-${args.teaSpecialistName || ''}`
}
// 获取门店余额
if (args.storeId) {
storeId.value = Number(args.storeId)
const balance = await getTeaRoomBalance({ store_id: storeId.value })
storeMoney.value = Number(balance.data.money) || 0
}
// 设置支付来源标题
from.value = args.from || ''
if (from.value === OrderType.TeaRoomOrder) {
// 茶室订单:预定、套餐团购两种方式
if (args.name && isGroupBuying.value == 0) {
title.value = renew.value ? `茶室续订-${args.name}` : `茶室预定-${args.name}`
} else {
title.value = `茶室套餐购买-${args.name}`
hideStoreBalance.value = true // 隐藏门店余额支付
}
} else if (from.value === OrderType.TeaSpecialist) {
// 预约茶艺师
renewOrderId.value = args.renewOrderId
teaSpecialistOrderAmount.value = Number(args.teaSpecialistOrderAmount) || 0
reserveTeaSpecialist.value = true
if (renewOrderId.value > 0) {
title.value = `续订茶艺师-${args.teaSpecialistName || ''}`
} else {
title.value = `预订茶艺师-${args.teaSpecialistName || ''}`
}
// 用户信息
const userStore = useUserStore()
userInfo.value = userStore.userInfo
hideStoreBalance.value = true // 隐藏门店余额支付
}
// 用户信息
const userStore = useUserStore()
userInfo.value = userStore.userInfo
// 获取订单详情
if (args.from == OrderType.TeaRoomOrder && args.orderId) {
// 获取订单详情
if (args.from == OrderType.TeaRoomOrder && args.orderId) {
// 获取订单详情
if (isGroupBuying.value == 0) {
// 茶室预约订单走这个接口,续订的不需要
if (renew.value) {
Cashier.handleGetRenewOrderDetails()
} else {
Cashier.handleGetOrderDetails()
}
if (isGroupBuying.value == 0) {
// 茶室预约订单走这个接口,续订的不需要
if (renew.value) {
Cashier.handleGetRenewOrderDetails()
} else {
Cashier.handleGetRoomPackageDetails(args.money)
Cashier.handleGetOrderDetails()
}
} else if (args.from == OrderType.TeaSpecialist && args.orderId) {
// 获取预定茶艺师订单详情
Cashier.handleGetTeaSpecialistOrderDetails()
} else {
Cashier.handleGetRoomPackageDetails(args.money)
}
})
} else if (args.from == OrderType.TeaSpecialist && args.orderId) {
// 获取预定茶艺师订单详情
Cashier.handleGetTeaSpecialistOrderDetails()
}
})
onUnload(() => {
switch(result.value) {
case 'success':
uni.$emit('payment', { result: true, orderId: orderId.value })
break;
case 'fail':
default: uni.$emit('payment', { result: false, orderId: orderId.value })
onUnload(() => {
switch (result.value) {
case 'success':
uni.$emit('payment', { result: true, orderId: orderId.value })
break;
case 'fail':
default: uni.$emit('payment', { result: false, orderId: orderId.value })
}
})
const Cashier = {
/**获取茶艺师订单详情
*
* @param id
* @param user_id
*/
handleGetTeaSpecialistOrderDetails: async () => {
const res = await getTeaSpecialistOrderDetails({
id: orderId.value
})
order.value = res
if (teaSpecialistOrderAmount.value > 0) {
money.value = teaSpecialistOrderAmount.value
} else {
money.value = Number(res.order_amount)
}
})
},
const Cashier = {
/**获取茶艺师订单详情
*
* @param id
* @param user_id
*/
handleGetTeaSpecialistOrderDetails: async () => {
const res = await getTeaSpecialistOrderDetails({
id: orderId.value
})
order.value = res
if (teaSpecialistOrderAmount.value > 0) {
money.value = teaSpecialistOrderAmount.value
/**
* 获取预定茶室订单详情
*/
handleGetOrderDetails: async () => {
// 获取订单详情接口
// const res = await getTeaRoomOrderDetail({
// id: orderId.value,
// latitude: uni.getStorageSync('latitude') || import.meta.env.VITE_DEFAULT_LATITUDE,
// longitude: uni.getStorageSync('longitude') || import.meta.env.VITE_DEFAULT_LONGITUDE,
// })
const res = await getTeaRoomPayOrderAmount({
id: orderId.value,
pay_way: pay.value || PayValue.PlatformBalance
})
console.log("🚀 ~ getTeaRoomPayOrderAmount res:", res)
order.value = res
money.value = Number(res.details.order_amount)
},
/**
* 获取茶室续订订单详情
*/
handleGetRenewOrderDetails: async () => {
const res = await getRenewTeaRoomPayOrderAmount({
id: orderId.value,
pay_way: pay.value || PayValue.PlatformBalance
})
console.log("🚀 ~ getRenewTeaRoomPayOrderAmount res:", res)
order.value = res
money.value = Number(res.details.order_amount)
},
/**
* 获取茶室套餐订单详情(改为args传过来金额)
* @param value
*/
handleGetRoomPackageDetails: async (m: number) => {
// const res = await getTeaRoomPackageDetail({
// id: comboId.value
// })
money.value = m
},
/**
* 获取支付方式
* @param value 支付value
*/
handleGetPayValue: (value: number) => {
pay.value = value
// 茶室订单:为了获取不同支付方式下的订单金额
if (from.value == OrderType.TeaRoomOrder && orderId.value && isGroupBuying.value == 0) {
if (renew.value) {
// 续订订单
Cashier.handleGetRenewOrderDetails()
} else {
money.value = Number(res.order_amount)
// 正常茶室订单
Cashier.handleGetOrderDetails()
}
},
}
},
/**
* 获取预定茶室订单详情
*/
handleGetOrderDetails: async () => {
// 获取订单详情接口
// const res = await getTeaRoomOrderDetail({
// id: orderId.value,
// latitude: uni.getStorageSync('latitude') || import.meta.env.VITE_DEFAULT_LATITUDE,
// longitude: uni.getStorageSync('longitude') || import.meta.env.VITE_DEFAULT_LONGITUDE,
// })
const res = await getTeaRoomPayOrderAmount({
id: orderId.value,
pay_way: pay.value || PayValue.PlatformBalance
})
console.log("🚀 ~ getTeaRoomPayOrderAmount res:", res)
order.value = res
money.value = Number(res.details.order_amount)
},
/**
* 支付
*/
handleToPay: async () => {
if (isSubmitting.value) return
/**
* 获取茶室续订订单详情
*/
handleGetRenewOrderDetails: async () => {
const res = await getRenewTeaRoomPayOrderAmount({
id: orderId.value,
pay_way: pay.value || PayValue.PlatformBalance
})
console.log("🚀 ~ getRenewTeaRoomPayOrderAmount res:", res)
order.value = res
money.value = Number(res.details.order_amount)
},
/**
* 获取茶室套餐订单详情(改为args传过来金额)
* @param value
*/
handleGetRoomPackageDetails: async (m: number) => {
// const res = await getTeaRoomPackageDetail({
// id: comboId.value
// })
money.value = m
},
if (pay.value == null || pay.value == undefined) {
toast.info('请选择支付方式')
return
}
/**
* 获取支付方式
* @param value 支付value
*/
handleGetPayValue: (value: number) => {
pay.value = value
isSubmitting.value = true
// 茶室订单:为了获取不同支付方式下的订单金额
if (from.value == OrderType.TeaRoomOrder && orderId.value && isGroupBuying.value == 0) {
if (renew.value) {
// 续订订单
Cashier.handleGetRenewOrderDetails()
} else {
// 正常茶室订单
Cashier.handleGetOrderDetails()
}
}
},
if (from.value == OrderType.TeaRoomOrder) {
// 预支付-茶室订单
uni.showLoading({ title: '支付中...' })
try {
// 预支付
const res = await prePay({
from: PayValueMap[pay.value],
order_id: orderId.value,
pay_way: pay.value,
order_source: PayOrderSource.MINI, //订单来源:1-小程序; 2-h5; 3app
order_type: Cashier.handleGetOrderType(), // 0为茶艺师 1为茶室包间 2为茶室套餐
store_id: storeId.value || 0
})
/**
* 支付
*/
handleToPay: async () => {
if (isSubmitting.value) return
if (pay.value == null || pay.value == undefined) {
toast.info('请选择支付方式')
return
}
isSubmitting.value = true
if (from.value == OrderType.TeaRoomOrder) {
// 预支付-茶室订单
uni.showLoading({ title: '支付中...' })
try {
// 预支付
const res = await prePay({
from: PayValueMap[pay.value],
order_id: orderId.value,
pay_way: pay.value,
order_source: PayOrderSource.MINI, //订单来源:1-小程序; 2-h5; 3app
order_type: Cashier.handleGetOrderType(), // 0为茶艺师 1为茶室包间 2为茶室套餐
store_id: storeId.value || 0
// 余额支付(平台余额、门店余额)
if (pay.value == PayValue.PlatformBalance || pay.value == PayValue.StoreBalance) {
await balancePay({
id: res.pay,
store_id: storeId.value || 0,
pay_way: pay.value,
})
// 余额支付(平台余额、门店余额)
if (pay.value == PayValue.PlatformBalance || pay.value == PayValue.StoreBalance) {
await balancePay({
id: res.pay,
store_id: storeId.value || 0,
pay_way: pay.value,
})
uni.hideLoading()
result.value = 'success'
uni.hideLoading()
isSubmitting.value = false
router.navigateBack(1, 500)
} else if (pay.value == PayValue.WeChatPay) {
// 微信支付
console.log("🚀 ~ res.pay:", res.pay)
if (res.pay_type && res.pay_type == 1) {
toast.success('支付成功')
result.value = 'success'
isSubmitting.value = false
router.navigateBack(1, 500)
} else if (pay.value == PayValue.WeChatPay) {
// 微信支付
console.log("🚀 ~ res.pay:", res.pay)
if (res.pay_type && res.pay_type == 1) {
toast.success('支付成功')
result.value = 'success'
return
} else {
wechatPay(res.pay.config).then((res) => {
console.log("🚀 ~ res:", res)
uni.hideLoading()
if (res === 'success') {
toast.success('支付成功')
result.value = 'success'
} else if (res === 'cancel') {
toast.info('已取消支付')
result.value = 'fail'
} else {
toast.info('支付失败,请重试')
result.value = 'fail'
}
isSubmitting.value = false
router.navigateBack(1, 500)
return
} else {
wechatPay(res.pay.config).then((res) => {
console.log("🚀 ~ res:", res)
uni.hideLoading()
if (res === 'success') {
toast.success('支付成功')
result.value = 'success'
} else if (res === 'cancel') {
toast.info('已取消支付')
result.value = 'fail'
} else {
toast.info('支付失败,请重试')
result.value = 'fail'
}
isSubmitting.value = false
router.navigateBack(1, 500)
return
}).catch((e) => {
console.log("🚀 ~ error:", e)
uni.hideLoading()
toast.info('支付失败,请重试')
return
})
}
}).catch((e) => {
console.log("🚀 ~ error:", e)
uni.hideLoading()
toast.info('支付失败,请重试')
return
})
}
} catch (error) {
isSubmitting.value = false
uni.hideLoading()
result.value = 'fail'
router.navigateBack(1, 500)
return
}
} else if (from.value == OrderType.TeaSpecialist) {
// 预支付-茶艺师订单
uni.showLoading({ title: '支付中...' })
try {
// 预支付
const res = await newPrePay({
from: PayValueMap[pay.value],
order_id: renewOrderId.value > 0 ? renewOrderId.value : orderId.value, //如果是续单的话,则用续单的订单ID
pay_way: pay.value,
order_source: PayOrderSource.MINI, //订单来源:1-小程序; 2-h5; 3app
order_type: renewOrderId.value > 0 ? PayOrderType.RenewTeaSpecialist : PayOrderType.TeaSpecialis, // 茶艺师订单
})
} catch (error) {
isSubmitting.value = false
uni.hideLoading()
result.value = 'fail'
router.navigateBack(1, 500)
return
}
} else if (from.value == OrderType.TeaSpecialist) {
// 预支付-茶艺师订单
uni.showLoading({ title: '支付中...' })
try {
// 预支付
const res = await newPrePay({
from: PayValueMap[pay.value],
order_id: renewOrderId.value > 0 ? renewOrderId.value : orderId.value, //如果是续单的话,则用续单的订单ID
pay_way: pay.value,
order_source: PayOrderSource.MINI, //订单来源:1-小程序; 2-h5; 3app
order_type: renewOrderId.value > 0 ? PayOrderType.RenewTeaSpecialist : PayOrderType.TeaSpecialis, // 茶艺师订单
})
// 余额支付(平台余额)
if (pay.value == PayValue.PlatformBalance && res) {
// 余额支付(平台余额)
if (pay.value == PayValue.PlatformBalance && res) {
uni.hideLoading()
uni.hideLoading()
result.value = 'success'
isSubmitting.value = false
router.navigateBack(1, 500)
} else if (pay.value == PayValue.WeChatPay) {
if (res.order_amount && Number(res.order_amount) == 0) {
toast.success('支付成功')
result.value = 'success'
isSubmitting.value = false
router.navigateBack(1, 500)
} else if (pay.value == PayValue.WeChatPay) {
return
} else {
// 微信支付
wechatPay(res.config).then((res) => {
uni.hideLoading()
@ -408,40 +427,41 @@
return
})
}
} catch (error) {
isSubmitting.value = false
uni.hideLoading()
result.value = 'fail'
router.navigateBack(1, 500)
return
}
}
},
/**
* 订单类型
*/
handleGetOrderType: () => {
if (isGroupBuying.value) {
// 购买团购套餐
return PayOrderType.BuyCombo
} else if (renew.value) {
// 续订包间
return PayOrderType.RenewRoom
} else if (reserveTeaSpecialist.value) {
// 预约茶艺师
return PayOrderType.TeaSpecialis
} else {
// 默认是购买茶室
return PayOrderType.TeaRoom
} catch (error) {
isSubmitting.value = false
uni.hideLoading()
result.value = 'fail'
router.navigateBack(1, 500)
return
}
}
},
/**
* 订单类型
*/
handleGetOrderType: () => {
if (isGroupBuying.value) {
// 购买团购套餐
return PayOrderType.BuyCombo
} else if (renew.value) {
// 续订包间
return PayOrderType.RenewRoom
} else if (reserveTeaSpecialist.value) {
// 预约茶艺师
return PayOrderType.TeaSpecialis
} else {
// 默认是购买茶室
return PayOrderType.TeaRoom
}
}
}
</script>
<style lang="scss">
page {
background-color: $cz-page-background;
}
page {
background-color: $cz-page-background;
}
</style>

View File

@ -86,6 +86,7 @@
* 选择城市
*/
handleChooseCity: (item: any) => {
console.log("🚀 ~ item:", item)
const params = {
// latitude: item.latitude,
// longitude: item.longitude,
@ -106,7 +107,7 @@
handleInitDefaultCity: () => {
City.handleChooseCity({
id: 0,
city: '嘉兴市'
name: city.value || LOCATION_DEFAULT_CITY
})
}
}

View File

@ -49,7 +49,7 @@
</wd-navbar>
</view>
<view class="bg-white rounded-b-20rpx" :style="{ paddingTop: navbarHeight + 'px' }">
<view class="bg-white rounded-b-20rpx pb-30rpx" :style="{ paddingTop: navbarHeight + 'px' }">
<view class="mt-32rpx mx-30rpx">
<wd-swiper
height="320rpx"
@ -61,63 +61,44 @@
></wd-swiper>
</view>
<view class="text-36rpx text-[#303133] leading-50rpx mx-30rpx mt-40rpx mb-32rpx font-bold">热门场景</view>
<view class="package-row px-30rpx">
<view class="grid grid-cols-3 gap-x-0 gap-y-20rpx">
<view
v-for="(item, idx) in teaPackageList"
:key="item.id || idx"
class="w-216rpx rounded-20rpx pb-20rpx box-border"
:style="{
background: idx === 0
? 'linear-gradient( 180deg, #EAFFF9 0%, #F6F7F9 100%)'
: idx === 1
? 'linear-gradient( 180deg, #FFF3FB 0%, #F6F7F9 100%)'
: 'linear-gradient( 180deg, #FFF9EA 0%, #F6F7F9 100%)'
}"
@click="router.navigateTo(`/bundle/package/detail?id=${item.id}&type=${item.type}`)"
>
<view class="px-10rpx pt-10rpx flex justify-center">
<wd-img width="204rpx" height="124rpx" :src="item.img" radius="16rpx" mode="aspectFill" />
</view>
<view class="px-12rpx mt-8rpx">
<view class="text-28rpx font-bold text-[#303133] line-1">{{ item.title }}</view>
<view class="text-[#FF5951] mt-6rpx font-400 w-180rpx">
<text class="text-30rpx block" style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">¥{{ item.discount_price }}/{{ item.hour }}小时</text>
</view>
</view>
</view>
</view>
</view>
<!-- 专属定制 -->
<view class="flex justify-center mt-30rpx mb-26rpx" @click="router.navigateTo(`/bundle_b/pages/tea-specialist/list`)">
<wd-img :src="`${OSS}images/home/home_image_15.png`" width="690rpx" height="148rpx" mode="scaleToFill" />
<view class="flex justify-center mt-24rpx" @click="router.navigateTo(`/bundle_b/pages/tea-specialist/list`)">
<wd-img :src="`${OSS}images/home/home_image_19.png`" width="690rpx" height="200rpx" mode="scaleToFill" />
</view>
<view class="mx-30rpx pb-30rpx flex justify-between">
<view class="w-216rpx rounded-20rpx pb-20rpx box-border" style="background: linear-gradient( 180deg, #EAFFF9 0%, #F6F7F9 100%);"
@click="router.navigateTo(`/bundle/package/detail`)">
<view class="px-10rpx pt-10rpx flex justify-center">
<wd-img width="204rpx" height="124rpx" :src="`${OSS}images/home/home_image_16.png`" radius="16rpx" mode="aspectFill" />
</view>
<view class="px-12rpx mt-8rpx">
<view class="text-28rpx font-bold text-[#303133] line-1">清风套餐</view>
<view class="text-[#FF5951] mt-6rpx flex items-baseline font-400">
<text class="text-28rpx">¥</text>
<text class="text-30rpx">298</text>
<text class="text-24rpx">/2小时</text>
</view>
</view>
</view>
<view class="w-216rpx rounded-20rpx pb-20rpx box-border" style="background: linear-gradient( 180deg, #FFF3FB 0%, #F6F7F9 100%);">
<view class="px-10rpx pt-10rpx flex justify-center">
<wd-img width="204rpx" height="124rpx" :src="`${OSS}images/home/home_image_17.png`" radius="16rpx" mode="aspectFill" />
</view>
<view class="px-12rpx mt-8rpx">
<view class="text-28rpx font-bold text-[#303133] line-1">明月套餐</view>
<view class="text-[#FF5951] mt-6rpx flex items-baseline font-400">
<text class="text-28rpx">¥</text>
<text class="text-30rpx">598</text>
<text class="text-24rpx">/2小时</text>
</view>
</view>
</view>
<view class="w-216rpx rounded-20rpx pb-20rpx box-border" style="background: linear-gradient( 180deg, #FFF9EA 0%, #F6F7F9 100%);">
<view class="px-10rpx pt-10rpx flex justify-center">
<wd-img width="204rpx" height="124rpx" :src="`${OSS}images/home/home_image_18.png`" radius="16rpx" mode="aspectFill" />
</view>
<view class="px-12rpx mt-8rpx">
<view class="text-28rpx font-bold text-[#303133] line-1">鹤鸣套餐</view>
<view class="text-[#FF5951] mt-6rpx flex items-baseline font-400">
<text class="text-28rpx">¥</text>
<text class="text-30rpx">998</text>
<text class="text-24rpx">/2小时</text>
</view>
</view>
</view>
</view>
<!-- <view class="mt-24rpx relative w-690rpx h-224rpx mx-30rpx" @click="router.navigateTo(`/bundle_b/pages/tea-specialist/list`)">
<wd-img width="690rpx" height="224rpx" :src="`${OSS}images/home/home_image14.png`" mode="scaleToFill" />
</view> -->
</view>
<view>
<view class="mx-30rpx mt-36rpx mb-10rpx">
<view class="mx-30rpx mt-20rpx">
<scroll-view class="w-[100%] whitespace-nowrap " :scroll-x="true" :show-scrollbar="false" :enhanced="true">
<!-- <view class="scroll-item text-26rpx text-#333 mr-16rpx" :class="distance === 1 ? 'active' : ''" @click="Index.handleSort('distance')">距离优先</view> -->
<view class="scroll-item text-26rpx text-#333 mr-16rpx" :class="sort.smart === 1 ? 'active' : ''" @click="Index.handleSort('smart')">智能排序</view>
@ -128,7 +109,7 @@
</view>
<view>
<mescroll-body @init="mescrollInit" :down="downOption" @down="downCallback" :up="upOption" @up="Index.upCallback" top="28rpx"
<mescroll-body @init="mescrollInit" :down="downOption" @down="downCallback" :up="upOption" @up="Index.upCallback" top="20rpx"
:fixed="true">
<view class="relative p-20rp mb-24rpx" v-for="(item, index) in list" :key="index" @click="Index.handleToReserveRoom(item.id, item.operation_type)">
<!-- <view class="absolute top--28rpx left-0 z-1" v-if="item.operation_type == 1">
@ -194,7 +175,8 @@
import { onPageScroll, onReachBottom } from '@dcloudio/uni-app'
import useMescroll from "@/uni_modules/mescroll-uni/hooks/useMescroll.js"
import { LOCATION_DENY_TIME_KEY, handleEnsureLocationAuthHooks, LOCATION_DEFAULT_CITY, handleGetLocationCity, LOCATION_CITY_KEY, handleForceGetLocation } from '@/hooks/useLocation'
import { getHomeBannerList, getHomeCouponPopup, claimIndexCoupon, getWxacode } from '@/api/home'
import { getHomeBannerList, getHomeCouponPopup, claimIndexCoupon } from '@/api/home'
import { getTeaPackageList } from '@/api/tea-package'
import { getHomeTeaStoreList } from '@/api/tea-room'
import { useUserStore } from '@/store'
import { useToast } from 'wot-design-uni'
@ -233,6 +215,9 @@
let lastLocation = { lat: 0, lng: 0 }
const canLocation = ref<boolean>(false)
// 套餐列表
const teaPackageList = ref<Array<any>>([])
// 显示优惠券弹窗
const showCoupon = ref<boolean>(false)
@ -355,6 +340,11 @@
getHomeBannerList().then(res => {
swiperList.value = res.list.map(item => item.address)
})
// 获取套餐列表
getTeaPackageList().then(res => {
teaPackageList.value = res
})
},
/**
@ -362,6 +352,7 @@
*/
handleToCity: () => {
uni.$on('locationUpdate', params => {
console.log("🚀 ~ params:", params)
uni.$off('locationUpdate')
city.value = params.city
city_id.value = params.id
@ -560,4 +551,19 @@ page {
line-height: 44rpx;
border-radius: 0 0 16rpx 16rpx;
}
/* 套餐一行三列布局样式 */
.package-row {
width: 100%;
}
.package-row .grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0 0;
}
.package-row .w-216rpx {
margin-right: 0;
margin-left: 0;
}
</style>

View File

@ -43,10 +43,10 @@
<view class="flex items-center mx-32rpx mt-64rpx">
<view class="w-32rpx h-32rpx">
<wd-checkbox v-model="agree" @change="Login.handleAgree" checked-color="#4C9F44" size="large"> </wd-checkbox>
<wd-checkbox v-model="agree" checked-color="#4C9F44" size="large"> </wd-checkbox>
</view>
<view class="font-400 text-26rpx leading-40rpx text-[#8F959E] ml-14rpx flex-1" @click="agree = !agree">
我已阅读并同意 <text class="text-[#4C9F44]" @click.stop="Login.handleToService">《服务协议》</text> 和<text class="text-[#4C9F44]" @click.stop="Login.handleToPrivacy">《隐私政策》</text>,未注册手机号登录后将自动你为您创建账号
我已阅读并同意 <text class="text-[#4C9F44]" @click.stop="Login.handleToWebview('service')">《服务协议》</text> 和<text class="text-[#4C9F44]" @click.stop="Login.handleToWebview('privacy')">《隐私政策》</text>,未注册手机号登录后将自动你为您创建账号
</view>
</view>
</view>
@ -57,7 +57,7 @@
import { toast } from '@/utils/toast'
import { getWxCode } from '@/api/login'
import { useUserStore } from '@/store'
import { router } from '@/utils/tools'
import { router, strToParams } from '@/utils/tools'
import { getMobileByMnp } from '@/api/user'
const OSS = inject('OSS')
@ -70,10 +70,20 @@
const redirectUrl = ref<string>('')
const redirectParams = ref<string>('')
// 门店ID
const storeId = ref<number>(0)
// 绑定手机号码popup
const showBindMobilePopup = ref<boolean>(false)
onLoad((args) => {
const scene = args?.scene
console.log("🚀 ~ scene:", scene)
if (scene) {
storeId.value = strToParams(decodeURIComponent(args.scene))?.store_id
console.log("🚀 ~ storeId.value:", storeId.value)
}
redirectUrl.value = args.redirect || ''
})
@ -88,37 +98,20 @@
return
}
uni.showLoading({
title: '登录中...',
mask: true
})
try {
console.log("🚀 ~ Login userRegisterSource=", userStore.userRegisterSource)
const res = await userStore.wxLogin()
console.log("🚀 ~ res:", res)
console.log("🚀 ~ token:", uni.getStorageSync('token'))
uni.hideLoading()
uni.showLoading({ title: '登录中...', mask: true });
const res = await userStore.wxLogin(Number(storeId.value) || 0);
if (!res.mobile) {
showBindMobilePopup.value = true
return false
showBindMobilePopup.value = true;
toast.info('请先绑定手机号');
return;
}
Login.handleToLogin()
// Login.handleToLogin()
// if (res) {
// const decoded = decodeURIComponent(redirectUrl.value)
// console.log("🚀 ~ decoded:", decoded)
// if (decoded == '/bundle/order/tea-room/order-detail') {
// const transferOrderParams = uni.getStorageSync('transferOrderParams')
// uni.$emit('transferOrder', transferOrderParams)
// }
// toast.info('登录成功')
// router.navigateBack(1, 500)
// }
} catch(error) {
uni.hideLoading()
Login.handleToLogin();
} catch (error) {
console.error(error);
toast.info('登录失败,请重试');
} finally {
uni.hideLoading();
}
},
@ -180,20 +173,14 @@
}
toast.info('登录成功')
router.navigateBack(1, 500)
},
handleAgree: async (e: any) => {
router.navigateBack(1, 500).catch(err => {
router.switchTab('/pages/my/my')
})
},
// 跳转到服务协议页面
handleToService: () => {
router.navigateTo('/pages/webview/webview?type=service')
},
// 跳转到隐私政策页面
handleToPrivacy: () => {
router.navigateTo('/pages/webview/webview?type=privacy')
handleToWebview: (type: string) => {
router.navigateTo(`/pages/webview/webview?type=${type}`)
},
}
</script>

View File

@ -220,18 +220,16 @@
<view class="bg-white rounded-16rpx mx-30rpx mt-28rpx pb-34rpx">
<view class="px-30rpx pt-30rpx py-24rpx">更多服务</view>
<view class="flex px-30rpx">
<view v-for="(item, index) in serviceMenuList" :key="index">
<navigator :url="item.url" hover-class="none">
<view class="text-center flex flex-col items-center justify-center mr-30rpx relative" :class="item.badge ? 'w-180rpx ml-[-10rpx]' : 'w-120rpx'">
<view class="w-60rpx h-60rpx flex items-center justify-center">
<wd-img width="100%" height="100%" :src="item.icon"></wd-img>
</view>
<view class="font-400 text-24rpx text-[#303133] leading-34rpx mt-8rpx">{{ item.title }}</view>
<view class="service-badge rounded-18rpx text-[#63300E] text-16rpx leading-20rpx w-72rpx h-28rpx text-center leading-28rpx absolute top-0 right-0" v-if="item.badge">
{{item.badge}}
</view>
<view v-for="(item, index) in serviceMenuList" :key="index" @click="router.navigateTo(`${item.url}`)">
<view class="text-center flex flex-col items-center justify-center mr-30rpx relative" :class="item.badge ? 'w-180rpx ml-[-10rpx]' : 'w-120rpx'">
<view class="w-60rpx h-60rpx flex items-center justify-center">
<wd-img width="100%" height="100%" :src="item.icon"></wd-img>
</view>
</navigator>
<view class="font-400 text-24rpx text-[#303133] leading-34rpx mt-8rpx">{{ item.title }}</view>
<view class="service-badge rounded-18rpx text-[#63300E] text-16rpx leading-20rpx w-72rpx h-28rpx text-center leading-28rpx absolute top-0 right-0" v-if="item.badge">
{{item.badge}}
</view>
</view>
</view>
</view>
</view>
@ -270,7 +268,7 @@
import { toast } from '@/utils/toast'
import { router } from '@/utils/tools'
import { useUserStore } from '@/store'
import { getUserInfo, getMyCoupon, claimMyCoupon, getUserMember } from '@/api/user'
import { getUserInfo, getMyCoupon, claimMyCoupon, getUserMember, getConfig } from '@/api/user'
import type { IUserResult } from '@/api/types/user'
const OSS = inject('OSS')
@ -396,6 +394,14 @@
getUserMember().then(res => {
expireTime.value = res?.data?.expiration_time
})
// 获取配置项
getConfig().then(res => {
const serviceMobile = res.service_telephone
if (serviceMobile) {
sheetMenu.value = [{ name: serviceMobile }]
}
})
},
/**
@ -426,9 +432,6 @@
// 点击显示客服电话
handleShowService: () => {
showServiceMobile.value = true
sheetMenu.value = [
{ name: '13000000000' },
]
},
// 选择菜单-拨打客服电话

View File

@ -39,7 +39,16 @@
<reserve-notice title="团体茶艺师预约成功" desc="团体茶艺师预约成功">
<template #layout>
<view class="pb-22rpx mt-40rpx mx-30rpx flex justify-between items-center text-[32rpx] text-center">
<view class='bg-[#4C9F44] text-[#fff] rounded-8rpx h-90rpx leading-90rpx w-632rpx' @click="reserve.handleRoomDone">完成</view>
<view class='bg-[#4C9F44] text-[#fff] rounded-8rpx h-90rpx leading-90rpx w-632rpx' @click="router.switchTab('/pages/index/index')">完成</view>
</view>
</template>
</reserve-notice>
</view>
<view v-if="type == OrderType.TeaPackage">
<reserve-notice title="套餐购买" desc="套餐购买成功">
<template #layout>
<view class="pb-22rpx mt-40rpx mx-30rpx flex justify-between items-center text-[32rpx] text-center">
<view class='bg-[#4C9F44] text-[#fff] rounded-8rpx h-90rpx leading-90rpx w-632rpx' @click="reserve.handleToTeaSpecialistOrder">完成</view>
</view>
</template>
</reserve-notice>
@ -107,6 +116,13 @@
handleRoomDone: () => {
router.redirectTo('/bundle_b/pages/tea-specialist/list')
// router.switchTab('/pages/index/index')
},
/**
* 套餐购买关闭所有页面跳转到茶艺师列表
*/
handleToTeaSpecialistOrder: () => {
router.reLaunch('/bundle/order/tea-specialist/order-list')
}
}
</script>

View File

@ -115,13 +115,13 @@ export const useUserStore = defineStore(
/**
* 微信登录
*/
const wxLogin = async () => {
const wxLogin = async (storeId: number) => {
// 获取微信小程序登录的code
const data = await getWxCode()
console.log('微信登录code', data)
// 添加注册来源
const loginData = { ...data, source: userRegisterSource.value }
const loginData = { ...data, source: userRegisterSource.value, store_id: storeId }
const res = await _wxLogin(loginData)
uni.setStorageSync('token', res.token)

View File

@ -0,0 +1,39 @@
/**
* @file 百度移动统计配置文件
*/
module.exports = {
/**
* 从百度移动统计获取的AppKey
* @type {string}
*/
appKey: 'd4a524dade',
/**
* 是否使用了插件
* @type {boolean}
*/
hasPlugin: false,
/**
* 是否获取当前的地理位置和速度信息
* @type {boolean}
*/
getLocation: false,
/**
* 是否获取组件滚动信息
* @type {boolean}
*/
getComponentScroll: false,
/**
* 是否开启了A/B 测试
* @type {boolean}
*/
hasABTest: false,
/**
* 是否开启热力图功能
* @type {boolean}
*/
hasHeatmap: false,
};

1
src/utils/mtj-wx-sdk.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -211,6 +211,7 @@ export const OrderType = {
TeaRoomOrder: 'teaRoomOrder',
TeaSpecialist: 'teaSpecialist',
ReserveGroup: 'reserveGroup',
TeaPackage: 'teaPackage',
}
// 包间订单状态数字(根据UI图还缺已退款、待接单、售后中、售后完成)

View File

@ -9,18 +9,18 @@ export enum TeaSpecialistLevel {
// 等级名称对应值
export const TeaSpecialistLevelValue = {
['金牌茶艺师']: 'gold',
['高级茶艺师']: 'senior',
['中级茶艺师']: 'intermediate',
['初级茶艺师']: 'junior',
['云华茶艺师']: 'gold',
['竹影茶艺师']: 'senior',
['明月茶艺师']: 'intermediate',
['清风茶艺师']: 'junior',
// ['茶艺爱好者']: 'enthusiast',
}
// 茶艺师对象结构
export const TeaSpecialistLevels = [
{ id: 4, value: 'junior', label: TeaSpecialistLevel.Junior, level: 2 },
{ id: 3, value: 'intermediate', label: TeaSpecialistLevel.Intermediate, level: 3 },
{ id: 2, value: 'senior', label: TeaSpecialistLevel.Senior, level: 4 },
{ id: 1, value: 'gold', label: TeaSpecialistLevel.Gold, level: 5 },
{ id: 4, value: 'junior', label: TeaSpecialistLevel.Junior, level: 1 },
{ id: 3, value: 'intermediate', label: TeaSpecialistLevel.Intermediate, level: 2 },
{ id: 2, value: 'senior', label: TeaSpecialistLevel.Senior, level: 3 },
{ id: 1, value: 'gold', label: TeaSpecialistLevel.Gold, level: 4 },
// { id: 5, value: 'enthusiast', label: TeaSpecialistLevel.Enthusiast, level: 1 }
];

File diff suppressed because one or more lines are too long