import {useEffect, useState} from "react"; import { Image, Button, Cell, CellGroup, Input, Space, ActionSheet, Popup, InputNumber, ConfigProvider } from '@nutui/nutui-react-taro' import {Location, ArrowRight} from '@nutui/icons-react-taro' import Taro, {useDidShow} from '@tarojs/taro' import {ShopGoods} from "@/api/shop/shopGoods/model"; import {getShopGoods} from "@/api/shop/shopGoods"; import {View, Text} from '@tarojs/components'; import {listShopUserAddress} from "@/api/shop/shopUserAddress"; import {ShopUserAddress} from "@/api/shop/shopUserAddress/model"; import './index.scss' import Gap from "@/components/Gap"; import {selectPayment} from "@/api/system/payment"; import {Payment} from "@/api/system/payment/model"; import {PaymentHandler, PaymentType, buildSingleGoodsOrder} from "@/utils/payment"; import OrderConfirmSkeleton from "@/components/OrderConfirmSkeleton"; import CouponList from "@/components/CouponList"; import {CouponCardProps} from "@/components/CouponCard"; import {getMyAvailableCoupons} from "@/api/shop/shopUserCoupon"; import { transformCouponData, calculateCouponDiscount, isCouponUsable, getCouponUnusableReason, sortCoupons, filterUsableCoupons, filterUnusableCoupons } from "@/utils/couponUtils"; const OrderConfirm = () => { const [goods, setGoods] = useState(null); const [address, setAddress] = useState() const [payments, setPayments] = useState([]) const [payment, setPayment] = useState() const [isVisible, setIsVisible] = useState(false) const [quantity, setQuantity] = useState(1) const [orderRemark, setOrderRemark] = useState('') const [loading, setLoading] = useState(true) const [error, setError] = useState('') const [payLoading, setPayLoading] = useState(false) // InputNumber 主题配置 const customTheme = { nutuiInputnumberButtonWidth: '28px', nutuiInputnumberButtonHeight: '28px', nutuiInputnumberInputWidth: '40px', nutuiInputnumberInputHeight: '28px', nutuiInputnumberInputBorderRadius: '4px', nutuiInputnumberButtonBorderRadius: '4px', } // 优惠券相关状态 const [selectedCoupon, setSelectedCoupon] = useState(null) const [couponVisible, setCouponVisible] = useState(false) const [availableCoupons, setAvailableCoupons] = useState([]) const [couponLoading, setCouponLoading] = useState(false) const router = Taro.getCurrentInstance().router; const goodsId = router?.params?.goodsId; // 计算商品总价 const getGoodsTotal = () => { if (!goods) return 0 return parseFloat(goods.price || '0') * quantity } // 计算优惠券折扣 const getCouponDiscount = () => { if (!selectedCoupon || !goods) return 0 const total = getGoodsTotal() return calculateCouponDiscount(selectedCoupon, total) } // 计算实付金额 const getFinalPrice = () => { const total = getGoodsTotal() const discount = getCouponDiscount() return Math.max(0, total - discount) } const handleSelect = (item: any) => { setPayment(payments.find(payment => payment.name === item.name)) setIsVisible(false) } // 处理数量变化 const handleQuantityChange = (value: string | number) => { const newQuantity = typeof value === 'string' ? parseInt(value) || 1 : value const finalQuantity = Math.max(1, Math.min(newQuantity, goods?.stock || 999)) setQuantity(finalQuantity) // 数量变化时,重新排序优惠券并检查当前选中的优惠券是否还可用 if (availableCoupons.length > 0) { const newTotal = parseFloat(goods?.price || '0') * finalQuantity const sortedCoupons = sortCoupons(availableCoupons, newTotal) setAvailableCoupons(sortedCoupons) // 检查当前选中的优惠券是否还可用 if (selectedCoupon && !isCouponUsable(selectedCoupon, newTotal)) { setSelectedCoupon(null) Taro.showToast({ title: '当前优惠券不满足使用条件,已自动取消', icon: 'none' }) } } } // 处理优惠券选择 const handleCouponSelect = (coupon: CouponCardProps) => { const total = getGoodsTotal() // 检查是否可用 if (!isCouponUsable(coupon, total)) { const reason = getCouponUnusableReason(coupon, total) Taro.showToast({ title: reason || '优惠券不可用', icon: 'none' }) return } setSelectedCoupon(coupon) setCouponVisible(false) Taro.showToast({ title: '优惠券选择成功', icon: 'success' }) } // 取消选择优惠券 const handleCouponCancel = () => { setSelectedCoupon(null) Taro.showToast({ title: '已取消使用优惠券', icon: 'success' }) } // 加载用户优惠券 const loadUserCoupons = async () => { try { setCouponLoading(true) // 使用新的API获取可用优惠券 const res = await getMyAvailableCoupons() if (res && res.length > 0) { // 转换数据格式 const transformedCoupons = res.map(transformCouponData) // 按优惠金额排序 const total = getGoodsTotal() const sortedCoupons = sortCoupons(transformedCoupons, total) setAvailableCoupons(sortedCoupons) console.log('加载优惠券成功:', { originalData: res, transformedData: transformedCoupons, sortedData: sortedCoupons }) } else { setAvailableCoupons([]) console.log('暂无可用优惠券') } } catch (error) { console.error('加载优惠券失败:', error) setAvailableCoupons([]) Taro.showToast({ title: '加载优惠券失败', icon: 'none' }) } finally { setCouponLoading(false) } } /** * 统一支付入口 */ const onPay = async (goods: ShopGoods) => { try { setPayLoading(true) // 基础校验 if (!address) { Taro.showToast({ title: '请选择收货地址', icon: 'error' }) return; } if (!payment) { Taro.showToast({ title: '请选择支付方式', icon: 'error' }) return; } // 库存校验 if (goods.stock !== undefined && quantity > goods.stock) { Taro.showToast({ title: '商品库存不足', icon: 'error' }) return; } // 构建订单数据 const orderData = buildSingleGoodsOrder( goods.goodsId!, quantity, address.id, { comments: goods.name, deliveryType: 0, buyerRemarks: orderRemark, couponId: selectedCoupon ? selectedCoupon.id : undefined } ); // 根据支付方式选择支付类型 const paymentType = payment.type === 0 ? PaymentType.BALANCE : PaymentType.WECHAT; // 执行支付 await PaymentHandler.pay(orderData, paymentType); Taro.showToast({ title: '支付成功', icon: 'success' }) } catch (error) { console.error('支付失败:', error) Taro.showToast({ title: '支付失败,请重试', icon: 'error' }) } finally { setPayLoading(false) } }; // 统一的数据加载函数 const loadAllData = async () => { try { setLoading(true) setError('') // 分别加载数据,避免类型推断问题 let goodsRes: ShopGoods | null = null if (goodsId) { goodsRes = await getShopGoods(Number(goodsId)) } const [addressRes, paymentRes] = await Promise.all([ listShopUserAddress({isDefault: true}), selectPayment({}) ]) // 设置商品信息 if (goodsRes) { setGoods(goodsRes) } // 设置默认收货地址 if (addressRes && addressRes.length > 0) { setAddress(addressRes[0]) } // 设置支付方式 if (paymentRes && paymentRes.length > 0) { setPayments(paymentRes.map((d) => ({ type: d.type, name: d.name }))) setPayment(paymentRes[0]) } // 加载优惠券(在商品信息加载完成后) if (goodsRes) { await loadUserCoupons() } } catch (err) { console.error('加载数据失败:', err) setError('加载数据失败,请重试') } finally { setLoading(false) } } useDidShow(() => { loadAllData() }) useEffect(() => { loadAllData() }, [goodsId]); // 重新加载数据 const handleRetry = () => { loadAllData() } // 错误状态 if (error) { return ( {error} ) } // 加载状态 if (loading || !goods) { return } return (
{ address && ( Taro.navigateTo({url: '/user/address/index'})}> 送至 {address.province} {address.city} {address.region} {address.address} {address.name} {address.phone} ) } {!address && ( Taro.navigateTo({url: '/user/address/index'})}> 添加收货地址 )} {goods.name} 80g/袋 ¥{goods.price} {goods.stock !== undefined && ( 库存 {goods.stock} 件 )} {payment?.name} )} onClick={() => setIsVisible(true)} /> ¥{getGoodsTotal().toFixed(2)}} /> {selectedCoupon ? `-¥${getCouponDiscount().toFixed(2)}` : '暂未使用'} )} onClick={() => setCouponVisible(true)} /> 已优惠 ¥{getCouponDiscount().toFixed(2)} 实付 ¥{getFinalPrice().toFixed(2)} )}/> setOrderRemark(value)} maxLength={100} /> )}/> {/* 支付方式选择 */} setIsVisible(false)} /> {/* 优惠券选择弹窗 */} setCouponVisible(false)} style={{height: '60vh'}} > 选择优惠券 {couponLoading ? ( 加载优惠券中... ) : ( <> {selectedCoupon && ( 当前使用 {selectedCoupon.title} -¥{calculateCouponDiscount(selectedCoupon, getGoodsTotal()).toFixed(2)} )} {(() => { const total = getGoodsTotal() const usableCoupons = filterUsableCoupons(availableCoupons, total) const unusableCoupons = filterUnusableCoupons(availableCoupons, total) return ( <> {unusableCoupons.length > 0 && ( ({ ...coupon, status: 2 as const }))} layout="vertical" showEmpty={false} /> )} ) })()} )}
实付金额: ¥{getFinalPrice().toFixed(2)} {selectedCoupon && ( 已优惠 ¥{getCouponDiscount().toFixed(2)} )}
); }; export default OrderConfirm;