refactor(invite): 重构邀请二维码生成逻辑

- 优化了 generateMiniProgramCode 函数,直接返回完整的二维码 URL
- 移除了未使用的 getInviteStats 函数调用
- 增加了二维码加载失败时的错误处理和重新生成逻辑
-调整了页面布局,隐藏了邀请统计数据部分
This commit is contained in:
2025-08-23 05:54:10 +08:00
parent a15333da07
commit 0b83e67ac1
5 changed files with 307 additions and 157 deletions

View File

@@ -1,6 +1,5 @@
import request from '@/utils/request'; import request from '@/utils/request';
import type { ApiResult, PageResult } from '@/api'; 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) { export async function generateMiniProgramCode(data: MiniProgramCodeParam) {
const res = await request.get<ApiResult<string>>( try {
'/wx-login/getOrderQRCodeUnlimited/' + data.scene const url = '/wx-login/getOrderQRCodeUnlimited/' + data.scene;
); // 由于接口直接返回图片buffer我们直接构建完整的URL
console.log(res,'res....') return `${API_BASE_URL}${url}`;
if (res.code === 0) { } catch (error: any) {
return res.data; 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) { export async function createInviteRelation(data: InviteRelationParam) {
const res = await request.post<ApiResult<unknown>>( const res = await request.post<ApiResult<unknown>>(
SERVER_API_URL + '/invite/create-relation', '/invite/create-relation',
data data
); );
if (res.code === 0) { if (res.code === 0) {
@@ -140,7 +138,7 @@ export async function createInviteRelation(data: InviteRelationParam) {
*/ */
export async function processInviteScene(scene: string, userId: number) { export async function processInviteScene(scene: string, userId: number) {
const res = await request.post<ApiResult<unknown>>( const res = await request.post<ApiResult<unknown>>(
SERVER_API_URL + '/invite/process-scene', '/invite/process-scene',
{ scene, userId } { scene, userId }
); );
if (res.code === 0) { if (res.code === 0) {
@@ -154,7 +152,7 @@ export async function processInviteScene(scene: string, userId: number) {
*/ */
export async function getInviteStats(inviterId: number) { export async function getInviteStats(inviterId: number) {
const res = await request.get<ApiResult<InviteStats>>( const res = await request.get<ApiResult<InviteStats>>(
SERVER_API_URL + `/invite/stats/${inviterId}` `/invite/stats/${inviterId}`
); );
if (res.code === 0) { if (res.code === 0) {
return res.data; return res.data;
@@ -167,7 +165,7 @@ export async function getInviteStats(inviterId: number) {
*/ */
export async function pageInviteRecords(params: InviteRecordParam) { export async function pageInviteRecords(params: InviteRecordParam) {
const res = await request.get<ApiResult<PageResult<InviteRecord>>>( const res = await request.get<ApiResult<PageResult<InviteRecord>>>(
SERVER_API_URL + '/invite/records/page', '/invite/records/page',
params params
); );
if (res.code === 0) { if (res.code === 0) {
@@ -181,7 +179,7 @@ export async function pageInviteRecords(params: InviteRecordParam) {
*/ */
export async function getMyInviteRecords(params: InviteRecordParam) { export async function getMyInviteRecords(params: InviteRecordParam) {
const res = await request.get<ApiResult<PageResult<InviteRecord>>>( const res = await request.get<ApiResult<PageResult<InviteRecord>>>(
SERVER_API_URL + '/invite/my-records', '/invite/my-records',
params params
); );
if (res.code === 0) { if (res.code === 0) {
@@ -195,7 +193,7 @@ export async function getMyInviteRecords(params: InviteRecordParam) {
*/ */
export async function validateInviteCode(scene: string) { export async function validateInviteCode(scene: string) {
const res = await request.post<ApiResult<{ valid: boolean; inviterId?: number; source?: string }>>( const res = await request.post<ApiResult<{ valid: boolean; inviterId?: number; source?: string }>>(
SERVER_API_URL + '/invite/validate-code', '/invite/validate-code',
{ scene } { scene }
); );
if (res.code === 0) { if (res.code === 0) {
@@ -209,7 +207,7 @@ export async function validateInviteCode(scene: string) {
*/ */
export async function updateInviteStatus(inviteId: number, status: 'registered' | 'activated') { export async function updateInviteStatus(inviteId: number, status: 'registered' | 'activated') {
const res = await request.put<ApiResult<unknown>>( const res = await request.put<ApiResult<unknown>>(
SERVER_API_URL + `/invite/update-status/${inviteId}`, `/invite/update-status/${inviteId}`,
{ status } { status }
); );
if (res.code === 0) { if (res.code === 0) {
@@ -229,7 +227,7 @@ export async function getInviteRanking(params?: { limit?: number; period?: 'day'
successCount: number; successCount: number;
conversionRate: number; conversionRate: number;
}>>>( }>>>(
SERVER_API_URL + '/invite/ranking', '/invite/ranking',
params params
); );
if (res.code === 0) { if (res.code === 0) {

View File

@@ -1,4 +1,5 @@
import type { PageParam } from '@/api/index'; import type { PageParam } from '@/api/index';
import {OrderGoods} from "@/api/system/orderGoods/model";
/** /**
* 订单 * 订单
@@ -144,6 +145,8 @@ export interface ShopOrder {
selfTakeCode?: string; selfTakeCode?: string;
// 是否已收到赠品 // 是否已收到赠品
hasTakeGift?: string; hasTakeGift?: string;
// 订单商品项
orderGoods?: OrderGoods[];
} }
/** /**

View File

@@ -4,61 +4,66 @@ import {Button, Loading} from '@nutui/nutui-react-taro'
import {Share, Download, Copy, QrCode} from '@nutui/icons-react-taro' import {Share, Download, Copy, QrCode} from '@nutui/icons-react-taro'
import Taro from '@tarojs/taro' import Taro from '@tarojs/taro'
import {useDealerUser} from '@/hooks/useDealerUser' import {useDealerUser} from '@/hooks/useDealerUser'
import {generateInviteCode, getInviteStats} from '@/api/invite' import {generateInviteCode} from '@/api/invite'
import type {InviteStats} from '@/api/invite' // import type {InviteStats} from '@/api/invite'
import {businessGradients} from '@/styles/gradients' import {businessGradients} from '@/styles/gradients'
const DealerQrcode: React.FC = () => { const DealerQrcode: React.FC = () => {
const [miniProgramCodeUrl, setMiniProgramCodeUrl] = useState<string>('') const [miniProgramCodeUrl, setMiniProgramCodeUrl] = useState<string>('')
const [loading, setLoading] = useState<boolean>(false) const [loading, setLoading] = useState<boolean>(false)
const [inviteStats, setInviteStats] = useState<InviteStats | null>(null) // const [inviteStats, setInviteStats] = useState<InviteStats | null>(null)
const [statsLoading, setStatsLoading] = useState<boolean>(false) // const [statsLoading, setStatsLoading] = useState<boolean>(false)
const {dealerUser} = useDealerUser() const {dealerUser} = useDealerUser()
// 生成小程序码 // 生成小程序码
const generateMiniProgramCode = async () => { const generateMiniProgramCode = async () => {
if (!dealerUser?.userId) return if (!dealerUser?.userId) {
return
}
try { try {
setLoading(true) setLoading(true)
// 生成邀请小程序码 // 生成邀请小程序码
const codeUrl = await generateInviteCode(dealerUser.userId) const codeUrl = await generateInviteCode(dealerUser.userId)
console.log('小程序码生成成功:', codeUrl)
if (codeUrl) { if (codeUrl) {
setMiniProgramCodeUrl(codeUrl) setMiniProgramCodeUrl(codeUrl)
} else {
throw new Error('返回的小程序码URL为空')
} }
} catch (error) { } catch (error: any) {
console.error('生成小程序码失败:', error)
Taro.showToast({ Taro.showToast({
title: '生成小程序码失败', title: error.message || '生成小程序码失败',
icon: 'error' icon: 'error'
}) })
// 清空之前的二维码
setMiniProgramCodeUrl('')
} finally { } finally {
setLoading(false) setLoading(false)
} }
} }
// 获取邀请统计数据 // 获取邀请统计数据
const fetchInviteStats = async () => { // const fetchInviteStats = async () => {
if (!dealerUser?.userId) return // if (!dealerUser?.userId) return
//
try { // try {
setStatsLoading(true) // setStatsLoading(true)
const stats = await getInviteStats(dealerUser.userId) // const stats = await getInviteStats(dealerUser.userId)
stats && setInviteStats(stats) // stats && setInviteStats(stats)
} catch (error) { // } catch (error) {
console.error('获取邀请统计失败:', error) // // 静默处理错误,不影响用户体验
} finally { // } finally {
setStatsLoading(false) // setStatsLoading(false)
} // }
} // }
// 初始化生成小程序码和获取统计数据 // 初始化生成小程序码和获取统计数据
useEffect(() => { useEffect(() => {
if (dealerUser?.userId) { if (dealerUser?.userId) {
generateMiniProgramCode() generateMiniProgramCode()
fetchInviteStats() // fetchInviteStats()
} }
}, [dealerUser?.userId]) }, [dealerUser?.userId])
@@ -189,11 +194,6 @@ const DealerQrcode: React.FC = () => {
<View className="px-4"> <View className="px-4">
{/* 小程序码展示区 */} {/* 小程序码展示区 */}
{/*<Image*/}
{/* src={'http://127.0.0.1:9200/api/wx-login/getOrderQRCodeUnlimited/33103'}*/}
{/* className="w-full h-full"*/}
{/* mode="aspectFit"*/}
{/*/>*/}
<View className="bg-white rounded-2xl p-6 mb-6 shadow-sm"> <View className="bg-white rounded-2xl p-6 mb-6 shadow-sm">
<View className="text-center"> <View className="text-center">
{loading ? ( {loading ? (
@@ -207,6 +207,20 @@ const DealerQrcode: React.FC = () => {
src={miniProgramCodeUrl} src={miniProgramCodeUrl}
className="w-full h-full" className="w-full h-full"
mode="aspectFit" mode="aspectFit"
onError={() => {
Taro.showModal({
title: '二维码加载失败',
content: '请检查网络连接或联系管理员',
showCancel: true,
confirmText: '重新生成',
success: (res) => {
if (res.confirm) {
generateMiniProgramCode();
}
}
});
}}
/> />
</View> </View>
) : ( ) : (
@@ -227,9 +241,11 @@ const DealerQrcode: React.FC = () => {
<View className="text-lg font-semibold text-gray-800 mb-2"> <View className="text-lg font-semibold text-gray-800 mb-2">
</View> </View>
<View className="text-sm text-gray-500 mb-6"> <View className="text-sm text-gray-500 mb-4">
</View> </View>
</View> </View>
</View> </View>
@@ -273,7 +289,7 @@ const DealerQrcode: React.FC = () => {
</View> </View>
{/* 推广说明 */} {/* 推广说明 */}
<View className="bg-white rounded-2xl p-4 mt-6"> <View className="bg-white rounded-2xl p-4 mt-6 hidden">
<Text className="font-semibold text-gray-800 mb-3">广</Text> <Text className="font-semibold text-gray-800 mb-3">广</Text>
<View className="space-y-2"> <View className="space-y-2">
<View className="flex items-start"> <View className="flex items-start">
@@ -298,82 +314,82 @@ const DealerQrcode: React.FC = () => {
</View> </View>
{/* 邀请统计数据 */} {/* 邀请统计数据 */}
<View className="bg-white rounded-2xl p-4 mt-4 mb-6"> {/*<View className="bg-white rounded-2xl p-4 mt-4 mb-6">*/}
<Text className="font-semibold text-gray-800 mb-3"></Text> {/* <Text className="font-semibold text-gray-800 mb-3">我的邀请数据</Text>*/}
{statsLoading ? ( {/* {statsLoading ? (*/}
<View className="flex items-center justify-center py-8"> {/* <View className="flex items-center justify-center py-8">*/}
<Loading/> {/* <Loading/>*/}
<Text className="text-gray-500 mt-2">...</Text> {/* <Text className="text-gray-500 mt-2">加载中...</Text>*/}
</View> {/* </View>*/}
) : inviteStats ? ( {/* ) : inviteStats ? (*/}
<View className="space-y-4"> {/* <View className="space-y-4">*/}
<View className="grid grid-cols-2 gap-4"> {/* <View className="grid grid-cols-2 gap-4">*/}
<View className="text-center"> {/* <View className="text-center">*/}
<Text className="text-2xl font-bold text-blue-500"> {/* <Text className="text-2xl font-bold text-blue-500">*/}
{inviteStats.totalInvites || 0} {/* {inviteStats.totalInvites || 0}*/}
</Text> {/* </Text>*/}
<Text className="text-sm text-gray-500"></Text> {/* <Text className="text-sm text-gray-500">总邀请数</Text>*/}
</View> {/* </View>*/}
<View className="text-center"> {/* <View className="text-center">*/}
<Text className="text-2xl font-bold text-green-500"> {/* <Text className="text-2xl font-bold text-green-500">*/}
{inviteStats.successfulRegistrations || 0} {/* {inviteStats.successfulRegistrations || 0}*/}
</Text> {/* </Text>*/}
<Text className="text-sm text-gray-500"></Text> {/* <Text className="text-sm text-gray-500">成功注册</Text>*/}
</View> {/* </View>*/}
</View> {/* </View>*/}
<View className="grid grid-cols-2 gap-4"> {/* <View className="grid grid-cols-2 gap-4">*/}
<View className="text-center"> {/* <View className="text-center">*/}
<Text className="text-2xl font-bold text-purple-500"> {/* <Text className="text-2xl font-bold text-purple-500">*/}
{inviteStats.conversionRate ? `${(inviteStats.conversionRate * 100).toFixed(1)}%` : '0%'} {/* {inviteStats.conversionRate ? `${(inviteStats.conversionRate * 100).toFixed(1)}%` : '0%'}*/}
</Text> {/* </Text>*/}
<Text className="text-sm text-gray-500"></Text> {/* <Text className="text-sm text-gray-500">转化率</Text>*/}
</View> {/* </View>*/}
<View className="text-center"> {/* <View className="text-center">*/}
<Text className="text-2xl font-bold text-orange-500"> {/* <Text className="text-2xl font-bold text-orange-500">*/}
{inviteStats.todayInvites || 0} {/* {inviteStats.todayInvites || 0}*/}
</Text> {/* </Text>*/}
<Text className="text-sm text-gray-500"></Text> {/* <Text className="text-sm text-gray-500">今日邀请</Text>*/}
</View> {/* </View>*/}
</View> {/* </View>*/}
{/* 邀请来源统计 */} {/* /!* 邀请来源统计 *!/*/}
{inviteStats.sourceStats && inviteStats.sourceStats.length > 0 && ( {/* {inviteStats.sourceStats && inviteStats.sourceStats.length > 0 && (*/}
<View className="mt-4"> {/* <View className="mt-4">*/}
<Text className="text-sm font-medium text-gray-700 mb-2"></Text> {/* <Text className="text-sm font-medium text-gray-700 mb-2">邀请来源分布</Text>*/}
<View className="space-y-2"> {/* <View className="space-y-2">*/}
{inviteStats.sourceStats.map((source, index) => ( {/* {inviteStats.sourceStats.map((source, index) => (*/}
<View key={index} className="flex items-center justify-between py-2 px-3 bg-gray-50 rounded-lg"> {/* <View key={index} className="flex items-center justify-between py-2 px-3 bg-gray-50 rounded-lg">*/}
<View className="flex items-center"> {/* <View className="flex items-center">*/}
<View className="w-3 h-3 rounded-full bg-blue-500 mr-2"></View> {/* <View className="w-3 h-3 rounded-full bg-blue-500 mr-2"></View>*/}
<Text className="text-sm text-gray-700">{source.source}</Text> {/* <Text className="text-sm text-gray-700">{source.source}</Text>*/}
</View> {/* </View>*/}
<View className="text-right"> {/* <View className="text-right">*/}
<Text className="text-sm font-medium text-gray-800">{source.count}</Text> {/* <Text className="text-sm font-medium text-gray-800">{source.count}</Text>*/}
<Text className="text-xs text-gray-500"> {/* <Text className="text-xs text-gray-500">*/}
{source.conversionRate ? `${(source.conversionRate * 100).toFixed(1)}%` : '0%'} {/* {source.conversionRate ? `${(source.conversionRate * 100).toFixed(1)}%` : '0%'}*/}
</Text> {/* </Text>*/}
</View> {/* </View>*/}
</View> {/* </View>*/}
))} {/* ))}*/}
</View> {/* </View>*/}
</View> {/* </View>*/}
)} {/* )}*/}
</View> {/* </View>*/}
) : ( {/* ) : (*/}
<View className="text-center py-8"> {/* <View className="text-center py-8">*/}
<View className="text-gray-500"></View> {/* <View className="text-gray-500">暂无邀请数据</View>*/}
<Button {/* <Button*/}
size="small" {/* size="small"*/}
type="primary" {/* type="primary"*/}
className="mt-2" {/* className="mt-2"*/}
onClick={fetchInviteStats} {/* onClick={fetchInviteStats}*/}
> {/* >*/}
{/* 刷新数据*/}
</Button> {/* </Button>*/}
</View> {/* </View>*/}
)} {/* )}*/}
</View> {/*</View>*/}
</View> </View>
</View> </View>
) )

View File

@@ -1,6 +1,7 @@
import {useEffect, useState} from "react"; import {useEffect, useState} from "react";
import {Cell, CellGroup, Image, Space, Button} from '@nutui/nutui-react-taro' import {Cell, CellGroup, Image, Space, Button} from '@nutui/nutui-react-taro'
import Taro from '@tarojs/taro' import Taro from '@tarojs/taro'
import {View} from '@tarojs/components'
import {ShopOrder} from "@/api/shop/shopOrder/model"; import {ShopOrder} from "@/api/shop/shopOrder/model";
import {getShopOrder, updateShopOrder} from "@/api/shop/shopOrder"; import {getShopOrder, updateShopOrder} from "@/api/shop/shopOrder";
import {listShopOrderGoods} from "@/api/shop/shopOrderGoods"; 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({ Taro.showToast({
title: '订单已自动取消', title: '订单已自动取消',
@@ -65,25 +66,33 @@ const OrderDetail = () => {
const getPayTypeText = (payType?: number) => { const getPayTypeText = (payType?: number) => {
switch (payType) { switch (payType) {
case 0: return '余额支付'; case 0:
case 1: return '微信支付'; return '余额支付';
case 102: return '微信Native'; case 1:
case 2: return '会员卡支付'; return '微信支付';
case 3: return '支付宝'; case 102:
case 4: return '现金'; return '微信Native';
case 5: return 'POS机'; case 2:
default: return '未知支付方式'; return '会员卡支付';
case 3:
return '支付宝';
case 4:
return '现金';
case 5:
return 'POS机';
default:
return '未知支付方式';
} }
}; };
useEffect(() => { useEffect(() => {
if (orderId) { if (orderId) {
console.log('shop-goods',orderId) console.log('shop-goods', orderId)
getShopOrder(Number(orderId)).then(async (res) => { getShopOrder(Number(orderId)).then(async (res) => {
setOrder(res); setOrder(res);
// 获取订单商品列表 // 获取订单商品列表
const goodsRes = await listShopOrderGoods({ orderId: Number(orderId) }); const goodsRes = await listShopOrderGoods({orderId: Number(orderId)});
if (goodsRes && goodsRes.length > 0) { if (goodsRes && goodsRes.length > 0) {
setOrderGoodsList(goodsRes); setOrderGoodsList(goodsRes);
} }
@@ -101,7 +110,7 @@ const OrderDetail = () => {
<div className={'order-detail-page'}> <div className={'order-detail-page'}>
{/* 支付倒计时显示 - 详情页实时更新 */} {/* 支付倒计时显示 - 详情页实时更新 */}
{!order.payStatus && order.orderStatus !== 2 && ( {!order.payStatus && order.orderStatus !== 2 && (
<div className="order-detail-countdown flex justify-center p-4 bg-red-50 border-b border-red-100"> <div className="order-detail-countdown flex justify-center p-4 border-b border-gray-50">
<PaymentCountdown <PaymentCountdown
createTime={order.createTime} createTime={order.createTime}
payStatus={order.payStatus} payStatus={order.payStatus}
@@ -113,17 +122,11 @@ const OrderDetail = () => {
</div> </div>
)} )}
<CellGroup title="订单信息"> <CellGroup>
<Cell title="订单编号" description={order.orderNo} />
<Cell title="下单时间" description={dayjs(order.createTime).format('YYYY-MM-DD HH:mm:ss')} />
<Cell title="订单状态" description={getOrderStatusText(order)} />
</CellGroup>
<CellGroup title="商品信息">
{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.image || '/default-goods.png'} 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>} {item.spec && <div className={'text-gray-500 text-xs'}>{item.spec}</div>}
@@ -135,25 +138,36 @@ const OrderDetail = () => {
))} ))}
</CellGroup> </CellGroup>
<CellGroup title="收货信息"> <CellGroup>
<Cell title="收货人" description={order.realName} /> <Cell title="订单编号" description={order.orderNo}/>
<Cell title="手机号" description={order.phone} /> <Cell title="下单时间" description={dayjs(order.createTime).format('YYYY-MM-DD HH:mm:ss')}/>
<Cell title="收货地址" description={order.address} /> <Cell title="订单状态" description={getOrderStatusText(order)}/>
</CellGroup> </CellGroup>
<CellGroup title="支付信息"> <CellGroup>
<Cell title="支付方式" description={getPayTypeText(order.payType)} /> <Cell title="收货人" description={order.realName}/>
<Cell title="实付金额" description={`${order.payPrice}`} /> <Cell title="手机号" description={order.phone}/>
<Cell title="收货地址" description={order.address}/>
</CellGroup> </CellGroup>
<div className={'fixed-bottom'}> {order.payStatus && (
<Space> <CellGroup>
{!order.payStatus && <Button onClick={() => console.log('取消订单')}></Button>} <Cell title="支付方式" description={getPayTypeText(order.payType)}/>
{!order.payStatus && <Button type="primary" onClick={() => console.log('立即支付')}></Button>} <Cell title="实付金额" description={`${order.payPrice}`}/>
{order.orderStatus === 1 && <Button onClick={() => console.log('申请退款')}>退</Button>} </CellGroup>
{order.deliveryStatus === 20 && <Button type="primary" onClick={() => console.log('确认收货')}></Button>} )}
</Space>
</div> <View className={'h5-div fixed z-50 bg-white w-full bottom-0 left-0 pt-4 pb-5 border-t border-gray-200'}>
<View className={'flex justify-end px-4'}>
<Space>
{!order.payStatus && <Button onClick={() => console.log('取消订单')}></Button>}
{!order.payStatus && <Button type="primary" onClick={() => console.log('立即支付')}></Button>}
{order.orderStatus === 1 && <Button onClick={() => console.log('申请退款')}>退</Button>}
{order.deliveryStatus === 20 &&
<Button type="primary" onClick={() => console.log('确认收货')}></Button>}
</Space>
</View>
</View>
</div> </div>
); );
}; };

View File

@@ -4,12 +4,13 @@ import {View, Text} from '@tarojs/components'
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, updateShopOrder} from "@/api/shop/shopOrder"; import {pageShopOrder, updateShopOrder, createOrder} from "@/api/shop/shopOrder";
import {ShopOrder, ShopOrderParam} from "@/api/shop/shopOrder/model"; import {ShopOrder, ShopOrderParam} from "@/api/shop/shopOrder/model";
import {listShopOrderGoods} from "@/api/shop/shopOrderGoods"; import {listShopOrderGoods} from "@/api/shop/shopOrderGoods";
import {ShopOrderGoods} from "@/api/shop/shopOrderGoods/model"; import {ShopOrderGoods} from "@/api/shop/shopOrderGoods/model";
import {copyText} from "@/utils/common"; import {copyText} from "@/utils/common";
import PaymentCountdown from "@/components/PaymentCountdown"; import PaymentCountdown from "@/components/PaymentCountdown";
import {PaymentType} from "@/utils/payment";
// 判断订单是否支付已过期 // 判断订单是否支付已过期
const isPaymentExpired = (createTime: string, timeoutHours: number = 24): boolean => { 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(() => { useEffect(() => {
void reload(true); // 首次加载或tab切换时重置页码 void reload(true); // 首次加载或tab切换时重置页码
}, [tapIndex]); // 监听tapIndex变化 }, [tapIndex]); // 监听tapIndex变化
@@ -499,7 +618,7 @@ function OrderList(props: OrderListProps) {
}}></Button> }}></Button>
<Button size={'small'} type="primary" onClick={(e) => { <Button size={'small'} type="primary" onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
console.log('立即支付') void payOrder(item);
}}></Button> }}></Button>
</Space> </Space>
)} )}