完善站点运营设置:分路由管理、登录可见评论、邮件模板内联与关闭注册 SSR。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
97
frontend/app/admin/settings/SettingsShell.tsx
Normal file
97
frontend/app/admin/settings/SettingsShell.tsx
Normal file
@@ -0,0 +1,97 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { useLayoutEffect, useRef, type KeyboardEvent, type ReactNode } from "react";
|
||||
import { Globe } from "lucide-react";
|
||||
import {
|
||||
AdminPageHeader,
|
||||
AdminSettingsPage,
|
||||
AdminSettingsWork,
|
||||
} from "@/components/admin";
|
||||
|
||||
const NAV: { href: string; label: string }[] = [
|
||||
{ href: "/admin/settings/basic", label: "基本信息" },
|
||||
{ href: "/admin/settings/access", label: "互动与安全" },
|
||||
{ href: "/admin/settings/mail", label: "邮件服务" },
|
||||
{ href: "/admin/settings/storage", label: "文件与存储" },
|
||||
{ href: "/admin/settings/filter", label: "内容过滤" },
|
||||
{ href: "/admin/settings/maintenance", label: "维护与诊断" },
|
||||
];
|
||||
|
||||
/** 旧 #hash / ?section= → 独立路由 */
|
||||
const LEGACY: Record<string, string> = {
|
||||
basic: "/admin/settings/basic",
|
||||
content: "/admin/settings/access",
|
||||
security: "/admin/settings/access",
|
||||
access: "/admin/settings/access",
|
||||
mail: "/admin/settings/mail",
|
||||
storage: "/admin/settings/storage",
|
||||
filter: "/admin/settings/filter",
|
||||
maintenance: "/admin/settings/maintenance",
|
||||
};
|
||||
|
||||
export default function SettingsShell({ children }: { children: ReactNode }) {
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
const navRefs = useRef<(HTMLAnchorElement | null)[]>([]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const hash = window.location.hash.replace(/^#/, "").trim();
|
||||
const section = new URLSearchParams(window.location.search).get("section") || hash;
|
||||
if (!section) return;
|
||||
const dest = LEGACY[section];
|
||||
if (!dest || dest === pathname) return;
|
||||
router.replace(dest);
|
||||
}, [pathname, router]);
|
||||
|
||||
const onNavKeyDown = (e: KeyboardEvent<HTMLAnchorElement>, i: number) => {
|
||||
if (
|
||||
e.key !== "ArrowDown" &&
|
||||
e.key !== "ArrowUp" &&
|
||||
e.key !== "Home" &&
|
||||
e.key !== "End" &&
|
||||
e.key !== "ArrowRight" &&
|
||||
e.key !== "ArrowLeft"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
let next = i;
|
||||
if (e.key === "ArrowDown" || e.key === "ArrowRight") next = (i + 1) % NAV.length;
|
||||
else if (e.key === "ArrowUp" || e.key === "ArrowLeft") next = (i + NAV.length - 1) % NAV.length;
|
||||
else if (e.key === "Home") next = 0;
|
||||
else next = NAV.length - 1;
|
||||
navRefs.current[next]?.focus();
|
||||
router.push(NAV[next].href);
|
||||
};
|
||||
|
||||
const nav = (
|
||||
<nav className="admin-settings-nav" aria-label="设置分类">
|
||||
{NAV.map((item, i) => {
|
||||
const isActive = pathname === item.href;
|
||||
return (
|
||||
<Link
|
||||
key={item.href}
|
||||
ref={(el) => {
|
||||
navRefs.current[i] = el;
|
||||
}}
|
||||
href={item.href}
|
||||
className="admin-settings-nav-link"
|
||||
aria-current={isActive ? "page" : undefined}
|
||||
onKeyDown={(e) => onNavKeyDown(e, i)}
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</nav>
|
||||
);
|
||||
|
||||
return (
|
||||
<AdminSettingsPage>
|
||||
<AdminPageHeader icon={Globe} title="站点设置" description="基本信息、互动安全与运营模块" />
|
||||
<AdminSettingsWork nav={nav}>{children}</AdminSettingsWork>
|
||||
</AdminSettingsPage>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user