- 门店订单修改金额时,修改原因从追加 comments 改为保存 merchantRemarks 字段 - ShopOrder 和 OrderCreateRequest 接口新增 buyerRemarks 和 merchantRemarks 字段,实现订单备注字段分离 - 结算页由 comments 注入变更为 buyerRemarks,门店订单列表新增买家备注展示块 - 修复线下付款订单状态显示:统一线下付款(payType=9)与货到付款(payType=8)的待发货状态显示 - 取消按钮逻辑优化,已付款订单不显示取消按钮,文案由"关闭订单"改为"取消订单" - 订单列表和详情页弹窗标题及提示文案更新,确保状态描述准确,提升用户体验
258 lines
8.2 KiB
TypeScript
258 lines
8.2 KiB
TypeScript
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])
|
||
|
||
// 页面每次显示时刷新数据(tabBar 页 switchTab 不会重新挂载组件)
|
||
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
|