Files
chazhi_teaspecialist_manage/src/order/pending-order-detail.vue
wangxiaowei 9266b6b80d 更新文件
2025-12-28 14:23:16 +08:00

755 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

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

<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 } 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 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>
<template>
<view class="pending-order-detail-page" :class="{ 'popup-open': showCostDetailPopup }">
<!-- 消息提示框 -->
<wd-message-box selector="wd-message-box-slot" />
<!-- 导航栏 -->
<navbar title="待接单" custom-class="!bg-[#F6F7F8]">
<template #right>
<view class="navbar-right-icons">
<wd-icon name="more" size="40rpx" color="#303133" class="mr-24rpx" />
<wd-icon name="eye" size="40rpx" color="#303133" />
</view>
</template>
</navbar>
<scroll-view class="content-scroll" scroll-y>
<!-- 倒计时提示 -->
<!-- <view class="countdown-tip">
<text class="countdown-text">还剩</text>
<text class="countdown-time">{{ countdownTime }}</text>
<text class="countdown-text">订单自动取消</text>
</view> -->
<!-- 订单卡片 -->
<view class="order-card" style="position: 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="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>
<!-- 预约信息 -->
<view class="info-card">
<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 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>
<!-- 订单备注 -->
<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">
<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="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>
<style lang="scss">
page {
background-color: #f5f5f5;
height: 100%;
}
.pending-order-detail-page {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f5f5f5;
}
.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;
}
</style>