# 题库模块实现 ## 1. 概述 题库模块(`src/modules/questions`)是教师管理考试资源的核心组件,提供完整的 CRUD 能力,并支持搜索/筛选等常用管理能力。 **状态**:已实现 **日期**:2025-12-23 **作者**:前端高级工程师 --- ## 2. 架构与技术栈 ### 2.1 垂直切片(Vertical Slice)架构 遵循项目的架构规范,所有与题库相关的逻辑都收敛在 `src/modules/questions` 下: - `components/`:UI 组件(表格、弹窗、筛选器) - `actions.ts`:Server Actions(数据变更) - `data-access.ts`:数据库查询逻辑 - `schema.ts`:Zod 校验 Schema - `types.ts`:TypeScript 类型定义 ### 2.2 关键技术 - **数据表格**:`@tanstack/react-table`(高性能表格渲染) - **状态管理**:`nuqs`(基于 URL Query 的筛选/搜索状态) - **表单**:`react-hook-form` + `zod` + `shadcn/ui` 表单组件 - **校验**:Zod 提供严格的服务端/客户端校验 --- ## 3. 组件设计 ### 3.1 QuestionDataTable(`question-data-table.tsx`) - **能力**:分页、排序、行选择 - **性能**:尽可能采用与 `React.memo` 兼容的写法(`useReactTable` 本身不做 memo) - **响应式**:移动端优先;复杂列支持横向滚动 ### 3.2 QuestionColumns(`question-columns.tsx`) 用于增强单元格展示的自定义渲染: - **题型 Badge**:不同题型的颜色/样式区分(单选、多选等) - **难度展示**:难度标签 + 数值 - **行操作**:下拉菜单(编辑、删除、查看详情、复制 ID) ### 3.3 创建/编辑弹窗(`create-question-dialog.tsx`) 创建与编辑共用同一个弹窗组件: - **动态字段**:根据题型显示/隐藏“选项”区域 - **选项编辑**:支持添加/删除选项(选择题) - **交互反馈**:提交中 Loading 状态 ### 3.4 筛选器(`question-filters.tsx`) - **URL 同步**:搜索、题型、难度等筛选条件与 URL 参数保持同步 - **无 Debounce(当前)**:搜索输入每次变更都会更新 URL - **服务端筛选**:在服务端组件中通过 `getQuestions` 执行筛选查询 --- ## 4. 实现细节 ### 4.1 数据流 1. **读取**:`page.tsx`(Server Component)根据 `searchParams` 拉取数据 2. **写入**:客户端组件调用 Server Actions -> `revalidatePath` -> UI 更新 3. **筛选**:用户操作 -> 更新 URL -> 服务端组件重新渲染 -> 返回新数据 ### 4.2 类型安全 共享的 `Question` 类型用于保证前后端一致: ```typescript export interface Question { id: string content: unknown type: QuestionType difficulty: number // ... 关联字段 } ``` ### 4.3 UI/UX 规范 - **空状态**:无数据时展示 `EmptyState` - **加载态**:表格加载使用 Skeleton - **反馈**:`Sonner` toast 展示成功/失败提示 - **确认弹窗**:删除等破坏性操作使用 `AlertDialog` --- ## 5. 后续计划 - [x] 接入真实数据库(替换 Mock Data) - [ ] 为题目内容引入富文本编辑器(Slate.js / Tiptap) - [ ] 增加“批量导入”能力 - [ ] 增加知识点“标签”管理能力 --- ## 6. 实现更新(2025-12-30) ### 6.1 教师路由与加载态 - 实现 `/teacher/questions` 页面(Suspense + 空状态) - 新增路由级加载 UI:`/teacher/questions/loading.tsx` ### 6.2 Content JSON 约定 为与考试组卷/预览组件保持一致,`questions.content` 采用最小 JSON 结构: ```typescript type ChoiceOption = { id: string text: string isCorrect?: boolean } type QuestionContent = { text: string options?: ChoiceOption[] } ``` ### 6.3 数据访问层(`getQuestions`) - 新增服务端筛选:`q`(content LIKE)、`type`、`difficulty`、`knowledgePointId` - 默认仅返回根题(`parentId IS NULL`),除非显式按 `ids` 查询 - 返回 `{ data, meta }`(包含分页统计),并为 UI 映射关联数据 ### 6.4 Server Actions(CRUD) - `createNestedQuestion` 支持 FormData(字段 `json`)与递归 `subQuestions` - `updateQuestionAction` 更新题目与知识点关联 - `deleteQuestionAction` 递归删除子题 - 所有变更都会对 `/teacher/questions` 做缓存再验证 ### 6.5 UI 集成 - `CreateQuestionDialog` 提交 `QuestionContent` JSON,并支持选择题正确答案勾选 - `QuestionActions` 在编辑/删除后刷新列表 - 表格内容预览优先展示 `content.text` ### 6.6 校验 - `npm run lint`:0 errors(仓库其他位置仍存在 warnings) - `npm run typecheck`:通过