fix(api): 替换所有接口请求的域名为 guilixu-api.websoft.top
- 将获取 STS Token 的接口地址由 paopao-api 改为 guilixu-api - 更新图片上传接口地址为新的 guilixu-api 域名 - 修改用户推广页面中邀请码链接和二维码接口的域名 - 更改注册页微信登录接口请求的域名为 guilixu-api
This commit is contained in:
519
src/pages/order/detail.tsx
Normal file
519
src/pages/order/detail.tsx
Normal file
@@ -0,0 +1,519 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react'
|
||||
import { View, Text, Image, ScrollView } from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { Button } from '@nutui/nutui-react-taro'
|
||||
import { getShopOrder, prepayShopOrder, updateShopOrder, repairOrder, removeShopOrder } from '@/api/shop/shopOrder'
|
||||
import { listShopOrderGoodsByOrderId } from '@/api/shop/shopOrderGoods'
|
||||
import type { ShopOrder } from '@/api/shop/shopOrder/model'
|
||||
import { OrderStatus, OrderStatusText } from '@/types/order'
|
||||
import Price from '@/components/common/Price'
|
||||
|
||||
definePageConfig({
|
||||
navigationBarTitleText: '订单详情',
|
||||
})
|
||||
|
||||
// 支付方式映射
|
||||
const PAY_TYPE_MAP: Record<number, string> = {
|
||||
0: '余额支付',
|
||||
1: '微信支付',
|
||||
2: '会员卡支付',
|
||||
3: '支付宝',
|
||||
15: '积分支付',
|
||||
}
|
||||
|
||||
// 发票状态映射
|
||||
const INVOICE_STATUS_MAP: Record<number, string> = {
|
||||
0: '未开票',
|
||||
1: '已开票',
|
||||
2: '不可开票',
|
||||
}
|
||||
|
||||
// 发货状态映射
|
||||
const DELIVERY_STATUS_MAP: Record<number, string> = {
|
||||
10: '未发货',
|
||||
20: '已发货',
|
||||
30: '部分发货',
|
||||
}
|
||||
|
||||
/** 根据订单状态计算展示用的状态标签 */
|
||||
const getOrderDisplayStatus = (order: ShopOrder): { title: string; bgColor: string; subtitle: string } => {
|
||||
const { payStatus, deliveryStatus, orderStatus } = order
|
||||
|
||||
// 退款/取消相关状态
|
||||
if (orderStatus === OrderStatus.Cancelled) {
|
||||
return { bgColor: '#999', title: '已取消', subtitle: '订单已取消' }
|
||||
}
|
||||
if (orderStatus === OrderStatus.Cancelling) {
|
||||
return { bgColor: '#ff7d00', title: '取消中', subtitle: '退款处理中' }
|
||||
}
|
||||
if (orderStatus === OrderStatus.RefundSuccess) {
|
||||
return { bgColor: '#999', title: '已退款', subtitle: '退款已原路返回' }
|
||||
}
|
||||
if (orderStatus === OrderStatus.RefundApply || orderStatus === OrderStatus.ClientRefundApply) {
|
||||
return { bgColor: '#ee0a24', title: '退款申请中', subtitle: '商家正在处理退款' }
|
||||
}
|
||||
if (orderStatus === OrderStatus.RefundRejected) {
|
||||
return { bgColor: '#ee0a24', title: '退款被拒绝', subtitle: '退款申请已被拒绝' }
|
||||
}
|
||||
|
||||
// 正常订单流程
|
||||
if (!payStatus) {
|
||||
return { bgColor: '#ff7d00', title: '待付款', subtitle: '请尽快完成支付' }
|
||||
}
|
||||
if (deliveryStatus === 10) {
|
||||
return { bgColor: '#4b9cf5', title: '待发货', subtitle: '商家正在准备商品' }
|
||||
}
|
||||
if (deliveryStatus === 20 || deliveryStatus === 30) {
|
||||
return { bgColor: '#4b9cf5', title: '待收货', subtitle: '商品运输中,请注意查收' }
|
||||
}
|
||||
if (orderStatus === OrderStatus.Completed) {
|
||||
return { bgColor: '#0e932e', title: '已完成', subtitle: '交易已完成,感谢购买' }
|
||||
}
|
||||
if (orderStatus === OrderStatus.Unused) {
|
||||
return { bgColor: '#0e932e', title: '已付款', subtitle: '订单已支付' }
|
||||
}
|
||||
|
||||
// 默认
|
||||
return { bgColor: '#999', title: OrderStatusText[orderStatus ?? 0] || '未知', subtitle: '' }
|
||||
}
|
||||
|
||||
/** 根据订单状态判断当前操作阶段 */
|
||||
type OrderPhase = 'unpaid' | 'unshipped' | 'shipped' | 'completed' | 'cancelled' | 'refund'
|
||||
|
||||
const getOrderPhase = (order: ShopOrder): OrderPhase => {
|
||||
const { payStatus, deliveryStatus, orderStatus } = order
|
||||
|
||||
// 退款/取消
|
||||
if ([OrderStatus.Cancelled, OrderStatus.Cancelling, OrderStatus.RefundSuccess, OrderStatus.RefundApply, OrderStatus.ClientRefundApply, OrderStatus.RefundRejected].includes(orderStatus as OrderStatus)) {
|
||||
return orderStatus === OrderStatus.Cancelled || orderStatus === OrderStatus.RefundSuccess ? 'cancelled' : 'refund'
|
||||
}
|
||||
|
||||
if (!payStatus) return 'unpaid'
|
||||
if (deliveryStatus === 10) return 'unshipped'
|
||||
if (deliveryStatus === 20 || deliveryStatus === 30) return 'shipped'
|
||||
if (orderStatus === OrderStatus.Completed) return 'completed'
|
||||
|
||||
return 'unshipped'
|
||||
}
|
||||
|
||||
const OrderDetailPage: React.FC = () => {
|
||||
const { id } = Taro.getCurrentInstance().router?.params || {}
|
||||
const [order, setOrder] = useState<ShopOrder | null>(null)
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const loadOrder = useCallback(async () => {
|
||||
if (!id) return
|
||||
try {
|
||||
const orderData = await getShopOrder(Number(id))
|
||||
// 如果订单中没有商品清单,则单独加载
|
||||
if (!orderData.orderGoods || orderData.orderGoods.length === 0) {
|
||||
try {
|
||||
const goods = await listShopOrderGoodsByOrderId(Number(id))
|
||||
orderData.orderGoods = goods
|
||||
} catch {
|
||||
// 忽略加载商品失败
|
||||
}
|
||||
}
|
||||
setOrder(orderData)
|
||||
} catch {
|
||||
Taro.showToast({ title: '加载失败', icon: 'none' })
|
||||
}
|
||||
}, [id])
|
||||
|
||||
useEffect(() => {
|
||||
loadOrder()
|
||||
}, [loadOrder])
|
||||
|
||||
// 格式化时间
|
||||
const formatTime = (time: string | undefined) => {
|
||||
if (!time) return '-'
|
||||
return time.replace('T', ' ').slice(0, 19)
|
||||
}
|
||||
|
||||
// 取消订单
|
||||
const handleCancelOrder = () => {
|
||||
Taro.showModal({
|
||||
title: '确认取消',
|
||||
content: '确定要取消该订单吗?',
|
||||
confirmColor: '#ee0a24',
|
||||
success: async (res) => {
|
||||
if (res.confirm && order) {
|
||||
try {
|
||||
await updateShopOrder({ ...order, orderStatus: OrderStatus.Cancelled })
|
||||
Taro.showToast({ title: '订单已取消', icon: 'success' })
|
||||
loadOrder()
|
||||
} catch {
|
||||
Taro.showToast({ title: '取消失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 立即支付
|
||||
const handlePay = async () => {
|
||||
if (!order || loading) return
|
||||
setLoading(true)
|
||||
try {
|
||||
const payResult = await prepayShopOrder({ orderId: order.orderId!, payType: 1 })
|
||||
if (payResult) {
|
||||
await Taro.requestPayment({
|
||||
timeStamp: payResult.timeStamp,
|
||||
nonceStr: payResult.nonceStr,
|
||||
package: payResult.package,
|
||||
signType: payResult.signType,
|
||||
paySign: payResult.paySign,
|
||||
})
|
||||
Taro.showToast({ title: '支付成功', icon: 'success' })
|
||||
loadOrder()
|
||||
}
|
||||
} catch (err: any) {
|
||||
if (err?.message !== 'requestPayment:fail cancel') {
|
||||
Taro.showToast({ title: err?.message || '支付失败', icon: 'none' })
|
||||
}
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
// 确认收货
|
||||
const handleConfirmReceive = () => {
|
||||
Taro.showModal({
|
||||
title: '确认收货',
|
||||
content: '确认已收到商品?',
|
||||
confirmColor: '#0e932e',
|
||||
success: async (res) => {
|
||||
if (res.confirm && order) {
|
||||
try {
|
||||
await updateShopOrder({ ...order, orderStatus: OrderStatus.Completed })
|
||||
Taro.showToast({ title: '已确认收货', icon: 'success' })
|
||||
loadOrder()
|
||||
} catch {
|
||||
Taro.showToast({ title: '操作失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 删除订单
|
||||
const handleDeleteOrder = () => {
|
||||
Taro.showModal({
|
||||
title: '删除订单',
|
||||
content: '确定要删除该订单吗?删除后不可恢复',
|
||||
confirmColor: '#ee0a24',
|
||||
success: async (res) => {
|
||||
if (res.confirm && order) {
|
||||
try {
|
||||
await removeShopOrder(order.orderId)
|
||||
Taro.showToast({ title: '订单已删除', icon: 'success' })
|
||||
setTimeout(() => Taro.navigateBack(), 1000)
|
||||
} catch {
|
||||
Taro.showToast({ title: '删除失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (!order) {
|
||||
return (
|
||||
<View className='min-h-screen bg-gray-50 flex items-center justify-center'>
|
||||
<Text className='text-gray-400 text-sm'>加载中...</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
const displayStatus = getOrderDisplayStatus(order)
|
||||
const phase = getOrderPhase(order)
|
||||
const isExpress = order.deliveryType === 0 || order.deliveryType === undefined
|
||||
const goodsCount = order.orderGoods?.reduce((sum, g) => sum + (g.totalNum || 1), 0) || 0
|
||||
|
||||
return (
|
||||
<View className='min-h-screen bg-gray-50 flex flex-col'>
|
||||
<ScrollView scrollY className='flex-1'>
|
||||
{/* 状态卡片 */}
|
||||
<View
|
||||
className='p-4'
|
||||
style={{ backgroundColor: displayStatus.bgColor }}
|
||||
>
|
||||
<Text className='text-white text-lg font-medium block'>{displayStatus.title}</Text>
|
||||
<Text className='text-white text-sm opacity-90 mt-1 block'>{displayStatus.subtitle}</Text>
|
||||
{phase === 'shipped' && (
|
||||
<View
|
||||
className='mt-2 px-3 py-2 rounded-lg inline-flex items-center'
|
||||
style={{ backgroundColor: 'rgba(255,255,255,0.2)' }}
|
||||
onClick={() => Taro.navigateTo({ url: `/pages/order/logistics?orderId=${order.orderId}` })}
|
||||
>
|
||||
<Text className='text-white text-xs'>查看物流</Text>
|
||||
<Text className='text-white text-xs ml-1'>›</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
|
||||
{/* 收货信息 */}
|
||||
{isExpress ? (
|
||||
<View className='bg-white mx-3 mt-3 p-3 rounded-lg'>
|
||||
<View className='flex items-start gap-2'>
|
||||
<Text className='text-base'>📦</Text>
|
||||
<View className='flex-1'>
|
||||
<View className='flex gap-2 mb-1'>
|
||||
<Text className='text-sm font-medium text-gray-800'>{order.realName || '未知'}</Text>
|
||||
<Text className='text-sm text-gray-600'>{order.mobile || order.phone || '-'}</Text>
|
||||
</View>
|
||||
<Text className='text-xs text-gray-500'>{order.address || '未填写收货地址'}</Text>
|
||||
{(order.sendStartTime || order.sendEndTime) && (
|
||||
<Text className='text-xs text-gray-400 mt-1'>
|
||||
期望配送: {order.sendStartTime?.slice(0, 10)} ~ {order.sendEndTime?.slice(0, 10)}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
) : (
|
||||
<View className='bg-white mx-3 mt-3 p-3 rounded-lg'>
|
||||
<View className='flex items-start gap-2'>
|
||||
<Text className='text-base'>🏪</Text>
|
||||
<View className='flex-1'>
|
||||
<Text className='text-sm font-medium text-gray-800'>{order.selfTakeMerchantName || '自提点'}</Text>
|
||||
<Text className='text-xs text-gray-500 mt-1'>{order.address || '-'}</Text>
|
||||
{order.selfTakeCode && (
|
||||
<View className='mt-2 px-3 py-2 rounded-lg inline-flex items-center' style={{ backgroundColor: '#fff7e6' }}>
|
||||
<Text className='text-xs text-orange-600'>自提码: {order.selfTakeCode}</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* 商品列表 */}
|
||||
<View className='bg-white mx-3 mt-3 rounded-lg overflow-hidden'>
|
||||
<View className='p-3 border-b border-gray-100'>
|
||||
<Text className='text-sm font-medium text-gray-800'>商品清单 ({goodsCount}件)</Text>
|
||||
</View>
|
||||
{order.orderGoods?.map((item, idx) => (
|
||||
<View
|
||||
key={idx}
|
||||
className='flex gap-3 p-3 border-b border-gray-50'
|
||||
style={idx === (order.orderGoods?.length ?? 0) - 1 ? { borderBottom: 'none' } : undefined}
|
||||
onClick={() => item.goodsId && Taro.navigateTo({ url: `/pages/shop/product-detail?id=${item.goodsId}` })}
|
||||
>
|
||||
<View className='w-16 h-16 rounded-md bg-gray-100 flex-shrink-0 overflow-hidden'>
|
||||
{item.image ? (
|
||||
<Image className='w-full h-full' src={item.image} mode='aspectFill' />
|
||||
) : (
|
||||
<View className='w-full h-full flex items-center justify-center'>
|
||||
<Text className='text-xs text-gray-400'>暂无</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<View className='flex-1 flex flex-col justify-between h-16'>
|
||||
<Text className='text-sm text-gray-800 line-clamp-1'>{item.goodsName || '未知商品'}</Text>
|
||||
{item.spec ? (
|
||||
<Text className='text-xs text-gray-500 mt-1'>规格: {item.spec}</Text>
|
||||
) : null}
|
||||
</View>
|
||||
<View className='flex flex-col items-end justify-between h-16'>
|
||||
<Price price={item.price || '0'} size='small' />
|
||||
<Text className='text-xs text-gray-500'>x{item.totalNum || 1}</Text>
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
|
||||
{/* 金额明细 */}
|
||||
<View className='bg-white mx-3 mt-3 p-3 rounded-lg'>
|
||||
<Text className='text-sm font-medium text-gray-800 mb-3 block'>金额明细</Text>
|
||||
<View className='space-y-2'>
|
||||
<View className='flex justify-between'>
|
||||
<Text className='text-sm text-gray-500'>商品总额</Text>
|
||||
<Text className='text-sm text-gray-700'>¥{order.totalPrice || '0.00'}</Text>
|
||||
</View>
|
||||
{order.reducePrice && Number(order.reducePrice) > 0 && (
|
||||
<View className='flex justify-between'>
|
||||
<Text className='text-sm text-gray-500'>优惠</Text>
|
||||
<Text className='text-sm text-green-600'>-¥{order.reducePrice}</Text>
|
||||
</View>
|
||||
)}
|
||||
<View className='flex justify-between pt-2 border-t border-gray-100'>
|
||||
<Text className='text-sm font-medium text-gray-800'>实付金额</Text>
|
||||
<Price price={order.payPrice || '0'} size='normal' />
|
||||
</View>
|
||||
<View className='flex justify-between pt-2 border-t border-gray-100'>
|
||||
<Text className='text-xs text-gray-400'>支付方式</Text>
|
||||
<Text className='text-xs text-gray-500'>{PAY_TYPE_MAP[order.payType ?? 0] || '未知'}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 订单信息 */}
|
||||
<View className='bg-white mx-3 mt-3 p-3 rounded-lg mb-4'>
|
||||
<Text className='text-sm font-medium text-gray-800 mb-3 block'>订单信息</Text>
|
||||
<View className='space-y-2'>
|
||||
<View className='flex justify-between'>
|
||||
<Text className='text-xs text-gray-400'>订单编号</Text>
|
||||
<View className='flex items-center gap-1'>
|
||||
<Text className='text-xs text-gray-600'>{order.orderNo}</Text>
|
||||
<Text
|
||||
className='text-xs text-blue-500'
|
||||
onClick={() => {
|
||||
Taro.setClipboardData({ data: order.orderNo || '' })
|
||||
Taro.showToast({ title: '已复制', icon: 'none' })
|
||||
}}
|
||||
>
|
||||
复制
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View className='flex justify-between'>
|
||||
<Text className='text-xs text-gray-400'>发货状态</Text>
|
||||
<Text className='text-xs text-gray-600'>{DELIVERY_STATUS_MAP[order.deliveryStatus ?? 10]}</Text>
|
||||
</View>
|
||||
<View className='flex justify-between'>
|
||||
<Text className='text-xs text-gray-400'>创建时间</Text>
|
||||
<Text className='text-xs text-gray-600'>{formatTime(order.createTime)}</Text>
|
||||
</View>
|
||||
{order.payTime && (
|
||||
<View className='flex justify-between'>
|
||||
<Text className='text-xs text-gray-400'>支付时间</Text>
|
||||
<Text className='text-xs text-gray-600'>{formatTime(order.payTime)}</Text>
|
||||
</View>
|
||||
)}
|
||||
{order.deliveryTime && (
|
||||
<View className='flex justify-between'>
|
||||
<Text className='text-xs text-gray-400'>发货时间</Text>
|
||||
<Text className='text-xs text-gray-600'>{formatTime(order.deliveryTime)}</Text>
|
||||
</View>
|
||||
)}
|
||||
{order.expirationTime && (
|
||||
<View className='flex justify-between'>
|
||||
<Text className='text-xs text-gray-400'>过期时间</Text>
|
||||
<Text className='text-xs text-gray-600'>{formatTime(order.expirationTime)}</Text>
|
||||
</View>
|
||||
)}
|
||||
{order.comments && (
|
||||
<View className='flex justify-start'>
|
||||
<Text className='text-xs text-gray-400 mr-2'>备注</Text>
|
||||
<Text className='text-xs text-gray-600 flex-1'>{order.comments}</Text>
|
||||
</View>
|
||||
)}
|
||||
<View className='flex justify-between'>
|
||||
<Text className='text-xs text-gray-400'>发票状态</Text>
|
||||
<Text className='text-xs text-gray-600'>{INVOICE_STATUS_MAP[order.isInvoice ?? 0]}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</ScrollView>
|
||||
|
||||
{/* 底部操作按钮(根据订单阶段动态显示) */}
|
||||
<View
|
||||
className='bg-white border-t border-gray-100 p-3 flex flex-row gap-2'
|
||||
style={{ paddingBottom: '20px' }}
|
||||
>
|
||||
{/* 待付款: 取消 + 支付 */}
|
||||
{phase === 'unpaid' && (
|
||||
<>
|
||||
<Button
|
||||
size='small'
|
||||
className='flex-1'
|
||||
style={{ borderColor: '#ddd', color: '#666' }}
|
||||
onClick={handleCancelOrder}
|
||||
>
|
||||
取消订单
|
||||
</Button>
|
||||
<Button
|
||||
size='small'
|
||||
className='flex-1'
|
||||
style={{ backgroundColor: '#ee0a24' }}
|
||||
loading={loading}
|
||||
onClick={handlePay}
|
||||
>
|
||||
立即支付
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 待发货: 退款 + 联系客服 */}
|
||||
{phase === 'unshipped' && (
|
||||
<>
|
||||
<Button
|
||||
size='small'
|
||||
className='flex-1'
|
||||
style={{ borderColor: '#ddd', color: '#666' }}
|
||||
onClick={() => Taro.navigateTo({ url: `/pages/order/refund?orderId=${order.orderId}` })}
|
||||
>
|
||||
退款
|
||||
</Button>
|
||||
<Button
|
||||
size='small'
|
||||
className='flex-1'
|
||||
style={{ backgroundColor: '#4b9cf5' }}
|
||||
onClick={() => Taro.makePhoneCall({ phoneNumber: '400-000-0000' })}
|
||||
>
|
||||
联系客服
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 待收货: 物流 + 确认收货 */}
|
||||
{phase === 'shipped' && (
|
||||
<>
|
||||
<Button
|
||||
size='small'
|
||||
className='flex-1'
|
||||
style={{ borderColor: '#4b9cf5', color: '#4b9cf5' }}
|
||||
onClick={() => Taro.navigateTo({ url: `/pages/order/logistics?orderId=${order.orderId}` })}
|
||||
>
|
||||
查看物流
|
||||
</Button>
|
||||
<Button
|
||||
size='small'
|
||||
className='flex-1'
|
||||
style={{ backgroundColor: '#0e932e' }}
|
||||
onClick={handleConfirmReceive}
|
||||
>
|
||||
确认收货
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 已完成: 售后 + 删除 */}
|
||||
{phase === 'completed' && (
|
||||
<>
|
||||
<Button
|
||||
size='small'
|
||||
className='flex-1'
|
||||
style={{ borderColor: '#ddd', color: '#666' }}
|
||||
onClick={() => Taro.navigateTo({ url: `/pages/order/after-sale-apply?orderId=${order.orderId}&type=refund` })}
|
||||
>
|
||||
申请售后
|
||||
</Button>
|
||||
<Button
|
||||
size='small'
|
||||
className='flex-1'
|
||||
style={{ borderColor: '#ee0a24', color: '#ee0a24' }}
|
||||
onClick={handleDeleteOrder}
|
||||
>
|
||||
删除订单
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 已取消/已退款: 删除 */}
|
||||
{phase === 'cancelled' && (
|
||||
<Button
|
||||
size='small'
|
||||
className='flex-1'
|
||||
style={{ borderColor: '#ee0a24', color: '#ee0a24' }}
|
||||
onClick={handleDeleteOrder}
|
||||
>
|
||||
删除订单
|
||||
</Button>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
export default OrderDetailPage
|
||||
Reference in New Issue
Block a user