import {useEffect, useState} from "react"; import {Image, Badge, Popup, CellGroup, Cell} from "@nutui/nutui-react-taro"; import {ArrowLeft, Headphones, Share, Cart, ArrowRight} from "@nutui/icons-react-taro"; import Taro, {useShareAppMessage} from "@tarojs/taro"; import {RichText, View, Text} from '@tarojs/components' import {ShopGoods} from "@/api/shop/shopGoods/model"; import {getShopGoods} from "@/api/shop/shopGoods"; import {listShopGoodsSpec} from "@/api/shop/shopGoodsSpec"; import {ShopGoodsSpec} from "@/api/shop/shopGoodsSpec/model"; import {listShopGoodsSku} from "@/api/shop/shopGoodsSku"; import {ShopGoodsSku} from "@/api/shop/shopGoodsSku/model"; import {Swiper} from '@nutui/nutui-react-taro' import navTo, {wxParse} from "@/utils/common"; import SpecSelector from "@/components/SpecSelector"; import "./index.scss"; 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(44); const [windowWidth, setWindowWidth] = useState(390) const [goods, setGoods] = useState(null); const [files, setFiles] = useState([]); const [specs, setSpecs] = useState([]); const [skus, setSkus] = useState([]); const [showSpecSelector, setShowSpecSelector] = useState(false); const [specAction, setSpecAction] = useState<'cart' | 'buy'>('cart'); const [showBottom, setShowBottom] = useState(false) const [bottomItem, setBottomItem] = useState({ title: '', content: '' }) // 水票套票模板:存在时该商品不允许加入购物车(购物车无法支付此类商品) const [ticketTemplate, setTicketTemplate] = useState(null) const [ticketTemplateChecked, setTicketTemplateChecked] = useState(false) // const [selectedSku, setSelectedSku] = useState(null); const [loading, setLoading] = useState(false); const router = Taro.getCurrentInstance().router; const goodsId = router?.params?.id; // 使用购物车Hook const {cartCount, addToCart} = useCart() const {config} = useConfig() // 如果从分享链接进入(携带 inviter/source/t),且当前未登录,则暂存邀请信息用于注册后绑定关系 useEffect(() => { try { const currentUserId = Taro.getStorageSync('UserId') if (currentUserId) return const inviteParams = parseInviteParams({query: router?.params}) if (inviteParams?.inviter) { saveInviteParams(inviteParams) trackInviteSource(inviteParams.source || 'share', parseInt(inviteParams.inviter)) } } catch (e) { // 邀请参数解析/存储失败不影响正常浏览商品 console.error('商品详情页处理邀请参数失败:', e) } // router 在 Taro 中可能不稳定;这里仅在 goodsId 变化时尝试处理一次即可 }, [goodsId]) // 处理加入购物车 const handleAddToCart = async () => { if (!goods) return; // 水票套票商品:不允许加入购物车(购物车无法支付) // 优先使用已加载的 ticketTemplate;若尚未加载则补一次查询 let tpl = ticketTemplate let checked = ticketTemplateChecked if (!tpl && goods?.goodsId) { try { tpl = await getGltTicketTemplateByGoodsId(Number(goods.goodsId)) setTicketTemplate(tpl) setTicketTemplateChecked(true) checked = true } catch (_e) { tpl = null setTicketTemplateChecked(true) checked = true } } if (!checked || tpl) { return } if (!ensureLoggedIn(`/shop/goodsDetail/index?id=${goods.goodsId}`)) return // 如果有规格,显示规格选择器 if (specs.length > 0) { setSpecAction('cart'); setShowSpecSelector(true); return; } // 没有规格,直接加入购物车 addToCart({ goodsId: goods.goodsId!, name: goods.name || '', price: goods.price || '0', image: goods.image || '' }); }; // 处理立即购买 const handleBuyNow = () => { if (!goods) return; if (!ensureLoggedIn(`/shop/orderConfirm/index?goodsId=${goods.goodsId}`)) return // 如果有规格,显示规格选择器 if (specs.length > 0) { setSpecAction('buy'); setShowSpecSelector(true); return; } // 没有规格,直接购买 navTo(`/shop/orderConfirm/index?goodsId=${goods?.goodsId}`, true); }; // 规格选择确认回调 const handleSpecConfirm = async (sku: ShopGoodsSku, quantity: number, action?: 'cart' | 'buy') => { // setSelectedSku(sku); setShowSpecSelector(false); if (action === 'cart') { // 水票套票商品:不允许加入购物车(购物车无法支付) let tpl = ticketTemplate let checked = ticketTemplateChecked if (!tpl && goods?.goodsId) { try { tpl = await getGltTicketTemplateByGoodsId(Number(goods.goodsId)) setTicketTemplate(tpl) setTicketTemplateChecked(true) checked = true } catch (_e) { tpl = null setTicketTemplateChecked(true) checked = true } } if (!checked || tpl) { return } // 加入购物车 addToCart({ goodsId: goods!.goodsId!, skuId: sku.id, name: goods!.name || '', price: sku.price || goods!.price || '0', image: goods!.image || '', specInfo: sku.sku, // sku字段包含规格信息 }, quantity); } else if (action === 'buy') { // 立即购买 const orderData = { goodsId: goods!.goodsId!, skuId: sku.id, quantity, price: sku.price || goods!.price || '0' }; navTo(`/shop/orderConfirm/index?orderData=${encodeURIComponent(JSON.stringify(orderData))}`, true); } else { // 默认情况:如果action未定义,默认为立即购买 const orderData = { goodsId: goods!.goodsId!, skuId: sku.id, quantity, price: sku.price || goods!.price || '0' }; navTo(`/shop/orderConfirm/index?orderData=${encodeURIComponent(JSON.stringify(orderData))}`, true); } }; const openBottom = (title: string, content: string) => { setBottomItem({ title, content }) setShowBottom(true) } 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) setTicketTemplateChecked(false) // 加载商品详情 getShopGoods(Number(goodsId)) .then((res) => { // 处理富文本内容,去掉图片间距 if (res.content) { res.content = wxParse(res.content); } if (!alive) return setGoods(res); if (res.files) { const arr = JSON.parse(res.files); arr.length > 0 && setFiles(arr); } }) .catch((error) => { console.error("Failed to fetch goods detail:", error); }) .finally(() => { if (!alive) return setLoading(false); }); // 查询商品是否绑定水票模板(失败/无数据时不影响正常浏览) getGltTicketTemplateByGoodsId(Number(goodsId)) .then((tpl) => { if (!alive) return setTicketTemplate(tpl) setTicketTemplateChecked(true) }) .catch((_e) => { if (!alive) return setTicketTemplate(null) setTicketTemplateChecked(true) }) // 加载商品规格 listShopGoodsSpec({goodsId: Number(goodsId)} as any) .then((data) => { if (!alive) return setSpecs(data || []); }) .catch((error) => { console.error("Failed to fetch goods specs:", error); }); // 加载商品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]); // 分享给好友 useShareAppMessage(() => { const inviter = Taro.getStorageSync('UserId') const sharePath = inviter ? `/shop/goodsDetail/index?id=${goodsId}&inviter=${inviter}&source=goods_share&t=${Date.now()}` : `/shop/goodsDetail/index?id=${goodsId}` return { title: goods?.name || '精选商品', path: sharePath, imageUrl: goods?.image ? `${goods.image}?x-oss-process=image/resize,w_500,h_400,m_fill` : undefined, // 分享图片,调整为5:4比例 success: function (res: any) { console.log('分享成功', res); Taro.showToast({ title: '分享成功', icon: 'success', duration: 2000 }); }, fail: function (res: any) { console.log('分享失败', res); Taro.showToast({ title: '分享失败', icon: 'none', duration: 2000 }); } }; }); if (!goods || loading) { return 加载中...; } const showAddToCart = ticketTemplateChecked && !ticketTemplate return ( Taro.navigateBack()} > Taro.switchTab({url: `/pages/cart/cart`})}> { files.length > 0 && ( {files.map((item) => ( ))} ) } { files.length == 0 && ( ) } <> {goods.buyingPrice} 会员价 ¥{goods.salePrice}/{goods.unitName} 已售 {goods.sales} {goods.name} {goods.comments} { config?.deliveryText && ( {config?.deliveryText || '14:30下单,明天配送'} } onClick={() => openBottom('配送', `${config?.deliveryText}`)}/> {config?.guaranteeText || '支持7天无理由退货'} } onClick={() => openBottom('保障', `${config?.guaranteeText}`)}/> ) } {config?.openComments == '1' && ( 查看全部 } onClick={() => navTo(`/shop/comments/index`)}/> 暂无评价 )} {/*底部弹窗*/} { setShowBottom(false) }} lockScroll > {bottomItem.title} {bottomItem.content} {/*底部购买按钮*/} {showAddToCart && ( handleAddToCart()}>加入购物车 )} handleBuyNow()}>立即购买 {/* 规格选择器 */} {showSpecSelector && ( setShowSpecSelector(false)} /> )} ); }; export default GoodsDetail;