feat(share): 添加分享功能并限制水票商品加入购物车

- 在二维码页面启用分享给朋友和分享到朋友圈功能
- 实现分享菜单显示和分享内容自定义逻辑
- 移除原有的复制邀请信息和分享给好友按钮
- 新增水票套票模板查询接口和类型定义
- 阻止水票套票商品加入购物车并提示用户立即购买
- 添加组件卸载时的清理逻辑防止内存泄漏
- 优化商品详情页异步操作的状态管理
This commit is contained in:
2026-02-26 12:11:30 +08:00
parent f9dcaa9ce9
commit 78ac461ef9
3 changed files with 98 additions and 27 deletions

View File

@@ -1,3 +1,6 @@
export default definePageConfig({
navigationBarTitleText: '桂乐淘分享中心'
navigationBarTitleText: '桂乐淘分享中心',
// Enable "Share to friends" and "Share to Moments" (timeline) for this page.
enableShareAppMessage: true,
enableShareTimeline: true
})

View File

@@ -2,7 +2,7 @@ import React, {useState, useEffect} from 'react'
import {View, Text, Image} from '@tarojs/components'
import {Button, Loading} from '@nutui/nutui-react-taro'
import {Download, QrCode} from '@nutui/icons-react-taro'
import Taro from '@tarojs/taro'
import Taro, {useShareAppMessage} from '@tarojs/taro'
import {useDealerUser} from '@/hooks/useDealerUser'
import {generateInviteCode} from '@/api/invite'
// import type {InviteStats} from '@/api/invite'
@@ -16,6 +16,39 @@ const DealerQrcode: React.FC = () => {
// const [statsLoading, setStatsLoading] = useState<boolean>(false)
const {dealerUser, loading: dealerLoading, error, refresh} = useDealerUser()
// Enable "转发给朋友" + "分享到朋友圈" items in the share panel/menu.
useEffect(() => {
// Some clients require explicit call to show both share entries.
Taro.showShareMenu({
withShareTicket: true,
showShareItems: ['shareAppMessage', 'shareTimeline']
}).catch(() => {})
}, [])
// 转发给朋友(分享小程序链接)
useShareAppMessage(() => {
const inviterRaw = dealerUser?.userId ?? Taro.getStorageSync('UserId')
const inviter = Number(inviterRaw)
const hasInviter = Number.isFinite(inviter) && inviter > 0
const user = Taro.getStorageSync('User') || {}
const nickname = (user && (user.nickname || user.realName || user.username)) || ''
const title = hasInviter ? `${nickname || '我'}邀请你加入桂乐淘伙伴计划` : '桂乐淘伙伴计划'
return {
title,
path: hasInviter
? `/pages/index/index?inviter=${inviter}&source=dealer_qrcode&t=${Date.now()}`
: `/pages/index/index`,
success: function () {
Taro.showToast({title: '分享成功', icon: 'success', duration: 2000})
},
fail: function () {
Taro.showToast({title: '分享失败', icon: 'none', duration: 2000})
}
}
})
// 生成小程序码
const generateMiniProgramCode = async () => {
if (!dealerUser?.userId) {
@@ -376,29 +409,7 @@ const DealerQrcode: React.FC = () => {
</Button>
</View>
{/*<View className={'my-2 bg-white'}>*/}
{/* <Button*/}
{/* size="large"*/}
{/* block*/}
{/* icon={<Copy/>}*/}
{/* onClick={copyInviteInfo}*/}
{/* disabled={!dealerUser?.userId || codeLoading}*/}
{/* >*/}
{/* 复制邀请信息*/}
{/* </Button>*/}
{/*</View>*/}
{/*<View className={'my-2 bg-white'}>*/}
{/* <Button*/}
{/* size="large"*/}
{/* block*/}
{/* fill="outline"*/}
{/* icon={<Share/>}*/}
{/* onClick={shareMiniProgramCode}*/}
{/* disabled={!dealerUser?.userId || codeLoading}*/}
{/* >*/}
{/* 分享给好友*/}
{/* </Button>*/}
{/*</View>*/}
</View>
{/* 推广说明 */}

View File

@@ -17,6 +17,8 @@ import {useCart} from "@/hooks/useCart";
import {useConfig} from "@/hooks/useConfig";
import {parseInviteParams, saveInviteParams, trackInviteSource} from "@/utils/invite";
import { ensureLoggedIn } from '@/utils/auth'
import {getGltTicketTemplateByGoodsId} from "@/api/glt/gltTicketTemplate";
import type {GltTicketTemplate} from "@/api/glt/gltTicketTemplate/model";
const GoodsDetail = () => {
const [statusBarHeight, setStatusBarHeight] = useState<number>(44);
@@ -32,6 +34,8 @@ const GoodsDetail = () => {
title: '',
content: ''
})
// 水票套票模板:存在时该商品不允许加入购物车(购物车无法支付此类商品)
const [ticketTemplate, setTicketTemplate] = useState<GltTicketTemplate | null>(null)
// const [selectedSku, setSelectedSku] = useState<ShopGoodsSku | null>(null);
const [loading, setLoading] = useState(false);
const router = Taro.getCurrentInstance().router;
@@ -60,9 +64,25 @@ const GoodsDetail = () => {
}, [goodsId])
// 处理加入购物车
const handleAddToCart = () => {
const handleAddToCart = async () => {
if (!goods) return;
// 水票套票商品:不允许加入购物车(购物车无法支付)
// 优先使用已加载的 ticketTemplate若尚未加载则补一次查询
let tpl = ticketTemplate
if (!tpl && goods?.goodsId) {
try {
tpl = await getGltTicketTemplateByGoodsId(Number(goods.goodsId))
setTicketTemplate(tpl)
} catch (_e) {
tpl = null
}
}
if (tpl) {
Taro.showToast({title: '该商品为水票套票商品,请点击“立即购买”下单', icon: 'none'})
return
}
if (!ensureLoggedIn(`/shop/goodsDetail/index?id=${goods.goodsId}`)) return
// 如果有规格,显示规格选择器
@@ -99,11 +119,26 @@ const GoodsDetail = () => {
};
// 规格选择确认回调
const handleSpecConfirm = (sku: ShopGoodsSku, quantity: number, action?: 'cart' | 'buy') => {
const handleSpecConfirm = async (sku: ShopGoodsSku, quantity: number, action?: 'cart' | 'buy') => {
// setSelectedSku(sku);
setShowSpecSelector(false);
if (action === 'cart') {
// 水票套票商品:不允许加入购物车(购物车无法支付)
let tpl = ticketTemplate
if (!tpl && goods?.goodsId) {
try {
tpl = await getGltTicketTemplateByGoodsId(Number(goods.goodsId))
setTicketTemplate(tpl)
} catch (_e) {
tpl = null
}
}
if (tpl) {
Taro.showToast({title: '该商品为水票套票商品,请点击“立即购买”下单', icon: 'none'})
return
}
// 加入购物车
addToCart({
goodsId: goods!.goodsId!,
@@ -143,14 +178,18 @@ const GoodsDetail = () => {
}
useEffect(() => {
let alive = true
Taro.getSystemInfo({
success: (res) => {
if (!alive) return
setWindowWidth(res.windowWidth)
setStatusBarHeight(Number(res.statusBarHeight) + 5)
},
});
if (goodsId) {
setLoading(true);
// 切换商品时先重置套票模板,避免复用上一个商品状态
setTicketTemplate(null)
// 加载商品详情
getShopGoods(Number(goodsId))
@@ -159,6 +198,7 @@ const GoodsDetail = () => {
if (res.content) {
res.content = wxParse(res.content);
}
if (!alive) return
setGoods(res);
if (res.files) {
const arr = JSON.parse(res.files);
@@ -169,12 +209,25 @@ const GoodsDetail = () => {
console.error("Failed to fetch goods detail:", error);
})
.finally(() => {
if (!alive) return
setLoading(false);
});
// 查询商品是否绑定水票模板(失败/无数据时不影响正常浏览)
getGltTicketTemplateByGoodsId(Number(goodsId))
.then((tpl) => {
if (!alive) return
setTicketTemplate(tpl)
})
.catch((_e) => {
if (!alive) return
setTicketTemplate(null)
})
// 加载商品规格
listShopGoodsSpec({goodsId: Number(goodsId)} as any)
.then((data) => {
if (!alive) return
setSpecs(data || []);
})
.catch((error) => {
@@ -184,12 +237,16 @@ const GoodsDetail = () => {
// 加载商品SKU
listShopGoodsSku({goodsId: Number(goodsId)} as any)
.then((data) => {
if (!alive) return
setSkus(data || []);
})
.catch((error) => {
console.error("Failed to fetch goods skus:", error);
});
}
return () => {
alive = false
}
}, [goodsId]);
// 分享给好友