主要改进: - 新增题目导航抽屉组件,支持快速跳转到任意题目 - 新增悬浮球导航按钮,实时显示答题进度和统计信息 - 优化顶部导航栏,移除进度条,简化为返回、标题和设置三个按钮 - 将答题设置改为弹窗模式,提供更好的交互体验 - 优化题目列表卡片设计,减小高度使其更紧凑 - 题目列表显示题号、分类标签、题目内容和答题状态 - 支持答题进度持久化,刷新页面不丢失进度 技术细节: - 使用 Ant Design 的 Drawer、Modal、Tag 等组件 - 采用 CSS Modules 实现样式隔离 - 使用 LocalStorage 保存答题进度和设置 - 响应式设计,适配移动端和PC端 - 修复 TypeScript 编译错误 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
答题组件说明
本目录包含答题功能的子组件,从原来的 Question.tsx 大组件拆分而来。
组件列表
1. QuestionProgress.tsx
功能: 显示答题进度条和统计信息
Props:
currentIndex: number - 当前题目索引totalQuestions: number - 总题目数correctCount: number - 正确题目数wrongCount: number - 错误题目数
职责:
- 显示当前答题进度(百分比和题号)
- 显示正确和错误的统计数量
- 使用渐变色进度条增强视觉效果
2. QuestionCard.tsx
功能: 显示单个题目的卡片,包含题目内容、选项和答案提交
Props:
question: Question - 题目对象selectedAnswer: string | string[] - 选中的答案showResult: boolean - 是否显示答题结果answerResult: AnswerResult | null - 答题结果loading: boolean - 提交加载状态autoNextLoading: boolean - 自动下一题加载状态onAnswerChange: (answer: string | string[]) => void - 答案变更回调onSubmit: () => void - 提交答案回调onNext: () => void - 下一题回调
职责:
- 根据题目类型渲染不同的答题界面(单选、多选、填空、简答、判断)
- 处理填空题的特殊渲染逻辑
- 显示题目编号和分类标签
- 显示答案结果(使用 AnswerResult 组件)
- 提供提交和下一题按钮
3. AnswerResult.tsx
功能: 显示答题结果的Alert组件
Props:
answerResult: AnswerResult - 答题结果对象selectedAnswer: string | string[] - 用户选择的答案questionType: string - 题目类型
职责:
- 显示正确或错误的提示图标和颜色
- 显示用户答案和正确答案
- 显示答案解析(如果有)
- 特殊处理判断题的答案显示(true/false → 正确/错误)
4. CompletionSummary.tsx
功能: 完成所有题目后的统计摘要弹窗
Props:
visible: boolean - 弹窗是否可见totalQuestions: number - 总题目数correctCount: number - 正确数wrongCount: number - 错误数category: string | undefined - 题目类型分类onClose: () => void - 关闭回调(返回首页)onRetry: () => void - 重新开始回调
职责:
- 显示完成奖杯图标
- 展示本次答题的完整统计数据
- 计算并显示正确率(根据正确率显示不同颜色)
- 提供返回首页和重新开始两个操作
组件拆分的优势
- 单一职责: 每个组件只负责一个特定的功能
- 可维护性: 更容易定位和修改问题
- 可测试性: 每个组件可以独立测试
- 可复用性: 组件可以在其他页面复用
- 代码清晰: 主组件 Question.tsx 从 600+ 行缩减到 300 行左右
主组件 Question.tsx
保留职责:
- 状态管理(题目、答案、进度等)
- 业务逻辑(加载题目、提交答案、保存进度等)
- API 调用
- 组件组合和布局
文件大小变化:
- 重构前: ~605 行
- 重构后: ~303 行
- 减少: ~50%
使用示例
// 在 Question.tsx 中使用
<QuestionProgress
currentIndex={currentIndex}
totalQuestions={allQuestions.length}
correctCount={correctCount}
wrongCount={wrongCount}
/>
<QuestionCard
question={currentQuestion}
selectedAnswer={selectedAnswer}
showResult={showResult}
answerResult={answerResult}
loading={loading}
autoNextLoading={autoNextLoading}
onAnswerChange={setSelectedAnswer}
onSubmit={handleSubmit}
onNext={handleNext}
/>
<CompletionSummary
visible={showSummary}
totalQuestions={allQuestions.length}
correctCount={correctCount}
wrongCount={wrongCount}
category={currentQuestion?.category}
onClose={() => navigate("/")}
onRetry={handleRetry}
/>