feat(share): 添加分享功能并限制水票商品加入购物车
- 在二维码页面启用分享给朋友和分享到朋友圈功能 - 实现分享菜单显示和分享内容自定义逻辑 - 移除原有的复制邀请信息和分享给好友按钮 - 新增水票套票模板查询接口和类型定义 - 阻止水票套票商品加入购物车并提示用户立即购买 - 添加组件卸载时的清理逻辑防止内存泄漏 - 优化商品详情页异步操作的状态管理
This commit is contained in:
@@ -1,3 +1,6 @@
|
|||||||
export default definePageConfig({
|
export default definePageConfig({
|
||||||
navigationBarTitleText: '桂乐淘分享中心'
|
navigationBarTitleText: '桂乐淘分享中心',
|
||||||
|
// Enable "Share to friends" and "Share to Moments" (timeline) for this page.
|
||||||
|
enableShareAppMessage: true,
|
||||||
|
enableShareTimeline: true
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, {useState, useEffect} from 'react'
|
|||||||
import {View, Text, Image} from '@tarojs/components'
|
import {View, Text, Image} from '@tarojs/components'
|
||||||
import {Button, Loading} from '@nutui/nutui-react-taro'
|
import {Button, Loading} from '@nutui/nutui-react-taro'
|
||||||
import {Download, QrCode} from '@nutui/icons-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 {useDealerUser} from '@/hooks/useDealerUser'
|
||||||
import {generateInviteCode} from '@/api/invite'
|
import {generateInviteCode} from '@/api/invite'
|
||||||
// import type {InviteStats} from '@/api/invite'
|
// import type {InviteStats} from '@/api/invite'
|
||||||
@@ -16,6 +16,39 @@ const DealerQrcode: React.FC = () => {
|
|||||||
// const [statsLoading, setStatsLoading] = useState<boolean>(false)
|
// const [statsLoading, setStatsLoading] = useState<boolean>(false)
|
||||||
const {dealerUser, loading: dealerLoading, error, refresh} = useDealerUser()
|
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 () => {
|
const generateMiniProgramCode = async () => {
|
||||||
if (!dealerUser?.userId) {
|
if (!dealerUser?.userId) {
|
||||||
@@ -376,29 +409,7 @@ const DealerQrcode: React.FC = () => {
|
|||||||
保存小程序码到相册
|
保存小程序码到相册
|
||||||
</Button>
|
</Button>
|
||||||
</View>
|
</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>
|
||||||
|
|
||||||
{/* 推广说明 */}
|
{/* 推广说明 */}
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ import {useCart} from "@/hooks/useCart";
|
|||||||
import {useConfig} from "@/hooks/useConfig";
|
import {useConfig} from "@/hooks/useConfig";
|
||||||
import {parseInviteParams, saveInviteParams, trackInviteSource} from "@/utils/invite";
|
import {parseInviteParams, saveInviteParams, trackInviteSource} from "@/utils/invite";
|
||||||
import { ensureLoggedIn } from '@/utils/auth'
|
import { ensureLoggedIn } from '@/utils/auth'
|
||||||
|
import {getGltTicketTemplateByGoodsId} from "@/api/glt/gltTicketTemplate";
|
||||||
|
import type {GltTicketTemplate} from "@/api/glt/gltTicketTemplate/model";
|
||||||
|
|
||||||
const GoodsDetail = () => {
|
const GoodsDetail = () => {
|
||||||
const [statusBarHeight, setStatusBarHeight] = useState<number>(44);
|
const [statusBarHeight, setStatusBarHeight] = useState<number>(44);
|
||||||
@@ -32,6 +34,8 @@ const GoodsDetail = () => {
|
|||||||
title: '',
|
title: '',
|
||||||
content: ''
|
content: ''
|
||||||
})
|
})
|
||||||
|
// 水票套票模板:存在时该商品不允许加入购物车(购物车无法支付此类商品)
|
||||||
|
const [ticketTemplate, setTicketTemplate] = useState<GltTicketTemplate | null>(null)
|
||||||
// const [selectedSku, setSelectedSku] = useState<ShopGoodsSku | null>(null);
|
// const [selectedSku, setSelectedSku] = useState<ShopGoodsSku | null>(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const router = Taro.getCurrentInstance().router;
|
const router = Taro.getCurrentInstance().router;
|
||||||
@@ -60,9 +64,25 @@ const GoodsDetail = () => {
|
|||||||
}, [goodsId])
|
}, [goodsId])
|
||||||
|
|
||||||
// 处理加入购物车
|
// 处理加入购物车
|
||||||
const handleAddToCart = () => {
|
const handleAddToCart = async () => {
|
||||||
if (!goods) return;
|
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
|
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);
|
// setSelectedSku(sku);
|
||||||
setShowSpecSelector(false);
|
setShowSpecSelector(false);
|
||||||
|
|
||||||
if (action === 'cart') {
|
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({
|
addToCart({
|
||||||
goodsId: goods!.goodsId!,
|
goodsId: goods!.goodsId!,
|
||||||
@@ -143,14 +178,18 @@ const GoodsDetail = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
let alive = true
|
||||||
Taro.getSystemInfo({
|
Taro.getSystemInfo({
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
|
if (!alive) return
|
||||||
setWindowWidth(res.windowWidth)
|
setWindowWidth(res.windowWidth)
|
||||||
setStatusBarHeight(Number(res.statusBarHeight) + 5)
|
setStatusBarHeight(Number(res.statusBarHeight) + 5)
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (goodsId) {
|
if (goodsId) {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
// 切换商品时先重置套票模板,避免复用上一个商品状态
|
||||||
|
setTicketTemplate(null)
|
||||||
|
|
||||||
// 加载商品详情
|
// 加载商品详情
|
||||||
getShopGoods(Number(goodsId))
|
getShopGoods(Number(goodsId))
|
||||||
@@ -159,6 +198,7 @@ const GoodsDetail = () => {
|
|||||||
if (res.content) {
|
if (res.content) {
|
||||||
res.content = wxParse(res.content);
|
res.content = wxParse(res.content);
|
||||||
}
|
}
|
||||||
|
if (!alive) return
|
||||||
setGoods(res);
|
setGoods(res);
|
||||||
if (res.files) {
|
if (res.files) {
|
||||||
const arr = JSON.parse(res.files);
|
const arr = JSON.parse(res.files);
|
||||||
@@ -169,12 +209,25 @@ const GoodsDetail = () => {
|
|||||||
console.error("Failed to fetch goods detail:", error);
|
console.error("Failed to fetch goods detail:", error);
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
|
if (!alive) return
|
||||||
setLoading(false);
|
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)
|
listShopGoodsSpec({goodsId: Number(goodsId)} as any)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
|
if (!alive) return
|
||||||
setSpecs(data || []);
|
setSpecs(data || []);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@@ -184,12 +237,16 @@ const GoodsDetail = () => {
|
|||||||
// 加载商品SKU
|
// 加载商品SKU
|
||||||
listShopGoodsSku({goodsId: Number(goodsId)} as any)
|
listShopGoodsSku({goodsId: Number(goodsId)} as any)
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
|
if (!alive) return
|
||||||
setSkus(data || []);
|
setSkus(data || []);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.error("Failed to fetch goods skus:", error);
|
console.error("Failed to fetch goods skus:", error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
return () => {
|
||||||
|
alive = false
|
||||||
|
}
|
||||||
}, [goodsId]);
|
}, [goodsId]);
|
||||||
|
|
||||||
// 分享给好友
|
// 分享给好友
|
||||||
|
|||||||
Reference in New Issue
Block a user