From 0b83e67ac1b5a613e3ccf5c9c362bd26a0b825d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Sat, 23 Aug 2025 05:54:10 +0800 Subject: [PATCH] =?UTF-8?q?refactor(invite):=20=E9=87=8D=E6=9E=84=E9=82=80?= =?UTF-8?q?=E8=AF=B7=E4=BA=8C=E7=BB=B4=E7=A0=81=E7=94=9F=E6=88=90=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 优化了 generateMiniProgramCode 函数,直接返回完整的二维码 URL - 移除了未使用的 getInviteStats 函数调用 - 增加了二维码加载失败时的错误处理和重新生成逻辑 -调整了页面布局,隐藏了邀请统计数据部分 --- src/api/invite/index.ts | 30 ++-- src/api/shop/shopOrder/model/index.ts | 3 + src/dealer/qrcode/index.tsx | 224 +++++++++++++----------- src/shop/orderDetail/index.tsx | 84 +++++---- src/user/order/components/OrderList.tsx | 123 ++++++++++++- 5 files changed, 307 insertions(+), 157 deletions(-) diff --git a/src/api/invite/index.ts b/src/api/invite/index.ts index c8c007a..2d72ec2 100644 --- a/src/api/invite/index.ts +++ b/src/api/invite/index.ts @@ -1,6 +1,5 @@ import request from '@/utils/request'; import type { ApiResult, PageResult } from '@/api'; -import { SERVER_API_URL } from '@/utils/server'; /** * 小程序码生成参数 @@ -96,14 +95,13 @@ export interface InviteRecordParam { * 生成小程序码 */ export async function generateMiniProgramCode(data: MiniProgramCodeParam) { - const res = await request.get>( - '/wx-login/getOrderQRCodeUnlimited/' + data.scene - ); - console.log(res,'res....') - if (res.code === 0) { - return res.data; + try { + const url = '/wx-login/getOrderQRCodeUnlimited/' + data.scene; + // 由于接口直接返回图片buffer,我们直接构建完整的URL + return `${API_BASE_URL}${url}`; + } catch (error: any) { + throw new Error(error.message || '生成小程序码失败'); } - return Promise.reject(new Error(res.message)); } /** @@ -126,7 +124,7 @@ export async function generateInviteCode(inviterId: number) { */ export async function createInviteRelation(data: InviteRelationParam) { const res = await request.post>( - SERVER_API_URL + '/invite/create-relation', + '/invite/create-relation', data ); if (res.code === 0) { @@ -140,7 +138,7 @@ export async function createInviteRelation(data: InviteRelationParam) { */ export async function processInviteScene(scene: string, userId: number) { const res = await request.post>( - SERVER_API_URL + '/invite/process-scene', + '/invite/process-scene', { scene, userId } ); if (res.code === 0) { @@ -154,7 +152,7 @@ export async function processInviteScene(scene: string, userId: number) { */ export async function getInviteStats(inviterId: number) { const res = await request.get>( - SERVER_API_URL + `/invite/stats/${inviterId}` + `/invite/stats/${inviterId}` ); if (res.code === 0) { return res.data; @@ -167,7 +165,7 @@ export async function getInviteStats(inviterId: number) { */ export async function pageInviteRecords(params: InviteRecordParam) { const res = await request.get>>( - SERVER_API_URL + '/invite/records/page', + '/invite/records/page', params ); if (res.code === 0) { @@ -181,7 +179,7 @@ export async function pageInviteRecords(params: InviteRecordParam) { */ export async function getMyInviteRecords(params: InviteRecordParam) { const res = await request.get>>( - SERVER_API_URL + '/invite/my-records', + '/invite/my-records', params ); if (res.code === 0) { @@ -195,7 +193,7 @@ export async function getMyInviteRecords(params: InviteRecordParam) { */ export async function validateInviteCode(scene: string) { const res = await request.post>( - SERVER_API_URL + '/invite/validate-code', + '/invite/validate-code', { scene } ); if (res.code === 0) { @@ -209,7 +207,7 @@ export async function validateInviteCode(scene: string) { */ export async function updateInviteStatus(inviteId: number, status: 'registered' | 'activated') { const res = await request.put>( - SERVER_API_URL + `/invite/update-status/${inviteId}`, + `/invite/update-status/${inviteId}`, { status } ); if (res.code === 0) { @@ -229,7 +227,7 @@ export async function getInviteRanking(params?: { limit?: number; period?: 'day' successCount: number; conversionRate: number; }>>>( - SERVER_API_URL + '/invite/ranking', + '/invite/ranking', params ); if (res.code === 0) { diff --git a/src/api/shop/shopOrder/model/index.ts b/src/api/shop/shopOrder/model/index.ts index 8a6b167..92709f8 100644 --- a/src/api/shop/shopOrder/model/index.ts +++ b/src/api/shop/shopOrder/model/index.ts @@ -1,4 +1,5 @@ import type { PageParam } from '@/api/index'; +import {OrderGoods} from "@/api/system/orderGoods/model"; /** * 订单 @@ -144,6 +145,8 @@ export interface ShopOrder { selfTakeCode?: string; // 是否已收到赠品 hasTakeGift?: string; + // 订单商品项 + orderGoods?: OrderGoods[]; } /** diff --git a/src/dealer/qrcode/index.tsx b/src/dealer/qrcode/index.tsx index 31c2e6d..789c6fa 100644 --- a/src/dealer/qrcode/index.tsx +++ b/src/dealer/qrcode/index.tsx @@ -4,61 +4,66 @@ import {Button, Loading} from '@nutui/nutui-react-taro' import {Share, Download, Copy, QrCode} from '@nutui/icons-react-taro' import Taro from '@tarojs/taro' import {useDealerUser} from '@/hooks/useDealerUser' -import {generateInviteCode, getInviteStats} from '@/api/invite' -import type {InviteStats} from '@/api/invite' +import {generateInviteCode} from '@/api/invite' +// import type {InviteStats} from '@/api/invite' import {businessGradients} from '@/styles/gradients' const DealerQrcode: React.FC = () => { const [miniProgramCodeUrl, setMiniProgramCodeUrl] = useState('') const [loading, setLoading] = useState(false) - const [inviteStats, setInviteStats] = useState(null) - const [statsLoading, setStatsLoading] = useState(false) + // const [inviteStats, setInviteStats] = useState(null) + // const [statsLoading, setStatsLoading] = useState(false) const {dealerUser} = useDealerUser() // 生成小程序码 const generateMiniProgramCode = async () => { - if (!dealerUser?.userId) return + if (!dealerUser?.userId) { + return + } try { setLoading(true) // 生成邀请小程序码 const codeUrl = await generateInviteCode(dealerUser.userId) - console.log('小程序码生成成功:', codeUrl) + if (codeUrl) { setMiniProgramCodeUrl(codeUrl) + } else { + throw new Error('返回的小程序码URL为空') } - } catch (error) { - console.error('生成小程序码失败:', error) + } catch (error: any) { Taro.showToast({ - title: '生成小程序码失败', + title: error.message || '生成小程序码失败', icon: 'error' }) + // 清空之前的二维码 + setMiniProgramCodeUrl('') } finally { setLoading(false) } } // 获取邀请统计数据 - const fetchInviteStats = async () => { - if (!dealerUser?.userId) return - - try { - setStatsLoading(true) - const stats = await getInviteStats(dealerUser.userId) - stats && setInviteStats(stats) - } catch (error) { - console.error('获取邀请统计失败:', error) - } finally { - setStatsLoading(false) - } - } + // const fetchInviteStats = async () => { + // if (!dealerUser?.userId) return + // + // try { + // setStatsLoading(true) + // const stats = await getInviteStats(dealerUser.userId) + // stats && setInviteStats(stats) + // } catch (error) { + // // 静默处理错误,不影响用户体验 + // } finally { + // setStatsLoading(false) + // } + // } // 初始化生成小程序码和获取统计数据 useEffect(() => { if (dealerUser?.userId) { generateMiniProgramCode() - fetchInviteStats() + // fetchInviteStats() } }, [dealerUser?.userId]) @@ -189,11 +194,6 @@ const DealerQrcode: React.FC = () => { {/* 小程序码展示区 */} - {/**/} {loading ? ( @@ -207,6 +207,20 @@ const DealerQrcode: React.FC = () => { src={miniProgramCodeUrl} className="w-full h-full" mode="aspectFit" + onError={() => { + Taro.showModal({ + title: '二维码加载失败', + content: '请检查网络连接或联系管理员', + showCancel: true, + confirmText: '重新生成', + success: (res) => { + if (res.confirm) { + generateMiniProgramCode(); + } + } + }); + }} + /> ) : ( @@ -227,9 +241,11 @@ const DealerQrcode: React.FC = () => { 扫码加入我的团队 - + 好友扫描小程序码即可直接进入小程序并建立邀请关系 + + @@ -273,7 +289,7 @@ const DealerQrcode: React.FC = () => { {/* 推广说明 */} - + 推广说明 @@ -298,82 +314,82 @@ const DealerQrcode: React.FC = () => { {/* 邀请统计数据 */} - - 我的邀请数据 - {statsLoading ? ( - - - 加载中... - - ) : inviteStats ? ( - - - - - {inviteStats.totalInvites || 0} - - 总邀请数 - - - - {inviteStats.successfulRegistrations || 0} - - 成功注册 - - + {/**/} + {/* 我的邀请数据*/} + {/* {statsLoading ? (*/} + {/* */} + {/* */} + {/* 加载中...*/} + {/* */} + {/* ) : inviteStats ? (*/} + {/* */} + {/* */} + {/* */} + {/* */} + {/* {inviteStats.totalInvites || 0}*/} + {/* */} + {/* 总邀请数*/} + {/* */} + {/* */} + {/* */} + {/* {inviteStats.successfulRegistrations || 0}*/} + {/* */} + {/* 成功注册*/} + {/* */} + {/* */} - - - - {inviteStats.conversionRate ? `${(inviteStats.conversionRate * 100).toFixed(1)}%` : '0%'} - - 转化率 - - - - {inviteStats.todayInvites || 0} - - 今日邀请 - - + {/* */} + {/* */} + {/* */} + {/* {inviteStats.conversionRate ? `${(inviteStats.conversionRate * 100).toFixed(1)}%` : '0%'}*/} + {/* */} + {/* 转化率*/} + {/* */} + {/* */} + {/* */} + {/* {inviteStats.todayInvites || 0}*/} + {/* */} + {/* 今日邀请*/} + {/* */} + {/* */} - {/* 邀请来源统计 */} - {inviteStats.sourceStats && inviteStats.sourceStats.length > 0 && ( - - 邀请来源分布 - - {inviteStats.sourceStats.map((source, index) => ( - - - - {source.source} - - - {source.count} - - {source.conversionRate ? `${(source.conversionRate * 100).toFixed(1)}%` : '0%'} - - - - ))} - - - )} - - ) : ( - - 暂无邀请数据 - - - )} - + {/* /!* 邀请来源统计 *!/*/} + {/* {inviteStats.sourceStats && inviteStats.sourceStats.length > 0 && (*/} + {/* */} + {/* 邀请来源分布*/} + {/* */} + {/* {inviteStats.sourceStats.map((source, index) => (*/} + {/* */} + {/* */} + {/* */} + {/* {source.source}*/} + {/* */} + {/* */} + {/* {source.count}*/} + {/* */} + {/* {source.conversionRate ? `${(source.conversionRate * 100).toFixed(1)}%` : '0%'}*/} + {/* */} + {/* */} + {/* */} + {/* ))}*/} + {/* */} + {/* */} + {/* )}*/} + {/* */} + {/* ) : (*/} + {/* */} + {/* 暂无邀请数据*/} + {/* */} + {/* 刷新数据*/} + {/* */} + {/* */} + {/* )}*/} + {/**/} ) diff --git a/src/shop/orderDetail/index.tsx b/src/shop/orderDetail/index.tsx index fcd3e47..c51595e 100644 --- a/src/shop/orderDetail/index.tsx +++ b/src/shop/orderDetail/index.tsx @@ -1,6 +1,7 @@ import {useEffect, useState} from "react"; import {Cell, CellGroup, Image, Space, Button} from '@nutui/nutui-react-taro' import Taro from '@tarojs/taro' +import {View} from '@tarojs/components' import {ShopOrder} from "@/api/shop/shopOrder/model"; import {getShopOrder, updateShopOrder} from "@/api/shop/shopOrder"; import {listShopOrderGoods} from "@/api/shop/shopOrderGoods"; @@ -27,7 +28,7 @@ const OrderDetail = () => { }); // 更新本地状态 - setOrder(prev => prev ? { ...prev, orderStatus: 2 } : null); + setOrder(prev => prev ? {...prev, orderStatus: 2} : null); Taro.showToast({ title: '订单已自动取消', @@ -65,25 +66,33 @@ const OrderDetail = () => { const getPayTypeText = (payType?: number) => { switch (payType) { - case 0: return '余额支付'; - case 1: return '微信支付'; - case 102: return '微信Native'; - case 2: return '会员卡支付'; - case 3: return '支付宝'; - case 4: return '现金'; - case 5: return 'POS机'; - default: return '未知支付方式'; + case 0: + return '余额支付'; + case 1: + return '微信支付'; + case 102: + return '微信Native'; + case 2: + return '会员卡支付'; + case 3: + return '支付宝'; + case 4: + return '现金'; + case 5: + return 'POS机'; + default: + return '未知支付方式'; } }; useEffect(() => { if (orderId) { - console.log('shop-goods',orderId) + console.log('shop-goods', orderId) getShopOrder(Number(orderId)).then(async (res) => { setOrder(res); // 获取订单商品列表 - const goodsRes = await listShopOrderGoods({ orderId: Number(orderId) }); + const goodsRes = await listShopOrderGoods({orderId: Number(orderId)}); if (goodsRes && goodsRes.length > 0) { setOrderGoodsList(goodsRes); } @@ -101,7 +110,7 @@ const OrderDetail = () => {
{/* 支付倒计时显示 - 详情页实时更新 */} {!order.payStatus && order.orderStatus !== 2 && ( -
+
{
)} - - - - - - - + {orderGoodsList.map((item, index) => (
- +
{item.goodsName}
{item.spec &&
规格:{item.spec}
} @@ -135,25 +138,36 @@ const OrderDetail = () => { ))} - - - - + + + + - - - + + + + -
- - {!order.payStatus && } - {!order.payStatus && } - {order.orderStatus === 1 && } - {order.deliveryStatus === 20 && } - -
+ {order.payStatus && ( + + + + + )} + + + + + {!order.payStatus && } + {!order.payStatus && } + {order.orderStatus === 1 && } + {order.deliveryStatus === 20 && + } + + +
); }; diff --git a/src/user/order/components/OrderList.tsx b/src/user/order/components/OrderList.tsx index 642dbef..609fb1c 100644 --- a/src/user/order/components/OrderList.tsx +++ b/src/user/order/components/OrderList.tsx @@ -4,12 +4,13 @@ import {View, Text} from '@tarojs/components' import Taro from '@tarojs/taro'; import {InfiniteLoading} from '@nutui/nutui-react-taro' import dayjs from "dayjs"; -import {pageShopOrder, updateShopOrder} from "@/api/shop/shopOrder"; +import {pageShopOrder, updateShopOrder, createOrder} from "@/api/shop/shopOrder"; import {ShopOrder, ShopOrderParam} from "@/api/shop/shopOrder/model"; import {listShopOrderGoods} from "@/api/shop/shopOrderGoods"; import {ShopOrderGoods} from "@/api/shop/shopOrderGoods/model"; import {copyText} from "@/utils/common"; import PaymentCountdown from "@/components/PaymentCountdown"; +import {PaymentType} from "@/utils/payment"; // 判断订单是否支付已过期 const isPaymentExpired = (createTime: string, timeoutHours: number = 24): boolean => { @@ -314,6 +315,124 @@ function OrderList(props: OrderListProps) { } }; + // 立即支付 + const payOrder = async (order: ShopOrder) => { + try { + if (!order.orderId || !order.orderNo) { + Taro.showToast({ + title: '订单信息错误', + icon: 'error' + }); + return; + } + + // 检查订单是否已过期 + if (order.createTime && isPaymentExpired(order.createTime)) { + Taro.showToast({ + title: '订单已过期,无法支付', + icon: 'error' + }); + return; + } + + // 检查订单状态 + if (order.payStatus) { + Taro.showToast({ + title: '订单已支付', + icon: 'none' + }); + return; + } + + if (order.orderStatus === 2) { + Taro.showToast({ + title: '订单已取消,无法支付', + icon: 'error' + }); + return; + } + + Taro.showLoading({ title: '发起支付...' }); + + // 构建商品数据 + const goodsItems = order.orderGoods?.map(goods => ({ + goodsId: goods.goodsId, + quantity: goods.totalNum || 1 + })) || []; + + // 对于已存在的订单,我们需要重新发起支付 + // 构建支付请求数据,包含完整的商品信息 + const paymentData = { + orderId: order.orderId, + orderNo: order.orderNo, + goodsItems: goodsItems, + addressId: order.addressId, + payType: PaymentType.WECHAT + }; + + console.log('重新支付数据:', paymentData); + + // 直接调用createOrder API进行重新支付 + const result = await createOrder(paymentData as any); + + if (!result) { + throw new Error('支付发起失败'); + } + + // 验证微信支付必要参数 + if (!result.timeStamp || !result.nonceStr || !result.package || !result.paySign) { + throw new Error('微信支付参数不完整'); + } + + // 调用微信支付 + await Taro.requestPayment({ + timeStamp: result.timeStamp, + nonceStr: result.nonceStr, + package: result.package, + signType: result.signType as any, + paySign: result.paySign, + }); + + // 支付成功 + Taro.showToast({ + title: '支付成功', + icon: 'success' + }); + + // 重新加载订单列表 + void reload(true); + props.onReload?.(); + + // 跳转到订单页面 + setTimeout(() => { + Taro.navigateTo({ url: '/user/order/order' }); + }, 2000); + + } catch (error: any) { + console.error('支付失败:', error); + + let errorMessage = '支付失败,请重试'; + if (error.message) { + if (error.message.includes('cancel')) { + errorMessage = '用户取消支付'; + } else if (error.message.includes('余额不足')) { + errorMessage = '账户余额不足'; + } else { + errorMessage = error.message; + } + } + + Taro.showToast({ + title: errorMessage, + icon: 'error' + }); + } finally { + Taro.hideLoading(); + } + }; + + + useEffect(() => { void reload(true); // 首次加载或tab切换时重置页码 }, [tapIndex]); // 监听tapIndex变化 @@ -499,7 +618,7 @@ function OrderList(props: OrderListProps) { }}>取消订单 )}