feat(shop): 添加水票套票活动功能支持
- 移除未使用的 Shop 图标导入和 navTo 工具函数 - 新增水票套票模板查询接口和类型定义 - 实现套票活动状态判断逻辑和最小购买量校验 - 添加购买数量变更时的赠送水票计算功能 - 在商品详情区域显示最低购买量和赠送水票信息 - 为套票活动商品添加注意事项展示 - 禁用不符合最低购买量要求的支付按钮 - 注释掉门店选择相关UI组件以优化界面
This commit is contained in:
@@ -11,7 +11,7 @@ import {
|
|||||||
InputNumber,
|
InputNumber,
|
||||||
ConfigProvider
|
ConfigProvider
|
||||||
} from '@nutui/nutui-react-taro'
|
} from '@nutui/nutui-react-taro'
|
||||||
import {Location, ArrowRight, Shop} from '@nutui/icons-react-taro'
|
import {Location, ArrowRight} from '@nutui/icons-react-taro'
|
||||||
import Taro, {useDidShow} from '@tarojs/taro'
|
import Taro, {useDidShow} from '@tarojs/taro'
|
||||||
import {ShopGoods} from "@/api/shop/shopGoods/model";
|
import {ShopGoods} from "@/api/shop/shopGoods/model";
|
||||||
import {getShopGoods} from "@/api/shop/shopGoods";
|
import {getShopGoods} from "@/api/shop/shopGoods";
|
||||||
@@ -27,6 +27,8 @@ import OrderConfirmSkeleton from "@/components/OrderConfirmSkeleton";
|
|||||||
import CouponList from "@/components/CouponList";
|
import CouponList from "@/components/CouponList";
|
||||||
import {CouponCardProps} from "@/components/CouponCard";
|
import {CouponCardProps} from "@/components/CouponCard";
|
||||||
import {getMyAvailableCoupons} from "@/api/shop/shopUserCoupon";
|
import {getMyAvailableCoupons} from "@/api/shop/shopUserCoupon";
|
||||||
|
import {getGltTicketTemplateByGoodsId} from "@/api/glt/gltTicketTemplate";
|
||||||
|
import type {GltTicketTemplate} from "@/api/glt/gltTicketTemplate/model";
|
||||||
import {
|
import {
|
||||||
transformCouponData,
|
transformCouponData,
|
||||||
calculateCouponDiscount,
|
calculateCouponDiscount,
|
||||||
@@ -36,7 +38,6 @@ import {
|
|||||||
filterUsableCoupons,
|
filterUsableCoupons,
|
||||||
filterUnusableCoupons
|
filterUnusableCoupons
|
||||||
} from "@/utils/couponUtils";
|
} from "@/utils/couponUtils";
|
||||||
import navTo from "@/utils/common";
|
|
||||||
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";
|
||||||
@@ -54,6 +55,9 @@ const OrderConfirm = () => {
|
|||||||
const [error, setError] = useState<string>('')
|
const [error, setError] = useState<string>('')
|
||||||
const [payLoading, setPayLoading] = useState<boolean>(false)
|
const [payLoading, setPayLoading] = useState<boolean>(false)
|
||||||
|
|
||||||
|
// 水票套票活动(若存在则按规则限制最小购买量等)
|
||||||
|
const [ticketTemplate, setTicketTemplate] = useState<GltTicketTemplate | null>(null)
|
||||||
|
|
||||||
// InputNumber 主题配置
|
// InputNumber 主题配置
|
||||||
const customTheme = {
|
const customTheme = {
|
||||||
nutuiInputnumberButtonWidth: '28px',
|
nutuiInputnumberButtonWidth: '28px',
|
||||||
@@ -79,6 +83,23 @@ const OrderConfirm = () => {
|
|||||||
const router = Taro.getCurrentInstance().router;
|
const router = Taro.getCurrentInstance().router;
|
||||||
const goodsId = router?.params?.goodsId;
|
const goodsId = router?.params?.goodsId;
|
||||||
|
|
||||||
|
const isTicketTemplateActive =
|
||||||
|
!!ticketTemplate &&
|
||||||
|
ticketTemplate.enabled !== false &&
|
||||||
|
ticketTemplate.status !== 1 &&
|
||||||
|
ticketTemplate.deleted !== 1
|
||||||
|
|
||||||
|
// 需求:套票活动最低购买量固定为 20 桶
|
||||||
|
const minBuyQty = isTicketTemplateActive ? 20 : 1
|
||||||
|
|
||||||
|
const getGiftTicketQty = (buyQty: number) => {
|
||||||
|
if (!isTicketTemplateActive) return 0
|
||||||
|
const multiplier = Number(ticketTemplate?.giftMultiplier || 0)
|
||||||
|
const startSend = Number(ticketTemplate?.startSendQty || 0)
|
||||||
|
if (multiplier > 0) return Math.max(0, buyQty) * multiplier
|
||||||
|
return Math.max(0, startSend)
|
||||||
|
}
|
||||||
|
|
||||||
const loadStores = async () => {
|
const loadStores = async () => {
|
||||||
if (storeLoading) return
|
if (storeLoading) return
|
||||||
try {
|
try {
|
||||||
@@ -94,6 +115,7 @@ const OrderConfirm = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
const openStorePopup = async () => {
|
const openStorePopup = async () => {
|
||||||
setStorePopupVisible(true)
|
setStorePopupVisible(true)
|
||||||
if (!stores.length) {
|
if (!stores.length) {
|
||||||
@@ -393,6 +415,15 @@ const OrderConfirm = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 水票套票活动:最小购买量校验
|
||||||
|
if (isTicketTemplateActive && quantity < minBuyQty) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: `最低购买量:${minBuyQty}桶`,
|
||||||
|
icon: 'none'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 库存校验
|
// 库存校验
|
||||||
if (goods.stock !== undefined && quantity > goods.stock) {
|
if (goods.stock !== undefined && quantity > goods.stock) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
@@ -513,7 +544,7 @@ const OrderConfirm = () => {
|
|||||||
// icon: 'success'
|
// icon: 'success'
|
||||||
// })
|
// })
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
return navTo('/user/order/order?statusFilter=0', true)
|
// return navTo('/user/order/order?statusFilter=0', true)
|
||||||
// console.error('支付失败:', error)
|
// console.error('支付失败:', error)
|
||||||
|
|
||||||
// 只处理PaymentHandler未处理的错误
|
// 只处理PaymentHandler未处理的错误
|
||||||
@@ -565,6 +596,18 @@ const OrderConfirm = () => {
|
|||||||
setGoods(goodsRes)
|
setGoods(goodsRes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询当前商品是否存在水票套票活动(失败/无数据时不影响正常下单)
|
||||||
|
if (goodsId) {
|
||||||
|
try {
|
||||||
|
const tpl = await getGltTicketTemplateByGoodsId(Number(goodsId))
|
||||||
|
setTicketTemplate(tpl)
|
||||||
|
} catch (e) {
|
||||||
|
setTicketTemplate(null)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setTicketTemplate(null)
|
||||||
|
}
|
||||||
|
|
||||||
// 设置默认收货地址
|
// 设置默认收货地址
|
||||||
if (addressRes && addressRes.length > 0) {
|
if (addressRes && addressRes.length > 0) {
|
||||||
setAddress(addressRes[0])
|
setAddress(addressRes[0])
|
||||||
@@ -656,25 +699,25 @@ const OrderConfirm = () => {
|
|||||||
)}
|
)}
|
||||||
</CellGroup>
|
</CellGroup>
|
||||||
|
|
||||||
<CellGroup className={'hidden'}>
|
{/*<CellGroup>*/}
|
||||||
<Cell
|
{/* <Cell*/}
|
||||||
title={(
|
{/* title={(*/}
|
||||||
<View className="flex items-center gap-2">
|
{/* <View className="flex items-center gap-2">*/}
|
||||||
<Shop className={'text-gray-500'}/>
|
{/* <Shop className={'text-gray-500'}/>*/}
|
||||||
<Text>门店</Text>
|
{/* <Text>门店</Text>*/}
|
||||||
</View>
|
{/* </View>*/}
|
||||||
)}
|
{/* )}*/}
|
||||||
extra={(
|
{/* extra={(*/}
|
||||||
<View className={'flex items-center gap-2'}>
|
{/* <View className={'flex items-center gap-2'}>*/}
|
||||||
<View className={'text-gray-900'}>
|
{/* <View className={'text-gray-900'}>*/}
|
||||||
{selectedStore?.name || '请选择门店'}
|
{/* {selectedStore?.name || '请选择门店'}*/}
|
||||||
</View>
|
{/* </View>*/}
|
||||||
<ArrowRight className={'text-gray-400'} size={14}/>
|
{/* <ArrowRight className={'text-gray-400'} size={14}/>*/}
|
||||||
</View>
|
{/* </View>*/}
|
||||||
)}
|
{/* )}*/}
|
||||||
onClick={openStorePopup}
|
{/* onClick={openStorePopup}*/}
|
||||||
/>
|
{/* />*/}
|
||||||
</CellGroup>
|
{/*</CellGroup>*/}
|
||||||
|
|
||||||
<CellGroup>
|
<CellGroup>
|
||||||
<Cell key={goods.goodsId}>
|
<Cell key={goods.goodsId}>
|
||||||
@@ -705,6 +748,12 @@ const OrderConfirm = () => {
|
|||||||
库存 {goods.stock} 件
|
库存 {goods.stock} 件
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
{isTicketTemplateActive && (
|
||||||
|
<View className={'text-xs text-gray-500'}>
|
||||||
|
<Text>最低购买量:{minBuyQty}桶</Text>
|
||||||
|
<Text className={'ml-2'}>赠送水票:{getGiftTicketQty(quantity)}张</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -789,12 +838,19 @@ const OrderConfirm = () => {
|
|||||||
)}/>
|
)}/>
|
||||||
</CellGroup>
|
</CellGroup>
|
||||||
|
|
||||||
<div className={'text-gray-400'}>
|
{ticketTemplate && (
|
||||||
注意事项:
|
<CellGroup>
|
||||||
1.最低起送量≥20桶;
|
<Cell extra={(
|
||||||
2.配送范围要在电子围栏内;
|
<div className={'text-red-500 text-sm'}>
|
||||||
3.上楼费暂不收取,收费另行通知。
|
注意事项:
|
||||||
</div>
|
1.最低起送量≥20桶;
|
||||||
|
2.配送范围要在电子围栏内;
|
||||||
|
3.上楼费暂不收取,收费另行通知。
|
||||||
|
</div>
|
||||||
|
)}/>
|
||||||
|
</CellGroup>
|
||||||
|
)}
|
||||||
|
|
||||||
|
|
||||||
{/* 支付方式选择 */}
|
{/* 支付方式选择 */}
|
||||||
<ActionSheet
|
<ActionSheet
|
||||||
@@ -953,6 +1009,7 @@ const OrderConfirm = () => {
|
|||||||
type="success"
|
type="success"
|
||||||
size="large"
|
size="large"
|
||||||
loading={payLoading}
|
loading={payLoading}
|
||||||
|
disabled={isTicketTemplateActive && quantity < minBuyQty}
|
||||||
onClick={() => onPay(goods)}
|
onClick={() => onPay(goods)}
|
||||||
>
|
>
|
||||||
{payLoading ? '支付中...' : '立即付款'}
|
{payLoading ? '支付中...' : '立即付款'}
|
||||||
|
|||||||
Reference in New Issue
Block a user