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) {
|
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
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',
|
'orderDetail/index',
|
||||||
'goodsDetail/index',
|
'goodsDetail/index',
|
||||||
'orderConfirm/index',
|
'orderConfirm/index',
|
||||||
|
'orderConfirmCart/index'
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -115,10 +115,17 @@ function Cart() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 这里可以跳转到结算页面
|
// 获取选中的商品
|
||||||
Taro.showToast({
|
const selectedCartItems = cartItems.filter((item: CartItem) =>
|
||||||
title: '跳转到结算页面',
|
selectedItems.includes(item.goodsId)
|
||||||
icon: 'success'
|
);
|
||||||
|
|
||||||
|
// 将选中的商品信息存储到本地,供结算页面使用
|
||||||
|
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 {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 => {
|
pageShopOrder({ page: currentPage, ...params }).then(async res => {
|
||||||
let newList: ShopOrder[] | undefined = [];
|
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'
|
src='/default-goods.png'
|
||||||
size={'45'}
|
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>
|
||||||
|
|||||||
@@ -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
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 {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()
|
reload().then();
|
||||||
}, [goodsId]);
|
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 (
|
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>
|
||||||
|
|||||||
@@ -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 {listShopOrderGoods} from "@/api/shop/shopOrderGoods";
|
||||||
import {OrderGoods} from "@/api/system/orderGoods/model";
|
import {ShopOrderGoods} from "@/api/shop/shopOrderGoods/model";
|
||||||
import {getShopGoods} from "@/api/shop/shopGoods";
|
|
||||||
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) => {
|
setOrderGoodsList(goodsRes);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}).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>
|
||||||
|
|||||||
@@ -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('微信支付参数错误');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user