This commit is contained in:
liu
2026-08-07 08:45:25 +08:00
parent 39b789154c
commit 0203c84357
19 changed files with 1652 additions and 1085 deletions
+234
View File
@@ -0,0 +1,234 @@
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_NAV_ITEMS, ORDER_STATUS_MAP } from '@/types/order'
import type { Order, OrderStatus } from '@/types/order'
import './index.less'
const PAGE_SIZE = 10
/**
* 订单列表页(框架)
* 入口:/pages/order-list/index?status=0|1|2|3|9|all
* status 与 ORDER_NAV_ITEMS 映射(业务语言 → 后端枚举),缺省为全部
*/
export default function OrderListPage() {
const router = useRouter()
const token = useAuthStore(s => s.token)
/** 当前状态筛选(undefined = 全部) */
const [status, setStatus] = useState<number | undefined>(undefined)
const [orders, setOrders] = useState<Order[]>([])
const [page, setPage] = useState(1)
const [total, setTotal] = useState(0)
const [loading, setLoading] = useState(false)
const [finished, setFinished] = useState(false)
const loadingRef = useRef(false)
/** 订单详情弹层 */
const [showDetail, setShowDetail] = useState(false)
const [orderDetail, setOrderDetail] = useState<Order | null>(null)
const loggedIn = !!token
/** 解析路由参数中的 status'all' / 数字 / 缺省 → undefined */
const parseStatus = useCallback((raw?: string): number | undefined => {
if (!raw || raw === 'all' || raw === '') return undefined
const num = Number(raw)
return Number.isNaN(num) ? undefined : num
}, [])
/** 拉取订单列表 */
const loadOrders = useCallback(
async (pageNum: number, reset: boolean, statusParam?: number) => {
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: totalCount } = res.data
setOrders(prev => (reset ? data : [...prev, ...data]))
setTotal(totalCount)
setPage(pageNum)
setFinished(pageNum * PAGE_SIZE >= totalCount)
} 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?: number) => {
setStatus(value)
setFinished(false)
loadOrders(1, true, value)
},
[loadOrders],
)
/** 查看订单详情 */
const handleOrderTap = useCallback(async (order: Order) => {
try {
const res = await getOrderDetailApi(order.id)
setOrderDetail(res.data)
setShowDetail(true)
} catch {
// 错误已由 request 层 toast
}
}, [])
/** 取消订单(仅待汇总可取消) */
const handleCancelOrder = useCallback(
(order: Order) => {
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 goLogin = useCallback(() => {
Taro.navigateTo({ url: '/pages/login/index' })
}, [])
return (
<View className='order-list-page'>
{/* ========== 状态筛选 ========== */}
<ScrollView scrollX className='status-scroll'>
<View
className={`status-chip ${status === undefined ? 'active' : ''}`}
onClick={() => handleStatusTap(undefined)}
>
<Text></Text>
</View>
{ORDER_NAV_ITEMS.map(item => (
<View
key={item.key}
className={`status-chip ${status === item.status ? 'active' : ''}`}
onClick={() => handleStatusTap(item.status)}
>
<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_STATUS_MAP[order.status]}</Text>
</View>
<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.status === 0 && (
<View
className='order-item__cancel'
onClick={e => {
e.stopPropagation()
handleCancelOrder(order)
}}
>
</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}</Text>
<Text className='detail-popup__status'>{ORDER_STATUS_MAP[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.price} × {item.quantity}
</Text>
</View>
<Text className='detail-popup__item-amount'>{item.amount}</Text>
</View>
))}
</ScrollView>
<View className='detail-popup__footer'>
<Text className='detail-popup__total'> {orderDetail.total_amount}</Text>
</View>
</View>
)}
</Popup>
</View>
)
}