更新文件
This commit is contained in:
1224
src/index/index.vue
Normal file
1224
src/index/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
698
src/order/cancelled-order-detail.vue
Normal file
698
src/order/cancelled-order-detail.vue
Normal file
@ -0,0 +1,698 @@
|
||||
<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>
|
||||
|
||||
272
src/order/order-detail.vue
Normal file
272
src/order/order-detail.vue
Normal file
@ -0,0 +1,272 @@
|
||||
<route lang="jsonc" type="page">
|
||||
{
|
||||
"needLogin": true,
|
||||
"layout": "default",
|
||||
"style": {
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<view class="pb-254rpx">
|
||||
<!-- 取消订单 -->
|
||||
<wd-message-box selector="wd-message-box-slot"></wd-message-box>
|
||||
|
||||
<view>
|
||||
<navbar :title="title" custom-class='!bg-[#F6F7F8]'></navbar>
|
||||
</view>
|
||||
|
||||
<view class="mx-30rpx coupon-bg" >
|
||||
<view class="flex items-center px-30rpx pt-30rpx pb-40rpx">
|
||||
<view class="mr-30rpx">
|
||||
<wd-img width="190rpx" height="190rpx" :src="order.room_msg.img" mode="scaleToFill" radius="10rpx"</wd-img>
|
||||
</view>
|
||||
<view class="flex-1">
|
||||
<view class="flex justify-between items-center">
|
||||
<view class="flex items-center font-bold text-30rpx leading-42rpx text-[#303133] mr-10rpx">
|
||||
<view class="line-1 w-300rpx">
|
||||
{{ order.room_msg.title }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="text-26rpx leading-36rpx text-[#909399]">¥{{ order.order_amount }}</view>
|
||||
</view>
|
||||
<view class="flex justify-between items-center text-26rpx leading-36rpx text-[#909399] mt-18rpx">
|
||||
<view class="">
|
||||
¥{{ order.room_price }}/小时
|
||||
</view>
|
||||
<view class="">
|
||||
x{{ order.hours }}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="mt-28rpx pb-36rpx">
|
||||
<view class="text-30rpx leading-42rpx text-[#303133] px-30rpx">预约信息</view>
|
||||
<view class="font-500 text-26rpx leading-48rpx text-[#606266] mt-20rpx">
|
||||
<view class="mb-20rpx px-30rpx">预约时间:{{ order.day_time }} {{ order?.renew_dtime?.start_time || order.start_time }}-{{ order?.renew_dtime?.end_time || order.end_time }}</view>
|
||||
<view class="flex justify-between items-center pl-30rpx" >
|
||||
<view>预约时长:{{ order.hours }}小时</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单信息 -->
|
||||
<view class="bg-white rounded-16rpx px-30rpx py-34rpx mx-30rpx mt-20rpx">
|
||||
<view class="text-[#303133] text-32rpx leading-44rpx">订单信息</view>
|
||||
<view class="text-28rpx leading-40rpx text-[#606266] flex items-center justify-between mt-22rpx">
|
||||
<view>订单编号</view>
|
||||
<view>
|
||||
<text>{{ order.order_sn }}</text>
|
||||
<wd-divider vertical />
|
||||
<text class="text-[#4C9F44]" @click="copy(order.order_sn)">复制</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="text-28rpx leading-40rpx text-[#606266] flex items-center justify-between mt-22rpx">
|
||||
<view>创建时间</view>
|
||||
<view>{{ order.dtime }}</view>
|
||||
</view>
|
||||
<view class="text-28rpx leading-40rpx text-[#606266] flex items-center justify-between mt-22rpx">
|
||||
<view>支付方式</view>
|
||||
<view>{{ order.pay_way_title }}</view>
|
||||
</view>
|
||||
<view class="text-28rpx leading-40rpx text-[#606266] flex items-center justify-between mt-22rpx">
|
||||
<view>用户手机号</view>
|
||||
<view>{{ order.mobile }}</view>
|
||||
</view>
|
||||
<view class="my-22rpx">
|
||||
<wd-gap height="2rpx" bg-color='#F6F7F9'></wd-gap>
|
||||
</view>
|
||||
<view class="text-28rpx leading-40rpx text-[#606266] flex items-center justify-between mt-22rpx">
|
||||
<view>续单时长</view>
|
||||
<view>¥{{ order.renew_hour }}</view>
|
||||
</view>
|
||||
<view class="text-28rpx leading-40rpx text-[#606266] flex items-center justify-between mt-22rpx">
|
||||
<view>续单费用</view>
|
||||
<view>¥{{ order.renew_price }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="w-full fixed bottom-0 left-0 right-0 bg-white h-152rpx" v-if="order.order_status == AdminOrderStatus.ToUse || order.order_status == AdminOrderStatus.Consuming">
|
||||
<!-- 待使用 -->
|
||||
<view class="flex items-center justify-between mx-58rpx mt-34rpx" v-if="order.order_status == AdminOrderStatus.ToUse">
|
||||
<view class="w-630rpx h-90rpx leading-90rpx text-center bg-[#4C9F44] rounded-8rpx text-[#fff]" @click="OrderDetail.handleCancelOrder">取消订单</view>
|
||||
</view>
|
||||
|
||||
<!-- 使用中 -->
|
||||
<view class="flex items-center justify-between mx-58rpx mt-34rpx" v-if="order.order_status == AdminOrderStatus.Consuming">
|
||||
<view class="flex items-center">
|
||||
<view class="text-28rpx leading-40rpx text-[#606266] mr-16rpx" @click="OrderDetail.handleCancelOrder">取消订单</view>
|
||||
</view>
|
||||
<view class="w-360rpx h-90rpx leading-90rpx text-center bg-[#4C9F44] rounded-8rpx text-[#fff]" @click="OrderDetail.handleReleaseOrder">释放时间</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { AdminOrderStatus, AdminOrderStatusTextValue } from '@/utils/order'
|
||||
import { useMessage } from 'wot-design-uni'
|
||||
import {toast} from '@/utils/toast'
|
||||
import type { ITeaSpecialistOrderDetailsResult } from '@/api/types/tea'
|
||||
import { copy } from '@/utils/tools'
|
||||
import { getOrderStoreDetail } from '@/api/order'
|
||||
import { handleReleaseTeaRoomOrderHookds, handleCancelOrderHooks } from '@/hooks/useOrder'
|
||||
|
||||
const title = ref<string>('')
|
||||
|
||||
|
||||
|
||||
// 取消订单弹窗
|
||||
const message = useMessage('wd-message-box-slot')
|
||||
|
||||
// 订单
|
||||
const orderId = ref<number>(0)
|
||||
const order = ref<ITeaSpecialistOrderDetailsResult>({
|
||||
order_amount: '',
|
||||
order_sn: '',
|
||||
store_id: 0,
|
||||
mobile: '',
|
||||
room_msg: {
|
||||
id: 0,
|
||||
price: 0,
|
||||
img: '',
|
||||
title: '',
|
||||
},
|
||||
store_msg: {
|
||||
id: 0,
|
||||
address: '',
|
||||
name: '',
|
||||
operation_type: 1,
|
||||
image: '',
|
||||
latitude: '',
|
||||
longitude: '',
|
||||
contact_phone: ''
|
||||
},
|
||||
time1: 0,
|
||||
order_status: 0,
|
||||
room_all_price: 0,
|
||||
room_price: 0,
|
||||
day_time: '',
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
hours: 0,
|
||||
distance: 0,
|
||||
pay_way: 0,
|
||||
pay_way_title: '',
|
||||
dtime: '',
|
||||
update_dtime: '',
|
||||
renew_price: 0,
|
||||
renew_hour: 0,
|
||||
renew_dtime: {
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
renew_price: 0
|
||||
}
|
||||
})
|
||||
|
||||
onLoad(async (args) => {
|
||||
orderId.value = args.id
|
||||
// 获取订单详情
|
||||
OrderDetail.handleInit()
|
||||
})
|
||||
|
||||
onUnload(() => {
|
||||
uni.$off('refreshOrderDetail')
|
||||
})
|
||||
|
||||
const OrderDetail = {
|
||||
/**
|
||||
* 获取订单详情
|
||||
*/
|
||||
handleInit: async () => {
|
||||
const res = await getOrderStoreDetail(orderId.value)
|
||||
order.value = res.details
|
||||
title.value = AdminOrderStatusTextValue[order.value.order_status].title || '订单详情'
|
||||
},
|
||||
|
||||
/**
|
||||
* 取消订单
|
||||
*/
|
||||
handleCancelOrder: () => {
|
||||
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') {
|
||||
uni.$on('refreshOrderList', () => {
|
||||
uni.$off('refreshOrderList')
|
||||
OrderDetail.handleInit()
|
||||
})
|
||||
handleCancelOrderHooks(orderId.value)
|
||||
}
|
||||
}).catch(() => {
|
||||
// 点击取消按钮回调事件
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* 释放时间
|
||||
*/
|
||||
handleReleaseOrder: () => {
|
||||
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') {
|
||||
uni.$on('refreshOrderList', () => {
|
||||
uni.$off('refreshOrderList')
|
||||
OrderDetail.handleInit()
|
||||
})
|
||||
handleReleaseTeaRoomOrderHookds(orderId.value)
|
||||
}
|
||||
}).catch(() => { })
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: $cz-page-background;
|
||||
}
|
||||
|
||||
.coupon-bg {
|
||||
background-image: url(#{$OSS}images/order/order_image2.png);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.collapse {
|
||||
:deep() {
|
||||
.wd-collapse-item::after,
|
||||
.wd-collapse-item__header.is-expanded::after {
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
.wd-collapse-item__body {
|
||||
padding-top: 0 !important;
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
806
src/order/order.vue
Normal file
806
src/order/order.vue
Normal file
@ -0,0 +1,806 @@
|
||||
<!-- 使用 type="home" 属性设置首页,其他页面不需要设置,默认为page -->
|
||||
<route lang="jsonc">
|
||||
{
|
||||
"needLogin": false,
|
||||
"layout": "tabbar",
|
||||
"style": {
|
||||
// 'custom' 表示开启自定义导航栏,默认 'default'
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "订单"
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useMessage } from 'wot-design-uni'
|
||||
import PriceFormat from '@/components/PriceFormat.vue'
|
||||
import { router } from '@/utils/tools'
|
||||
|
||||
const OSS = inject('OSS')
|
||||
|
||||
// 消息提示框
|
||||
const message = useMessage('wd-message-box-slot')
|
||||
|
||||
// 地图选择弹窗
|
||||
const showMapSelectPopup = ref(false)
|
||||
// 当前要导航的订单
|
||||
const currentNavigateOrder = ref<any>(null)
|
||||
|
||||
// 当前选中的标签
|
||||
const activeTab = ref('all')
|
||||
|
||||
// 模拟订单数据
|
||||
const orders = ref<Array<any>>([
|
||||
{
|
||||
id: 1,
|
||||
serviceName: '苓苑共享茶室空间',
|
||||
serviceType: '到店服务',
|
||||
status: 'pending',
|
||||
statusText: '待接单',
|
||||
price: 212.20,
|
||||
appointmentTime: '09/03 08:00-12:00',
|
||||
address: '上海浦东新区新金桥路58号新银东大厦15楼F室',
|
||||
timer: '09:52',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
serviceName: '韩梅梅预约单',
|
||||
serviceType: '上门服务',
|
||||
status: 'waiting',
|
||||
statusText: '待服务',
|
||||
price: 212.20,
|
||||
appointmentTime: '09/03 08:00-12:00',
|
||||
address: '上海浦东新区新金桥路58号新银东大厦15楼F室',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
serviceName: '韩梅梅预约单',
|
||||
serviceType: '上门服务',
|
||||
status: 'waiting_arrived',
|
||||
statusText: '待服务',
|
||||
price: 212.20,
|
||||
appointmentTime: '09/03 08:00-12:00',
|
||||
address: '上海浦东新区新金桥路58号新银东大厦15楼F室',
|
||||
timer: '09:52',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
serviceName: '韩梅梅预约单',
|
||||
serviceType: '上门服务',
|
||||
status: 'serving',
|
||||
statusText: '服务中',
|
||||
price: 212.20,
|
||||
appointmentTime: '09/03 08:00-12:00',
|
||||
address: '上海浦东新区新金桥路58号新银东大厦15楼F室',
|
||||
timer: '99:52',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
serviceName: '韩梅梅预约单',
|
||||
serviceType: '上门服务',
|
||||
status: 'serving',
|
||||
statusText: '服务中',
|
||||
price: 212.20,
|
||||
appointmentTime: '09/03 08:00-12:00',
|
||||
address: '上海浦东新区新金桥路58号新银东大厦15楼F室',
|
||||
timer: '00:00',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
serviceName: '韩梅梅预约单',
|
||||
serviceType: '上门服务',
|
||||
status: 'completed',
|
||||
statusText: '完成',
|
||||
price: 212.20,
|
||||
appointmentTime: '09/03 08:00-12:00',
|
||||
address: '上海浦东新区新金桥路58号新银东大厦15楼F室',
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
serviceName: '韩梅梅预约单',
|
||||
serviceType: '上门服务',
|
||||
status: 'cancelled',
|
||||
statusText: '订单取消',
|
||||
price: 212.20,
|
||||
appointmentTime: '09/03 08:00-12:00',
|
||||
address: '上海浦东新区新金桥路58号新银东大厦15楼F室',
|
||||
},
|
||||
])
|
||||
|
||||
// 根据标签过滤订单
|
||||
const filteredOrders = computed(() => {
|
||||
if (activeTab.value === 'all') {
|
||||
return orders.value
|
||||
}
|
||||
// 处理待服务状态,包含 waiting 和 waiting_arrived
|
||||
if (activeTab.value === 'waiting') {
|
||||
return orders.value.filter(order => order.status === 'waiting' || order.status === 'waiting_arrived')
|
||||
}
|
||||
return orders.value.filter(order => order.status === activeTab.value)
|
||||
})
|
||||
|
||||
// 获取状态样式类
|
||||
function getStatusClass(status: string) {
|
||||
const statusMap: Record<string, string> = {
|
||||
pending: 'status-pending',
|
||||
waiting: 'status-waiting',
|
||||
serving: 'status-serving',
|
||||
completed: 'status-completed',
|
||||
cancelled: 'status-cancelled',
|
||||
}
|
||||
return statusMap[status] || ''
|
||||
}
|
||||
|
||||
// 获取服务类型样式类
|
||||
function getServiceTypeClass(serviceType: string) {
|
||||
return serviceType === '到店服务' ? 'service-type-store' : 'service-type-home'
|
||||
}
|
||||
|
||||
// 获取订单操作按钮
|
||||
function getOrderActions(status: string) {
|
||||
const actionMap: Record<string, Array<any>> = {
|
||||
pending: [
|
||||
{ text: '放弃接单', type: 'default', plain: true, customClass: '!border-[#dcdfe6] !text-[#909399] !bg-transparent !border-2rpx !text-28rpx !px-32rpx !h-64rpx !leading-64rpx !rounded-8rpx', action: 'decline' },
|
||||
{ text: '立即接单', type: 'primary', plain: true, customClass: '!border-[#4c9f44] !text-[#4c9f44] !bg-transparent !border-2rpx !text-28rpx !px-32rpx !h-64rpx !leading-64rpx !rounded-8rpx', action: 'accept' },
|
||||
],
|
||||
waiting: [
|
||||
{ text: '导航', type: 'default', plain: true, customClass: '!border-[#dcdfe6] !text-[#909399] !bg-transparent !border-2rpx !text-28rpx !px-32rpx !h-64rpx !leading-64rpx !rounded-8rpx', action: 'navigate' },
|
||||
{ text: '立即出发', type: 'primary', plain: true, customClass: '!border-[#4c9f44] !text-[#4c9f44] !bg-transparent !border-2rpx !text-28rpx !px-32rpx !h-64rpx !leading-64rpx !rounded-8rpx', action: 'depart' },
|
||||
],
|
||||
waiting_arrived: [
|
||||
{ text: '导航', type: 'default', plain: true, customClass: '!border-[#dcdfe6] !text-[#909399] !bg-transparent !border-2rpx !text-28rpx !px-32rpx !h-64rpx !leading-64rpx !rounded-8rpx', action: 'navigate' },
|
||||
{ text: '已到达', type: 'primary', plain: true, customClass: '!border-[#4c9f44] !text-[#4c9f44] !bg-transparent !border-2rpx !text-28rpx !px-32rpx !h-64rpx !leading-64rpx !rounded-8rpx', action: 'arrived' },
|
||||
],
|
||||
serving: [
|
||||
{ text: '完成服务', type: 'primary', plain: true, customClass: '!border-[#4c9f44] !text-[#4c9f44] !bg-transparent !border-2rpx !text-28rpx !px-32rpx !h-64rpx !leading-64rpx !rounded-8rpx', action: 'complete' },
|
||||
],
|
||||
completed: [
|
||||
{ text: '删除订单', type: 'default', plain: true, customClass: '!border-[#dcdfe6] !text-[#909399] !bg-transparent !border-2rpx !text-28rpx !px-32rpx !h-64rpx !leading-64rpx !rounded-8rpx', action: 'delete' },
|
||||
],
|
||||
cancelled: [
|
||||
{ text: '删除订单', type: 'default', plain: true, customClass: '!border-[#dcdfe6] !text-[#909399] !bg-transparent !border-2rpx !text-28rpx !px-32rpx !h-64rpx !leading-64rpx !rounded-8rpx', action: 'delete' },
|
||||
],
|
||||
}
|
||||
return actionMap[status] || []
|
||||
}
|
||||
|
||||
// 处理操作
|
||||
function handleAction(order: any, action: string) {
|
||||
console.log('操作:', action, order)
|
||||
// 这里可以添加具体的操作逻辑
|
||||
switch (action) {
|
||||
case 'accept':
|
||||
// 接单逻辑
|
||||
break
|
||||
case 'decline':
|
||||
// 放弃接单逻辑
|
||||
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') {
|
||||
// 确认放弃接单的逻辑
|
||||
console.log('确认放弃接单', order)
|
||||
// TODO: 调用放弃接单的 API
|
||||
}
|
||||
}).catch(() => {
|
||||
// 点击取消按钮
|
||||
console.log('取消放弃接单')
|
||||
})
|
||||
break
|
||||
case 'navigate':
|
||||
// 导航逻辑 - 显示地图选择弹窗
|
||||
currentNavigateOrder.value = order
|
||||
showMapSelectPopup.value = true
|
||||
break
|
||||
case 'depart':
|
||||
console.log('出发逻辑', order)
|
||||
// 出发逻辑 - 跳转到详情页,状态改为 waiting_arrived(已出发,等待到达)
|
||||
router.navigateTo(`/pages/order/waiting-service-detail?id=${order.id}&status=waiting_arrived`)
|
||||
break
|
||||
case 'arrived':
|
||||
// 已到达逻辑 - 直接跳转到服务中详情页
|
||||
router.navigateTo(`/pages/order/serving-detail?id=${order.id}`)
|
||||
break
|
||||
case 'complete':
|
||||
// 完成服务逻辑
|
||||
break
|
||||
case 'delete':
|
||||
// 删除订单逻辑
|
||||
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') {
|
||||
// 确认删除订单的逻辑
|
||||
console.log('确认删除订单', order)
|
||||
// TODO: 调用删除订单的 API
|
||||
uni.showToast({
|
||||
title: '删除订单成功',
|
||||
icon: 'success',
|
||||
})
|
||||
}
|
||||
}).catch(() => {
|
||||
// 点击取消按钮
|
||||
console.log('取消删除订单')
|
||||
})
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 切换标签
|
||||
function handleTabChange(e: { index: number, name: string }) {
|
||||
activeTab.value = e.name
|
||||
}
|
||||
|
||||
// 跳转搜索
|
||||
function handleToSearch() {
|
||||
router.navigateTo('/pages/search/search')
|
||||
}
|
||||
|
||||
// 点击订单跳转到详情页
|
||||
function handleOrderClick(order: any) {
|
||||
console.log('点击订单', order)
|
||||
if (order.status === 'pending') {
|
||||
router.navigateTo(`/pages/order/pending-order-detail?id=${order.id}`)
|
||||
}
|
||||
else if (order.status === 'waiting' || order.status === 'waiting_arrived') {
|
||||
router.navigateTo(`/pages/order/waiting-service-detail?id=${order.id}`)
|
||||
}
|
||||
else if (order.status === 'cancelled') {
|
||||
router.navigateTo(`/pages/order/cancelled-order-detail?id=${order.id}`)
|
||||
}
|
||||
}
|
||||
|
||||
// 选择地图应用
|
||||
function handleSelectMap(mapType: string) {
|
||||
if (!currentNavigateOrder.value) {
|
||||
return
|
||||
}
|
||||
|
||||
const order = currentNavigateOrder.value
|
||||
const address = order.address || ''
|
||||
// 默认使用上海的坐标(如果订单没有经纬度)
|
||||
const latitude = order.latitude || 31.2304
|
||||
const longitude = order.longitude || 121.4737
|
||||
|
||||
// 关闭弹窗
|
||||
showMapSelectPopup.value = false
|
||||
|
||||
// 根据不同地图类型打开相应的应用
|
||||
// #ifdef MP-WEIXIN
|
||||
// 微信小程序使用 uni.openLocation(会打开系统默认地图选择器)
|
||||
uni.openLocation({
|
||||
latitude,
|
||||
longitude,
|
||||
name: order.serviceName || '',
|
||||
address,
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifndef MP-WEIXIN
|
||||
// H5 和 App 使用 URL scheme
|
||||
let mapUrl = ''
|
||||
const encodedAddress = encodeURIComponent(address)
|
||||
|
||||
switch (mapType) {
|
||||
case 'gaode':
|
||||
// 高德地图
|
||||
// #ifdef APP-PLUS
|
||||
// App 使用 scheme
|
||||
if (uni.getSystemInfoSync().platform === 'ios') {
|
||||
mapUrl = `iosamap://path?sourceApplication=applicationName&dname=${encodedAddress}&dlat=${latitude}&dlon=${longitude}&dev=0&t=0`
|
||||
}
|
||||
else {
|
||||
mapUrl = `androidamap://route?sourceApplication=amap&dname=${encodedAddress}&dlat=${latitude}&dlon=${longitude}&dev=0&t=0`
|
||||
}
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
mapUrl = `https://uri.amap.com/navigation?to=${longitude},${latitude}&toName=${encodedAddress}&mode=car&policy=0&src=mypage&coordinate=gaode&callnative=0`
|
||||
// #endif
|
||||
break
|
||||
case 'tencent':
|
||||
// 腾讯地图
|
||||
// #ifdef APP-PLUS
|
||||
if (uni.getSystemInfoSync().platform === 'ios') {
|
||||
mapUrl = `qqmap://map/routeplan?type=drive&to=${encodedAddress}&tocoord=${latitude},${longitude}`
|
||||
}
|
||||
else {
|
||||
mapUrl = `qqmap://map/routeplan?type=drive&to=${encodedAddress}&tocoord=${latitude},${longitude}`
|
||||
}
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
mapUrl = `https://apis.map.qq.com/uri/v1/routeplan?type=drive&to=${encodedAddress}&tocoord=${latitude},${longitude}&policy=LEAST_TIME&referer=myapp`
|
||||
// #endif
|
||||
break
|
||||
case 'apple':
|
||||
// 苹果地图
|
||||
// #ifdef APP-PLUS
|
||||
mapUrl = `http://maps.apple.com/?daddr=${latitude},${longitude}&dirflg=d`
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
mapUrl = `http://maps.apple.com/?daddr=${latitude},${longitude}&dirflg=d`
|
||||
// #endif
|
||||
break
|
||||
}
|
||||
|
||||
if (mapUrl) {
|
||||
// #ifdef APP-PLUS
|
||||
const plusRuntime = (globalThis as any).plus?.runtime
|
||||
if (plusRuntime) {
|
||||
plusRuntime.openURL(mapUrl, (error: any) => {
|
||||
console.error('打开地图失败:', error)
|
||||
// 如果打开失败,尝试使用系统默认地图
|
||||
uni.openLocation({
|
||||
latitude,
|
||||
longitude,
|
||||
name: order.serviceName || '',
|
||||
address,
|
||||
})
|
||||
})
|
||||
}
|
||||
else {
|
||||
// 如果 plus 不可用,使用系统默认地图
|
||||
uni.openLocation({
|
||||
latitude,
|
||||
longitude,
|
||||
name: order.serviceName || '',
|
||||
address,
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
window.open(mapUrl, '_blank')
|
||||
// #endif
|
||||
}
|
||||
else {
|
||||
// 如果没有指定地图 URL,使用系统默认地图
|
||||
uni.openLocation({
|
||||
latitude,
|
||||
longitude,
|
||||
name: order.serviceName || '',
|
||||
address,
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="order-page">
|
||||
<!-- 顶部搜索和标签栏 -->
|
||||
<view class="order-header bg-[#F6F7F8]">
|
||||
<wd-navbar safe-area-inset-top custom-class="!bg-[#F6F7F8]" :bordered="false" placeholder>
|
||||
<template #left>
|
||||
<view class="search-box" @click="handleToSearch">
|
||||
<wd-search
|
||||
placeholder="搜索订单信息" hide-cancel disabled :placeholder-left="true"
|
||||
custom-class="search-input"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
</wd-navbar>
|
||||
<view class="tabs-container">
|
||||
<wd-tabs v-model="activeTab" swipeable @change="handleTabChange">
|
||||
<wd-tab title="全部" name="all" />
|
||||
<wd-tab title="待接单" name="pending" />
|
||||
<wd-tab title="待服务" name="waiting" />
|
||||
<wd-tab title="服务中" name="serving" />
|
||||
<wd-tab title="已完成" name="completed" />
|
||||
</wd-tabs>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 订单列表 -->
|
||||
<scroll-view class="order-list" scroll-y>
|
||||
<view v-for="(order, index) in filteredOrders" :key="order.id || index" class="order-item">
|
||||
<!-- 订单内容区域(可点击跳转) -->
|
||||
<view class="order-content" @click="handleOrderClick(order)">
|
||||
<!-- 订单头部 -->
|
||||
<view class="order-header-row">
|
||||
<view class="order-title-row">
|
||||
<wd-img
|
||||
width="28rpx" height="28rpx" :src="`${OSS}images/chayishi/order-icon.png`"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
<text class="order-title">订单</text>
|
||||
</view>
|
||||
<view class="order-status-row">
|
||||
<text class="order-status" :class="getStatusClass(order.status)">
|
||||
{{ order.statusText }}
|
||||
</text>
|
||||
<text v-if="order.timer" class="order-timer">{{ order.timer }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 服务信息 -->
|
||||
<view class="order-service-info flex items-center justify-between">
|
||||
<text class="service-name">{{ order.serviceName }}</text>
|
||||
<view class="order-price">
|
||||
<price-format
|
||||
color="#303133" :first-size="32" :second-size="32" :subscript-size="24"
|
||||
:price="order.price"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 价格 -->
|
||||
<view class="mb-24rpx">
|
||||
<text class="service-type" :class="getServiceTypeClass(order.serviceType)">
|
||||
{{ order.serviceType }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 预约时间 -->
|
||||
<view class="order-info-row">
|
||||
<text class="info-label">预约时间:</text>
|
||||
<text class="info-value">{{ order.appointmentTime }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 地址 -->
|
||||
<view class="order-info-row">
|
||||
<text class="info-label">地址:</text>
|
||||
<text class="info-value">{{ order.address }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<view class="order-actions" @click.stop>
|
||||
<wd-button
|
||||
v-for="(action, actionIndex) in getOrderActions(order.status)" :key="actionIndex"
|
||||
:type="action.type" :plain="action.plain" :custom-class="action.customClass" size="small"
|
||||
@click="handleAction(order, action.action)"
|
||||
>
|
||||
{{ action.text }}
|
||||
</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<view v-if="filteredOrders.length === 0" class="empty-state">
|
||||
<text class="empty-text">暂无订单</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 消息提示框 -->
|
||||
<wd-message-box selector="wd-message-box-slot" />
|
||||
|
||||
<!-- 地图选择弹窗 -->
|
||||
<wd-popup
|
||||
v-model="showMapSelectPopup"
|
||||
lock-scroll
|
||||
custom-style="border-radius: 32rpx 32rpx 0rpx 0rpx;"
|
||||
position="bottom"
|
||||
:z-index="200"
|
||||
:close-on-click-overlay="true"
|
||||
>
|
||||
<view class="map-select-popup">
|
||||
<!-- 拖拽指示条 -->
|
||||
<view class="popup-handle" />
|
||||
|
||||
<!-- 地图选项列表 -->
|
||||
<view class="map-options">
|
||||
<view class="map-option-item" @click="handleSelectMap('gaode')">
|
||||
<text class="map-option-text">高德地图</text>
|
||||
</view>
|
||||
<view class="map-divider" />
|
||||
<view class="map-option-item" @click="handleSelectMap('tencent')">
|
||||
<text class="map-option-text">腾讯地图</text>
|
||||
</view>
|
||||
<view class="map-divider" />
|
||||
<view class="map-option-item" @click="handleSelectMap('apple')">
|
||||
<text class="map-option-text">苹果地图</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 取消按钮 -->
|
||||
<view class="map-cancel-wrapper">
|
||||
<view class="map-cancel-divider" />
|
||||
<view class="map-option-item map-cancel-item" @click="showMapSelectPopup = false">
|
||||
<text class="map-option-text map-cancel-text">取消</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f5f5f5;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.order-page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.order-header {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
padding-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
width: 100%;
|
||||
padding: 0 30rpx;
|
||||
margin-top: 20rpx;
|
||||
|
||||
:deep() {
|
||||
.wd-search {
|
||||
background: transparent !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.wd-search__block {
|
||||
background-color: #fff !important;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.wd-search__input {
|
||||
padding-left: 24rpx !important;
|
||||
font-size: 32rpx !important;
|
||||
color: #c9c9c9 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tabs-container {
|
||||
padding: 0 30rpx;
|
||||
margin-top: 20rpx;
|
||||
|
||||
:deep() {
|
||||
.wd-tabs,
|
||||
.wd-tabs__nav {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.wd-tabs__nav-item {
|
||||
font-weight: 400 !important;
|
||||
font-size: 28rpx !important;
|
||||
color: #606266 !important;
|
||||
line-height: 40rpx !important;
|
||||
padding: 0 30rpx !important;
|
||||
}
|
||||
|
||||
.wd-tabs__nav-item.is-active {
|
||||
font-weight: 500 !important;
|
||||
color: #303133 !important;
|
||||
font-size: 32rpx !important;
|
||||
line-height: 44rpx !important;
|
||||
}
|
||||
|
||||
.wd-tabs__line {
|
||||
background-color: #4c9f44 !important;
|
||||
height: 4rpx !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 地图选择弹窗样式
|
||||
.map-select-popup {
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
border-radius: 32rpx 32rpx 0 0;
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
.popup-handle {
|
||||
width: 80rpx;
|
||||
height: 8rpx;
|
||||
background-color: #e5e5e5;
|
||||
border-radius: 4rpx;
|
||||
margin: 20rpx auto 30rpx;
|
||||
}
|
||||
|
||||
.map-options {
|
||||
padding: 0 30rpx;
|
||||
}
|
||||
|
||||
.map-option-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 32rpx 0;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
|
||||
&:active {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
}
|
||||
|
||||
.map-option-text {
|
||||
font-size: 32rpx;
|
||||
color: #303133;
|
||||
line-height: 44rpx;
|
||||
}
|
||||
|
||||
.map-divider {
|
||||
height: 2rpx;
|
||||
background-color: #f6f7f9;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.map-cancel-wrapper {
|
||||
margin-top: 20rpx;
|
||||
padding: 0 30rpx 30rpx;
|
||||
}
|
||||
|
||||
.map-cancel-divider {
|
||||
height: 16rpx;
|
||||
background-color: #f6f7f9;
|
||||
margin: 0 -30rpx 20rpx;
|
||||
}
|
||||
|
||||
.map-cancel-item {
|
||||
border-radius: 16rpx;
|
||||
background-color: #f6f7f9;
|
||||
}
|
||||
|
||||
.map-cancel-text {
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.order-list {
|
||||
flex: 1;
|
||||
padding: 20rpx 30rpx;
|
||||
}
|
||||
|
||||
.order-item {
|
||||
background-color: #fff;
|
||||
border-radius: 16rpx;
|
||||
padding: 30rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.order-content {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.order-header-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.order-title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.order-title {
|
||||
font-size: 28rpx;
|
||||
color: #303133;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.order-status-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.order-status {
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
|
||||
&.status-pending {
|
||||
color: #ff6b35;
|
||||
}
|
||||
|
||||
&.status-waiting {
|
||||
color: #4c9f44;
|
||||
}
|
||||
|
||||
&.status-serving {
|
||||
color: #4c9f44;
|
||||
}
|
||||
|
||||
&.status-completed {
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
&.status-cancelled {
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
|
||||
.order-timer {
|
||||
font-size: 24rpx;
|
||||
color: #ff5951;
|
||||
}
|
||||
|
||||
.order-service-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.service-name {
|
||||
font-size: 30rpx;
|
||||
color: #303133;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.service-type {
|
||||
font-size: 22rpx;
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 4rpx;
|
||||
white-space: nowrap;
|
||||
|
||||
&.service-type-store {
|
||||
border: 2rpx solid #f55726;
|
||||
color: #f55726;
|
||||
}
|
||||
|
||||
&.service-type-home {
|
||||
border: 2rpx solid #b8e6b8;
|
||||
color: #4c9f44;
|
||||
}
|
||||
}
|
||||
|
||||
.order-price {
|
||||
// margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.order-info-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 16rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 34rpx;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
color: #909399;
|
||||
margin-right: 8rpx;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: #606266;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.order-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 20rpx;
|
||||
margin-top: 30rpx;
|
||||
padding-top: 30rpx;
|
||||
border-top: 2rpx solid #f6f7f9;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 100rpx 0;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 28rpx;
|
||||
color: #909399;
|
||||
}
|
||||
</style>
|
||||
754
src/order/pending-order-detail.vue
Normal file
754
src/order/pending-order-detail.vue
Normal file
@ -0,0 +1,754 @@
|
||||
<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>
|
||||
1172
src/order/serving-detail.vue
Normal file
1172
src/order/serving-detail.vue
Normal file
File diff suppressed because it is too large
Load Diff
806
src/order/waiting-service-detail.vue
Normal file
806
src/order/waiting-service-detail.vue
Normal 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>
|
||||
@ -3,11 +3,11 @@ import type { TabBar } from '@uni-helper/vite-plugin-uni-pages'
|
||||
type NativeTabBarItem = TabBar['list'][0]
|
||||
|
||||
type CustomTabBarItem = (Pick<NativeTabBarItem, 'text' | 'pagePath'> & {
|
||||
iconType: 'uniUi' | 'uiLib' | 'unocss' | 'iconfont' | 'image' // 不建议用 image 模式,需要配置2张图
|
||||
icon: any // 其实是 string 类型,这里是为了避免 ts 报错 (tabbar/index.vue 里面 uni-icons 那行)
|
||||
activeIcon?: string // 只有在 image 模式下才需要,传递的是高亮的图片(PS: 不建议用 image 模式)
|
||||
badge?: number | 'dot' // badge 显示一个数字或 小红点(样式可以直接在 tabbar/index.vue 里面修改)
|
||||
isBulge?: boolean // 是否是中间的鼓包tabbarItem
|
||||
iconType: 'uniUi' | 'uiLib' | 'unocss' | 'iconfont' | 'image' // 不建议用 image 模式,需要配置2张图
|
||||
icon: any // 其实是 string 类型,这里是为了避免 ts 报错 (tabbar/index.vue 里面 uni-icons 那行)
|
||||
activeIcon?: string // 只有在 image 模式下才需要,传递的是高亮的图片(PS: 不建议用 image 模式)
|
||||
badge?: number | 'dot' // badge 显示一个数字或 小红点(样式可以直接在 tabbar/index.vue 里面修改)
|
||||
isBulge?: boolean // 是否是中间的鼓包tabbarItem
|
||||
})
|
||||
|
||||
/**
|
||||
@ -20,117 +20,121 @@ type CustomTabBarItem = (Pick<NativeTabBarItem, 'text' | 'pagePath'> & {
|
||||
* 温馨提示:本文件的任何代码更改了之后,都需要重新运行,否则 pages.json 不会更新导致配置不生效
|
||||
*/
|
||||
export const TABBAR_MAP = {
|
||||
NO_TABBAR: 0,
|
||||
NATIVE_TABBAR: 1,
|
||||
CUSTOM_TABBAR_WITH_CACHE: 2,
|
||||
CUSTOM_TABBAR_WITHOUT_CACHE: 3,
|
||||
NO_TABBAR: 0,
|
||||
NATIVE_TABBAR: 1,
|
||||
CUSTOM_TABBAR_WITH_CACHE: 2,
|
||||
CUSTOM_TABBAR_WITHOUT_CACHE: 3,
|
||||
}
|
||||
|
||||
// TODO: 1/3. 通过这里切换使用tabbar的策略
|
||||
export const selectedTabbarStrategy = TABBAR_MAP.NATIVE_TABBAR
|
||||
// 使用自定义tabbar(wot-design-uni组件),支持缓存
|
||||
export const selectedTabbarStrategy = TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE
|
||||
|
||||
// TODO: 2/3. 更新下面的 tabbar 配置
|
||||
// 如果是使用 NO_TABBAR(0),nativeTabbarList 和 customTabbarList 都不生效(里面的配置不用管)
|
||||
// 如果是使用 NATIVE_TABBAR(1),customTabbarList 不生效(里面的配置不用管)
|
||||
// 如果是使用 CUSTOM_TABBAR(2,3),nativeTabbarList 不生效(里面的配置不用管)
|
||||
// pagePath 是 nativeTabbarList 和 customTabbarList 的关联点,如果没有对应上,会有问题!!
|
||||
export const nativeTabbarList: NativeTabBarItem[] = [
|
||||
{
|
||||
iconPath: 'static/tabbar/home.png',
|
||||
selectedIconPath: 'static/tabbar/home_s.png',
|
||||
pagePath: 'pages/index/index',
|
||||
text: '首页',
|
||||
},
|
||||
{
|
||||
iconPath: 'static/tabbar/order.png',
|
||||
selectedIconPath: 'static/tabbar/order_s.png',
|
||||
pagePath: 'pages/order/order',
|
||||
text: '订单',
|
||||
},
|
||||
{
|
||||
iconPath: 'static/tabbar/store.png',
|
||||
selectedIconPath: 'static/tabbar/store_s.png',
|
||||
pagePath: 'pages/store/store',
|
||||
text: '门店管理',
|
||||
},
|
||||
{
|
||||
iconPath: 'static/tabbar/my.png',
|
||||
selectedIconPath: 'static/tabbar/my_s.png',
|
||||
pagePath: 'pages/my/my',
|
||||
text: '我的',
|
||||
}
|
||||
export const nativeTabbarList: (NativeTabBarItem & {
|
||||
icon?: string // wot-design-uni 图标名称
|
||||
selectedIcon?: string // wot-design-uni 选中图标名称
|
||||
})[] = [
|
||||
{
|
||||
iconPath: 'static/tabbar/home.png',
|
||||
selectedIconPath: 'static/tabbar/home_s.png',
|
||||
pagePath: 'pages/index/index',
|
||||
text: '首页',
|
||||
// icon: 'home-o',
|
||||
// selectedIcon: 'home',
|
||||
},
|
||||
{
|
||||
iconPath: 'static/tabbar/order.png',
|
||||
selectedIconPath: 'static/tabbar/order_s.png',
|
||||
pagePath: 'pages/order/order',
|
||||
text: '订单',
|
||||
// icon: 'orders-o',
|
||||
// selectedIcon: 'orders',
|
||||
},
|
||||
{
|
||||
iconPath: 'static/tabbar/my.png',
|
||||
selectedIconPath: 'static/tabbar/my_s.png',
|
||||
pagePath: 'pages/my/my',
|
||||
text: '我的',
|
||||
// icon: 'user-o',
|
||||
// selectedIcon: 'user',
|
||||
},
|
||||
]
|
||||
|
||||
// pagePath 是 nativeTabbarList 和 customTabbarList 的关联点,如果没有对应上,会有问题!!
|
||||
// 如果希望通过接口调用 customTabbarList,可以在 tabbar/index.vue 文件里面调用接口
|
||||
// 本文件因为需要提前编译生成 pages.json, 接口拦截还不生效,无法正常调用接口
|
||||
export const customTabbarList: CustomTabBarItem[] = [
|
||||
{
|
||||
// text 和 pagePath 可以自己直接写,也可以通过索引从 nativeTabbarList 中获取
|
||||
text: '首页',
|
||||
pagePath: 'pages/index/index', // pagePath 是两者的关联点
|
||||
// 本框架内置了 uniapp 官方UI库 (uni-ui)的图标库
|
||||
// 使用方式如:<uni-icons type="home" size="30"/>
|
||||
// 图标列表地址:https://uniapp.dcloud.net.cn/component/uniui/uni-icons.html
|
||||
iconType: 'uniUi',
|
||||
icon: 'home',
|
||||
// badge: 'dot',
|
||||
},
|
||||
{
|
||||
text: nativeTabbarList[1].text,
|
||||
pagePath: nativeTabbarList[1].pagePath, // pagePath 是两者的关联点
|
||||
// 注意 unocss 图标需要如下处理:(二选一)
|
||||
// 1)在fg-tabbar.vue页面上引入一下并注释掉(见tabbar/index.vue代码第2行)
|
||||
// 2)配置到 unocss.config.ts 的 safelist 中
|
||||
iconType: 'unocss',
|
||||
icon: 'i-carbon-code',
|
||||
// badge: 10,
|
||||
},
|
||||
{
|
||||
// text 和 pagePath 可以自己直接写,也可以通过索引从 nativeTabbarList 中获取
|
||||
text: '首页',
|
||||
pagePath: 'pages/index/index', // pagePath 是两者的关联点
|
||||
// 本框架内置了 uniapp 官方UI库 (uni-ui)的图标库
|
||||
// 使用方式如:<uni-icons type="home" size="30"/>
|
||||
// 图标列表地址:https://uniapp.dcloud.net.cn/component/uniui/uni-icons.html
|
||||
iconType: 'uniUi',
|
||||
icon: 'home',
|
||||
// badge: 'dot',
|
||||
},
|
||||
{
|
||||
text: nativeTabbarList[1].text,
|
||||
pagePath: nativeTabbarList[1].pagePath, // pagePath 是两者的关联点
|
||||
// 注意 unocss 图标需要如下处理:(二选一)
|
||||
// 1)在fg-tabbar.vue页面上引入一下并注释掉(见tabbar/index.vue代码第2行)
|
||||
// 2)配置到 unocss.config.ts 的 safelist 中
|
||||
iconType: 'unocss',
|
||||
icon: 'i-carbon-code',
|
||||
// badge: 10,
|
||||
},
|
||||
|
||||
// {
|
||||
// pagePath: 'pages/mine/index',
|
||||
// text: '我的',
|
||||
// // 注意 iconfont 图标需要额外加上 'iconfont',如下
|
||||
// iconType: 'iconfont',
|
||||
// icon: 'iconfont icon-my',
|
||||
// },
|
||||
// {
|
||||
// pagePath: 'pages/index/index',
|
||||
// text: '首页',
|
||||
// // 使用 ‘image’时,需要配置 icon + iconActive 2张图片(不推荐)
|
||||
// // 既然已经用了自定义tabbar了,就不建议用图片了,所以不推荐
|
||||
// iconType: 'image',
|
||||
// icon: '/static/tabbar/home.png',
|
||||
// iconActive: '/static/tabbar/homeHL.png',
|
||||
// },
|
||||
// {
|
||||
// pagePath: 'pages/mine/index',
|
||||
// text: '我的',
|
||||
// // 注意 iconfont 图标需要额外加上 'iconfont',如下
|
||||
// iconType: 'iconfont',
|
||||
// icon: 'iconfont icon-my',
|
||||
// },
|
||||
// {
|
||||
// pagePath: 'pages/index/index',
|
||||
// text: '首页',
|
||||
// // 使用 ‘image’时,需要配置 icon + iconActive 2张图片(不推荐)
|
||||
// // 既然已经用了自定义tabbar了,就不建议用图片了,所以不推荐
|
||||
// iconType: 'image',
|
||||
// icon: '/static/tabbar/home.png',
|
||||
// iconActive: '/static/tabbar/homeHL.png',
|
||||
// },
|
||||
]
|
||||
|
||||
// NATIVE_TABBAR(1) 和 CUSTOM_TABBAR_WITH_CACHE(2) 时,需要tabbar缓存
|
||||
/** 是否启用 tabbar 缓存 */
|
||||
export const tabbarCacheEnable
|
||||
= [TABBAR_MAP.NATIVE_TABBAR, TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE].includes(selectedTabbarStrategy)
|
||||
= [TABBAR_MAP.NATIVE_TABBAR, TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE].includes(selectedTabbarStrategy)
|
||||
|
||||
// CUSTOM_TABBAR_WITH_CACHE(2) 和 CUSTOM_TABBAR_WITHOUT_CACHE(3) 时,启用自定义tabbar
|
||||
/** 是否启用自定义 tabbar */
|
||||
export const customTabbarEnable
|
||||
= [TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE, TABBAR_MAP.CUSTOM_TABBAR_WITHOUT_CACHE].includes(selectedTabbarStrategy)
|
||||
= [TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE, TABBAR_MAP.CUSTOM_TABBAR_WITHOUT_CACHE].includes(selectedTabbarStrategy)
|
||||
|
||||
// CUSTOM_TABBAR_WITH_CACHE(2)时,需要隐藏原生tabbar
|
||||
/** 是否需要隐藏原生 tabbar */
|
||||
export const nativeTabbarNeedHide = selectedTabbarStrategy === TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE
|
||||
|
||||
const _tabbar: TabBar = {
|
||||
// 只有微信小程序支持 custom。App 和 H5 不生效
|
||||
custom: selectedTabbarStrategy === TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE,
|
||||
color: '#999999',
|
||||
selectedColor: '#018d71',
|
||||
backgroundColor: '#FFF',
|
||||
borderStyle: 'black',
|
||||
height: '50px',
|
||||
fontSize: '10px',
|
||||
iconWidth: '24px',
|
||||
spacing: '3px',
|
||||
list: nativeTabbarList as unknown as TabBar['list'],
|
||||
// 只有微信小程序支持 custom。App 和 H5 不生效
|
||||
custom: selectedTabbarStrategy === TABBAR_MAP.CUSTOM_TABBAR_WITH_CACHE,
|
||||
color: '#999999',
|
||||
selectedColor: '#018d71',
|
||||
backgroundColor: '#FFF',
|
||||
borderStyle: 'black',
|
||||
height: '50px',
|
||||
fontSize: '10px',
|
||||
iconWidth: '24px',
|
||||
spacing: '3px',
|
||||
list: nativeTabbarList as unknown as TabBar['list'],
|
||||
}
|
||||
|
||||
export const tabbarList = nativeTabbarList
|
||||
|
||||
@ -1,45 +1,62 @@
|
||||
<script setup lang="ts">
|
||||
// 'i-carbon-code',
|
||||
import { customTabbarList as _tabBarList, customTabbarEnable, nativeTabbarNeedHide, tabbarCacheEnable } from './config'
|
||||
import { customTabbarEnable, nativeTabbarList, nativeTabbarNeedHide, tabbarCacheEnable } from './config'
|
||||
import { tabbarStore } from './store'
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 将自定义节点设置成虚拟的(去掉自定义组件包裹层),更加接近Vue组件的表现,能更好的使用flex属性
|
||||
defineOptions({
|
||||
virtualHost: true,
|
||||
const OSS = inject('OSS')
|
||||
|
||||
// 根据当前路由确定激活的 tabbar
|
||||
const activeTabbar = ref(nativeTabbarList[tabbarStore.curIdx]?.pagePath || nativeTabbarList[0]?.pagePath || '')
|
||||
|
||||
// 初始化激活的 tabbar
|
||||
function initActiveTabbar() {
|
||||
const pages = getCurrentPages()
|
||||
if (pages.length > 0) {
|
||||
const currentPage = pages[pages.length - 1]
|
||||
const path = `/${currentPage.route}`
|
||||
|
||||
// 根据当前路径找到对应的 tabbar
|
||||
const item = nativeTabbarList.find(tab => `/${tab.pagePath}` === path)
|
||||
if (item) {
|
||||
const index = nativeTabbarList.findIndex(tab => tab.pagePath === item.pagePath)
|
||||
tabbarStore.setCurIdx(index)
|
||||
activeTabbar.value = item.pagePath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化
|
||||
onLoad(() => {
|
||||
initActiveTabbar()
|
||||
|
||||
// 解决原生 tabBar 未隐藏导致有2个 tabBar 的问题
|
||||
if (customTabbarEnable && nativeTabbarNeedHide) {
|
||||
uni.hideTabBar({
|
||||
fail(err) {
|
||||
console.log('hideTabBar fail: ', err)
|
||||
},
|
||||
success(res) {
|
||||
// console.log('hideTabBar success: ', res)
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
|
||||
// TODO 1/2: 中间的鼓包tabbarItem的开关
|
||||
const BULGE_ENABLE = false
|
||||
function handleClickBulge() {
|
||||
uni.showToast({
|
||||
title: '点击了中间的鼓包tabbarItem',
|
||||
icon: 'none',
|
||||
})
|
||||
}
|
||||
// 监听页面变化
|
||||
onShow(() => {
|
||||
initActiveTabbar()
|
||||
})
|
||||
|
||||
/** tabbarList 里面的 path 从 pages.config.ts 得到 */
|
||||
const tabbarList = _tabBarList.map(item => ({ ...item, path: `/${item.pagePath}` }))
|
||||
if (BULGE_ENABLE) {
|
||||
if (tabbarList.length % 2 === 1) {
|
||||
console.error('tabbar 数量必须是偶数,否则样式很奇怪!!')
|
||||
}
|
||||
tabbarList.splice(tabbarList.length / 2, 0, {
|
||||
isBulge: true,
|
||||
} as any)
|
||||
}
|
||||
function handleClick(index: number) {
|
||||
// 点击原来的不做操作
|
||||
if (index === tabbarStore.curIdx) {
|
||||
// tabbar 切换处理
|
||||
function handleTabbarChange({ value }: { value: string }) {
|
||||
const item = nativeTabbarList.find(tab => tab.pagePath === value)
|
||||
if (!item)
|
||||
return
|
||||
}
|
||||
if (tabbarList[index].isBulge) {
|
||||
handleClickBulge()
|
||||
return
|
||||
}
|
||||
const url = tabbarList[index].path
|
||||
|
||||
const index = nativeTabbarList.findIndex(tab => tab.pagePath === value)
|
||||
tabbarStore.setCurIdx(index)
|
||||
activeTabbar.value = value
|
||||
|
||||
const url = `/${item.pagePath}`
|
||||
if (tabbarCacheEnable) {
|
||||
uni.switchTab({ url })
|
||||
}
|
||||
@ -47,119 +64,43 @@ function handleClick(index: number) {
|
||||
uni.navigateTo({ url })
|
||||
}
|
||||
}
|
||||
onLoad(() => {
|
||||
// 解决原生 tabBar 未隐藏导致有2个 tabBar 的问题
|
||||
nativeTabbarNeedHide
|
||||
&& uni.hideTabBar({
|
||||
fail(err) {
|
||||
console.log('hideTabBar fail: ', err)
|
||||
},
|
||||
success(res) {
|
||||
// console.log('hideTabBar success: ', res)
|
||||
},
|
||||
})
|
||||
})
|
||||
const activeColor = '#1890ff'
|
||||
const inactiveColor = '#666'
|
||||
function getColorByIndex(index: number) {
|
||||
return tabbarStore.curIdx === index ? activeColor : inactiveColor
|
||||
}
|
||||
|
||||
function getImageByIndex(index: number, item: { iconActive?: string, icon: string }) {
|
||||
if (!item.iconActive) {
|
||||
console.warn('image 模式下,需要配置 iconActive (高亮时的图片),否则无法切换高亮图片')
|
||||
return item.icon
|
||||
}
|
||||
return tabbarStore.curIdx === index ? item.iconActive : item.icon
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view v-if="customTabbarEnable" class="h-50px pb-safe">
|
||||
<view class="border-and-fixed bg-white" @touchmove.stop.prevent>
|
||||
<view class="h-50px flex items-center">
|
||||
<view
|
||||
v-for="(item, index) in tabbarList" :key="index"
|
||||
class="flex flex-1 flex-col items-center justify-center"
|
||||
:style="{ color: getColorByIndex(index) }"
|
||||
@click="handleClick(index)"
|
||||
>
|
||||
<view v-if="item.isBulge" class="relative">
|
||||
<!-- 中间一个鼓包tabbarItem的处理 -->
|
||||
<view class="bulge">
|
||||
<!-- TODO 2/2: 通常是一个图片,或者icon,点击触发业务逻辑 -->
|
||||
<!-- 常见的是:扫描按钮、发布按钮、更多按钮等 -->
|
||||
<image class="mt-6rpx h-200rpx w-200rpx" src="/static/tabbar/scan.png" />
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="relative px-3 text-center">
|
||||
<template v-if="item.iconType === 'uniUi'">
|
||||
<uni-icons :type="item.icon" size="20" :color="getColorByIndex(index)" />
|
||||
</template>
|
||||
<template v-if="item.iconType === 'uiLib'">
|
||||
<!-- TODO: 以下内容请根据选择的UI库自行替换 -->
|
||||
<!-- 如:<wd-icon name="home" /> (https://wot-design-uni.cn/component/icon.html) -->
|
||||
<!-- 如:<uv-icon name="home" /> (https://www.uvui.cn/components/icon.html) -->
|
||||
<!-- 如:<sar-icon name="image" /> (https://sard.wzt.zone/sard-uniapp-docs/components/icon)(sar没有home图标^_^) -->
|
||||
<wd-icon :name="item.icon" size="20" />
|
||||
</template>
|
||||
<template v-if="item.iconType === 'unocss' || item.iconType === 'iconfont'">
|
||||
<view :class="item.icon" class="text-20px" />
|
||||
</template>
|
||||
<template v-if="item.iconType === 'image'">
|
||||
<image :src="getImageByIndex(index, item)" mode="scaleToFill" class="h-20px w-20px" />
|
||||
</template>
|
||||
<view class="mt-2px text-12px">
|
||||
{{ item.text }}
|
||||
</view>
|
||||
<!-- 角标显示 -->
|
||||
<view v-if="item.badge">
|
||||
<template v-if="item.badge === 'dot'">
|
||||
<view class="absolute right-0 top-0 h-2 w-2 rounded-full bg-#f56c6c" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<view class="absolute right-0 top-0 h-4 w-4 center rounded-full bg-#f56c6c text-center text-xs text-white">
|
||||
{{ item.badge }}
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<wd-tabbar
|
||||
v-if="customTabbarEnable"
|
||||
v-model="activeTabbar"
|
||||
active-color="#018d71"
|
||||
inactive-color="#999999"
|
||||
|
||||
<view class="pb-safe" />
|
||||
</view>
|
||||
</view>
|
||||
safe-area-inset-bottom fixed
|
||||
@change="handleTabbarChange"
|
||||
>
|
||||
<wd-tabbar-item
|
||||
v-for="(item, index) in nativeTabbarList"
|
||||
:key="index"
|
||||
:title="item.text"
|
||||
:name="item.pagePath"
|
||||
>
|
||||
<template #icon>
|
||||
<!-- 如果配置了图标名称,使用 wot-design-uni 图标,否则使用图片 -->
|
||||
<wd-icon
|
||||
v-if="item.icon && item.selectedIcon"
|
||||
:name="activeTabbar === item.pagePath ? item.selectedIcon : item.icon"
|
||||
:color="activeTabbar === item.pagePath ? '#018d71' : '#999999'"
|
||||
size="40rpx"
|
||||
/>
|
||||
<wd-img
|
||||
v-else
|
||||
height="40rpx"
|
||||
width="40rpx"
|
||||
:src="activeTabbar === item.pagePath ? `/${item.selectedIconPath}` : `/${item.iconPath}`"
|
||||
/>
|
||||
</template>
|
||||
</wd-tabbar-item>
|
||||
</wd-tabbar>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.border-and-fixed {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
border-top: 1px solid #eee;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
// 中间鼓包的样式
|
||||
.bulge {
|
||||
position: absolute;
|
||||
top: -20px;
|
||||
left: 50%;
|
||||
transform-origin: top center;
|
||||
transform: translateX(-50%) scale(0.5) translateY(-33%);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 250rpx;
|
||||
height: 250rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #fff;
|
||||
box-shadow: inset 0 0 0 1px #fefefe;
|
||||
|
||||
&:active {
|
||||
// opacity: 0.8;
|
||||
}
|
||||
}
|
||||
// wot-design-uni tabbar 组件自带样式,无需额外样式
|
||||
</style>
|
||||
|
||||
@ -82,6 +82,63 @@ export default ({ command, mode }) => {
|
||||
},
|
||||
},
|
||||
UnoCSS(),
|
||||
// 修复微信小程序 WXSS 兼容性问题
|
||||
{
|
||||
name: 'fix-wxss-compatibility',
|
||||
enforce: 'post',
|
||||
transform(code, id) {
|
||||
if (UNI_PLATFORM === 'mp-weixin' && (id.endsWith('.wxss') || id.endsWith('.css'))) {
|
||||
// 将 rgb(r g b / alpha) 转换为 rgba(r, g, b, alpha)
|
||||
code = code.replace(/rgb\((\d+)\s+(\d+)\s+(\d+)\s*\/\s*([\d.]+)\)/g, 'rgba($1, $2, $3, $4)')
|
||||
// 移除 ::backdrop 伪元素及其内容(微信小程序不支持)
|
||||
code = code.replace(/::backdrop\s*\{[^}]*\}/g, '')
|
||||
// 移除行首空格,避免 WXSS 解析错误
|
||||
code = code.replace(/^\s+/gm, '')
|
||||
// 移除或修复可能导致问题的属性选择器(UnoCSS attributify 生成)
|
||||
// 这些选择器包含转义序列(如 \32, \33)可能导致 WXSS 解析错误
|
||||
code = code.replace(/\[\\?\d+[^\]]*_a_[^\]]*=""]\{[^}]*\}/g, '')
|
||||
code = code.replace(/\[[^\]]*_a_[^\]]*=""]\{[^}]*\}/g, '')
|
||||
// 移除包含转义序列的属性选择器(如 [\32 6_a_25=""])
|
||||
code = code.replace(/\[\\\d+[^\]]*\]\{[^}]*\}/g, '')
|
||||
// 修复 SVG 数据 URL 中的引号问题,转义单引号
|
||||
code = code.replace(/url\("data:image\/svg\+xml[^"]*"\)/g, (match) => {
|
||||
return match.replace(/'/g, "\\'")
|
||||
})
|
||||
return {
|
||||
code,
|
||||
map: null,
|
||||
}
|
||||
}
|
||||
},
|
||||
generateBundle(options, bundle) {
|
||||
if (UNI_PLATFORM === 'mp-weixin') {
|
||||
// 处理所有生成的 CSS 文件
|
||||
Object.keys(bundle).forEach((fileName) => {
|
||||
const file = bundle[fileName]
|
||||
if (file.type === 'asset' && (fileName.endsWith('.wxss') || fileName.endsWith('.css'))) {
|
||||
let css = file.source.toString()
|
||||
// 将 rgb(r g b / alpha) 转换为 rgba(r, g, b, alpha)
|
||||
css = css.replace(/rgb\((\d+)\s+(\d+)\s+(\d+)\s*\/\s*([\d.]+)\)/g, 'rgba($1, $2, $3, $4)')
|
||||
// 移除 ::backdrop 伪元素及其内容(微信小程序不支持)
|
||||
css = css.replace(/::backdrop\s*\{[^}]*\}/g, '')
|
||||
// 移除行首空格,避免 WXSS 解析错误
|
||||
css = css.replace(/^\s+/gm, '')
|
||||
// 移除或修复可能导致问题的属性选择器(UnoCSS attributify 生成)
|
||||
// 这些选择器包含转义序列(如 \32, \33)可能导致 WXSS 解析错误
|
||||
css = css.replace(/\[\\?\d+[^\]]*_a_[^\]]*=""]\{[^}]*\}/g, '')
|
||||
css = css.replace(/\[[^\]]*_a_[^\]]*=""]\{[^}]*\}/g, '')
|
||||
// 移除包含转义序列的属性选择器(如 [\32 6_a_25=""])
|
||||
css = css.replace(/\[\\\d+[^\]]*\]\{[^}]*\}/g, '')
|
||||
// 修复 SVG 数据 URL 中的引号问题,转义单引号
|
||||
css = css.replace(/url\("data:image\/svg\+xml[^"]*"\)/g, (match) => {
|
||||
return match.replace(/'/g, "\\'")
|
||||
})
|
||||
file.source = css
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
AutoImport({
|
||||
imports: ['vue', 'uni-app'],
|
||||
dts: 'src/types/auto-import.d.ts',
|
||||
|
||||
Reference in New Issue
Block a user