feat(order): 添加配送时间选择功能
- 在订单模型中新增 sendStartTime 字段用于预约配送时间 - 为水票套票商品添加配送时间选择器组件和日期选择逻辑 - 实现配送时间验证确保水票套票商品必须选择配送时间 - 优化支付错误处理增加配送范围提示和地址更换引导 - 调整套票购买注意事项显示动态最低起送量信息 - 移除用户票据页面重复的时间选择相关代码以保持一致性
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {useEffect, useMemo, useState} from "react";
|
||||
import {
|
||||
Image,
|
||||
Button,
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
ActionSheet,
|
||||
Popup,
|
||||
InputNumber,
|
||||
DatePicker,
|
||||
ConfigProvider
|
||||
} from '@nutui/nutui-react-taro'
|
||||
import {Location, ArrowRight} from '@nutui/icons-react-taro'
|
||||
@@ -38,6 +39,7 @@ import {
|
||||
filterUsableCoupons,
|
||||
filterUnusableCoupons
|
||||
} from "@/utils/couponUtils";
|
||||
import dayjs from 'dayjs'
|
||||
import type {ShopStore} from "@/api/shop/shopStore/model";
|
||||
import {getShopStore, listShopStore} from "@/api/shop/shopStore";
|
||||
import {getSelectedStoreFromStorage, saveSelectedStoreToStorage} from "@/utils/storeSelection";
|
||||
@@ -54,6 +56,9 @@ const OrderConfirm = () => {
|
||||
const [loading, setLoading] = useState<boolean>(true)
|
||||
const [error, setError] = useState<string>('')
|
||||
const [payLoading, setPayLoading] = useState<boolean>(false)
|
||||
// 配送时间(仅水票套票商品需要)
|
||||
const [sendTime, setSendTime] = useState<Date>(() => dayjs().startOf('day').toDate())
|
||||
const [sendTimePickerVisible, setSendTimePickerVisible] = useState(false)
|
||||
|
||||
// 水票套票活动(若存在则按规则限制最小购买量等)
|
||||
const [ticketTemplate, setTicketTemplate] = useState<GltTicketTemplate | null>(null)
|
||||
@@ -88,6 +93,7 @@ const OrderConfirm = () => {
|
||||
ticketTemplate.enabled !== false &&
|
||||
ticketTemplate.status !== 1 &&
|
||||
ticketTemplate.deleted !== 1
|
||||
const hasTicketTemplate = !!ticketTemplate
|
||||
|
||||
// 套票活动最低购买量:优先取模板配置
|
||||
const ticketMinBuyQty = (() => {
|
||||
@@ -96,6 +102,10 @@ const OrderConfirm = () => {
|
||||
})()
|
||||
const minBuyQty = isTicketTemplateActive ? ticketMinBuyQty : 1
|
||||
|
||||
const sendTimeText = useMemo(() => {
|
||||
return dayjs(sendTime).format('YYYY-MM-DD')
|
||||
}, [sendTime])
|
||||
|
||||
const getGiftTicketQty = (buyQty: number) => {
|
||||
if (!isTicketTemplateActive) return 0
|
||||
const multiplier = Number(ticketTemplate?.giftMultiplier || 0)
|
||||
@@ -420,6 +430,12 @@ const OrderConfirm = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
// 水票套票商品:保存配送时间到 ShopOrder.sendStartTime
|
||||
if (hasTicketTemplate && !sendTime) {
|
||||
Taro.showToast({ title: '请选择配送时间', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
// 水票套票活动:最小购买量校验
|
||||
if (isTicketTemplateActive && quantity < minBuyQty) {
|
||||
Taro.showToast({
|
||||
@@ -482,6 +498,9 @@ const OrderConfirm = () => {
|
||||
comments: goods.name,
|
||||
deliveryType: 0,
|
||||
buyerRemarks: orderRemark,
|
||||
sendStartTime: hasTicketTemplate
|
||||
? dayjs(sendTime).startOf('day').format('YYYY-MM-DD HH:mm:ss')
|
||||
: undefined,
|
||||
couponId: parseInt(String(bestCoupon.id), 10)
|
||||
}
|
||||
);
|
||||
@@ -507,6 +526,9 @@ const OrderConfirm = () => {
|
||||
comments: '桂乐淘',
|
||||
deliveryType: 0,
|
||||
buyerRemarks: orderRemark,
|
||||
sendStartTime: hasTicketTemplate
|
||||
? dayjs(sendTime).startOf('day').format('YYYY-MM-DD HH:mm:ss')
|
||||
: undefined,
|
||||
// 🔧 确保 couponId 是正确的数字类型,且不传递 undefined
|
||||
couponId: selectedCoupon ? parseInt(String(selectedCoupon.id), 10) : undefined
|
||||
}
|
||||
@@ -549,31 +571,35 @@ const OrderConfirm = () => {
|
||||
// icon: 'success'
|
||||
// })
|
||||
} catch (error: any) {
|
||||
// return navTo('/user/order/order?statusFilter=0', true)
|
||||
// console.error('支付失败:', error)
|
||||
const message = String(error?.message || '')
|
||||
const isOutOfDeliveryRange =
|
||||
message.includes('不在配送范围') ||
|
||||
message.includes('配送范围') ||
|
||||
message.includes('电子围栏') ||
|
||||
message.includes('围栏')
|
||||
|
||||
// 只处理PaymentHandler未处理的错误
|
||||
// if (!error.handled) {
|
||||
// let errorMessage = '支付失败,请重试';
|
||||
//
|
||||
// // 根据错误类型提供具体提示
|
||||
// if (error.message?.includes('余额不足')) {
|
||||
// errorMessage = '账户余额不足,请充值后重试';
|
||||
// } else if (error.message?.includes('优惠券')) {
|
||||
// errorMessage = '优惠券使用失败,请重新选择';
|
||||
// } else if (error.message?.includes('库存')) {
|
||||
// errorMessage = '商品库存不足,请减少购买数量';
|
||||
// } else if (error.message?.includes('地址')) {
|
||||
// errorMessage = '收货地址信息有误,请重新选择';
|
||||
// } else if (error.message) {
|
||||
// errorMessage = error.message;
|
||||
// }
|
||||
// Taro.showToast({
|
||||
// title: errorMessage,
|
||||
// icon: 'error'
|
||||
// })
|
||||
// console.log('跳去未付款的订单列表页面')
|
||||
// }
|
||||
// “配送范围”类错误给出更友好的解释,并提供快捷入口去更换收货地址
|
||||
if (isOutOfDeliveryRange) {
|
||||
try {
|
||||
const res = await Taro.showModal({
|
||||
title: '暂不支持配送',
|
||||
content: '当前收货地址超出配送范围。您可以更换收货地址后再下单,或联系门店确认配送范围。',
|
||||
confirmText: '更换地址',
|
||||
cancelText: '我知道了'
|
||||
})
|
||||
if (res?.confirm) {
|
||||
Taro.navigateTo({ url: '/user/address/index' })
|
||||
}
|
||||
} catch (_e) {
|
||||
// ignore
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 兜底:仅在 PaymentHandler 未弹过提示时再提示一次
|
||||
if (!error?.handled) {
|
||||
Taro.showToast({ title: message || '支付失败,请重试', icon: 'none' })
|
||||
}
|
||||
} finally {
|
||||
setPayLoading(false)
|
||||
}
|
||||
@@ -673,6 +699,9 @@ const OrderConfirm = () => {
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
// 切换商品时重置配送时间,避免沿用上一次选择
|
||||
setSendTime(dayjs().startOf('day').toDate())
|
||||
setSendTimePickerVisible(false)
|
||||
loadAllData()
|
||||
}, [goodsId]);
|
||||
|
||||
@@ -731,6 +760,21 @@ const OrderConfirm = () => {
|
||||
)}
|
||||
</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={() => setSendTimePickerVisible(true)}
|
||||
/>
|
||||
</CellGroup>
|
||||
)}
|
||||
|
||||
{/*<CellGroup>*/}
|
||||
{/* <Cell*/}
|
||||
{/* title={(*/}
|
||||
@@ -874,10 +918,10 @@ const OrderConfirm = () => {
|
||||
<CellGroup>
|
||||
<Cell extra={(
|
||||
<div className={'text-red-500 text-sm'}>
|
||||
注意事项:
|
||||
1.最低起送量≥20桶;
|
||||
2.配送范围要在电子围栏内;
|
||||
3.上楼费暂不收取,收费另行通知。
|
||||
<Text>注意事项:</Text>
|
||||
<Text>最低起送量≥{ticketTemplate.minBuyQty}桶;</Text>
|
||||
<Text>配送范围要在电子围栏内;</Text>
|
||||
<Text>上楼费暂不收取,收费另行通知。</Text>
|
||||
</div>
|
||||
)}/>
|
||||
</CellGroup>
|
||||
@@ -1023,6 +1067,23 @@ const OrderConfirm = () => {
|
||||
|
||||
<Gap height={50}/>
|
||||
|
||||
<DatePicker
|
||||
visible={sendTimePickerVisible}
|
||||
title="选择配送时间"
|
||||
type="date"
|
||||
startDate={dayjs().startOf('day').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'}>
|
||||
<View className={'btn-bar flex justify-between items-center'}>
|
||||
<div className={'flex flex-col justify-center items-start mx-4'}>
|
||||
|
||||
Reference in New Issue
Block a user