更新文件

This commit is contained in:
wangxiaowei
2025-12-28 14:23:16 +08:00
parent 21af83652b
commit 9266b6b80d
10 changed files with 5961 additions and 227 deletions

View File

@ -0,0 +1,806 @@
<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>