feat(admin-portal): 完整实现 admin-portal 管理端微前端

包含 src 全部实现、Dockerfile、配置文件等
This commit is contained in:
SpecialX
2026-07-10 19:09:12 +08:00
parent 02d09c47fa
commit b3511910d1
69 changed files with 11955 additions and 693 deletions

View File

@@ -0,0 +1,115 @@
"use client";
import { type ReactNode, useState, useEffect } from "react";
import { useTeachers, useTeacherFilter } from "@/hooks/use-teachers";
import {
PageHeader,
PaperCard,
Input,
Select,
Badge,
LoadingState,
ErrorState,
EmptyState,
Table,
TableRow,
TableCell,
Pagination,
} from "@/components/ui";
import { t } from "@/lib/i18n";
export default function TeachersPage(): ReactNode {
const { filter, setPage, setSearch, setStatus } = useTeacherFilter();
const { data, loading, error } = useTeachers(filter);
const [searchInput, setSearchInput] = useState("");
useEffect(() => {
const timer = setTimeout(() => setSearch(searchInput), 300);
return () => clearTimeout(timer);
}, [searchInput, setSearch]);
return (
<div style={{ padding: 32, maxWidth: 1200, margin: "0 auto" }}>
<PageHeader
title={t("admin.teachers.title")}
description="全校教师一览"
/>
<PaperCard className="p-4 mb-4">
<div className="flex gap-3 items-center">
<Input
type="search"
placeholder={t("admin.common.search")}
value={searchInput}
onChange={(e) => setSearchInput(e.target.value)}
style={{ width: 240 }}
/>
<Select
value={filter.status ?? "all"}
onChange={(e) => setStatus(e.target.value)}
>
<option value="all">{t("admin.common.all")}</option>
<option value="active">{t("admin.common.active")}</option>
<option value="disabled">{t("admin.common.disabled")}</option>
<option value="locked">{t("admin.common.locked")}</option>
</Select>
</div>
</PaperCard>
<PaperCard className="p-4">
{loading && <LoadingState />}
{error && <ErrorState message={error.message} />}
{!loading && !error && data && (
<>
{data.items.length === 0 ? (
<EmptyState message={t("admin.common.empty")} />
) : (
<>
<Table
headers={[
t("admin.common.email"),
t("admin.common.name"),
t("admin.common.school"),
t("admin.teachers.subjects"),
t("admin.teachers.classCount"),
t("admin.common.status"),
t("admin.common.lastLoginAt"),
]}
>
{data.items.map((teacher) => (
<TableRow key={teacher.id}>
<TableCell>{teacher.email}</TableCell>
<TableCell>{teacher.name}</TableCell>
<TableCell>{teacher.schoolName}</TableCell>
<TableCell>
{teacher.subjects.join(", ") || "—"}
</TableCell>
<TableCell>{teacher.classCount}</TableCell>
<TableCell>
<Badge status={teacher.status} />
</TableCell>
<TableCell>
{teacher.lastLoginAt
? new Date(teacher.lastLoginAt).toLocaleDateString(
"zh-CN",
)
: "—"}
</TableCell>
</TableRow>
))}
</Table>
<Pagination
page={data.page}
pageSize={data.pageSize}
total={data.total}
hasNext={data.hasNext}
onPageChange={setPage}
/>
</>
)}
</>
)}
</PaperCard>
</div>
);
}