Files
xinlong-shop-taro/src/pages/order/list.tsx
赵忠林 fbaa5c32bc fix(tabBar): 修正“订单”页改为非tabBar页面的导航链接
- 将原来 switchTab 跳转“订单”页改为 navigateTo,以适配非 tabBar 页面
- 统一更新多个文件中关于订单页的跳转方式共计 6 处
- 更新 tabBar 配置,从包含“订单”页改为实际的四个 tabBar 页面
- 修正 isTabBarUrl 相关判断,去除不存在或非 tabBar 路径
- 调整多个 tabBar 判断列表,保证逻辑一致性与正确性
- 修改购物车页图片压缩宽度加入限制,优化资源加载
- 更新订单列表页注释,去除“tabBar 页”描述
- 备注遗留问题,部分积分页跳转仍用错误方式,待进一步修复
2026-07-15 23:42:27 +08:00

258 lines
8.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 React, { useState, useEffect, useCallback, useRef } from 'react'
import { View, Text, ScrollView } from '@tarojs/components'
import Taro from '@tarojs/taro'
import { pageShopOrder, updateShopOrder } from '@/api/shop/shopOrder'
import type { ShopOrder, ShopOrderParam } from '@/api/shop/shopOrder/model'
import { useUser } from '@/hooks/useUser'
import { OrderListStatus, OrderListTabText, OrderStatus } from '@/types/order'
import OrderCard from '@/components/common/OrderCard'
import EmptyState from '@/components/common/EmptyState'
import LoadMore from '@/components/common/LoadMore'
import { requireLogin } from '@/utils/login-guard'
definePageConfig({
navigationBarTitleText: '我的订单',
})
// Tab 配置(使用后端 statusFilter 字段)
const tabList = [
{ title: OrderListTabText[OrderListStatus.All], status: OrderListStatus.All },
{ title: OrderListTabText[OrderListStatus.Unpaid], status: OrderListStatus.Unpaid },
{ title: OrderListTabText[OrderListStatus.Unshipped], status: OrderListStatus.Unshipped },
{ title: OrderListTabText[OrderListStatus.Shipped], status: OrderListStatus.Shipped },
{ title: OrderListTabText[OrderListStatus.Completed], status: OrderListStatus.Completed },
]
interface TabState {
orders: ShopOrder[]
page: number
finished: boolean
loading: boolean
initialized: boolean
}
const OrderListPage: React.FC = () => {
const { user, isLoggedIn } = useUser()
const initTab = Taro.getCurrentInstance().router?.params?.tab
?? Taro.getStorageSync('order_tab')
// 读取后清除,避免影响下次进入
if (Taro.getStorageSync('order_tab') !== '') {
Taro.removeStorageSync('order_tab')
}
const [tabIndex, setTabIndex] = useState(Number(initTab) || 0)
// 每个 tab 独立维护状态
const [tabStates, setTabStates] = useState<TabState[]>(
tabList.map(() => ({
orders: [],
page: 1,
finished: false,
loading: false,
initialized: false,
}))
)
const currentState = tabStates[tabIndex]
// 用 ref 避免 loadOrders 闭包中捕获过期状态
const tabStatesRef = useRef(tabStates)
tabStatesRef.current = tabStates
// userId 也用 ref防止闭包过期导致登录后仍传 undefined
const userIdRef = useRef(user?.userId)
userIdRef.current = user?.userId
const updateTabState = useCallback(
(index: number, partial: Partial<TabState>) => {
setTabStates(prev => {
const next = [...prev]
next[index] = { ...next[index], ...partial }
return next
})
},
[]
)
const loadOrders = useCallback(
async (targetIndex: number, p: number) => {
// 没有当前登录用户 userId禁止加载防止看到别人的订单
const userId = userIdRef.current
if (!userId) return
const state = tabStatesRef.current[targetIndex]
if (state.loading) return
updateTabState(targetIndex, { loading: true })
try {
const params: ShopOrderParam = { page: p, limit: 10, userId }
const status = tabList[targetIndex]?.status
// 使用后端 statusFilter 字段进行筛选
// -1=全部, 0=待支付, 1=待发货, 3=待收货, 5=已完成
if (status !== undefined && status !== OrderListStatus.All) {
params.statusFilter = status
}
const res = await pageShopOrder(params)
const list = res?.list || []
updateTabState(targetIndex, {
orders: p === 1 ? list : [...state.orders, ...list],
page: p,
finished: list.length < 10,
loading: false,
initialized: true,
})
} catch {
updateTabState(targetIndex, { loading: false, initialized: true })
}
},
[updateTabState]
)
// 切换 tab 时加载数据
useEffect(() => {
if (!isLoggedIn) {
requireLogin({ action: 'viewOrder', redirect: '/pages/order/list', content: '登录后才能查看订单,是否前往登录?' })
return
}
// 已登录但 userId 还没拿到,等下次渲染再加载
if (!user?.userId) return
if (!tabStates[tabIndex].initialized) {
loadOrders(tabIndex, 1)
}
}, [tabIndex, isLoggedIn, user?.userId])
// 页面每次显示时刷新数据(覆盖从订单详情页返回等再次显示场景)
const isFirstShow = useRef(true)
Taro.useDidShow(() => {
// 首次显示由 useEffect 处理,跳过
if (isFirstShow.current) {
isFirstShow.current = false
return
}
const userId = userIdRef.current
if (!userId) return
// 标记所有 tab 为未初始化,切换时会重新加载
setTabStates(prev => prev.map(s => ({ ...s, initialized: false })))
// 立即刷新当前 tab
loadOrders(tabIndex, 1)
})
const handleLoadMore = () => {
if (!currentState.finished && !currentState.loading) {
loadOrders(tabIndex, currentState.page + 1)
}
}
/** 取消订单(未付款/待确认收款状态下允许客户取消) */
const handleCloseOrder = (order: ShopOrder) => {
Taro.showModal({
title: '确认取消订单',
content: '取消后订单将关闭,是否确认?',
confirmColor: '#ee0a24',
success: async (res) => {
if (res.confirm && order?.orderId) {
try {
await updateShopOrder({ ...order, orderStatus: OrderStatus.Cancelled })
Taro.showToast({ title: '订单已取消', icon: 'success' })
// 标记所有 tab 为未初始化,切换时会重新加载(确保「全部」等 tab 能看到最新状态)
setTabStates(prev => prev.map(s => ({ ...s, initialized: false })))
// 立即刷新当前 tab
loadOrders(tabIndex, 1)
} catch (err: any) {
Taro.showToast({ title: err?.message || '取消失败', icon: 'none' })
}
}
}
})
}
const handleTabChange = (index: number) => {
if (index !== tabIndex) {
setTabIndex(index)
}
}
if (!isLoggedIn) {
return null
}
return (
<View className='min-h-screen bg-gray-50 flex flex-col'>
{/* 自定义 Tabs 标题栏 */}
<View className='bg-white flex flex-row items-center border-b border-gray-100'>
{tabList.map((tab, index) => (
<View
key={tab.title}
className='flex-1 flex flex-col items-center justify-center py-3'
onClick={() => handleTabChange(index)}
>
<Text
className='text-sm font-medium'
style={{
color: index === tabIndex ? '#ee0a24' : '#666',
}}
>
{tab.title}
</Text>
{index === tabIndex && (
<View
className='mt-1 rounded-full'
style={{
width: '20px',
height: '3px',
backgroundColor: '#ee0a24',
}}
/>
)}
</View>
))}
</View>
{/* 内容区域 - 每个 tab 独立渲染 */}
<View className='flex-1 relative'>
{tabList.map((tab, idx) => (
<View
key={tab.title}
className='absolute inset-0'
style={{
display: idx === tabIndex ? 'flex' : 'none',
flexDirection: 'column',
}}
>
<ScrollView
scrollY
className='flex-1'
onScrollToLower={handleLoadMore}
lowerThreshold={100}
>
<View className='p-3'>
{tabStates[idx].orders.length > 0 ? (
tabStates[idx].orders.map(order => (
<OrderCard
key={order.orderId}
order={order}
onCloseOrder={handleCloseOrder}
/>
))
) : tabStates[idx].initialized && !tabStates[idx].loading ? (
<EmptyState text='暂无订单' />
) : null}
<LoadMore
loading={tabStates[idx].loading}
finished={tabStates[idx].finished}
/>
</View>
</ScrollView>
</View>
))}
</View>
</View>
)
}
export default OrderListPage