diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..626c799 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# Dependency directories +node_modules/ +client/node_modules/ + +# Build outputs +client/dist/ +dist/ + +# Python cache +__pycache__/ +*.pyc + +# Local backups and configs +app.ini.bak +database.json +settings.json +.idea/ +.vscode/ diff --git a/client/src/App.jsx b/client/src/App.jsx index ac372c5..cae323e 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -1,5 +1,6 @@ -import { Routes, Route } from 'react-router-dom'; +import { Routes, Route, useLocation } from 'react-router-dom'; import Navbar from './components/Navbar'; +import Footer from './components/Footer'; import Login from './pages/Login'; import Register from './pages/Register'; import MD2Doc from './pages/MD2Doc'; @@ -9,19 +10,38 @@ import Issues from './pages/Issues'; import IssueDetail from './pages/IssueDetail'; export default function App() { + const location = useLocation(); + const isHomePage = location.pathname === '/'; + + if (isHomePage) { + // 首页(MD2Doc)完全平铺屏幕,配合 flex 容器,使 MD2Doc 高度自动填充 Navbar 下方剩余空间,不产生页面滚动条 + return ( +
+ +
+ + } /> + +
+
+ ); + } + + // 其它分页面采用 Flex 自适应贴底页脚布局 return ( - <> +
- - } /> - } /> - } /> - } /> - } /> - } /> - {/* 取消拦截,将转换工具设为首页 */} - } /> - - +
+ + } /> + } /> + } /> + } /> + } /> + } /> + +
+
); } \ No newline at end of file diff --git a/client/src/components/Footer.jsx b/client/src/components/Footer.jsx new file mode 100644 index 0000000..c7fd0da --- /dev/null +++ b/client/src/components/Footer.jsx @@ -0,0 +1,57 @@ +import React from 'react'; + +export default function Footer() { + const currentYear = new Date().getFullYear(); + + return ( + + ); +} + +const styles = { + footer: { + padding: '20px 0', + backgroundColor: '#f9fafb', + borderTop: '1px solid #e5e7eb', + width: '100%', + boxSizing: 'border-box', + marginTop: 'auto' + }, + container: { + maxWidth: '1200px', + margin: '0 auto', + padding: '0 20px', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + gap: '16px', + flexWrap: 'wrap', + fontSize: '13px', + color: '#9ca3af' + }, + copyright: { + fontWeight: '400' + }, + link: { + color: '#9ca3af', + textDecoration: 'none', + transition: 'color 0.2s ease', + fontWeight: '500' + } +}; diff --git a/client/src/pages/Admin.jsx b/client/src/pages/Admin.jsx index 5a63385..57868cd 100644 --- a/client/src/pages/Admin.jsx +++ b/client/src/pages/Admin.jsx @@ -48,9 +48,9 @@ export default function Admin() { try { const token = localStorage.getItem('token'); const [usersRes, settingsRes, issuesRes] = await Promise.all([ - fetch('http://localhost:3001/api/admin/users', { headers: { Authorization: `Bearer ${token}` } }), - fetch('http://localhost:3001/api/admin/settings', { headers: { Authorization: `Bearer ${token}` } }), - fetch('http://localhost:3001/api/admin/issues', { headers: { Authorization: `Bearer ${token}` } }) + fetch('/api/admin/users', { headers: { Authorization: `Bearer ${token}` } }), + fetch('/api/admin/settings', { headers: { Authorization: `Bearer ${token}` } }), + fetch('/api/admin/issues', { headers: { Authorization: `Bearer ${token}` } }) ]); if (!usersRes.ok || !settingsRes.ok) { @@ -82,7 +82,7 @@ export default function Admin() { try { const token = localStorage.getItem('token'); - const res = await fetch('http://localhost:3001/api/admin/settings', { + const res = await fetch('/api/admin/settings', { method: 'POST', headers: { Authorization: `Bearer ${token}`, @@ -105,7 +105,7 @@ export default function Admin() { const fetchIssueDetail = async (issueId) => { try { const token = localStorage.getItem('token'); - const res = await fetch(`http://localhost:3001/api/issues/${issueId}`, { + const res = await fetch(`/api/issues/${issueId}`, { headers: { Authorization: `Bearer ${token}` } }); const data = await res.json(); @@ -125,7 +125,7 @@ export default function Admin() { setReplyMsg(''); try { const token = localStorage.getItem('token'); - const res = await fetch(`http://localhost:3001/api/admin/issues/${selectedIssue.id}/reply`, { + const res = await fetch(`/api/admin/issues/${selectedIssue.id}/reply`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -150,7 +150,7 @@ export default function Admin() { const handleStatusChange = async (issueId, newStatus) => { try { const token = localStorage.getItem('token'); - const res = await fetch(`http://localhost:3001/api/admin/issues/${issueId}/status`, { + const res = await fetch(`/api/admin/issues/${issueId}/status`, { method: 'PUT', headers: { 'Content-Type': 'application/json', diff --git a/client/src/pages/IssueDetail.jsx b/client/src/pages/IssueDetail.jsx index 8e85383..4415a3e 100644 --- a/client/src/pages/IssueDetail.jsx +++ b/client/src/pages/IssueDetail.jsx @@ -35,7 +35,7 @@ export default function IssueDetail() { const fetchIssue = async () => { try { const token = localStorage.getItem('token'); - const res = await fetch(`http://localhost:3001/api/issues/${id}`, { + const res = await fetch(`/api/issues/${id}`, { headers: { Authorization: `Bearer ${token}` } }); const data = await res.json(); @@ -56,7 +56,7 @@ export default function IssueDetail() { setReplyMsg(''); try { const token = localStorage.getItem('token'); - const res = await fetch(`http://localhost:3001/api/issues/${id}/reply`, { + const res = await fetch(`/api/issues/${id}/reply`, { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/client/src/pages/Issues.jsx b/client/src/pages/Issues.jsx index fc9b008..46cd7b1 100644 --- a/client/src/pages/Issues.jsx +++ b/client/src/pages/Issues.jsx @@ -51,7 +51,7 @@ export default function Issues() { const fetchMyIssues = async () => { try { const token = localStorage.getItem('token'); - const res = await fetch('http://localhost:3001/api/issues/my', { + const res = await fetch('/api/issues/my', { headers: { Authorization: `Bearer ${token}` } }); if (res.ok) { @@ -82,7 +82,7 @@ export default function Issues() { description: description.trim(), markdown_sample: attachMarkdown ? getEditorMarkdown() : '' }; - const res = await fetch('http://localhost:3001/api/issues', { + const res = await fetch('/api/issues', { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/client/src/pages/MD2Doc.jsx b/client/src/pages/MD2Doc.jsx index 8c12e27..fe309a1 100644 --- a/client/src/pages/MD2Doc.jsx +++ b/client/src/pages/MD2Doc.jsx @@ -268,7 +268,7 @@ export default function MD2Doc() { const token = localStorage.getItem('token'); if (!token) return; try { - const res = await fetch('http://localhost:3001/api/history', { + const res = await fetch('/api/history', { headers: { 'Authorization': `Bearer ${token}` } }); if (res.ok) { @@ -452,7 +452,7 @@ export default function MD2Doc() { headers['Authorization'] = `Bearer ${token}`; } - const response = await fetch('http://localhost:3001/api/convert', { + const response = await fetch('/api/convert', { method: 'POST', headers, body: JSON.stringify({ markdown, styleOptions }) @@ -499,14 +499,11 @@ export default function MD2Doc() { {user && (