4 changed files with 442 additions and 227 deletions
-
388src/pagesOrder/list/components/OrderList.vue
-
243src/pagesOrder/list/list.vue
-
14src/services/order.ts
-
24src/types/order.d.ts
@ -0,0 +1,388 @@ |
|||||
|
<template> |
||||
|
<scroll-view |
||||
|
enable-back-to-top |
||||
|
scroll-y |
||||
|
class="orders" |
||||
|
refresher-enabled |
||||
|
:refresher-triggered="isTriggered" |
||||
|
@refresherrefresh="onRefresherrefresh" |
||||
|
@scrolltolower="getMemberOrderData" |
||||
|
> |
||||
|
<view class="card" v-for="order in orderList" :key="order.id"> |
||||
|
<!-- 订单信息 --> |
||||
|
<view class="status"> |
||||
|
<text class="date">{{ order.createTime }}</text> |
||||
|
<!-- 订单状态文字 --> |
||||
|
<text>{{ orderStateList[order.orderState].text }}</text> |
||||
|
<!-- 待评价/已完成/已取消 状态: 展示删除订单 --> |
||||
|
<text |
||||
|
v-if="order.orderState >= OrderState.DaiPingJia" |
||||
|
class="icon-delete" |
||||
|
@tap="onOrderDelete(order.id)" |
||||
|
></text> |
||||
|
</view> |
||||
|
<!-- 商品信息,点击商品跳转到订单详情,不是商品详情 --> |
||||
|
<navigator |
||||
|
v-for="item in order.skus" |
||||
|
:key="item.id" |
||||
|
class="goods" |
||||
|
:url="`/pagesOrder/detail/detail?id=${order.id}`" |
||||
|
hover-class="none" |
||||
|
> |
||||
|
<view class="cover"> |
||||
|
<image class="image" mode="aspectFit" :src="item.image"></image> |
||||
|
</view> |
||||
|
<view class="meta"> |
||||
|
<view class="name ellipsis">{{ item.name }}</view> |
||||
|
<view class="type">{{ item.attrsText }}</view> |
||||
|
</view> |
||||
|
</navigator> |
||||
|
<!-- 支付信息 --> |
||||
|
<view class="payment"> |
||||
|
<text class="quantity">共{{ order.totalNum }}件商品</text> |
||||
|
<text>实付</text> |
||||
|
<text class="amount"> <text class="symbol">¥</text>{{ order.payMoney }}</text> |
||||
|
</view> |
||||
|
<!-- 订单操作按钮 --> |
||||
|
<view class="action"> |
||||
|
<!-- 待付款状态:显示去支付按钮 --> |
||||
|
<template v-if="order.orderState === OrderState.DaiFuKuan"> |
||||
|
<view class="button primary" @tap="onOrderPay(order.id)">去支付</view> |
||||
|
</template> |
||||
|
<template v-else> |
||||
|
<navigator |
||||
|
class="button secondary" |
||||
|
:url="`/pagesOrder/create/create?orderId=${order.id}`" |
||||
|
hover-class="none" |
||||
|
> |
||||
|
再次购买 |
||||
|
</navigator> |
||||
|
<!-- 待收货状态: 展示确认收货 --> |
||||
|
<view |
||||
|
v-if="order.orderState === OrderState.DaiShouHuo" |
||||
|
class="button primary" |
||||
|
@tap="onOrderConfirm(order.id)" |
||||
|
> |
||||
|
确认收货 |
||||
|
</view> |
||||
|
</template> |
||||
|
</view> |
||||
|
</view> |
||||
|
<!-- 底部提示文字 --> |
||||
|
<view class="loading-text" :style="{ paddingBottom: safeAreaInsets?.bottom + 'px' }"> |
||||
|
{{ isFinish ? '没有更多数据~' : '正在加载...' }} |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { OrderState, orderStateList } from '@/services/constants' |
||||
|
import { |
||||
|
putMemberOrderReceiptByIdAPI, |
||||
|
deleteMemberOrderAPI, |
||||
|
getMemberOrderAPI, |
||||
|
} from '@/services/order' |
||||
|
import { getPayMockAPI, getPayWxPayMiniPayAPI } from '@/services/pay' |
||||
|
import type { OrderItem, OrderListParams } from '@/types/order' |
||||
|
import { onMounted, ref } from 'vue' |
||||
|
|
||||
|
// 获取屏幕边界到安全区域距离 |
||||
|
const { safeAreaInsets } = uni.getSystemInfoSync() |
||||
|
|
||||
|
// 定义 porps |
||||
|
const props = defineProps<{ |
||||
|
orderState: number |
||||
|
}>() |
||||
|
|
||||
|
// 请求参数 |
||||
|
const queryParams: Required<OrderListParams> = { |
||||
|
page: 1, |
||||
|
pageSize: 5, |
||||
|
orderState: props.orderState, |
||||
|
} |
||||
|
|
||||
|
// 获取订单列表 |
||||
|
const orderList = ref<OrderItem[]>([]) |
||||
|
// 是否加载中标记,用于防止滚动触底触发多次请求 |
||||
|
const isLoading = ref(false) |
||||
|
const getMemberOrderData = async () => { |
||||
|
// 如果数据出于加载中,退出函数 |
||||
|
if (isLoading.value) return |
||||
|
// 退出分页判断 |
||||
|
if (isFinish.value === true) { |
||||
|
return uni.showToast({ icon: 'none', title: '没有更多数据~' }) |
||||
|
} |
||||
|
// 发送请求前,标记为加载中 |
||||
|
isLoading.value = true |
||||
|
// 发送请求 |
||||
|
const res = await getMemberOrderAPI(queryParams) |
||||
|
// 发送请求后,重置标记 |
||||
|
isLoading.value = false |
||||
|
// 数组追加 |
||||
|
orderList.value.push(...res.result.items) |
||||
|
// 分页条件 |
||||
|
if (queryParams.page < res.result.pages) { |
||||
|
// 页码累加 |
||||
|
queryParams.page++ |
||||
|
} else { |
||||
|
// 分页已结束 |
||||
|
isFinish.value = true |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
getMemberOrderData() |
||||
|
}) |
||||
|
|
||||
|
// 订单支付 |
||||
|
const onOrderPay = async (id: string) => { |
||||
|
if (import.meta.env.DEV) { |
||||
|
// 开发环境模拟支付 |
||||
|
await getPayMockAPI({ orderId: id }) |
||||
|
} else { |
||||
|
// #ifdef MP-WEIXIN |
||||
|
|
||||
|
// 正式环境支付:1.获取支付订单信息,2.调用微信支付API |
||||
|
// const res = await getPayWxPayMiniPayAPI({ orderId: id }) |
||||
|
// await wx.requestPayment(res.result) |
||||
|
|
||||
|
// 注意:因小程序上线后被恶意投诉:理由为支付 0.01 元后不发货,现调整为模拟支付 |
||||
|
await getPayMockAPI({ orderId: id }) |
||||
|
// #endif |
||||
|
} |
||||
|
// 成功提示 |
||||
|
uni.showToast({ title: '模拟支付成功' }) |
||||
|
// 模拟支付提示 |
||||
|
setTimeout(() => { |
||||
|
wx.showModal({ |
||||
|
title: '温馨提示', |
||||
|
content: '此交易是模拟支付,您并未付款,不会导致实际购买商品或服务', |
||||
|
confirmText: '知道了', |
||||
|
showCancel: false, |
||||
|
}) |
||||
|
}, 2000) |
||||
|
// 更新订单状态 |
||||
|
const order = orderList.value.find((v) => v.id === id) |
||||
|
order!.orderState = OrderState.DaiFaHuo |
||||
|
} |
||||
|
|
||||
|
// 确认收货 |
||||
|
const onOrderConfirm = (id: string) => { |
||||
|
uni.showModal({ |
||||
|
content: '为保障您的权益,请收到货并确认无误后,再确认收货', |
||||
|
confirmColor: '#27BA9B', |
||||
|
success: async (res) => { |
||||
|
if (res.confirm) { |
||||
|
await putMemberOrderReceiptByIdAPI(id) |
||||
|
uni.showToast({ icon: 'success', title: '确认收货成功' }) |
||||
|
// 确认成功,更新为待评价 |
||||
|
const order = orderList.value.find((v) => v.id === id) |
||||
|
order!.orderState = OrderState.DaiPingJia |
||||
|
} |
||||
|
}, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 删除订单 |
||||
|
const onOrderDelete = (id: string) => { |
||||
|
uni.showModal({ |
||||
|
content: '你确定要删除该订单?', |
||||
|
confirmColor: '#27BA9B', |
||||
|
success: async (res) => { |
||||
|
if (res.confirm) { |
||||
|
await deleteMemberOrderAPI({ ids: [id] }) |
||||
|
// 删除成功,界面中删除订单 |
||||
|
const index = orderList.value.findIndex((v) => v.id === id) |
||||
|
orderList.value.splice(index, 1) |
||||
|
} |
||||
|
}, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 是否分页结束 |
||||
|
const isFinish = ref(false) |
||||
|
// 是否触发下拉刷新 |
||||
|
const isTriggered = ref(false) |
||||
|
// 自定义下拉刷新被触发 |
||||
|
const onRefresherrefresh = async () => { |
||||
|
// 开始动画 |
||||
|
isTriggered.value = true |
||||
|
// 重置数据 |
||||
|
queryParams.page = 1 |
||||
|
orderList.value = [] |
||||
|
isFinish.value = false |
||||
|
// 加载数据 |
||||
|
await getMemberOrderData() |
||||
|
// 关闭动画 |
||||
|
isTriggered.value = false |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss"> |
||||
|
// 订单列表 |
||||
|
.orders { |
||||
|
.card { |
||||
|
min-height: 100rpx; |
||||
|
padding: 20rpx; |
||||
|
margin: 20rpx 20rpx 0; |
||||
|
border-radius: 10rpx; |
||||
|
background-color: #fff; |
||||
|
|
||||
|
&:last-child { |
||||
|
padding-bottom: 40rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.status { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
font-size: 28rpx; |
||||
|
color: #999; |
||||
|
margin-bottom: 15rpx; |
||||
|
|
||||
|
.date { |
||||
|
color: #666; |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.primary { |
||||
|
color: #ff9240; |
||||
|
} |
||||
|
|
||||
|
.icon-delete { |
||||
|
line-height: 1; |
||||
|
margin-left: 10rpx; |
||||
|
padding-left: 10rpx; |
||||
|
border-left: 1rpx solid #e3e3e3; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.goods { |
||||
|
display: flex; |
||||
|
margin-bottom: 20rpx; |
||||
|
|
||||
|
.cover { |
||||
|
width: 170rpx; |
||||
|
height: 170rpx; |
||||
|
margin-right: 20rpx; |
||||
|
border-radius: 10rpx; |
||||
|
overflow: hidden; |
||||
|
position: relative; |
||||
|
.image { |
||||
|
width: 170rpx; |
||||
|
height: 170rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.quantity { |
||||
|
position: absolute; |
||||
|
bottom: 0; |
||||
|
right: 0; |
||||
|
line-height: 1; |
||||
|
padding: 6rpx 4rpx 6rpx 8rpx; |
||||
|
font-size: 24rpx; |
||||
|
color: #fff; |
||||
|
border-radius: 10rpx 0 0 0; |
||||
|
background-color: rgba(0, 0, 0, 0.6); |
||||
|
} |
||||
|
|
||||
|
.meta { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.name { |
||||
|
height: 80rpx; |
||||
|
font-size: 26rpx; |
||||
|
color: #444; |
||||
|
} |
||||
|
|
||||
|
.type { |
||||
|
line-height: 1.8; |
||||
|
padding: 0 15rpx; |
||||
|
margin-top: 10rpx; |
||||
|
font-size: 24rpx; |
||||
|
align-self: flex-start; |
||||
|
border-radius: 4rpx; |
||||
|
color: #888; |
||||
|
background-color: #f7f7f8; |
||||
|
} |
||||
|
|
||||
|
.more { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
font-size: 22rpx; |
||||
|
color: #333; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.payment { |
||||
|
display: flex; |
||||
|
justify-content: flex-end; |
||||
|
align-items: center; |
||||
|
line-height: 1; |
||||
|
padding: 20rpx 0; |
||||
|
text-align: right; |
||||
|
color: #999; |
||||
|
font-size: 28rpx; |
||||
|
border-bottom: 1rpx solid #eee; |
||||
|
|
||||
|
.quantity { |
||||
|
font-size: 24rpx; |
||||
|
margin-right: 16rpx; |
||||
|
} |
||||
|
|
||||
|
.amount { |
||||
|
color: #444; |
||||
|
margin-left: 6rpx; |
||||
|
} |
||||
|
|
||||
|
.symbol { |
||||
|
font-size: 20rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.action { |
||||
|
display: flex; |
||||
|
justify-content: flex-end; |
||||
|
align-items: center; |
||||
|
padding-top: 20rpx; |
||||
|
|
||||
|
.button { |
||||
|
width: 180rpx; |
||||
|
height: 60rpx; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
margin-left: 20rpx; |
||||
|
border-radius: 60rpx; |
||||
|
border: 1rpx solid #ccc; |
||||
|
font-size: 26rpx; |
||||
|
color: #444; |
||||
|
} |
||||
|
|
||||
|
.secondary { |
||||
|
color: #27ba9b; |
||||
|
border-color: #27ba9b; |
||||
|
} |
||||
|
|
||||
|
.primary { |
||||
|
color: #fff; |
||||
|
background-color: #27ba9b; |
||||
|
border-color: #27ba9b; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.loading-text { |
||||
|
text-align: center; |
||||
|
font-size: 28rpx; |
||||
|
color: #666; |
||||
|
padding: 20rpx 0; |
||||
|
} |
||||
|
} |
||||
|
</style> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue