forked from gxwebsoft/mp-10550
refactor(order): 优化订单商品数据显示逻辑
- 将订单模型中的 orderGoods 类型从 OrderGoods 改为 ShopOrderGoods - 移除 OrderWithGoods 接口定义和 normalizeOrderGoodsList 函数 - 直接使用订单分页接口返回的 orderGoods 字段渲染商品信息 - 添加 utils/orderGoods.ts 工具函数处理订单商品数据标准化 - 在骑手端订单页面实现商品名称汇总显示功能 - 优化再次购买和支付功能中的商品数据获取逻辑
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import type { PageParam } from '@/api/index';
|
import type { PageParam } from '@/api/index';
|
||||||
import {OrderGoods} from "@/api/system/orderGoods/model";
|
import type { ShopOrderGoods } from '@/api/shop/shopOrderGoods/model';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 订单
|
* 订单
|
||||||
@@ -156,7 +156,7 @@ export interface ShopOrder {
|
|||||||
// 是否已收到赠品
|
// 是否已收到赠品
|
||||||
hasTakeGift?: string;
|
hasTakeGift?: string;
|
||||||
// 订单商品项
|
// 订单商品项
|
||||||
orderGoods?: OrderGoods[];
|
orderGoods?: ShopOrderGoods[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -241,6 +241,14 @@ export default function RiderOrders() {
|
|||||||
const flow2Done = o.deliveryStatus === 20 || o.deliveryStatus === 30
|
const flow2Done = o.deliveryStatus === 20 || o.deliveryStatus === 30
|
||||||
const flow3Done = !!o.sendEndTime
|
const flow3Done = !!o.sendEndTime
|
||||||
const flow4Done = o.orderStatus === 1
|
const flow4Done = o.orderStatus === 1
|
||||||
|
// 直接使用订单分页接口返回的 orderGoods
|
||||||
|
const goodsList = o.orderGoods || []
|
||||||
|
const goodsNameList = goodsList
|
||||||
|
.map(g => g?.goodsName || (g as any)?.goodsTitle || (g as any)?.title || (g as any)?.name)
|
||||||
|
.filter(Boolean) as string[]
|
||||||
|
const goodsSummary = goodsNameList.length
|
||||||
|
? `${goodsNameList.slice(0, 3).join('、')}${goodsList.length > 3 ? ` 等${goodsList.length}件` : ''}`
|
||||||
|
: (o.title || '-')
|
||||||
|
|
||||||
const autoConfirmAt = o.sendEndTime
|
const autoConfirmAt = o.sendEndTime
|
||||||
? dayjs(o.sendEndTime).add(AUTO_CONFIRM_RECEIVE_HOURS_FALLBACK, 'hour')
|
? dayjs(o.sendEndTime).add(AUTO_CONFIRM_RECEIVE_HOURS_FALLBACK, 'hour')
|
||||||
@@ -278,6 +286,10 @@ export default function RiderOrders() {
|
|||||||
<Text className="text-gray-500 ml-3">数量:</Text>
|
<Text className="text-gray-500 ml-3">数量:</Text>
|
||||||
<Text>{o.totalNum ?? '-'}</Text>
|
<Text>{o.totalNum ?? '-'}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
<View className="text-sm text-gray-700 mt-1">
|
||||||
|
<Text className="text-gray-500">商品:</Text>
|
||||||
|
<Text>{goodsSummary}</Text>
|
||||||
|
</View>
|
||||||
{o.sendEndTime && (
|
{o.sendEndTime && (
|
||||||
<View className="text-sm text-gray-700 mt-1">
|
<View className="text-sm text-gray-700 mt-1">
|
||||||
<Text className="text-gray-500">送达时间:</Text>
|
<Text className="text-gray-500">送达时间:</Text>
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import dayjs from "dayjs";
|
|||||||
import {pageShopOrder, updateShopOrder, createOrder} from "@/api/shop/shopOrder";
|
import {pageShopOrder, updateShopOrder, createOrder} from "@/api/shop/shopOrder";
|
||||||
import {ShopOrder, ShopOrderParam} from "@/api/shop/shopOrder/model";
|
import {ShopOrder, ShopOrderParam} from "@/api/shop/shopOrder/model";
|
||||||
import {listShopOrderGoods} from "@/api/shop/shopOrderGoods";
|
import {listShopOrderGoods} from "@/api/shop/shopOrderGoods";
|
||||||
import {ShopOrderGoods} from "@/api/shop/shopOrderGoods/model";
|
|
||||||
import {copyText} from "@/utils/common";
|
import {copyText} from "@/utils/common";
|
||||||
import PaymentCountdown from "@/components/PaymentCountdown";
|
import PaymentCountdown from "@/components/PaymentCountdown";
|
||||||
import {PaymentType} from "@/utils/payment";
|
import {PaymentType} from "@/utils/payment";
|
||||||
@@ -78,36 +77,6 @@ const tabs = [
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
// 扩展订单接口,包含商品信息
|
|
||||||
interface OrderWithGoods extends ShopOrder {
|
|
||||||
// 避免与 ShopOrder.orderGoods (OrderGoods[]) 冲突:这里使用单独字段保存接口返回的商品明细
|
|
||||||
orderGoodsList?: ShopOrderGoods[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 后端订单分页接口通常已返回 orderGoods(订单商品明细)。
|
|
||||||
// 这里把各种可能的字段名做一次归一化,避免再为每个订单额外请求一次“订单商品”接口。
|
|
||||||
const normalizeOrderGoodsList = (order: any): ShopOrderGoods[] => {
|
|
||||||
const raw =
|
|
||||||
order?.orderGoods ||
|
|
||||||
order?.orderGoodsList ||
|
|
||||||
order?.goodsList ||
|
|
||||||
order?.goods ||
|
|
||||||
[];
|
|
||||||
if (!Array.isArray(raw)) return [];
|
|
||||||
|
|
||||||
return raw.map((g: any) => ({
|
|
||||||
...g,
|
|
||||||
goodsId: g?.goodsId ?? g?.itemId ?? g?.goods_id,
|
|
||||||
skuId: g?.skuId ?? g?.sku_id,
|
|
||||||
// 当接口只返回了最小字段时,用订单标题兜底,避免列表出现空白商品名
|
|
||||||
goodsName: g?.goodsName ?? g?.goodsTitle ?? g?.title ?? g?.name ?? order?.title ?? '商品',
|
|
||||||
image: g?.image ?? g?.goodsImage ?? g?.cover ?? g?.pic,
|
|
||||||
spec: g?.spec ?? g?.specInfo ?? g?.spec_name,
|
|
||||||
totalNum: g?.totalNum ?? g?.quantity ?? g?.num ?? g?.count,
|
|
||||||
price: g?.price ?? g?.payPrice ?? g?.goodsPrice ?? g?.unitPrice
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
interface OrderListProps {
|
interface OrderListProps {
|
||||||
onReload?: () => void;
|
onReload?: () => void;
|
||||||
searchParams?: ShopOrderParam;
|
searchParams?: ShopOrderParam;
|
||||||
@@ -122,7 +91,7 @@ interface OrderListProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function OrderList(props: OrderListProps) {
|
function OrderList(props: OrderListProps) {
|
||||||
const [list, setList] = useState<OrderWithGoods[]>([])
|
const [list, setList] = useState<ShopOrder[]>([])
|
||||||
const pageRef = useRef(1)
|
const pageRef = useRef(1)
|
||||||
const [hasMore, setHasMore] = useState(true)
|
const [hasMore, setHasMore] = useState(true)
|
||||||
const [payingOrderId, setPayingOrderId] = useState<number | null>(null)
|
const [payingOrderId, setPayingOrderId] = useState<number | null>(null)
|
||||||
@@ -254,20 +223,17 @@ function OrderList(props: OrderListProps) {
|
|||||||
const res = await pageShopOrder(searchConditions);
|
const res = await pageShopOrder(searchConditions);
|
||||||
|
|
||||||
if (res?.list && res?.list.length > 0) {
|
if (res?.list && res?.list.length > 0) {
|
||||||
// 订单分页接口已返回 orderGoods:直接读取并归一化,避免 N+1 请求导致列表加载慢
|
// 订单分页接口已返回 orderGoods:列表直接使用该字段
|
||||||
const ordersWithGoods: OrderWithGoods[] = res.list.map((order: any) => ({
|
const incoming = res.list as ShopOrder[];
|
||||||
...order,
|
|
||||||
orderGoodsList: normalizeOrderGoodsList(order)
|
|
||||||
}));
|
|
||||||
|
|
||||||
// 使用函数式更新避免依赖 list
|
// 使用函数式更新避免依赖 list
|
||||||
setList(prevList => {
|
setList(prevList => {
|
||||||
const newList = resetPage ? ordersWithGoods : (prevList || []).concat(ordersWithGoods);
|
const newList = resetPage ? incoming : (prevList || []).concat(incoming);
|
||||||
return newList;
|
return newList;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 正确判断是否还有更多数据
|
// 正确判断是否还有更多数据
|
||||||
const hasMoreData = res.list.length >= 10; // 假设每页10条数据
|
const hasMoreData = incoming.length >= 10; // 假设每页10条数据
|
||||||
setHasMore(hasMoreData);
|
setHasMore(hasMoreData);
|
||||||
} else {
|
} else {
|
||||||
setList(prevList => resetPage ? [] : prevList);
|
setList(prevList => resetPage ? [] : prevList);
|
||||||
@@ -377,9 +343,9 @@ function OrderList(props: OrderListProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 再次购买 (已完成状态)
|
// 再次购买 (已完成状态)
|
||||||
const buyAgain = (order: OrderWithGoods) => {
|
const buyAgain = (order: ShopOrder) => {
|
||||||
console.log('再次购买:', order);
|
console.log('再次购买:', order);
|
||||||
const goodsId = order.orderGoodsList?.[0]?.goodsId
|
const goodsId = order.orderGoods?.[0]?.goodsId
|
||||||
if (!goodsId) {
|
if (!goodsId) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '订单商品信息缺失',
|
title: '订单商品信息缺失',
|
||||||
@@ -437,7 +403,7 @@ function OrderList(props: OrderListProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 立即支付
|
// 立即支付
|
||||||
const payOrder = async (order: OrderWithGoods) => {
|
const payOrder = async (order: ShopOrder) => {
|
||||||
try {
|
try {
|
||||||
if (!order.orderId || !order.orderNo) {
|
if (!order.orderId || !order.orderNo) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
@@ -480,8 +446,8 @@ function OrderList(props: OrderListProps) {
|
|||||||
|
|
||||||
Taro.showLoading({title: '发起支付...'});
|
Taro.showLoading({title: '发起支付...'});
|
||||||
|
|
||||||
// 构建商品数据:优先使用列表已加载的商品信息;缺失时再补拉一次,避免goodsItems为空导致后端拒绝/再次支付失败
|
// 构建商品数据:优先使用订单分页接口返回的 orderGoods;缺失时再补拉一次,避免goodsItems为空导致后端拒绝/再次支付失败
|
||||||
let orderGoods = order.orderGoodsList || [];
|
let orderGoods = order.orderGoods || [];
|
||||||
if (!orderGoods.length) {
|
if (!orderGoods.length) {
|
||||||
try {
|
try {
|
||||||
orderGoods = (await listShopOrderGoods({orderId: order.orderId})) || [];
|
orderGoods = (await listShopOrderGoods({orderId: order.orderId})) || [];
|
||||||
@@ -492,13 +458,13 @@ function OrderList(props: OrderListProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const goodsItems = orderGoods
|
const goodsItems = orderGoods
|
||||||
.filter(g => !!g.goodsId)
|
.filter(g => !!(g as any).goodsId || !!(g as any).itemId)
|
||||||
.map(goods => ({
|
.map(goods => ({
|
||||||
goodsId: goods.goodsId as number,
|
goodsId: (goods.goodsId ?? (goods as any).itemId) as number,
|
||||||
quantity: goods.totalNum || 1,
|
quantity: ((goods as any).quantity ?? goods.totalNum ?? 1) as number,
|
||||||
// 若后端按SKU计算价格/库存,补齐SKU/规格信息更安全
|
// 若后端按SKU计算价格/库存,补齐SKU/规格信息更安全
|
||||||
skuId: (goods as any).skuId,
|
skuId: (goods as any).skuId ?? (goods as any).sku_id,
|
||||||
specInfo: (goods as any).spec || (goods as any).specInfo
|
specInfo: (goods as any).specInfo ?? (goods as any).spec
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (!goodsItems.length) {
|
if (!goodsItems.length) {
|
||||||
@@ -753,8 +719,8 @@ function OrderList(props: OrderListProps) {
|
|||||||
|
|
||||||
{/* 商品信息 */}
|
{/* 商品信息 */}
|
||||||
<View className={'goods-info'}>
|
<View className={'goods-info'}>
|
||||||
{item.orderGoodsList && item.orderGoodsList.length > 0 ? (
|
{item.orderGoods && item.orderGoods.length > 0 ? (
|
||||||
item.orderGoodsList.map((goods, goodsIndex) => (
|
item.orderGoods.map((goods, goodsIndex) => (
|
||||||
<View key={goodsIndex} className={'flex items-center mb-2'}>
|
<View key={goodsIndex} className={'flex items-center mb-2'}>
|
||||||
<Image
|
<Image
|
||||||
src={goods.image || '/default-goods.png'}
|
src={goods.image || '/default-goods.png'}
|
||||||
@@ -764,11 +730,15 @@ function OrderList(props: OrderListProps) {
|
|||||||
className={'rounded'}
|
className={'rounded'}
|
||||||
/>
|
/>
|
||||||
<View className={'ml-2 flex flex-col flex-1'}>
|
<View className={'ml-2 flex flex-col flex-1'}>
|
||||||
<Text className={'text-sm font-bold'}>{goods.goodsName}</Text>
|
<Text className={'text-sm font-bold'}>
|
||||||
{goods.spec && <Text className={'text-gray-500 text-xs'}>规格:{goods.spec}</Text>}
|
{goods.goodsName || (goods as any).goodsTitle || (goods as any).title || item.title || '订单商品'}
|
||||||
<Text className={'text-gray-500 text-xs'}>数量:{goods.totalNum}</Text>
|
</Text>
|
||||||
|
{(goods.spec || (goods as any).specInfo) && (
|
||||||
|
<Text className={'text-gray-500 text-xs'}>规格:{goods.spec || (goods as any).specInfo}</Text>
|
||||||
|
)}
|
||||||
|
<Text className={'text-gray-500 text-xs'}>数量:{(goods as any).quantity ?? goods.totalNum}</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text className={'text-sm'}>¥{goods.price}</Text>
|
<Text className={'text-sm'}>¥{goods.price || (goods as any).payPrice}</Text>
|
||||||
</View>
|
</View>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
32
src/utils/orderGoods.ts
Normal file
32
src/utils/orderGoods.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import type { ShopOrderGoods } from '@/api/shop/shopOrderGoods/model';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize order goods data returned by the order/page API.
|
||||||
|
*
|
||||||
|
* In practice different backends may return different field names (orderGoods/orderGoodsList/goodsList...),
|
||||||
|
* and the item fields can also differ (goodsName/title/name, totalNum/quantity, etc.).
|
||||||
|
*
|
||||||
|
* We normalize them to ShopOrderGoods so list pages can render without doing N+1 requests per order.
|
||||||
|
*/
|
||||||
|
export const normalizeOrderGoodsList = (order: any): ShopOrderGoods[] => {
|
||||||
|
const raw =
|
||||||
|
order?.orderGoods ||
|
||||||
|
order?.orderGoodsList ||
|
||||||
|
order?.goodsList ||
|
||||||
|
order?.goods ||
|
||||||
|
[];
|
||||||
|
if (!Array.isArray(raw)) return [];
|
||||||
|
|
||||||
|
return raw.map((g: any) => ({
|
||||||
|
...g,
|
||||||
|
goodsId: g?.goodsId ?? g?.itemId ?? g?.goods_id,
|
||||||
|
skuId: g?.skuId ?? g?.sku_id,
|
||||||
|
// When the API returns minimal fields, fall back to order title to avoid blank names.
|
||||||
|
goodsName: g?.goodsName ?? g?.goodsTitle ?? g?.title ?? g?.name ?? order?.title ?? '商品',
|
||||||
|
image: g?.image ?? g?.goodsImage ?? g?.cover ?? g?.pic,
|
||||||
|
spec: g?.spec ?? g?.specInfo ?? g?.spec_name,
|
||||||
|
totalNum: g?.totalNum ?? g?.quantity ?? g?.num ?? g?.count,
|
||||||
|
price: g?.price ?? g?.payPrice ?? g?.goodsPrice ?? g?.unitPrice
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
Reference in New Issue
Block a user