import React, { useState, useEffect } from 'react'; import { useParams, useLocation } from 'react-router-dom'; import axios from 'axios'; import './ProductDetail.css'; const ProductDetail = () => { const { series, sub, idx } = useParams(); const location = useLocation(); const product = location.state?.product; const [images, setImages] = useState([]); const [active, setActive] = useState(0); const [specs, setSpecs] = useState([]); const [sections, setSections] = useState([]); const [expanded, setExpanded] = useState({}); const [specsLoading, setSpecsLoading] = useState(false); const [imgOpen, setImgOpen] = useState(true); const [highlightsOpen, setHighlightsOpen] = useState(false); const [compareOpen, setCompareOpen] = useState(false); const [promoOpen, setPromoOpen] = useState(false); const [faqOpen, setFaqOpen] = useState(false); const [reviewOpen, setReviewOpen] = useState(false); useEffect(() => { const srcs = product ? (Array.isArray(product.images) && product.images.length ? product.images : (product.image ? [product.image] : [])) : []; setImages(srcs); setActive(0); }, [product]); useEffect(() => { const loadImages = async () => { try { const qs = new URLSearchParams({ series, sub }).toString(); const res = await fetch(`/api/docs/images?${qs}`); const data = await res.json(); if (data.success && Array.isArray(data.data) && data.data.length) { setImages(data.data.map((d) => `${d.url}`)); } } catch {} }; loadImages(); }, [series, sub]); useEffect(() => { const loadSpecs = async () => { try { setSpecsLoading(true); const qs = new URLSearchParams({ series, sub }).toString(); const res = await fetch(`/api/docs/specs?${qs}`); const data = await res.json(); if (data.success && Array.isArray(data.data)) { if (data.data.length && typeof data.data[0]?.title === 'string' && Array.isArray(data.data[0]?.rows)) { setSections(data.data); setSpecs([]); setExpanded(Object.fromEntries(data.data.map((_, i) => [i, false]))); } else { setSections([{ title: '参数', rows: data.data }]); setSpecs(data.data); setExpanded({ 0: false }); } } } catch {} finally { setSpecsLoading(false); } }; loadSpecs(); }, [series, sub]); useEffect(() => { if (images.length <= 1) return; const timer = setInterval(() => { setActive((prev) => (prev + 1) % images.length); }, 5000); return () => clearInterval(timer); }, [images.length]); if (!product) { return (
未找到产品信息
); } const prevSlide = () => setActive((prev) => (prev - 1 + images.length) % images.length); const nextSlide = () => setActive((prev) => (prev + 1) % images.length); return (
{product.name}
{imgOpen && (
{images.length > 1 ? (
{images.map((src, idx) => (
{`${product.name}-${idx+1}`}
))}
{images.map((_, idx) => ( setActive(idx)} /> ))}
) : (
{(images.length ? images : (product?.image ? [product.image] : [])).map((src, idx) => ( {`${product.name}-${idx+1}`} ))}
)}
)} {highlightsOpen &&
} {compareOpen && (
{specsLoading ? (
加载中...
) : sections.length ? ( sections.map((sec, i) => { const isOpen = !!expanded[i]; const toggle = () => setExpanded((prev) => ({ ...prev, [i]: !prev[i] })); return (
{isOpen && (
{sec.rows.length ? ( sec.rows.map((row) => { const k = String(row.key || ''); const v = String(row.value || ''); const kb = /^\*\*.*\*\*$/.test(k) || k.startsWith('!'); const vb = /^\*\*.*\*\*$/.test(v) || v.startsWith('!'); const bold = kb || vb; const cleanKey = k.replace(/^!/, '').replace(/^\*\*/, '').replace(/\*\*$/, ''); const cleanVal = v.replace(/^!/, '').replace(/^\*\*/, '').replace(/\*\*$/, ''); return ( ); }) ) : ( )}
{cleanKey} {cleanVal}
暂无参数
)}
); }) ) : (
暂无参数
)}
)} {/* */} {promoOpen &&
} {faqOpen &&
} {reviewOpen && (
)}
); }; const DocsList = ({ series, sub }) => { const [docs, setDocs] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const handleDownload = async (d) => { try { const res = await axios.get(d.url, { responseType: 'blob' }); const blob = new Blob([res.data], { type: res.data.type || 'application/octet-stream' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = d.name || (String(d.url).split('/').pop() || '下载文件'); document.body.appendChild(a); a.click(); a.remove(); URL.revokeObjectURL(url); } catch (e) { setError('下载失败或文件不存在'); } }; const handleOpen = async (d) => { try { const res = await axios.get(d.url, { responseType: 'blob' }); const blob = new Blob([res.data], { type: 'application/pdf' }); const url = URL.createObjectURL(blob); window.open(url, '_blank'); setTimeout(() => URL.revokeObjectURL(url), 60000); } catch (e) { window.open(d.url, '_blank'); } }; React.useEffect(() => { const fetchDocs = async () => { try { setLoading(true); setError(''); const qs = new URLSearchParams({ series, sub }).toString(); const res = await fetch(`/api/docs/list?${qs}`); const data = await res.json(); if (data.success) { setDocs(data.data || []); } else { setError('获取资料失败'); } } catch (e) { setError('网络错误'); } finally { setLoading(false); } }; fetchDocs(); }, [series, sub]); if (loading) return
加载中...
; if (error) return
{error}
; if (!docs.length) return
暂无资料
; return ( ); }; export default ProductDetail;