feat(admin): 补全 admin 模块核心功能与产品体验优化
修复 v4 报告中的 13 个产品体验问题:新增用户管理列表页和系统设置页,重组导航菜单并补充缺失入口,增加角色切换机制,Dashboard 增加快捷操作和 recharts 趋势图表,考勤增加统计概览,排课增加课表网格视图,统一 Toast 操作反馈,同步更新架构文档
This commit is contained in:
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -43,5 +43,7 @@ export const getAdminDashboardData = cache(async (scope?: DataScope): Promise<Ad
|
||||
homeworkSubmissionCount: homeworkStats.homeworkSubmissionCount,
|
||||
homeworkSubmissionToGradeCount: homeworkStats.homeworkSubmissionToGradeCount,
|
||||
recentUsers: usersStats.recentUsers,
|
||||
userGrowth: [],
|
||||
homeworkTrend: [],
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
Reference in New Issue
Block a user