新增订单列表页面,支持按状态筛选、下拉刷新和加载更多功能。新增订单详情页面,展示服务地址、宠物信息、服务项目及费用等详细信息。优化支付成功页面,调整布局和样式,提升用户体验。新增取消订单功能,并调整导航路径以适配新页面结构。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> |