import { useCallback, useEffect, useMemo, useState } from 'react'; import { View, Text, Image } from '@tarojs/components'; import '../../styles/product-detail.css'; import Taro, { useDidShow, usePageScroll, useRouter, useShareAppMessage, useShareTimeline, } from '@tarojs/taro'; import type { ProductDetailContentDto, ProductSkuDto, ProductSpecAttrDto } from '@dukang/shared-types'; import PageShell from '../../components/PageShell'; import PageNavBar from '../../components/PageNavBar'; import ProductCarousel from '../../components/ProductCarousel'; import WechatShareReady from '../../components/WechatShareReady'; import BenefitFigure from '../../components/BenefitFigure'; import { goLogin } from '../../lib/auth-nav'; import { ensurePayReady } from '../../lib/pay-ready'; import { isLoggedIn, request, toast } from '../../lib/api'; import { getProductCarouselImages, getProductDetailImages, getProductMainImage, type ProductImageSource, } from '../../lib/product-images'; import { canBuyOnline, canPickupOnSite, normalizeFulfillmentFlags, } from '../../lib/product-fulfillment'; import { buildSceneSharePayload, toWeappShareMessage, toWeappShareTimeline, } from '../../lib/wechat-share'; import iconHome from '../../assets/tabbar/home.png'; import { usePageView } from '../../lib/usePageView'; type Product = ProductImageSource & { id: string; name: string; subtitle?: string | null; price: number; benefitAmount?: number; benefitDisplay?: number; detailContent?: ProductDetailContentDto | null; allowOnSitePickup?: boolean; allowOnlinePurchase?: boolean; allowCrossCityDelivery?: boolean; specEnabled?: boolean; specAttrs?: ProductSpecAttrDto[]; skus?: ProductSkuDto[]; defaultSkuId?: string; saleUnit?: 'BOTTLE' | 'BOX'; }; function findSku( skus: ProductSkuDto[], selected: Record, attrs: ProductSpecAttrDto[], ): ProductSkuDto | undefined { const valueIds = attrs.map((a) => selected[a.id]).filter(Boolean); if (valueIds.length !== attrs.length) return undefined; const key = [...valueIds].sort().join('_'); return skus.find((s) => [...s.specValueIds].sort().join('_') === key); } function isValueAvailable( skus: ProductSkuDto[], attrs: ProductSpecAttrDto[], selected: Record, attrId: string, valueId: string, ): boolean { const trial = { ...selected, [attrId]: valueId }; const partialIds = attrs.map((a) => trial[a.id]).filter(Boolean); return skus.some((s) => { if (s.status !== 'ON_SALE') return false; return partialIds.every((id) => s.specValueIds.includes(id)); }); } export default function ProductDetailPage() { const router = useRouter(); const productId = router.params.id ?? ''; usePageView( 'product_detail_view', productId ? { refType: 'PRODUCT', refId: productId, productId } : undefined, ); const [product, setProduct] = useState(null); const [headerSolid, setHeaderSolid] = useState(false); const [selected, setSelected] = useState>({}); usePageScroll(({ scrollTop }) => { setHeaderSolid(scrollTop > 100); }); const loadProduct = useCallback(() => { if (!productId) return; request(`/catalog/products/${productId}`) .then((p) => { if (!p) { setProduct(null); toast('商品不存在或暂未开放'); return; } const normalized = normalizeFulfillmentFlags(p); setProduct(normalized); const attrs = p.specAttrs ?? []; const skus = p.skus ?? []; const def = skus.find((s) => s.id === p.defaultSkuId) || skus.find((s) => s.isDefault && s.status === 'ON_SALE') || skus.find((s) => s.status === 'ON_SALE') || skus[0]; if (def && attrs.length) { const next: Record = {}; for (const attr of attrs) { const hit = attr.values.find((v) => def.specValueIds.includes(v.id)); if (hit) next[attr.id] = hit.id; } setSelected(next); } else { setSelected({}); } }) .catch((e) => { setProduct(null); toast(e instanceof Error ? e.message : '加载失败'); }); }, [productId]); useEffect(() => { loadProduct(); }, [loadProduct]); useDidShow(() => { loadProduct(); }); const attrs = product?.specAttrs ?? []; const skus = product?.skus ?? []; const specEnabled = !!(product?.specEnabled && attrs.length > 0); const activeSku = useMemo(() => { if (!product) return undefined; if (!specEnabled) { return ( skus.find((s) => s.id === product.defaultSkuId) || skus.find((s) => s.isDefault) || skus.find((s) => s.status === 'ON_SALE') || skus[0] ); } return findSku(skus, selected, attrs); }, [product, specEnabled, skus, selected, attrs]); const displayPrice = activeSku ? Number(activeSku.price) : Number(product?.price ?? 0); const displayBenefit = activeSku ? Number(activeSku.benefitAmount) : Number(product?.benefitDisplay ?? product?.benefitAmount ?? product?.price ?? 0); const fulfillment = activeSku ? { allowOnlinePurchase: activeSku.allowOnlinePurchase, allowCrossCityDelivery: activeSku.allowCrossCityDelivery, allowOnSitePickup: activeSku.allowOnSitePickup, } : product; const carouselImages = (() => { const base = getProductCarouselImages(product); const skuImg = activeSku?.imageUrl?.trim(); if (!skuImg) return base; return [skuImg, ...base.filter((url) => url !== skuImg)]; })(); const sharePayload = useMemo( () => buildSceneSharePayload('productDetail', { path: `/pages/product-detail/index?id=${productId}`, dynamicTitle: product?.name, dynamicDesc: product?.subtitle, dynamicImageUrl: (activeSku?.imageUrl?.trim() || (product ? getProductMainImage(product) : undefined)), }), [product, productId, activeSku], ); useShareAppMessage(() => toWeappShareMessage(sharePayload)); useShareTimeline(() => toWeappShareTimeline(sharePayload, productId ? `id=${productId}` : ''), ); function goBack() { const pages = Taro.getCurrentPages(); if (pages.length > 1) { Taro.navigateBack(); return; } Taro.switchTab({ url: '/pages/home/index' }); } function goHome() { Taro.switchTab({ url: '/pages/home/index' }); } function ensureSkuSelected(): string | null { if (!specEnabled) return activeSku?.id ?? product?.defaultSkuId ?? null; if (!activeSku || activeSku.status !== 'ON_SALE') { toast('请选择完整规格'); return null; } return activeSku.id; } async function goBuy() { if (!productId) return; const skuId = ensureSkuSelected(); if (specEnabled && !skuId) return; const qty = activeSku?.saleUnit === 'BOX' ? 1 : 2; const qs = [`productId=${productId}`, `qty=${qty}`]; if (skuId) qs.push(`skuId=${skuId}`); const returnPath = `/pages/order-confirm/index?${qs.join('&')}`; if (!isLoggedIn()) { goLogin(returnPath); return; } const ready = await ensurePayReady(returnPath); if (!ready) return; Taro.navigateTo({ url: returnPath }); } async function goOnSitePickup() { if (!productId) return; const skuId = ensureSkuSelected(); if (specEnabled && !skuId) return; const qty = activeSku?.saleUnit === 'BOX' ? 1 : 1; const qs = [`productId=${productId}`, `qty=${qty}`]; if (skuId) qs.push(`skuId=${skuId}`); const returnPath = `/pages/order-confirm-pickup/index?${qs.join('&')}`; if (!isLoggedIn()) { goLogin(returnPath); return; } const ready = await ensurePayReady(returnPath); if (!ready) return; Taro.navigateTo({ url: returnPath }); } if (!product) { return ( 加载中… ); } const allowOnline = canBuyOnline(fulfillment ?? {}); const allowOnSite = canPickupOnSite(fulfillment ?? {}); const detailImages = getProductDetailImages(product); const detail = product.detailContent ?? {}; const features = detail.features ?? []; return ( ¥ {displayPrice.toFixed(2)} {product.name} {product.subtitle ? ( {product.subtitle} ) : null} {activeSku?.specText ? ( {activeSku.specText} ) : null} {specEnabled ? ( {attrs.map((attr) => ( {attr.name} {attr.values.map((val) => { const active = selected[attr.id] === val.id; const available = isValueAvailable(skus, attrs, selected, attr.id, val.id); return ( { if (!available) return; setSelected((prev) => ({ ...prev, [attr.id]: val.id })); }} > {val.name} ); })} ))} ) : null} 买杜康美酒 · 享全城好客礼遇 购酒即享本城专属好客权益,为您安排一场地道饭局。权益金可直接用于本地签约门店消费,到店享用,醇香美酒配佳肴。 商品详情 {detailImages.map((src, index) => ( ))} {(detail.storyTitle || detail.storyText || features.length > 0) ? ( {(detail.storyTitle || detail.storyText) ? ( {detail.storyTitle ? ( {detail.storyTitle} ) : null} {detail.storyText ? ( {detail.storyText} ) : null} ) : null} {features.length > 0 ? ( {features.map((f) => ( {f.title} {f.desc} ))} ) : null} ) : null} 首页 {allowOnSite ? ( void goOnSitePickup()}> 现场取货 ) : null} {allowOnline ? ( void goBuy()}> 立即购买 ) : null} {!allowOnline && !allowOnSite ? ( 暂不可购 ) : null} ); }