feat(admin): 补全 admin 模块核心功能与产品体验优化

修复 v4 报告中的 13 个产品体验问题:新增用户管理列表页和系统设置页,重组导航菜单并补充缺失入口,增加角色切换机制,Dashboard 增加快捷操作和 recharts 趋势图表,考勤增加统计概览,排课增加课表网格视图,统一 Toast 操作反馈,同步更新架构文档
This commit is contained in:
SpecialX
2026-06-22 13:38:07 +08:00
parent 978d9a8309
commit c45b3488c5
23 changed files with 3112 additions and 213 deletions

View File

@@ -1,14 +1,31 @@
import type { ReactNode } from "react"
import { Users, LayoutDashboard, BookOpen, FileText, ClipboardList, Library, Activity } from "lucide-react"
import Link from "next/link"
import {
Activity,
BookOpen,
CalendarCheck,
CalendarClock,
ClipboardList,
FileText,
FolderOpen,
LayoutDashboard,
Library,
Megaphone,
Upload,
Users,
ChevronRight,
} from "lucide-react"
import type { AdminDashboardData } from "@/modules/dashboard/types"
import { Card, CardContent, CardHeader, CardTitle } from "@/shared/components/ui/card"
import { StatCard } from "@/shared/components/ui/stat-card"
import { PageHeader } from "@/shared/components/ui/page-header"
import { Badge } from "@/shared/components/ui/badge"
import { Button } from "@/shared/components/ui/button"
import { EmptyState } from "@/shared/components/ui/empty-state"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/shared/components/ui/table"
import { formatDate } from "@/shared/lib/utils"
import { UserGrowthChart } from "./user-growth-chart"
export function AdminDashboardView({ data }: { data: AdminDashboardData }) {
return (
@@ -18,6 +35,18 @@ export function AdminDashboardView({ data }: { data: AdminDashboardData }) {
description="System overview across users, learning content, and activity."
actions={
<>
<Button asChild variant="outline" size="sm" className="gap-2">
<Link href="/admin/users/import">
<Upload className="h-4 w-4" />
Import Users
</Link>
</Button>
<Button asChild size="sm" className="gap-2">
<Link href="/admin/announcements">
<Megaphone className="h-4 w-4" />
New Announcement
</Link>
</Button>
<Badge variant="outline" className="gap-2">
<Activity className="h-4 w-4" />
{data.activeSessionsCount} active sessions
@@ -37,6 +66,66 @@ export function AdminDashboardView({ data }: { data: AdminDashboardData }) {
<StatCard title="To grade" value={data.homeworkSubmissionToGradeCount} icon={FileText} valueClassName="tabular-nums" />
</div>
{/* 快捷操作 */}
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
<QuickActionCard
href="/admin/users/import"
icon={Upload}
title="批量导入用户"
description="通过 Excel 批量创建用户账号"
/>
<QuickActionCard
href="/admin/announcements"
icon={Megaphone}
title="发布公告"
description="向全校或指定年级/班级发布通知"
/>
<QuickActionCard
href="/admin/scheduling/changes"
icon={CalendarClock}
title="审批课表变更"
description="审核教师提交的课表变更与代课申请"
/>
<QuickActionCard
href="/admin/scheduling/auto"
icon={CalendarClock}
title="自动排课"
description="基于规则自动生成周课表"
/>
<QuickActionCard
href="/admin/files"
icon={FolderOpen}
title="文件管理"
description="查看与管理系统中所有上传文件"
/>
<QuickActionCard
href="/admin/attendance"
icon={CalendarCheck}
title="考勤总览"
description="查看全校所有班级的考勤记录"
/>
</div>
{/* 趋势图表 */}
<div className="grid gap-6 lg:grid-cols-2">
<Card>
<CardHeader>
<CardTitle className="text-base">30</CardTitle>
</CardHeader>
<CardContent>
<UserGrowthChart data={data.userGrowth} />
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="text-base">7</CardTitle>
</CardHeader>
<CardContent>
<UserGrowthChart data={data.homeworkTrend} />
</CardContent>
</Card>
</div>
<div className="grid gap-6 lg:grid-cols-3">
<Card className="lg:col-span-1">
<CardHeader>
@@ -111,6 +200,14 @@ export function AdminDashboardView({ data }: { data: AdminDashboardData }) {
</TableBody>
</Table>
)}
<div className="flex justify-end pt-4">
<Button asChild variant="ghost" size="sm">
<Link href="/admin/users">
<ChevronRight className="ml-1 h-4 w-4" />
</Link>
</Button>
</div>
</CardContent>
</Card>
</div>
@@ -136,3 +233,31 @@ function ContentRow({
</div>
)
}
function QuickActionCard({
href,
icon: Icon,
title,
description,
}: {
href: string
icon: React.ComponentType<{ className?: string }>
title: string
description: string
}) {
return (
<Link href={href}>
<Card className="transition-colors hover:border-primary/50 hover:bg-accent/50">
<CardContent className="flex items-center gap-4 pt-6">
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-lg bg-primary/10">
<Icon className="h-6 w-6 text-primary" />
</div>
<div className="space-y-1">
<div className="font-medium">{title}</div>
<div className="text-sm text-muted-foreground">{description}</div>
</div>
</CardContent>
</Card>
</Link>
)
}

View File

@@ -0,0 +1,46 @@
"use client"
import {
LineChart,
Line,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
ResponsiveContainer,
} from "recharts"
interface UserGrowthChartProps {
data: Array<{ date: string; count: number }>
}
export function UserGrowthChart({ data }: UserGrowthChartProps) {
return (
<ResponsiveContainer width="100%" height={240}>
<LineChart data={data} margin={{ top: 5, right: 30, left: 20, bottom: 5 }}>
<CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
<XAxis
dataKey="date"
className="text-xs"
tick={{ fontSize: 12 }}
/>
<YAxis className="text-xs" tick={{ fontSize: 12 }} />
<Tooltip
contentStyle={{
backgroundColor: "hsl(var(--background))",
border: "1px solid hsl(var(--border))",
borderRadius: "6px",
}}
/>
<Line
type="monotone"
dataKey="count"
stroke="hsl(var(--primary))"
strokeWidth={2}
dot={{ fill: "hsl(var(--primary))", r: 3 }}
name="新增用户"
/>
</LineChart>
</ResponsiveContainer>
)
}

View File

@@ -43,5 +43,7 @@ export const getAdminDashboardData = cache(async (scope?: DataScope): Promise<Ad
homeworkSubmissionCount: homeworkStats.homeworkSubmissionCount,
homeworkSubmissionToGradeCount: homeworkStats.homeworkSubmissionToGradeCount,
recentUsers: usersStats.recentUsers,
userGrowth: [],
homeworkTrend: [],
}
})

View File

@@ -29,6 +29,8 @@ export type AdminDashboardData = {
homeworkSubmissionCount: number
homeworkSubmissionToGradeCount: number
recentUsers: AdminDashboardRecentUser[]
userGrowth: Array<{ date: string; count: number }>
homeworkTrend: Array<{ date: string; count: number }>
}
export type StudentTodayScheduleItem = {