feat: 订单详情页交互功能

This commit is contained in:
jqtmviyu 2025-05-11 11:48:03 +08:00
parent 029074ae97
commit 1617b09400
7 changed files with 520 additions and 68 deletions

View File

@ -159,6 +159,18 @@
"navigationBarTitleText": "订单详情",
"navigationStyle": "custom"
}
},
{
"path": "payment/payment",
"style": {
"navigationBarTitleText": "支付结果"
}
},
{
"path": "list/list",
"style": {
"navigationBarTitleText": "订单列表"
}
}
]
}

View File

@ -95,7 +95,12 @@
</template>
<script setup lang="ts">
import { getMemberOrderPreAPI, getMemberOrderPreNowAPI, postCreateOrderAPI } from '@/services/order'
import {
getMemberOrderPreAPI,
getMemberOrderPreNowAPI,
getMemberOrderRepurchaseByIdAPI,
postCreateOrderAPI,
} from '@/services/order'
import { useSelectedAddressStore } from '@/stores/modules/selectedAddress'
import type { OrderPreResult } from '@/types/order'
import { onShow } from '@dcloudio/uni-app'
@ -122,18 +127,24 @@ const onChangeDelivery: UniHelper.SelectorPickerOnChange = (ev) => {
//
const orderPreList = ref<OrderPreResult>()
const props = defineProps<{
const query = defineProps<{
skuId?: string
count?: number
id?: string
}>()
const getOrderPre = async () => {
let res
if (props?.skuId && props?.count) {
if (query?.skuId && query?.count) {
//
res = await getMemberOrderPreNowAPI({
skuId: props.skuId,
count: props.count,
skuId: query.skuId,
count: query.count,
})
} else if (query?.id) {
//
res = await getMemberOrderRepurchaseByIdAPI(query.id)
} else {
//
res = await getMemberOrderPreAPI()
}
console.log(res)
@ -149,10 +160,16 @@ onShow(() => {
//
const useSelectedAddress = useSelectedAddressStore()
const selectedAddress = computed(() => {
return (
useSelectedAddress.selectedAddress ||
orderPreList.value?.userAddresses.find((item) => item.isDefault === 1)
)
// 使
if (useSelectedAddress.selectedAddress) {
return useSelectedAddress.selectedAddress
}
// orderPreList
if (orderPreList.value?.userAddresses && orderPreList.value.userAddresses.length > 0) {
return orderPreList.value.userAddresses.find((item) => item.isDefault === 1)
}
// null
return null
})
//
@ -199,7 +216,7 @@ const handleSubmitOrder = async () => {
// ID
uni.redirectTo({
url: `/pagesOrder/detail/detail?orderId=${res.result.id}`,
url: `/pagesOrder/detail/detail?id=${res.result.id}`,
})
} else {
uni.showToast({

View File

@ -9,49 +9,72 @@
</view>
</view>
<scroll-view scroll-y class="viewport" id="scroller" @scrolltolower="onScrollToLower">
<template v-if="true">
<template v-if="orderDetail">
<!-- 订单状态 -->
<view class="overview" :style="{ paddingTop: safeAreaInsets!.top + 20 + 'px' }">
<!-- 待付款状态:展示去支付按钮和倒计时 -->
<template v-if="true">
<template v-if="orderDetail.orderState === OrderState.PendingPayment">
<view class="status icon-clock">等待付款</view>
<view class="tips">
<text class="money">应付金额: ¥ 99.00</text>
<text class="time">支付剩余</text>
00 29 59
<uni-countdown
color="#fff"
splitor-color="#fff"
:show-day="false"
:second="orderDetail.countdown"
:show-colon="false"
@timeup="onTimeUp"
/>
</view>
<view class="button">去支付</view>
<view class="button" @tap="handlePay">去支付</view>
</template>
<!-- 其他订单状态:展示再次购买按钮 -->
<template v-else>
<!-- 订单状态文字 -->
<view class="status"> 待付款 </view>
<view class="status"> {{ OrderStateList[orderDetail.orderState].text }} </view>
<view class="button-group">
<navigator
class="button"
:url="`/pagesOrder/create/create?orderId=${query.id}`"
:url="`/pagesOrder/create/create?id=${query.id}`"
hover-class="none"
>
再次购买
</navigator>
<!-- 待发货状态模拟发货,开发期间使用,用于修改订单状态为已发货 -->
<view v-if="false" class="button"> 模拟发货 </view>
<view
v-if="isDev && orderDetail.orderState === OrderState.PendingShipment"
class="button"
@tap="handleMockShipment"
>
模拟发货
</view>
<!-- 待收货状态确认收货 -->
<view
v-if="orderDetail.orderState === OrderState.PendingDelivery"
class="button"
@tap="handleConfirmReceipt"
>
确认收货
</view>
</view>
</template>
</view>
<!-- 配送状态 -->
<view class="shipment">
<!-- 订单物流信息 -->
<view v-for="item in 1" :key="item" class="item">
<view class="message">
您已在广州市天河区黑马程序员完成取件感谢使用菜鸟驿站期待再次为您服务
<template v-if="orderLogistics">
<view v-for="item in orderLogistics.list" :key="item.id" class="item">
<view class="message">{{ item.text }}</view>
<view class="date">{{ item.time }}</view>
</view>
<view class="date"> 2023-04-14 13:14:20 </view>
</view>
</template>
<!-- 用户收货地址 -->
<view class="locate">
<view class="user"> 张三 13333333333 </view>
<view class="address"> 广东省 广州市 天河区 黑马程序员 </view>
<view class="user">
{{ orderDetail.receiverContact }} {{ orderDetail.receiverMobile }}
</view>
<view class="address"> {{ orderDetail.receiverAddress }} </view>
</view>
</view>
@ -60,29 +83,26 @@
<view class="item">
<navigator
class="navigator"
v-for="item in 2"
:key="item"
:url="`/pages/goods/goods?id=${item}`"
v-for="item in orderDetail.skus"
:key="item.id"
:url="`/pages/goods/goods?id=${item.spuId}`"
hover-class="none"
>
<image
class="cover"
src="https://yanxuan-item.nosdn.127.net/c07edde1047fa1bd0b795bed136c2bb2.jpg"
></image>
<image class="cover" :src="item.image"></image>
<view class="meta">
<view class="name ellipsis">ins风小碎花泡泡袖衬110-160cm</view>
<view class="type">藏青小花 130</view>
<view class="name ellipsis">{{ item.name }}</view>
<view class="type">{{ item.attrsText.replaceAll('', ' ') }}</view>
<view class="price">
<view class="actual">
<text class="symbol">¥</text>
<text>99.00</text>
<text>{{ item.curPrice.toFixed(2) }}</text>
</view>
</view>
<view class="quantity">x1</view>
<view class="quantity">x{{ item.quantity }}</view>
</view>
</navigator>
<!-- 待评价状态:展示按钮 -->
<view class="action" v-if="true">
<view class="action" v-if="orderDetail.orderState === OrderState.PendingReview">
<view class="button primary">申请售后</view>
<navigator url="" class="button"> 去评价 </navigator>
</view>
@ -95,11 +115,11 @@
</view>
<view class="row">
<view class="text">运费: </view>
<view class="symbol">10.00</view>
<view class="symbol">{{ orderDetail.postFee.toFixed(2) }}</view>
</view>
<view class="row">
<view class="text">应付金额: </view>
<view class="symbol primary">109.00</view>
<view class="symbol primary">{{ orderDetail.payMoney.toFixed(2) }}</view>
</view>
</view>
</view>
@ -111,36 +131,54 @@
<view class="item">
订单编号: {{ query.id }} <text class="copy" @tap="onCopy(query.id)">复制</text>
</view>
<view class="item">下单时间: 2023-04-14 13:14:20</view>
<view class="item">下单时间: {{ orderDetail.createTime }}</view>
</view>
</view>
<!-- 猜你喜欢 -->
<XtxGuess ref="guessRef" />
<JbcGuess ref="guessRef" />
<!-- 底部操作栏 -->
<view class="toolbar-height" :style="{ paddingBottom: safeAreaInsets?.bottom + 'px' }"></view>
<view class="toolbar" :style="{ paddingBottom: safeAreaInsets?.bottom + 'px' }">
<!-- 待付款状态:展示支付按钮 -->
<template v-if="true">
<view class="button primary"> 去支付 </view>
<template v-if="orderDetail.orderState === OrderState.PendingPayment">
<view class="button primary" @tap="handlePay"> 去支付 </view>
<view class="button" @tap="popup?.open?.()"> 取消订单 </view>
</template>
<!-- 其他订单状态:按需展示按钮 -->
<template v-else>
<navigator
class="button secondary"
:url="`/pagesOrder/create/create?orderId=${query.id}`"
:url="`/pagesOrder/create/create?id=${query.id}`"
hover-class="none"
>
再次购买
</navigator>
<!-- 待收货状态: 展示确认收货 -->
<view class="button primary"> 确认收货 </view>
<view
class="button primary"
v-if="orderDetail.orderState === OrderState.PendingDelivery"
@tap="handleConfirmReceipt"
>
确认收货
</view>
<!-- 待评价状态: 展示去评价 -->
<view class="button"> 去评价 </view>
<view
class="button"
v-if="orderDetail.orderState === OrderState.PendingReview"
@tap="handleReview"
>
去评价
</view>
<!-- 待评价/已完成/已取消 状态: 展示删除订单 -->
<view class="button delete"> 删除订单 </view>
<view
class="button delete"
v-if="orderDetail.orderState >= OrderState.PendingReview"
@tap="handleDeleteOrder"
>
删除订单
</view>
</template>
</view>
</template>
@ -162,17 +200,29 @@
</view>
<view class="footer">
<view class="button" @tap="popup?.close?.()">取消</view>
<view class="button primary">确认</view>
<view class="button primary" @tap="handleCancelOrder">确认</view>
</view>
</view>
</uni-popup>
</template>
<script setup lang="ts">
import {
deleteMemberOrderAPI,
getMemberOrderLogisticsAPI,
getMockOrderConsignmentAPI,
OrderState,
OrderStateList,
putMemberOrderCancleAPI,
putMemberOrderReceiptAPI,
} from '@/services/order'
import { useGuessList } from '@/composables'
import { getMemberOrderDetailAPI } from '@/services/order'
import type { AnimationMethods } from '@/types/animation'
import { onReady } from '@dcloudio/uni-app'
import { computed, ref } from 'vue'
import type { OrderLogisticResult, OrderResult } from '@/types/order'
import { onReady, onShow } from '@dcloudio/uni-app'
import { ref } from 'vue'
import { getMockPayParamsAPI, getWechatPayParamsAPI } from '@/services/pay'
//
const { safeAreaInsets } = uni.getSystemInfoSync()
@ -207,8 +257,8 @@ const hasBack = pages.length > 1
//
const pageInstance = pages.at(-1) as AnimationMethods
console.log(pageInstance)
//
onReady(() => {
// :
pageInstance.animate(
@ -270,6 +320,139 @@ onReady(() => {
},
)
})
//
const orderDetail = ref<OrderResult>()
const getOrderDetail = async () => {
const res = await getMemberOrderDetailAPI(query.id)
console.log(res)
orderDetail.value = res.result
//
if (
[OrderState.PendingDelivery, OrderState.PendingReview, OrderState.Completed].includes(
res.result.orderState,
)
) {
getOrderLogistics()
}
}
//
const orderLogistics = ref<OrderLogisticResult>()
const getOrderLogistics = async () => {
const res = await getMemberOrderLogisticsAPI(query.id)
console.log(res)
orderLogistics.value = res.result
}
onShow(() => {
getOrderDetail()
})
//
const onTimeUp = () => {
console.log('倒计时结束')
orderDetail.value!.orderState = OrderState.Cancelled
}
//
const isDev = import.meta.env.DEV
const handlePay = async () => {
let res = null
if (isDev) {
//
res = await getMockPayParamsAPI({ orderId: query.id })
} else {
//
res = await getWechatPayParamsAPI({ orderId: query.id })
}
if (res.code === '1') {
if (isDev) {
uni.redirectTo({ url: `/pagesOrder/payment/payment?id=${query.id}` })
} else {
// 使
await wx.requestPayment(res.result as WechatMiniprogram.RequestPaymentOption)
uni.redirectTo({ url: `/pagesOrder/payment/payment?id=${query.id}` })
}
} else {
uni.showToast({ title: res.msg, icon: 'error' })
}
}
//
const handleMockShipment = async () => {
console.log('模拟发货')
if (isDev) {
const res = await getMockOrderConsignmentAPI(query.id)
if (res.code === '1') {
uni.showToast({ title: '模拟发货成功', icon: 'success' })
orderDetail.value!.orderState = OrderState.PendingDelivery
getOrderLogistics()
}
}
}
//
const handleConfirmReceipt = async () => {
console.log('确认收货')
uni.showModal({
content: '确认收货后,订单将无法取消,是否继续?',
success: async ({ confirm }) => {
if (confirm) {
const res = await putMemberOrderReceiptAPI(query.id)
if (res.code === '1') {
await uni.showToast({ title: '确认收货成功', icon: 'success' })
orderDetail.value!.orderState = OrderState.Completed
setTimeout(() => {
uni.navigateBack()
}, 500)
}
}
},
})
}
//
const handleCancelOrder = async () => {
console.log('取消订单')
if (reason.value === '') {
await uni.showToast({ title: '请选择取消订单的原因', icon: 'none' })
return
}
const res = await putMemberOrderCancleAPI(query.id, { cancelReason: reason.value })
console.log(res)
if (res.code === '1') {
popup.value?.close?.()
orderDetail.value!.orderState = OrderState.Cancelled
uni.showToast({ title: '取消订单成功', icon: 'success' })
} else {
await uni.showToast({ title: res.msg, icon: 'error' })
}
}
//
const handleReview = async () => {
console.log('去评价')
}
//
const handleDeleteOrder = async () => {
console.log('删除订单')
uni.showModal({
content: '确定删除订单吗?',
success: async ({ confirm }) => {
if (confirm) {
const res = await deleteMemberOrderAPI({ ids: [query.id] })
if (res.code === '1') {
await uni.showToast({ title: '删除订单成功', icon: 'success' })
setTimeout(() => {
uni.redirectTo({ url: '/pagesOrder/list/list' })
}, 500)
}
}
},
})
}
</script>
<style lang="scss">

View File

@ -0,0 +1,95 @@
<script setup lang="ts">
import { useGuessList } from '@/composables'
//
const query = defineProps<{
id: string
}>()
//
const { guessRef, onScrollToLower } = useGuessList()
</script>
<template>
<scroll-view class="viewport" scroll-y @scrolltolower="onScrollToLower">
<!-- 订单状态 -->
<view class="overview">
<view class="status icon-checked">支付成功</view>
<view class="buttons">
<navigator
hover-class="none"
class="button navigator"
url="/pages/index/index"
open-type="switchTab"
>
返回首页
</navigator>
<navigator
hover-class="none"
class="button navigator"
:url="`/pagesOrder/detail/detail?id=${query.id}`"
open-type="redirect"
>
查看订单
</navigator>
</view>
</view>
<!-- 猜你喜欢 -->
<JbcGuess ref="guessRef" />
</scroll-view>
</template>
<style lang="scss">
page {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
.viewport {
background-color: #f7f7f8;
}
.overview {
line-height: 1;
padding: 50rpx 0;
color: #fff;
background-color: #27ba9b;
.status {
font-size: 36rpx;
font-weight: 500;
text-align: center;
}
.status::before {
display: block;
font-size: 110rpx;
margin-bottom: 20rpx;
}
.buttons {
height: 60rpx;
line-height: 60rpx;
display: flex;
justify-content: center;
align-items: center;
margin-top: 60rpx;
}
.button {
text-align: center;
margin: 0 10rpx;
font-size: 28rpx;
color: #fff;
&:first-child {
width: 200rpx;
border-radius: 64rpx;
border: 1rpx solid #fff;
}
}
}
</style>

View File

@ -1,23 +1,31 @@
import type { OrderCreateParams, OrderCreateResult, OrderPreResult } from '@/types/order'
import type {
OrderCreateParams,
OrderCreateResult,
OrderListParams,
OrderListResult,
OrderLogisticResult,
OrderPreResult,
OrderResult,
} from '@/types/order'
import { http } from '@/utils/http'
/** 订单状态枚举 */
export enum OrderState {
/** 待付款 */
DaiFuKuan = 1,
PendingPayment = 1,
/** 待发货 */
DaiFaHuo = 2,
PendingShipment = 2,
/** 待收货 */
DaiShouHuo = 3,
PendingDelivery = 3,
/** 待评价 */
DaiPingJia = 4,
PendingReview = 4,
/** 已完成 */
YiWanCheng = 5,
Completed = 5,
/** 已取消 */
YiQuXiao = 6,
Cancelled = 6,
}
/** 订单状态列表 */
export const orderStateList = [
export const OrderStateList = [
{ id: 0, text: '' },
{ id: 1, text: '待付款' },
{ id: 2, text: '待发货' },
@ -54,6 +62,17 @@ export const getMemberOrderPreNowAPI = (data: getMemberOrderPreNowParams) => {
})
}
/**
* -
* @param id id
*/
export const getMemberOrderRepurchaseByIdAPI = (id: string) => {
return http<OrderPreResult>({
method: 'GET',
url: `/member/order/repurchase/${id}`,
})
}
/**
*
* @param data -
@ -65,3 +84,86 @@ export const postCreateOrderAPI = (data: OrderCreateParams) => {
data,
})
}
/**
*
* @param id - ID
*/
export const getMemberOrderDetailAPI = (id: string) => {
return http<OrderResult>({
url: `/member/order/${id}`,
method: 'GET',
})
}
/**
* 模拟发货: 只在测试环境且待发货状态时生效
* @param id - ID
*/
export const getMockOrderConsignmentAPI = (id: string) => {
return http({
url: `/member/order/consignment/${id}`,
method: 'GET',
})
}
/**
*
* @param id - ID
*/
export const putMemberOrderReceiptAPI = (id: string) => {
return http({
url: `/member/order/${id}/receipt`,
method: 'PUT',
})
}
/**
* 获取物流信息: 在待收货
* @param id - ID
*/
export const getMemberOrderLogisticsAPI = (id: string) => {
return http<OrderLogisticResult>({
url: `/member/order/${id}/logistics`,
method: 'GET',
})
}
/**
*
* @param id - ID
*/
export const deleteMemberOrderAPI = (data: { ids: string[] }) => {
return http({
url: '/member/order',
method: 'DELETE',
data,
})
}
/**
*
* @param id - ID
*/
export const putMemberOrderCancleAPI = (id: string, data: { cancelReason: string }) => {
return http({
url: `/member/order/${id}/cancel`,
method: 'PUT',
data,
})
}
/**
*
* @param data -
* @param data.page -
* @param data.pageSize -
* @param data.orderState - 1 2 3 4 5 6
*/
export const getMemberOrderListAPI = (data: OrderListParams) => {
return http<OrderListResult>({
url: '/member/order',
method: 'GET',
data,
})
}

25
src/services/pay.ts Normal file
View File

@ -0,0 +1,25 @@
import { http } from '@/utils/http'
/**
*
* @param data Id
*/
export const getWechatPayParamsAPI = (data: { orderId: string }) => {
return http<WechatMiniprogram.RequestPaymentOption>({
url: '/pay/wxpay/miniapp',
method: 'GET',
data,
})
}
/**
* 模拟支付: 只在测试环境且待发货状态时生效
* @param data Id
*/
export const getMockPayParamsAPI = (data: { orderId: string }) => {
return http({
url: '/pay/mock',
method: 'GET',
data,
})
}

View File

@ -5,15 +5,33 @@ import { ref } from 'vue'
/**
*
*/
export const useSelectedAddressStore = defineStore('selectedAddress', () => {
const selectedAddress = ref<AddressItem | null>(null)
export const useSelectedAddressStore = defineStore(
'selectedAddress',
() => {
const selectedAddress = ref<AddressItem | null>(null)
const setSelectedAddress = (address: AddressItem) => {
selectedAddress.value = address
}
const setSelectedAddress = (address: AddressItem) => {
selectedAddress.value = address
}
return {
selectedAddress,
setSelectedAddress,
}
})
return {
selectedAddress,
setSelectedAddress,
}
},
{
// 网页端配置
// persist: true,
// 小程序端配置
persist: {
storage: {
getItem(key) {
return uni.getStorageSync(key)
},
setItem(key, value) {
uni.setStorageSync(key, value)
},
},
},
},
)