import React, { useEffect, useState } from 'react' import { View, Text, Image, ScrollView } from '@tarojs/components' import Taro, { useRouter } from '@tarojs/taro' import { getShopSeckill, createSeckillOrder } from '@/api/shop/shopSeckill' import { getShopGoods } from '@/api/shop/shopGoods' import type { ShopSeckill } from '@/api/shop/shopSeckill/model' import type { ShopGoods } from '@/api/shop/shopGoods/model' import type { ShopGoodsSku } from '@/api/shop/shopGoodsSku/model' import SkuSelector from '@/components/business/SkuSelector' definePageConfig({ navigationBarTitleText: '秒杀详情', }) const SeckillDetailPage: React.FC = () => { const router = useRouter() const seckillId = Number(router.params.seckillId || 0) const [seckill, setSeckill] = useState(null) const [product, setProduct] = useState(null) const [loading, setLoading] = useState(true) const [submitting, setSubmitting] = useState(false) const [currentTime, setCurrentTime] = useState(Date.now()) const [countdown, setCountdown] = useState('') const [skuVisible, setSkuVisible] = useState(false) useEffect(() => { if (seckillId) { fetchData() } const timer = setInterval(() => { setCurrentTime(Date.now()) }, 1000) return () => clearInterval(timer) }, [seckillId]) useEffect(() => { if (seckill) { updateCountdown() } }, [currentTime, seckill]) const fetchData = async () => { try { setLoading(true) const res = await getShopSeckill(seckillId) if (res.code === 0 && res.data) { const data = res.data setSeckill(data) // 加载商品完整信息(含规格/SKU) if (data.goodsId) { fetchGoodsDetail(data.goodsId) } } } catch (err) { console.error('获取秒杀详情失败', err) Taro.showToast({ title: '加载失败', icon: 'none' }) } finally { setLoading(false) } } const fetchGoodsDetail = async (goodsId: number) => { try { const res = await getShopGoods(goodsId) if (res.code === 0 && res.data) { setProduct(res.data) } } catch (err) { console.error('获取商品详情失败', err) } } const updateCountdown = () => { if (!seckill?.endTime) return const end = new Date(seckill.endTime).getTime() const diff = end - currentTime if (diff <= 0) { setCountdown('已结束') return } const hours = Math.floor(diff / (1000 * 60 * 60)) const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)) const seconds = Math.floor((diff % (1000 * 60)) / 1000) setCountdown(`${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`) } const handleBuyClick = () => { if (!seckill) return if (seckill.status === 0) { Taro.showToast({ title: '活动尚未开始,请耐心等待', icon: 'none' }) return } if (seckill.status === 2 || new Date(seckill.endTime).getTime() <= Date.now()) { Taro.showToast({ title: '活动已结束,下次再来', icon: 'none' }) return } if (seckill.stock <= 0) { Taro.showToast({ title: '已抢光,下次再来', icon: 'none' }) return } if (submitting) return // 先拉取商品规格(如果没有的话) if (!product && seckill.goodsId) { Taro.showLoading({ title: '加载规格...' }) getShopGoods(seckill.goodsId) .then(res => { Taro.hideLoading() if (res.code === 0 && res.data) { setProduct(res.data) setSkuVisible(true) } else { // 没有规格,直接下单 handleSeckillOrder(0, 1) } }) .catch(() => { Taro.hideLoading() handleSeckillOrder(0, 1) }) } else { setSkuVisible(true) } } const handleSeckillOrder = async (skuId: number, quantity: number) => { if (!seckill) return setSubmitting(true) try { const res = await createSeckillOrder({ seckillId: seckill.id!, goodsId: seckill.goodsId, skuId, quantity, }) if (res.code === 0) { Taro.showToast({ title: '抢购成功', icon: 'success' }) fetchData() // 刷新库存 } } catch (err: any) { Taro.showToast({ title: err?.message || '抢购失败', icon: 'none' }) } finally { setSubmitting(false) } } const handleSkuConfirm = (sku: ShopGoodsSku, quantity: number) => { handleSeckillOrder(sku.id || 0, quantity) } if (loading) { return ( 加载中... ) } if (!seckill) { return ( 秒杀活动不存在 ) } const progress = seckill.stock === 0 ? 100 : Math.round((seckill.soldCount / (seckill.soldCount + seckill.stock)) * 100) const isActive = seckill.status === 1 && seckill.stock > 0 return ( {/* 商品图片 */} {seckill.goodsImage || seckill.product?.image ? ( ) : ( 暂无图片 )} {/* 价格和倒计时 */} ¥{seckill.seckillPrice} ¥{seckill.product?.price || 0} {countdown} 距结束 {/* 商品信息 */} {seckill.goodsName || seckill.product?.name || '商品名称'} 限购{seckill.limitPerUser}件 已抢 {progress}% {isActive && 进行中} {!isActive && {seckill.status === 0 ? '未开始' : '已结束'}} 剩余 {seckill.stock} 件 已售 {seckill.soldCount} 件 {/* 抢购提示 */} 活动规则 • 秒杀商品限购{seckill.limitPerUser}件/人 • 秒杀商品不支持退款 • 请在15分钟内完成支付,逾期订单自动取消 {/* 商品详情 */} {seckill.product?.content && ( 商品详情 )} {/* 底部抢购按钮 */} {submitting ? '抢购中...' : isActive ? (seckill.stock > 0 ? '立即抢购' : '已抢光') : seckill.status === 0 ? '即将开始' : '已结束'} {/* SKU 选择器 */} setSkuVisible(false)} onConfirm={handleSkuConfirm} /> ) } export default SeckillDetailPage