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