import {Avatar, Cell, Space, Empty, Tabs, Button, TabPane, Image, Dialog} from '@nutui/nutui-react-taro' import {useEffect, useState, useCallback, CSSProperties} from "react"; import {View, Text} from '@tarojs/components' import Taro from '@tarojs/taro'; import {InfiniteLoading} from '@nutui/nutui-react-taro' import dayjs from "dayjs"; import {pageShopOrder, updateShopOrder, createOrder} from "@/api/shop/shopOrder"; import {ShopOrder, ShopOrderParam} from "@/api/shop/shopOrder/model"; import {listShopOrderGoods} from "@/api/shop/shopOrderGoods"; import {ShopOrderGoods} from "@/api/shop/shopOrderGoods/model"; import {copyText} from "@/utils/common"; import PaymentCountdown from "@/components/PaymentCountdown"; import {PaymentType} from "@/utils/payment"; // 判断订单是否支付已过期 const isPaymentExpired = (createTime: string, timeoutHours: number = 24): boolean => { if (!createTime) return false; const createTimeObj = dayjs(createTime); const expireTime = createTimeObj.add(timeoutHours, 'hour'); const now = dayjs(); return now.isAfter(expireTime); }; const getInfiniteUlStyle = (showSearch: boolean = false): CSSProperties => ({ marginTop: showSearch ? '0' : '0', // 如果显示搜索框,增加更多的上边距 height: showSearch ? '75vh' : '84vh', // 相应调整高度 width: '100%', padding: '0', overflowY: 'auto', overflowX: 'hidden' // 注意:小程序不支持 boxShadow }) // 统一的订单状态标签配置,与后端 statusFilter 保持一致 const tabs = [ { index: 0, key: '全部', title: '全部', description: '所有订单', statusFilter: -1 // 使用-1表示全部订单 }, { index: 1, key: '待付款', title: '待付款', description: '等待付款的订单', statusFilter: 0 // 对应后端:pay_status = false }, { index: 2, key: '待发货', title: '待发货', description: '已付款待发货的订单', statusFilter: 1 // 对应后端:pay_status = true AND delivery_status = 10 }, { index: 3, key: '待收货', title: '待收货', description: '已发货待收货的订单', statusFilter: 3 // 对应后端:pay_status = true AND delivery_status = 20 }, { index: 4, key: '已完成', title: '已完成', description: '已完成的订单', statusFilter: 5 // 对应后端:order_status = 1 }, { index: 5, key: '退货/售后', title: '退货/售后', description: '退货/售后的订单', statusFilter: 6 // 对应后端:order_status = 6 (已退款) } ] // 扩展订单接口,包含商品信息 interface OrderWithGoods extends ShopOrder { orderGoods?: ShopOrderGoods[]; } interface OrderListProps { onReload?: () => void; searchParams?: ShopOrderParam; showSearch?: boolean; onSearchParamsChange?: (params: ShopOrderParam) => void; // 新增:通知父组件参数变化 } function OrderList(props: OrderListProps) { const [list, setList] = useState([]) const [page, setPage] = useState(1) const [hasMore, setHasMore] = useState(true) // 根据传入的statusFilter设置初始tab索引 const getInitialTabIndex = () => { if (props.searchParams?.statusFilter !== undefined) { const tab = tabs.find(t => t.statusFilter === props.searchParams?.statusFilter); return tab ? tab.index : 0; } return 0; }; const [tapIndex, setTapIndex] = useState(() => { const initialIndex = getInitialTabIndex(); console.log('初始化tapIndex:', initialIndex, '对应statusFilter:', props.searchParams?.statusFilter); return initialIndex; }) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [cancelDialogVisible, setCancelDialogVisible] = useState(false) const [orderToCancel, setOrderToCancel] = useState(null) const [confirmReceiveDialogVisible, setConfirmReceiveDialogVisible] = useState(false) const [orderToConfirmReceive, setOrderToConfirmReceive] = useState(null) // 获取订单状态文本 const getOrderStatusText = (order: ShopOrder) => { // 优先检查订单状态 if (order.orderStatus === 2) return '已取消'; if (order.orderStatus === 4) return '退款申请中'; if (order.orderStatus === 5) return '退款被拒绝'; if (order.orderStatus === 6) return '退款成功'; if (order.orderStatus === 7) return '客户端申请退款'; // 检查支付状态 (payStatus为boolean类型,false/0表示未付款,true/1表示已付款) if (!order.payStatus) return '等待买家付款'; // 已付款后检查发货状态 if (order.deliveryStatus === 10) return '待发货'; if (order.deliveryStatus === 20) return '待收货'; if (order.deliveryStatus === 30) return '已完成'; // 最后检查订单完成状态 if (order.orderStatus === 1) return '已完成'; if (order.orderStatus === 0) return '未使用'; return '未知状态'; }; // 获取订单状态颜色 const getOrderStatusColor = (order: ShopOrder) => { // 优先检查订单状态 if (order.orderStatus === 2) return 'text-gray-500'; // 已取消 if (order.orderStatus === 4) return 'text-orange-500'; // 退款申请中 if (order.orderStatus === 5) return 'text-red-500'; // 退款被拒绝 if (order.orderStatus === 6) return 'text-green-500'; // 退款成功 if (order.orderStatus === 7) return 'text-orange-500'; // 客户端申请退款 // 检查支付状态 if (!order.payStatus) return 'text-orange-500'; // 等待买家付款 // 已付款后检查发货状态 if (order.deliveryStatus === 10) return 'text-blue-500'; // 待发货 if (order.deliveryStatus === 20) return 'text-purple-500'; // 待收货 if (order.deliveryStatus === 30) return 'text-green-500'; // 已收货 // 最后检查订单完成状态 if (order.orderStatus === 1) return 'text-green-600'; // 已完成 if (order.orderStatus === 0) return 'text-gray-500'; // 未使用 return 'text-gray-600'; // 默认颜色 }; // 使用后端统一的 statusFilter 进行筛选 const getOrderStatusParams = (index: string | number) => { let params: ShopOrderParam = {}; // 添加用户ID过滤 params.userId = Taro.getStorageSync('UserId'); // 获取当前tab的statusFilter配置 const currentTab = tabs.find(tab => tab.index === Number(index)); if (currentTab && currentTab.statusFilter !== undefined) { params.statusFilter = currentTab.statusFilter; } // 注意:当statusFilter为undefined时,不要添加到params中,这样API请求就不会包含这个参数 console.log(`Tab ${index} (${currentTab?.title}) 筛选参数:`, params); return params; }; const reload = useCallback(async (resetPage = false, targetPage?: number) => { setLoading(true); setError(null); // 清除之前的错误 const currentPage = resetPage ? 1 : (targetPage || page); const statusParams = getOrderStatusParams(tapIndex); // 合并搜索条件,tab的statusFilter优先级更高 const searchConditions: any = { page: currentPage, userId: statusParams.userId, // 用户ID ...props.searchParams, // 搜索关键词等其他条件 }; // statusFilter总是添加到搜索条件中(包括-1表示全部) if (statusParams.statusFilter !== undefined) { searchConditions.statusFilter = statusParams.statusFilter; } console.log('订单筛选条件:', { tapIndex, statusParams, searchConditions, finalStatusFilter: searchConditions.statusFilter }); try { const res = await pageShopOrder(searchConditions); let newList: OrderWithGoods[]; if (res?.list && res?.list.length > 0) { // 批量获取订单商品信息,限制并发数量 const batchSize = 3; // 限制并发数量为3 const ordersWithGoods: OrderWithGoods[] = []; for (let i = 0; i < res.list.length; i += batchSize) { const batch = res.list.slice(i, i + batchSize); const batchResults = await Promise.all( batch.map(async (order) => { try { const orderGoods = await listShopOrderGoods({orderId: order.orderId}); return { ...order, orderGoods: orderGoods || [] }; } catch (error) { console.error('获取订单商品失败:', error); return { ...order, orderGoods: [] }; } }) ); ordersWithGoods.push(...batchResults); } // 使用函数式更新避免依赖 list setList(prevList => { const newList = resetPage ? ordersWithGoods : (prevList || []).concat(ordersWithGoods); return newList; }); // 正确判断是否还有更多数据 const hasMoreData = res.list.length >= 10; // 假设每页10条数据 setHasMore(hasMoreData); } else { setList(prevList => resetPage ? [] : prevList); setHasMore(false); } setPage(currentPage); setLoading(false); } catch (error) { console.error('加载订单失败:', error); setLoading(false); setError('加载订单失败,请重试'); // 添加错误提示 Taro.showToast({ title: '加载失败,请重试', icon: 'none' }); } }, [tapIndex, page, props.searchParams]); // 移除 list 依赖 const reloadMore = useCallback(async () => { if (loading || !hasMore) return; // 防止重复加载 const nextPage = page + 1; setPage(nextPage); await reload(false, nextPage); }, [loading, hasMore, page, reload]); // 确认收货 - 显示确认对话框 const confirmReceive = (order: ShopOrder) => { setOrderToConfirmReceive(order); setConfirmReceiveDialogVisible(true); }; // 确认收货 - 执行收货操作 const handleConfirmReceive = async () => { if (!orderToConfirmReceive) return; try { setConfirmReceiveDialogVisible(false); await updateShopOrder({ ...orderToConfirmReceive, deliveryStatus: 30, // 已收货 orderStatus: 1 // 已完成 }); Taro.showToast({ title: '确认收货成功', icon: 'success' }); await reload(true); // 重新加载列表 props.onReload?.(); // 通知父组件刷新 // 清空状态 setOrderToConfirmReceive(null); } catch (error) { console.error('确认收货失败:', error); Taro.showToast({ title: '确认收货失败', icon: 'none' }); // 重新显示对话框 setConfirmReceiveDialogVisible(true); } }; // 取消确认收货对话框 const handleCancelReceiveDialog = () => { setConfirmReceiveDialogVisible(false); setOrderToConfirmReceive(null); }; // 取消订单 const cancelOrder = (order: ShopOrder) => { setOrderToCancel(order); setCancelDialogVisible(true); }; // 确认取消订单 const handleConfirmCancel = async () => { if (!orderToCancel) return; try { setCancelDialogVisible(false); // 更新订单状态为已取消,而不是删除订单 await updateShopOrder({ ...orderToCancel, orderStatus: 2 // 已取消 }); Taro.showToast({ title: '订单已取消', icon: 'success' }); void reload(true); // 重新加载列表 props.onReload?.(); // 通知父组件刷新 } catch (error) { console.error('取消订单失败:', error); Taro.showToast({ title: '取消订单失败', icon: 'error' }); } finally { setOrderToCancel(null); } }; // 取消对话框的取消操作 const handleCancelDialog = () => { setCancelDialogVisible(false); setOrderToCancel(null); }; // 立即支付 const payOrder = async (order: ShopOrder) => { try { if (!order.orderId || !order.orderNo) { Taro.showToast({ title: '订单信息错误', icon: 'error' }); return; } // 检查订单是否已过期 if (order.createTime && isPaymentExpired(order.createTime)) { Taro.showToast({ title: '订单已过期,无法支付', icon: 'error' }); return; } // 检查订单状态 if (order.payStatus) { Taro.showToast({ title: '订单已支付', icon: 'none' }); return; } if (order.orderStatus === 2) { Taro.showToast({ title: '订单已取消,无法支付', icon: 'error' }); return; } Taro.showLoading({ title: '发起支付...' }); // 构建商品数据 const goodsItems = order.orderGoods?.map(goods => ({ goodsId: goods.goodsId, quantity: goods.totalNum || 1 })) || []; // 对于已存在的订单,我们需要重新发起支付 // 构建支付请求数据,包含完整的商品信息 const paymentData = { orderId: order.orderId, orderNo: order.orderNo, goodsItems: goodsItems, addressId: order.addressId, payType: PaymentType.WECHAT }; console.log('重新支付数据:', paymentData); // 直接调用createOrder API进行重新支付 const result = await createOrder(paymentData as any); if (!result) { throw new Error('支付发起失败'); } // 验证微信支付必要参数 if (!result.timeStamp || !result.nonceStr || !result.package || !result.paySign) { throw new Error('微信支付参数不完整'); } // 调用微信支付 await Taro.requestPayment({ timeStamp: result.timeStamp, nonceStr: result.nonceStr, package: result.package, signType: (result.signType || 'MD5') as 'MD5' | 'HMAC-SHA256', paySign: result.paySign, }); // 支付成功 Taro.showToast({ title: '支付成功', icon: 'success' }); // 重新加载订单列表 void reload(true); props.onReload?.(); // 跳转到订单页面 setTimeout(() => { Taro.navigateTo({ url: '/user/order/order' }); }, 2000); } catch (error: any) { console.error('支付失败:', error); let errorMessage = '支付失败,请重试'; if (error.message) { if (error.message.includes('cancel')) { errorMessage = '用户取消支付'; } else if (error.message.includes('余额不足')) { errorMessage = '账户余额不足'; } else { errorMessage = error.message; } } Taro.showToast({ title: errorMessage, icon: 'error' }); } finally { Taro.hideLoading(); } }; useEffect(() => { void reload(true); // 首次加载或tab切换时重置页码 }, [tapIndex]); // 只监听tapIndex变化,避免reload依赖循环 // 监听外部statusFilter变化,同步更新tab索引 useEffect(() => { // 获取当前的statusFilter,如果未定义则默认为-1(全部) const currentStatusFilter = props.searchParams?.statusFilter !== undefined ? props.searchParams.statusFilter : -1; const tab = tabs.find(t => t.statusFilter === currentStatusFilter); const targetTabIndex = tab ? tab.index : 0; console.log('外部statusFilter变化:', { statusFilter: currentStatusFilter, originalStatusFilter: props.searchParams?.statusFilter, currentTapIndex: tapIndex, targetTabIndex, shouldUpdate: targetTabIndex !== tapIndex }); if (targetTabIndex !== tapIndex) { setTapIndex(targetTabIndex); // 不需要调用reload,因为tapIndex变化会触发reload } }, [props.searchParams?.statusFilter, tapIndex]); // 监听statusFilter变化 return ( <> { console.log('Tab切换:', paneKey, '类型:', typeof paneKey); const newTapIndex = Number(paneKey); setTapIndex(newTapIndex); // 通知父组件更新 searchParams.statusFilter const currentTab = tabs.find(tab => tab.index === newTapIndex); if (currentTab && props.onSearchParamsChange) { const newSearchParams = { ...props.searchParams, statusFilter: currentTab.statusFilter }; console.log('通知父组件更新searchParams:', newSearchParams); props.onSearchParamsChange(newSearchParams); } }} > { tabs?.map((item, _) => { return ( ) }) } {error ? ( {error} ) : ( { }} onScrollToUpper={() => { }} loadingText={ <> 加载中 } loadMoreText={ list.length === 0 ? ( ) : ( 没有更多了 ) } > {/* 订单列表 */} {list.length > 0 && list ?.filter((item) => { // 如果是待付款标签页(tapIndex === 1),过滤掉支付已过期的订单 if (tapIndex === 1 && !item.payStatus && item.orderStatus !== 2 && item.createTime) { return !isPaymentExpired(item.createTime); } return true; }) ?.map((item, index) => { return ( Taro.navigateTo({url: `/shop/orderDetail/index?orderId=${item.orderId}`})}> { e.stopPropagation(); copyText(`${item.orderNo}`) }}>{item.orderNo} {/* 右侧显示合并的状态和倒计时 */} {!item.payStatus && item.orderStatus !== 2 ? ( ) : ( getOrderStatusText(item) )} {dayjs(item.createTime).format('YYYY年MM月DD日 HH:mm:ss')} {/* 商品信息 */} {item.orderGoods && item.orderGoods.length > 0 ? ( item.orderGoods.map((goods, goodsIndex) => ( {goods.goodsName} {goods.spec && 规格:{goods.spec}} 数量:{goods.totalNum} ¥{goods.price} )) ) : ( {item.title || '订单商品'} {item.totalNum}件商品 )} 实付金额:¥{item.payPrice} {/* 操作按钮 */} {/* 待付款状态:显示取消订单和立即支付 */} {(!item.payStatus) && item.orderStatus !== 2 && ( )} {/* 待收货状态:显示确认收货 */} {item.deliveryStatus === 20 && ( )} {/* 已完成状态:显示申请退款 */} {item.orderStatus === 1 && ( )} {/* 退款相关状态的按钮可以在这里添加 */} ) })} )} {/* 取消订单确认对话框 */} 确定要取消这个订单吗? {/* 确认收货确认对话框 */} 确定已经收到商品了吗?确认收货后订单将完成。 ) } export default OrderList