diff --git a/src/features/user-management/components/generic-page.tsx b/src/features/user-management/components/generic-page.tsx
index 79f029b..6f6e5bb 100644
--- a/src/features/user-management/components/generic-page.tsx
+++ b/src/features/user-management/components/generic-page.tsx
@@ -2,7 +2,8 @@ import prisma from "@/lib/prisma";
import { UserTable } from "./user-table";
import { Role } from "@/generated/client";
import { UserConfig } from "./user-table";
-import type { User, Problem } from "@/generated/client";
+import type { User } from "@/generated/client";
+import { getLocale } from "next-intl/server";
interface GenericPageProps {
resourceType: "admin" | "teacher" | "student" | "problem";
@@ -14,7 +15,28 @@ export default async function GenericPage({
config,
}: GenericPageProps) {
if (resourceType === "problem") {
- const data: Problem[] = await prisma.problem.findMany({});
+ const locale = await getLocale();
+ const problems = await prisma.problem.findMany({
+ select: {
+ id: true,
+ displayId: true,
+ difficulty: true,
+ localizations: {
+ where: { type: "TITLE", locale: locale === "en" ? "en" : "zh" },
+ select: { content: true },
+ take: 1,
+ },
+ },
+ orderBy: { displayId: "asc" },
+ });
+
+ const data = problems.map((problem) => ({
+ id: problem.id,
+ displayId: problem.displayId,
+ difficulty: problem.difficulty,
+ title: problem.localizations[0]?.content ?? "-",
+ }));
+
return ;
} else {
const role = resourceType.toUpperCase() as Role;
diff --git a/src/features/user-management/components/user-table.tsx b/src/features/user-management/components/user-table.tsx
index fbe24c3..f47396b 100644
--- a/src/features/user-management/components/user-table.tsx
+++ b/src/features/user-management/components/user-table.tsx
@@ -65,6 +65,7 @@ import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";
import { zodResolver } from "@hookform/resolvers/zod";
+import { useTranslations } from "next-intl";
import { Difficulty, Role } from "@/generated/client";
import type { User, Problem } from "@/generated/client";
import {
@@ -110,7 +111,11 @@ export interface UserConfig {
type UserTableProps =
| { config: UserConfig; data: User[] }
- | { config: UserConfig; data: Problem[] };
+ | { config: UserConfig; data: ProblemRow[] };
+
+type ProblemRow = Pick & {
+ title: string;
+};
type UserForm = {
id?: string;
@@ -154,13 +159,14 @@ const addProblemSchema = z.object({
});
export function UserTable(props: UserTableProps) {
+ const tDifficulty = useTranslations("Difficulty");
const isProblem = props.config.resourceType === "problem";
const router = useRouter();
- const problemData = isProblem ? (props.data as Problem[]) : undefined;
+ const problemData = isProblem ? (props.data as ProblemRow[]) : undefined;
const [isAddDialogOpen, setIsAddDialogOpen] = useState(false);
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false);
- const [editingUser, setEditingUser] = useState(null);
+ const [editingUser, setEditingUser] = useState(null);
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [deleteBatch, setDeleteBatch] = useState(false);
const [rowSelection, setRowSelection] = useState({});
@@ -174,15 +180,15 @@ export function UserTable(props: UserTableProps) {
const [pageInput, setPageInput] = useState(pagination.pageIndex + 1);
const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
const [pendingDeleteItem, setPendingDeleteItem] = useState<
- User | Problem | null
+ User | ProblemRow | null
>(null);
useEffect(() => {
setPageInput(pagination.pageIndex + 1);
}, [pagination.pageIndex]);
// 表格列
- const tableColumns = React.useMemo[]>(() => {
- const columns: ColumnDef[] = [
+ const tableColumns = React.useMemo[]>(() => {
+ const columns: ColumnDef[] = [
{
id: "select",
header: ({ table }) => (
@@ -206,13 +212,20 @@ export function UserTable(props: UserTableProps) {
},
];
props.config.columns.forEach((col) => {
- const column: ColumnDef = {
+ const column: ColumnDef = {
accessorKey: col.key,
header: col.label,
cell: ({ row }) => {
// 类型安全分流
if (col.key === "displayId" && isProblem) {
- return (row.original as Problem).displayId;
+ return (row.original as ProblemRow).displayId;
+ }
+ if (col.key === "title" && isProblem) {
+ return (row.original as ProblemRow).title;
+ }
+ if (col.key === "difficulty" && isProblem) {
+ const difficulty = String(row.getValue(col.key)) as Difficulty;
+ return tDifficulty(difficulty);
}
if (col.key === "createdAt" || col.key === "updatedAt") {
const value = row.getValue(col.key);
@@ -250,7 +263,7 @@ export function UserTable(props: UserTableProps) {
onClick={() => {
if (isProblem) {
// 如果是problem类型,跳转到编辑路由,使用displayId
- const problem = item as Problem;
+ const problem = item as ProblemRow;
router.push(`/dashboard/admin/problems/${problem.id}/edit`);
} else {
// 如果是用户类型,打开编辑弹窗
@@ -280,7 +293,7 @@ export function UserTable(props: UserTableProps) {
},
});
return columns;
- }, [props.config, router, isProblem]);
+ }, [props.config, router, isProblem, tDifficulty]);
const table = useReactTable({
data: props.data,
@@ -761,7 +774,7 @@ export function UserTable(props: UserTableProps) {
}
// 用ref保证获取最新data
- const dataRef = React.useRef(props.data);
+ const dataRef = React.useRef(props.data);
React.useEffect(() => {
dataRef.current = props.data;
}, [props.data]);
@@ -798,6 +811,7 @@ export function UserTable(props: UserTableProps) {
createdAt: "创建时间",
actions: "操作",
displayId: "题目编号",
+ title: "题目标题",
difficulty: "难度",
};
return (
@@ -1065,7 +1079,7 @@ export function UserTable(props: UserTableProps) {
table.getFilteredSelectedRowModel().rows;
for (const row of selectedRows) {
if (isProblem) {
- await deleteProblem((row.original as Problem).id);
+ await deleteProblem((row.original as ProblemRow).id);
} else {
await deleteUser(
props.config.resourceType as
@@ -1110,7 +1124,7 @@ export function UserTable(props: UserTableProps) {
onClick={async () => {
if (pendingDeleteItem) {
if (isProblem) {
- await deleteProblem((pendingDeleteItem as Problem).id);
+ await deleteProblem((pendingDeleteItem as ProblemRow).id);
} else {
await deleteUser(
props.config.resourceType as "admin" | "teacher" | "student",
diff --git a/src/features/user-management/config/problem.ts b/src/features/user-management/config/problem.ts
index 71eec9d..9254a43 100644
--- a/src/features/user-management/config/problem.ts
+++ b/src/features/user-management/config/problem.ts
@@ -3,6 +3,7 @@ import { z } from "zod";
export const problemSchema = z.object({
id: z.string(),
displayId: z.number(),
+ title: z.string().optional(),
difficulty: z.string(),
createdAt: z.string(),
});
@@ -23,7 +24,6 @@ export const problemConfig = {
title: "题目列表",
apiPath: "/api/problem",
columns: [
- { key: "id", label: "ID", sortable: true },
{
key: "displayId",
label: "题目编号",
@@ -31,6 +31,13 @@ export const problemConfig = {
searchable: true,
placeholder: "搜索编号",
},
+ {
+ key: "title",
+ label: "题目标题",
+ sortable: true,
+ searchable: true,
+ placeholder: "搜索标题",
+ },
{
key: "difficulty",
label: "难度",