diff --git a/src/pages/AircraftDetail.css b/src/pages/AircraftDetail.css
deleted file mode 100755
index 8fed1d8..0000000
--- a/src/pages/AircraftDetail.css
+++ /dev/null
@@ -1,221 +0,0 @@
-.aircraft-detail-page {
- min-height: 100vh;
- background-color: #f5f5f5;
-}
-
-.page-header {
- background: white;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
- margin-bottom: 20px;
-}
-
-.header-content {
- max-width: 1200px;
- margin: 0 auto;
- padding: 20px;
- display: flex;
- justify-content: space-between;
- align-items: center;
-}
-
-.header-content h1 {
- margin: 0;
- color: #333;
- font-size: 24px;
-}
-
-.aircraft-detail-card {
- background: white;
- border-radius: 8px;
- padding: 32px;
- margin-bottom: 24px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-}
-
-.aircraft-header {
- border-bottom: 2px solid #f0f0f0;
- padding-bottom: 24px;
- margin-bottom: 24px;
-}
-
-.aircraft-code-large {
- display: inline-block;
- background: #1890ff;
- color: white;
- padding: 6px 16px;
- border-radius: 6px;
- font-size: 14px;
- font-weight: 600;
- margin-bottom: 12px;
-}
-
-.aircraft-name-large {
- margin: 0 0 16px 0;
- color: #333;
- font-size: 32px;
- font-weight: 600;
-}
-
-.aircraft-meta {
- display: flex;
- gap: 12px;
-}
-
-.manufacturer-badge,
-.type-badge {
- padding: 6px 12px;
- border-radius: 4px;
- font-size: 14px;
-}
-
-.manufacturer-badge {
- background: #e6f7ff;
- color: #1890ff;
-}
-
-.type-badge {
- background: #f6ffed;
- color: #52c41a;
-}
-
-.aircraft-description {
- margin-bottom: 32px;
-}
-
-.aircraft-description h3,
-.aircraft-specifications h3 {
- margin: 0 0 16px 0;
- color: #333;
- font-size: 20px;
- font-weight: 600;
-}
-
-.aircraft-description p {
- color: #666;
- line-height: 1.8;
- font-size: 15px;
-}
-
-.aircraft-specifications {
- margin-top: 32px;
-}
-
-.specs-grid {
- display: grid;
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
- gap: 20px;
-}
-
-.spec-item {
- display: flex;
- flex-direction: column;
- padding: 16px;
- background: #fafafa;
- border-radius: 6px;
-}
-
-.spec-label {
- color: #999;
- font-size: 12px;
- margin-bottom: 8px;
- text-transform: uppercase;
-}
-
-.spec-value {
- color: #333;
- font-size: 18px;
- font-weight: 600;
-}
-
-.pdf-materials-section {
- background: white;
- border-radius: 8px;
- padding: 32px;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
-}
-
-.section-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin-bottom: 24px;
- flex-wrap: wrap;
- gap: 16px;
-}
-
-.section-header h3 {
- margin: 0;
- color: #333;
- font-size: 20px;
- font-weight: 600;
-}
-
-.search-input-small {
- padding: 8px 12px;
- border: 1px solid #d9d9d9;
- border-radius: 4px;
- font-size: 14px;
- min-width: 200px;
-}
-
-.search-input-small:focus {
- outline: none;
- border-color: #1890ff;
- box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
-}
-
-.pdf-materials-list {
- display: flex;
- flex-direction: column;
- gap: 16px;
-}
-
-.pdf-material-card {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 20px;
- background: #fafafa;
- border-radius: 6px;
- border: 1px solid #e8e8e8;
- transition: all 0.3s;
-}
-
-.pdf-material-card:hover {
- background: #f0f0f0;
- border-color: #1890ff;
-}
-
-.pdf-material-info {
- flex: 1;
-}
-
-.pdf-material-title {
- margin: 0 0 8px 0;
- color: #333;
- font-size: 16px;
- font-weight: 600;
-}
-
-.pdf-material-description {
- margin: 0 0 12px 0;
- color: #666;
- font-size: 14px;
-}
-
-.pdf-material-meta {
- display: flex;
- gap: 20px;
- color: #999;
- font-size: 12px;
-}
-
-.loading,
-.error-state,
-.empty-state {
- text-align: center;
- padding: 60px 20px;
- color: #999;
- font-size: 16px;
-}
-
diff --git a/src/pages/AircraftDetail.js b/src/pages/AircraftDetail.js
deleted file mode 100755
index f0b25d0..0000000
--- a/src/pages/AircraftDetail.js
+++ /dev/null
@@ -1,186 +0,0 @@
-import React, { useState, useEffect, useCallback } from 'react';
-import { useParams, useNavigate } from 'react-router-dom';
-import { useAuth } from '../context/AuthContext';
-import axios from 'axios';
-import './AircraftDetail.css';
-
-const AircraftDetail = () => {
- const { id } = useParams();
- const navigate = useNavigate();
- const { logout } = useAuth();
- const [aircraft, setAircraft] = useState(null);
- const [pdfMaterials, setPdfMaterials] = useState([]);
- const [loading, setLoading] = useState(true);
- const [searchTerm, setSearchTerm] = useState('');
-
- const fetchAircraftDetail = useCallback(async () => {
- try {
- setLoading(true);
- const response = await axios.get(`/api/aircraft/${id}`);
- if (response.data.success) {
- setAircraft(response.data.data);
- }
- } catch (error) {
- console.error('获取机型详情失败:', error);
- if (error.response?.status === 401) {
- logout();
- navigate('/login');
- }
- } finally {
- setLoading(false);
- }
- }, [id, logout, navigate]);
-
- const fetchPdfMaterials = useCallback(async () => {
- try {
- const response = await axios.get(`/api/pdf/aircraft/${id}`, {
- params: { search: searchTerm }
- });
- if (response.data.success) {
- setPdfMaterials(response.data.data);
- }
- } catch (error) {
- console.error('获取PDF资料失败:', error);
- }
- }, [id, searchTerm]);
-
- useEffect(() => {
- fetchAircraftDetail();
- fetchPdfMaterials();
- }, [fetchAircraftDetail, fetchPdfMaterials]);
-
- useEffect(() => {
- if (id) {
- fetchPdfMaterials();
- }
- }, [id, searchTerm, fetchPdfMaterials]);
-
-
-
- const handleViewPdf = async (materialId) => {
- try {
- const response = await axios.get(`/api/pdf/file/${materialId}`);
- if (response.data.success) {
- const fileUrl = `http://localhost:3001${response.data.data.fileUrl}`;
- window.open(fileUrl, '_blank');
- }
- } catch (error) {
- console.error('打开PDF失败:', error);
- alert('无法打开PDF文件,请确保文件存在');
- }
- };
-
- if (loading) {
- return (
-
- );
- }
-
- if (!aircraft) {
- return (
-
- );
- }
-
- return (
-
-
-
-
-
机型详情
-
-
-
-
-
{aircraft.code}
-
{aircraft.name}
-
- {aircraft.manufacturer}
- {aircraft.type}
-
-
-
-
-
机型简介
-
{aircraft.description}
-
-
-
-
技术规格
-
-
- 最大载客量
- {aircraft.specifications.maxPassengers} 人
-
-
- 最大航程
- {aircraft.specifications.maxRange}
-
-
- 巡航速度
- {aircraft.specifications.cruiseSpeed}
-
-
- 机长
- {aircraft.specifications.length}
-
-
- 翼展
- {aircraft.specifications.wingspan}
-
-
-
-
-
-
-
-
相关资料
- setSearchTerm(e.target.value)}
- className="search-input-small"
- />
-
-
- {pdfMaterials.length === 0 ? (
-
暂无相关资料
- ) : (
-
- {pdfMaterials.map((material) => (
-
-
-
{material.title}
-
{material.description}
-
- 上传日期: {material.uploadDate}
- 文件大小: {material.fileSize}
-
-
-
-
- ))}
-
- )}
-
-
-
- );
-};
-
-export default AircraftDetail;
-
diff --git a/src/pages/AircraftList.css b/src/pages/AircraftList.css
deleted file mode 100755
index ccac66c..0000000
--- a/src/pages/AircraftList.css
+++ /dev/null
@@ -1,142 +0,0 @@
-.aircraft-list-page {
- min-height: 100vh;
- background-color: #f5f5f5;
-}
-
-.page-header {
- background: white;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
- margin-bottom: 20px;
-}
-
-.header-content {
- max-width: 1200px;
- margin: 0 auto;
- padding: 20px;
- display: flex;
- justify-content: space-between;
- align-items: center;
-}
-
-.header-content h1 {
- margin: 0;
- color: #333;
- font-size: 24px;
-}
-
-.header-actions {
- display: flex;
- align-items: center;
- gap: 15px;
-}
-
-.user-info {
- color: #666;
- font-size: 14px;
-}
-
-.search-section {
- margin-bottom: 24px;
-}
-
-.search-input {
- width: 100%;
- max-width: 600px;
- padding: 12px 16px;
- border: 1px solid #d9d9d9;
- border-radius: 6px;
- font-size: 14px;
- transition: all 0.3s;
-}
-
-.search-input:focus {
- outline: none;
- border-color: #1890ff;
- box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
-}
-
-.loading {
- text-align: center;
- padding: 40px;
- color: #999;
-}
-
-.aircraft-grid {
- display: grid;
- grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
- gap: 20px;
-}
-
-.aircraft-card {
- background: white;
- border-radius: 8px;
- padding: 24px;
- cursor: pointer;
- transition: all 0.3s;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
- position: relative;
- border: 2px solid transparent;
-}
-
-.aircraft-card:hover {
- transform: translateY(-4px);
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
- border-color: #1890ff;
-}
-
-.aircraft-code {
- display: inline-block;
- background: #1890ff;
- color: white;
- padding: 4px 12px;
- border-radius: 4px;
- font-size: 12px;
- font-weight: 600;
- margin-bottom: 12px;
-}
-
-.aircraft-name {
- margin: 0 0 12px 0;
- color: #333;
- font-size: 20px;
- font-weight: 600;
-}
-
-.aircraft-info {
- display: flex;
- flex-direction: column;
- gap: 6px;
- color: #666;
- font-size: 14px;
-}
-
-.manufacturer {
- font-weight: 500;
-}
-
-.type {
- color: #999;
-}
-
-.card-arrow {
- position: absolute;
- right: 24px;
- top: 50%;
- transform: translateY(-50%);
- font-size: 24px;
- color: #d9d9d9;
- transition: all 0.3s;
-}
-
-.aircraft-card:hover .card-arrow {
- color: #1890ff;
- transform: translateY(-50%) translateX(4px);
-}
-
-.empty-state {
- text-align: center;
- padding: 60px 20px;
- color: #999;
- font-size: 16px;
-}
-
diff --git a/src/pages/AircraftList.js b/src/pages/AircraftList.js
deleted file mode 100755
index 1f089d7..0000000
--- a/src/pages/AircraftList.js
+++ /dev/null
@@ -1,89 +0,0 @@
-import React, { useState, useEffect, useCallback } from 'react';
-import { useNavigate, useLocation } from 'react-router-dom';
-import { useAuth } from '../context/AuthContext';
-import axios from 'axios';
-import './AircraftList.css';
-
-const AircraftList = () => {
- const [aircrafts, setAircrafts] = useState([]);
- const [loading, setLoading] = useState(true);
- const location = useLocation();
- const initialSearch = location.state?.searchTerm || '';
- const [searchTerm, setSearchTerm] = useState(initialSearch);
- const { logout } = useAuth();
- const navigate = useNavigate();
-
- const fetchAircrafts = useCallback(async () => {
- try {
- setLoading(true);
- const response = await axios.get('/api/aircraft/list', {
- params: { search: searchTerm }
- });
- if (response.data.success) {
- setAircrafts(response.data.data);
- }
- } catch (error) {
- console.error('获取机型列表失败:', error);
- if (error.response?.status === 401) {
- logout();
- navigate('/login');
- }
- } finally {
- setLoading(false);
- }
- }, [searchTerm, logout, navigate]);
-
- useEffect(() => {
- fetchAircrafts();
- }, [fetchAircrafts]);
-
- const handleAircraftClick = (id) => {
- navigate(`/aircraft/${id}`);
- };
-
- return (
-
-
-
通信技术部
-
- setSearchTerm(e.target.value)}
- className="search-input"
- />
-
-
- {loading ? (
-
加载中...
- ) : (
-
- {aircrafts.length === 0 ? (
-
未找到机型信息
- ) : (
- aircrafts.map((aircraft) => (
-
handleAircraftClick(aircraft.id)}
- >
-
{aircraft.code}
-
{aircraft.name}
-
- {aircraft.manufacturer}
- {aircraft.type}
-
-
→
-
- ))
- )}
-
- )}
-
-
- );
-};
-
-export default AircraftList;
-
diff --git a/src/pages/Home.js b/src/pages/Home.js
index 563cde0..ba86b35 100755
--- a/src/pages/Home.js
+++ b/src/pages/Home.js
@@ -5,26 +5,26 @@ const slidesData = [
{
id: 1,
title: '行业资讯:新机型发布',
- description: '最新机型亮相航空展,性能全面升级',
- image: 'http://localhost:3001/uploads/menu/home_1.png'
+ description: '最新机型亮相展览,性能全面升级',
+ image: '/uploads/menu/home_1.jpg'
},
{
id: 2,
title: '解决方案:资料管理优化',
description: '一体化资料管理平台上线,提效30%',
- image: 'http://localhost:3001/uploads/menu/home_2.png'
+ image: '/uploads/menu/home_2.jpg'
},
{
id: 3,
title: '产品更新:PDF在线预览',
description: '新增在线预览与多端适配功能',
- image: 'http://localhost:3001/uploads/menu/home_3.png'
+ image: '/uploads/menu/home_3.jpg'
},
{
id: 4,
title: '客户案例:数字化转型',
description: '多行业落地实践,推动数据驱动决策',
- image: 'http://localhost:3001/uploads/menu/home_4.png'
+ image: '/uploads/menu/home_4.jpg'
}
];
diff --git a/src/pages/Login.js b/src/pages/Login.js
index 1ef20c9..b3a28cf 100755
--- a/src/pages/Login.js
+++ b/src/pages/Login.js
@@ -18,11 +18,11 @@ const Login = () => {
const result = await login(username, password);
- if (result.success) {
- navigate('/aircraft');
- } else {
- setError(result.error);
- }
+ if (result.success) {
+ navigate('/');
+ } else {
+ setError(result.error);
+ }
setLoading(false);
};
diff --git a/src/pages/ProductDetail.css b/src/pages/ProductDetail.css
index 71467b8..8d35a44 100644
--- a/src/pages/ProductDetail.css
+++ b/src/pages/ProductDetail.css
@@ -15,14 +15,16 @@
.product-title { margin: 0; color: #333; font-size: 22px; font-weight: 600; }
.product-desc { color: #666; }
.specs-section { margin-top: 24px; }
-.specs-body { margin-top: 12px; background: #f6ffed; border: 1px solid #d9f7be; border-radius: 0; }
-.specs-group { margin: 12px 0; }
-.specs-group-header { width: 100%; text-align: left; padding: 10px 12px; border: 1px solid #d9f7be; border-radius: 4px; background: #f6ffed; cursor: pointer; font-weight: 600; }
-.specs-summary { padding: 12px 14px; border-bottom: 1px solid #f0f0f0; }
-.specs-table { width: 100%; border-collapse: collapse; border: 1px solid #d9d9d9; }
-.specs-table td { padding: 10px 14px; border: 1px solid #e8e8e8; }
-.specs-key { width: 180px; color: #666; }
-.specs-val { color: #333; }
+.specs-body { margin-top: 0; background: #fff; border: none; border-radius: 0; padding: 0; }
+.specs-group { margin: 0; }
+.specs-group-header { width: 100%; text-align: left; padding: 18px 0; border: none; background: transparent; cursor: pointer; font-weight: 700; color: #111; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #e8e8e8; }
+.specs-summary { padding: 12px 0; border-bottom: 1px solid #e8e8e8; }
+.specs-table { width: 100%; border-collapse: collapse; border: none; background: #fff; }
+.specs-table tr { border-bottom: 1px solid #e8e8e8; }
+.specs-table tr:last-child { border-bottom: none; }
+.specs-table td { padding: 12px 14px; border: none; }
+.specs-key { width: 220px; color: #666; text-align: left; }
+.specs-val { color: #1890ff; text-align: center; }
.error-state { text-align: center; padding: 60px 20px; color: #999; }
.docs-section { margin-top: 16px; }
.docs-list { list-style: none; padding: 0 8px 12px; margin: 0; }
@@ -37,7 +39,36 @@
.tag-specs { background: #f6ffed; color: #52c41a; }
.tag-docs { background: #fff7e6; color: #fa8c16; }
.carousel-full .section-tag { position: absolute; top: 12px; left: 12px; margin: 0; z-index: 2; }
-.specs-table tr:nth-child(even) { background: #fafafa; }
.docs-item:hover { background: #fafafa; }
-.docs-body { background: #fff7e6; border: 1px solid #ffd591; border-radius: 0; padding: 8px 8px; }
+.docs-body { background: #f5f5f5; border: none; border-radius: 0; padding: 8px 8px; }
+
+.pd-title { text-align: center; font-size: 24px; font-weight: 700; color: #111; padding: 16px 0; }
+.pd-panel { margin: 12px 0; }
+.pd-panel { border: none; border-radius: 0; background: #fff; }
+.pd-panel-header { width: 100%; text-align: left; padding: 12px 0; background: transparent; cursor: pointer; font-weight: 700; color: #111; display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid #e8e8e8; }
+.pd-panel-body { background: #fff; padding: 12px 0; }
+.pd-panel.expanded { border: 1px dashed #bfbfbf; border-radius: 6px; }
+.pd-panel.expanded .pd-panel-header { padding: 12px 14px; border-bottom: none; }
+.image-row { display: flex; justify-content: center; align-items: center; gap: 40px; padding: 12px; }
+.image-row img { height: 420px; width: auto; object-fit: contain; display: block; }
+.size-carousel { position: relative; background: #fff; overflow: hidden; width: 100%; min-height: 420px; border-radius: 0; }
+.size-slides { display: grid; grid-template-columns: 100%; }
+.size-slide { opacity: 0; transition: opacity 0.3s ease; display: none; }
+.size-slide.active { opacity: 1; display: block; }
+.size-slide-img { width: 100%; height: 420px; object-fit: contain; display: block; }
+.size-arrow { position: absolute; top: 50%; transform: translateY(-50%); background: rgba(255,255,255,0.85); border: 1px solid #e8e8e8; border-radius: 50%; width: 32px; height: 32px; cursor: pointer; }
+.size-arrow.left { left: 8px; }
+.size-arrow.right { right: 8px; }
+.size-indicators { display: flex; justify-content: center; gap: 8px; margin-top: 8px; }
+.size-dot { width: 8px; height: 8px; border-radius: 50%; background: #d9d9d9; cursor: pointer; }
+.size-dot.active { background: #1890ff; }
+.device-figure { position: relative; display: flex; align-items: center; justify-content: center; }
+.device-title { position: absolute; top: -26px; left: 50%; transform: translateX(-50%); font-weight: 700; color: #111; }
+.diag-line { position: absolute; left: 10%; top: 10%; width: 80%; height: 0; border-top: 2px solid rgba(255,255,255,0.9); transform: rotate(-35deg); }
+.diag-label { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); color: #fff; font-weight: 700; text-shadow: 0 1px 2px rgba(0,0,0,0.6); }
+.device-figure.stylus .diag-line, .device-figure.stylus .diag-label, .device-figure.stylus .device-title { display: none; }
+.pd-list { margin-top: 24px; }
+.pd-list-item { width: 100%; text-align: left; background: transparent; border: none; border-bottom: 1px solid #e8e8e8; padding: 18px 0; font-weight: 700; color: #111; display: flex; justify-content: space-between; align-items: center; }
+.pd-list-body { padding: 12px 0; }
+.specs-sublist { padding-left: 28px; }
diff --git a/src/pages/ProductDetail.js b/src/pages/ProductDetail.js
index f2d3d67..edb7117 100644
--- a/src/pages/ProductDetail.js
+++ b/src/pages/ProductDetail.js
@@ -12,6 +12,12 @@ const ProductDetail = () => {
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(true);
+ const [promoOpen, setPromoOpen] = useState(false);
+ const [faqOpen, setFaqOpen] = useState(false);
+ const [reviewOpen, setReviewOpen] = useState(false);
useEffect(() => {
const srcs = product
@@ -29,10 +35,10 @@ const ProductDetail = () => {
const loadImages = async () => {
try {
const qs = new URLSearchParams({ series, sub }).toString();
- const res = await fetch(`http://localhost:3001/api/docs/images?${qs}`);
+ 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) => `http://localhost:3001${d.url}`));
+ setImages(data.data.map((d) => `${d.url}`));
}
} catch {}
};
@@ -44,17 +50,17 @@ const ProductDetail = () => {
try {
setSpecsLoading(true);
const qs = new URLSearchParams({ series, sub }).toString();
- const res = await fetch(`http://localhost:3001/api/docs/specs?${qs}`);
+ 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({});
+ setExpanded(Object.fromEntries(data.data.map((_, i) => [i, true])));
} else {
setSections([{ title: '参数', rows: data.data }]);
setSpecs(data.data);
- setExpanded({});
+ setExpanded({ 0: true });
}
}
} catch {}
@@ -89,90 +95,129 @@ const ProductDetail = () => {
return (
-
-
机型图片
- {images.length > 1 &&
}
-
-
- {images.map((src, idx) => (
-
-

-
- ))}
-
-
- {images.length > 1 &&
}
-
- {images.length > 1 && (
-
- {images.map((_, idx) => (
- setActive(idx)}
- />
- ))}
-
- )}
-
-
-
-
-
详细参数
- {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} |
-
- );
- })
- ) : (
- | 暂无参数 |
- )}
-
-
-
- )}
+
{product.name}{product.desc ? ` | ${product.desc}` : ''}
+
+
+
+ {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 && (
+
)}
-
+
+
);
@@ -189,7 +234,7 @@ const DocsList = ({ series, sub }) => {
setLoading(true);
setError('');
const qs = new URLSearchParams({ series, sub }).toString();
- const res = await fetch(`http://localhost:3001/api/docs/list?${qs}`);
+ const res = await fetch(`/api/docs/list?${qs}`);
const data = await res.json();
if (data.success) {
setDocs(data.data || []);
@@ -214,11 +259,11 @@ const DocsList = ({ series, sub }) => {
{docs.map((d) => (
-
-
+
{d.name}
{(d.size / 1024 / 1024).toFixed(2)}MB
- 下载
+ 下载
))}