forked from gxwebsoft/mp-10550
优化下单流程
This commit is contained in:
103
docs/ORDER_IMPROVEMENTS.md
Normal file
103
docs/ORDER_IMPROVEMENTS.md
Normal file
@@ -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. 添加订单操作的二次确认
|
||||
@@ -118,7 +118,7 @@ export interface WxPayResult {
|
||||
*/
|
||||
export async function createOrder(data: OrderCreateRequest) {
|
||||
const res = await request.post<ApiResult<WxPayResult>>(
|
||||
'/shop/shop-order/create',
|
||||
'http://127.0.0.1:9200/api/shop/shop-order',
|
||||
data
|
||||
);
|
||||
if (res.code === 0) {
|
||||
@@ -128,7 +128,7 @@ export async function createOrder(data: OrderCreateRequest) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改订单
|
||||
* 修复订单支付状态
|
||||
*/
|
||||
export async function repairOrder(data: ShopOrder) {
|
||||
const res = await request.put<ApiResult<unknown>>(
|
||||
|
||||
101
src/api/shop/shopOrderGoods/index.ts
Normal file
101
src/api/shop/shopOrderGoods/index.ts
Normal file
@@ -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
Normal file
70
src/api/shop/shopOrderGoods/model/index.ts
Normal file
@@ -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;
|
||||
}
|
||||
@@ -45,6 +45,7 @@ export default defineAppConfig({
|
||||
'orderDetail/index',
|
||||
'goodsDetail/index',
|
||||
'orderConfirm/index',
|
||||
'orderConfirmCart/index'
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
@@ -115,10 +115,17 @@ function Cart() {
|
||||
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'
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -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 Taro from '@tarojs/taro';
|
||||
import {InfiniteLoading} from '@nutui/nutui-react-taro'
|
||||
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 {listShopOrderGoods} from "@/api/shop/shopOrderGoods";
|
||||
import {ShopOrderGoods} from "@/api/shop/shopOrderGoods/model";
|
||||
import {copyText} from "@/utils/common";
|
||||
|
||||
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 [hasMore, setHasMore] = useState(true)
|
||||
const [tapIndex, setTapIndex] = useState<string | number>('0')
|
||||
|
||||
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) => {
|
||||
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) {
|
||||
case '1': // 待付款
|
||||
params.payStatus = 0;
|
||||
@@ -77,13 +105,32 @@ function OrderList(props: any) {
|
||||
const reload = async (resetPage = false) => {
|
||||
const currentPage = resetPage ? 1 : page;
|
||||
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) {
|
||||
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);
|
||||
} else {
|
||||
newList = res?.list;
|
||||
newList = [];
|
||||
setHasMore(false);
|
||||
}
|
||||
setList(newList || []);
|
||||
@@ -96,6 +143,39 @@ function OrderList(props: any) {
|
||||
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(() => {
|
||||
reload(true); // 首次加载或tab切换时重置页码
|
||||
}, [tapIndex]); // 监听tapIndex变化
|
||||
@@ -140,33 +220,69 @@ function OrderList(props: any) {
|
||||
</>
|
||||
}
|
||||
>
|
||||
{props.data?.map(item => {
|
||||
{list?.map((item, index) => {
|
||||
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'}>
|
||||
<div className={'order-no flex justify-between'}>
|
||||
<span className={'text-gray-700 font-bold text-sm'}
|
||||
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
|
||||
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={'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'}>
|
||||
<Avatar
|
||||
src='34'
|
||||
size={'45'}
|
||||
src='/default-goods.png'
|
||||
size={'50'}
|
||||
shape={'square'}
|
||||
/>
|
||||
<div className={'ml-2'}>{item.realName}</div>
|
||||
</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 className={' w-full text-right'}>实付金额:¥{item.payPrice}</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={'w-full text-right'}>实付金额:¥{item.payPrice}</div>
|
||||
|
||||
{/* 操作按钮 */}
|
||||
<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>
|
||||
</Cell>
|
||||
|
||||
@@ -78,7 +78,7 @@ function Order() {
|
||||
{/*订单列表*/}
|
||||
{
|
||||
list.length > 0 && (
|
||||
<OrderList data={list}/>
|
||||
<OrderList data={list} onReload={reload}/>
|
||||
)
|
||||
}
|
||||
</>
|
||||
|
||||
54
src/pages/order/test-order.tsx
Normal file
54
src/pages/order/test-order.tsx
Normal file
@@ -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;
|
||||
@@ -8,7 +8,7 @@ import {View} from '@tarojs/components';
|
||||
import {listShopUserAddress} from "@/api/shop/shopUserAddress";
|
||||
import {ShopUserAddress} from "@/api/shop/shopUserAddress/model";
|
||||
import './index.scss'
|
||||
import {useCart} from "@/hooks/useCart";
|
||||
import {useCart, CartItem} from "@/hooks/useCart";
|
||||
import Gap from "@/components/Gap";
|
||||
import {createOrder} from "@/api/shop/shopOrder";
|
||||
import {OrderCreateRequest} from "@/api/shop/shopOrder/model";
|
||||
@@ -19,11 +19,13 @@ const OrderConfirm = () => {
|
||||
const [goods, setGoods] = useState<ShopGoods | null>(null);
|
||||
const [address, setAddress] = useState<ShopUserAddress>()
|
||||
const [payment, setPayment] = useState<Payment>()
|
||||
const [checkoutItems, setCheckoutItems] = useState<CartItem[]>([]);
|
||||
const router = Taro.getCurrentInstance().router;
|
||||
const goodsId = router?.params?.goodsId;
|
||||
|
||||
const {
|
||||
cartItems
|
||||
cartItems,
|
||||
removeFromCart
|
||||
} = useCart();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!cartItems || cartItems.length === 0) {
|
||||
if (!checkoutItems || checkoutItems.length === 0) {
|
||||
Taro.showToast({
|
||||
title: '购物车为空',
|
||||
title: '没有要结算的商品',
|
||||
icon: 'error'
|
||||
});
|
||||
return;
|
||||
@@ -57,7 +78,7 @@ const OrderConfirm = () => {
|
||||
|
||||
// 构建订单数据
|
||||
const orderData = buildCartOrder(
|
||||
cartItems.map(item => ({
|
||||
checkoutItems.map(item => ({
|
||||
goodsId: item.goodsId!,
|
||||
quantity: item.quantity || 1
|
||||
})),
|
||||
@@ -72,7 +93,14 @@ const OrderConfirm = () => {
|
||||
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(() => {
|
||||
@@ -83,12 +111,21 @@ const OrderConfirm = () => {
|
||||
console.error("Failed to fetch goods detail:", error);
|
||||
});
|
||||
}
|
||||
reload().then()
|
||||
}, [goodsId]);
|
||||
reload().then();
|
||||
loadCheckoutItems();
|
||||
}, [goodsId, cartItems]);
|
||||
|
||||
if (!goods) {
|
||||
return <div>加载中...</div>;
|
||||
}
|
||||
// 计算总价
|
||||
const getTotalPrice = () => {
|
||||
return checkoutItems.reduce((total, item) => {
|
||||
return total + (parseFloat(item.price) * item.quantity);
|
||||
}, 0).toFixed(2);
|
||||
};
|
||||
|
||||
// 计算商品总数量
|
||||
const getTotalQuantity = () => {
|
||||
return checkoutItems.reduce((total, item) => total + item.quantity, 0);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={'order-confirm-page'}>
|
||||
@@ -122,7 +159,7 @@ const OrderConfirm = () => {
|
||||
</CellGroup>
|
||||
|
||||
<CellGroup>
|
||||
{cartItems.map((goods, _) => (
|
||||
{checkoutItems.map((goods, _) => (
|
||||
<Cell key={goods.goodsId}>
|
||||
<Space>
|
||||
<Image src={goods.image} mode={'aspectFill'} style={{
|
||||
@@ -143,10 +180,10 @@ const OrderConfirm = () => {
|
||||
</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={(
|
||||
<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}/>
|
||||
</View>
|
||||
)}/>
|
||||
@@ -164,7 +201,7 @@ const OrderConfirm = () => {
|
||||
<View className={'btn-bar flex justify-between items-center'}>
|
||||
<div className={'flex justify-center items-center mx-4'}>
|
||||
<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 className={'buy-btn mx-4'}>
|
||||
<Button type="success" size="large" onClick={onPay}>立即付款</Button>
|
||||
|
||||
@@ -3,20 +3,14 @@ import {Cell, CellGroup, Image, Space, Button} from '@nutui/nutui-react-taro'
|
||||
import Taro from '@tarojs/taro'
|
||||
import {ShopOrder} from "@/api/shop/shopOrder/model";
|
||||
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 './index.scss'
|
||||
|
||||
interface OrderGoodsDetail extends OrderGoods {
|
||||
goodsName?: string;
|
||||
goodsImage?: string;
|
||||
}
|
||||
|
||||
const OrderDetail = () => {
|
||||
const [order, setOrder] = useState<ShopOrder | null>(null);
|
||||
const [orderGoodsList, setOrderGoodsList] = useState<OrderGoodsDetail[]>([]);
|
||||
const [orderGoodsList, setOrderGoodsList] = useState<ShopOrderGoods[]>([]);
|
||||
const router = Taro.getCurrentInstance().router;
|
||||
const orderId = router?.params?.orderId;
|
||||
|
||||
@@ -55,19 +49,9 @@ const OrderDetail = () => {
|
||||
setOrder(res);
|
||||
|
||||
// 获取订单商品列表
|
||||
const goodsRes = await listOrderGoods({ orderId: Number(orderId) });
|
||||
const goodsRes = await listShopOrderGoods({ orderId: Number(orderId) });
|
||||
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 => {
|
||||
console.error("Failed to fetch order detail:", error);
|
||||
@@ -91,11 +75,12 @@ const OrderDetail = () => {
|
||||
{orderGoodsList.map((item, index) => (
|
||||
<Cell key={index}>
|
||||
<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={'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-red-500 text-lg'}>¥{item.payPrice}</div>
|
||||
<div className={'text-red-500 text-lg'}>¥{item.price}</div>
|
||||
</div>
|
||||
</div>
|
||||
</Cell>
|
||||
|
||||
@@ -97,7 +97,7 @@ export class PaymentHandler {
|
||||
* 处理微信支付
|
||||
*/
|
||||
private static async handleWechatPay(result: WxPayResult): Promise<void> {
|
||||
if (!result || !result.prepayId) {
|
||||
if (!result) {
|
||||
throw new Error('微信支付参数错误');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user