增加登录显示的页面和修改bug
This commit is contained in:
19
src/App.js
19
src/App.js
@@ -3,6 +3,9 @@ import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
|
|||||||
import { AuthProvider } from './context/AuthContext';
|
import { AuthProvider } from './context/AuthContext';
|
||||||
import Login from './pages/Login';
|
import Login from './pages/Login';
|
||||||
import ProductDetail from './pages/ProductDetail';
|
import ProductDetail from './pages/ProductDetail';
|
||||||
|
import DevGuide from './pages/DevGuide';
|
||||||
|
import Training from './pages/Training';
|
||||||
|
import PrivateRoute from './components/PrivateRoute';
|
||||||
import Navbar from './components/Navbar';
|
import Navbar from './components/Navbar';
|
||||||
import Home from './pages/Home';
|
import Home from './pages/Home';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
@@ -16,6 +19,22 @@ function App() {
|
|||||||
<Route path="/login" element={<Login />} />
|
<Route path="/login" element={<Login />} />
|
||||||
|
|
||||||
<Route path="/product/:series/:sub/:idx" element={<ProductDetail />} />
|
<Route path="/product/:series/:sub/:idx" element={<ProductDetail />} />
|
||||||
|
<Route
|
||||||
|
path="/dev"
|
||||||
|
element={
|
||||||
|
<PrivateRoute>
|
||||||
|
<DevGuide />
|
||||||
|
</PrivateRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="/training"
|
||||||
|
element={
|
||||||
|
<PrivateRoute>
|
||||||
|
<Training />
|
||||||
|
</PrivateRoute>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<Route path="/" element={<Home />} />
|
<Route path="/" element={<Home />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</Router>
|
</Router>
|
||||||
|
|||||||
@@ -231,23 +231,17 @@ const Navbar = () => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* <div className="nav-item">
|
{user && (
|
||||||
<span className="nav-label">解决方案</span>
|
<div className="nav-item">
|
||||||
<div className="dropdown">
|
<span className="nav-label" onClick={() => navigate('/dev')}>开发指南</span>
|
||||||
<Link to="/" className="dropdown-item">解决方案A</Link>
|
|
||||||
<Link to="/" className="dropdown-item">解决方案B</Link>
|
|
||||||
<Link to="/" className="dropdown-item">解决方案C</Link>
|
|
||||||
</div>
|
</div>
|
||||||
</div> */}
|
)}
|
||||||
|
|
||||||
{/* <div className="nav-item">
|
{user && (
|
||||||
<span className="nav-label">行业</span>
|
<div className="nav-item">
|
||||||
<div className="dropdown">
|
<span className="nav-label" onClick={() => navigate('/training')}>培训资料</span>
|
||||||
<Link to="/" className="dropdown-item">民航</Link>
|
|
||||||
<Link to="/" className="dropdown-item">通航</Link>
|
|
||||||
<Link to="/" className="dropdown-item">研发制造</Link>
|
|
||||||
</div>
|
</div>
|
||||||
</div> */}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="navbar-right">
|
<div className="navbar-right">
|
||||||
|
|||||||
12
src/pages/DevGuide.js
Normal file
12
src/pages/DevGuide.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const DevGuide = () => {
|
||||||
|
return (
|
||||||
|
<div style={{ maxWidth: 1200, margin: '0 auto', padding: '20px' }}>
|
||||||
|
<h1 style={{ margin: 0, padding: '12px 0', color: '#111' }}>开发指南</h1>
|
||||||
|
<p style={{ color: '#333' }}>API、SDK、示例代码等开发资源的统一入口。</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DevGuide;
|
||||||
@@ -34,7 +34,7 @@ const Home = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const timer = setInterval(() => {
|
const timer = setInterval(() => {
|
||||||
setActive((prev) => (prev + 1) % slidesData.length);
|
setActive((prev) => (prev + 1) % slidesData.length);
|
||||||
}, 4000);
|
}, 5000);
|
||||||
return () => clearInterval(timer);
|
return () => clearInterval(timer);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ const Login = () => {
|
|||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
<div className="login-hint">
|
<div className="login-hint">
|
||||||
<p>默认账号:admin / admin123</p>
|
<p>测试账号:admin / admin123</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -50,9 +50,9 @@
|
|||||||
.pd-panel-body { background: #fff; padding: 12px 0; }
|
.pd-panel-body { background: #fff; padding: 12px 0; }
|
||||||
.pd-panel.expanded { border: 1px dashed #bfbfbf; border-radius: 6px; }
|
.pd-panel.expanded { border: 1px dashed #bfbfbf; border-radius: 6px; }
|
||||||
.pd-panel.expanded .pd-panel-header { padding: 12px 14px; border-bottom: none; }
|
.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 { display: flex; justify-content: center; align-items: center; gap: 40px; padding: 12px; background: #f5f5f5; }
|
||||||
.image-row img { height: 420px; width: auto; object-fit: contain; display: block; }
|
.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-carousel { position: relative; background: #f5f5f5; overflow: hidden; width: 100%; min-height: 420px; border-radius: 0; }
|
||||||
.size-slides { display: grid; grid-template-columns: 100%; }
|
.size-slides { display: grid; grid-template-columns: 100%; }
|
||||||
.size-slide { opacity: 0; transition: opacity 0.3s ease; display: none; }
|
.size-slide { opacity: 0; transition: opacity 0.3s ease; display: none; }
|
||||||
.size-slide.active { opacity: 1; display: block; }
|
.size-slide.active { opacity: 1; display: block; }
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useParams, useLocation } from 'react-router-dom';
|
import { useParams, useLocation } from 'react-router-dom';
|
||||||
|
import axios from 'axios';
|
||||||
import './ProductDetail.css';
|
import './ProductDetail.css';
|
||||||
|
|
||||||
const ProductDetail = () => {
|
const ProductDetail = () => {
|
||||||
@@ -14,7 +15,7 @@ const ProductDetail = () => {
|
|||||||
const [specsLoading, setSpecsLoading] = useState(false);
|
const [specsLoading, setSpecsLoading] = useState(false);
|
||||||
const [imgOpen, setImgOpen] = useState(true);
|
const [imgOpen, setImgOpen] = useState(true);
|
||||||
const [highlightsOpen, setHighlightsOpen] = useState(false);
|
const [highlightsOpen, setHighlightsOpen] = useState(false);
|
||||||
const [compareOpen, setCompareOpen] = useState(true);
|
const [compareOpen, setCompareOpen] = useState(false);
|
||||||
const [promoOpen, setPromoOpen] = useState(false);
|
const [promoOpen, setPromoOpen] = useState(false);
|
||||||
const [faqOpen, setFaqOpen] = useState(false);
|
const [faqOpen, setFaqOpen] = useState(false);
|
||||||
const [reviewOpen, setReviewOpen] = useState(false);
|
const [reviewOpen, setReviewOpen] = useState(false);
|
||||||
@@ -56,11 +57,11 @@ const ProductDetail = () => {
|
|||||||
if (data.data.length && typeof data.data[0]?.title === 'string' && Array.isArray(data.data[0]?.rows)) {
|
if (data.data.length && typeof data.data[0]?.title === 'string' && Array.isArray(data.data[0]?.rows)) {
|
||||||
setSections(data.data);
|
setSections(data.data);
|
||||||
setSpecs([]);
|
setSpecs([]);
|
||||||
setExpanded(Object.fromEntries(data.data.map((_, i) => [i, true])));
|
setExpanded(Object.fromEntries(data.data.map((_, i) => [i, false])));
|
||||||
} else {
|
} else {
|
||||||
setSections([{ title: '参数', rows: data.data }]);
|
setSections([{ title: '参数', rows: data.data }]);
|
||||||
setSpecs(data.data);
|
setSpecs(data.data);
|
||||||
setExpanded({ 0: true });
|
setExpanded({ 0: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
@@ -75,7 +76,7 @@ const ProductDetail = () => {
|
|||||||
if (images.length <= 1) return;
|
if (images.length <= 1) return;
|
||||||
const timer = setInterval(() => {
|
const timer = setInterval(() => {
|
||||||
setActive((prev) => (prev + 1) % images.length);
|
setActive((prev) => (prev + 1) % images.length);
|
||||||
}, 4000);
|
}, 5000);
|
||||||
return () => clearInterval(timer);
|
return () => clearInterval(timer);
|
||||||
}, [images.length]);
|
}, [images.length]);
|
||||||
|
|
||||||
@@ -95,7 +96,7 @@ const ProductDetail = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="product-detail-page">
|
<div className="product-detail-page">
|
||||||
<div className="container">
|
<div className="container">
|
||||||
<div className="pd-title">{product.name}{product.desc ? ` | ${product.desc}` : ''}</div>
|
<div className="pd-title">{product.name}</div>
|
||||||
<div className="section-narrow">
|
<div className="section-narrow">
|
||||||
<div className="pd-list">
|
<div className="pd-list">
|
||||||
<button className="pd-list-item" onClick={() => setImgOpen(!imgOpen)}>
|
<button className="pd-list-item" onClick={() => setImgOpen(!imgOpen)}>
|
||||||
@@ -228,6 +229,35 @@ const DocsList = ({ series, sub }) => {
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [error, setError] = useState('');
|
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(() => {
|
React.useEffect(() => {
|
||||||
const fetchDocs = async () => {
|
const fetchDocs = async () => {
|
||||||
try {
|
try {
|
||||||
@@ -259,11 +289,23 @@ const DocsList = ({ series, sub }) => {
|
|||||||
<ul className="docs-list">
|
<ul className="docs-list">
|
||||||
{docs.map((d) => (
|
{docs.map((d) => (
|
||||||
<li key={d.url} className="docs-item">
|
<li key={d.url} className="docs-item">
|
||||||
<a href={`${d.url}`} target="_blank" rel="noreferrer" className="docs-link">
|
{String(d.url || '').toLowerCase().endsWith('.pdf') ? (
|
||||||
{d.name}
|
<a
|
||||||
</a>
|
href={`${d.url}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="docs-link"
|
||||||
|
onClick={(e) => { e.preventDefault(); handleOpen(d); }}
|
||||||
|
>
|
||||||
|
{d.name}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<a href={`${d.url}`} target="_blank" rel="noreferrer" className="docs-link">
|
||||||
|
{d.name}
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
<span className="docs-size">{(d.size / 1024 / 1024).toFixed(2)}MB</span>
|
<span className="docs-size">{(d.size / 1024 / 1024).toFixed(2)}MB</span>
|
||||||
<a href={`${d.url}`} download className="docs-download">下载</a>
|
<button type="button" className="docs-download" onClick={() => handleDownload(d)}>下载</button>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
12
src/pages/Training.js
Normal file
12
src/pages/Training.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const Training = () => {
|
||||||
|
return (
|
||||||
|
<div style={{ maxWidth: 1200, margin: '0 auto', padding: '20px' }}>
|
||||||
|
<h1 style={{ margin: 0, padding: '12px 0', color: '#111' }}>培训资料</h1>
|
||||||
|
<p style={{ color: '#333' }}>课程、教材、视频等培训资源的统一入口。</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Training;
|
||||||
Reference in New Issue
Block a user