From 6334dccf4a0c5084c8cd7a7cb4545b5de5e1906b Mon Sep 17 00:00:00 2001 From: Blizzard Date: Fri, 17 Jul 2026 16:31:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(admin):=20=E4=BE=A7=E8=BE=B9=E6=A0=8F?= =?UTF-8?q?=E5=B8=83=E5=B1=80=20+=20=E4=BB=AA=E8=A1=A8=E7=9B=98=20+=20?= =?UTF-8?q?=E5=85=A8=E5=B1=80=E9=9A=90=E8=97=8F=E6=BB=9A=E5=8A=A8=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 全局隐藏滚动条(web + admin),保留正常滚动 - admin 顶栏改侧边栏导航(仪表盘/文章),响应式移动端抽屉 - 路由重构:首页→仪表盘,文章列表移到 /posts - 仪表盘页:文章总数/已发布/草稿统计卡片 + 最近更新列表 - 后端 GET /api/admin/stats Co-Authored-By: Claude Opus 4.8 --- .claude/launch.json | 7 ++ admin/src/App.tsx | 4 +- admin/src/api/posts.ts | 11 +++ admin/src/components/admin-layout.tsx | 73 +++++++--------- admin/src/components/admin-sidebar.tsx | 109 +++++++++++++++++++++++ admin/src/index.css | 11 +++ admin/src/pages/dashboard.tsx | 114 +++++++++++++++++++++++++ server/internal/handler/admin_post.go | 20 +++++ server/internal/router/router.go | 1 + web/src/index.css | 11 +++ 10 files changed, 316 insertions(+), 45 deletions(-) create mode 100644 admin/src/components/admin-sidebar.tsx create mode 100644 admin/src/pages/dashboard.tsx diff --git a/.claude/launch.json b/.claude/launch.json index 3263e92..b62ef28 100644 --- a/.claude/launch.json +++ b/.claude/launch.json @@ -7,6 +7,13 @@ "runtimeArgs": ["run", "dev"], "cwd": "web", "port": 5173 + }, + { + "name": "admin", + "runtimeExecutable": "npm", + "runtimeArgs": ["run", "dev"], + "cwd": "admin", + "port": 5174 } ] } diff --git a/admin/src/App.tsx b/admin/src/App.tsx index 3802189..11e20e8 100644 --- a/admin/src/App.tsx +++ b/admin/src/App.tsx @@ -2,6 +2,7 @@ import { Navigate, Outlet, Route, Routes } from 'react-router-dom' import { getToken } from '@/api/client' import { AdminLayout } from '@/components/admin-layout' import LoginPage from '@/pages/login' +import DashboardPage from '@/pages/dashboard' import PostsListPage from '@/pages/posts-list' import PostEditPage from '@/pages/post-edit' @@ -16,7 +17,8 @@ export default function App() { } /> }> }> - } /> + } /> + } /> } /> } /> diff --git a/admin/src/api/posts.ts b/admin/src/api/posts.ts index c431d0a..7073117 100644 --- a/admin/src/api/posts.ts +++ b/admin/src/api/posts.ts @@ -61,3 +61,14 @@ export function updatePost(id: string, form: PostForm) { export function deletePost(id: string) { return request(`/api/admin/posts/${id}`, { method: 'DELETE' }) } + +export interface Stats { + total: number + published: number + draft: number + recent: PostListItem[] +} + +export function fetchStats() { + return request('/api/admin/stats') +} diff --git a/admin/src/components/admin-layout.tsx b/admin/src/components/admin-layout.tsx index 0b2007b..c5f538c 100644 --- a/admin/src/components/admin-layout.tsx +++ b/admin/src/components/admin-layout.tsx @@ -1,52 +1,37 @@ -import { Link, Outlet, useNavigate } from 'react-router-dom' -import { LogOut, Moon, Sun } from 'lucide-react' -import { logout } from '@/api/auth' -import { useTheme } from '@/components/theme-provider' +import { useState } from 'react' +import { Outlet } from 'react-router-dom' +import { Menu } from 'lucide-react' +import { AdminSidebar } from '@/components/admin-sidebar' import { LogoMark } from '@/components/logo' export function AdminLayout() { - const navigate = useNavigate() - const { resolved, setTheme } = useTheme() + const [open, setOpen] = useState(false) return ( -
-
-
- - - sundynix admin - -
- - -
-
-
-
- -
+
+ setOpen(false)} /> + +
+ {/* 移动端顶部条 */} +
+ + + sundynix{' '} + admin + +
+ +
+ +
+
) } diff --git a/admin/src/components/admin-sidebar.tsx b/admin/src/components/admin-sidebar.tsx new file mode 100644 index 0000000..dc5777d --- /dev/null +++ b/admin/src/components/admin-sidebar.tsx @@ -0,0 +1,109 @@ +import { Link, NavLink, useNavigate } from 'react-router-dom' +import { FileText, LayoutDashboard, LogOut, Moon, Sun, X } from 'lucide-react' +import { logout } from '@/api/auth' +import { useTheme } from '@/components/theme-provider' +import { LogoMark } from '@/components/logo' +import { cn } from '@/lib/utils' + +const NAV = [ + { to: '/', label: '仪表盘', icon: LayoutDashboard, end: true }, + { to: '/posts', label: '文章', icon: FileText, end: false }, +] + +interface Props { + open: boolean + onClose: () => void +} + +export function AdminSidebar({ open, onClose }: Props) { + const navigate = useNavigate() + const { resolved, setTheme } = useTheme() + + const itemCls = ({ isActive }: { isActive: boolean }) => + cn( + 'flex items-center gap-3 rounded-lg px-3 py-2.5 text-[14px] transition-colors', + isActive + ? 'bg-accent-soft font-medium text-accent-ink' + : 'text-ink-2 hover:bg-ground hover:text-ink', + ) + + const bottomBtnCls = + 'flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-[14px] text-ink-2 transition-colors hover:bg-ground hover:text-ink' + + return ( + <> + {open && ( +