| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- import { View, ScrollView, Text } from '@tarojs/components'
- import { useInfiniteQuery } from '@tanstack/react-query'
- import { useState } from 'react'
- import Taro from '@tarojs/taro'
- import { orderClient } from '@/api'
- import { InferResponseType } from 'hono'
- import { Navbar } from '@/components/ui/navbar'
- import { Card } from '@/components/ui/card'
- import { Button } from '@/components/ui/button'
- import { useAuth } from '@/utils/auth'
- type OrderResponse = InferResponseType<typeof orderClient.$get, 200>
- type Order = OrderResponse['data'][0]
- // 订单状态映射
- const orderStatusMap = {
- 0: { text: '待发货', color: 'text-orange-500' },
- 1: { text: '已发货', color: 'text-blue-500' },
- 2: { text: '已完成', color: 'text-green-500' },
- 3: { text: '已退货', color: 'text-red-500' }
- }
- // 支付状态映射
- const payStatusMap = {
- 0: { text: '未支付', color: 'text-red-500' },
- 1: { text: '支付中', color: 'text-yellow-500' },
- 2: { text: '已支付', color: 'text-green-500' },
- 3: { text: '已退款', color: 'text-gray-500' },
- 4: { text: '支付失败', color: 'text-red-500' },
- 5: { text: '已关闭', color: 'text-gray-500' }
- }
- export default function OrderListPage() {
- const { user } = useAuth()
- const [activeTab, setActiveTab] = useState('all')
- const {
- data,
- isLoading,
- isFetchingNextPage,
- fetchNextPage,
- hasNextPage,
- refetch
- } = useInfiniteQuery({
- queryKey: ['orders', user?.id, activeTab],
- queryFn: async ({ pageParam = 1 }) => {
- let filters: any = { userId: user?.id }
-
- // 根据标签筛选
- switch (activeTab) {
- case 'unpaid':
- filters.payState = 0
- break
- case 'unshipped':
- filters.payState = 2
- filters.state = 0
- break
- case 'shipped':
- filters.state = 1
- break
- case 'completed':
- filters.state = 2
- break
- }
- const response = await orderClient.$get({
- query: {
- page: pageParam,
- pageSize: 10,
- filters: JSON.stringify(filters),
- order: JSON.stringify({ createdAt: 'DESC' })
- }
- })
- if (response.status !== 200) {
- throw new Error('获取订单失败')
- }
- return response.json()
- },
- enabled: !!user?.id,
- getNextPageParam: (lastPage) => {
- const { pagination } = lastPage
- const totalPages = Math.ceil(pagination.total / pagination.pageSize)
- return pagination.current < totalPages ? pagination.current + 1 : undefined
- },
- staleTime: 5 * 60 * 1000,
- initialPageParam: 1,
- })
- // 合并所有分页数据
- const allOrders = data?.pages.flatMap(page => page.data) || []
- // 触底加载更多
- const handleScrollToLower = () => {
- if (hasNextPage && !isFetchingNextPage) {
- fetchNextPage()
- }
- }
- // 下拉刷新
- const onPullDownRefresh = () => {
- refetch().finally(() => {
- Taro.stopPullDownRefresh()
- })
- }
- // 查看订单详情
- const handleOrderDetail = (order: Order) => {
- Taro.navigateTo({
- url: `/pages/order-detail/index?id=${order.id}`
- })
- }
- // 解析商品详情
- const parseGoodsDetail = (goodsDetail: string | null) => {
- try {
- return goodsDetail ? JSON.parse(goodsDetail) : []
- } catch {
- return []
- }
- }
- // 计算订单商品数量
- const getOrderItemCount = (order: Order) => {
- const goods = parseGoodsDetail(order.goodsDetail)
- return goods.reduce((sum: number, item: any) => sum + (item.num || 0), 0)
- }
- const tabs = [
- { key: 'all', label: '全部' },
- { key: 'unpaid', label: '待付款' },
- { key: 'unshipped', label: '待发货' },
- { key: 'shipped', label: '待收货' },
- { key: 'completed', label: '已完成' }
- ]
- return (
- <View className="min-h-screen bg-gray-50">
- <Navbar
- title="我的订单"
- leftIcon="i-heroicons-chevron-left-20-solid"
- onClickLeft={() => Taro.navigateBack()}
- />
-
- <ScrollView
- className="h-screen pt-12"
- scrollY
- onScrollToLower={handleScrollToLower}
- refresherEnabled
- refresherTriggered={false}
- onRefresherRefresh={onPullDownRefresh}
- >
- {/* 标签栏 */}
- <View className="bg-white mb-4">
- <View className="flex">
- {tabs.map((tab) => (
- <View
- key={tab.key}
- className={`flex-1 py-3 text-center ${
- activeTab === tab.key
- ? 'text-blue-500 border-b-2 border-blue-500'
- : 'text-gray-600'
- }`}
- onClick={() => setActiveTab(tab.key)}
- >
- {tab.label}
- </View>
- ))}
- </View>
- </View>
- <View className="px-4">
- {isLoading ? (
- <View className="flex justify-center py-10">
- <View className="i-heroicons-arrow-path-20-solid animate-spin w-8 h-8 text-blue-500" />
- </View>
- ) : (
- <>
- {allOrders.map((order) => {
- const goods = parseGoodsDetail(order.goodsDetail)
- const totalQuantity = getOrderItemCount(order)
-
- return (
- <Card key={order.id} className="mb-4">
- <View className="p-4">
- {/* 订单头部 */}
- <View className="flex justify-between items-center mb-3 pb-3 border-b border-gray-100">
- <Text className="text-sm text-gray-600">
- 订单号: {order.orderNo}
- </Text>
- <Text className={`text-sm font-medium ${payStatusMap[order.payState as keyof typeof payStatusMap].color}`}>
- {payStatusMap[order.payState as keyof typeof payStatusMap].text}
- </Text>
- </View>
- {/* 商品列表 */}
- <View className="mb-3">
- {goods.slice(0, 3).map((item: any, index: number) => (
- <View key={index} className="flex items-center py-2">
- <img
- src={item.image || ''}
- className="w-16 h-16 rounded-lg mr-3"
- mode="aspectFill"
- />
- <View className="flex-1">
- <Text className="text-sm font-medium line-clamp-2">
- {item.name}
- </Text>
- <Text className="text-sm text-gray-500">
- ¥{item.price.toFixed(2)} × {item.num}
- </Text>
- </View>
- </View>
- ))}
-
- {goods.length > 3 && (
- <Text className="text-sm text-gray-500 text-center mt-2">
- 共 {totalQuantity} 件商品
- </Text>
- )}
- </View>
- {/* 订单信息 */}
- <View className="border-t border-gray-100 pt-3">
- <View className="flex justify-between items-center mb-3">
- <Text className="text-sm text-gray-600">
- 实付款: ¥{order.payAmount.toFixed(2)}
- </Text>
- <Text className={`text-sm font-medium ${orderStatusMap[order.state as keyof typeof orderStatusMap].color}`}>
- {orderStatusMap[order.state as keyof typeof orderStatusMap].text}
- </Text>
- </View>
- {/* 操作按钮 */}
- <View className="flex justify-end space-x-2">
- <Button
- size="sm"
- variant="outline"
- onClick={() => handleOrderDetail(order)}
- >
- 查看详情
- </Button>
-
- {order.payState === 0 && (
- <Button
- size="sm"
- variant="primary"
- onClick={() => {
- // 跳转到支付页面
- Taro.navigateTo({
- url: `/pages/payment/index?orderId=${order.id}`
- })
- }}
- >
- 去支付
- </Button>
- )}
-
- {order.state === 2 && (
- <Button
- size="sm"
- variant="outline"
- onClick={() => {
- // 申请退款
- Taro.showModal({
- title: '确认收货',
- content: '确认已收到商品吗?',
- success: (res) => {
- if (res.confirm) {
- // 这里可以调用确认收货的API
- Taro.showToast({
- title: '已确认收货',
- icon: 'success'
- })
- }
- }
- })
- }}
- >
- 确认收货
- </Button>
- )}
- </View>
- </View>
- </View>
- </Card>
- )
- })}
-
- {isFetchingNextPage && (
- <View className="flex justify-center py-4">
- <View className="i-heroicons-arrow-path-20-solid animate-spin w-6 h-6 text-blue-500" />
- <Text className="ml-2 text-sm text-gray-500">加载更多...</Text>
- </View>
- )}
-
- {!hasNextPage && allOrders.length > 0 && (
- <View className="text-center py-4 text-sm text-gray-400">
- 没有更多了
- </View>
- )}
-
- {!isLoading && allOrders.length === 0 && (
- <View className="flex flex-col items-center py-20">
- <View className="i-heroicons-clipboard-document-list-20-solid w-16 h-16 text-gray-300 mb-4" />
- <Text className="text-gray-500 mb-4">暂无订单</Text>
- <Button
- onClick={() => Taro.navigateTo({ url: '/pages/goods-list/index' })}
- >
- 去购物
- </Button>
- </View>
- )}
- </>
- )}
- </View>
- </ScrollView>
- </View>
- )
- }
|