新增订单列表页面,支持按状态筛选、下拉刷新和加载更多功能。新增订单详情页面,展示服务地址、宠物信息、服务项目及费用等详细信息。优化支付成功页面,调整布局和样式,提升用户体验。新增取消订单功能,并调整导航路径以适配新页面结构。master
@ -0,0 +1,91 @@ | |||
<template> | |||
<view class="order-info-card"> | |||
<view class="card-title"> | |||
<text>其他信息</text> | |||
</view> | |||
<view class="info-content"> | |||
<view class="info-item"> | |||
<text class="info-label">订单编号:</text> | |||
<text class="info-value">{{orderInfo.orderNumber}}</text> | |||
</view> | |||
<view class="info-item"> | |||
<text class="info-label">下单时间:</text> | |||
<text class="info-value">{{orderInfo.orderTime}}</text> | |||
</view> | |||
<view class="info-item" v-if="orderInfo.paymentTime"> | |||
<text class="info-label">支付时间:</text> | |||
<text class="info-value">{{orderInfo.paymentTime}}</text> | |||
</view> | |||
</view> | |||
</view> | |||
</template> | |||
<script> | |||
export default { | |||
props: { | |||
orderInfo: { | |||
type: Object, | |||
default: () => ({ | |||
orderNumber: '', | |||
orderTime: '', | |||
paymentTime: '' | |||
}) | |||
} | |||
} | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.order-info-card { | |||
background-color: #FFFFFF; | |||
border-radius: 20rpx; | |||
padding: 30rpx; | |||
margin-bottom: 20rpx; | |||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); | |||
} | |||
.card-title { | |||
font-size: 32rpx; | |||
font-weight: bold; | |||
color: #333; | |||
margin-bottom: 20rpx; | |||
display: flex; | |||
align-items: center; | |||
&::before { | |||
content: ''; | |||
display: inline-block; | |||
width: 8rpx; | |||
height: 32rpx; | |||
background-color: #FFAA48; | |||
margin-right: 16rpx; | |||
border-radius: 4rpx; | |||
} | |||
} | |||
.info-content { | |||
.info-item { | |||
display: flex; | |||
margin-bottom: 16rpx; | |||
&:last-child { | |||
margin-bottom: 0; | |||
} | |||
.info-label { | |||
font-size: 26rpx; | |||
color: #666; | |||
width: 140rpx; | |||
} | |||
.info-value { | |||
font-size: 26rpx; | |||
color: #333; | |||
flex: 1; | |||
} | |||
} | |||
} | |||
</style> |
@ -0,0 +1,71 @@ | |||
<template> | |||
<view class="service-address-card"> | |||
<view class="card-title"> | |||
<text>服务地址</text> | |||
</view> | |||
<view class="address-content"> | |||
<view class="address-text">{{address.address}}</view> | |||
<view class="contact-text">{{address.contact}}</view> | |||
</view> | |||
</view> | |||
</template> | |||
<script> | |||
export default { | |||
props: { | |||
address: { | |||
type: Object, | |||
default: () => ({ | |||
address: '', | |||
contact: '' | |||
}) | |||
} | |||
} | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.service-address-card { | |||
background-color: #FFFFFF; | |||
border-radius: 20rpx; | |||
padding: 30rpx; | |||
margin-bottom: 20rpx; | |||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); | |||
} | |||
.card-title { | |||
font-size: 32rpx; | |||
font-weight: bold; | |||
color: #333; | |||
margin-bottom: 20rpx; | |||
display: flex; | |||
align-items: center; | |||
&::before { | |||
content: ''; | |||
display: inline-block; | |||
width: 8rpx; | |||
height: 32rpx; | |||
background-color: #FFAA48; | |||
margin-right: 16rpx; | |||
border-radius: 4rpx; | |||
} | |||
} | |||
.address-content { | |||
padding: 20rpx; | |||
background-color: #FFF9EF; | |||
border-radius: 10rpx; | |||
} | |||
.address-text { | |||
font-size: 28rpx; | |||
color: #333; | |||
margin-bottom: 10rpx; | |||
} | |||
.contact-text { | |||
font-size: 26rpx; | |||
color: #666; | |||
} | |||
</style> |
@ -0,0 +1,216 @@ | |||
<template> | |||
<view class="service-items-card"> | |||
<view class="card-title"> | |||
<text>服务项目及费用</text> | |||
</view> | |||
<!-- 服务项目列表 --> | |||
<view class="service-items-list"> | |||
<view class="service-item" v-for="(item, index) in items" :key="index"> | |||
<view class="item-header"> | |||
<view class="item-id">{{item.id}}</view> | |||
<view class="item-name">{{item.name}}</view> | |||
<view class="item-price">¥{{item.price.toFixed(2)}}</view> | |||
</view> | |||
<!-- 宠物名称 --> | |||
<view class="item-pet"> | |||
<text>{{item.pet}}</text> | |||
</view> | |||
<!-- 定制服务 --> | |||
<view class="custom-services" v-if="item.customServices && item.customServices.length > 0"> | |||
<view class="custom-service-item" v-for="(service, serviceIndex) in item.customServices" :key="serviceIndex"> | |||
<view class="service-name">- {{service.name}}</view> | |||
<view class="service-price">¥{{service.price.toFixed(2)}} × {{service.quantity}} 次</view> | |||
</view> | |||
</view> | |||
</view> | |||
</view> | |||
<!-- 费用合计 --> | |||
<view class="cost-summary"> | |||
<view class="cost-item"> | |||
<text class="cost-label">费用合计</text> | |||
<text class="cost-value">¥{{totalAmount.toFixed(2)}}</text> | |||
</view> | |||
<view class="cost-item discount" v-if="discount > 0"> | |||
<text class="cost-label">平台优惠</text> | |||
<text class="cost-value">- ¥{{discount.toFixed(2)}}</text> | |||
</view> | |||
<view class="cost-item discount" v-if="memberDiscount > 0"> | |||
<text class="cost-label">会员优惠</text> | |||
<text class="cost-value">- ¥{{memberDiscount.toFixed(2)}}</text> | |||
</view> | |||
<view class="cost-item total"> | |||
<text class="cost-label">应付金额</text> | |||
<text class="cost-value">¥{{finalAmount.toFixed(2)}}</text> | |||
</view> | |||
</view> | |||
</view> | |||
</template> | |||
<script> | |||
export default { | |||
props: { | |||
items: { | |||
type: Array, | |||
default: () => [] | |||
}, | |||
totalAmount: { | |||
type: Number, | |||
default: 0 | |||
}, | |||
discount: { | |||
type: Number, | |||
default: 0 | |||
}, | |||
memberDiscount: { | |||
type: Number, | |||
default: 0 | |||
}, | |||
finalAmount: { | |||
type: Number, | |||
default: 0 | |||
} | |||
} | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.service-items-card { | |||
background-color: #FFFFFF; | |||
border-radius: 20rpx; | |||
padding: 30rpx; | |||
margin-bottom: 20rpx; | |||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); | |||
} | |||
.card-title { | |||
font-size: 32rpx; | |||
font-weight: bold; | |||
color: #333; | |||
margin-bottom: 20rpx; | |||
display: flex; | |||
align-items: center; | |||
&::before { | |||
content: ''; | |||
display: inline-block; | |||
width: 8rpx; | |||
height: 32rpx; | |||
background-color: #FFAA48; | |||
margin-right: 16rpx; | |||
border-radius: 4rpx; | |||
} | |||
} | |||
.service-items-list { | |||
.service-item { | |||
padding: 20rpx 0; | |||
border-bottom: 1px solid #EEEEEE; | |||
&:last-child { | |||
border-bottom: none; | |||
} | |||
.item-header { | |||
display: flex; | |||
align-items: center; | |||
margin-bottom: 10rpx; | |||
.item-id { | |||
font-size: 24rpx; | |||
color: #999; | |||
margin-right: 10rpx; | |||
} | |||
.item-name { | |||
font-size: 28rpx; | |||
color: #333; | |||
flex: 1; | |||
} | |||
.item-price { | |||
font-size: 28rpx; | |||
color: #FF5252; | |||
font-weight: bold; | |||
} | |||
} | |||
.item-pet { | |||
font-size: 24rpx; | |||
color: #666; | |||
margin-bottom: 10rpx; | |||
} | |||
.custom-services { | |||
padding: 10rpx 0 10rpx 20rpx; | |||
.custom-service-item { | |||
display: flex; | |||
justify-content: space-between; | |||
align-items: center; | |||
margin-bottom: 6rpx; | |||
.service-name { | |||
font-size: 24rpx; | |||
color: #666; | |||
} | |||
.service-price { | |||
font-size: 24rpx; | |||
color: #999; | |||
} | |||
} | |||
} | |||
} | |||
} | |||
.cost-summary { | |||
margin-top: 30rpx; | |||
padding-top: 20rpx; | |||
border-top: 1px dashed #EEEEEE; | |||
.cost-item { | |||
display: flex; | |||
justify-content: space-between; | |||
align-items: center; | |||
margin-bottom: 10rpx; | |||
.cost-label { | |||
font-size: 26rpx; | |||
color: #666; | |||
} | |||
.cost-value { | |||
font-size: 26rpx; | |||
color: #333; | |||
} | |||
&.discount { | |||
.cost-value { | |||
color: #FF5252; | |||
} | |||
} | |||
&.total { | |||
margin-top: 20rpx; | |||
padding-top: 20rpx; | |||
border-top: 1px dashed #EEEEEE; | |||
.cost-label { | |||
font-size: 28rpx; | |||
font-weight: bold; | |||
color: #333; | |||
} | |||
.cost-value { | |||
font-size: 32rpx; | |||
font-weight: bold; | |||
color: #FF5252; | |||
} | |||
} | |||
} | |||
} | |||
</style> |
@ -0,0 +1,120 @@ | |||
<template> | |||
<view class="service-pets-card"> | |||
<view class="card-title"> | |||
<text>服务宠物</text> | |||
</view> | |||
<view class="pets-list"> | |||
<view class="pet-item" v-for="(pet, index) in pets" :key="index"> | |||
<view class="pet-avatar"> | |||
<image :src="pet.avatar || '/static/images/personal/pet.png'" mode="aspectFill"></image> | |||
<view class="pet-tag" v-if="pet.tag"> | |||
<text>{{pet.tag}}</text> | |||
</view> | |||
</view> | |||
<view class="pet-details"> | |||
<view class="pet-name-gender" style="display: flex; align-items: center;"> | |||
<text class="pet-name">{{pet.name}}</text> | |||
<view class="pet-gender" style="margin-left: 10rpx; display: flex; align-items: center;"> | |||
<image :src="pet.gender=='男生'?'/static/images/details/boy.svg':'/static/images/details/girl.svg'" style="width: 24rpx; height: 24rpx;" alt="sex"></image> | |||
</view> | |||
</view> | |||
<text class="pet-service">专业喂养{{pet.serviceDays}}天: {{pet.serviceDates.join(',')}}</text> | |||
</view> | |||
</view> | |||
</view> | |||
</view> | |||
</template> | |||
<script> | |||
export default { | |||
props: { | |||
pets: { | |||
type: Array, | |||
default: () => [] | |||
} | |||
} | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.service-pets-card { | |||
background-color: #FFFFFF; | |||
border-radius: 20rpx; | |||
padding: 30rpx; | |||
margin-bottom: 20rpx; | |||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); | |||
} | |||
.card-title { | |||
font-size: 32rpx; | |||
font-weight: bold; | |||
color: #333; | |||
margin-bottom: 20rpx; | |||
display: flex; | |||
align-items: center; | |||
&::before { | |||
content: ''; | |||
display: inline-block; | |||
width: 8rpx; | |||
height: 32rpx; | |||
background-color: #FFAA48; | |||
margin-right: 16rpx; | |||
border-radius: 4rpx; | |||
} | |||
} | |||
.pets-list { | |||
.pet-item { | |||
display: flex; | |||
align-items: center; | |||
margin-bottom: 20rpx; | |||
&:last-child { | |||
margin-bottom: 0; | |||
} | |||
.pet-avatar { | |||
position: relative; | |||
width: 80rpx; | |||
height: 80rpx; | |||
margin-right: 20rpx; | |||
image { | |||
width: 100%; | |||
height: 100%; | |||
border-radius: 50%; | |||
} | |||
.pet-tag { | |||
position: absolute; | |||
right: -10rpx; | |||
bottom: -10rpx; | |||
background-color: #FFAA48; | |||
color: #FFFFFF; | |||
font-size: 20rpx; | |||
padding: 4rpx 8rpx; | |||
border-radius: 10rpx; | |||
line-height: 1; | |||
} | |||
} | |||
.pet-details { | |||
display: flex; | |||
flex-direction: column; | |||
.pet-name { | |||
font-size: 28rpx; | |||
color: #333; | |||
font-weight: bold; | |||
} | |||
.pet-service { | |||
font-size: 24rpx; | |||
color: #666; | |||
margin-top: 4rpx; | |||
} | |||
} | |||
} | |||
} | |||
</style> |
@ -0,0 +1,116 @@ | |||
<template> | |||
<view class="service-remarks-card"> | |||
<view class="card-title"> | |||
<text>服务备注</text> | |||
</view> | |||
<view class="remarks-content"> | |||
<view class="payment-method"> | |||
<text class="label">付款方式:</text> | |||
<text class="value">{{remarks.paymentMethod}}</text> | |||
</view> | |||
<view class="urgent-flag" v-if="remarks.isUrgent"> | |||
<view class="urgent-icon"> | |||
<image src="/static/images/details/girl.svg" style="width: 32rpx; height: 32rpx;"></image> | |||
</view> | |||
<text class="urgent-text">是否紧急预约</text> | |||
</view> | |||
<view class="notes-section" v-if="remarks.notes"> | |||
<text class="notes-text">{{remarks.notes}}</text> | |||
</view> | |||
</view> | |||
</view> | |||
</template> | |||
<script> | |||
export default { | |||
props: { | |||
remarks: { | |||
type: Object, | |||
default: () => ({ | |||
paymentMethod: '', | |||
isUrgent: false, | |||
notes: '' | |||
}) | |||
} | |||
} | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.service-remarks-card { | |||
background-color: #FFFFFF; | |||
border-radius: 20rpx; | |||
padding: 30rpx; | |||
margin-bottom: 20rpx; | |||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); | |||
} | |||
.card-title { | |||
font-size: 32rpx; | |||
font-weight: bold; | |||
color: #333; | |||
margin-bottom: 20rpx; | |||
display: flex; | |||
align-items: center; | |||
&::before { | |||
content: ''; | |||
display: inline-block; | |||
width: 8rpx; | |||
height: 32rpx; | |||
background-color: #FFAA48; | |||
margin-right: 16rpx; | |||
border-radius: 4rpx; | |||
} | |||
} | |||
.remarks-content { | |||
.payment-method { | |||
display: flex; | |||
align-items: center; | |||
margin-bottom: 20rpx; | |||
.label { | |||
font-size: 26rpx; | |||
color: #666; | |||
margin-right: 10rpx; | |||
} | |||
.value { | |||
font-size: 26rpx; | |||
color: #333; | |||
} | |||
} | |||
.urgent-flag { | |||
display: flex; | |||
align-items: center; | |||
margin-bottom: 20rpx; | |||
.urgent-icon { | |||
margin-right: 10rpx; | |||
} | |||
.urgent-text { | |||
font-size: 26rpx; | |||
color: #FF5252; | |||
} | |||
} | |||
.notes-section { | |||
padding: 20rpx; | |||
background-color: #FFF5E6; | |||
border-radius: 10rpx; | |||
.notes-text { | |||
font-size: 26rpx; | |||
color: #A94F20; | |||
line-height: 1.5; | |||
white-space: pre-wrap; | |||
} | |||
} | |||
} | |||
</style> |
@ -0,0 +1,241 @@ | |||
<template> | |||
<view class="order-detail-page"> | |||
<!-- 订单内容区域 --> | |||
<view class="order-detail-content"> | |||
<!-- 服务地址组件 --> | |||
<service-address :address="orderDetail.address"></service-address> | |||
<!-- 服务宠物组件 --> | |||
<service-pets :pets="orderDetail.pets"></service-pets> | |||
<!-- 服务项目及费用组件 --> | |||
<service-items :items="orderDetail.items" :totalAmount="orderDetail.totalAmount" | |||
:discount="orderDetail.discount" :finalAmount="orderDetail.finalAmount"></service-items> | |||
<!-- 服务备注组件 --> | |||
<service-remarks :remarks="orderDetail.remarks"></service-remarks> | |||
<!-- 其他信息组件 --> | |||
<order-info :orderInfo="orderDetail.orderInfo"></order-info> | |||
</view> | |||
<!-- 底部按钮区域 --> | |||
<view class="order-detail-footer"> | |||
<view class="footer-btn cancel-btn" v-if="orderDetail.status === '1'"> | |||
<text>取消订单</text> | |||
</view> | |||
<view class="footer-btn pay-btn" v-if="orderDetail.status === '1'" @click="goToPay"> | |||
<text>去付款</text> | |||
</view> | |||
<view class="footer-btn contact-btn"> | |||
<text>联系客服</text> | |||
</view> | |||
</view> | |||
<!-- 客服组件 --> | |||
<Kefu></Kefu> | |||
</view> | |||
</template> | |||
<script> | |||
import Kefu from '@/pages/common/kefu.vue' | |||
import ServiceAddress from '@/pages_order/components/order/ServiceAddress.vue' | |||
import ServicePets from '@/pages_order/components/order/ServicePets.vue' | |||
import ServiceItems from '@/pages_order/components/order/ServiceItems.vue' | |||
import ServiceRemarks from '@/pages_order/components/order/ServiceRemarks.vue' | |||
import OrderInfo from '@/pages_order/components/order/OrderInfo.vue' | |||
import { getOrderList } from "@/api/system/user.js" | |||
import { getOpenIdKey } from '@/utils/auth' | |||
export default { | |||
components: { | |||
Kefu, | |||
ServiceAddress, | |||
ServicePets, | |||
ServiceItems, | |||
ServiceRemarks, | |||
OrderInfo | |||
}, | |||
data() { | |||
return { | |||
orderId: null, | |||
orderDetail: { | |||
status: '1', // 1-待付款, 2-已接单, 3-服务中, 4-已完成 | |||
address: { | |||
address: '上海青浦区盈港路888号1-2号-东部公寓', | |||
contact: '联系电话: 18629356678' | |||
}, | |||
pets: [ | |||
{ | |||
name: '小咪', | |||
avatar: '/static/images/personal/pet.png', | |||
tag: '猫', | |||
gender: '女生', | |||
serviceDays: 2, | |||
serviceDates: ['03-20', '03-22'] | |||
}, | |||
{ | |||
name: '小汪', | |||
avatar: '/static/images/personal/pet.png', | |||
tag: '狗', | |||
gender: '男生', | |||
serviceDays: 3, | |||
serviceDates: ['03-20', '03-22', '03-25'] | |||
} | |||
], | |||
items: [ | |||
{ | |||
id: '07-07', | |||
name: '专业喂养', | |||
price: 75.00, | |||
pet: '小咪', | |||
quantity: 1 | |||
}, | |||
{ | |||
id: '07-08', | |||
name: '专业喂养 + 定制服务1项', | |||
price: 75.00, | |||
pet: '小汪', | |||
quantity: 1, | |||
customServices: [ | |||
{ | |||
name: '专业喂养', | |||
price: 75.00, | |||
quantity: 1 | |||
}, | |||
{ | |||
name: '上门遛狗', | |||
price: 40.00, | |||
quantity: 1 | |||
} | |||
] | |||
}, | |||
{ | |||
id: '07-10', | |||
name: '专业喂养 + 定制服务2项', | |||
price: 95.00, | |||
pet: '小汪', | |||
quantity: 1, | |||
customServices: [ | |||
{ | |||
name: '专业喂养', | |||
price: 75.00, | |||
quantity: 1 | |||
}, | |||
{ | |||
name: '上门遛狗', | |||
price: 40.00, | |||
quantity: 1 | |||
}, | |||
{ | |||
name: '梳毛', | |||
price: 40.00, | |||
quantity: 1 | |||
} | |||
] | |||
} | |||
], | |||
totalAmount: 290.00, | |||
discount: 10.00, | |||
memberDiscount: 20.00, | |||
finalAmount: 260.00, | |||
remarks: { | |||
paymentMethod: '存于平台账户', | |||
isUrgent: true, | |||
notes: '* 到达05/2楼\n* 猫咪很胆小,请轻声说话,保持安静。猫粮放在门口柜子上,猫砂盆在卫生间。\n* 狗狗很活泼,喜欢玩球,球放在客厅的篮子里。' | |||
}, | |||
orderInfo: { | |||
orderNumber: 'fc241200396263', | |||
orderTime: '2024-12-03 13:05:57', | |||
paymentTime: '2024-12-03 13:06:06' | |||
} | |||
} | |||
}; | |||
}, | |||
onLoad(options) { | |||
if (options.id) { | |||
this.orderId = options.id; | |||
this.getOrderDetail(); | |||
} | |||
}, | |||
methods: { | |||
// 获取订单详情 | |||
getOrderDetail() { | |||
// 实际项目中应调用API获取订单详情 | |||
// 示例代码: | |||
/* | |||
const params = { | |||
openId: getOpenIdKey(), | |||
orderId: this.orderId | |||
}; | |||
getOrderDetail(params).then(res => { | |||
if (res && res.code === 200) { | |||
this.orderDetail = res.data; | |||
} | |||
}).catch(err => { | |||
console.error('获取订单详情失败', err); | |||
}); | |||
*/ | |||
// 这里使用模拟数据 | |||
console.log('获取订单详情,ID:', this.orderId); | |||
}, | |||
// 去付款 | |||
goToPay() { | |||
uni.navigateTo({ | |||
url: `/pages/details/order?id=${this.orderId}` | |||
}); | |||
} | |||
} | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.order-detail-page { | |||
background-color: #f5f5f5; | |||
min-height: 100vh; | |||
display: flex; | |||
flex-direction: column; | |||
} | |||
.order-detail-content { | |||
flex: 1; | |||
padding: 20rpx; | |||
} | |||
.order-detail-footer { | |||
display: flex; | |||
justify-content: flex-end; | |||
align-items: center; | |||
padding: 20rpx 30rpx; | |||
background-color: #FFFFFF; | |||
border-top: 1px solid #EEEEEE; | |||
.footer-btn { | |||
padding: 16rpx 30rpx; | |||
border-radius: 30rpx; | |||
font-size: 26rpx; | |||
margin-left: 20rpx; | |||
} | |||
.cancel-btn { | |||
background-color: #FFFFFF; | |||
color: #666; | |||
border: 1px solid #DDDDDD; | |||
} | |||
.pay-btn { | |||
background-color: #FFAA48; | |||
color: #FFFFFF; | |||
} | |||
.contact-btn { | |||
background-color: #FFFFFF; | |||
color: #666; | |||
border: 1px solid #DDDDDD; | |||
} | |||
} | |||
</style> |
@ -0,0 +1,463 @@ | |||
<template> | |||
<view class="order-list-page"> | |||
<!-- 顶部标签栏 --> | |||
<view class="order-tabs"> | |||
<view | |||
v-for="(tab, index) in tabs" | |||
:key="index" | |||
class="tab-item" | |||
:class="{active: currentTab === tab.value}" | |||
@click="switchTab(tab.value)" | |||
> | |||
<text>{{tab.name}}</text> | |||
</view> | |||
</view> | |||
<!-- 订单列表 --> | |||
<scroll-view | |||
scroll-y | |||
class="order-list-scroll" | |||
@scrolltolower="loadMore" | |||
@refresherrefresh="refresh" | |||
:refresher-enabled="true" | |||
:refresher-triggered="refreshing" | |||
> | |||
<view class="order-list"> | |||
<!-- 订单卡片 --> | |||
<view class="order-card" v-for="(order, index) in orderList" :key="index"> | |||
<view class="order-header"> | |||
<view class="order-status"> | |||
<text>{{order.statusText}}</text> | |||
</view> | |||
<view class="order-amount"> | |||
<text>金额:</text> | |||
<text class="amount-value">¥{{order.amount}}</text> | |||
</view> | |||
</view> | |||
<!-- 宠物信息 --> | |||
<view class="pet-info" v-for="(pet, petIndex) in order.pets" :key="petIndex"> | |||
<view class="pet-avatar"> | |||
<image :src="pet.avatar || '/static/images/personal/pet.png'" mode="aspectFill"></image> | |||
<view class="pet-tag" v-if="pet.tag"> | |||
<text>{{pet.tag}}</text> | |||
</view> | |||
</view> | |||
<view class="pet-details"> | |||
<view class="pet-name-gender" style="display: flex; align-items: center;"> | |||
<text class="pet-name">{{pet.name}}</text> | |||
<view class="pet-gender" style="margin-left: 10rpx; display: flex; align-items: center;"> | |||
<image :src="pet.gender=='男生'?'/static/images/details/boy.svg':'/static/images/details/girl.svg'" style="width: 24rpx; height: 24rpx;" alt="sex"></image> | |||
</view> | |||
</view> | |||
<text class="pet-service">{{pet.serviceTime}}</text> | |||
</view> | |||
</view> | |||
<!-- 订单信息 --> | |||
<view class="order-info"> | |||
<text class="order-time">下单时间: {{order.orderTime}}</text> | |||
<view class="companion-info"> | |||
<text>伴宠师: {{order.companionName}}</text> | |||
<text v-if="order.companionNote">({{order.companionNote}})</text> | |||
</view> | |||
</view> | |||
<!-- 订单操作 --> | |||
<view class="order-actions"> | |||
<view class="action-btn details-btn" @click="viewOrderDetails(order.id)"> | |||
<text>查看详情</text> | |||
</view> | |||
<view class="action-btn pay-btn" v-if="order.status === 'unpaid'" @click="goToPay(order.id)"> | |||
<text>去付款</text> | |||
</view> | |||
</view> | |||
</view> | |||
<!-- 加载更多 --> | |||
<view class="loading-more" v-if="orderList.length > 0 && hasMore"> | |||
<text>加载中...</text> | |||
</view> | |||
<!-- 无更多数据 --> | |||
<view class="no-more" v-if="orderList.length > 0 && !hasMore"> | |||
<text>没有更多订单了</text> | |||
</view> | |||
<!-- 无订单提示 --> | |||
<view class="no-order" v-if="orderList.length === 0 && !loading"> | |||
<image src="/static/images/personal/no-data.png" mode="aspectFit" class="no-data-image"></image> | |||
<text>暂无订单</text> | |||
</view> | |||
</view> | |||
</scroll-view> | |||
<!-- 客服组件 --> | |||
<Kefu></Kefu> | |||
</view> | |||
</template> | |||
<script> | |||
import Kefu from '@/pages/common/kefu.vue' | |||
import { getOrderList } from "@/api/system/user.js" | |||
import { getOpenIdKey } from '@/utils/auth' | |||
export default { | |||
components: { | |||
Kefu | |||
}, | |||
data() { | |||
return { | |||
tabs: [ | |||
{ name: '全部', value: '' }, | |||
{ name: '待付款', value: '1' }, | |||
{ name: '已接单', value: '2' }, | |||
{ name: '服务中', value: '3' }, | |||
{ name: '已完成', value: '4' } | |||
], | |||
currentTab: '', | |||
orderList: [], | |||
page: 1, | |||
size: 10, | |||
hasMore: true, | |||
loading: false, | |||
refreshing: false, | |||
// 示例数据,实际应从API获取 | |||
exampleData: [ | |||
{ | |||
id: '1', | |||
status: 'unpaid', | |||
statusText: '待付款', | |||
amount: '264', | |||
pets: [ | |||
{ | |||
name: '小咪', | |||
avatar: '/static/images/personal/pet.png', | |||
tag: '猫', | |||
gender: '女生', | |||
serviceTime: '专业喂养2天: 03-20,03-22' | |||
}, | |||
{ | |||
name: '小汪', | |||
avatar: '/static/images/personal/pet.png', | |||
tag: '狗', | |||
gender: '男生', | |||
serviceTime: '专业喂养2天: 03-20,03-22' | |||
} | |||
], | |||
orderTime: '2025-12-14 18:23:06', | |||
companionName: '张三', | |||
companionNote: '伴宠师名称暂不可见' | |||
} | |||
] | |||
}; | |||
}, | |||
methods: { | |||
// 切换标签 | |||
switchTab(tabValue) { | |||
if (this.currentTab === tabValue) return; | |||
this.currentTab = tabValue; | |||
this.orderList = []; | |||
this.page = 1; | |||
this.hasMore = true; | |||
this.getOrderList(); | |||
}, | |||
// 下拉刷新 | |||
refresh() { | |||
this.refreshing = true; | |||
this.page = 1; | |||
this.orderList = []; | |||
this.hasMore = true; | |||
this.getOrderList().then(() => { | |||
this.refreshing = false; | |||
}).catch(() => { | |||
this.refreshing = false; | |||
}); | |||
}, | |||
// 加载更多 | |||
loadMore() { | |||
if (this.loading || !this.hasMore) return; | |||
this.page++; | |||
this.getOrderList(); | |||
}, | |||
// 查看订单详情 | |||
viewOrderDetails(orderId) { | |||
uni.navigateTo({ | |||
url: `/pages_order/order/orderDetail?id=${orderId}` | |||
}); | |||
}, | |||
// 去付款 | |||
goToPay(orderId) { | |||
uni.navigateTo({ | |||
url: `/pages/details/order?id=${orderId}` | |||
}); | |||
}, | |||
// 获取订单列表 | |||
getOrderList() { | |||
if (this.loading) return Promise.reject(); | |||
this.loading = true; | |||
// 构建请求参数 | |||
const params = { | |||
status: this.currentTab, | |||
openId: getOpenIdKey(), | |||
page: this.page, | |||
size: this.size | |||
}; | |||
// 实际API调用 | |||
return new Promise((resolve, reject) => { | |||
// 模拟API调用,实际项目中应使用真实API | |||
setTimeout(() => { | |||
try { | |||
// 模拟数据,实际应调用API | |||
if (this.page === 1) { | |||
this.orderList = [...this.exampleData]; | |||
} else if (this.page < 3) { | |||
this.orderList = [...this.orderList, ...this.exampleData]; | |||
} else { | |||
this.hasMore = false; | |||
} | |||
// 实际API调用应该是这样: | |||
// getOrderList(params).then(res => { | |||
// if (res && res.code === 200) { | |||
// const newList = res.data.records || []; | |||
// if (this.page === 1) { | |||
// this.orderList = newList; | |||
// } else { | |||
// this.orderList = [...this.orderList, ...newList]; | |||
// } | |||
// this.hasMore = newList.length === this.size; | |||
// } else { | |||
// this.hasMore = false; | |||
// } | |||
// this.loading = false; | |||
// resolve(); | |||
// }).catch(err => { | |||
// this.loading = false; | |||
// reject(err); | |||
// }); | |||
this.loading = false; | |||
resolve(); | |||
} catch (err) { | |||
this.loading = false; | |||
reject(err); | |||
} | |||
}, 500); | |||
}); | |||
} | |||
}, | |||
onLoad() { | |||
// 页面加载时获取订单列表 | |||
this.getOrderList(); | |||
} | |||
} | |||
</script> | |||
<style lang="scss" scoped> | |||
.order-list-page { | |||
background-color: #f5f5f5; | |||
min-height: 100vh; | |||
display: flex; | |||
flex-direction: column; | |||
} | |||
.order-tabs { | |||
display: flex; | |||
justify-content: space-around; | |||
align-items: center; | |||
height: 88rpx; | |||
background-color: #FFBF60; | |||
color: #FFFFFF; | |||
.tab-item { | |||
padding: 0 20rpx; | |||
height: 100%; | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
font-size: 28rpx; | |||
position: relative; | |||
&.active { | |||
font-weight: bold; | |||
&::after { | |||
content: ''; | |||
position: absolute; | |||
bottom: 0; | |||
left: 50%; | |||
transform: translateX(-50%); | |||
width: 40rpx; | |||
height: 4rpx; | |||
background-color: #FFFFFF; | |||
border-radius: 2rpx; | |||
} | |||
} | |||
} | |||
} | |||
.order-list-scroll { | |||
flex: 1; | |||
width: 100%; | |||
} | |||
.order-list { | |||
padding: 20rpx; | |||
padding-bottom: 40rpx; | |||
} | |||
.order-card { | |||
background-color: #FFFFFF; | |||
border-radius: 20rpx; | |||
padding: 30rpx; | |||
margin-bottom: 20rpx; | |||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05); | |||
} | |||
.order-header { | |||
display: flex; | |||
justify-content: space-between; | |||
align-items: center; | |||
margin-bottom: 20rpx; | |||
.order-status { | |||
background-color: #FFF5E6; | |||
color: #FFAA48; | |||
padding: 6rpx 16rpx; | |||
border-radius: 20rpx; | |||
font-size: 24rpx; | |||
} | |||
.order-amount { | |||
font-size: 28rpx; | |||
color: #333; | |||
.amount-value { | |||
color: #FF5252; | |||
font-weight: bold; | |||
} | |||
} | |||
} | |||
.pet-info { | |||
display: flex; | |||
align-items: center; | |||
margin-bottom: 20rpx; | |||
.pet-avatar { | |||
position: relative; | |||
width: 80rpx; | |||
height: 80rpx; | |||
margin-right: 20rpx; | |||
image { | |||
width: 100%; | |||
height: 100%; | |||
border-radius: 50%; | |||
} | |||
.pet-tag { | |||
position: absolute; | |||
right: -10rpx; | |||
bottom: -10rpx; | |||
background-color: #FFAA48; | |||
color: #FFFFFF; | |||
font-size: 20rpx; | |||
padding: 4rpx 8rpx; | |||
border-radius: 10rpx; | |||
line-height: 1; | |||
} | |||
} | |||
.pet-details { | |||
display: flex; | |||
flex-direction: column; | |||
.pet-name { | |||
font-size: 28rpx; | |||
color: #333; | |||
font-weight: bold; | |||
} | |||
.pet-service { | |||
font-size: 24rpx; | |||
color: #666; | |||
margin-top: 4rpx; | |||
} | |||
} | |||
} | |||
.order-info { | |||
margin-bottom: 30rpx; | |||
.order-time { | |||
font-size: 24rpx; | |||
color: #999; | |||
display: block; | |||
margin-bottom: 10rpx; | |||
} | |||
.companion-info { | |||
font-size: 24rpx; | |||
color: #999; | |||
background-color: #F0F9FF; | |||
padding: 16rpx; | |||
border-radius: 10rpx; | |||
} | |||
} | |||
.order-actions { | |||
display: flex; | |||
justify-content: flex-end; | |||
align-items: center; | |||
.action-btn { | |||
padding: 16rpx 30rpx; | |||
border-radius: 30rpx; | |||
font-size: 26rpx; | |||
margin-left: 20rpx; | |||
} | |||
.details-btn { | |||
background-color: #FFFFFF; | |||
color: #666; | |||
border: 1px solid #DDDDDD; | |||
} | |||
.pay-btn { | |||
background-color: #FFAA48; | |||
color: #FFFFFF; | |||
} | |||
} | |||
.loading-more, .no-more { | |||
text-align: center; | |||
padding: 20rpx 0; | |||
color: #999; | |||
font-size: 24rpx; | |||
} | |||
.no-order { | |||
text-align: center; | |||
padding: 100rpx 0; | |||
color: #999; | |||
font-size: 28rpx; | |||
display: flex; | |||
flex-direction: column; | |||
align-items: center; | |||
.no-data-image { | |||
width: 200rpx; | |||
height: 200rpx; | |||
margin-bottom: 20rpx; | |||
} | |||
} | |||
</style> |