Browse Source

refactor(user): 优化用户订单相关功能

- 移除 coupon API 中的 SERVER_API_URL,直接使用相对路径
- 优化 order 页面的搜索和重置逻辑- 更新 OrderList 组件,支持空订单时显示 Empty 组件- 调整 UserCard 中的用户统计数据加载逻辑
- 修改 UserOrder 组件中的订单状态文本和链接
master
科技小王子 2 weeks ago
parent
commit
1802a27234
  1. 11
      src/api/user/coupon/index.ts
  2. 2
      src/pages/user/components/UserCard.tsx
  3. 4
      src/pages/user/components/UserOrder.tsx
  4. 220
      src/user/order/components/OrderList.tsx
  5. 64
      src/user/order/order.tsx

11
src/api/user/coupon/index.ts

@ -1,14 +1,13 @@
import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api/index';
import type { UserCoupon, UserCouponParam } from './model';
import {SERVER_API_URL} from "@/utils/server";
/**
*
*/
export async function pageUserCoupon(params: UserCouponParam) {
const res = await request.get<ApiResult<PageResult<UserCoupon>>>(
SERVER_API_URL + '/sys/user-coupon/page',
'/sys/user-coupon/page',
params
);
if (res.code === 0) {
@ -22,7 +21,7 @@ export async function pageUserCoupon(params: UserCouponParam) {
*/
export async function listUserCoupon(params?: UserCouponParam) {
const res = await request.get<ApiResult<UserCoupon[]>>(
SERVER_API_URL + '/sys/user-coupon',
'/sys/user-coupon',
params
);
if (res.code === 0 && res.data) {
@ -41,7 +40,7 @@ export async function getUserCouponCount(userId: number) {
used: number;
expired: number;
}>>(
SERVER_API_URL + '/sys/user-coupon/count',
'/sys/user-coupon/count',
{ userId }
);
if (res.code === 0 && res.data) {
@ -55,7 +54,7 @@ export async function getUserCouponCount(userId: number) {
*/
export async function getUserCoupon(id: number) {
const res = await request.get<ApiResult<UserCoupon>>(
SERVER_API_URL + '/sys/user-coupon/' + id
'/sys/user-coupon/' + id
);
if (res.code === 0 && res.data) {
return res.data;
@ -68,7 +67,7 @@ export async function getUserCoupon(id: number) {
*/
export async function useCoupon(couponId: number, orderId: number) {
const res = await request.put<ApiResult<unknown>>(
SERVER_API_URL + '/sys/user-coupon/use',
'/sys/user-coupon/use',
{ couponId, orderId }
);
if (res.code === 0) {

2
src/pages/user/components/UserCard.tsx

@ -71,7 +71,7 @@ function UserCard() {
// 加载用户统计数据
if (data.userId) {
// loadUserStats(data.userId)
loadUserStats(data.userId)
}
// 获取openId

4
src/pages/user/components/UserOrder.tsx

@ -51,10 +51,10 @@ function UserOrder() {
<View className={'item flex justify-center flex-col items-center'}
onClick={() => navTo('/user/order/order?statusFilter=5', true)}>
<Comment size={24} className={'text-gray-500 font-normal'}/>
<Text className={'text-sm text-gray-600 py-1'}></Text>
<Text className={'text-sm text-gray-600 py-1'}></Text>
</View>
<View className={'item flex justify-center flex-col items-center'}
onClick={() => navTo('/user/order/order?statusFilter=7', true)}>
onClick={() => navTo('/user/order/order?statusFilter=6', true)}>
<Refund size={26} className={'font-normal text-gray-500'}/>
<Text className={'text-sm text-gray-600 py-1'}>退/</Text>
</View>

220
src/user/order/components/OrderList.tsx

@ -1,4 +1,4 @@
import {Avatar, Cell, Space, Tabs, Button, TabPane, Image} from '@nutui/nutui-react-taro'
import {Avatar, Cell, Space, Empty, Tabs, Button, TabPane, Image} from '@nutui/nutui-react-taro'
import {useEffect, useState, CSSProperties} from "react";
import {View} from '@tarojs/components'
import Taro from '@tarojs/taro';
@ -27,7 +27,7 @@ const tabs = [
key: '全部',
title: '全部',
description: '所有订单',
statusFilter: undefined // 不传statusFilter,显示所有订单
statusFilter: -1 // 使用-1表示全部订单
},
{
index: 1,
@ -59,9 +59,9 @@ const tabs = [
},
{
index: 5,
key: '已取消',
title: '已取消',
description: '已取消/退款的订单',
key: '退货/售后',
title: '退货/售后',
description: '退货/售后的订单',
statusFilter: 6 // 对应后端:order_status = 6 (已退款)
}
]
@ -84,10 +84,6 @@ function OrderList(props: OrderListProps) {
// 根据传入的statusFilter设置初始tab索引
const getInitialTabIndex = () => {
if (props.searchParams?.statusFilter !== undefined) {
// 如果statusFilter为-1,表示全部,对应index为0
if (props.searchParams.statusFilter === -1) {
return 0;
}
const tab = tabs.find(t => t.statusFilter === props.searchParams?.statusFilter);
return tab ? tab.index : 0;
}
@ -117,7 +113,7 @@ function OrderList(props: OrderListProps) {
// 已付款后检查发货状态
if (order.deliveryStatus === 10) return '待发货';
if (order.deliveryStatus === 20) return '待收货';
if (order.deliveryStatus === 30) return '已收货';
if (order.deliveryStatus === 30) return '已完成';
// 最后检查订单完成状态
if (order.orderStatus === 1) return '已完成';
@ -161,6 +157,7 @@ function OrderList(props: OrderListProps) {
if (currentTab && currentTab.statusFilter !== undefined) {
params.statusFilter = currentTab.statusFilter;
}
// 注意:当statusFilter为undefined时,不要添加到params中,这样API请求就不会包含这个参数
console.log(`Tab ${index} (${currentTab?.title}) 筛选参数:`, params);
return params;
@ -172,16 +169,21 @@ function OrderList(props: OrderListProps) {
const currentPage = resetPage ? 1 : (targetPage || page);
const statusParams = getOrderStatusParams(tapIndex);
// 合并搜索条件,tab的statusFilter优先级更高
const searchConditions = {
const searchConditions: any = {
page: currentPage,
userId: statusParams.userId, // 用户ID
...props.searchParams, // 搜索关键词等其他条件
statusFilter: statusParams.statusFilter // tab的statusFilter优先级最高
};
// statusFilter总是添加到搜索条件中(包括-1表示全部)
if (statusParams.statusFilter !== undefined) {
searchConditions.statusFilter = statusParams.statusFilter;
}
console.log('订单筛选条件:', {
tapIndex,
statusParams,
searchConditions
searchConditions,
finalStatusFilter: searchConditions.statusFilter
});
try {
@ -198,7 +200,7 @@ function OrderList(props: OrderListProps) {
const batchResults = await Promise.all(
batch.map(async (order) => {
try {
const orderGoods = await listShopOrderGoods({ orderId: order.orderId });
const orderGoods = await listShopOrderGoods({orderId: order.orderId});
return {
...order,
orderGoods: orderGoods || []
@ -306,10 +308,34 @@ function OrderList(props: OrderListProps) {
reload(true).then(); // 首次加载或tab切换时重置页码
}, [tapIndex]); // 监听tapIndex变化
// 监听外部statusFilter变化,同步更新tab索引
useEffect(() => {
// 当外部传入的搜索参数变化时(不包括statusFilter,因为tab切换会处理)
// 获取当前的statusFilter,如果未定义则默认为-1(全部)
const currentStatusFilter = props.searchParams?.statusFilter !== undefined
? props.searchParams.statusFilter
: -1;
const tab = tabs.find(t => t.statusFilter === currentStatusFilter);
const targetTabIndex = tab ? tab.index : 0;
console.log('外部statusFilter变化:', {
statusFilter: currentStatusFilter,
originalStatusFilter: props.searchParams?.statusFilter,
currentTapIndex: tapIndex,
targetTabIndex,
shouldUpdate: targetTabIndex !== tapIndex
});
if (targetTabIndex !== tapIndex) {
setTapIndex(targetTabIndex);
// 不需要调用reload,因为tapIndex变化会触发reload
}
}, [props.searchParams?.statusFilter]); // 监听statusFilter变化
useEffect(() => {
// 当外部传入的搜索参数变化时(不包括statusFilter,因为上面已经处理)
// 只有当搜索关键词等其他条件变化时才重新加载
const { statusFilter, ...otherParams } = props.searchParams || {};
const {statusFilter, ...otherParams} = props.searchParams || {};
// 检查是否有除statusFilter外的其他搜索条件变化
const hasOtherSearchParams = Object.keys(otherParams).some(key =>
@ -385,82 +411,104 @@ function OrderList(props: OrderListProps) {
</>
}
loadMoreText={
<View className={'h-24'}>
</View>
list.length === 0 ? (
<Empty style={{ backgroundColor: 'transparent' }} description="您还没有订单哦"/>
) : (
<View className={'h-24'}>
</View>
)
}
>
{list?.map((item, index) => {
return (
<Cell key={index} style={{padding: '16px'}} onClick={() => Taro.navigateTo({url: `/shop/orderDetail/index?orderId=${item.orderId}`})}>
<Space direction={'vertical'} className={'w-full flex flex-col'}>
<View className={'order-no flex justify-between'}>
<View className={'text-gray-600 font-bold text-sm'}
onClick={(e) => {e.stopPropagation(); copyText(`${item.orderNo}`)}}>{item.orderNo}</View>
<View className={`${getOrderStatusColor(item)} font-medium`}>{getOrderStatusText(item)}</View>
</View>
<div
className={'create-time text-gray-400 text-xs'}>{dayjs(item.createTime).format('YYYY年MM月DD日 HH:mm:ss')}</div>
{/* 商品信息 */}
<div className={'goods-info'}>
{item.orderGoods && item.orderGoods.length > 0 ? (
item.orderGoods.map((goods, goodsIndex) => (
<div key={goodsIndex} className={'flex items-center mb-2'}>
<Image
src={goods.image || '/default-goods.png'}
width="50"
height="50"
lazyLoad={false}
className={'rounded'}
{/* 订单列表 */}
{list.length > 0 && list?.map((item, index) => {
return (
<Cell key={index} style={{padding: '16px'}}
onClick={() => Taro.navigateTo({url: `/shop/orderDetail/index?orderId=${item.orderId}`})}>
<Space direction={'vertical'} className={'w-full flex flex-col'}>
<View className={'order-no flex justify-between'}>
<View className={'text-gray-600 font-bold text-sm'}
onClick={(e) => {
e.stopPropagation();
copyText(`${item.orderNo}`)
}}>{item.orderNo}</View>
<View className={`${getOrderStatusColor(item)} font-medium`}>{getOrderStatusText(item)}</View>
</View>
<div
className={'create-time text-gray-400 text-xs'}>{dayjs(item.createTime).format('YYYY年MM月DD日 HH:mm:ss')}</div>
{/* 商品信息 */}
<div className={'goods-info'}>
{item.orderGoods && item.orderGoods.length > 0 ? (
item.orderGoods.map((goods, goodsIndex) => (
<div key={goodsIndex} className={'flex items-center mb-2'}>
<Image
src={goods.image || '/default-goods.png'}
width="50"
height="50"
lazyLoad={false}
className={'rounded'}
/>
<div className={'ml-2 flex-1'}>
<div className={'text-sm font-bold'}>{goods.goodsName}</div>
{goods.spec && <div className={'text-gray-500 text-xs'}>{goods.spec}</div>}
<div className={'text-gray-500 text-xs'}>{goods.totalNum}</div>
</div>
<div className={'text-sm'}>{goods.price}</div>
</div>
))
) : (
<div className={'flex items-center'}>
<Avatar
src='/default-goods.png'
size={'50'}
shape={'square'}
/>
<div className={'ml-2 flex-1'}>
<div className={'text-sm font-bold'}>{goods.goodsName}</div>
{goods.spec && <div className={'text-gray-500 text-xs'}>{goods.spec}</div>}
<div className={'text-gray-500 text-xs'}>{goods.totalNum}</div>
<div className={'ml-2'}>
<div className={'text-sm'}>{item.title || '订单商品'}</div>
<div className={'text-gray-400 text-xs'}>{item.totalNum}</div>
</div>
<div className={'text-sm'}>{goods.price}</div>
</div>
))
) : (
<div className={'flex items-center'}>
<Avatar
src='/default-goods.png'
size={'50'}
shape={'square'}
/>
<div className={'ml-2'}>
<div className={'text-sm'}>{item.title || '订单商品'}</div>
<div className={'text-gray-400 text-xs'}>{item.totalNum}</div>
</div>
</div>
)}
</div>
<div className={'w-full text-right'}>{item.payPrice}</div>
{/* 操作按钮 */}
<Space className={'btn flex justify-end'}>
{/* 待付款状态:显示取消订单和立即支付 */}
{(!item.payStatus) && item.orderStatus !== 2 && (
<Space>
<Button size={'small'} onClick={(e) => {e.stopPropagation(); cancelOrder(item)}}></Button>
<Button size={'small'} type="primary" onClick={(e) => {e.stopPropagation(); console.log('立即支付')}}></Button>
</Space>
)}
{/* 待收货状态:显示确认收货 */}
{item.deliveryStatus === 20 && (
<Button size={'small'} type="primary" onClick={(e) => {e.stopPropagation(); confirmReceive(item)}}></Button>
)}
{/* 已完成状态:显示申请退款 */}
{item.orderStatus === 1 && (
<Button size={'small'} onClick={(e) => {e.stopPropagation(); console.log('申请退款')}}>退</Button>
)}
{/* 退款相关状态的按钮可以在这里添加 */}
)}
</div>
<div className={'w-full text-right'}>{item.payPrice}</div>
{/* 操作按钮 */}
<Space className={'btn flex justify-end'}>
{/* 待付款状态:显示取消订单和立即支付 */}
{(!item.payStatus) && item.orderStatus !== 2 && (
<Space>
<Button size={'small'} onClick={(e) => {
e.stopPropagation();
cancelOrder(item)
}}></Button>
<Button size={'small'} type="primary" onClick={(e) => {
e.stopPropagation();
console.log('立即支付')
}}></Button>
</Space>
)}
{/* 待收货状态:显示确认收货 */}
{item.deliveryStatus === 20 && (
<Button size={'small'} type="primary" onClick={(e) => {
e.stopPropagation();
confirmReceive(item)
}}></Button>
)}
{/* 已完成状态:显示申请退款 */}
{item.orderStatus === 1 && (
<Button size={'small'} onClick={(e) => {
e.stopPropagation();
console.log('申请退款')
}}>退</Button>
)}
{/* 退款相关状态的按钮可以在这里添加 */}
</Space>
</Space>
</Space>
</Cell>
)
</Cell>
)
})}
</InfiniteLoading>
)}

64
src/user/order/order.tsx

@ -1,5 +1,5 @@
import {useState, useCallback, useRef} from "react";
import Taro, {useDidShow} from '@tarojs/taro'
import {useState, useCallback, useRef, useEffect} from "react";
import Taro from '@tarojs/taro'
import {Space, NavBar, Button, Input} from '@nutui/nutui-react-taro'
import {Search, Filter, ArrowLeft} from '@nutui/icons-react-taro'
import {View} from '@tarojs/components';
@ -32,26 +32,27 @@ function Order() {
searchTimeoutRef.current = setTimeout(() => {
if (keyword.trim()) {
handleSearch({keywords: keyword.trim()});
} else {
// 如果搜索关键词为空,清除keywords参数
const newSearchParams = { ...searchParams };
delete newSearchParams.keywords;
setSearchParams(newSearchParams);
reload(newSearchParams).then();
}
}, 500); // 500ms防抖延迟
}, []);
}, [searchParams]);
// 处理搜索
const handleSearch = (where: ShopOrderParam) => {
setSearchParams(where)
reload(where).then()
}
// 重置搜索
const handleResetSearch = () => {
setSearchKeyword(''); // 清空搜索关键词
setSearchParams({
statusFilter: params.statusFilter != undefined && params.statusFilter != '' ? parseInt(params.statusFilter) : -1
}); // 重置搜索参数,但保留初始状态筛选
reload().then()
// 合并搜索参数,保留当前的statusFilter
const newSearchParams = {
...searchParams, // 保留当前的所有参数(包括statusFilter)
...where // 应用新的搜索条件
};
setSearchParams(newSearchParams)
reload(newSearchParams).then()
}
useDidShow(() => {
useEffect(() => {
// 获取状态栏高度
Taro.getSystemInfo({
success: (res) => {
@ -69,7 +70,7 @@ function Order() {
});
reload().then()
});
}, []);
return (
<View className="bg-gray-50 min-h-screen">
@ -142,38 +143,11 @@ function Order() {
>
</Button>
<Button
onClick={() => {
setSearchKeyword('');
handleResetSearch();
}}
>
</Button>
</Space>
</View>
</View>
)}
{/*暂无订单*/}
{/*{list.length == 0 && (*/}
{/* <ConfigProvider>*/}
{/* <div className={'h-full flex flex-col justify-center items-center'} style={{*/}
{/* height: showSearch ? 'calc(100vh - 200px)' : 'calc(100vh - 150px)',*/}
{/* marginTop: showSearch ? '60px' : '0'*/}
{/* }}>*/}
{/* <Empty*/}
{/* style={{*/}
{/* backgroundColor: 'transparent'*/}
{/* }}*/}
{/* description="您还没有订单哦"*/}
{/* />*/}
{/* <Space>*/}
{/* <Button type="success" fill="dashed"*/}
{/* onClick={() => Taro.switchTab({url: '/pages/index/index'})}>去挑选商品</Button>*/}
{/* </Space>*/}
{/* </div>*/}
{/* </ConfigProvider>*/}
{/*)}*/}
{/*订单列表*/}
<OrderList
onReload={() => reload(searchParams)}

Loading…
Cancel
Save