Browse Source

优化下单流程

master
科技小王子 4 weeks ago
parent
commit
5dd02be44b
  1. 103
      docs/ORDER_IMPROVEMENTS.md
  2. 4
      src/api/shop/shopOrder/index.ts
  3. 101
      src/api/shop/shopOrderGoods/index.ts
  4. 70
      src/api/shop/shopOrderGoods/model/index.ts
  5. 1
      src/app.config.ts
  6. 15
      src/pages/cart/cart.tsx
  7. 156
      src/pages/order/components/OrderList.tsx
  8. 2
      src/pages/order/order.tsx
  9. 54
      src/pages/order/test-order.tsx
  10. 67
      src/shop/orderConfirmCart/index.tsx
  11. 31
      src/shop/orderDetail/index.tsx
  12. 2
      src/utils/payment.ts

103
docs/ORDER_IMPROVEMENTS.md

@ -0,0 +1,103 @@
# 订单列表功能完善说明
## 完善的功能
### 1. 订单商品正确显示
- **问题**: 原来只显示订单基本信息,没有显示具体的商品信息
- **解决方案**:
- 扩展了订单接口,添加了 `OrderWithGoods` 类型
- 在加载订单列表时,同时获取每个订单的商品信息
- 使用 `listShopOrderGoods` API 获取订单商品详情
- 显示商品图片、名称、规格、数量和价格
### 2. 订单状态正确显示
- **问题**: 原来固定显示"待付款"状态
- **解决方案**:
- 添加了 `getOrderStatusText` 函数,根据订单的 `payStatus`、`deliveryStatus` 和 `orderStatus` 动态显示状态
- 支持的状态包括:待付款、待发货、待收货、已收货、已完成、已取消、退款申请中、退款成功等
### 3. 确认收货功能
- **新增功能**:
- 当订单状态为"待收货"时,显示"确认收货"按钮
- 点击确认收货后,更新订单状态为"已收货"和"已完成"
- 操作成功后显示提示信息并刷新列表
### 4. 取消订单功能
- **新增功能**:
- 当订单状态为"待付款"时,显示"取消订单"按钮
- 点击取消订单后,更新订单状态为"已取消"
- 操作成功后显示提示信息并刷新列表
### 5. 操作按钮优化
- **改进**: 根据订单状态动态显示不同的操作按钮
- 待付款:显示"取消订单"和"立即支付"按钮
- 待收货:显示"确认收货"按钮
- 已完成:显示"申请退款"按钮(预留功能)
### 6. 订单详情页面修复
- **问题**: 订单详情页面使用了错误的API
- **解决方案**:
- 修改为使用正确的 `listShopOrderGoods` API
- 直接显示商品信息,无需额外查询商品详情
- 优化了商品信息的显示格式
## 技术改进
### 1. 类型安全
- 添加了 `OrderWithGoods` 接口扩展
- 完善了 `OrderListProps` 接口定义
- 使用了正确的 TypeScript 类型
### 2. 错误处理
- 添加了完善的错误处理机制
- 操作失败时显示友好的错误提示
- 防止因单个订单商品获取失败而影响整个列表
### 3. 用户体验
- 添加了操作成功的提示信息
- 操作完成后自动刷新列表
- 阻止事件冒泡,避免误触
### 4. 数据一致性
- 操作完成后通知父组件刷新数据
- 确保订单状态的实时更新
## 使用说明
### 订单状态说明
- **待付款**: `payStatus = 0`
- **待发货**: `payStatus = 1 && deliveryStatus = 10`
- **待收货**: `deliveryStatus = 20`
- **已收货**: `deliveryStatus = 30`
- **已完成**: `orderStatus = 1`
- **已取消**: `orderStatus = 2`
### API 依赖
- `pageShopOrder`: 分页查询订单
- `listShopOrderGoods`: 查询订单商品
- `updateShopOrder`: 更新订单状态
### 组件结构
```
src/pages/order/
├── order.tsx # 订单主页面
├── components/
│ └── OrderList.tsx # 订单列表组件
└── test-order.tsx # 测试页面(可选)
```
## 测试建议
1. 创建不同状态的测试订单
2. 验证订单商品信息显示是否正确
3. 测试确认收货功能
4. 测试取消订单功能
5. 验证订单状态切换是否正常
## 后续优化建议
1. 添加订单搜索功能
2. 实现立即支付功能
3. 添加申请退款功能
4. 优化商品图片加载和缓存
5. 添加订单操作的二次确认

4
src/api/shop/shopOrder/index.ts

@ -118,7 +118,7 @@ export interface WxPayResult {
*/ */
export async function createOrder(data: OrderCreateRequest) { export async function createOrder(data: OrderCreateRequest) {
const res = await request.post<ApiResult<WxPayResult>>( const res = await request.post<ApiResult<WxPayResult>>(
'/shop/shop-order/create',
'http://127.0.0.1:9200/api/shop/shop-order',
data data
); );
if (res.code === 0) { if (res.code === 0) {
@ -128,7 +128,7 @@ export async function createOrder(data: OrderCreateRequest) {
} }
/** /**
*
*
*/ */
export async function repairOrder(data: ShopOrder) { export async function repairOrder(data: ShopOrder) {
const res = await request.put<ApiResult<unknown>>( const res = await request.put<ApiResult<unknown>>(

101
src/api/shop/shopOrderGoods/index.ts

@ -0,0 +1,101 @@
import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api/index';
import type { ShopOrderGoods, ShopOrderGoodsParam } from './model';
/**
*
*/
export async function pageShopOrderGoods(params: ShopOrderGoodsParam) {
const res = await request.get<ApiResult<PageResult<ShopOrderGoods>>>(
'/shop/shop-order-goods/page',
params
);
if (res.code === 0) {
return res.data;
}
return Promise.reject(new Error(res.message));
}
/**
*
*/
export async function listShopOrderGoods(params?: ShopOrderGoodsParam) {
const res = await request.get<ApiResult<ShopOrderGoods[]>>(
'/shop/shop-order-goods',
params
);
if (res.code === 0 && res.data) {
return res.data;
}
return Promise.reject(new Error(res.message));
}
/**
*
*/
export async function addShopOrderGoods(data: ShopOrderGoods) {
const res = await request.post<ApiResult<unknown>>(
'/shop/shop-order-goods',
data
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
*
*/
export async function updateShopOrderGoods(data: ShopOrderGoods) {
const res = await request.put<ApiResult<unknown>>(
'/shop/shop-order-goods',
data
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
*
*/
export async function removeShopOrderGoods(id?: number) {
const res = await request.del<ApiResult<unknown>>(
'/shop/shop-order-goods/' + id
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
*
*/
export async function removeBatchShopOrderGoods(data: (number | undefined)[]) {
const res = await request.del<ApiResult<unknown>>(
'/shop/shop-order-goods/batch',
{
data
}
);
if (res.code === 0) {
return res.message;
}
return Promise.reject(new Error(res.message));
}
/**
* id查询商品信息
*/
export async function getShopOrderGoods(id: number) {
const res = await request.get<ApiResult<ShopOrderGoods>>(
'/shop/shop-order-goods/' + id
);
if (res.code === 0 && res.data) {
return res.data;
}
return Promise.reject(new Error(res.message));
}

70
src/api/shop/shopOrderGoods/model/index.ts

@ -0,0 +1,70 @@
import type { PageParam } from '@/api';
/**
*
*/
export interface ShopOrderGoods {
// 自增ID
id?: number;
// 关联订单表id
orderId?: number;
// 订单标识
orderCode?: string;
// 关联商户ID
merchantId?: number;
// 商户名称
merchantName?: string;
// 商品封面图
image?: string;
// 关联商品id
goodsId?: number;
// 商品名称
goodsName?: string;
// 商品规格
spec?: string;
//
skuId?: number;
// 单价
price?: string;
// 购买数量
totalNum?: number;
// 0 未付款 1已付款,2无需付款或占用状态
payStatus?: number;
// 0未使用,1已完成,2已取消,3取消中,4退款申请中,5退款被拒绝,6退款成功,7客户端申请退款
orderStatus?: number;
// 是否免费:0收费、1免费
isFree?: string;
// 系统版本 0当前版本 其他版本
version?: number;
// 预约时间段
timePeriod?: string;
// 预定日期
dateTime?: string;
// 开场时间
startTime?: string;
// 结束时间
endTime?: string;
// 毫秒时间戳
timeFlag?: string;
// 过期时间
expirationTime?: string;
// 备注
comments?: string;
// 用户id
userId?: number;
// 租户id
tenantId?: number;
// 更新时间
updateTime?: string;
// 创建时间
createTime?: string;
}
/**
*
*/
export interface ShopOrderGoodsParam extends PageParam {
id?: number;
orderId?: number;
keywords?: string;
}

1
src/app.config.ts

@ -45,6 +45,7 @@ export default defineAppConfig({
'orderDetail/index', 'orderDetail/index',
'goodsDetail/index', 'goodsDetail/index',
'orderConfirm/index', 'orderConfirm/index',
'orderConfirmCart/index'
] ]
} }
], ],

15
src/pages/cart/cart.tsx

@ -115,10 +115,17 @@ function Cart() {
return; return;
} }
// 这里可以跳转到结算页面
Taro.showToast({
title: '跳转到结算页面',
icon: 'success'
// 获取选中的商品
const selectedCartItems = cartItems.filter((item: CartItem) =>
selectedItems.includes(item.goodsId)
);
// 将选中的商品信息存储到本地,供结算页面使用
Taro.setStorageSync('checkout_items', JSON.stringify(selectedCartItems));
// 跳转到购物车结算页面
Taro.navigateTo({
url: '/shop/orderConfirmCart/index'
}); });
}; };

156
src/pages/order/components/OrderList.tsx

@ -1,10 +1,12 @@
import {Avatar, Cell, Space, Tabs, Button, TabPane} from '@nutui/nutui-react-taro'
import {Avatar, Cell, Space, Tabs, Button, TabPane, Image, Toast} from '@nutui/nutui-react-taro'
import {useEffect, useState, CSSProperties} from "react"; import {useEffect, useState, CSSProperties} from "react";
import Taro from '@tarojs/taro'; import Taro from '@tarojs/taro';
import {InfiniteLoading} from '@nutui/nutui-react-taro' import {InfiniteLoading} from '@nutui/nutui-react-taro'
import dayjs from "dayjs"; import dayjs from "dayjs";
import {pageShopOrder} from "@/api/shop/shopOrder";
import {pageShopOrder, updateShopOrder} from "@/api/shop/shopOrder";
import {ShopOrder} from "@/api/shop/shopOrder/model"; import {ShopOrder} from "@/api/shop/shopOrder/model";
import {listShopOrderGoods} from "@/api/shop/shopOrderGoods";
import {ShopOrderGoods} from "@/api/shop/shopOrderGoods/model";
import {copyText} from "@/utils/common"; import {copyText} from "@/utils/common";
const InfiniteUlStyle: CSSProperties = { const InfiniteUlStyle: CSSProperties = {
@ -43,16 +45,42 @@ const tabs = [
} }
] ]
function OrderList(props: any) {
const [list, setList] = useState<ShopOrder[]>([])
// 扩展订单接口,包含商品信息
interface OrderWithGoods extends ShopOrder {
orderGoods?: ShopOrderGoods[];
}
interface OrderListProps {
data: ShopOrder[];
onReload?: () => void;
}
function OrderList(props: OrderListProps) {
const [list, setList] = useState<OrderWithGoods[]>([])
const [page, setPage] = useState(1) const [page, setPage] = useState(1)
const [hasMore, setHasMore] = useState(true) const [hasMore, setHasMore] = useState(true)
const [tapIndex, setTapIndex] = useState<string | number>('0') const [tapIndex, setTapIndex] = useState<string | number>('0')
console.log(props.statusBarHeight, 'ppp') console.log(props.statusBarHeight, 'ppp')
// 获取订单状态文本
const getOrderStatusText = (order: ShopOrder) => {
if (order.payStatus === 0) return '待付款';
if (order.payStatus === 1 && order.deliveryStatus === 10) return '待发货';
if (order.deliveryStatus === 20) return '待收货';
if (order.deliveryStatus === 30) return '已收货';
if (order.orderStatus === 1) return '已完成';
if (order.orderStatus === 2) return '已取消';
if (order.orderStatus === 4) return '退款申请中';
if (order.orderStatus === 6) return '退款成功';
return '未知状态';
};
const getOrderStatusParams = (index: string | number) => { const getOrderStatusParams = (index: string | number) => {
let params: { payStatus?: number; deliveryStatus?: number; orderStatus?: number } = {};
let params: { payStatus?: number; deliveryStatus?: number; orderStatus?: number; userId?: number } = {};
// 添加用户ID过滤
params.userId = Taro.getStorageSync('UserId');
switch (index) { switch (index) {
case '1': // 待付款 case '1': // 待付款
params.payStatus = 0; params.payStatus = 0;
@ -77,13 +105,32 @@ function OrderList(props: any) {
const reload = async (resetPage = false) => { const reload = async (resetPage = false) => {
const currentPage = resetPage ? 1 : page; const currentPage = resetPage ? 1 : page;
const params = getOrderStatusParams(tapIndex); const params = getOrderStatusParams(tapIndex);
pageShopOrder({ page: currentPage, ...params }).then(res => {
let newList: ShopOrder[] | undefined = [];
pageShopOrder({ page: currentPage, ...params }).then(async res => {
let newList: OrderWithGoods[] | undefined = [];
if (res?.list && res?.list.length > 0) { if (res?.list && res?.list.length > 0) {
newList = resetPage ? res.list : list?.concat(res.list);
// 为每个订单获取商品信息
const ordersWithGoods = await Promise.all(
res.list.map(async (order) => {
try {
const orderGoods = await listShopOrderGoods({ orderId: order.orderId });
return {
...order,
orderGoods: orderGoods || []
};
} catch (error) {
console.error('获取订单商品失败:', error);
return {
...order,
orderGoods: []
};
}
})
);
newList = resetPage ? ordersWithGoods : list?.concat(ordersWithGoods);
setHasMore(true); setHasMore(true);
} else { } else {
newList = res?.list;
newList = [];
setHasMore(false); setHasMore(false);
} }
setList(newList || []); setList(newList || []);
@ -96,6 +143,39 @@ function OrderList(props: any) {
reload(); reload();
}; };
// 确认收货
const confirmReceive = async (order: ShopOrder) => {
try {
await updateShopOrder({
...order,
deliveryStatus: 30, // 已收货
orderStatus: 1 // 已完成
});
Toast.show('确认收货成功');
reload(true); // 重新加载列表
props.onReload?.(); // 通知父组件刷新
} catch (error) {
console.error('确认收货失败:', error);
Toast.show('确认收货失败');
}
};
// 取消订单
const cancelOrder = async (order: ShopOrder) => {
try {
await updateShopOrder({
...order,
orderStatus: 2 // 已取消
});
Toast.show('订单已取消');
reload(true); // 重新加载列表
props.onReload?.(); // 通知父组件刷新
} catch (error) {
console.error('取消订单失败:', error);
Toast.show('取消订单失败');
}
};
useEffect(() => { useEffect(() => {
reload(true); // 首次加载或tab切换时重置页码 reload(true); // 首次加载或tab切换时重置页码
}, [tapIndex]); // 监听tapIndex变化 }, [tapIndex]); // 监听tapIndex变化
@ -140,33 +220,69 @@ function OrderList(props: any) {
</> </>
} }
> >
{props.data?.map(item => {
{list?.map((item, index) => {
return ( return (
<Cell style={{padding: '16px'}} onClick={() => Taro.navigateTo({url: `/shop/orderDetail/index?orderId=${item.orderId}`})}>
<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'}> <Space direction={'vertical'} className={'w-full flex flex-col'}>
<div className={'order-no flex justify-between'}> <div className={'order-no flex justify-between'}>
<span className={'text-gray-700 font-bold text-sm'} <span className={'text-gray-700 font-bold text-sm'}
onClick={(e) => {e.stopPropagation(); copyText(`${item.orderNo}`)}}>{item.orderNo}</span> onClick={(e) => {e.stopPropagation(); copyText(`${item.orderNo}`)}}>{item.orderNo}</span>
<span className={'text-orange-500'}></span> {/* 这里可以根据item.orderStatus显示不同的状态 */}
<span className={'text-orange-500'}>{getOrderStatusText(item)}</span>
</div> </div>
<div <div
className={'create-time text-gray-400 text-xs'}>{dayjs(item.createTime).format('YYYY年MM月DD日 HH:mm:ss')}</div> className={'create-time text-gray-400 text-xs'}>{dayjs(item.createTime).format('YYYY年MM月DD日 HH:mm:ss')}</div>
{/* 商品信息 */}
<div className={'goods-info'}> <div className={'goods-info'}>
<div className={'flex items-center'}>
{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-red-500 text-sm'}>{goods.price}</div>
</div>
))
) : (
<div className={'flex items-center'}> <div className={'flex items-center'}>
<Avatar <Avatar
src='34'
size={'45'}
src='/default-goods.png'
size={'50'}
shape={'square'} shape={'square'}
/> />
<div className={'ml-2'}>{item.realName}</div>
<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={'text-gray-400 text-xs'}>{item.totalNum}</div>
</div>
)}
</div> </div>
<div className={' w-full text-right'}>{item.payPrice}</div>
<div className={'w-full text-right'}>{item.payPrice}</div>
{/* 操作按钮 */}
<Space className={'btn flex justify-end'}> <Space className={'btn flex justify-end'}>
<Button size={'small'}></Button>
{item.payStatus === 0 && (
<>
<Button size={'small'} onClick={(e) => {e.stopPropagation(); cancelOrder(item)}}></Button>
<Button size={'small'} type="primary" onClick={(e) => {e.stopPropagation(); console.log('立即支付')}}></Button>
</>
)}
{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> </Space>
</Cell> </Cell>

2
src/pages/order/order.tsx

@ -78,7 +78,7 @@ function Order() {
{/*订单列表*/} {/*订单列表*/}
{ {
list.length > 0 && ( list.length > 0 && (
<OrderList data={list}/>
<OrderList data={list} onReload={reload}/>
) )
} }
</> </>

54
src/pages/order/test-order.tsx

@ -0,0 +1,54 @@
import React from 'react';
import { Button, Space, Toast } from '@nutui/nutui-react-taro';
import Taro from '@tarojs/taro';
import { createOrder } from '@/api/shop/shopOrder';
import { OrderCreateRequest } from '@/api/shop/shopOrder/model';
const TestOrder = () => {
// 创建测试订单
const createTestOrder = async () => {
try {
const orderData: OrderCreateRequest = {
goodsItems: [
{
goodsId: 1,
quantity: 2,
skuId: 1,
specInfo: '红色 L码'
}
],
payType: 1, // 微信支付
deliveryType: 0, // 快递
comments: '测试订单'
};
const result = await createOrder(orderData);
console.log('订单创建成功:', result);
Toast.show('测试订单创建成功');
// 跳转到订单列表
setTimeout(() => {
Taro.navigateTo({ url: '/pages/order/order' });
}, 1000);
} catch (error) {
console.error('创建订单失败:', error);
Toast.show('创建订单失败');
}
};
return (
<div style={{ padding: '20px' }}>
<h2></h2>
<Space direction="vertical">
<Button type="primary" onClick={createTestOrder}>
</Button>
<Button onClick={() => Taro.navigateTo({ url: '/pages/order/order' })}>
</Button>
</Space>
</div>
);
};
export default TestOrder;

67
src/shop/orderConfirmCart/index.tsx

@ -8,7 +8,7 @@ import {View} from '@tarojs/components';
import {listShopUserAddress} from "@/api/shop/shopUserAddress"; import {listShopUserAddress} from "@/api/shop/shopUserAddress";
import {ShopUserAddress} from "@/api/shop/shopUserAddress/model"; import {ShopUserAddress} from "@/api/shop/shopUserAddress/model";
import './index.scss' import './index.scss'
import {useCart} from "@/hooks/useCart";
import {useCart, CartItem} from "@/hooks/useCart";
import Gap from "@/components/Gap"; import Gap from "@/components/Gap";
import {createOrder} from "@/api/shop/shopOrder"; import {createOrder} from "@/api/shop/shopOrder";
import {OrderCreateRequest} from "@/api/shop/shopOrder/model"; import {OrderCreateRequest} from "@/api/shop/shopOrder/model";
@ -19,11 +19,13 @@ const OrderConfirm = () => {
const [goods, setGoods] = useState<ShopGoods | null>(null); const [goods, setGoods] = useState<ShopGoods | null>(null);
const [address, setAddress] = useState<ShopUserAddress>() const [address, setAddress] = useState<ShopUserAddress>()
const [payment, setPayment] = useState<Payment>() const [payment, setPayment] = useState<Payment>()
const [checkoutItems, setCheckoutItems] = useState<CartItem[]>([]);
const router = Taro.getCurrentInstance().router; const router = Taro.getCurrentInstance().router;
const goodsId = router?.params?.goodsId; const goodsId = router?.params?.goodsId;
const { const {
cartItems
cartItems,
removeFromCart
} = useCart(); } = useCart();
const reload = async () => { const reload = async () => {
@ -34,6 +36,25 @@ const OrderConfirm = () => {
} }
} }
// 加载结算商品数据
const loadCheckoutItems = () => {
try {
const checkoutData = Taro.getStorageSync('checkout_items');
if (checkoutData) {
const items = JSON.parse(checkoutData) as CartItem[];
setCheckoutItems(items);
// 清除临时存储的数据
Taro.removeStorageSync('checkout_items');
} else {
// 如果没有选中商品数据,使用全部购物车商品
setCheckoutItems(cartItems);
}
} catch (error) {
console.error('加载结算商品失败:', error);
setCheckoutItems(cartItems);
}
};
/** /**
* *
*/ */
@ -47,9 +68,9 @@ const OrderConfirm = () => {
return; return;
} }
if (!cartItems || cartItems.length === 0) {
if (!checkoutItems || checkoutItems.length === 0) {
Taro.showToast({ Taro.showToast({
title: '购物车为空',
title: '没有要结算的商品',
icon: 'error' icon: 'error'
}); });
return; return;
@ -57,7 +78,7 @@ const OrderConfirm = () => {
// 构建订单数据 // 构建订单数据
const orderData = buildCartOrder( const orderData = buildCartOrder(
cartItems.map(item => ({
checkoutItems.map(item => ({
goodsId: item.goodsId!, goodsId: item.goodsId!,
quantity: item.quantity || 1 quantity: item.quantity || 1
})), })),
@ -72,7 +93,14 @@ const OrderConfirm = () => {
const paymentType = payment?.type === 0 ? PaymentType.BALANCE : PaymentType.WECHAT; const paymentType = payment?.type === 0 ? PaymentType.BALANCE : PaymentType.WECHAT;
// 执行支付 // 执行支付
await PaymentHandler.pay(orderData, paymentType);
await PaymentHandler.pay(orderData, paymentType, {
onSuccess: () => {
// 支付成功后,从购物车中移除已下单的商品
checkoutItems.forEach(item => {
removeFromCart(item.goodsId);
});
}
});
}; };
useEffect(() => { useEffect(() => {
@ -83,12 +111,21 @@ const OrderConfirm = () => {
console.error("Failed to fetch goods detail:", error); console.error("Failed to fetch goods detail:", error);
}); });
} }
reload().then()
}, [goodsId]);
reload().then();
loadCheckoutItems();
}, [goodsId, cartItems]);
// 计算总价
const getTotalPrice = () => {
return checkoutItems.reduce((total, item) => {
return total + (parseFloat(item.price) * item.quantity);
}, 0).toFixed(2);
};
if (!goods) {
return <div>...</div>;
}
// 计算商品总数量
const getTotalQuantity = () => {
return checkoutItems.reduce((total, item) => total + item.quantity, 0);
};
return ( return (
<div className={'order-confirm-page'}> <div className={'order-confirm-page'}>
@ -122,7 +159,7 @@ const OrderConfirm = () => {
</CellGroup> </CellGroup>
<CellGroup> <CellGroup>
{cartItems.map((goods, _) => (
{checkoutItems.map((goods, _) => (
<Cell key={goods.goodsId}> <Cell key={goods.goodsId}>
<Space> <Space>
<Image src={goods.image} mode={'aspectFill'} style={{ <Image src={goods.image} mode={'aspectFill'} style={{
@ -143,10 +180,10 @@ const OrderConfirm = () => {
</CellGroup> </CellGroup>
<CellGroup> <CellGroup>
<Cell title={'商品总价(共3件)'} extra={<View className={'font-medium'}>{'¥' + goods.price}</View>}/>
<Cell title={`商品总价(共${getTotalQuantity()}件)`} extra={<View className={'font-medium'}>{'¥' + getTotalPrice()}</View>}/>
<Cell title={'优惠券'} extra={( <Cell title={'优惠券'} extra={(
<View className={'flex justify-between items-center'}> <View className={'flex justify-between items-center'}>
<View className={'text-red-500 text-sm mr-1'}>-10.00</View>
<View className={'text-red-500 text-sm mr-1'}>-0.00</View>
<ArrowRight className={'text-gray-400'} size={14}/> <ArrowRight className={'text-gray-400'} size={14}/>
</View> </View>
)}/> )}/>
@ -164,7 +201,7 @@ const OrderConfirm = () => {
<View className={'btn-bar flex justify-between items-center'}> <View className={'btn-bar flex justify-between items-center'}>
<div className={'flex justify-center items-center mx-4'}> <div className={'flex justify-center items-center mx-4'}>
<span className={'total-price text-sm text-gray-500'}></span> <span className={'total-price text-sm text-gray-500'}></span>
<span className={'text-red-500 text-xl font-bold'}>{goods.price}</span>
<span className={'text-red-500 text-xl font-bold'}>{getTotalPrice()}</span>
</div> </div>
<div className={'buy-btn mx-4'}> <div className={'buy-btn mx-4'}>
<Button type="success" size="large" onClick={onPay}></Button> <Button type="success" size="large" onClick={onPay}></Button>

31
src/shop/orderDetail/index.tsx

@ -3,20 +3,14 @@ import {Cell, CellGroup, Image, Space, Button} from '@nutui/nutui-react-taro'
import Taro from '@tarojs/taro' import Taro from '@tarojs/taro'
import {ShopOrder} from "@/api/shop/shopOrder/model"; import {ShopOrder} from "@/api/shop/shopOrder/model";
import {getShopOrder} from "@/api/shop/shopOrder"; import {getShopOrder} from "@/api/shop/shopOrder";
import {listOrderGoods} from "@/api/system/orderGoods";
import {OrderGoods} from "@/api/system/orderGoods/model";
import {getShopGoods} from "@/api/shop/shopGoods";
import {listShopOrderGoods} from "@/api/shop/shopOrderGoods";
import {ShopOrderGoods} from "@/api/shop/shopOrderGoods/model";
import dayjs from "dayjs"; import dayjs from "dayjs";
import './index.scss' import './index.scss'
interface OrderGoodsDetail extends OrderGoods {
goodsName?: string;
goodsImage?: string;
}
const OrderDetail = () => { const OrderDetail = () => {
const [order, setOrder] = useState<ShopOrder | null>(null); const [order, setOrder] = useState<ShopOrder | null>(null);
const [orderGoodsList, setOrderGoodsList] = useState<OrderGoodsDetail[]>([]);
const [orderGoodsList, setOrderGoodsList] = useState<ShopOrderGoods[]>([]);
const router = Taro.getCurrentInstance().router; const router = Taro.getCurrentInstance().router;
const orderId = router?.params?.orderId; const orderId = router?.params?.orderId;
@ -55,19 +49,9 @@ const OrderDetail = () => {
setOrder(res); setOrder(res);
// 获取订单商品列表 // 获取订单商品列表
const goodsRes = await listOrderGoods({ orderId: Number(orderId) });
const goodsRes = await listShopOrderGoods({ orderId: Number(orderId) });
if (goodsRes && goodsRes.length > 0) { if (goodsRes && goodsRes.length > 0) {
const goodsDetailsPromises = goodsRes.map(async (item) => {
console.log(item,'item.>>>')
const shopGoods = await getShopGoods(Number(item.goodsId));
return {
...item,
goodsName: shopGoods?.name,
goodsImage: shopGoods?.image,
};
});
const detailedGoodsList = await Promise.all(goodsDetailsPromises);
setOrderGoodsList(detailedGoodsList);
setOrderGoodsList(goodsRes);
} }
}).catch(error => { }).catch(error => {
console.error("Failed to fetch order detail:", error); console.error("Failed to fetch order detail:", error);
@ -91,11 +75,12 @@ const OrderDetail = () => {
{orderGoodsList.map((item, index) => ( {orderGoodsList.map((item, index) => (
<Cell key={index}> <Cell key={index}>
<div className={'flex items-center'}> <div className={'flex items-center'}>
<Image src={item.goodsImage} width="80" height="80" lazyLoad={false} />
<Image src={item.image || '/default-goods.png'} width="80" height="80" lazyLoad={false} />
<div className={'ml-2'}> <div className={'ml-2'}>
<div className={'text-sm font-bold'}>{item.goodsName}</div> <div className={'text-sm font-bold'}>{item.goodsName}</div>
{item.spec && <div className={'text-gray-500 text-xs'}>{item.spec}</div>}
<div className={'text-gray-500 text-xs'}>{item.totalNum}</div> <div className={'text-gray-500 text-xs'}>{item.totalNum}</div>
<div className={'text-red-500 text-lg'}>{item.payPrice}</div>
<div className={'text-red-500 text-lg'}>{item.price}</div>
</div> </div>
</div> </div>
</Cell> </Cell>

2
src/utils/payment.ts

@ -97,7 +97,7 @@ export class PaymentHandler {
* *
*/ */
private static async handleWechatPay(result: WxPayResult): Promise<void> { private static async handleWechatPay(result: WxPayResult): Promise<void> {
if (!result || !result.prepayId) {
if (!result) {
throw new Error('微信支付参数错误'); throw new Error('微信支付参数错误');
} }

Loading…
Cancel
Save