feat(teacher): 题库模块(QuestionBank)
工作内容 - 新增 /teacher/questions 页面,支持 Suspense/空状态/加载态 - 题库 CRUD Server Actions:创建/更新/递归删除子题,变更后 revalidatePath - getQuestions 支持 q/type/difficulty/knowledgePointId 筛选与分页返回 meta - UI:表格列/筛选器/创建编辑弹窗,content JSON 兼容组卷 - 更新中文设计文档:docs/design/004_question_bank_implementation.md
This commit is contained in:
@@ -1,87 +1,132 @@
|
||||
# Question Bank Module Implementation
|
||||
# 题库模块实现
|
||||
|
||||
## 1. Overview
|
||||
The Question Bank module (`src/modules/questions`) is a core component for teachers to manage their examination resources. It implements a comprehensive CRUD interface with advanced filtering and batch operations.
|
||||
## 1. 概述
|
||||
题库模块(`src/modules/questions`)是教师管理考试资源的核心组件,提供完整的 CRUD 能力,并支持搜索/筛选等常用管理能力。
|
||||
|
||||
**Status**: IMPLEMENTED
|
||||
**Date**: 2025-12-23
|
||||
**Author**: Senior Frontend Engineer
|
||||
**状态**:已实现
|
||||
**日期**:2025-12-23
|
||||
**作者**:前端高级工程师
|
||||
|
||||
---
|
||||
|
||||
## 2. Architecture & Tech Stack
|
||||
## 2. 架构与技术栈
|
||||
|
||||
### 2.1 Vertical Slice Architecture
|
||||
Following the project's architectural guidelines, all question-related logic is encapsulated within `src/modules/questions`:
|
||||
- `components/`: UI components (Data Table, Dialogs, Filters)
|
||||
- `actions.ts`: Server Actions for data mutation
|
||||
- `data-access.ts`: Database query logic
|
||||
- `schema.ts`: Zod schemas for validation
|
||||
- `types.ts`: TypeScript interfaces
|
||||
### 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 Key Technologies
|
||||
- **Data Grid**: `@tanstack/react-table` for high-performance rendering.
|
||||
- **State Management**: `nuqs` for URL-based state (filters, search).
|
||||
- **Forms**: `react-hook-form` + `zod` + `shadcn/ui` form components.
|
||||
- **Validation**: Strict server-side and client-side validation using Zod schemas.
|
||||
### 2.2 关键技术
|
||||
- **数据表格**:`@tanstack/react-table`(高性能表格渲染)
|
||||
- **状态管理**:`nuqs`(基于 URL Query 的筛选/搜索状态)
|
||||
- **表单**:`react-hook-form` + `zod` + `shadcn/ui` 表单组件
|
||||
- **校验**:Zod 提供严格的服务端/客户端校验
|
||||
|
||||
---
|
||||
|
||||
## 3. Component Design
|
||||
## 3. 组件设计
|
||||
|
||||
### 3.1 QuestionDataTable (`question-data-table.tsx`)
|
||||
- **Features**: Pagination, Sorting, Row Selection.
|
||||
- **Performance**: Uses `React.memo` compatible patterns where possible (though `useReactTable` itself is not memoized).
|
||||
- **Responsiveness**: Mobile-first design with horizontal scroll for complex columns.
|
||||
### 3.1 QuestionDataTable(`question-data-table.tsx`)
|
||||
- **能力**:分页、排序、行选择
|
||||
- **性能**:尽可能采用与 `React.memo` 兼容的写法(`useReactTable` 本身不做 memo)
|
||||
- **响应式**:移动端优先;复杂列支持横向滚动
|
||||
|
||||
### 3.2 QuestionColumns (`question-columns.tsx`)
|
||||
Custom cell renderers for rich data display:
|
||||
- **Type Badge**: Color-coded badges for different question types (Single Choice, Multiple Choice, etc.).
|
||||
- **Difficulty**: Visual indicator with color (Green -> Red) and numerical value.
|
||||
- **Actions**: Dropdown menu for Edit, Delete, View Details, and Copy ID.
|
||||
### 3.2 QuestionColumns(`question-columns.tsx`)
|
||||
用于增强单元格展示的自定义渲染:
|
||||
- **题型 Badge**:不同题型的颜色/样式区分(单选、多选等)
|
||||
- **难度展示**:难度标签 + 数值
|
||||
- **行操作**:下拉菜单(编辑、删除、查看详情、复制 ID)
|
||||
|
||||
### 3.3 Create/Edit Dialog (`create-question-dialog.tsx`)
|
||||
A unified dialog component for both creating and editing questions.
|
||||
- **Dynamic Fields**: Shows/hides "Options" field based on question type.
|
||||
- **Interactive Options**: Allows adding/removing/reordering options for choice questions.
|
||||
- **Optimistic UI**: Shows loading states during submission.
|
||||
### 3.3 创建/编辑弹窗(`create-question-dialog.tsx`)
|
||||
创建与编辑共用同一个弹窗组件:
|
||||
- **动态字段**:根据题型显示/隐藏“选项”区域
|
||||
- **选项编辑**:支持添加/删除选项(选择题)
|
||||
- **交互反馈**:提交中 Loading 状态
|
||||
|
||||
### 3.4 Filters (`question-filters.tsx`)
|
||||
- **URL Sync**: All filter states (Search, Type, Difficulty) are synced to URL parameters.
|
||||
- **Debounce**: Search input uses debounce to prevent excessive requests.
|
||||
- **Server Filtering**: Filtering logic is executed on the server side (currently simulated in `page.tsx`, ready for DB integration).
|
||||
### 3.4 筛选器(`question-filters.tsx`)
|
||||
- **URL 同步**:搜索、题型、难度等筛选条件与 URL 参数保持同步
|
||||
- **无 Debounce(当前)**:搜索输入每次变更都会更新 URL
|
||||
- **服务端筛选**:在服务端组件中通过 `getQuestions` 执行筛选查询
|
||||
|
||||
---
|
||||
|
||||
## 4. Implementation Details
|
||||
## 4. 实现细节
|
||||
|
||||
### 4.1 Data Flow
|
||||
1. **Read**: `page.tsx` (Server Component) fetches data based on `searchParams`.
|
||||
2. **Write**: Client components invoke Server Actions (simulated) -> Revalidate Path -> UI Updates.
|
||||
3. **Filter**: User interaction -> Update URL -> Server Component Re-render -> New Data.
|
||||
### 4.1 数据流
|
||||
1. **读取**:`page.tsx`(Server Component)根据 `searchParams` 拉取数据
|
||||
2. **写入**:客户端组件调用 Server Actions -> `revalidatePath` -> UI 更新
|
||||
3. **筛选**:用户操作 -> 更新 URL -> 服务端组件重新渲染 -> 返回新数据
|
||||
|
||||
### 4.2 类型安全
|
||||
共享的 `Question` 类型用于保证前后端一致:
|
||||
|
||||
### 4.2 Type Safety
|
||||
A shared `Question` interface ensures consistency across the stack:
|
||||
```typescript
|
||||
export interface Question {
|
||||
id: string;
|
||||
content: any; // Rich text structure
|
||||
type: QuestionType;
|
||||
difficulty: number;
|
||||
// ... relations
|
||||
id: string
|
||||
content: unknown
|
||||
type: QuestionType
|
||||
difficulty: number
|
||||
// ... 关联字段
|
||||
}
|
||||
```
|
||||
|
||||
### 4.3 UI/UX Standards
|
||||
- **Empty States**: Custom `EmptyState` component when no data matches.
|
||||
- **Loading States**: Skeleton screens for table loading.
|
||||
- **Feedback**: `Sonner` toasts for success/error notifications.
|
||||
- **Confirmation**: `AlertDialog` for destructive actions (Delete).
|
||||
### 4.3 UI/UX 规范
|
||||
- **空状态**:无数据时展示 `EmptyState`
|
||||
- **加载态**:表格加载使用 Skeleton
|
||||
- **反馈**:`Sonner` toast 展示成功/失败提示
|
||||
- **确认弹窗**:删除等破坏性操作使用 `AlertDialog`
|
||||
|
||||
---
|
||||
|
||||
## 5. Next Steps
|
||||
- [ ] Integrate with real Database (replace Mock Data).
|
||||
- [ ] Implement Rich Text Editor (Slate.js / Tiptap) for question content.
|
||||
- [ ] Add "Batch Import" functionality.
|
||||
- [ ] Implement "Tags" management for Knowledge Points.
|
||||
## 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`:通过
|
||||
|
||||
Reference in New Issue
Block a user