Files
xin-procurement-weapp/src/pages/order-list/index.tsx
T
2026-08-14 20:21:50 +08:00

261 lines
9.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useCallback, useRef, useState } from 'react'
import Taro, { useDidShow, useReachBottom, useRouter } from '@tarojs/taro'
import { View, Text, ScrollView } from '@tarojs/components'
import { Empty, Popup } from '@antmjs/vantui'
import useAuthStore from '@/stores/auth/useAuthStore'
import { cancelOrderApi, getOrderDetailApi, getOrderListApi } from '@/services/order'
import { ORDER_STATUS_FILTERS, ORDER_STATUS_TEXT } from '@/types/order'
import type { OrderDetail, OrderListItem, OrderStatus } from '@/types/order'
import './index.less'
const PAGE_SIZE = 10
/**
* 订单列表页
* 入口:/pages/order-list/index?status=0|1|2|3|4|9|all
* 状态文案一律使用接口返回的 status_name(详情接口无该字段时沿用列表行)
*/
export default function OrderListPage() {
const router = useRouter()
const token = useAuthStore(s => s.token)
/** 当前状态筛选(undefined = 全部) */
const [status, setStatus] = useState<OrderStatus | undefined>(undefined)
const [orders, setOrders] = useState<OrderListItem[]>([])
const [page, setPage] = useState(1)
const [loading, setLoading] = useState(false)
const [finished, setFinished] = useState(false)
const loadingRef = useRef(false)
/** 订单详情弹层(statusName 沿用列表行,详情接口不返回 status_name */
const [showDetail, setShowDetail] = useState(false)
const [orderDetail, setOrderDetail] = useState<OrderDetail | null>(null)
const [detailStatusName, setDetailStatusName] = useState('')
const loggedIn = !!token
/** 解析路由参数中的 status'all' / 数字 / 缺省 → undefined */
const parseStatus = useCallback((raw?: string): OrderStatus | undefined => {
if (!raw || raw === 'all' || raw === '') return undefined
const num = Number(raw)
return Number.isNaN(num) ? undefined : (num as OrderStatus)
}, [])
/** 拉取订单列表 */
const loadOrders = useCallback(
async (pageNum: number, reset: boolean, statusParam?: OrderStatus) => {
if (!loggedIn || loadingRef.current) return
loadingRef.current = true
setLoading(true)
try {
const res = await getOrderListApi({
status: statusParam,
page: pageNum,
pageSize: PAGE_SIZE,
})
const { data, total } = res.data
setOrders(prev => (reset ? data : [...prev, ...data]))
setPage(pageNum)
setFinished(pageNum * PAGE_SIZE >= total)
} catch {
// 错误已由 request 层 toast
} finally {
loadingRef.current = false
setLoading(false)
}
},
[loggedIn],
)
useDidShow(() => {
const statusParam = parseStatus(router.params.status)
setStatus(statusParam)
loadOrders(1, true, statusParam)
})
useReachBottom(() => {
if (!finished && !loadingRef.current && loggedIn) {
loadOrders(page + 1, false, status)
}
})
/** 切换状态筛选 */
const handleStatusTap = useCallback(
(value?: OrderStatus) => {
setStatus(value)
setFinished(false)
loadOrders(1, true, value)
},
[loadOrders],
)
/** 查看订单详情 */
const handleOrderTap = useCallback(async (order: OrderListItem) => {
try {
const res = await getOrderDetailApi(order.id)
setOrderDetail(res.data)
setDetailStatusName(order.status_name)
setShowDetail(true)
} catch {
// 错误已由 request 层 toast
}
}, [])
/** 取消订单(仅待接单可取消,以 can_cancel 为准) */
const handleCancelOrder = useCallback(
(order: OrderListItem) => {
Taro.showModal({
title: '取消订单',
content: `确定取消订单 ${order.order_no} 吗?`,
confirmColor: '#ee0a24',
success: res => {
if (res.confirm) {
cancelOrderApi(order.id)
.then(() => {
Taro.showToast({ title: '订单已取消', icon: 'success' })
loadOrders(1, true, status)
})
.catch(() => {})
}
},
})
},
[loadOrders, status],
)
/** 跳转关联账单详情 */
const handleBillTap = useCallback((billId: number) => {
Taro.navigateTo({ url: `/pages/bill-detail/index?id=${billId}` })
}, [])
const goLogin = useCallback(() => {
Taro.navigateTo({ url: '/pages/login/index' })
}, [])
return (
<View className='order-list-page'>
{/* ========== 状态筛选 ========== */}
<ScrollView scrollX className='status-scroll'>
{ORDER_STATUS_FILTERS.map(item => (
<View
key={item.label}
className={`status-chip ${status === item.value ? 'active' : ''}`}
onClick={() => handleStatusTap(item.value)}
>
<Text>{item.label}</Text>
</View>
))}
</ScrollView>
{/* ========== 订单列表 ========== */}
{!loggedIn ? (
<Empty description='登录后查看订单' className='order-empty'>
<View className='order-empty__btn' onClick={goLogin}>去登录</View>
</Empty>
) : orders.length === 0 ? (
loading ? (
<View className='order-loading'><Text>加载中...</Text></View>
) : (
<Empty description='暂无订单' className='order-empty' />
)
) : (
orders.map(order => (
<View key={order.id} className='order-item' onClick={() => handleOrderTap(order)}>
<View className='order-item__header'>
<Text className='order-item__no'>{order.order_no}</Text>
<Text className={`order-item__status order-item__status--${order.status}`}>
{order.status_name}
</Text>
</View>
{/* 商品预览(仅前 3 条,完整明细见详情) */}
<Text className='order-item__preview'>
{order.items.map(i => `${i.product_name}×${i.quantity}`).join('、')}
{order.item_count > 3 ? ` 等${order.item_count}种` : ''}
</Text>
<View className='order-item__body'>
<Text className='order-item__date'>订货日期 {order.order_date}</Text>
<View className='order-item__amounts'>
<Text className='order-item__qty'> {order.total_quantity} </Text>
<Text className='order-item__amount'>{order.total_amount}</Text>
</View>
</View>
{order.remark && <Text className='order-item__remark'>备注:{order.remark}</Text>}
{(order.can_cancel || order.bill_id > 0) && (
<View className='order-item__footer'>
{order.bill_id > 0 && (
<View
className='order-item__bill'
onClick={e => {
e.stopPropagation()
handleBillTap(order.bill_id)
}}
>
查看账单
</View>
)}
{order.can_cancel && (
<View
className='order-item__cancel'
onClick={e => {
e.stopPropagation()
handleCancelOrder(order)
}}
>
取消订单
</View>
)}
</View>
)}
</View>
))
)}
{loggedIn && finished && orders.length > 0 && (
<View className='order-loading'><Text>没有更多了</Text></View>
)}
{/* ========== 订单详情弹层 ========== */}
<Popup
show={showDetail}
position='bottom'
round
closeable
closeOnClickOverlay
safeAreaInsetBottom
onClose={() => setShowDetail(false)}
>
{orderDetail && (
<View className='detail-popup'>
<Text className='detail-popup__title'>{orderDetail.order_no}</Text>
<View className='detail-popup__meta'>
<Text>{orderDetail.order_date} 下单 {orderDetail.created_at}</Text>
<Text className='detail-popup__status'>
{detailStatusName || ORDER_STATUS_TEXT[orderDetail.status] || ''}
</Text>
</View>
<ScrollView scrollY className='detail-popup__list'>
{(orderDetail.items ?? []).map(item => (
<View key={item.id} className='detail-popup__item'>
<View className='detail-popup__item-info'>
<Text className='detail-popup__item-name'>{item.product_name}</Text>
<Text className='detail-popup__item-spec'>
{item.product_spec ? `${item.product_spec} ` : ''}{item.price}/{item.unit} × {item.quantity}
</Text>
</View>
<Text className='detail-popup__item-amount'>{item.amount}</Text>
</View>
))}
</ScrollView>
{orderDetail.remark && (
<Text className='detail-popup__remark'>备注:{orderDetail.remark}</Text>
)}
<View className='detail-popup__footer'>
<Text className='detail-popup__total'>合计 {orderDetail.total_amount}</Text>
</View>
</View>
)}
</Popup>
</View>
)
}