feat(shop): 添加水票套票活动功能支持

- 移除未使用的 Shop 图标导入和 navTo 工具函数
- 新增水票套票模板查询接口和类型定义
- 实现套票活动状态判断逻辑和最小购买量校验
- 添加购买数量变更时的赠送水票计算功能
- 在商品详情区域显示最低购买量和赠送水票信息
- 为套票活动商品添加注意事项展示
- 禁用不符合最低购买量要求的支付按钮
- 注释掉门店选择相关UI组件以优化界面
This commit is contained in:
2026-02-07 13:12:46 +08:00
parent 50ffd2c9da
commit 6e0a5aa1fe

View File

@@ -11,7 +11,7 @@ import {
InputNumber,
ConfigProvider
} 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 {ShopGoods} from "@/api/shop/shopGoods/model";
import {getShopGoods} from "@/api/shop/shopGoods";
@@ -27,6 +27,8 @@ import OrderConfirmSkeleton from "@/components/OrderConfirmSkeleton";
import CouponList from "@/components/CouponList";
import {CouponCardProps} from "@/components/CouponCard";
import {getMyAvailableCoupons} from "@/api/shop/shopUserCoupon";
import {getGltTicketTemplateByGoodsId} from "@/api/glt/gltTicketTemplate";
import type {GltTicketTemplate} from "@/api/glt/gltTicketTemplate/model";
import {
transformCouponData,
calculateCouponDiscount,
@@ -36,7 +38,6 @@ import {
filterUsableCoupons,
filterUnusableCoupons
} from "@/utils/couponUtils";
import navTo from "@/utils/common";
import type {ShopStore} from "@/api/shop/shopStore/model";
import {getShopStore, listShopStore} from "@/api/shop/shopStore";
import {getSelectedStoreFromStorage, saveSelectedStoreToStorage} from "@/utils/storeSelection";
@@ -54,6 +55,9 @@ const OrderConfirm = () => {
const [error, setError] = useState<string>('')
const [payLoading, setPayLoading] = useState<boolean>(false)
// 水票套票活动(若存在则按规则限制最小购买量等)
const [ticketTemplate, setTicketTemplate] = useState<GltTicketTemplate | null>(null)
// InputNumber 主题配置
const customTheme = {
nutuiInputnumberButtonWidth: '28px',
@@ -79,6 +83,23 @@ const OrderConfirm = () => {
const router = Taro.getCurrentInstance().router;
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 () => {
if (storeLoading) return
try {
@@ -94,6 +115,7 @@ const OrderConfirm = () => {
}
}
// @ts-ignore
const openStorePopup = async () => {
setStorePopupVisible(true)
if (!stores.length) {
@@ -393,6 +415,15 @@ const OrderConfirm = () => {
return;
}
// 水票套票活动:最小购买量校验
if (isTicketTemplateActive && quantity < minBuyQty) {
Taro.showToast({
title: `最低购买量:${minBuyQty}`,
icon: 'none'
})
return
}
// 库存校验
if (goods.stock !== undefined && quantity > goods.stock) {
Taro.showToast({
@@ -513,7 +544,7 @@ const OrderConfirm = () => {
// icon: 'success'
// })
} catch (error: any) {
return navTo('/user/order/order?statusFilter=0', true)
// return navTo('/user/order/order?statusFilter=0', true)
// console.error('支付失败:', error)
// 只处理PaymentHandler未处理的错误
@@ -565,6 +596,18 @@ const OrderConfirm = () => {
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) {
setAddress(addressRes[0])
@@ -656,25 +699,25 @@ const OrderConfirm = () => {
)}
</CellGroup>
<CellGroup className={'hidden'}>
<Cell
title={(
<View className="flex items-center gap-2">
<Shop className={'text-gray-500'}/>
<Text></Text>
</View>
)}
extra={(
<View className={'flex items-center gap-2'}>
<View className={'text-gray-900'}>
{selectedStore?.name || '请选择门店'}
</View>
<ArrowRight className={'text-gray-400'} size={14}/>
</View>
)}
onClick={openStorePopup}
/>
</CellGroup>
{/*<CellGroup>*/}
{/* <Cell*/}
{/* title={(*/}
{/* <View className="flex items-center gap-2">*/}
{/* <Shop className={'text-gray-500'}/>*/}
{/* <Text>门店</Text>*/}
{/* </View>*/}
{/* )}*/}
{/* extra={(*/}
{/* <View className={'flex items-center gap-2'}>*/}
{/* <View className={'text-gray-900'}>*/}
{/* {selectedStore?.name || '请选择门店'}*/}
{/* </View>*/}
{/* <ArrowRight className={'text-gray-400'} size={14}/>*/}
{/* </View>*/}
{/* )}*/}
{/* onClick={openStorePopup}*/}
{/* />*/}
{/*</CellGroup>*/}
<CellGroup>
<Cell key={goods.goodsId}>
@@ -705,6 +748,12 @@ const OrderConfirm = () => {
{goods.stock}
</Text>
)}
{isTicketTemplateActive && (
<View className={'text-xs text-gray-500'}>
<Text>{minBuyQty}</Text>
<Text className={'ml-2'}>{getGiftTicketQty(quantity)}</Text>
</View>
)}
</View>
</View>
</View>
@@ -789,12 +838,19 @@ const OrderConfirm = () => {
)}/>
</CellGroup>
<div className={'text-gray-400'}>
{ticketTemplate && (
<CellGroup>
<Cell extra={(
<div className={'text-red-500 text-sm'}>
1.20
2.
3.
</div>
)}/>
</CellGroup>
)}
{/* 支付方式选择 */}
<ActionSheet
@@ -953,6 +1009,7 @@ const OrderConfirm = () => {
type="success"
size="large"
loading={payLoading}
disabled={isTicketTemplateActive && quantity < minBuyQty}
onClick={() => onPay(goods)}
>
{payLoading ? '支付中...' : '立即付款'}