import {Avatar, Cell, Space, Empty, Tabs, Button, TabPane, Image, Dialog} from '@nutui/nutui-react-taro' import {useEffect, useState, useCallback, useRef, 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"; import {goTo} from "@/utils/navigation"; // 判断订单是否支付已过期 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 { // 避免与 ShopOrder.orderGoods (OrderGoods[]) 冲突:这里使用单独字段保存接口返回的商品明细 orderGoodsList?: ShopOrderGoods[]; } // 后端订单分页接口通常已返回 orderGoods(订单商品明细)。 // 这里把各种可能的字段名做一次归一化,避免再为每个订单额外请求一次“订单商品”接口。 const normalizeOrderGoodsList = (order: any): ShopOrderGoods[] => { const raw = order?.orderGoods || order?.orderGoodsList || order?.goodsList || order?.goods || []; if (!Array.isArray(raw)) return []; return raw.map((g: any) => ({ ...g, goodsId: g?.goodsId ?? g?.itemId ?? g?.goods_id, skuId: g?.skuId ?? g?.sku_id, // 当接口只返回了最小字段时,用订单标题兜底,避免列表出现空白商品名 goodsName: g?.goodsName ?? g?.goodsTitle ?? g?.title ?? g?.name ?? order?.title ?? '商品', image: g?.image ?? g?.goodsImage ?? g?.cover ?? g?.pic, spec: g?.spec ?? g?.specInfo ?? g?.spec_name, totalNum: g?.totalNum ?? g?.quantity ?? g?.num ?? g?.count, price: g?.price ?? g?.payPrice ?? g?.goodsPrice ?? g?.unitPrice })); }; interface OrderListProps { onReload?: () => void; searchParams?: ShopOrderParam; showSearch?: boolean; onSearchParamsChange?: (params: ShopOrderParam) => void; // 新增:通知父组件参数变化 // 订单视图模式:用户/门店/骑手 mode?: 'user' | 'store' | 'rider'; // 固定过滤条件(例如 storeId / riderId),会合并到每次请求里 baseParams?: ShopOrderParam; // 只读模式:隐藏“支付/取消/确认收货/退款”等用户操作按钮 readOnly?: boolean; } function OrderList(props: OrderListProps) { const [list, setList] = useState([]) const pageRef = useRef(1) const [hasMore, setHasMore] = useState(true) const [payingOrderId, setPayingOrderId] = useState(null) // 根据传入的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 isReadOnly = props.readOnly || props.mode === 'store' || props.mode === 'rider' // 获取订单状态文本 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) { // 若订单没有配送员,沿用原“待收货”语义 if (!order.riderId) return '待收货'; // 配送员确认送达后(sendEndTime有值),才进入“待确认收货” if (order.sendEndTime && order.orderStatus !== 1) return '待确认收货'; 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) { if (!order.riderId) return 'text-purple-500'; // 待收货 if (order.sendEndTime && order.orderStatus !== 1) return 'text-purple-500'; // 待确认收货 return 'text-blue-500'; // 配送中 } if (order.deliveryStatus === 30) return 'text-blue-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 = { ...(props.baseParams || {}) }; // 默认是用户视图:添加 userId 过滤;门店/骑手视图由 baseParams 控制 if (!props.mode || props.mode === 'user') { 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 || pageRef.current); const statusParams = getOrderStatusParams(tapIndex); // 合并搜索条件,tab的statusFilter优先级更高 const searchConditions: any = { page: currentPage, ...statusParams, ...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); if (res?.list && res?.list.length > 0) { // 订单分页接口已返回 orderGoods:直接读取并归一化,避免 N+1 请求导致列表加载慢 const ordersWithGoods: OrderWithGoods[] = res.list.map((order: any) => ({ ...order, orderGoodsList: normalizeOrderGoodsList(order) })); // 使用函数式更新避免依赖 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); } pageRef.current = currentPage; setLoading(false); } catch (error) { console.error('加载订单失败:', error); setLoading(false); setError('加载订单失败,请重试'); // 添加错误提示 Taro.showToast({ title: '加载失败,请重试', icon: 'none' }); } }, [tapIndex, props.searchParams]); // 移除 list/page 依赖,避免useEffect触发循环 const reloadMore = useCallback(async () => { if (loading || !hasMore) return; // 防止重复加载 const nextPage = pageRef.current + 1; pageRef.current = nextPage; await reload(false, nextPage); }, [loading, hasMore, reload]); // 确认收货 - 显示确认对话框 const confirmReceive = (order: ShopOrder) => { setOrderToConfirmReceive(order); setConfirmReceiveDialogVisible(true); }; // 确认收货 - 执行收货操作 const handleConfirmReceive = async () => { if (!orderToConfirmReceive) return; try { setConfirmReceiveDialogVisible(false); await updateShopOrder({ ...orderToConfirmReceive, deliveryStatus: orderToConfirmReceive.deliveryStatus, // 10未发货 20已发货 30部分发货(收货由orderStatus控制) 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 applyRefund = async (order: ShopOrder) => { try { // 更新订单状态为"退款申请中" await updateShopOrder({ orderId: order.orderId, orderStatus: 4 // 退款申请中 }); // 更新本地状态 setList(prev => prev.map(item => item.orderId === order.orderId ? {...item, orderStatus: 4} : item )); // 跳转到退款申请页面 Taro.navigateTo({ url: `/user/order/refund/index?orderId=${order.orderId}&orderNo=${order.orderNo}` }); } catch (error) { console.error('更新订单状态失败:', error); Taro.showToast({ title: '操作失败,请重试', icon: 'none' }); } }; // 查看物流 (待收货状态) const viewLogistics = (order: ShopOrder) => { // 跳转到物流查询页面 Taro.navigateTo({ url: `/user/order/logistics/index?orderId=${order.orderId}&orderNo=${order.orderNo}&expressNo=${order.transactionId || ''}&expressCompany=SF` }); }; // 再次购买 (已完成状态) const buyAgain = (order: OrderWithGoods) => { console.log('再次购买:', order); const goodsId = order.orderGoodsList?.[0]?.goodsId if (!goodsId) { Taro.showToast({ title: '订单商品信息缺失', icon: 'none' }); return; } goTo(`/shop/orderConfirm/index?goodsId=${goodsId}`) // Taro.showToast({ // title: '再次购买功能开发中', // icon: 'none' // }); }; // 取消订单 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: OrderWithGoods) => { try { if (!order.orderId || !order.orderNo) { Taro.showToast({ title: '订单信息错误', icon: 'error' }); return; } if (payingOrderId === order.orderId) { return; } setPayingOrderId(order.orderId); // 检查订单是否已过期 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: '发起支付...'}); // 构建商品数据:优先使用列表已加载的商品信息;缺失时再补拉一次,避免goodsItems为空导致后端拒绝/再次支付失败 let orderGoods = order.orderGoodsList || []; if (!orderGoods.length) { try { orderGoods = (await listShopOrderGoods({orderId: order.orderId})) || []; } catch (e) { // 继续走下面的校验提示 console.error('补拉订单商品失败:', e); } } const goodsItems = orderGoods .filter(g => !!g.goodsId) .map(goods => ({ goodsId: goods.goodsId as number, quantity: goods.totalNum || 1, // 若后端按SKU计算价格/库存,补齐SKU/规格信息更安全 skuId: (goods as any).skuId, specInfo: (goods as any).spec || (goods as any).specInfo })); if (!goodsItems.length) { Taro.showToast({ title: '订单商品信息缺失,请稍后重试', icon: 'none' }); return; } // 对于已存在的订单,我们需要重新发起支付 // 构建支付请求数据,包含完整的商品信息 const paymentData = { orderId: order.orderId, orderNo: order.orderNo, goodsItems: goodsItems, addressId: order.addressId, payType: PaymentType.WECHAT, // 尽量携带原订单信息,避免后端重新计算/校验不一致(如使用了优惠券/自提等) couponId: order.couponId, deliveryType: order.deliveryType, selfTakeMerchantId: order.selfTakeMerchantId, comments: order.comments, title: order.title }; 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('微信支付参数不完整'); } // 调用微信支付 try { await Taro.requestPayment({ timeStamp: result.timeStamp, nonceStr: result.nonceStr, package: result.package, signType: (result.signType || 'MD5') as 'MD5' | 'HMAC-SHA256', paySign: result.paySign, }); } catch (payError: any) { const msg: string = payError?.errMsg || payError?.message || ''; if (msg.includes('cancel')) { // 用户主动取消,不当作“失败”强提示 Taro.showToast({ title: '已取消支付', icon: 'none' }); return; } throw payError; } // 支付成功 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 = '支付失败,请重试'; const rawMsg: string = error?.errMsg || error?.message || ''; if (rawMsg) { if (rawMsg.includes('cancel')) { errorMessage = '用户取消支付'; } else if (rawMsg.includes('余额不足')) { errorMessage = '账户余额不足'; } else { errorMessage = rawMsg; } } Taro.showToast({ title: errorMessage, icon: 'error' }); } finally { Taro.hideLoading(); setPayingOrderId(null); } }; useEffect(() => { void reload(true); // 首次加载、tab切换或搜索条件变化时重置页码 }, [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.orderGoodsList && item.orderGoodsList.length > 0 ? ( item.orderGoodsList.map((goods, goodsIndex) => ( {goods.goodsName} {goods.spec && 规格:{goods.spec}} 数量:{goods.totalNum} ¥{goods.price} )) ) : ( {item.title || '订单商品'} {item.totalNum}件商品 )} 实付金额:¥{item.payPrice} {/* 操作按钮 */} {!isReadOnly && ( {/* 待付款状态:显示取消订单和立即支付 */} {(!item.payStatus) && item.orderStatus !== 2 && ( )} {/* 待发货状态:显示申请退款 */} {item.payStatus && item.deliveryStatus === 10 && item.orderStatus !== 2 && item.orderStatus !== 4 && ( )} {/* 待收货状态:显示查看物流和确认收货 */} {item.deliveryStatus === 20 && (!item.riderId || !!item.sendEndTime) && item.orderStatus !== 2 && ( )} {/* 已完成状态:显示再次购买、评价商品、申请退款 */} {item.orderStatus === 1 && ( {/**/} )} {/* 退款/售后状态:显示查看进度和撤销申请 */} {(item.orderStatus === 4 || item.orderStatus === 7) && ( {/**/} )} {/* 退款成功状态:显示再次购买 */} {item.orderStatus === 6 && ( )} )} ) })} )} {/* 取消订单确认对话框 */} 确定要取消这个订单吗? {/* 确认收货确认对话框 */} 确定已经收到商品了吗?确认收货后订单将完成。 ) } export default OrderList