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) => (
))}
{images.map((_, idx) => (
setActive(idx)}
/>
))}
) : (
{(images.length ? images : (product?.image ? [product.image] : [])).map((src, idx) => (

))}
)}
)}
{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;