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, 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 ? '支付中...' : '立即付款'}