初始化提交
This commit is contained in:
commit
887c1f449e
963 files changed
+166869
No files matched your search
@@ -0,0 +1,237 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useCallback } from "react";
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import {
|
||||
AdminIcon,
|
||||
AgentIcon,
|
||||
AuditIcon,
|
||||
GoalIcon,
|
||||
MemoryIcon,
|
||||
PerceptionIcon,
|
||||
PlanIcon,
|
||||
TenantIcon,
|
||||
ToolsIcon,
|
||||
TraceIcon,
|
||||
UserIcon,
|
||||
WorldIcon,
|
||||
} from "@/components/ui/Icons";
|
||||
import { cn } from "@/lib/utils/cn";
|
||||
|
||||
type AdminNavItem = { href: string; label: string; icon: ReactNode };
|
||||
|
||||
/**
|
||||
* 司命台治理导航(横向 — 设计稿 topbar-nav)。
|
||||
*/
|
||||
const ADMIN_NAV: AdminNavItem[] = [
|
||||
{ href: "/admin", label: "概览", icon: <AdminIcon /> },
|
||||
{ href: "/admin/audit", label: "起居注", icon: <AuditIcon /> },
|
||||
{ href: "/admin/traces", label: "链路追踪", icon: <TraceIcon /> },
|
||||
{ href: "/admin/users", label: "用户管理", icon: <UserIcon /> },
|
||||
{ href: "/admin/tenants", label: "多租户", icon: <TenantIcon /> },
|
||||
{
|
||||
href: "/admin/characters/templates",
|
||||
label: "人物模板",
|
||||
icon: <AdminIcon />,
|
||||
},
|
||||
];
|
||||
|
||||
/** 九枢治理导航(设计稿 pivot nav)。与 pivots/[pivot] 路由对齐 */
|
||||
const PIVOT_NAV: AdminNavItem[] = [
|
||||
{ href: "/admin/goals", label: "太一", icon: <GoalIcon /> },
|
||||
{ href: "/admin/safety", label: "司命", icon: <AdminIcon /> },
|
||||
{ href: "/admin/perception", label: "烛照", icon: <PerceptionIcon /> },
|
||||
{ href: "/admin/world", label: "河图", icon: <WorldIcon /> },
|
||||
{ href: "/admin/plans", label: "洛书", icon: <PlanIcon /> },
|
||||
{ href: "/admin/memory", label: "息壤", icon: <MemoryIcon /> },
|
||||
{ href: "/admin/tools", label: "百工", icon: <ToolsIcon /> },
|
||||
{ href: "/admin/agents", label: "天工阁", icon: <AgentIcon /> },
|
||||
];
|
||||
|
||||
/**
|
||||
* 司命台外壳:独立冷色壳层(admin-shell → slate 色温分化)。
|
||||
* 设计稿 admin-preview.html §4 AdminShell 完全对齐。
|
||||
*
|
||||
* 壳层特性:
|
||||
* - 独立冷色 CSS 变量(.admin-shell 激活 `--slate` 色系)
|
||||
* - 顶部横向双层导航(治理 nav + 九枢 pivot nav)
|
||||
* - 主题切换 + 返回主域入口
|
||||
* - iOS safe-area 适配
|
||||
*/
|
||||
export function AdminShell({ children }: { children: ReactNode }) {
|
||||
const pathname = usePathname();
|
||||
|
||||
/** 设计稿 toggleTheme — 通过触发表单更改 / 自定义事件 */
|
||||
const toggleTheme = useCallback(() => {
|
||||
const root = document.documentElement;
|
||||
const isDark = root.classList.contains("dark");
|
||||
if (isDark) {
|
||||
root.classList.remove("dark");
|
||||
} else {
|
||||
root.classList.add("dark");
|
||||
}
|
||||
// 持久化到 settings
|
||||
try {
|
||||
const raw = localStorage.getItem("hunjing-settings");
|
||||
if (raw) {
|
||||
const parsed = JSON.parse(raw);
|
||||
if (parsed.state) {
|
||||
parsed.state.theme = isDark ? "light" : "dark";
|
||||
localStorage.setItem("hunjing-settings", JSON.stringify(parsed));
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// silent
|
||||
}
|
||||
}, []);
|
||||
|
||||
const navItemClass = (href: string, opts?: { pivot?: boolean }) =>
|
||||
cn(
|
||||
"inline-flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-sm font-medium transition-colors shrink-0",
|
||||
opts?.pivot ? "text-xs" : "text-sm",
|
||||
pathname === href || pathname.startsWith(`${href}/`)
|
||||
? "bg-patina-wash text-patina"
|
||||
: "text-muted hover:bg-overlay-hover hover:text-fg",
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="relative flex min-h-screen flex-col admin-shell">
|
||||
<header
|
||||
className="sticky top-0 z-30 flex flex-wrap items-center gap-2 border-b px-4 py-2.5 md:gap-3 md:px-6"
|
||||
style={{
|
||||
background: "var(--glass)",
|
||||
backdropFilter: "blur(16px) saturate(140%)",
|
||||
borderColor: "var(--rule)",
|
||||
paddingLeft: "calc(1rem + env(safe-area-inset-left, 0px))",
|
||||
paddingRight: "calc(1rem + env(safe-area-inset-right, 0px))",
|
||||
}}
|
||||
>
|
||||
{/* 品牌标志 — 设计稿 topbar-brand */}
|
||||
<Link
|
||||
href="/admin"
|
||||
className="flex items-center gap-1.5 font-serif text-base font-bold tracking-wide text-fg shrink-0"
|
||||
style={{ letterSpacing: "0.06em" }}
|
||||
>
|
||||
{/* 设计稿 SVG knob:描边铜镜无极标记 */}
|
||||
<svg
|
||||
className="shrink-0"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.6"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<circle cx="12" cy="12" r="8.5" />
|
||||
<circle cx="12" cy="12" r="4.5" opacity="0.45" />
|
||||
<circle
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="1.6"
|
||||
stroke="none"
|
||||
fill="var(--cinnabar)"
|
||||
/>
|
||||
</svg>
|
||||
司命台
|
||||
</Link>
|
||||
|
||||
{/* 分隔符 */}
|
||||
<span className="shrink-0 text-fg-faint opacity-50 select-none">/</span>
|
||||
|
||||
{/* 司命治理导航 — 设计稿 topbar-nav */}
|
||||
<nav
|
||||
aria-label="司命台导航"
|
||||
className="order-3 flex w-full items-center gap-0.5 overflow-x-auto md:order-none md:w-auto md:flex-shrink"
|
||||
>
|
||||
{ADMIN_NAV.map((item) => (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={navItemClass(item.href)}
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* 九枢分隔 + 导航 — 设计稿中金色竖线 | 分隔 */}
|
||||
<span className="mx-1 hidden shrink-0 select-none text-fg-faint opacity-40 md:inline">
|
||||
|
|
||||
</span>
|
||||
<nav
|
||||
aria-label="九枢导航"
|
||||
className="order-4 flex w-full items-center gap-0.5 overflow-x-auto md:order-none md:w-auto"
|
||||
>
|
||||
{PIVOT_NAV.map((item) => (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={navItemClass(item.href, { pivot: true })}
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* 弹性间隔 */}
|
||||
<span className="flex-1 min-w-0" />
|
||||
|
||||
{/* 返回主域 — 设计稿 topbar-back */}
|
||||
<Link
|
||||
href="/chat"
|
||||
className="flex shrink-0 items-center gap-1 rounded-lg px-2 py-1 text-xs font-medium text-muted transition-colors hover:bg-overlay-hover hover:text-fg"
|
||||
title="返回主域"
|
||||
>
|
||||
<svg
|
||||
width="12"
|
||||
height="12"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
aria-hidden
|
||||
>
|
||||
<path
|
||||
d="M10 4l-4 4 4 4"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.4"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
主域
|
||||
</Link>
|
||||
|
||||
{/* 主题切换 — 设计稿 topbar-back 风格 */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleTheme}
|
||||
className="flex shrink-0 items-center gap-1 rounded-lg px-2 py-1 text-xs font-medium text-muted transition-colors hover:bg-overlay-hover hover:text-fg"
|
||||
title="切换主题"
|
||||
>
|
||||
<svg
|
||||
width="13"
|
||||
height="13"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="1.3"
|
||||
strokeLinecap="round"
|
||||
>
|
||||
<circle cx="8" cy="8" r="3" />
|
||||
<path d="M8 1v2M8 13v2M2 8h2M12 8h2M3.5 3.5l1.5 1.5M11 11l1.5 1.5M3.5 12.5l1.5-1.5M11 5l1.5-1.5" />
|
||||
</svg>
|
||||
<span id="theme-label-admin">{/* 客户端渲染时显示当前主题 */}</span>
|
||||
</button>
|
||||
</header>
|
||||
|
||||
<main className="flex min-h-0 flex-1 flex-col overflow-hidden">
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user