forked from gxwebsoft/mp-10550
feat(rider): 添加配送员模块和订单图片保存功能
- 新增配送员首页界面,包含订单管理、工资明细、配送小区、仓库地址等功能入口 - 实现小程序码保存到相册功能,支持权限检查和错误处理 - 添加相册写入权限配置和图片下载临时路径处理 - 修复订单列表商品信息显示问题,优化支付流程 - 更新首页轮播图广告代码,调整用户中心网格布局 - 增加订单页面返回时的数据刷新机制,提升用户体验
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import {Avatar, Cell, Space, Empty, Tabs, Button, TabPane, Image, Dialog} from '@nutui/nutui-react-taro'
|
||||
import {useEffect, useState, useCallback, CSSProperties} from "react";
|
||||
import {useEffect, useState, useCallback, useRef, CSSProperties} from "react";
|
||||
import {View, Text} from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro';
|
||||
import {InfiniteLoading} from '@nutui/nutui-react-taro'
|
||||
@@ -80,7 +80,8 @@ const tabs = [
|
||||
|
||||
// 扩展订单接口,包含商品信息
|
||||
interface OrderWithGoods extends ShopOrder {
|
||||
orderGoods?: ShopOrderGoods[];
|
||||
// 避免与 ShopOrder.orderGoods (OrderGoods[]) 冲突:这里使用单独字段保存接口返回的商品明细
|
||||
orderGoodsList?: ShopOrderGoods[];
|
||||
}
|
||||
|
||||
interface OrderListProps {
|
||||
@@ -92,8 +93,9 @@ interface OrderListProps {
|
||||
|
||||
function OrderList(props: OrderListProps) {
|
||||
const [list, setList] = useState<OrderWithGoods[]>([])
|
||||
const [page, setPage] = useState(1)
|
||||
const pageRef = useRef(1)
|
||||
const [hasMore, setHasMore] = useState(true)
|
||||
const [payingOrderId, setPayingOrderId] = useState<number | null>(null)
|
||||
// 根据传入的statusFilter设置初始tab索引
|
||||
const getInitialTabIndex = () => {
|
||||
if (props.searchParams?.statusFilter !== undefined) {
|
||||
@@ -183,7 +185,7 @@ function OrderList(props: OrderListProps) {
|
||||
const reload = useCallback(async (resetPage = false, targetPage?: number) => {
|
||||
setLoading(true);
|
||||
setError(null); // 清除之前的错误
|
||||
const currentPage = resetPage ? 1 : (targetPage || page);
|
||||
const currentPage = resetPage ? 1 : (targetPage || pageRef.current);
|
||||
const statusParams = getOrderStatusParams(tapIndex);
|
||||
// 合并搜索条件,tab的statusFilter优先级更高
|
||||
const searchConditions: any = {
|
||||
@@ -205,7 +207,6 @@ function OrderList(props: OrderListProps) {
|
||||
|
||||
try {
|
||||
const res = await pageShopOrder(searchConditions);
|
||||
let newList: OrderWithGoods[];
|
||||
|
||||
if (res?.list && res?.list.length > 0) {
|
||||
// 批量获取订单商品信息,限制并发数量
|
||||
@@ -214,19 +215,19 @@ function OrderList(props: OrderListProps) {
|
||||
|
||||
for (let i = 0; i < res.list.length; i += batchSize) {
|
||||
const batch = res.list.slice(i, i + batchSize);
|
||||
const batchResults = await Promise.all(
|
||||
const batchResults = await Promise.all(
|
||||
batch.map(async (order) => {
|
||||
try {
|
||||
const orderGoods = await listShopOrderGoods({orderId: order.orderId});
|
||||
return {
|
||||
...order,
|
||||
orderGoods: orderGoods || []
|
||||
orderGoodsList: orderGoods || []
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('获取订单商品失败:', error);
|
||||
return {
|
||||
...order,
|
||||
orderGoods: []
|
||||
orderGoodsList: []
|
||||
};
|
||||
}
|
||||
})
|
||||
@@ -248,7 +249,7 @@ function OrderList(props: OrderListProps) {
|
||||
setHasMore(false);
|
||||
}
|
||||
|
||||
setPage(currentPage);
|
||||
pageRef.current = currentPage;
|
||||
setLoading(false);
|
||||
} catch (error) {
|
||||
console.error('加载订单失败:', error);
|
||||
@@ -260,14 +261,14 @@ function OrderList(props: OrderListProps) {
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
}, [tapIndex, page, props.searchParams]); // 移除 list 依赖
|
||||
}, [tapIndex, props.searchParams]); // 移除 list/page 依赖,避免useEffect触发循环
|
||||
|
||||
const reloadMore = useCallback(async () => {
|
||||
if (loading || !hasMore) return; // 防止重复加载
|
||||
const nextPage = page + 1;
|
||||
setPage(nextPage);
|
||||
const nextPage = pageRef.current + 1;
|
||||
pageRef.current = nextPage;
|
||||
await reload(false, nextPage);
|
||||
}, [loading, hasMore, page, reload]);
|
||||
}, [loading, hasMore, reload]);
|
||||
|
||||
// 确认收货 - 显示确认对话框
|
||||
const confirmReceive = (order: ShopOrder) => {
|
||||
@@ -325,7 +326,7 @@ function OrderList(props: OrderListProps) {
|
||||
});
|
||||
|
||||
// 更新本地状态
|
||||
setDataSource(prev => prev.map(item =>
|
||||
setList(prev => prev.map(item =>
|
||||
item.orderId === order.orderId ? {...item, orderStatus: 4} : item
|
||||
));
|
||||
|
||||
@@ -351,49 +352,23 @@ function OrderList(props: OrderListProps) {
|
||||
};
|
||||
|
||||
// 再次购买 (已完成状态)
|
||||
const buyAgain = (order: ShopOrder) => {
|
||||
const buyAgain = (order: OrderWithGoods) => {
|
||||
console.log('再次购买:', order);
|
||||
goTo(`/shop/orderConfirm/index?goodsId=${order.orderGoods[0].goodsId}`)
|
||||
const goodsId = order.orderGoodsList?.[0]?.goodsId
|
||||
if (!goodsId) {
|
||||
Taro.showToast({
|
||||
title: '订单商品信息缺失',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
goTo(`/shop/orderConfirm/index?goodsId=${goodsId}`)
|
||||
// Taro.showToast({
|
||||
// title: '再次购买功能开发中',
|
||||
// icon: 'none'
|
||||
// });
|
||||
};
|
||||
|
||||
// 评价商品 (已完成状态)
|
||||
const evaluateGoods = (order: ShopOrder) => {
|
||||
// 跳转到评价页面
|
||||
Taro.navigateTo({
|
||||
url: `/user/order/evaluate/index?orderId=${order.orderId}&orderNo=${order.orderNo}`
|
||||
});
|
||||
};
|
||||
|
||||
// 查看进度 (退款/售后状态)
|
||||
const viewProgress = (order: ShopOrder) => {
|
||||
// 根据订单状态确定售后类型
|
||||
let afterSaleType = 'refund' // 默认退款
|
||||
|
||||
if (order.orderStatus === 4) {
|
||||
afterSaleType = 'refund' // 退款申请中
|
||||
} else if (order.orderStatus === 7) {
|
||||
afterSaleType = 'return' // 退货申请中
|
||||
}
|
||||
|
||||
// 跳转到售后进度页面
|
||||
Taro.navigateTo({
|
||||
url: `/user/order/progress/index?orderId=${order.orderId}&orderNo=${order.orderNo}&type=${afterSaleType}`
|
||||
});
|
||||
};
|
||||
|
||||
// 撤销申请 (退款/售后状态)
|
||||
const cancelApplication = (order: ShopOrder) => {
|
||||
console.log('撤销申请:', order);
|
||||
Taro.showToast({
|
||||
title: '撤销申请功能开发中',
|
||||
icon: 'none'
|
||||
});
|
||||
};
|
||||
|
||||
// 取消订单
|
||||
const cancelOrder = (order: ShopOrder) => {
|
||||
setOrderToCancel(order);
|
||||
@@ -437,7 +412,7 @@ function OrderList(props: OrderListProps) {
|
||||
};
|
||||
|
||||
// 立即支付
|
||||
const payOrder = async (order: ShopOrder) => {
|
||||
const payOrder = async (order: OrderWithGoods) => {
|
||||
try {
|
||||
if (!order.orderId || !order.orderNo) {
|
||||
Taro.showToast({
|
||||
@@ -447,6 +422,11 @@ function OrderList(props: OrderListProps) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (payingOrderId === order.orderId) {
|
||||
return;
|
||||
}
|
||||
setPayingOrderId(order.orderId);
|
||||
|
||||
// 检查订单是否已过期
|
||||
if (order.createTime && isPaymentExpired(order.createTime)) {
|
||||
Taro.showToast({
|
||||
@@ -475,11 +455,34 @@ function OrderList(props: OrderListProps) {
|
||||
|
||||
Taro.showLoading({title: '发起支付...'});
|
||||
|
||||
// 构建商品数据
|
||||
const goodsItems = order.orderGoods?.map(goods => ({
|
||||
goodsId: goods.goodsId,
|
||||
quantity: goods.totalNum || 1
|
||||
})) || [];
|
||||
// 构建商品数据:优先使用列表已加载的商品信息;缺失时再补拉一次,避免goodsItems为空导致后端拒绝/再次支付失败
|
||||
let orderGoods = order.orderGoodsList || [];
|
||||
if (!orderGoods.length) {
|
||||
try {
|
||||
orderGoods = (await listShopOrderGoods({orderId: order.orderId})) || [];
|
||||
} catch (e) {
|
||||
// 继续走下面的校验提示
|
||||
console.error('补拉订单商品失败:', e);
|
||||
}
|
||||
}
|
||||
|
||||
const goodsItems = orderGoods
|
||||
.filter(g => !!g.goodsId)
|
||||
.map(goods => ({
|
||||
goodsId: goods.goodsId as number,
|
||||
quantity: goods.totalNum || 1,
|
||||
// 若后端按SKU计算价格/库存,补齐SKU/规格信息更安全
|
||||
skuId: (goods as any).skuId,
|
||||
specInfo: (goods as any).spec || (goods as any).specInfo
|
||||
}));
|
||||
|
||||
if (!goodsItems.length) {
|
||||
Taro.showToast({
|
||||
title: '订单商品信息缺失,请稍后重试',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 对于已存在的订单,我们需要重新发起支付
|
||||
// 构建支付请求数据,包含完整的商品信息
|
||||
@@ -488,7 +491,13 @@ function OrderList(props: OrderListProps) {
|
||||
orderNo: order.orderNo,
|
||||
goodsItems: goodsItems,
|
||||
addressId: order.addressId,
|
||||
payType: PaymentType.WECHAT
|
||||
payType: PaymentType.WECHAT,
|
||||
// 尽量携带原订单信息,避免后端重新计算/校验不一致(如使用了优惠券/自提等)
|
||||
couponId: order.couponId,
|
||||
deliveryType: order.deliveryType,
|
||||
selfTakeMerchantId: order.selfTakeMerchantId,
|
||||
comments: order.comments,
|
||||
title: order.title
|
||||
};
|
||||
|
||||
console.log('重新支付数据:', paymentData);
|
||||
@@ -506,13 +515,26 @@ function OrderList(props: OrderListProps) {
|
||||
}
|
||||
|
||||
// 调用微信支付
|
||||
await Taro.requestPayment({
|
||||
timeStamp: result.timeStamp,
|
||||
nonceStr: result.nonceStr,
|
||||
package: result.package,
|
||||
signType: (result.signType || 'MD5') as 'MD5' | 'HMAC-SHA256',
|
||||
paySign: result.paySign,
|
||||
});
|
||||
try {
|
||||
await Taro.requestPayment({
|
||||
timeStamp: result.timeStamp,
|
||||
nonceStr: result.nonceStr,
|
||||
package: result.package,
|
||||
signType: (result.signType || 'MD5') as 'MD5' | 'HMAC-SHA256',
|
||||
paySign: result.paySign,
|
||||
});
|
||||
} catch (payError: any) {
|
||||
const msg: string = payError?.errMsg || payError?.message || '';
|
||||
if (msg.includes('cancel')) {
|
||||
// 用户主动取消,不当作“失败”强提示
|
||||
Taro.showToast({
|
||||
title: '已取消支付',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw payError;
|
||||
}
|
||||
|
||||
// 支付成功
|
||||
Taro.showToast({
|
||||
@@ -533,13 +555,14 @@ function OrderList(props: OrderListProps) {
|
||||
console.error('支付失败:', error);
|
||||
|
||||
let errorMessage = '支付失败,请重试';
|
||||
if (error.message) {
|
||||
if (error.message.includes('cancel')) {
|
||||
const rawMsg: string = error?.errMsg || error?.message || '';
|
||||
if (rawMsg) {
|
||||
if (rawMsg.includes('cancel')) {
|
||||
errorMessage = '用户取消支付';
|
||||
} else if (error.message.includes('余额不足')) {
|
||||
} else if (rawMsg.includes('余额不足')) {
|
||||
errorMessage = '账户余额不足';
|
||||
} else {
|
||||
errorMessage = error.message;
|
||||
errorMessage = rawMsg;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -549,13 +572,13 @@ function OrderList(props: OrderListProps) {
|
||||
});
|
||||
} finally {
|
||||
Taro.hideLoading();
|
||||
setPayingOrderId(null);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
void reload(true); // 首次加载或tab切换时重置页码
|
||||
}, [tapIndex]); // 只监听tapIndex变化,避免reload依赖循环
|
||||
void reload(true); // 首次加载、tab切换或搜索条件变化时重置页码
|
||||
}, [reload]);
|
||||
|
||||
// 监听外部statusFilter变化,同步更新tab索引
|
||||
useEffect(() => {
|
||||
@@ -705,8 +728,8 @@ function OrderList(props: OrderListProps) {
|
||||
|
||||
{/* 商品信息 */}
|
||||
<View className={'goods-info'}>
|
||||
{item.orderGoods && item.orderGoods.length > 0 ? (
|
||||
item.orderGoods.map((goods, goodsIndex) => (
|
||||
{item.orderGoodsList && item.orderGoodsList.length > 0 ? (
|
||||
item.orderGoodsList.map((goods, goodsIndex) => (
|
||||
<View key={goodsIndex} className={'flex items-center mb-2'}>
|
||||
<Image
|
||||
src={goods.image || '/default-goods.png'}
|
||||
|
||||
Reference in New Issue
Block a user