差距分析闭环(workline §7-§8): - P0:审计子模块 3 页 + 学校组织 7 页 - P1:系统设置 4 Card + 公告管理 + 邀请码管理 - P2:文件管理 + AI 配置 - P6:5 测试文件 / 69 用例全过 共享基础设施: - permissions +11 权限点 +14 路由 - view-models +20 接口 - i18n +100 key - graphql-client +25 operation - fixtures/handlers +15 mock +20 handler 质量:typecheck + lint 零错误,vitest 69/69 通过,arch.db 已更新
88 lines
2.3 KiB
TypeScript
88 lines
2.3 KiB
TypeScript
"use client";
|
|
|
|
import { type ReactNode } from "react";
|
|
import Link from "next/link";
|
|
import { usePathname } from "next/navigation";
|
|
import { PageHeader } from "@/components/ui";
|
|
import { t } from "@/lib/i18n";
|
|
|
|
interface TabItem {
|
|
href: string;
|
|
label: string;
|
|
}
|
|
|
|
const TABS: TabItem[] = [
|
|
{ href: "/admin/school/schools", label: t("admin.school.schools") },
|
|
{ href: "/admin/school/grades", label: t("admin.school.grades") },
|
|
{
|
|
href: "/admin/school/grades/insights",
|
|
label: t("admin.school.gradeInsights"),
|
|
},
|
|
{ href: "/admin/school/departments", label: t("admin.school.departments") },
|
|
{
|
|
href: "/admin/school/academic-year",
|
|
label: t("admin.school.academicYear"),
|
|
},
|
|
];
|
|
|
|
export default function SchoolLayout({
|
|
children,
|
|
}: {
|
|
children: ReactNode;
|
|
}): ReactNode {
|
|
const pathname = usePathname();
|
|
|
|
return (
|
|
<div style={{ padding: 32, maxWidth: 1200, margin: "0 auto" }}>
|
|
<PageHeader
|
|
title={t("admin.school.title")}
|
|
description="学校、年级、部门与学年学期管理"
|
|
/>
|
|
|
|
<nav
|
|
style={{
|
|
display: "flex",
|
|
gap: 4,
|
|
borderBottom: "1px solid var(--color-rule)",
|
|
marginBottom: 24,
|
|
}}
|
|
>
|
|
{TABS.map((tab) => {
|
|
const isActive =
|
|
pathname === tab.href ||
|
|
(tab.href !== "/admin/school/grades" &&
|
|
pathname.startsWith(tab.href));
|
|
// 年级管理 / 年级洞察 都在 /admin/school/grades 下,需要精确区分
|
|
const exactActive =
|
|
tab.href === "/admin/school/grades"
|
|
? pathname === "/admin/school/grades"
|
|
: isActive;
|
|
return (
|
|
<Link
|
|
key={tab.href}
|
|
href={tab.href}
|
|
style={{
|
|
padding: "8px 16px",
|
|
fontSize: 13,
|
|
color: exactActive
|
|
? "var(--color-ink)"
|
|
: "var(--color-ink-muted)",
|
|
textDecoration: "none",
|
|
borderBottom: exactActive
|
|
? "2px solid var(--color-accent)"
|
|
: "2px solid transparent",
|
|
marginBottom: "-1px",
|
|
fontWeight: exactActive ? 500 : 400,
|
|
}}
|
|
>
|
|
{tab.label}
|
|
</Link>
|
|
);
|
|
})}
|
|
</nav>
|
|
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|