删除不需要的文件

This commit is contained in:
wangxiaowei
2026-04-08 10:39:16 +08:00
parent fd11a46379
commit e978bd932e
15 changed files with 915 additions and 5923 deletions

View File

@ -2,7 +2,6 @@
import { onHide, onLaunch, onShow } from '@dcloudio/uni-app'
import { navigateToInterceptor } from '@/router/interceptor'
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'
import { LOCATION_DEFAULT_CITY, LOCATION_DEFAULT_LAT, LOCATION_DEFAULT_LNG, LOCATION_LAT_KEY, LOCATION_LNG_KEY, LOCATION_CITY_KEY } from '@/hooks/useLocation'
onLaunch((options) => {
// 处理直接进入页面路由的情况如h5直接输入路由、微信小程序分享后进入等

View File

@ -41,26 +41,6 @@ export function getLocationToCity(data: ILocationToCityParams) {
)
}
/**
* 获取门店列表
*/
export interface IHomeTeaStoreListParams {
page: number
size: number
search: string
latitude: number
longitude: number
}
export function getHomeTeaStoreList(data: IHomeTeaStoreListParams) {
return http.Post<IHomeTeaRoomListResult>('/api/teaStore/teaStoreLists',
data,
{
meta: { ignoreAuth: true }
}
)
}
/**
* 获取茶室开通城市列表
*/
@ -72,20 +52,6 @@ export function getOpenCityList() {
)
}
/**
* 茶室搜索历史
*/
export function getTeaRoomSearchHistory() {
return http.Post<{ list: Array<string> }>('/api/teaStore/teaStoreSearchHistory')
}
/**
* 清除茶室搜索历史
*/
export function clearTeaRoomSearchHistory() {
return http.Post('/api/teaStore/delTeaStoreSearchHistory', null)
}
/**
* 茶室详情
*/

View File

@ -164,4 +164,11 @@ export function getExclusiveCirclesLabel() {
meta: { ignoreAuth: true }
}
)
}
/**
* 获取当前用户(茶艺师)等级
*/
export function getUserLevel() {
return http.Post<any>('/teamapi/user/userLevel')
}

File diff suppressed because it is too large Load Diff

View File

@ -218,7 +218,7 @@
class="service-icon-wrapper h-96rpx w-96rpx flex items-center justify-center rounded-16rpx bg-[#FFFFFF]">
<wd-img width="48rpx" height="48rpx" :src="`${OSS}images/chayishi/data.png`" />
</view>
<text class="text-24rpx text-[#303133] leading-34rpx">上传资料</text>
<text class="text-24rpx text-[#303133] leading-34rpx">资质证书</text>
</view>
<!-- 行程记录 -->
<!-- <view class="service-item flex flex-col items-center" @click="My.handleServiceClick('record')">

View File

@ -36,7 +36,7 @@
</view>
<!-- 营业执照 -->
<view class="rounded-24rpx bg-white mx-30rpx relative">
<view class="rounded-24rpx bg-white mx-30rpx relative mt-20rpx">
<!-- 审核状态标签 -->
<view class="absolute top-20rpx right-34rpx">
<view v-if="auditStatus == 0"
@ -54,7 +54,7 @@
</view>
<!-- 营业执照 -->
<view class="info-card mt-30rpx px-30rpx pt-20rpx">
<!-- <view class="info-card mt-30rpx px-30rpx pt-20rpx">
<text class="mb-24rpx block text-32rpx text-[#303133] font-bold leading-44rpx">营业执照</text>
<view v-if="documents.businessLicense" class="relative w-[200rpx]">
<wd-img width="200rpx" height="200rpx" :src="documents.businessLicense" mode="aspectFill"
@ -64,10 +64,10 @@
<wd-img width="120rpx" height="120rpx" :src="`${OSS}images/chayishi/text.png`" />
<text class="mt-24rpx text-28rpx text-[#909399] leading-40rpx">暂未提交信息</text>
</view>
</view>
</view> -->
<!-- 资质证书 -->
<view class="info-card px-30rpx">
<view class="info-card px-30rpx pt-20rpx">
<text class="mb-24rpx block text-32rpx text-[#303133] font-bold leading-44rpx">资质证书</text>
<view v-if="documents.qualification" class="relative w-[200rpx]">
<wd-img width="200rpx" height="200rpx" :src="documents.qualification" mode="aspectFill"
@ -105,7 +105,7 @@
<!-- 新增/编辑状态 -->
<template v-else>
<!-- 营业执照 -->
<view class="info-card mx-30rpx mt-30rpx rounded-24rpx bg-white p-30rpx">
<!-- <view class="info-card mx-30rpx mt-30rpx rounded-24rpx bg-white p-30rpx">
<text class="mb-24rpx block text-32rpx text-[#303133] font-bold leading-44rpx">营业执照</text>
<view class="relative mb-24rpx w-[200rpx]">
<wd-upload
@ -125,7 +125,7 @@
</view>
</wd-upload>
</view>
</view>
</view> -->
<!-- 资质证书 -->
<view class="info-card mx-30rpx mt-30rpx rounded-24rpx bg-white p-30rpx">
@ -137,6 +137,8 @@
:limit="1"
image-mode="scaleToFill"
:action="action"
@progress="isUploading = true"
@fail="isUploading = false"
@success="UploadData.handleUploadQualification">
<view
class="border-2rpx border-dashed border-[#E5E5E5] w-184rpx h-184rpx flex flex-col items-center justify-center rounded-16rpx">
@ -160,6 +162,8 @@
:limit="1"
image-mode="scaleToFill"
:action="action"
@progress="isUploading = true"
@fail="isUploading = false"
@success="UploadData.handleUploadHealthCert">
<view
class="border-2rpx border-dashed border-[#E5E5E5] w-184rpx h-184rpx flex flex-col items-center justify-center rounded-16rpx">
@ -189,7 +193,8 @@
<view v-if="pageStatus === 'add' || pageStatus === 'edit'"
class="fixed bottom-0 left-0 right-0 px-30rpx pb-40rpx pt-24rpx z-100">
<view class="save-btn h-88rpx rounded-8rpx bg-[#4C9F44] text-center leading-88rpx"
@click="UploadData.handleSubmit">
:class="{'opacity-50': !canSubmit}"
@click="handleSubmitClick">
<text class="text-32rpx text-[#fff] font-bold">提交</text>
</view>
</view>
@ -197,6 +202,7 @@
</template>
<script lang="ts" setup>
import { ref, computed, inject } from 'vue'
import { router } from '@/utils/tools'
import { useToast } from 'wot-design-uni'
import { addTeaSpecialistQualification, getTeaSpecialistQualification, editTeaSpecialistQualification } from '@/api/tes-specialist'
@ -206,6 +212,7 @@
const rightPadding = inject('capsuleOffset')
const token = ref<string>('') // 用户token
const toast = useToast()
const isUploading = ref<boolean>(false)
// 页面状态:'add' | 'edit' | 'pending' | 'view'
// 可以通过路由参数或API获取状态这里使用静态数据模拟
@ -213,11 +220,22 @@
// 资料数据
const documents = ref({
businessLicense: '', // 营业执照
// businessLicense: '', // 营业执照
qualification: '', // 资质证书
healthCert: '', // 健康证
})
// 提交按钮是否可用(所有图片都已上传)
const canSubmit = computed(() => {
return !!documents.value.qualification && !!documents.value.healthCert && !isUploading.value
})
const handleSubmitClick = () => {
if (canSubmit.value) {
UploadData.handleSubmit()
}
}
// 审核状态
const auditStatus = ref<number>(0) // 0 待审核 1 通过 2 未通过
@ -259,13 +277,13 @@
const res = await getTeaSpecialistQualification(certId.value)
// 回显图片-编辑状态
fileList1.value = res.license_img ? [{ url: res.license_img }] : [] // 营业执照
// fileList1.value = res.license_img ? [{ url: res.license_img }] : [] // 营业执照
fileList2.value = res.cert_img ? [{ url: res.cert_img }] : [] // 资质证书
fileList3.value = res.fitness_img ? [{ url: res.fitness_img }] : [] // 健康证
// 回显图片-预览状态
documents.value = {
businessLicense: res.license_img || '',
// businessLicense: res.license_img || '',
qualification: res.cert_img || '',
healthCert: res.fitness_img || '',
}
@ -282,24 +300,25 @@
/**
* 上传营业执照
*/
handleUploadBusinessLicense: async (e: any) => {
try {
const response = JSON.parse(e.file.response)
if (response.code) {
const avatarUrl = response.data.url
documents.value.businessLicense = avatarUrl
} else {
throw new Error('上传失败')
}
} catch (error) {
toast.show('上传失败')
}
},
// handleUploadBusinessLicense: async (e: any) => {
// try {
// const response = JSON.parse(e.file.response)
// if (response.code) {
// const avatarUrl = response.data.url
// documents.value.businessLicense = avatarUrl
// } else {
// throw new Error('上传失败')
// }
// } catch (error) {
// toast.show('上传失败')
// }
// },
/**
* 上传资质证书
*/
handleUploadQualification: async (e: any) => {
isUploading.value = false
try {
const response = JSON.parse(e.file.response)
if (response.code) {
@ -317,6 +336,7 @@
* 上传健康证
*/
handleUploadHealthCert: async (e: any) => {
isUploading.value = false
try {
const response = JSON.parse(e.file.response)
if (response.code) {
@ -334,15 +354,15 @@
* 提交数据
*/
handleSubmit: async () => {
const licenseImg = documents.value.businessLicense ? documents.value.businessLicense.replace(import.meta.env.VITE_OSS_BASEURL, '') : ''
const certImg = documents.value.qualification ? documents.value.qualification.replace(import.meta.env.VITE_OSS_BASEURL, '') : ''
const fitnessImg = documents.value.healthCert ? documents.value.healthCert.replace(import.meta.env.VITE_OSS_BASEURL, '') : ''
// const licenseImg = documents.value.businessLicense ? documents.value.businessLicense.replace(import.meta.env.VITE_OSS_BASEURL, '') : ''
const certImg = documents.value.qualification ? documents.value.qualification.replace(import.meta.env.VITE_UPLOAD_IMAGE_URL, '') : ''
const fitnessImg = documents.value.healthCert ? documents.value.healthCert.replace(import.meta.env.VITE_UPLOAD_IMAGE_URL, '') : ''
if (certId.value > 0) {
// 编辑资料
await editTeaSpecialistQualification({
id: certId.value,
license_img: licenseImg,
// license_img: licenseImg,
cert_img: certImg,
fitness_img: fitnessImg,
})
@ -350,7 +370,7 @@
} else {
// 添加资料
await addTeaSpecialistQualification({
license_img: licenseImg,
// license_img: licenseImg,
cert_img: certImg,
fitness_img: fitnessImg,
})
@ -361,6 +381,7 @@
pageStatus.value = 'pending'
},
}
</script>
<style lang="scss">
@ -399,7 +420,7 @@
height: 200rpx;
transition: all 0.3s;
&:active {
&:active {
opacity: 0.8;
}
}

View File

@ -1,698 +0,0 @@
<route lang="jsonc" type="page">
{
"needLogin": false,
"layout": "default",
"style": {
"navigationStyle": "custom"
}
}
</route>
<script lang="ts" setup>
import { useMessage } from 'wot-design-uni'
import PriceFormat from '@/components/PriceFormat.vue'
import { copy, router } from '@/utils/tools'
const OSS = inject('OSS')
// 消息提示框
const message = useMessage('wd-message-box-slot')
// 订单记录弹窗
const showOrderRecordPopup = ref(false)
// 订单数据
const orderData = ref({
id: 7,
serviceName: '苓苑共享茶室空间',
serviceType: '到店服务',
price: 212.20,
appointmentTimeFull: '2025-03-18 09:00-12:00',
duration: '3小时',
servicePeople: 1,
teaName: '福鼎白茶 (3泡)',
teawareUsage: '客户自备',
address: '青浦区仓桥路478号',
latitude: 31.2304,
longitude: 121.4737,
orderSn: '7327328627526903',
paymentMethod: '微信支付',
createTime: '2019-05-16 12:20:26',
payTime: '2019-05-16 13:20:26',
})
// 订单记录数据
const orderRecord = ref({
orderSn: '7327328627526903',
steps: [
{
id: 1,
title: '客户下订单',
time: '2019-05-16 12:20:26',
status: 'completed',
photo: '',
},
{
id: 2,
title: '订单取消',
time: '2019-05-16 13:30:26',
status: 'completed',
photo: '',
},
],
})
// 复制订单编号
function handleCopyOrderSn() {
copy(orderData.value.orderSn)
uni.showToast({
title: '已复制',
icon: 'success',
})
}
// 导航
function handleNavigate() {
if (orderData.value.latitude && orderData.value.longitude) {
uni.openLocation({
latitude: orderData.value.latitude,
longitude: orderData.value.longitude,
name: orderData.value.serviceName,
address: orderData.value.address,
})
}
}
// 查看订单记录
function handleViewOrderRecord() {
// 更新订单号
orderRecord.value.orderSn = orderData.value.orderSn
showOrderRecordPopup.value = true
}
// 查看记录中的照片
function handleViewRecordPhoto(photoPath: string) {
if (photoPath) {
uni.previewImage({
current: 0,
urls: [photoPath],
})
}
}
// 删除订单
function handleDeleteOrder() {
message.confirm({
title: '确定删除订单?',
msg: '删除订单后无法恢复,是否确认删除订单?',
confirmButtonText: '确定',
cancelButtonText: '取消',
cancelButtonProps: {
customClass: '!bg-[#F6F7F8] !text-[#303133] !text-32rpx !leading-44rpx !rounded-8rpx',
},
confirmButtonProps: {
customClass: '!bg-[#4C9F44] !text-[#fff] !text-32rpx !leading-44rpx !rounded-8rpx',
},
}).then((res) => {
if (res.action === 'confirm') {
// TODO: 调用删除订单的 API
console.log('确认删除订单')
uni.showToast({
title: '删除订单成功',
icon: 'success',
})
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
}).catch(() => {
console.log('取消删除订单')
})
}
onLoad((args) => {
// 从路由参数获取订单ID
if (args.id) {
// TODO: 根据ID获取订单详情
console.log('订单ID:', args.id)
}
})
</script>
<template>
<view class="cancelled-order-detail-page" :class="{ 'popup-open': showOrderRecordPopup }">
<!-- 消息提示框 -->
<wd-message-box selector="wd-message-box-slot" />
<!-- 导航栏 -->
<navbar title="订单取消" custom-class="!bg-[#F6F7F8]" />
<scroll-view class="content-scroll" scroll-y>
<!-- 取消提示消息 -->
<view class="cancellation-message">
<text class="message-text">订单已取消,期待您下次的精彩服务!</text>
</view>
<!-- 订单卡片 -->
<view class="order-card" style="position: relative;">
<view class="price-btn" @click.stop>
<price-format
color="#FFFFFF" :first-size="32" :second-size="32" :subscript-size="24"
:price="orderData.price"
/>
<wd-icon name="arrow-right" size="24rpx" color="#FFFFFF" class="ml-8rpx" />
</view>
<view class="order-card-header">
<view class="order-icon-wrapper">
<wd-img
width="28rpx" height="28rpx" :src="`${OSS}images/chayishi/order-icon.png`"
mode="aspectFill"
/>
</view>
<text class="order-card-title">订单</text>
</view>
<view class="order-card-content">
<text class="service-name">{{ orderData.serviceName }}</text>
</view>
<!-- 预约信息 -->
<view class="info-section">
<view class="section-title">
预约信息
</view>
<view class="info-item">
<text class="info-label">预约时间:</text>
<text class="info-value">{{ orderData.appointmentTimeFull }}</text>
</view>
<view class="info-item">
<text class="info-label">预约时长:</text>
<text class="info-value">{{ orderData.duration }}</text>
</view>
</view>
<!-- 茶艺服务 -->
<view class="info-section">
<view class="section-title">
茶艺服务
</view>
<view class="info-item">
<text class="info-label">服务人数</text>
<text class="info-value">{{ orderData.servicePeople }}</text>
</view>
<view class="info-item">
<text class="info-label">预定茶叶</text>
<text class="info-value">{{ orderData.teaName }}</text>
</view>
<view class="info-item">
<text class="info-label">茶具使用</text>
<text class="info-value">{{ orderData.teawareUsage }}</text>
</view>
</view>
</view>
<!-- 服务方式 -->
<view class="info-card">
<view class="info-card-title">
服务方式
</view>
<view class="info-item">
<text class="info-label">服务方式</text>
<text class="info-value">{{ orderData.serviceType }}</text>
</view>
<view class="info-item">
<text class="info-label">服务地址</text>
<view class="address-wrapper">
<text class="info-value">{{ orderData.address }}</text>
<wd-icon name="location" size="32rpx" color="#4C9F44" class="ml-16rpx" @click.stop="handleNavigate" />
</view>
</view>
</view>
<!-- 订单记录 -->
<view class="info-card order-record-card" @click="handleViewOrderRecord">
<view class="order-record-wrapper">
<text class="order-record-text">订单记录</text>
<wd-icon name="arrow-right" size="24rpx" color="#909399" />
</view>
</view>
<!-- 订单信息 -->
<view class="info-card">
<view class="info-card-title">
订单信息
</view>
<view class="info-item">
<text class="info-label">订单编号</text>
<view class="order-sn-wrapper">
<text class="info-value">{{ orderData.orderSn }}</text>
<text class="copy-text" @click.stop="handleCopyOrderSn">|复制</text>
</view>
</view>
<view class="info-item">
<text class="info-label">交易方式</text>
<text class="info-value">{{ orderData.paymentMethod }}</text>
</view>
<view class="info-item">
<text class="info-label">创建时间</text>
<text class="info-value">{{ orderData.createTime }}</text>
</view>
<view class="info-item">
<text class="info-label">付款时间</text>
<text class="info-value">{{ orderData.payTime }}</text>
</view>
</view>
<!-- 底部占位避免内容被按钮遮挡 -->
<view class="bottom-placeholder" />
</scroll-view>
<!-- 订单记录弹窗 -->
<wd-popup
v-model="showOrderRecordPopup"
lock-scroll
custom-style="border-radius: 32rpx 32rpx 0rpx 0rpx;"
position="bottom"
:z-index="200"
:close-on-click-overlay="true"
>
<view class="order-record-popup">
<!-- 关闭按钮 -->
<view class="close-btn" @click="showOrderRecordPopup = false">
<wd-icon name="close" size="40rpx" color="#303133" />
</view>
<!-- 标题 -->
<view class="popup-title">
订单记录
</view>
<scroll-view class="record-content" scroll-y>
<!-- 订单号 -->
<view class="order-sn-display">
<text class="order-sn-label">订单号:</text>
<text class="order-sn-value">{{ orderRecord.orderSn }}</text>
</view>
<!-- 时间线 -->
<view class="timeline-container">
<view
v-for="(step, index) in orderRecord.steps"
:key="step.id"
class="timeline-item"
>
<!-- 时间线节点 -->
<view class="timeline-node-wrapper">
<view
class="timeline-node"
:class="{ 'timeline-node-completed': step.status === 'completed', 'timeline-node-pending': step.status === 'pending' }"
/>
<!-- 连接线最后一个不显示 -->
<view
v-if="index < orderRecord.steps.length - 1"
class="timeline-line"
:class="{ 'timeline-line-completed': step.status === 'completed' }"
/>
</view>
<!-- 时间线内容 -->
<view class="timeline-content">
<view class="timeline-step-header">
<text class="timeline-step-title">{{ step.title }}</text>
<text v-if="step.time" class="timeline-step-time">{{ step.time }}</text>
</view>
<!-- 照片如果有 -->
<view v-if="step.photo" class="timeline-photo" @click="handleViewRecordPhoto(step.photo)">
<wd-img
width="120rpx"
height="120rpx"
:src="step.photo"
mode="aspectFill"
radius="8rpx"
/>
</view>
</view>
</view>
</view>
</scroll-view>
<!-- 确定按钮 -->
<view class="record-confirm-btn" @click="showOrderRecordPopup = false">
<wd-button
type="primary"
custom-class="!bg-[#4C9F44] !text-[#fff] !text-32rpx !px-32rpx !h-88rpx !leading-88rpx !rounded-8rpx !w-full"
>
确定
</wd-button>
</view>
</view>
</wd-popup>
<!-- 底部操作按钮 -->
<view class="bottom-actions" @click.stop>
<wd-button
type="primary"
custom-class="!bg-[#FF5951] !text-[#fff] !text-32rpx !px-32rpx !h-88rpx !leading-88rpx !rounded-8rpx !w-full"
@click.stop="handleDeleteOrder"
>
删除订单
</wd-button>
</view>
</view>
</template>
<style lang="scss">
page {
background-color: #f5f5f5;
height: 100%;
}
.cancelled-order-detail-page {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f5f5f5;
}
.content-scroll {
flex: 1;
padding: 0 30rpx;
}
.cancellation-message {
padding: 30rpx 0;
text-align: center;
}
.message-text {
font-size: 28rpx;
color: #909399;
line-height: 40rpx;
}
.price-btn {
position: absolute;
right: 0rpx;
top: 0rpx;
width: 200rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #ff5951;
padding: 12rpx 24rpx;
border-radius: 8rpx;
box-shadow: 0 2rpx 8rpx rgba(255, 89, 81, 0.3);
min-width: 140rpx;
}
.order-card {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-top: 20rpx;
}
.order-card-header {
display: flex;
align-items: center;
margin-bottom: 24rpx;
}
.order-icon-wrapper {
position: relative;
width: 28rpx;
height: 28rpx;
margin-right: 8rpx;
}
.order-card-title {
font-size: 28rpx;
color: #303133;
font-weight: 500;
}
.order-card-content {
margin-bottom: 24rpx;
}
.service-name {
font-size: 30rpx;
color: #303133;
font-weight: 500;
}
.info-section {
margin-top: 24rpx;
padding-top: 24rpx;
border-top: 2rpx solid #f6f7f9;
&:first-of-type {
margin-top: 0;
padding-top: 0;
border-top: none;
}
}
.section-title {
font-size: 30rpx;
color: #303133;
font-weight: 500;
margin-bottom: 20rpx;
}
.info-item {
display: flex;
align-items: flex-start;
margin-bottom: 16rpx;
font-size: 28rpx;
line-height: 40rpx;
&:last-child {
margin-bottom: 0;
}
}
.info-label {
color: #909399;
margin-right: 16rpx;
white-space: nowrap;
}
.info-value {
color: #606266;
flex: 1;
}
.info-card {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-top: 20rpx;
}
.info-card-title {
font-size: 30rpx;
color: #303133;
font-weight: 500;
margin-bottom: 24rpx;
display: flex;
align-items: center;
}
.address-wrapper {
display: flex;
align-items: center;
flex: 1;
}
.order-sn-wrapper {
display: flex;
align-items: center;
}
.copy-text {
color: #4c9f44;
margin-left: 8rpx;
}
.order-record-card {
padding: 30rpx;
}
.order-record-wrapper {
display: flex;
align-items: center;
justify-content: space-between;
}
.order-record-text {
font-size: 30rpx;
color: #303133;
font-weight: 500;
}
.bottom-placeholder {
height: 120rpx;
}
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
padding: 20rpx 30rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.05);
z-index: 100;
}
// 当弹窗打开时,降低底部按钮的 z-index确保被弹窗遮盖
.cancelled-order-detail-page.popup-open .bottom-actions {
z-index: 50;
}
// 订单记录弹窗样式
.order-record-popup {
position: relative;
background-color: #fff;
border-radius: 32rpx 32rpx 0 0;
max-height: 80vh;
display: flex;
flex-direction: column;
z-index: 201;
}
.record-content {
flex: 1;
padding: 0 30rpx;
max-height: calc(80vh - 200rpx);
}
.order-sn-display {
padding: 30rpx 0;
border-bottom: 2rpx solid #f6f7f9;
margin-bottom: 30rpx;
}
.order-sn-label {
font-size: 28rpx;
color: #909399;
margin-right: 16rpx;
}
.order-sn-value {
font-size: 28rpx;
color: #303133;
font-weight: 500;
}
.timeline-container {
padding-bottom: 30rpx;
}
.timeline-item {
display: flex;
position: relative;
&:not(:last-child) {
margin-bottom: 30rpx;
}
}
.timeline-node-wrapper {
display: flex;
flex-direction: column;
align-items: center;
margin-right: 24rpx;
position: relative;
}
.timeline-node {
width: 24rpx;
height: 24rpx;
border-radius: 50%;
border: 4rpx solid #dcdfe6;
background-color: #fff;
flex-shrink: 0;
z-index: 2;
position: relative;
}
.timeline-node-completed {
border-color: #4c9f44;
background-color: #4c9f44;
}
.timeline-node-pending {
border-color: #dcdfe6;
background-color: #fff;
}
.timeline-line {
position: absolute;
left: 50%;
top: 24rpx;
transform: translateX(-50%);
width: 2rpx;
height: calc(100% + 30rpx);
background-color: #dcdfe6;
z-index: 1;
margin-top: 4rpx;
}
.timeline-line-completed {
background-color: #4c9f44;
}
.timeline-content {
flex: 1;
padding-top: 2rpx;
}
.timeline-step-header {
display: flex;
flex-direction: column;
margin-bottom: 16rpx;
}
.timeline-step-title {
font-size: 28rpx;
color: #303133;
font-weight: 500;
line-height: 40rpx;
margin-bottom: 8rpx;
}
.timeline-step-time {
font-size: 24rpx;
color: #909399;
line-height: 34rpx;
}
.timeline-photo {
margin-top: 12rpx;
}
.record-confirm-btn {
padding: 20rpx 30rpx 30rpx;
border-top: 2rpx solid #f6f7f9;
}
.close-btn {
position: absolute;
top: 18rpx;
right: 30rpx;
z-index: 10;
padding: 10rpx;
}
.popup-title {
text-align: center;
font-size: 36rpx;
font-weight: 500;
color: #121212;
line-height: 50rpx;
padding: 50rpx 0 40rpx;
}
</style>

View File

@ -1,755 +0,0 @@
<route lang="jsonc" type="page">{
"needLogin": false,
"layout": "default",
"style": {
"navigationStyle": "custom"
}
}</route>
<template>
<view class="pb-100rpx">
<!-- 用户订单备注内容 -->
<wd-popup v-model="showOrderMarksPopup" lock-scroll custom-style="border-radius: 32rpx 32rpx 0rpx 0rpx;" position="bottom">
<view class="relative pb-26rpx">
<view class="absolute top-18rpx right-30rpx" @click="showOrderMarksPopup = false">
<wd-img width="60rpx" height='60rpx' :src="`${OSS}icon/icon_close.png`"></wd-img>
</view>
<view class="text-36rpx text-[#121212] leading-50rpx text-center pt-50rpx pb-40rpx">订单备注</view>
<view>
</view>
</view>
</wd-popup>
<!-- 费用明细弹窗 -->
<wd-popup v-model="showCostDetailPopup" lock-scroll custom-style="border-radius: 32rpx 32rpx 0rpx 0rpx;"
position="bottom" :z-index="200" :close-on-click-overlay="true">
<view class="cost-detail-popup">
<!-- 关闭按钮 -->
<view class="close-btn" @click="showCostDetailPopup = false">
<wd-icon name="close" size="40rpx" color="#303133" />
</view>
<!-- 标题 -->
<view class="popup-title">
费用明细
</view>
<scroll-view class="cost-content !pb-40rpx" scroll-y :show-scrollbar="false" :enhanced="true">
<!-- 订单总额 -->
<view class="cost-item">
<text class="cost-label">订单总额</text>
<text class="cost-value">¥ {{ costDetail.orderTotal.toFixed(2) }}</text>
</view>
<view class="bg-[#FBFBFB] py-24rpx px-30rpx rounded-16rpx">
<!-- 服务费 -->
<view class="cost-item">
<text class="cost-label">服务费</text>
<text class="cost-value">¥ {{ costDetail.serviceFee.toFixed(2) }}</text>
</view>
<view class="cost-sub-item">
<text class="cost-sub-label">服务费 (¥ {{ costDetail.serviceFeePerHour }}/小时)</text>
<text class="cost-sub-value">x{{ costDetail.serviceHours }}</text>
</view>
<!-- 车马费 -->
<view class="cost-item">
<text class="cost-label">车马费</text>
<text class="cost-value">¥ {{ costDetail.travelFee.toFixed(2) }}</text>
</view>
<view class="cost-sub-item">
<text class="cost-sub-label">车马费 (¥ {{ costDetail.travelFeePerKm }}/公里)</text>
<text class="cost-sub-value">{{ costDetail.distance }}公里</text>
</view>
<!-- 茶艺服务 -->
<view class="cost-item">
<text class="cost-label">茶艺服务</text>
<text class="cost-value">¥ {{ costDetail.teaServiceFee.toFixed(2) }}</text>
</view>
<view class="cost-sub-item">
<text class="cost-sub-label">{{ costDetail.teaName }}</text>
<text class="cost-sub-value">¥ {{ costDetail.teaPrice }}</text>
</view>
<view class="cost-sub-item">
<text class="cost-sub-label">茶具使用</text>
<text class="cost-sub-value">¥ {{ costDetail.teawarePrice }}</text>
</view>
<!-- 优惠 -->
<view class="cost-item">
<text class="cost-label">优惠</text>
<text class="cost-value discount">- ¥ {{ costDetail.discount.toFixed(2) }}</text>
</view>
<view class="cost-sub-item">
<text class="cost-sub-label">优惠券</text>
<text class="cost-sub-value discount">- ¥ {{ costDetail.couponDiscount }}</text>
</view>
</view>
<!-- 分隔线 -->
<view class="cost-divider" />
<!-- 扣除费用区域 -->
<view class="deduct-section rounded-16rpx">
<view class="cost-item">
<text class="cost-label">扣除费用</text>
<text class="cost-value">¥ {{ costDetail.deductFee.toFixed(2) }}</text>
</view>
<view class="cost-sub-item">
<text class="cost-sub-label">平台服务费</text>
<text class="cost-sub-value">¥ {{ costDetail.platformFee.toFixed(2) }}</text>
</view>
</view>
<!-- 实际收入 -->
<view class="actual-income">
<text class="actual-income-label">实际收入</text>
<text class="actual-income-value">¥ {{ costDetail.actualIncome.toFixed(2) }}</text>
</view>
</scroll-view>
</view>
</wd-popup>
<!-- 消息提示框 -->
<wd-message-box selector="wd-message-box-slot" />
<!-- 导航栏 -->
<navbar title="待接单" custom-class="!bg-[#F6F7F8]"></navbar>
<view class="mx-30rpx">
<view class="h-374rpx bg-white rounded-16rpx">
<!-- 订单卡片 -->
<view class="order-card relative">
<view class="price-btn" @click.stop="handlePriceClick">
<price-format color="#FFFFFF" :first-size="32" :second-size="32" :subscript-size="24"
:price="orderData.price" />
<wd-icon name="arrow-right" size="24rpx" color="#FFFFFF" class="ml-8rpx" />
</view>
<view class="order-card-header">
<view class="order-icon-wrapper ">
<wd-img width="40rpx" height="40rpx" :src="`${OSS}images/chayishi/order-icon.png`"
mode="aspectFill" />
</view>
<text class="order-card-title ml-10rpx">订单</text>
</view>
<view class="order-card-content ml-50rpx">
<text class="service-name">{{ orderData.serviceName }}</text>
</view>
</view>
<view class="border-2rpx border-dashed border-[#E5E5E5]"></view>
<!-- 预约信息 -->
<view class="px-30rpx pt-20rpx pb-40rpx">
<view class="info-card-title">
预约信息
</view>
<view class="info-item">
<text class="info-label">预约时间:</text>
<text class="info-value">{{ orderData.appointmentTimeFull }}</text>
</view>
<view class="info-item">
<text class="info-label">预约时长:</text>
<text class="info-value">{{ orderData.duration }}</text>
</view>
</view>
</view>
<!-- 茶艺服务 -->
<view class="info-card">
<view class="info-card-title">
茶艺服务
</view>
<view class="info-item">
<text class="info-label">服务人数</text>
<text class="info-value">{{ orderData.servicePeople }}</text>
</view>
<view class="info-item">
<text class="info-label">预定茶叶</text>
<text class="info-value">{{ orderData.teaName }}</text>
</view>
<view class="info-item">
<text class="info-label">茶具使用</text>
<text class="info-value">{{ orderData.teawareUsage }}</text>
</view>
</view>
<!-- TODO 订单备注有内容在显示 -->
<view class="bg-[#FFFDF7] px-30rpx py-20rpx rounded-16rpx mt-20rpx" @click="showOrderMarksPopup = true">
<view class="info-card-title">
<wd-img :src="`${OSS}icon/icon_tips2.png`" width="40rpx" height="40rpx"></wd-img>
<view class="ml-12rpx">订单备注</view>
</view>
<view class="flex items-start">
<text class="text-28rpx leading-40rpx text-[#606266]">备注信息</text>
<text class="text-28rpx text-[#303133] ml-50rpx line-2 w-450rpx">12311231231231231221231123123123123122123112312312312312212311231231231231221231123123123123122123112312312312312212311231231231231221231123123123123122123112312312312312212311231231231231221231123123123123122</text>
</view>
</view>
<!-- 服务方式 -->
<view class="info-card">
<view class="info-card-title">
服务方式
</view>
<view class="info-item flex items-center">
<view class="info-label">服务方式</view>
<view class="info-value">{{ orderData.serviceType }}</view>
</view>
<view class="info-item flex items-center">
<view class="info-label mt-12rpx">服务地址</view>
<view class="address-wrapper flex items-center">
<view class="info-value w-200rpx line-1">{{ orderData.address }}</view>
<wd-img :src="`${OSS}icon/icon_nav.png`" width="64rpx" height="64rpx" @click.stop="handleNavigate"></wd-img>
</view>
</view>
</view>
<!-- 订单信息 -->
<view class="info-card">
<view class="info-card-title">
订单信息
</view>
<view class="info-item flex justify-between items-center">
<text class="info-label">订单编号</text>
<view class="order-sn-wrapper">
<text class="info-value">{{ orderData.orderSn }}</text>
<text class="copy-text" @click.stop="handleCopyOrderSn">|复制</text>
</view>
</view>
<view class="info-item flex items-center justify-between">
<view class="info-label">交易方式</view>
<view class="info-value text-right">{{ orderData.paymentMethod }}</view>
</view>
<view class="info-item flex items-center justify-between">
<view class="info-label">创建时间</view>
<view class="info-value text-right">{{ orderData.createTime }}</view>
</view>
<view class="info-item flex items-center justify-between">
<view class="info-label">付款时间</view>
<view class="info-value text-right">{{ orderData.payTime }}</view>
</view>
</view>
<!-- 底部占位避免内容被按钮遮挡 -->
<view class="bottom-placeholder" />
</view>
<view class="w-full fixed bottom-0 left-0 right-0 bg-white h-152rpx flex justify-between items-center px-58rpx">
<view class="w-112rpx h-90rpx text-[#606266] text-28rpx leading-90rpx">放弃接单</view>
<view class="w-360rpx h-90rpx bg-[#4C9F44] rounded-8rpx text-[#fff] text-center leading-90rpx">立即接单</view>
</view>
<!-- 底部操作按钮 -->
<!-- <view class="bottom-actions" @click.stop>
<wd-button type="default" plain
custom-class="!border-[#dcdfe6] !text-[#909399] !bg-[#F6F7F8] !border-2rpx !text-32rpx !px-32rpx !h-88rpx !leading-88rpx !rounded-8rpx !flex-1"
@click.stop="handleDecline">
放弃接单
</wd-button>
<wd-button type="primary"
custom-class="!bg-[#4C9F44] !text-[#fff] !text-32rpx !px-32rpx !h-88rpx !leading-88rpx !rounded-8rpx !flex-1 !ml-20rpx"
@click.stop="handleAccept">
立即接单
</wd-button>
</view> -->
</view>
</template>
<script lang="ts" setup>
import { useMessage } from 'wot-design-uni'
import PriceFormat from '@/components/PriceFormat.vue'
import { copy } from '@/utils/tools'
const OSS = inject('OSS')
// 订单备注
const showOrderMarksPopup = ref<boolean>(false)
// 消息提示框
const message = useMessage('wd-message-box-slot')
// 费用明细弹窗
const showCostDetailPopup = ref(false)
// 费用明细数据
const costDetail = ref({
orderTotal: 828.90,
serviceFee: 640.00,
serviceFeePerHour: 160,
serviceHours: 4,
travelFee: 30.90,
travelFeePerKm: 3.00,
distance: 10.3,
teaServiceFee: 178.00,
teaName: '红茶/绿茶/福鼎白茶/铁观音',
teaPrice: 158,
teawarePrice: 20,
discount: 20.00,
couponDiscount: 20,
deductFee: 130.00,
platformFee: 130.00,
actualIncome: 698.90,
})
// 订单数据
const orderData = ref({
id: 1,
serviceName: '苓苑共享茶室空间',
serviceType: '到店服务',
price: 212.20,
appointmentTimeFull: '2025-03-18 09:00-12:00',
duration: '3小时',
servicePeople: 1,
teaName: '福鼎白茶 (3泡)',
teawareUsage: '客户自备',
notes: '', // 如果为空则不显示备注卡片
address: '青浦区仓桥路478号',
orderSn: '7327328627526903',
paymentMethod: '微信支付',
createTime: '2019-05-16 12:20:26',
payTime: '2019-05-16 13:20:26',
// 倒计时相关(秒)
countdownSeconds: 9 * 3600 + 30 * 60 + 48, // 09:30:48
})
// 倒计时
const countdownTime = ref('09:30:48')
let countdownTimer: any = null
// 格式化倒计时
function formatCountdown(seconds: number) {
const hours = Math.floor(seconds / 3600)
const minutes = Math.floor((seconds % 3600) / 60)
const secs = seconds % 60
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(secs).padStart(2, '0')}`
}
// 开始倒计时
function startCountdown() {
if (countdownTimer) {
clearInterval(countdownTimer)
}
countdownTimer = setInterval(() => {
if (orderData.value.countdownSeconds > 0) {
orderData.value.countdownSeconds--
countdownTime.value = formatCountdown(orderData.value.countdownSeconds)
}
else {
clearInterval(countdownTimer)
// 倒计时结束,可以触发自动取消逻辑
console.log('订单自动取消')
}
}, 1000)
}
// 复制订单编号
function handleCopyOrderSn() {
copy(orderData.value.orderSn)
uni.showToast({
title: '已复制',
icon: 'success',
})
}
// 导航
function handleNavigate() {
// TODO: 实现导航功能
console.log('导航到:', orderData.value.address)
uni.showToast({
title: '导航功能待实现',
icon: 'none',
})
}
// 价格点击(查看详情)
function handlePriceClick() {
showCostDetailPopup.value = true
}
// 放弃接单
function handleDecline() {
message.confirm({
title: '确定放弃接单?',
msg: '放弃接单后无法恢复,确认放弃吗?',
confirmButtonText: '确定',
cancelButtonText: '取消',
cancelButtonProps: {
customClass: '!bg-[#F6F7F8] !text-[#303133] !text-32rpx !leading-44rpx !rounded-8rpx',
},
confirmButtonProps: {
customClass: '!bg-[#4C9F44] !text-[#fff] !text-32rpx !leading-44rpx !rounded-8rpx',
},
}).then((res) => {
if (res.action === 'confirm') {
// TODO: 调用放弃接单的 API
console.log('确认放弃接单')
uni.showToast({
title: '放弃接单成功',
icon: 'success',
})
// 可以返回上一页
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
}).catch(() => {
console.log('取消放弃接单')
})
}
// 立即接单
function handleAccept() {
// TODO: 调用接单的 API
console.log('立即接单')
uni.showToast({
title: '接单成功',
icon: 'success',
})
// 可以返回上一页
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
onLoad((args) => {
// 从路由参数获取订单ID
if (args.id) {
// TODO: 根据ID获取订单详情
console.log('订单ID:', args.id)
}
// 初始化倒计时
countdownTime.value = formatCountdown(orderData.value.countdownSeconds)
startCountdown()
})
onUnload(() => {
// 清理倒计时
if (countdownTimer) {
clearInterval(countdownTimer)
}
})
</script>
<style lang="scss">
page {
background-color: $cz-page-background;
height: 100%;
}
.navbar-right-icons {
display: flex;
align-items: center;
}
.content-scroll {
flex: 1;
padding: 0 30rpx;
}
.countdown-tip {
display: flex;
align-items: center;
justify-content: center;
padding: 24rpx 0;
font-size: 28rpx;
color: #303133;
}
.countdown-text {
font-size: 28rpx;
color: #303133;
}
.countdown-time {
font-size: 32rpx;
font-weight: 500;
color: #ff5951;
margin: 0 8rpx;
}
.order-card {
// background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-top: 20rpx;
}
.order-card-header {
display: flex;
align-items: center;
margin-bottom: 24rpx;
}
.order-icon-wrapper {
position: relative;
width: 28rpx;
height: 28rpx;
margin-right: 8rpx;
}
.order-icon-circle {
width: 20rpx;
height: 20rpx;
border: 2rpx solid #4c9f44;
border-radius: 50%;
position: absolute;
top: 0;
left: 0;
}
.order-icon-line {
width: 2rpx;
height: 12rpx;
background-color: #4c9f44;
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
}
.order-card-title {
font-size: 28rpx;
color: #303133;
font-weight: 500;
}
.order-card-content {
display: flex;
align-items: center;
}
.service-name {
font-size: 30rpx;
color: #303133;
font-weight: 500;
flex: 1;
}
.price-btn {
position: absolute;
right: 0rpx;
top: 0rpx;
width: 200rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #ff5951;
padding: 12rpx 24rpx;
border-radius: 8rpx;
box-shadow: 0 2rpx 8rpx rgba(255, 89, 81, 0.3);
min-width: 140rpx;
}
.info-card {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-top: 20rpx;
}
.info-card-title {
font-size: 30rpx;
color: #303133;
font-weight: 500;
margin-bottom: 24rpx;
display: flex;
align-items: center;
}
.info-item {
display: flex;
align-items: flex-start;
margin-bottom: 20rpx;
font-size: 28rpx;
line-height: 40rpx;
&:last-child {
margin-bottom: 0;
}
}
.info-label {
color: #909399;
margin-right: 16rpx;
white-space: nowrap;
}
.info-value {
color: #606266;
flex: 1;
}
.address-wrapper {
display: flex;
align-items: center;
flex: 1;
}
.order-sn-wrapper {
display: flex;
align-items: center;
}
.copy-text {
color: #4c9f44;
margin-left: 8rpx;
}
.bottom-placeholder {
height: 120rpx;
}
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
padding: 20rpx 30rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
display: flex;
align-items: center;
box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.05);
z-index: 100;
}
// 当弹窗打开时,降低底部按钮的 z-index确保被弹窗遮盖
.pending-order-detail-page.popup-open .bottom-actions {
z-index: 50;
}
// 费用明细弹窗样式
.cost-detail-popup {
position: relative;
background-color: #fff;
border-radius: 32rpx 32rpx 0 0;
max-height: 80vh;
display: flex;
flex-direction: column;
z-index: 201;
}
.close-btn {
position: absolute;
top: 18rpx;
right: 30rpx;
z-index: 10;
padding: 10rpx;
}
.popup-title {
text-align: center;
font-size: 36rpx;
font-weight: 500;
color: #121212;
line-height: 50rpx;
padding: 50rpx 0 40rpx;
}
.cost-content {
flex: 1;
padding: 0 30rpx 30rpx;
max-height: calc(80vh - 140rpx);
}
.cost-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.cost-label {
font-size: 30rpx;
font-weight: 500;
color: #303133;
line-height: 42rpx;
}
.cost-value {
font-size: 30rpx;
font-weight: 500;
color: #303133;
line-height: 42rpx;
&.discount {
color: #4c9f44;
}
}
.cost-sub-item {
display: flex;
justify-content: space-between;
align-items: center;
padding-left: 20rpx;
margin-bottom: 16rpx;
}
.cost-sub-label {
font-size: 28rpx;
font-weight: 400;
color: #909399;
line-height: 40rpx;
}
.cost-sub-value {
font-size: 28rpx;
font-weight: 400;
color: #303133;
line-height: 40rpx;
&.discount {
color: #4c9f44;
}
}
.cost-divider {
height: 2rpx;
background-color: #f6f7f9;
margin: 30rpx 0;
}
.deduct-section {
background-color: #f6f7f9;
border-radius: 16rpx;
padding: 20rpx;
margin: 20rpx 0;
}
.actual-income {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 40rpx;
padding-top: 30rpx;
}
.actual-income-label {
font-size: 30rpx;
font-weight: 500;
color: #303133;
line-height: 42rpx;
}
.actual-income-value {
font-size: 30rpx;
font-weight: 500;
color: #303133;
line-height: 42rpx;
}
.line-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
word-break: break-all;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -1,806 +0,0 @@
<route lang="jsonc" type="page">
{
"needLogin": false,
"layout": "default",
"style": {
"navigationStyle": "custom"
}
}
</route>
<script lang="ts" setup>
import { useMessage } from 'wot-design-uni'
import PriceFormat from '@/components/PriceFormat.vue'
import { copy, router } from '@/utils/tools'
const OSS = inject('OSS')
// 消息提示框
const message = useMessage('wd-message-box-slot')
// 费用明细弹窗
const showCostDetailPopup = ref(false)
// 费用明细数据
const costDetail = ref({
orderTotal: 828.90,
serviceFee: 640.00,
serviceFeePerHour: 160,
serviceHours: 4,
travelFee: 30.90,
travelFeePerKm: 3.00,
distance: 10.3,
teaServiceFee: 178.00,
teaName: '红茶/绿茶/福鼎白茶/铁观音',
teaPrice: 158,
teawarePrice: 20,
discount: 20.00,
couponDiscount: 20,
deductFee: 130.00,
platformFee: 130.00,
actualIncome: 698.90,
})
// 订单状态
const orderStatus = ref('waiting') // waiting 或 waiting_arrived
// 订单数据
const orderData = ref({
id: 2,
serviceName: '苓苑共享茶室空间',
serviceType: '到店服务',
price: 212.20,
appointmentTimeFull: '2025-03-18 09:00-12:00',
duration: '3小时',
servicePeople: 1,
teaName: '福鼎白茶 (3泡)',
teawareUsage: '客户自备',
notes: '这里是客户留言信息部分,客户未留言,隐藏这块信息', // 如果有备注则显示
address: '青浦区仓桥路478号',
latitude: 31.2304, // 纬度
longitude: 121.4737, // 经度
distance: 5, // 距离(公里)
estimatedTime: 20, // 预计时间(分钟)
orderSn: '7327328627526903',
paymentMethod: '微信支付',
createTime: '2019-05-16 12:20:26',
payTime: '2019-05-16 13:20:26',
})
// 价格点击(查看详情)
function handlePriceClick() {
showCostDetailPopup.value = true
}
// 复制订单编号
function handleCopyOrderSn() {
copy(orderData.value.orderSn)
uni.showToast({
title: '已复制',
icon: 'success',
})
}
// 导航
function handleNavigate() {
if (orderData.value.latitude && orderData.value.longitude) {
uni.openLocation({
latitude: orderData.value.latitude,
longitude: orderData.value.longitude,
name: orderData.value.serviceName,
address: orderData.value.address,
})
}
}
// 查看订单记录
function handleViewOrderRecord() {
// TODO: 跳转到订单记录页面
console.log('查看订单记录')
uni.showToast({
title: '订单记录功能待实现',
icon: 'none',
})
}
// 立即出发/已到达
function handleDepart() {
if (orderStatus.value === 'waiting') {
// TODO: 调用出发的 API
console.log('立即出发')
uni.showToast({
title: '出发成功',
icon: 'success',
})
// 可以返回上一页或跳转到服务中页面
setTimeout(() => {
uni.navigateBack()
}, 1500)
}
else if (orderStatus.value === 'waiting_arrived') {
// 弹出确认到达对话框
message.confirm({
title: '确定到达',
msg: '确认到达后需拍照记录,否则无法开始服务计时',
confirmButtonText: '去拍照',
cancelButtonText: '取消',
cancelButtonProps: {
customClass: '!bg-[#F6F7F8] !text-[#303133] !text-32rpx !leading-44rpx !rounded-8rpx',
},
confirmButtonProps: {
customClass: '!bg-[#4C9F44] !text-[#fff] !text-32rpx !leading-44rpx !rounded-8rpx',
},
}).then((res) => {
if (res.action === 'confirm') {
// 调用拍照功能
handleTakePhoto()
}
}).catch(() => {
// 点击取消按钮
console.log('取消到达确认')
})
}
}
// 拍照功能
function handleTakePhoto() {
// #ifdef MP-WEIXIN
// 微信小程序使用 chooseMedia
uni.chooseMedia({
count: 1,
mediaType: ['image'],
sourceType: ['camera'], // 只使用相机拍照
success: (res) => {
const file = res.tempFiles[0]
if (file) {
console.log('拍照成功:', file.tempFilePath)
// TODO: 上传照片并调用已到达的 API
// 这里可以上传照片到服务器
uni.showToast({
title: '拍照成功',
icon: 'success',
})
// 跳转到服务中页面,传递照片路径
setTimeout(() => {
router.navigateTo(`/pages/order/serving-detail?id=${orderData.value.id}&photoPath=${encodeURIComponent(file.tempFilePath)}`)
}, 1500)
}
},
fail: (err) => {
console.error('拍照失败:', err)
if (err.errMsg && !err.errMsg.includes('cancel')) {
uni.showToast({
title: '拍照失败',
icon: 'none',
})
}
},
})
// #endif
// #ifndef MP-WEIXIN
// 非微信小程序使用 chooseImage
uni.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['camera'], // 只使用相机拍照
success: (res) => {
if (res.tempFilePaths && res.tempFilePaths.length > 0) {
console.log('拍照成功:', res.tempFilePaths[0])
// TODO: 上传照片并调用已到达的 API
// 这里可以上传照片到服务器
uni.showToast({
title: '拍照成功',
icon: 'success',
})
// 跳转到服务中页面,传递照片路径
setTimeout(() => {
router.navigateTo(`/pages/order/serving-detail?id=${orderData.value.id}&photoPath=${encodeURIComponent(res.tempFilePaths[0])}`)
}, 1500)
}
},
fail: (err) => {
console.error('拍照失败:', err)
if (err.errMsg && !err.errMsg.includes('cancel')) {
uni.showToast({
title: '拍照失败',
icon: 'none',
})
}
},
})
// #endif
}
onLoad((args) => {
console.log('args', args)
// 从路由参数获取订单ID和状态
if (args.id) {
// TODO: 根据ID获取订单详情
console.log('订单ID:', args.id)
}
if (args.status) {
orderStatus.value = args.status
console.log('订单状态:', args.status)
}
})
</script>
<template>
<view class="waiting-service-detail-page" :class="{ 'popup-open': showCostDetailPopup }">
<!-- 消息提示框 -->
<wd-message-box selector="wd-message-box-slot" />
<!-- 导航栏 -->
<navbar title="待服务" custom-class="!bg-[#F6F7F8]" />
<scroll-view class="content-scroll" scroll-y>
<!-- 提示文字 -->
<view class="tip-text">
请务必提前确认服务日程,妥善安排好时间
</view>
<!-- 订单卡片 -->
<view style="position: relative;" class="order-card">
<view class="price-btn" @click.stop="handlePriceClick">
<price-format
color="#FFFFFF" :first-size="32" :second-size="32" :subscript-size="24"
:price="orderData.price"
/>
<wd-icon name="arrow-right" size="24rpx" color="#FFFFFF" class="ml-8rpx" />
</view>
<view class="order-card-header">
<view class="order-icon-wrapper">
<wd-img
width="28rpx" height="28rpx" :src="`${OSS}images/chayishi/order-icon.png`"
mode="aspectFill"
/>
</view>
<text class="order-card-title">订单</text>
</view>
<view class="order-card-content">
<text class="service-name">{{ orderData.serviceName }}</text>
</view>
<!-- 预约信息 -->
<view class="info-section">
<view class="section-title">
预约信息
</view>
<view class="info-item">
<text class="info-label">预约时间:</text>
<text class="info-value">{{ orderData.appointmentTimeFull }}</text>
</view>
<view class="info-item">
<text class="info-label">预约时长:</text>
<text class="info-value">{{ orderData.duration }}</text>
</view>
</view>
<!-- 茶艺服务 -->
<view class="info-section">
<view class="section-title">
茶艺服务
</view>
<view class="info-item">
<text class="info-label">服务人数</text>
<text class="info-value">{{ orderData.servicePeople }}</text>
</view>
<view class="info-item">
<text class="info-label">预定茶叶</text>
<text class="info-value">{{ orderData.teaName }}</text>
</view>
<view class="info-item">
<text class="info-label">茶具使用</text>
<text class="info-value">{{ orderData.teawareUsage }}</text>
</view>
</view>
</view>
<!-- 订单备注 -->
<view v-if="orderData.notes" class="info-card">
<view class="info-card-title">
<wd-icon name="chat" size="32rpx" color="#303133" class="mr-8rpx" />
订单备注
</view>
<view class="info-item">
<text class="info-label">备注信息</text>
<text class="info-value">{{ orderData.notes }}</text>
</view>
</view>
<!-- 服务方式 -->
<view class="info-card">
<view class="info-card-title">
服务方式
</view>
<view class="info-item">
<text class="info-label">服务方式</text>
<text class="info-value">{{ orderData.serviceType }}</text>
</view>
<view class="info-item">
<text class="info-label">服务地址</text>
<view class="address-wrapper">
<text class="info-value">{{ orderData.address }}</text>
<wd-icon name="location" size="32rpx" color="#4C9F44" class="ml-16rpx" @click.stop="handleNavigate" />
</view>
</view>
</view>
<!-- 订单记录 -->
<view class="info-card order-record-card" @click="handleViewOrderRecord">
<view class="order-record-wrapper">
<text class="order-record-text">订单记录</text>
<wd-icon name="arrow-right" size="24rpx" color="#909399" />
</view>
</view>
<!-- 订单信息 -->
<view class="info-card">
<view class="info-card-title">
订单信息
</view>
<view class="info-item">
<text class="info-label">订单编号</text>
<view class="order-sn-wrapper">
<text class="info-value">{{ orderData.orderSn }}</text>
<text class="copy-text" @click.stop="handleCopyOrderSn">|复制</text>
</view>
</view>
<view class="info-item">
<text class="info-label">交易方式</text>
<text class="info-value">{{ orderData.paymentMethod }}</text>
</view>
<view class="info-item">
<text class="info-label">创建时间</text>
<text class="info-value">{{ orderData.createTime }}</text>
</view>
<view class="info-item">
<text class="info-label">付款时间</text>
<text class="info-value">{{ orderData.payTime }}</text>
</view>
</view>
<!-- 底部占位避免内容被按钮遮挡 -->
<view class="bottom-placeholder" />
</scroll-view>
<!-- 费用明细弹窗 -->
<wd-popup
v-model="showCostDetailPopup"
lock-scroll
custom-style="border-radius: 32rpx 32rpx 0rpx 0rpx;"
position="bottom"
:z-index="200"
:close-on-click-overlay="true"
>
<view class="cost-detail-popup">
<!-- 关闭按钮 -->
<view class="close-btn" @click="showCostDetailPopup = false">
<wd-icon name="close" size="40rpx" color="#303133" />
</view>
<!-- 标题 -->
<view class="popup-title">
费用明细
</view>
<scroll-view class="cost-content" scroll-y>
<!-- 订单总额 -->
<view class="cost-item">
<text class="cost-label">订单总额</text>
<text class="cost-value">¥ {{ costDetail.orderTotal.toFixed(2) }}</text>
</view>
<!-- 服务费 -->
<view class="cost-item">
<text class="cost-label">服务费</text>
<text class="cost-value">¥ {{ costDetail.serviceFee.toFixed(2) }}</text>
</view>
<view class="cost-sub-item">
<text class="cost-sub-label">服务费 (¥ {{ costDetail.serviceFeePerHour }}/小时)</text>
<text class="cost-sub-value">x{{ costDetail.serviceHours }}</text>
</view>
<!-- 车马费 -->
<view class="cost-item">
<text class="cost-label">车马费</text>
<text class="cost-value">¥ {{ costDetail.travelFee.toFixed(2) }}</text>
</view>
<view class="cost-sub-item">
<text class="cost-sub-label">车马费 (¥ {{ costDetail.travelFeePerKm }}/公里)</text>
<text class="cost-sub-value">{{ costDetail.distance }}公里</text>
</view>
<!-- 茶艺服务 -->
<view class="cost-item">
<text class="cost-label">茶艺服务</text>
<text class="cost-value">¥ {{ costDetail.teaServiceFee.toFixed(2) }}</text>
</view>
<view class="cost-sub-item">
<text class="cost-sub-label">{{ costDetail.teaName }}</text>
<text class="cost-sub-value">¥ {{ costDetail.teaPrice }}</text>
</view>
<view class="cost-sub-item">
<text class="cost-sub-label">茶具使用</text>
<text class="cost-sub-value">¥ {{ costDetail.teawarePrice }}</text>
</view>
<!-- 优惠 -->
<view class="cost-item">
<text class="cost-label">优惠</text>
<text class="cost-value discount">- ¥ {{ costDetail.discount.toFixed(2) }}</text>
</view>
<view class="cost-sub-item">
<text class="cost-sub-label">优惠券</text>
<text class="cost-sub-value discount">- ¥ {{ costDetail.couponDiscount }}</text>
</view>
<!-- 分隔线 -->
<view class="cost-divider" />
<!-- 扣除费用区域 -->
<view class="deduct-section">
<view class="cost-item">
<text class="cost-label">扣除费用</text>
<text class="cost-value">¥ {{ costDetail.deductFee.toFixed(2) }}</text>
</view>
<view class="cost-sub-item">
<text class="cost-sub-label">平台服务费</text>
<text class="cost-sub-value">¥ {{ costDetail.platformFee.toFixed(2) }}</text>
</view>
</view>
<!-- 实际收入 -->
<view class="actual-income">
<text class="actual-income-label">实际收入</text>
<text class="actual-income-value">¥ {{ costDetail.actualIncome.toFixed(2) }}</text>
</view>
</scroll-view>
</view>
</wd-popup>
<!-- 底部操作按钮 -->
<view class="bottom-actions" @click.stop>
<wd-button
type="primary"
custom-class="!bg-[#4C9F44] !text-[#fff] !text-32rpx !px-32rpx !h-88rpx !leading-88rpx !rounded-8rpx !w-full"
@click.stop="handleDepart"
>
{{ orderStatus === 'waiting_arrived' ? '已到达' : '立即出发' }}
</wd-button>
</view>
</view>
</template>
<style lang="scss">
page {
background-color: #f5f5f5;
height: 100%;
}
.waiting-service-detail-page {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f5f5f5;
}
.price-btn {
position: absolute;
right: 30rpx;
top: 0rpx;
width: 200rpx;
display: flex;
align-items: center;
justify-content: center;
background-color: #ff5951;
padding: 12rpx 24rpx;
border-radius: 8rpx;
box-shadow: 0 2rpx 8rpx rgba(255, 89, 81, 0.3);
min-width: 140rpx;
}
.content-scroll {
flex: 1;
padding: 0 30rpx;
}
.tip-text {
font-size: 24rpx;
color: #909399;
line-height: 34rpx;
padding: 20rpx 0;
text-align: center;
}
.order-card {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-top: 20rpx;
}
.order-card-header {
display: flex;
align-items: center;
margin-bottom: 24rpx;
}
.order-icon-wrapper {
position: relative;
width: 28rpx;
height: 28rpx;
margin-right: 8rpx;
}
.order-card-title {
font-size: 28rpx;
color: #303133;
font-weight: 500;
}
.order-card-content {
margin-bottom: 24rpx;
}
.service-name {
font-size: 30rpx;
color: #303133;
font-weight: 500;
}
.info-section {
margin-top: 24rpx;
padding-top: 24rpx;
border-top: 2rpx solid #f6f7f9;
&:first-of-type {
margin-top: 0;
padding-top: 0;
border-top: none;
}
}
.section-title {
font-size: 30rpx;
color: #303133;
font-weight: 500;
margin-bottom: 20rpx;
}
.info-item {
display: flex;
align-items: flex-start;
margin-bottom: 16rpx;
font-size: 28rpx;
line-height: 40rpx;
&:last-child {
margin-bottom: 0;
}
}
.info-label {
color: #909399;
margin-right: 16rpx;
white-space: nowrap;
}
.info-value {
color: #606266;
flex: 1;
}
.info-card {
background-color: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-top: 20rpx;
}
.info-card-title {
font-size: 30rpx;
color: #303133;
font-weight: 500;
margin-bottom: 24rpx;
display: flex;
align-items: center;
}
.address-wrapper {
display: flex;
align-items: center;
flex: 1;
}
.order-sn-wrapper {
display: flex;
align-items: center;
}
.copy-text {
color: #4c9f44;
margin-left: 8rpx;
}
.map-section {
margin-top: 20rpx;
background-color: #fff;
border-radius: 16rpx;
overflow: hidden;
}
.map-container {
width: 100%;
height: 400rpx;
}
.map-info {
padding: 20rpx 30rpx;
background-color: #fff;
}
.map-info-text {
font-size: 24rpx;
color: #909399;
line-height: 34rpx;
}
.order-record-card {
padding: 30rpx;
}
.order-record-wrapper {
display: flex;
align-items: center;
justify-content: space-between;
}
.order-record-text {
font-size: 30rpx;
color: #303133;
font-weight: 500;
}
.bottom-placeholder {
height: 120rpx;
}
.bottom-actions {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #fff;
padding: 20rpx 30rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
box-shadow: 0 -2rpx 8rpx rgba(0, 0, 0, 0.05);
z-index: 100;
}
// 当弹窗打开时,降低底部按钮的 z-index确保被弹窗遮盖
.waiting-service-detail-page.popup-open .bottom-actions {
z-index: 50;
}
// 费用明细弹窗样式
.cost-detail-popup {
position: relative;
background-color: #fff;
border-radius: 32rpx 32rpx 0 0;
max-height: 80vh;
display: flex;
flex-direction: column;
z-index: 201;
}
.close-btn {
position: absolute;
top: 18rpx;
right: 30rpx;
z-index: 10;
padding: 10rpx;
}
.popup-title {
text-align: center;
font-size: 36rpx;
font-weight: 500;
color: #121212;
line-height: 50rpx;
padding: 50rpx 0 40rpx;
}
.cost-content {
flex: 1;
padding: 0 30rpx 30rpx;
max-height: calc(80vh - 140rpx);
}
.cost-item {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20rpx;
}
.cost-label {
font-size: 30rpx;
font-weight: 500;
color: #303133;
line-height: 42rpx;
}
.cost-value {
font-size: 30rpx;
font-weight: 500;
color: #303133;
line-height: 42rpx;
&.discount {
color: #4c9f44;
}
}
.cost-sub-item {
display: flex;
justify-content: space-between;
align-items: center;
padding-left: 20rpx;
margin-bottom: 16rpx;
}
.cost-sub-label {
font-size: 28rpx;
font-weight: 400;
color: #909399;
line-height: 40rpx;
}
.cost-sub-value {
font-size: 28rpx;
font-weight: 400;
color: #303133;
line-height: 40rpx;
&.discount {
color: #4c9f44;
}
}
.cost-divider {
height: 2rpx;
background-color: #f6f7f9;
margin: 30rpx 0;
}
.deduct-section {
background-color: #f6f7f9;
border-radius: 16rpx;
padding: 20rpx;
margin: 20rpx 0;
}
.actual-income {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 40rpx;
padding-top: 30rpx;
}
.actual-income-label {
font-size: 30rpx;
font-weight: 500;
color: #303133;
line-height: 42rpx;
}
.actual-income-value {
font-size: 30rpx;
font-weight: 500;
color: #303133;
line-height: 42rpx;
}
</style>

View File

@ -1,186 +0,0 @@
<route lang="jsonc" type="page">{
"layout": "default",
"style": {
"navigationStyle": "custom"
}
}</route>
<template>
<view class="pb-40rpx">
<view>
<navbar title="设备控制" custom-class='!bg-[#F6F7F8]'></navbar>
</view>
<view class="mt-50rpx mx-40rpx">
<view>
<view class="font-bold text-36rpx leading-50rpx text-[#303133]">大门</view>
<view class="relative mt-30rpx">
<wd-img width="670rpx" height="202rpx" :src="`${OSS}images/store/store/image1.png`" mode="aspectFill" />
<view class="absolute top-64rpx left-0 px-46rpx flex items-center justify-between w-full">
<view class="flex items-center">
<wd-img width="96rpx" height="96rpx" :src="`${OSS}images/store/store/image4.png`" mode="aspectFill" />
<view class="font-bold text-34rpx leading-48rpx text-[#303133] ml-32rpx">门锁</view>
</view>
<view class="relative h-64rpx">
<wd-img width="224rpx" height="64rpx" :src="`${OSS}images/reserve_room/reserve_room_image5.png`"/>
<view
class="text-[#4C9F44] font-bold text-32rpx leading-44rpx absolute top-[50%] transform translate-y-[-50%] left-74rpx"
v-if="device.is_lock == 1" @click="Device.handleOpenLock('door')">
点击开锁
</view>
<view
class="text-[#4C9F44] font-bold text-32rpx leading-44rpx absolute top-[50%] transform translate-y-[-50%] left-74rpx"
v-if="device.is_lock == 0">
暂无门锁
</view>
</view>
</view>
</view>
</view>
<view class="mt-30rpx">
<view class="mb-40rpx" v-for="(item, index) in device.roomDevice" :key="index">
<view class="font-bold text-36rpx leading-50rpx text-[#303133] mb-30rpx">{{ item.title }}</view>
<view class="flex items-center justify-between">
<view class="w-240rpx h-280rpx bg-white rounded-32rpx flex flex-col items-center justify-center">
<view class="">
<wd-img width="90rpx" height="90rpx" :src="`${OSS}images/store/store/image2.png`" mode="aspectFill" />
</view>
<view class="font-bold text-34rpx text-[#303133] leading-48rpx">门锁</view>
<view class="bg-[#4C9F44] rounded-20rpx w-168rpx h-60rpx text-center leading-60rpx text-[#fff] mt-10rpx"
@click="Device.handleOpenLock('room', item)">
开锁
</view>
</view>
<view class="w-410rpx h-280rpx bg-white rounded-16rpx pl-60rpx pr-34rpx">
<view class="flex items-top justify-between px-20rpx py-4rpx pt-40rpx">
<wd-img width="90rpx" height="90rpx" :src="`${OSS}images/store/store/image3.png`" mode="aspectFill" />
<view class="text-28rpx leading-40rpx text-[#303133] mt-12rpx">{{ item.is_open ? 'OFF' : 'ON' }}</view>
</view>
<view class="font-bold text-28rpx text-[#303133] leading-40rpx mt-34rpx flex items-center justify-between">
<view class="font-bold text-34rpx leading-48rpx text-[#303133]">插座空开</view>
<wd-switch v-model="item.is_open" size="48rpx" active-color="#4C9F44" @change="Device.handleToggleSocket($event, item)"/>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { getDeviceList, openLock, openPower } from '@/api/store'
import { router } from '@/utils/tools'
import { useStoreStore } from '@/store'
import { toast } from '@/utils/toast'
const OSS = inject('OSS')
const useStore = useStoreStore()
// 设备
const device = ref({
store_id: useStore.defaultStore.id,
is_lock: 0, //是否存在大门门锁 0:不存在 1:存在
lock_no: '', // 门锁编号
roomDevice: [
{
room_id: 0, // 包间ID
title: '', // 包间名称
lock_no: '', // 门锁编号
is_open: false, //空开状态:后端返回的是 0关闭 1打开前端转换为数字
}
]
})
onLoad((args) => {
Device.handleGetDeviceList()
})
onUnload(() => {
})
const Device = {
/**
* 初始化设备列表
*/
handleGetDeviceList: async () => {
const res = await getDeviceList(useStore.defaultStore.id)
device.value = res
device.value.roomDevice.map((item) => {
item.is_open = Boolean(item.is_open)
})
},
/**
* 开门锁
* @params type 类型 door:大门门锁 room:包间门锁
* @params item 设备项
*/
handleOpenLock: async (type: string, item: any = null) => {
let params = {
type: 0,
lock_no: '',
store_id: device.value.store_id,
room_id: 0
}
if (type === 'door') {
params.type = 1
params.lock_no = device.value.lock_no
} else if (type === 'room') {
params.type = 2
params.lock_no = item.lock_no
params.room_id = item.room_id
}
uni.showLoading({
title: '开锁中...',
mask: true,
})
try {
uni.hideLoading
await openLock(params)
Device.handleGetDeviceList()
toast.success('开锁成功')
} catch(e) {
uni.hideLoading
toast.info('开锁失败,请稍后重试')
}
},
/**
* 插座空开
* @params event 事件参数
* @params item 设备项
*/
handleToggleSocket: async (event: {value: boolean}, item: any) => {
let params = {
device_id: item.device_id,
state: Number(event.value),
room_id: item.room_id
}
uni.showLoading({
title: '操作中...',
mask: true,
})
try {
uni.hideLoading
await openPower(params)
Device.handleGetDeviceList()
toast.success('操作成功')
} catch(e) {
uni.hideLoading
toast.info('操作失败,请稍后重试')
}
}
}
</script>
<style lang="scss">
page {
background-color: $cz-page-background;
}
</style>

View File

@ -1,179 +0,0 @@
<route lang="jsonc" type="page">{
"layout": "default",
"style": {
"navigationStyle": "custom"
}
}</route>
<template>
<view class="pb-40rpx">
<view>
<navbar title="包间预定" custom-class='!bg-[#F6F7F8]'></navbar>
</view>
<view class="mt-38rpx bg-white rounded-16rpx py-22rpx px-30rpx mx-30rpx">
<view class="">
<view class="text-26rpx leading-32rpx text-[#606266] mb-20rpx">包厢选择</view>
<view class="">
<wd-picker :columns="columns" value-key="label" value-name="id" v-model="roomName" use-default-slot @confirm="Reserve.handleConfirmRoom">
<wd-input type="text" v-model="roomName" placeholder="请选择包厢" readonly>
<template #suffix>
<wd-icon name="arrow-down" size="32rpx" color="#D8D8D8"></wd-icon>
</template>
</wd-input>
</wd-picker>
</view>
</view>
<!-- <view class="mt-22rpx">
<view class="text-26rpx leading-32rpx text-[#606266] mb-20rpx">包厢价格</view>
<view class="">
<wd-input type="text" v-model="form.price" placeholder="请输入包厢价格">
<template #prefix>
<text class=""></text>
</template>
</wd-input>
</view>
</view> -->
<view class="mt-22rpx">
<view class="text-26rpx leading-32rpx text-[#606266] mb-20rpx">预定时间</view>
<view class="" @click="showBookTimePopup = true">
<wd-input type="text" v-model="displayTime" placeholder="请选择预定时间" readonly no-border>
<template #suffix>
<wd-icon name="arrow-down" size="32rpx" color="#D8D8D8"></wd-icon>
</template>
</wd-input>
</view>
</view>
</view>
<view class="bg-[#4C9F44] w-630rpx h-90rpx text-[#fff] text-center leading-90rpx rounded-8rpx mx-auto mt-50rpx" @click="Reserve.handleConfirm">确定</view>
<!-- 选择预定时间 -->
<booking-time v-model="showBookTimePopup" :day="sevenDay" @selectedTime="Reserve.handleChooseReserveTime"></booking-time>
</view>
</template>
<script lang="ts" setup>
import { router, getCurrentDate } from '@/utils/tools'
import type { ITeaSpecialistFuture7DaysResult } from '@/api/types/tea'
import { getNext7Days, getTeaRoomDetail } from '@/api/tea-room'
import { getAllRoomList, reserveRoom, getRoomDetails } from '@/api/store'
import { useStoreStore } from '@/store'
import { toast } from '@/utils/toast'
const useStore = useStoreStore()
const columns = ref<Array<{label: string, id: number}>>([])
// 选择预定时间
const showBookTimePopup = ref<boolean>(false)
const sevenDay = reactive<ITeaSpecialistFuture7DaysResult>({
minimum_time: 0,
time: []
})
const reserveTime = ref<any>({})
const dayTitle = ref<string>('') // 周三03/18
const dayTime = ref<string>('') // 2024-03-18
const timeSlots = ref<Array<string>>([]) // 连续选择的预约时间
const dayHours = ref<string>('') // 预定时长00:00,00:30
const displayTime = ref<string>('') // 2024-03-18,00:00,00:30
const form = ref({
room_id: 0, // 包间ID
hours: 0, // 预定小时数
timeslot: '', // 预定时间段
day_time: '', // 预定日期
day_title: '', // 预定日期标题
})
const roomName = ref<string>('')
onLoad(async (args) => {
// 获取包厢列表
const res = await getAllRoomList(useStore.defaultStore.id)
columns.value = res.list.map((item: any) => ({
label: item.title,
id: item.id
}))
})
onUnload(() => {
})
const Reserve = {
/**
* 选中包厢
* @param params
*/
handleConfirmRoom: async (item) => {
form.value.room_id = item.selectedItems.id
const res = await getRoomDetails(form.value.room_id)
// 预定时间
const next7 = await getNext7Days(form.value.room_id, getCurrentDate())
Object.assign(sevenDay, next7.data)
Object.assign(sevenDay, {minimum_time: res.details.room.hours})
},
/**
* 选中预定时间
*/
handleChooseReserveTime: (params: any) => {
reserveTime.value = params
timeSlots.value = params.selectedTimestamps
dayTitle.value = params.dayTitle
dayTime.value = params.dayTime
const times = params.selectedTime.map(item => {
return item.time
}).join(',')
dayHours.value = times
displayTime.value = params.dayTime + ',' + times
},
/**
* 确认预定
*/
handleConfirm: async () => {
if (!form.value.room_id) {
toast.info('请选择包厢')
return
}
if (reserveTime.value.length === 0) {
toast.info('请选择预定时间')
return
}
form.value.hours = reserveTime.value.countSelectedTime
form.value.timeslot = timeSlots.value.join(',')
form.value.day_time = reserveTime.value.dayTime
form.value.day_title = dayTitle.value,
uni.showLoading({
title: '操作中...',
mask: true
})
try {
await reserveRoom(form.value)
uni.hideLoading()
toast.info('预定成功')
router.navigateBack(1, 500)
} catch (error) {
uni.hideLoading()
return
}
}
}
</script>
<style lang="scss">
page {
background-color: $cz-page-background;
}
</style>

View File

@ -1,779 +0,0 @@
<route lang="jsonc" type="page">{
"layout": "default",
"style": {
"navigationStyle": "custom"
}
}</route>
<template>
<view class="pb-180rpx">
<view>
<navbar title="修改包间信息" custom-class="!bg-[#fff]" />
</view>
<view class="store-tabs">
<wd-tabs v-model="tab" @change="RoomDetail.handleChangeTab">
<wd-tab title="基础信息" />
<wd-tab title="规格与价格" />
<wd-tab title="套餐详情" />
<!-- <wd-tab title="购买须知" /> -->
</wd-tabs>
</view>
<view class="h-64rpx bg-[#FFF6EB] px-30rpx text-26rpx text-[#111827] font-400 leading-64rpx">
"<text class="text-[#ED2D2D]">*</text>"的为必填项
</view>
<view class="mt-20rpx bg-white p-30rpx">
<!-- 基础信息 -->
<view v-if="tab === 0">
<view class="text-34rpx text-[#303133] font-bold leading-48rpx">
基本信息
</view>
<!-- 包间名称 -->
<view class="mt-28rpx add-textarea">
<view class="mb-20rpx flex items-center">
<view class="mr-10rpx text-32rpx text-[#303133] font-bold leading-44rpx">
包间名称
</view>
<view class="flex items-center">
<wd-img width="16rpx" height="16rpx" :src="`${OSS}icon/icon_validate.png`" />
</view>
</view>
<wd-input v-model="form.title" no-border placeholder="请输入包间名称" :maxlength="10" show-word-limit
custom-class="!bg-[#F6F7F8] !rounded-16rpx !px-28rpx !py-20rpx" />
</view>
<!-- 包间标签 -->
<view class="mt-28rpx">
<view class="mb-20rpx flex items-center justify-between">
<view class="flex items-center">
<view class="mr-10rpx text-32rpx text-[#303133] font-bold leading-44rpx">
包间标签
</view>
<view class="flex items-center">
<wd-img width="16rpx" height="16rpx" :src="`${OSS}icon/icon_validate.png`" />
</view>
</view>
<view class="text-24rpx text-[#9CA3AF] font-400 leading-34rpx">
可选择1~2个标签,每个标签不超过5个字
</view>
</view>
<view class="flex flex-wrap items-center gap-16rpx">
<view v-for="(tag, index) in tags" :key="index"
class="flex items-center rounded-20rpx bg-[#F6F7F8] px-20rpx py-8rpx">
<view class="mr-8rpx text-26rpx text-[#303133] leading-36rpx">
{{ tag.label_name }}
</view>
<wd-icon name="close" size="14px" color="#909399"
@click="RoomDetail.handleRemoveTag(index)" />
</view>
<view v-if="form.tags.length < 2"
class="flex items-center border-2rpx border-[#E5E5E5] rounded-20rpx border-dashed px-20rpx py-8rpx"
@click="RoomDetail.handleAddTag">
<wd-icon name="add" size="16px" color="#909399" class="mr-8rpx" />
<view class="text-26rpx text-[#909399] leading-36rpx">
添加标签
</view>
</view>
</view>
</view>
<!-- 团购视频 -->
<!-- <view class="mt-28rpx">
<view class="mb-28rpx flex items-center">
<view class="mr-10rpx text-32rpx text-[#303133] font-bold leading-44rpx">
团购视频
</view>
</view>
<view v-if="form.video" class="relative h-184rpx w-184rpx overflow-hidden rounded-16rpx">
<wd-img width="100%" height="100%" :src="form.video.thumb" mode="aspectFill" />
<view class="absolute inset-0 flex items-center justify-center">
<wd-icon name="play" size="48rpx" color="#fff" />
</view>
<view
class="absolute right-8rpx top-8rpx h-32rpx w-32rpx flex items-center justify-center rounded-full bg-black bg-opacity-50"
@click="RoomDetail.handleRemoveVideo">
<wd-icon name="close" size="14px" color="#fff" />
</view>
</view>
<wd-upload v-else :file-list="[]" :limit="1" image-mode="scaleToFill" accept="video"
:action="action" @change="RoomDetail.handleUploadVideo">
<view
class="h-184rpx w-184rpx flex flex-col items-center justify-center border-2rpx border-[#E5E5E5] rounded-16rpx border-dashed">
<wd-img width="64rpx" height="64rpx" :src="`${OSS}icon/icon_video.png`" mode="aspectFill" />
<view class="mt-12rpx text-26rpx text-[#303133] font-400 leading-36rpx">
添加视频
</view>
</view>
</wd-upload>
</view> -->
<!-- 包间图片 -->
<view class="mt-28rpx">
<view class="mb-28rpx flex items-center justify-between">
<view class="flex items-center">
<view class="mr-10rpx text-32rpx text-[#303133] font-bold leading-44rpx">
包间图片
</view>
<view class="flex items-center">
<wd-img width="16rpx" height="16rpx" :src="`${OSS}icon/icon_validate.png`" />
</view>
</view>
<view class="text-26rpx text-[#9CA3AF] font-400 leading-36rpx">
可添加1张图片
</view>
</view>
<view class="flex flex-wrap items-center gap-16rpx">
<wd-upload
:header="{'token': token}"
:file-list="fileList"
:limit="1"
image-mode="scaleToFill"
:action="action"
@success="RoomDetail.handleUploadSuccess">
<wd-img width="184rpx" height="184rpx" :src="form.img || `${OSS}images/store/my/image1.png`" mode="aspectFill" radius="16rpx" />
</wd-upload>
</view>
</view>
</view>
<!-- 规格与价格 -->
<view v-if="tab === 1">
<view class="mb-30rpx text-34rpx text-[#303133] font-bold leading-48rpx">
规格与价格
</view>
<!-- 价格 -->
<view class="mb-28rpx">
<view class="mb-20rpx flex items-center">
<view class="mr-10rpx text-30rpx text-[#303133] font-bold leading-44rpx">
价格
</view>
<view class="flex items-center">
<wd-img width="16rpx" height="16rpx" :src="`${OSS}icon/icon_validate.png`" />
</view>
</view>
<wd-input v-model="form.price" no-border placeholder="价格" type="digit"
custom-class="!bg-[#F6F7F8] !rounded-16rpx !px-28rpx !py-20rpx">
<template #prefix>
<view class="mr-8rpx text-28rpx text-[#303133]">
¥
</view>
</template>
</wd-input>
</view>
<!-- 起订时间 -->
<view class="mb-28rpx">
<view class="mb-20rpx flex items-center justify-between">
<view class="flex items-center">
<view class="mr-10rpx text-30rpx text-[#303133] font-bold leading-44rpx">
起订时间
</view>
<view class="flex items-center">
<wd-img width="16rpx" height="16rpx" :src="`${OSS}icon/icon_validate.png`" />
</view>
</view>
<view class="text-24rpx text-[#9CA3AF] font-400 leading-34rpx">
<!-- 范围1~5 -->
</view>
</view>
<view class="flex items-center">
<wd-input v-model="form.hours" no-border placeholder="请输入起订时间" type="number" :max="5"
:min="1" custom-class="!bg-[#F6F7F8] !rounded-16rpx !px-28rpx !py-20rpx" />
<view class="ml-16rpx text-28rpx text-[#303133]">
小时
</view>
</view>
</view>
<!-- 库存 -->
<!-- <view>
<view class="mb-20rpx flex items-center justify-between">
<view class="flex items-center">
<view class="mr-10rpx text-30rpx text-[#303133] font-bold leading-44rpx">
库存
</view>
<view class="flex items-center">
<wd-img width="16rpx" height="16rpx" :src="`${OSS}icon/icon_validate.png`" />
</view>
</view>
<view class="text-24rpx text-[#9CA3AF] font-400 leading-34rpx">
输入范围1-999
</view>
</view>
<wd-input v-model="form.inventory" no-border placeholder="请输入库存" type="number" :max="999" :min="1"
custom-class="!bg-[#F6F7F8] !rounded-16rpx !px-28rpx !py-20rpx" />
</view> -->
</view>
<!-- 套餐详情 -->
<view v-if="tab === 2">
<view class="mb-30rpx text-34rpx text-[#303133] font-bold leading-48rpx">
套餐详情
</view>
<!-- 套餐介绍 -->
<!-- <view class="add-textarea mb-28rpx">
<view class="mb-20rpx flex items-center justify-between">
<view class="flex items-center">
<view class="mr-10rpx text-30rpx text-[#303133] font-bold leading-44rpx">
套餐介绍
</view>
<view class="flex items-center">
<wd-img width="16rpx" height="16rpx" :src="`${OSS}icon/icon_validate.png`" />
</view>
</view>
<view class="text-24rpx text-[#9CA3AF] font-400 leading-34rpx">
每条内容之间需要换行输入
</view>
</view>
<wd-textarea v-model="form.packageIntro"
custom-class="!rounded-18rpx !border-2rpx !border-[#EFF0EF] !bg-[#F8F9FA] !mt-20rpx"
custom-textarea-class="!bg-[#F8F9FA]" placeholder="请输入套餐介绍,每条内容换行输入" />
</view> -->
<!-- 其他说明 -->
<view class="add-textarea mt-30rpx">
<view class="mb-20rpx flex items-center">
<view class="mr-10rpx text-30rpx text-[#303133] font-bold leading-44rpx">
其他说明
</view>
</view>
<wd-textarea v-model="form.other_describe"
custom-class="!rounded-18rpx !border-2rpx !border-[#EFF0EF] !bg-[#F8F9FA] !mt-20rpx"
custom-textarea-class="!bg-[#F8F9FA]" placeholder="请输入其他说明" />
</view>
</view>
<!-- 购买须知 -->
<view v-if="tab === 3">
<!-- <view class="mb-30rpx text-34rpx text-[#303133] font-bold leading-48rpx">
购买须知
</view> -->
<!-- 使用人数 -->
<!-- <view class="mb-28rpx">
<view class="mb-20rpx flex items-center">
<view class="mr-10rpx text-30rpx text-[#303133] font-bold leading-44rpx">
使用人数
</view>
<view class="flex items-center">
<wd-img width="16rpx" height="16rpx" :src="`${OSS}icon/icon_validate.png`" />
</view>
</view>
<wd-input v-model="form.userCount" no-border placeholder="建议4~6人使用"
custom-class="!bg-[#F6F7F8] !rounded-16rpx !px-28rpx !py-20rpx" />
</view> -->
<!-- 退改说明 -->
<!-- <view class="add-textarea mt-28rpx">
<view class="mb-20rpx flex items-center">
<view class="mr-10rpx text-32rpx text-[#303133] font-bold leading-44rpx">
退改说明
</view>
<view class="flex items-center">
<wd-img width="16rpx" height="16rpx" :src="`${OSS}icon/icon_validate.png`" />
</view>
</view>
<wd-textarea v-model="form.refundPolicy"
custom-class="!rounded-18rpx !border-2rpx !border-[#EFF0EF] !bg-[#F8F9FA] !mt-20rpx"
custom-textarea-class="!bg-[#F8F9FA]" placeholder="请输入退改说明" />
</view> -->
</view>
</view>
<!-- 底部保存按钮 -->
<view class="fixed bottom-0 left-0 right-0 h-152rpx bg-white">
<view class="mt-34rpx flex items-center justify-center">
<wd-button custom-class="!text-32rpx !w-630rpx !h-90rpx !bg-[#4C9F44] !rounded-8rpx !text-[#fff]"
@click="RoomDetail.handleSave">
保存
</wd-button>
</view>
</view>
<!-- 选择标签弹窗 -->
<wd-popup v-model="showTagSelectPopup" lock-scroll custom-style="border-radius: 32rpx 32rpx 0rpx 0rpx;"
position="bottom" @close="RoomDetail.handleCloseTagSelect">
<view class="relative bg-white px-30rpx pb-56rpx pt-50rpx">
<!-- 关闭按钮 -->
<view class="absolute right-30rpx top-18rpx" @click="RoomDetail.handleCloseTagSelect">
<wd-icon name="close" size="20px" color="#C0C4CC" />
</view>
<!-- 标题 -->
<view class="mb-40rpx text-center text-36rpx text-[#121212] leading-50rpx">
选择标签
</view>
<!-- 我的标签标题和管理/退出管理 -->
<view class="mb-20rpx flex items-center justify-between">
<view class="text-32rpx text-[#303133] font-bold leading-44rpx">
我的标签
</view>
<view class="text-28rpx text-[#4C9F44] leading-40rpx"
@click="isTagManageMode ? RoomDetail.handleExitManagement() : RoomDetail.handleEnterManagement()">
{{ isTagManageMode ? '退出管理' : '管理' }}
</view>
</view>
<!-- 说明文字 -->
<view class="mb-28rpx text-24rpx text-[#9CA3AF] leading-34rpx">
可选择1~2个标签, 每个标签不超过5个字
</view>
<!-- 标签列表 -->
<view class="flex flex-wrap gap-16rpx">
<view v-for="(item, index) in availableTags" :key="item.id"
class="flex items-center rounded-20rpx px-20rpx py-8rpx"
:class="selectedTags.includes(index) ? 'bg-[#4C9F44]' : 'bg-[#F6F7F8]'"
@click="RoomDetail.handleSelectTag(index)">
<view class="text-26rpx leading-36rpx"
:class="selectedTags.includes(index) ? 'text-[#fff]' : 'text-[#303133]'"
:style="{ marginRight: isTagManageMode ? '8rpx' : '0' }">
{{ item.label_name }}
</view>
<wd-icon v-if="isTagManageMode" name="close-circle-filled" size="14px"
:color="selectedTags.includes(index) ? '#fff' : '#909399'"
@click.stop="RoomDetail.handleRemoveTagFromList(index, $event)" />
</view>
<!-- 新建标签按钮 -->
<view v-if="!isTagManageMode"
class="flex items-center border-2rpx border-[#E5E5E5] rounded-20rpx border-dashed px-20rpx py-8rpx"
@click="RoomDetail.handleShowCreateTag">
<wd-icon name="add" size="16px" color="#909399" class="mr-8rpx" />
<view class="text-26rpx text-[#909399] leading-36rpx">
新建标签
</view>
</view>
</view>
<!-- 确认按钮 -->
<view
class="mt-40rpx h-90rpx rounded-8rpx bg-[#4C9F44] text-center text-30rpx text-[#fff] leading-90rpx"
:class="isTagManageMode ? 'opacity-0 pointer-events-none' : ''"
@click="RoomDetail.handleConfirmTags">
确认
</view>
</view>
</wd-popup>
<!-- 新建标签弹窗 -->
<wd-popup v-model="showCreateTagPopup" lock-scroll custom-style="border-radius: 32rpx 32rpx 0rpx 0rpx;"
position="bottom" @close="RoomDetail.handleCloseCreateTag">
<view class="relative bg-white px-30rpx pb-78rpx pt-50rpx">
<!-- 导航栏 -->
<view class="mb-40rpx flex items-center justify-between">
<view class="flex items-center" @click="RoomDetail.handleCloseCreateTag">
<wd-icon name="arrow-left" size="20px" color="#303133" />
</view>
<view class="text-center text-36rpx text-[#121212] leading-50rpx">
新建标签
</view>
<view class="flex items-center" @click="RoomDetail.handleCloseCreateTag">
<wd-icon name="close" size="20px" color="#C0C4CC" />
</view>
</view>
<!-- 输入框 -->
<view class="mb-40rpx">
<wd-input v-model="newTagName" no-border placeholder="标签名称" :maxlength="5"
custom-class="!bg-[#F6F7F8] !rounded-16rpx !px-28rpx !py-20rpx" />
</view>
<!-- 完成按钮 -->
<view class="h-90rpx rounded-8rpx bg-[#4C9F44] text-center text-30rpx text-[#fff] leading-90rpx"
@click="RoomDetail.handleCompleteCreateTag">
完成
</view>
</view>
</wd-popup>
</view>
</template>
<script lang="ts" setup>
import { getRoomDetails, getRoomLabelList, handleCreateTag, handleDeleteTag, editRoom } from '@/api/store'
import { updateUserInfo } from '@/api/user'
import { router } from '@/utils/tools'
import { toast } from '@/utils/toast'
import { useStoreStore } from '@/store'
const OSS = inject('OSS')
const token = ref<string>('') // 用户token
const useStore = useStoreStore()
// tab
const tab = ref<number>(0)
// 上传文件
const fileList = ref<any[]>([])
const action = import.meta.env.VITE_UPLOAD_BASEURL
// 标签相关
const showTagSelectPopup = ref(false)
const showCreateTagPopup = ref(false)
const newTagName = ref('')
const isTagManageMode = ref(false) // 是否处于管理模式
const selectedTags = ref<number[]>([]) // 临时选中的标签,点击确认后才回填到表单
// Mock 已有标签列表
const availableTags = ref<Array<{
id: number
label_name: string
}>>([
{id: 0, label_name: ''}
])
const roomId = ref<number>(0) // 包间ID
// 表单
const form = reactive({
title: '',
image: '',
tags: [] as string[],
price: '',
hours: '',
video: null as any,
other_describe: '',
})
const tags = ref<Array<{ id: number, label_name: string, index: number }>>([])
const roomLabelId = ref<string>('')
onLoad(async (args) => {
token.value = uni.getStorageSync('token')
roomId.value = args.id || 0
await RoomDetail.handleGetRoomDetails()
await RoomDetail.handleGetRoomLabels()
})
const RoomDetail = {
/**
* 初始化信息
*/
handleGetRoomDetails: async () => {
const res = await getRoomDetails(roomId.value)
fileList.value = [{url: res.details.room.img, name: res.details.room.img }]
roomLabelId.value = res.details.room.label_id
form.title = res.details.room.title
form.image = res.details.room.img
form.price = res.details.room.price.toString()
form.hours = res.details.room.hours.toString()
form.other_describe = res.details.room.other_describe || ''
},
/**
* 获取包间标签列表
*/
handleGetRoomLabels: async () => {
const res = await getRoomLabelList(useStore.defaultStore.id)
availableTags.value = res.list
if (roomLabelId.value) {
const labelIds = roomLabelId.value.split(',').map((id: string) => parseInt(id))
tags.value = labelIds.map((id: number) => {
const tagIndex = availableTags.value.findIndex(tag => tag.id === id)
const tag = availableTags.value[tagIndex]
return {
id: tag.id,
label_name: tag.label_name,
index: tagIndex
}
})
// 设置已选择的标签
selectedTags.value = []
labelIds.forEach((id: number) => {
const index = availableTags.value.findIndex(tag => tag.id === id)
if (index !== -1) {
selectedTags.value.push(index)
}
})
}
console.log("🚀 ~ availableTags.value:", availableTags.value)
},
/**
* 切换tab
*/
handleChangeTab: (e: any) => {
tab.value = e.name
},
/**
* 添加标签 - 显示选择标签弹窗
*/
handleAddTag: () => {
// 初始化临时选中的标签为当前表单中的标签
// selectedTags.value = availableTags.value
showTagSelectPopup.value = true
},
/**
* 上传图片
*/
handleUploadSuccess: async (e: any) => {
try {
const response = JSON.parse(e.file.response)
if (response.code) {
const avatarUrl = response.data.url
await updateUserInfo({ avatar: avatarUrl })
form.image = avatarUrl
toast.info('图片上传成功')
} else {
throw new Error('上传失败')
}
} catch (error) {
toast.info('上传失败')
}
},
/**
* 关闭选择标签弹窗
*/
handleCloseTagSelect: () => {
showTagSelectPopup.value = false
isTagManageMode.value = false // 关闭时重置管理模式
// selectedTags.value = [] // 清空临时选中的标签
},
/**
* 选择标签(临时选择,不直接修改表单)
*/
handleSelectTag: (id: number) => {
if (isTagManageMode.value) {
// 管理模式时,点击标签不进行选择操作
return
}
if (selectedTags.value.includes(id)) {
// 如果已选择,则取消选择
const index = selectedTags.value.indexOf(id)
selectedTags.value.splice(index, 1)
}
else {
// 如果未选择,则添加
if (selectedTags.value.length >= 2) {
toast.info('最多只能选择2个标签')
return
}
selectedTags.value.push(id)
}
},
/**
* 从选择列表删除标签
*/
handleRemoveTagFromList: async (index: number, event: any) => {
event.stopPropagation()
uni.showLoading({
title: '操作中...',
mask: true
})
try {
const tagId = availableTags.value[index].id
availableTags.value.splice(index, 1)
await handleDeleteTag(tagId)
tags.value = tags.value.filter(tag => tag.id !== tagId)
uni.hideLoading()
toast.info('删除成功')
} catch(e) {
uni.hideLoading()
toast.info('删除失败,请稍后重试')
}
},
/**
* 确认选择标签
*/
handleConfirmTags: () => {
tags.value = selectedTags.value.map(index => {
return {
id: availableTags.value[index].id,
label_name: availableTags.value[index].label_name,
index
}
})
showTagSelectPopup.value = false
isTagManageMode.value = false
// selectedTags.value = []
},
/**
* 进入管理模式
*/
handleEnterManagement: () => {
isTagManageMode.value = true
selectedTags.value = []
},
/**
* 退出管理模式
*/
handleExitManagement: () => {
isTagManageMode.value = false
},
/**
* 显示新建标签弹窗
*/
handleShowCreateTag: () => {
showTagSelectPopup.value = false
showCreateTagPopup.value = true
},
/**
* 关闭新建标签弹窗
*/
handleCloseCreateTag: () => {
showCreateTagPopup.value = false
newTagName.value = ''
},
/**
* 完成新建标签
*/
handleCompleteCreateTag: async () => {
const tagName = newTagName.value.trim()
if (!tagName) {
toast.info('请输入标签名称')
return
}
if (tagName.length > 5) {
toast.info('标签不能超过5个字')
return
}
uni.showLoading({
title: '操作者中...'
})
try {
await handleCreateTag({
store_id: useStore.defaultStore.id,
label_name: tagName
})
uni.hideLoading()
RoomDetail.handleGetRoomLabels()
showCreateTagPopup.value = false
newTagName.value = ''
showTagSelectPopup.value = true
toast.info('标签创建成功')
} catch (e) {
uni.hideLoading()
}
},
/**
* 删除标签
*/
handleRemoveTag: (index: number) => {
tags.value.splice(index, 1)
},
/**
* 上传视频
*/
handleUploadVideo: (event: any) => {
if (event.fileList && event.fileList.length > 0) {
form.video = event.fileList[0]
}
},
/**
* 删除视频
*/
handleRemoveVideo: () => {
form.video = null
},
/**
* 保存
*/
handleSave: async () => {
// TODO: 实现保存功能
if (!form.title) {
toast.info('请输入包间名称')
return
}
if (tags.value.length === 0) {
toast.info('请选择包间标签')
return
}
if (!form.image) {
toast.info('请上传团购图片')
return
}
if (!form.price) {
toast.info('请输入价格')
return
}
if (!form.hours) {
toast.info('请输入起订时间')
return
}
let params = {
id: roomId.value,
img: form.image,
title: form.title,
label_id: tags.value.map(tag => tag.id).join(','),
price: Number(form.price),
hours: Number(form.hours),
other_describe: form.other_describe,
}
uni.showLoading({
title: '保存中...',
mask: true
})
try {
await editRoom(params)
uni.hideLoading()
toast.info('修改成功')
router.navigateBack(1, 500)
} catch (e) {
toast.info('修改失败,请稍后重试')
uni.hideLoading()
return
}
},
}
</script>
<style lang="scss">
page {
background: #f6f7f8;
}
.store-tabs {
:deep() {
.wd-tabs__line {
background-color: #4c9f44 !important;
}
}
}
.add-textarea {
:deep() {
.wd-input__value,
.wd-input__count {
background: transparent !important;
}
}
}
</style>

View File

@ -1,256 +0,0 @@
<route lang="jsonc" type="page">{
"layout": "default",
"style": {
"navigationStyle": "custom"
}
}</route>
<template>
<view>
<!-- 导航栏 -->
<view>
<navbar title="包间管理" custom-class="!bg-white" />
</view>
<!-- 房间列表 -->
<view class="px-30rpx pb-40rpx pt-30rpx">
<mescroll-body ref="mescrollItem0" @init="mescrollInit" @down="downCallback" @up="RoomManage.upCallback" :down="downOption" :up="upOption">
<view class="grid grid-cols-2 gap-24rpx">
<view v-for="(room, index) in list" :key="room.id"
class="flex flex-col overflow-hidden rounded-16rpx bg-white"
@click="RoomManage.handleClickRoom(room)">
<!-- 房间图片 -->
<view class="relative aspect-[4/3] w-full h-316rpx">
<wd-img width="100%" height="316rpx" :src="room.img" mode="aspectFill" />
<!-- 编辑图标覆盖层 -->
<view
class="absolute right-16rpx top-16rpx h-48rpx w-48rpx flex items-center justify-center rounded-24rpx bg-black bg-opacity-40"
@click.stop="RoomManage.handleEditImage(room, $event)">
<wd-icon name="edit" size="16px" color="#fff" />
</view>
</view>
<!-- 房间信息 -->
<view class="flex flex-col p-24rpx">
<!-- 房间名称 -->
<view class="mb-0rpx text-center text-28rpx text-[#303133] leading-40rpx">
{{ room.title }}
</view>
<!-- 状态指示器 -->
<view class="flex items-baseline justify-center align-middle" @click.stop="RoomManage.handleEditRoomStatus(room, index)">
<!-- 状态点 -->
<view class="mr-8rpx h-12rpx w-12rpx flex-shrink-0 rounded-full"
:style="{ backgroundColor: RoomManage.handleGetStatusConfig(room.status).color }" />
<!-- 状态文字 -->
<view class="text-32rpx text-[#303133] leading-34rpx"
:style="{ color: RoomManage.handleGetStatusConfig(room.status).color }">
{{ RoomManage.handleGetStatusConfig(room.status).text }}
</view>
<!-- 右箭头 -->
<view class="ml-8rpx">
<wd-icon name="arrow-right" size="12px" :color="RoomManage.handleGetStatusConfig(room.status).color" />
</view>
</view>
</view>
</view>
</view>
</mescroll-body>
</view>
<!-- 状态修改弹窗 -->
<wd-action-sheet v-model="showActionMenu" :actions="actions" @select="RoomManage.handleSelectAction" />
</view>
</template>
<script lang="ts" setup>
import { onPageScroll, onReachBottom } from '@dcloudio/uni-app'
import useMescroll from "@/uni_modules/mescroll-uni/hooks/useMescroll.js"
import { getRoomList, editRoom } from '@/api/store'
import { router } from '@/utils/tools'
import { toast } from '@/utils/toast'
import { useStoreStore } from '@/store'
const useStore = useStoreStore()
// 房间状态枚举
enum RoomStatus {
WAIT_CLEANING = 1, // 待打扫 - 灰色
AVAILABLE = 2, // 空闲中 - 绿色
MAINTENANCE = 3, // 维护中 - 红色
CLEANING = 4, // 打扫中 - 橙色
IN_USE = 5, // 使用中 - 蓝色
}
// 房间状态配置
const statusConfig = {
[RoomStatus.WAIT_CLEANING]: {
text: '待打扫',
color: '#818CA9', // 灰色
},
[RoomStatus.AVAILABLE]: {
text: '空闲中',
color: '#4C9F44', // 绿色
},
[RoomStatus.MAINTENANCE]: {
text: '维护中',
color: '#F65353', // 红色
},
[RoomStatus.CLEANING]: {
text: '打扫中',
color: '#F29747', // 橙色
},
[RoomStatus.IN_USE]: {
text: '使用中',
color: '#1890FF', // 蓝色
},
}
// 房间数据
const showActionMenu = ref<boolean>(false)
const actions = ref([
{
name: '待打扫',
value: RoomStatus.WAIT_CLEANING
},
{
name: '空闲中',
value: RoomStatus.AVAILABLE
},
{
name: '维护中',
value: RoomStatus.MAINTENANCE
},
{
name: '打扫中',
value: RoomStatus.CLEANING
},
{
name: '使用中',
value: RoomStatus.IN_USE
}
])
const selectRoom = ref<{
id: number,
index: number
}>({
id: 0,
index: -1
})
// mescroll
const { mescrollInit, downCallback, getMescroll } = useMescroll(onPageScroll, onReachBottom) // 调用mescroll的hook
const downOption = {
auto: true
}
const upOption = {
auto: true,
textNoMore: '~ 已经到底啦 ~', //无更多数据的提示
}
const orderStatus = ref<string>('')
const list = ref<Array<any>>([]) // 茶室列表
const RoomManage = {
/**
* 分页加载
* @param mescroll
*/
upCallback: (mescroll) => {
// 需要留一下数据为空的时候显示的空数据图标内容
const filter = {
page: mescroll.num,
size: mescroll.size,
store_id: useStore.defaultStore.id,
}
getRoomList(filter).then((res) => {
const curPageData = res.list || [] // 当前页数据
if(mescroll.num == 1) list.value = [] // 第一页需手动制空列表
list.value = list.value.concat(curPageData) //追加新数据
mescroll.endSuccess(curPageData.length, Boolean(res.more))
}).catch(() => {
mescroll.endErr() // 请求失败, 结束加载
})
},
/**
* 点击房间卡片
*/
handleClickRoom: (room: any) => {
router.navigateTo(`/bundle/tea-room/detail?id=${room.id}`)
},
/**
* 点击编辑房间图片
*/
handleEditImage: (room: any, event: any) => {
// event.stopPropagation()
// TODO: 实现编辑房间图片功能
console.log('编辑房间图片:', room.id)
router.navigateTo(`/pages/store/room-detail?id=${room.id}`)
},
/**
* 点击更多选项
*/
handleMore: () => {
// TODO: 实现更多选项功能
console.log('更多选项')
},
/**
* 点击目标图标
*/
handleTarget: () => {
// TODO: 实现目标功能
console.log('目标功能')
},
/**
* 点击编辑房间状态
*/
handleEditRoomStatus: (item: any, index: number) => {
selectRoom.value.id = item.id
selectRoom.value.index = index
showActionMenu.value = true
},
/**
* 获取房间状态
*/
handleGetStatusConfig: (status: RoomStatus) => {
return statusConfig[status] || statusConfig[RoomStatus.AVAILABLE]
},
/**
* 修改房间选择状态
*/
handleSelectAction: async (action: any) => {
uni.showLoading({
title: '修改中...'
})
try {
const status = action.item.value
await editRoom({
id: selectRoom.value.id,
status
})
uni.hideLoading()
toast.success('修改状态成功')
list.value[selectRoom.value.index].status = status
} catch (error) {
uni.hideLoading()
toast.info('修改状态失败')
}
}
}
</script>
<style lang="scss">
page {
background: #f6f7f8;
}
</style>

View File

@ -1,102 +0,0 @@
<route lang="jsonc" type="page">{
"layout": "tabbar",
"style": {
"navigationStyle": "custom"
}
}</route>
<script lang="ts" setup>
import { router } from '@/utils/tools'
const OSS = inject('OSS')
const navbarHeight = inject('navbarHeight')
console.log(`${OSS}/images/store/store/room.png`)
// 功能卡片数据
const storeMenus = ref([
{
id: 1,
title: '包间管理',
icon: `${OSS}images/store/store/room.png`,
path: '/pages/store/room-manage',
},
{
id: 2,
title: '设备控制',
icon: `${OSS}images/store/store/device.png`,
path: '/pages/store/device',
},
{
id: 3,
title: '包间预定',
icon: `${OSS}images/store/store/bookroom.png`,
path: '/pages/store/reserve',
},
{
id: 4,
title: '订单续订',
icon: `${OSS}images/store/store/renew.png`,
path: '/pages/store/renew',
},
])
const Store = {
/**
* 点击功能卡片
*/
handleClickMenu: (item: any) => {
router.navigateTo(item.path)
},
/**
* 点击更多选项
*/
handleMore: () => {
// TODO: 实现更多选项功能
console.log('更多选项')
},
/**
* 点击目标图标
*/
handleTarget: () => {
// TODO: 实现目标功能
console.log('目标功能')
},
}
</script>
<template>
<view>
<!-- 导航栏 -->
<view>
<navbar title="门店管理" custom-class="!bg-white" />
</view>
<!-- 功能卡片网格 -->
<view class="px-30rpx pt-40rpx">
<view class="grid grid-cols-2 gap-30rpx">
<view v-for="item in storeMenus" :key="item.id"
class="flex flex-col items-center rounded-16rpx bg-white p-40rpx"
@click="Store.handleClickMenu(item)">
<!-- 图标区域 -->
<view
class="mb-24rpx h-160rpx w-160rpx flex items-center justify-center rounded-16rpx bg-[#F0F9FF]">
<wd-img width="120rpx" height="120rpx" :src="item.icon" mode="aspectFit" />
</view>
<!-- 标题 -->
<view class="text-30rpx text-[#303133] leading-42rpx">
{{ item.title }}
</view>
</view>
</view>
</view>
</view>
</template>
<style lang="scss">
page {
background-color: $cz-page-background;
}
</style>