AnCao/web/src/components/AnswerResult.tsx
yanlongqi ea051e9380 添加AI评分系统和题目列表功能
新增功能:
1. AI智能评分系统
   - 集成OpenAI兼容API进行简答题评分
   - 提供分数、评语和改进建议
   - 支持自定义AI服务配置(BaseURL、APIKey、Model)

2. 题目列表页面
   - 展示所有题目和答案
   - Tab标签页形式的题型筛选(选择题、多选题、判断题、填空题、简答题)
   - 关键词搜索功能(支持题目内容和编号搜索)
   - 填空题特殊渲染:****显示为下划线
   - 判断题不显示选项,界面更简洁

3. UI优化
   - 答题结果组件重构,支持AI评分显示
   - 首页新增"题目列表"快速入口
   - 响应式设计,适配移动端和PC端

技术改进:
- 添加AI评分服务层(internal/services/ai_grading.go)
- 扩展题目模型支持AI评分结果
- 更新配置管理支持AI服务配置
- 添加AI评分测试脚本和文档

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-05 13:36:30 +08:00

172 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React from 'react'
import { Alert, Typography, Card, Space, Progress } from 'antd'
import { CheckOutlined, CloseOutlined, TrophyOutlined, CommentOutlined, BulbOutlined } from '@ant-design/icons'
import type { AnswerResult as AnswerResultType } from '../types/question'
const { Text, Paragraph } = Typography
interface AnswerResultProps {
answerResult: AnswerResultType
selectedAnswer: string | string[]
questionType: string
}
const AnswerResult: React.FC<AnswerResultProps> = ({
answerResult,
selectedAnswer,
questionType,
}) => {
// 格式化答案显示(判断题特殊处理)
const formatAnswer = (answer: string | string[] | boolean) => {
// 处理判断题的布尔值和字符串
if (questionType === 'true-false') {
if (typeof answer === 'boolean') {
return answer ? '正确' : '错误'
}
if (typeof answer === 'string') {
return answer === 'true' ? '正确' : answer === 'false' ? '错误' : answer
}
}
// 处理数组答案
if (Array.isArray(answer)) {
return answer.join(', ')
}
return String(answer)
}
// 获取评分等级颜色
const getScoreColor = (score: number) => {
if (score >= 90) return '#52c41a' // 优秀 - 绿色
if (score >= 80) return '#1890ff' // 良好 - 蓝色
if (score >= 60) return '#faad14' // 及格 - 橙色
return '#ff4d4f' // 不及格 - 红色
}
// 获取评分等级
const getScoreLevel = (score: number) => {
if (score >= 90) return '优秀'
if (score >= 80) return '良好'
if (score >= 60) return '及格'
return '不及格'
}
return (
<div style={{ marginTop: 20 }}>
<Alert
type={answerResult.correct ? 'success' : 'error'}
icon={answerResult.correct ? <CheckOutlined /> : <CloseOutlined />}
message={
<div>
<strong>{answerResult.correct ? '回答正确!' : '回答错误'}</strong>
</div>
}
description={
<div>
<div style={{ marginBottom: 8 }}>
<Text type="secondary"></Text>
<Text strong={answerResult.correct} type={answerResult.correct ? undefined : 'danger'}>
{formatAnswer(answerResult.user_answer || selectedAnswer)}
</Text>
</div>
<div style={{ marginBottom: 8 }}>
<Text strong style={{ color: '#52c41a' }}>
</Text>
<Text strong style={{ color: '#52c41a' }}>
{formatAnswer(
answerResult.correct_answer || (answerResult.correct ? selectedAnswer : '暂无')
)}
</Text>
</div>
{answerResult.explanation && (
<div>
<Text type="secondary"></Text>
<div style={{ marginTop: 4 }}>{answerResult.explanation}</div>
</div>
)}
</div>
}
/>
{/* AI评分结果 - 仅简答题显示 */}
{answerResult.ai_grading && (
<Card
style={{
marginTop: 16,
borderColor: getScoreColor(answerResult.ai_grading.score),
borderWidth: 2,
}}
title={
<Space>
<TrophyOutlined style={{ color: getScoreColor(answerResult.ai_grading.score) }} />
<Text strong>AI智能评分</Text>
</Space>
}
>
{/* 分数和进度条 */}
<div style={{ marginBottom: 20 }}>
<Space align="center" size="large">
<div>
<Text type="secondary" style={{ fontSize: 14 }}></Text>
<div>
<Text
strong
style={{
fontSize: 32,
color: getScoreColor(answerResult.ai_grading.score),
}}
>
{answerResult.ai_grading.score}
</Text>
<Text type="secondary" style={{ fontSize: 18 }}> / 100</Text>
</div>
</div>
<div style={{ flex: 1, minWidth: 200 }}>
<Progress
percent={answerResult.ai_grading.score}
strokeColor={getScoreColor(answerResult.ai_grading.score)}
format={(percent) => `${getScoreLevel(percent || 0)}`}
/>
</div>
</Space>
</div>
{/* 评语 */}
{answerResult.ai_grading.feedback && (
<div style={{ marginBottom: 16 }}>
<Space align="start">
<CommentOutlined style={{ fontSize: 16, color: '#1890ff', marginTop: 2 }} />
<div style={{ flex: 1 }}>
<Text strong style={{ fontSize: 14 }}></Text>
<Paragraph style={{ marginTop: 4, marginBottom: 0, color: '#595959' }}>
{answerResult.ai_grading.feedback}
</Paragraph>
</div>
</Space>
</div>
)}
{/* 改进建议 */}
{answerResult.ai_grading.suggestion && (
<div>
<Space align="start">
<BulbOutlined style={{ fontSize: 16, color: '#faad14', marginTop: 2 }} />
<div style={{ flex: 1 }}>
<Text strong style={{ fontSize: 14 }}></Text>
<Paragraph style={{ marginTop: 4, marginBottom: 0, color: '#595959' }}>
{answerResult.ai_grading.suggestion}
</Paragraph>
</div>
</Space>
</div>
)}
</Card>
)}
</div>
)
}
export default AnswerResult