refactor(shop): 移除水票套票商品配送时间选择功能

- 删除了配送时间相关的状态管理和日期选择器组件
- 移除了配送时间验证和格式化逻辑
- 更新了订单提交流程,不再传递配送时间参数
- 修改支付回调处理,支持自定义成功行为和跳转逻辑
- 简化了水票商品的购买流程,移除配送时间相关校验
This commit is contained in:
2026-03-09 12:17:29 +08:00
parent 58d3e884ab
commit 3248315f6e
2 changed files with 73 additions and 95 deletions

View File

@@ -1,4 +1,4 @@
import {useEffect, useMemo, useState} from "react"; import {useEffect, useState} from "react";
import { import {
Image, Image,
Button, Button,
@@ -9,7 +9,6 @@ import {
ActionSheet, ActionSheet,
Popup, Popup,
InputNumber, InputNumber,
DatePicker,
ConfigProvider ConfigProvider
} from '@nutui/nutui-react-taro' } from '@nutui/nutui-react-taro'
import {Location, ArrowRight} from '@nutui/icons-react-taro' import {Location, ArrowRight} from '@nutui/icons-react-taro'
@@ -39,7 +38,6 @@ import {
filterUsableCoupons, filterUsableCoupons,
filterUnusableCoupons filterUnusableCoupons
} from "@/utils/couponUtils"; } from "@/utils/couponUtils";
import dayjs from 'dayjs'
import type {ShopStore} from "@/api/shop/shopStore/model"; import type {ShopStore} from "@/api/shop/shopStore/model";
import {getShopStore, listShopStore} from "@/api/shop/shopStore"; import {getShopStore, listShopStore} from "@/api/shop/shopStore";
import {getSelectedStoreFromStorage, saveSelectedStoreToStorage} from "@/utils/storeSelection"; import {getSelectedStoreFromStorage, saveSelectedStoreToStorage} from "@/utils/storeSelection";
@@ -57,18 +55,6 @@ const OrderConfirm = () => {
const [loading, setLoading] = useState<boolean>(true) const [loading, setLoading] = useState<boolean>(true)
const [error, setError] = useState<string>('') const [error, setError] = useState<string>('')
const [payLoading, setPayLoading] = useState<boolean>(false) const [payLoading, setPayLoading] = useState<boolean>(false)
// 配送时间(仅水票套票商品需要)
// 当日截单时间:超过该时间下单,最早配送日顺延到次日(避免 21:00 下单仍显示“当天配送”)
const DELIVERY_CUTOFF_HOUR = 21
const getMinSendDate = () => {
const now = dayjs()
const cutoff = now.hour(DELIVERY_CUTOFF_HOUR).minute(0).second(0).millisecond(0)
const startOfToday = now.startOf('day')
// >= 截单时间则最早只能选次日
return now.isSame(cutoff) || now.isAfter(cutoff) ? startOfToday.add(1, 'day') : startOfToday
}
const [sendTime, setSendTime] = useState<Date>(() => getMinSendDate().toDate())
const [sendTimePickerVisible, setSendTimePickerVisible] = useState(false)
// 水票套票活动(若存在则按规则限制最小购买量等) // 水票套票活动(若存在则按规则限制最小购买量等)
const [ticketTemplate, setTicketTemplate] = useState<GltTicketTemplate | null>(null) const [ticketTemplate, setTicketTemplate] = useState<GltTicketTemplate | null>(null)
@@ -122,10 +108,6 @@ const OrderConfirm = () => {
})() })()
const minBuyQty = isTicketTemplateActive ? ticketMinBuyQty : 1 const minBuyQty = isTicketTemplateActive ? ticketMinBuyQty : 1
const sendTimeText = useMemo(() => {
return dayjs(sendTime).format('YYYY-MM-DD')
}, [sendTime])
const getGiftTicketQty = (buyQty: number) => { const getGiftTicketQty = (buyQty: number) => {
if (!isTicketTemplateActive) return 0 if (!isTicketTemplateActive) return 0
const multiplier = Number(ticketTemplate?.giftMultiplier || 0) const multiplier = Number(ticketTemplate?.giftMultiplier || 0)
@@ -451,22 +433,7 @@ const OrderConfirm = () => {
return; return;
} }
// 水票套票商品:保存配送时间到 ShopOrder.sendStartTime // 购买水票(囤券预付费)与水票核销(下单履约)为两个独立动作:下单页不再选择配送时间。
if (hasTicketTemplate && !sendTime) {
Taro.showToast({ title: '请选择配送时间', icon: 'none' })
return
}
if (hasTicketTemplate) {
const min = getMinSendDate()
if (dayjs(sendTime).isBefore(min, 'day')) {
setSendTime(min.toDate())
Taro.showToast({
title: `已过当日${DELIVERY_CUTOFF_HOUR}点截单,最早配送:${min.format('YYYY-MM-DD')}`,
icon: 'none'
})
return
}
}
// 水票套票活动:最小购买量校验 // 水票套票活动:最小购买量校验
if (isTicketTemplateActive && quantity < minBuyQty) { if (isTicketTemplateActive && quantity < minBuyQty) {
@@ -530,9 +497,6 @@ const OrderConfirm = () => {
comments: goods.name, comments: goods.name,
deliveryType: 0, deliveryType: 0,
buyerRemarks: orderRemark, buyerRemarks: orderRemark,
sendStartTime: hasTicketTemplate
? dayjs(sendTime).startOf('day').format('YYYY-MM-DD HH:mm:ss')
: undefined,
couponId: parseInt(String(bestCoupon.id), 10) couponId: parseInt(String(bestCoupon.id), 10)
} }
); );
@@ -540,7 +504,31 @@ const OrderConfirm = () => {
console.log('🎯 使用推荐优惠券的订单数据:', updatedOrderData); console.log('🎯 使用推荐优惠券的订单数据:', updatedOrderData);
// 执行支付 // 执行支付
await PaymentHandler.pay(updatedOrderData, currentPaymentType); await PaymentHandler.pay(updatedOrderData, currentPaymentType, hasTicketTemplate ? {
onSuccess: async () => {
const id = goods.goodsId
try {
const res = await Taro.showModal({
title: '提示',
content: '是否立刻送水?',
confirmText: '立刻送水',
cancelText: '稍后'
})
if (res?.confirm) {
if (id) {
await Taro.redirectTo({ url: `/user/ticket/use?goodsId=${id}` })
} else {
await Taro.redirectTo({ url: '/user/ticket/index' })
}
} else {
await Taro.redirectTo({ url: '/user/ticket/index' })
}
} catch (_e) {
await Taro.redirectTo({ url: '/user/ticket/index' })
}
return false
}
} : undefined);
return; // 提前返回,避免重复执行支付 return; // 提前返回,避免重复执行支付
} else { } else {
// 用户选择不使用优惠券,继续支付 // 用户选择不使用优惠券,继续支付
@@ -558,9 +546,6 @@ const OrderConfirm = () => {
comments: '桂乐淘', comments: '桂乐淘',
deliveryType: 0, deliveryType: 0,
buyerRemarks: orderRemark, buyerRemarks: orderRemark,
sendStartTime: hasTicketTemplate
? dayjs(sendTime).startOf('day').format('YYYY-MM-DD HH:mm:ss')
: undefined,
// 🔧 确保 couponId 是正确的数字类型,且不传递 undefined // 🔧 确保 couponId 是正确的数字类型,且不传递 undefined
couponId: selectedCoupon ? parseInt(String(selectedCoupon.id), 10) : undefined couponId: selectedCoupon ? parseInt(String(selectedCoupon.id), 10) : undefined
} }
@@ -595,7 +580,31 @@ const OrderConfirm = () => {
}); });
// 执行支付 - 移除这里的成功提示让PaymentHandler统一处理 // 执行支付 - 移除这里的成功提示让PaymentHandler统一处理
await PaymentHandler.pay(orderData, paymentType); await PaymentHandler.pay(orderData, paymentType, hasTicketTemplate ? {
onSuccess: async () => {
const id = goods.goodsId
try {
const res = await Taro.showModal({
title: '提示',
content: '是否立刻送水?',
confirmText: '立刻送水',
cancelText: '稍后'
})
if (res?.confirm) {
if (id) {
await Taro.redirectTo({ url: `/user/ticket/use?goodsId=${id}` })
} else {
await Taro.redirectTo({ url: '/user/ticket/index' })
}
} else {
await Taro.redirectTo({ url: '/user/ticket/index' })
}
} catch (_e) {
await Taro.redirectTo({ url: '/user/ticket/index' })
}
return false
}
} : undefined);
// ✅ 移除双重成功提示 - PaymentHandler会处理成功提示 // ✅ 移除双重成功提示 - PaymentHandler会处理成功提示
// Taro.showToast({ // Taro.showToast({
@@ -760,10 +769,7 @@ const OrderConfirm = () => {
}) })
useEffect(() => { useEffect(() => {
// 切换商品时重置配送时间,避免沿用上一次选择
if (!isLoggedIn()) return if (!isLoggedIn()) return
setSendTime(getMinSendDate().toDate())
setSendTimePickerVisible(false)
loadAllData() loadAllData()
}, [goodsId]); }, [goodsId]);
@@ -822,28 +828,6 @@ const OrderConfirm = () => {
)} )}
</CellGroup> </CellGroup>
{hasTicketTemplate && (
<CellGroup>
<Cell
title={'配送时间'}
extra={(
<View className={'flex items-center gap-2'}>
<View className={'text-gray-900'}>{sendTimeText}</View>
<ArrowRight className={'text-gray-400'} size={14}/>
</View>
)}
onClick={() => {
// 若页面停留跨过截单时间,打开选择器前再校正一次最早可选日期
const min = getMinSendDate()
if (dayjs(sendTime).isBefore(min, 'day')) {
setSendTime(min.toDate())
}
setSendTimePickerVisible(true)
}}
/>
</CellGroup>
)}
{/*<CellGroup>*/} {/*<CellGroup>*/}
{/* <Cell*/} {/* <Cell*/}
{/* title={(*/} {/* title={(*/}
@@ -1138,23 +1122,6 @@ const OrderConfirm = () => {
<Gap height={50}/> <Gap height={50}/>
<DatePicker
visible={sendTimePickerVisible}
title="选择配送时间"
type="date"
startDate={getMinSendDate().toDate()}
endDate={dayjs().add(30, 'day').toDate()}
value={sendTime}
onClose={() => setSendTimePickerVisible(false)}
onCancel={() => setSendTimePickerVisible(false)}
onConfirm={(_options, selectedValue) => {
const [y, m, d] = (selectedValue || []).map(v => Number(v))
const next = new Date(y, (m || 1) - 1, d || 1, 0, 0, 0)
setSendTime(next)
setSendTimePickerVisible(false)
}}
/>
<div className={'fixed z-50 bg-white w-full bottom-0 left-0 pt-4 pb-10 border-t border-gray-200'}> <div className={'fixed z-50 bg-white w-full bottom-0 left-0 pt-4 pb-10 border-t border-gray-200'}>
<View className={'btn-bar flex justify-between items-center'}> <View className={'btn-bar flex justify-between items-center'}>
<div className={'flex flex-col justify-center items-start mx-4'}> <div className={'flex flex-col justify-center items-start mx-4'}>

View File

@@ -19,7 +19,8 @@ export enum PaymentType {
* 支付结果回调 * 支付结果回调
*/ */
export interface PaymentCallback { export interface PaymentCallback {
onSuccess?: () => void; // Return `false` to skip default "支付成功" toast + redirect.
onSuccess?: () => void | boolean | Promise<void | boolean>;
onError?: (error: string) => void; onError?: (error: string) => void;
onComplete?: () => void; onComplete?: () => void;
} }
@@ -118,17 +119,27 @@ export class PaymentHandler {
if (paymentSuccess) { if (paymentSuccess) {
console.log('支付成功,订单号:', result.orderNo); console.log('支付成功,订单号:', result.orderNo);
// 先收起 loading避免遮挡 modal/toast
try {
Taro.hideLoading();
} catch (_e) {
// ignore
}
const onSuccessResult = await callback?.onSuccess?.();
const skipDefaultSuccessBehavior = onSuccessResult === false;
if (!skipDefaultSuccessBehavior) {
Taro.showToast({ Taro.showToast({
title: '支付成功', title: '支付成功',
icon: 'success' icon: 'success'
}); });
callback?.onSuccess?.();
// 跳转到订单页面 // 跳转到订单页面
setTimeout(() => { setTimeout(() => {
Taro.navigateTo({ url: '/user/order/order' }); Taro.navigateTo({ url: '/user/order/order' });
}, 2000); }, 2000);
}
} else { } else {
throw new Error('支付未完成'); throw new Error('支付未完成');
} }