init
This commit is contained in:
@@ -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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user