import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import Taro, { useDidShow } from '@tarojs/taro' import { View, Text } from '@tarojs/components' import { Tabs, TabPane, Cell, Space, Button, Dialog, Radio, RadioGroup, Image, Empty, InfiniteLoading, PullToRefresh, Loading } from '@nutui/nutui-react-taro' import dayjs from 'dayjs' import { pageGltTicketOrder, updateGltTicketOrder } from '@/api/glt/gltTicketOrder' import type { GltTicketOrder, GltTicketOrderParam } from '@/api/glt/gltTicketOrder/model' import { uploadFile } from '@/api/system/file' const PAGE_SIZE = 10 type DeliverConfirmMode = 'photoComplete' | 'waitCustomerConfirm' export default function TicketOrdersPage() { const riderId = useMemo(() => { const raw = Taro.getStorageSync('UserId') const id = Number(raw) return Number.isFinite(id) && id > 0 ? id : undefined }, []) const pageRef = useRef(1) const listRef = useRef([]) const [tabIndex, setTabIndex] = useState(0) const [list, setList] = useState([]) const [hasMore, setHasMore] = useState(true) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [deliverDialogVisible, setDeliverDialogVisible] = useState(false) const [deliverSubmitting, setDeliverSubmitting] = useState(false) const [deliverOrder, setDeliverOrder] = useState(null) const [deliverImg, setDeliverImg] = useState(undefined) const [deliverConfirmMode, setDeliverConfirmMode] = useState('photoComplete') const riderTabs = useMemo( () => [ { index: 0, title: '全部' }, { index: 1, title: '待配送', deliveryStatus: 10 }, { index: 2, title: '配送中', deliveryStatus: 20 }, { index: 3, title: '待确认', deliveryStatus: 30 }, { index: 4, title: '已完成', deliveryStatus: 40 } ], [] ) const getOrderStatusText = (order: GltTicketOrder) => { if (order.status === 1) return '已冻结' const deliveryStatus = order.deliveryStatus if (deliveryStatus === 40) return '已完成' if (deliveryStatus === 30) return '待客户确认' if (deliveryStatus === 20) return '配送中' if (deliveryStatus === 10) return '待配送' // 兼容:如果后端暂未下发 deliveryStatus,就用时间字段推断 if (order.receiveConfirmTime) return '已完成' if (order.sendEndTime) return '待客户确认' if (order.sendStartTime) return '配送中' if (order.riderId) return '待配送' return '待派单' } const getOrderStatusColor = (order: GltTicketOrder) => { const text = getOrderStatusText(order) if (text === '已完成') return 'text-green-600' if (text === '待客户确认') return 'text-purple-600' if (text === '配送中') return 'text-blue-600' if (text === '待配送') return 'text-amber-600' if (text === '已冻结') return 'text-orange-600' return 'text-gray-500' } const canStartDeliver = (order: GltTicketOrder) => { if (!order.id) return false if (order.status === 1) return false if (!riderId || order.riderId !== riderId) return false if (order.deliveryStatus && order.deliveryStatus !== 10) return false return !order.sendStartTime && !order.sendEndTime } const canConfirmDelivered = (order: GltTicketOrder) => { if (!order.id) return false if (order.status === 1) return false if (!riderId || order.riderId !== riderId) return false if (order.receiveConfirmTime) return false if (order.deliveryStatus === 40) return false if (order.sendEndTime) return false // 只允许在“配送中”阶段确认送达 if (typeof order.deliveryStatus === 'number') return order.deliveryStatus === 20 return !!order.sendStartTime } const canCompleteByPhoto = (order: GltTicketOrder) => { if (!order.id) return false if (order.status === 1) return false if (!riderId || order.riderId !== riderId) return false if (order.receiveConfirmTime) return false if (order.deliveryStatus === 40) return false // 已送达但未完成:允许补传照片并直接完成 return !!order.sendEndTime } const filterByTab = useCallback( (orders: GltTicketOrder[]) => { if (tabIndex === 0) return orders const current = riderTabs.find(t => t.index === tabIndex) const status = current?.deliveryStatus if (!status) return orders // 如果后端已实现 deliveryStatus 筛选,这里基本不会再过滤;否则用兼容逻辑兜底。 return orders.filter(o => { const ds = o.deliveryStatus if (typeof ds === 'number') return ds === status if (status === 10) return !!o.riderId && !o.sendStartTime && !o.sendEndTime if (status === 20) return !!o.sendStartTime && !o.sendEndTime if (status === 30) return !!o.sendEndTime && !o.receiveConfirmTime if (status === 40) return !!o.receiveConfirmTime return true }) }, [riderTabs, tabIndex] ) const reload = useCallback( async (resetPage = false) => { if (!riderId) return if (loading) return setLoading(true) setError(null) const currentPage = resetPage ? 1 : pageRef.current const currentTab = riderTabs.find(t => t.index === tabIndex) const params: GltTicketOrderParam = { page: currentPage, limit: PAGE_SIZE, riderId, deliveryStatus: currentTab?.deliveryStatus } try { const res = await pageGltTicketOrder(params as any) const incomingAll = (res?.list || []) as GltTicketOrder[] // 兼容:后端若暂未实现 riderId 过滤,前端兜底过滤掉非本人的订单 const incoming = incomingAll.filter(o => o?.deleted !== 1 && o?.riderId === riderId) const prev = resetPage ? [] : listRef.current const next = resetPage ? incoming : prev.concat(incoming) listRef.current = next setList(next) const total = typeof res?.count === 'number' ? res.count : undefined const filteredOut = incomingAll.length - incoming.length if (typeof total === 'number' && filteredOut === 0) { setHasMore(next.length < total) } else { setHasMore(incomingAll.length >= PAGE_SIZE) } pageRef.current = currentPage + 1 } catch (e) { console.error('加载配送订单失败:', e) setError('加载失败,请重试') setHasMore(false) } finally { setLoading(false) } }, [loading, riderId, riderTabs, tabIndex] ) const reloadMore = useCallback(async () => { if (loading || !hasMore) return await reload(false) }, [hasMore, loading, reload]) const openDeliverDialog = (order: GltTicketOrder, opts?: { mode?: DeliverConfirmMode }) => { setDeliverOrder(order) setDeliverImg(order.sendEndImg) setDeliverConfirmMode(opts?.mode || (order.sendEndImg ? 'photoComplete' : 'waitCustomerConfirm')) setDeliverDialogVisible(true) } const handleChooseDeliverImg = async () => { try { const file = await uploadFile() setDeliverImg(file?.url) } catch (e) { console.error('上传送达照片失败:', e) Taro.showToast({ title: '上传失败,请重试', icon: 'none' }) } } const handleStartDeliver = async (order: GltTicketOrder) => { if (!order?.id) return if (!canStartDeliver(order)) return try { await updateGltTicketOrder({ id: order.id, deliveryStatus: 20, sendStartTime: dayjs().format('YYYY-MM-DD HH:mm:ss') }) Taro.showToast({ title: '已开始配送', icon: 'success' }) pageRef.current = 1 await reload(true) } catch (e) { console.error('开始配送失败:', e) Taro.showToast({ title: '开始配送失败', icon: 'none' }) } } const handleConfirmDelivered = async () => { if (!deliverOrder?.id) return if (deliverSubmitting) return if (deliverConfirmMode === 'photoComplete' && !deliverImg) { Taro.showToast({ title: '请先拍照/上传送达照片', icon: 'none' }) return } setDeliverSubmitting(true) try { const now = dayjs().format('YYYY-MM-DD HH:mm:ss') // 送达时间:首次“确认送达”写入;补传照片时不要覆盖原送达时间 const deliveredAt = deliverOrder.sendEndTime || now // 说明: // - waitCustomerConfirm:只标记“已送达”,进入待客户确认(客户点击确认收货后完成) // - photoComplete:拍照留档后可直接完成(由后端策略决定是否允许) const payload: GltTicketOrder = deliverConfirmMode === 'photoComplete' ? { id: deliverOrder.id, deliveryStatus: 40, sendEndTime: deliveredAt, sendEndImg: deliverImg, receiveConfirmTime: now, receiveConfirmType: 20 } : { id: deliverOrder.id, deliveryStatus: 30, sendEndTime: deliveredAt, sendEndImg: deliverImg } await updateGltTicketOrder(payload) Taro.showToast({ title: '已确认送达', icon: 'success' }) setDeliverDialogVisible(false) setDeliverOrder(null) setDeliverImg(undefined) setDeliverConfirmMode('photoComplete') pageRef.current = 1 await reload(true) } catch (e) { console.error('确认送达失败:', e) Taro.showToast({ title: '确认送达失败', icon: 'none' }) } finally { setDeliverSubmitting(false) } } useEffect(() => { listRef.current = list }, [list]) useDidShow(() => { pageRef.current = 1 listRef.current = [] setList([]) setHasMore(true) void reload(true) // eslint-disable-next-line react-hooks/exhaustive-deps }) useEffect(() => { pageRef.current = 1 listRef.current = [] setList([]) setHasMore(true) void reload(true) // eslint-disable-next-line react-hooks/exhaustive-deps }, [tabIndex, riderId]) if (!riderId) { return ( 请先登录 ) } const displayList = filterByTab(list) return ( setTabIndex(Number(paneKey))} align="left"> {riderTabs.map(t => ( ))} { pageRef.current = 1 listRef.current = [] setList([]) setHasMore(true) await reload(true) }} > {error ? ( {error} ) : ( 加载中... } loadMoreText={ displayList.length === 0 ? ( ) : ( 没有更多了 ) } > {displayList.map(o => { const qty = Number(o.totalNum || 0) const timeText = o.createTime ? dayjs(o.createTime).format('YYYY-MM-DD HH:mm') : '-' const addr = o.address || (o.addressId ? `地址ID:${o.addressId}` : '-') const remark = o.buyerRemarks || o.comments || '' const ticketNo = o.userTicketId || '-' const flow1Done = !!o.riderId const flow2Done = !!o.sendStartTime || (typeof o.deliveryStatus === 'number' && o.deliveryStatus >= 20) const flow3Done = !!o.sendEndTime || (typeof o.deliveryStatus === 'number' && o.deliveryStatus >= 30) const flow4Done = !!o.receiveConfirmTime || o.deliveryStatus === 40 const phoneToCall = o.phone const storePhone = o.storePhone const pickupName = o.warehouseName || o.storeName const pickupAddr = o.warehouseAddress || o.storeAddress return ( {`订单#${o.id}`} {getOrderStatusText(o)} 下单时间:{timeText} 票号:{ticketNo} 收货地址: {addr} 客户: {o.nickname || '-'} {o.phone ? `(${o.phone})` : ''} 取货点: {pickupName || '-'} {pickupAddr ? ( 取货地址: {pickupAddr} ) : null} 预约配送: {o.sendTime ? dayjs(o.sendTime).format('YYYY-MM-DD HH:mm') : '-'} 数量: {qty || '-'} 门店: {o.storeName || '-'} {o.storePhone ? ( 门店电话: {o.storePhone} ) : null} {remark ? ( 备注: {remark} ) : null} {o.sendStartTime ? ( 开始配送: {dayjs(o.sendStartTime).format('YYYY-MM-DD HH:mm')} ) : null} {o.sendEndTime ? ( 送达时间: {dayjs(o.sendEndTime).format('YYYY-MM-DD HH:mm')} ) : null} {o.receiveConfirmTime ? ( 确认收货: {dayjs(o.receiveConfirmTime).format('YYYY-MM-DD HH:mm')} ) : null} {o.sendEndImg ? ( 送达照片: ) : null} {/* 配送流程 */} 流程: 1 派单 {'>'} 2 配送中 {'>'} 3 送达留档 {'>'} 4 完成 {!!phoneToCall && ( )} {!!addr && addr !== '-' && ( )} {!!storePhone && ( )} {canStartDeliver(o) && ( )} {canConfirmDelivered(o) && ( )} {canCompleteByPhoto(o) && ( )} ) })} )} { if (deliverSubmitting) return setDeliverDialogVisible(false) setDeliverOrder(null) setDeliverImg(undefined) setDeliverConfirmMode('photoComplete') }} > 到达收货点后,可选择“拍照留档直接完成”或“等待客户确认收货”。 setDeliverConfirmMode(v as DeliverConfirmMode)}> 拍照留档(直接完成) 客户确认收货(可不拍照) {deliverImg && ( )} 说明:如选择“客户确认收货”,订单进入“待客户确认”;客户在用户端确认收货或超时自动确认(需后端支持)。 ) }