优化首页UI设计和图标样式
主要改进: - 重构用户信息卡片,新增统计数据展示区域 - 统一题型卡片图标样式,添加渐变背景容器 - 优化快速开始卡片的图标设计 - 调整配色方案为明快风格,提升视觉效果 - 修改题型图标:判断题使用CloseCircleOutlined,填空题使用FormOutlined,论述题使用FileMarkdownOutlined - 完善响应式设计,优化移动端和PC端显示效果 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
b082e708ae
commit
4a7c9a2593
@ -661,23 +661,25 @@ func GetStatistics(c *gin.Context) {
|
|||||||
Where("user_id = ?", uid).
|
Where("user_id = ?", uid).
|
||||||
Count(&wrongQuestions)
|
Count(&wrongQuestions)
|
||||||
|
|
||||||
// 计算正确率
|
// 计算正确率和总答题数
|
||||||
var accuracy float64
|
var accuracy float64
|
||||||
if answeredQuestions > 0 {
|
var totalAnswers int64
|
||||||
|
db.Model(&models.UserAnswerRecord{}).
|
||||||
|
Where("user_id = ?", uid).
|
||||||
|
Count(&totalAnswers)
|
||||||
|
|
||||||
|
if totalAnswers > 0 {
|
||||||
// 正确率 = 答对题数 / 总答题数
|
// 正确率 = 答对题数 / 总答题数
|
||||||
var totalAnswers int64
|
|
||||||
db.Model(&models.UserAnswerRecord{}).
|
|
||||||
Where("user_id = ?", uid).
|
|
||||||
Count(&totalAnswers)
|
|
||||||
accuracy = float64(correctAnswers) / float64(totalAnswers) * 100
|
accuracy = float64(correctAnswers) / float64(totalAnswers) * 100
|
||||||
}
|
}
|
||||||
|
|
||||||
stats := models.UserStatistics{
|
stats := gin.H{
|
||||||
TotalQuestions: int(totalQuestions),
|
"total_questions": int(totalQuestions),
|
||||||
AnsweredQuestions: int(answeredQuestions),
|
"answered_questions": int(answeredQuestions),
|
||||||
CorrectAnswers: int(correctAnswers),
|
"correct_answers": int(correctAnswers),
|
||||||
WrongQuestions: int(wrongQuestions),
|
"wrong_questions": int(wrongQuestions),
|
||||||
Accuracy: accuracy,
|
"total_answers": int(totalAnswers), // 刷题次数
|
||||||
|
"accuracy": accuracy,
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
|||||||
@ -36,12 +36,12 @@ func NewAIGradingService() *AIGradingService {
|
|||||||
|
|
||||||
// AIGradingResult AI评分结果
|
// AIGradingResult AI评分结果
|
||||||
type AIGradingResult struct {
|
type AIGradingResult struct {
|
||||||
Score float64 `json:"score"` // 得分 (0-100)
|
Score float64 `json:"score"` // 得分 (0-100)
|
||||||
IsCorrect bool `json:"is_correct"` // 是否正确 (Score >= 60 视为正确)
|
IsCorrect bool `json:"is_correct"` // 是否正确 (Score >= 60 视为正确)
|
||||||
Feedback string `json:"feedback"` // 评语
|
Feedback string `json:"feedback"` // 评语
|
||||||
Suggestion string `json:"suggestion"` // 改进建议
|
Suggestion string `json:"suggestion"` // 改进建议
|
||||||
ReferenceAnswer string `json:"reference_answer"` // 参考答案(论述题)
|
ReferenceAnswer string `json:"reference_answer"` // 参考答案(论述题)
|
||||||
ScoringRationale string `json:"scoring_rationale"` // 评分依据
|
ScoringRationale string `json:"scoring_rationale"` // 评分依据
|
||||||
}
|
}
|
||||||
|
|
||||||
// GradeEssay 对论述题进行AI评分(不需要标准答案)
|
// GradeEssay 对论述题进行AI评分(不需要标准答案)
|
||||||
@ -275,7 +275,6 @@ func (s *AIGradingService) ExplainQuestionStream(writer http.ResponseWriter, que
|
|||||||
1. **必须基于保密法规**:解析时必须引用相关法规条文,说明依据哪些具体法律法规
|
1. **必须基于保密法规**:解析时必须引用相关法规条文,说明依据哪些具体法律法规
|
||||||
2. **必须实事求是**:只基于题目内容、标准答案和实际法规进行解析
|
2. **必须实事求是**:只基于题目内容、标准答案和实际法规进行解析
|
||||||
3. **不要胡编乱造**:如果某些信息不确定或题目没有提供,请如实说明,不要编造法规条文
|
3. **不要胡编乱造**:如果某些信息不确定或题目没有提供,请如实说明,不要编造法规条文
|
||||||
4. **使用Markdown格式**:使用标题、列表、加粗等markdown语法使内容更清晰易读
|
|
||||||
|
|
||||||
解析内容要求:
|
解析内容要求:
|
||||||
- **知识点**:说明题目考查的核心知识点,指出涉及哪些保密法规
|
- **知识点**:说明题目考查的核心知识点,指出涉及哪些保密法规
|
||||||
|
|||||||
@ -78,7 +78,7 @@ func (c *DatabaseConfig) GetDSN() string {
|
|||||||
// GetAIConfig 获取AI服务配置
|
// GetAIConfig 获取AI服务配置
|
||||||
// 优先使用环境变量,如果没有设置则使用默认值
|
// 优先使用环境变量,如果没有设置则使用默认值
|
||||||
func GetAIConfig() *AIConfig {
|
func GetAIConfig() *AIConfig {
|
||||||
baseURL := getEnv("AI_BASE_URL", "https://ai.yuchat.top")
|
baseURL := getEnv("AI_BASE_URL", "http://172.20.0.117")
|
||||||
apiKey := getEnv("AI_API_KEY", "sk-OKBmOpJx855juSOPU14cWG6Iz87tZQuv3Xg9PiaJYXdHoKcN")
|
apiKey := getEnv("AI_API_KEY", "sk-OKBmOpJx855juSOPU14cWG6Iz87tZQuv3Xg9PiaJYXdHoKcN")
|
||||||
model := getEnv("AI_MODEL", "deepseek-v3")
|
model := getEnv("AI_MODEL", "deepseek-v3")
|
||||||
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
.header {
|
.header {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: flex-start;
|
align-items: center;
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
|
||||||
.headerLeft {
|
.headerLeft {
|
||||||
@ -29,62 +29,196 @@
|
|||||||
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.08));
|
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.08));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.titleRow {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
color: #1d1d1f !important;
|
color: #1d1d1f !important;
|
||||||
margin-bottom: 4px !important;
|
margin-bottom: 0 !important;
|
||||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.02);
|
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.02);
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.totalBadge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 4px 12px;
|
||||||
|
background: linear-gradient(135deg, #e6f7ff 0%, #bae7ff 100%);
|
||||||
|
border: 1px solid #91d5ff;
|
||||||
|
border-radius: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #0958d9;
|
||||||
|
line-height: 1;
|
||||||
|
box-shadow: 0 2px 4px rgba(9, 88, 217, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
.subtitle {
|
.subtitle {
|
||||||
color: #6e6e73;
|
color: #6e6e73;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.userInfo {
|
.userInfo {
|
||||||
background: rgba(255, 255, 255, 0.85);
|
background: #fff;
|
||||||
padding: 12px 16px;
|
padding: 20px;
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
backdrop-filter: blur(30px) saturate(180%);
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03), 0 1px 6px -1px rgba(0, 0, 0, 0.02), 0 2px 4px rgba(0, 0, 0, 0.02);
|
||||||
-webkit-backdrop-filter: blur(30px) saturate(180%);
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
box-shadow:
|
transition: box-shadow 0.2s ease;
|
||||||
0 2px 8px rgba(0, 0, 0, 0.04),
|
min-width: 520px;
|
||||||
0 1px 3px rgba(0, 0, 0, 0.02),
|
|
||||||
0 0 0 1px rgba(0, 0, 0, 0.03);
|
|
||||||
border: 0.5px solid rgba(0, 0, 0, 0.04);
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
|
|
||||||
&:hover {
|
.userInfoContent {
|
||||||
background: rgba(255, 255, 255, 0.95);
|
display: flex;
|
||||||
box-shadow:
|
align-items: center;
|
||||||
0 4px 12px rgba(0, 0, 0, 0.06),
|
gap: 16px;
|
||||||
0 2px 6px rgba(0, 0, 0, 0.04),
|
margin-bottom: 16px;
|
||||||
0 0 0 1px rgba(0, 0, 0, 0.04);
|
padding-bottom: 16px;
|
||||||
transform: translateY(-2px);
|
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatarWrapper {
|
||||||
|
position: relative;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
.userAvatar {
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||||
|
border: 2px solid #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatarBadge {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -2px;
|
||||||
|
right: -2px;
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
:global {
|
||||||
|
.anticon {
|
||||||
|
&.anticon-check-circle {
|
||||||
|
color: #52c41a;
|
||||||
|
}
|
||||||
|
&.anticon-setting {
|
||||||
|
color: #ff4d4f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.userDetails {
|
.userDetails {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 3px;
|
gap: 4px;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
.userNickname {
|
.userNickname {
|
||||||
color: #1d1d1f !important;
|
color: #1d1d1f !important;
|
||||||
font-size: 15px;
|
font-size: 16px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
line-height: 1.2;
|
line-height: 1.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.userUsername {
|
.userUsername {
|
||||||
color: #6e6e73 !important;
|
color: #6e6e73 !important;
|
||||||
font-size: 12px;
|
font-size: 13px;
|
||||||
line-height: 1.2;
|
line-height: 1.3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.userType {
|
.userTypeBadge {
|
||||||
font-size: 11px;
|
display: inline-flex;
|
||||||
color: #6e6e73;
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
padding: 5px 12px;
|
||||||
|
background: linear-gradient(135deg, #e6f7ff 0%, #d6f0ff 100%);
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #91caff;
|
||||||
|
width: fit-content;
|
||||||
|
margin-top: 2px;
|
||||||
|
|
||||||
|
.badgeIcon {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userTypeText {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #1890ff;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdownTrigger {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: #e8e8e8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdownIcon {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #8c8c8c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.userStatsRow {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
gap: 12px;
|
||||||
|
|
||||||
|
.statItem {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
padding: 8px 4px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fafafa;
|
||||||
|
|
||||||
|
.statValue {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1890ff;
|
||||||
|
line-height: 1.3;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statLabel {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #8c8c8c;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.statDivider {
|
||||||
|
width: 1px;
|
||||||
|
height: 24px;
|
||||||
|
background: #e8e8e8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -121,6 +255,83 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 18px !important;
|
font-size: 18px !important;
|
||||||
|
|
||||||
|
:global {
|
||||||
|
.anticon {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #1890ff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progressSection {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
.sectionTitle {
|
||||||
|
color: #1d1d1f !important;
|
||||||
|
margin-bottom: 16px !important;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 18px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progressCard {
|
||||||
|
border-radius: 16px;
|
||||||
|
background: #fff;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03), 0 1px 6px -1px rgba(0, 0, 0, 0.02), 0 2px 4px rgba(0, 0, 0, 0.02);
|
||||||
|
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
:global {
|
||||||
|
.ant-card-body {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progressCardContent {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progressIcon {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.progressInfo {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
|
||||||
|
.progressLabel {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progressValue {
|
||||||
|
margin: 4px 0 0 0 !important;
|
||||||
|
font-size: 28px !important;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
color: #1d1d1f;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,8 +357,23 @@
|
|||||||
0 0 0 1px rgba(0, 0, 0, 0.04);
|
0 0 0 1px rgba(0, 0, 0, 0.04);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.typeIconWrapper {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
border-radius: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0 auto 12px;
|
||||||
|
border: 1.5px solid;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
.typeIcon {
|
.typeIcon {
|
||||||
margin-bottom: 10px;
|
font-size: 32px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.typeTitle {
|
.typeTitle {
|
||||||
@ -173,6 +399,13 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 18px !important;
|
font-size: 18px !important;
|
||||||
|
|
||||||
|
:global {
|
||||||
|
.anticon {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #73d13d;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.quickCard {
|
.quickCard {
|
||||||
@ -197,6 +430,22 @@
|
|||||||
0 0 0 1px rgba(0, 0, 0, 0.04);
|
0 0 0 1px rgba(0, 0, 0, 0.04);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.quickIconWrapper {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
border-radius: 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
border: 1.5px solid;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quickIcon {
|
||||||
|
font-size: 28px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 响应式设计 - 移动端 (< 768px)
|
// 响应式设计 - 移动端 (< 768px)
|
||||||
@ -224,27 +473,104 @@
|
|||||||
height: 48px;
|
height: 48px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.titleRow {
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 22px !important;
|
font-size: 22px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.totalBadge {
|
||||||
|
padding: 3px 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.subtitle {
|
.subtitle {
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.userInfo {
|
.userInfo {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 8px 12px;
|
min-width: auto;
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 12px;
|
||||||
|
|
||||||
:global {
|
.userInfoContent {
|
||||||
.ant-space {
|
gap: 12px;
|
||||||
width: 100%;
|
margin-bottom: 12px;
|
||||||
justify-content: space-between;
|
padding-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatarWrapper {
|
||||||
|
.userAvatar {
|
||||||
|
width: 48px !important;
|
||||||
|
height: 48px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ant-btn {
|
.avatarBadge {
|
||||||
font-size: 12px;
|
width: 20px;
|
||||||
padding: 4px 8px;
|
height: 20px;
|
||||||
|
font-size: 11px;
|
||||||
|
bottom: -1px;
|
||||||
|
right: -1px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.userDetails {
|
||||||
|
gap: 3px;
|
||||||
|
|
||||||
|
.userNickname {
|
||||||
|
font-size: 15px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userUsername {
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userTypeBadge {
|
||||||
|
padding: 3px 8px;
|
||||||
|
margin-top: 1px;
|
||||||
|
|
||||||
|
.badgeIcon {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userTypeText {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdownTrigger {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
|
||||||
|
.dropdownIcon {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.userStatsRow {
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.statItem {
|
||||||
|
padding: 6px 4px;
|
||||||
|
border-radius: 6px;
|
||||||
|
|
||||||
|
.statValue {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statLabel {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.statDivider {
|
||||||
|
height: 20px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -273,11 +599,59 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.progressSection {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
.sectionTitle {
|
||||||
|
font-size: 16px !important;
|
||||||
|
margin-bottom: 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progressCard {
|
||||||
|
border-radius: 12px;
|
||||||
|
|
||||||
|
:global {
|
||||||
|
.ant-card-body {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progressIcon {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
:global {
|
||||||
|
svg {
|
||||||
|
font-size: 20px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progressInfo {
|
||||||
|
.progressLabel {
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progressValue {
|
||||||
|
font-size: 22px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.typeCard {
|
.typeCard {
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
|
|
||||||
|
.typeIconWrapper {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
border-radius: 12px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.typeIcon {
|
.typeIcon {
|
||||||
font-size: 32px !important;
|
font-size: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.typeTitle {
|
.typeTitle {
|
||||||
@ -312,6 +686,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.quickIconWrapper {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quickIcon {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,11 +710,38 @@
|
|||||||
.title {
|
.title {
|
||||||
font-size: 28px !important;
|
font-size: 28px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.userInfo {
|
||||||
|
min-width: 450px;
|
||||||
|
|
||||||
|
.avatarWrapper .userAvatar {
|
||||||
|
width: 52px !important;
|
||||||
|
height: 52px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userStatsRow .statItem {
|
||||||
|
.statValue {
|
||||||
|
font-size: 17px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.typeCard {
|
.typeCard {
|
||||||
|
.typeIconWrapper {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
.typeIcon {
|
.typeIcon {
|
||||||
font-size: 36px !important;
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.progressSection {
|
||||||
|
.sectionTitle {
|
||||||
|
font-size: 17px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -354,6 +765,47 @@
|
|||||||
.subtitle {
|
.subtitle {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.userInfo {
|
||||||
|
min-width: 560px;
|
||||||
|
|
||||||
|
.avatarWrapper .userAvatar {
|
||||||
|
width: 60px !important;
|
||||||
|
height: 60px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userDetails {
|
||||||
|
.userNickname {
|
||||||
|
font-size: 17px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userUsername {
|
||||||
|
font-size: 14px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userTypeBadge {
|
||||||
|
padding: 5px 12px;
|
||||||
|
|
||||||
|
.badgeIcon {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.userTypeText {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.userStatsRow .statItem {
|
||||||
|
.statValue {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.statLabel {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.statsCard {
|
.statsCard {
|
||||||
@ -368,6 +820,14 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.progressSection {
|
||||||
|
margin-bottom: 32px;
|
||||||
|
|
||||||
|
.sectionTitle {
|
||||||
|
font-size: 20px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.quickStart {
|
.quickStart {
|
||||||
.sectionTitle {
|
.sectionTitle {
|
||||||
font-size: 20px !important;
|
font-size: 20px !important;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { Card, Statistic, Row, Col, Typography, message, Space, Avatar, Button, Modal, Form, Radio, Alert, Input, Switch, InputNumber, Divider, Badge, Dropdown } from 'antd'
|
import { Typography, message, Space, Avatar, Button, Modal, Form, Radio, Alert, Input, Switch, InputNumber, Divider, Dropdown, Row, Col, Card } from 'antd'
|
||||||
import type { MenuProps } from 'antd'
|
import type { MenuProps } from 'antd'
|
||||||
import {
|
import {
|
||||||
FileTextOutlined,
|
FileTextOutlined,
|
||||||
@ -15,7 +15,10 @@ import {
|
|||||||
UnorderedListOutlined as ListOutlined,
|
UnorderedListOutlined as ListOutlined,
|
||||||
LockOutlined,
|
LockOutlined,
|
||||||
IdcardOutlined,
|
IdcardOutlined,
|
||||||
DownOutlined,
|
MoreOutlined,
|
||||||
|
CloseCircleOutlined,
|
||||||
|
FormOutlined,
|
||||||
|
FileMarkdownOutlined,
|
||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
import * as questionApi from '../api/question'
|
import * as questionApi from '../api/question'
|
||||||
import { fetchWithAuth } from '../utils/request'
|
import { fetchWithAuth } from '../utils/request'
|
||||||
@ -24,48 +27,48 @@ import styles from './Home.module.less'
|
|||||||
|
|
||||||
const { Title, Paragraph, Text } = Typography
|
const { Title, Paragraph, Text } = Typography
|
||||||
|
|
||||||
// 题型配置 - 使用数据库中的实际类型
|
// 题型配置 - 使用数据库中的实际类型,采用明快的配色方案
|
||||||
const questionTypes = [
|
const questionTypes = [
|
||||||
{
|
{
|
||||||
key: 'multiple-choice',
|
key: 'multiple-choice',
|
||||||
title: '选择题',
|
title: '选择题',
|
||||||
icon: <CheckCircleOutlined />,
|
icon: <CheckCircleOutlined />,
|
||||||
color: '#1677ff',
|
color: '#1890ff', // 明亮的蓝色
|
||||||
description: '基础知识考察',
|
description: '基础知识考察',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'multiple-selection',
|
key: 'multiple-selection',
|
||||||
title: '多选题',
|
title: '多选题',
|
||||||
icon: <UnorderedListOutlined />,
|
icon: <UnorderedListOutlined />,
|
||||||
color: '#52c41a',
|
color: '#52c41a', // 明亮的绿色
|
||||||
description: '综合能力提升',
|
description: '综合能力提升',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'true-false',
|
key: 'true-false',
|
||||||
title: '判断题',
|
title: '判断题',
|
||||||
icon: <CheckCircleOutlined />,
|
icon: <CloseCircleOutlined />,
|
||||||
color: '#fa8c16',
|
color: '#fa8c16', // 明亮的橙色
|
||||||
description: '快速判断训练',
|
description: '快速判断训练',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'fill-in-blank',
|
key: 'fill-in-blank',
|
||||||
title: '填空题',
|
title: '填空题',
|
||||||
icon: <FileTextOutlined />,
|
icon: <FormOutlined />,
|
||||||
color: '#722ed1',
|
color: '#faad14', // 明亮的金色
|
||||||
description: '填空补充练习',
|
description: '填空补充练习',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'short-answer',
|
key: 'short-answer',
|
||||||
title: '简答题',
|
title: '简答题',
|
||||||
icon: <EditOutlined />,
|
icon: <EditOutlined />,
|
||||||
color: '#eb2f96',
|
color: '#722ed1', // 明亮的紫色
|
||||||
description: '深度理解练习',
|
description: '深度理解练习',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'essay', // 特殊标识,根据用户类型动态路由
|
key: 'essay', // 特殊标识,根据用户类型动态路由
|
||||||
title: '论述题',
|
title: '论述题',
|
||||||
icon: <FileTextOutlined />,
|
icon: <FileMarkdownOutlined />,
|
||||||
color: '#f759ab',
|
color: '#eb2f96', // 明亮的粉色
|
||||||
description: '深度分析与表达',
|
description: '深度分析与表达',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -93,6 +96,7 @@ const Home: React.FC = () => {
|
|||||||
answered_questions: 0,
|
answered_questions: 0,
|
||||||
correct_answers: 0,
|
correct_answers: 0,
|
||||||
wrong_questions: 0,
|
wrong_questions: 0,
|
||||||
|
total_answers: 0,
|
||||||
accuracy: 0,
|
accuracy: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -360,75 +364,74 @@ const Home: React.FC = () => {
|
|||||||
<div className={styles.logoArea}>
|
<div className={styles.logoArea}>
|
||||||
<img src="/icon.svg" alt="AnKao Logo" className={styles.logo} />
|
<img src="/icon.svg" alt="AnKao Logo" className={styles.logo} />
|
||||||
<div>
|
<div>
|
||||||
<Title level={2} className={styles.title}>AnKao 刷题</Title>
|
<div className={styles.titleRow}>
|
||||||
|
<Title level={2} className={styles.title}>AnKao 刷题</Title>
|
||||||
|
<span className={styles.totalBadge}>{statistics.total_questions} 题</span>
|
||||||
|
</div>
|
||||||
<Paragraph className={styles.subtitle}>安全保密考试题库</Paragraph>
|
<Paragraph className={styles.subtitle}>安全保密考试题库</Paragraph>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* 用户信息 */}
|
{/* 用户信息 */}
|
||||||
{userInfo && (
|
{userInfo && (
|
||||||
<Dropdown menu={{ items: userMenuItems }} trigger={['click']}>
|
<div className={styles.userInfo}>
|
||||||
<div className={styles.userInfo}>
|
<div className={styles.userInfoContent}>
|
||||||
<Space size="middle">
|
<div className={styles.avatarWrapper}>
|
||||||
<Avatar
|
<Avatar
|
||||||
src={userInfo.avatar || undefined}
|
src={userInfo.avatar || undefined}
|
||||||
size={44}
|
size={56}
|
||||||
icon={<UserOutlined />}
|
icon={<UserOutlined />}
|
||||||
|
className={styles.userAvatar}
|
||||||
/>
|
/>
|
||||||
<div className={styles.userDetails}>
|
<div className={styles.avatarBadge}>
|
||||||
<Text strong className={styles.userNickname}>{userInfo.nickname}</Text>
|
{userInfo.user_type ? (
|
||||||
<Text type="secondary" className={styles.userUsername}>@{userInfo.username}</Text>
|
<CheckCircleOutlined />
|
||||||
<Badge
|
) : (
|
||||||
color={userInfo.user_type ? '#52c41a' : '#ff4d4f'}
|
<SettingOutlined />
|
||||||
text={
|
)}
|
||||||
<Text className={styles.userType}>
|
|
||||||
{getUserTypeText(userInfo.user_type)}
|
|
||||||
</Text>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<DownOutlined style={{ fontSize: '12px', color: '#8c8c8c' }} />
|
</div>
|
||||||
</Space>
|
<div className={styles.userDetails}>
|
||||||
|
<Text strong className={styles.userNickname}>{userInfo.nickname}</Text>
|
||||||
|
<Text type="secondary" className={styles.userUsername}>@{userInfo.username}</Text>
|
||||||
|
<div className={styles.userTypeBadge}>
|
||||||
|
<IdcardOutlined className={styles.badgeIcon} />
|
||||||
|
<Text className={styles.userTypeText}>
|
||||||
|
{getUserTypeText(userInfo.user_type)}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Dropdown menu={{ items: userMenuItems }} trigger={['click']} placement="bottomRight">
|
||||||
|
<div className={styles.dropdownTrigger}>
|
||||||
|
<MoreOutlined className={styles.dropdownIcon} />
|
||||||
|
</div>
|
||||||
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
</Dropdown>
|
<div className={styles.userStatsRow}>
|
||||||
|
<div className={styles.statItem}>
|
||||||
|
<div className={styles.statValue}>{statistics.total_answers}</div>
|
||||||
|
<div className={styles.statLabel}>刷题次数</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.statDivider}></div>
|
||||||
|
<div className={styles.statItem}>
|
||||||
|
<div className={styles.statValue}>{statistics.answered_questions}</div>
|
||||||
|
<div className={styles.statLabel}>已刷</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.statDivider}></div>
|
||||||
|
<div className={styles.statItem}>
|
||||||
|
<div className={styles.statValue}>{statistics.wrong_questions}</div>
|
||||||
|
<div className={styles.statLabel}>错题本数</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.statDivider}></div>
|
||||||
|
<div className={styles.statItem}>
|
||||||
|
<div className={styles.statValue}>{statistics.accuracy.toFixed(0)}%</div>
|
||||||
|
<div className={styles.statLabel}>准确率</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 统计卡片 */}
|
|
||||||
<Card className={styles.statsCard}>
|
|
||||||
<Row gutter={[16, 16]}>
|
|
||||||
<Col xs={12} sm={12} md={6}>
|
|
||||||
<Statistic
|
|
||||||
title="题库总数"
|
|
||||||
value={statistics.total_questions}
|
|
||||||
valueStyle={{ color: '#1677ff', fontSize: '24px' }}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col xs={12} sm={12} md={6}>
|
|
||||||
<Statistic
|
|
||||||
title="已答题数"
|
|
||||||
value={statistics.answered_questions}
|
|
||||||
valueStyle={{ color: '#52c41a', fontSize: '24px' }}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col xs={12} sm={12} md={6}>
|
|
||||||
<Statistic
|
|
||||||
title="错题数量"
|
|
||||||
value={statistics.wrong_questions}
|
|
||||||
valueStyle={{ color: '#ff4d4f', fontSize: '24px' }}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
<Col xs={12} sm={12} md={6}>
|
|
||||||
<Statistic
|
|
||||||
title="正确率"
|
|
||||||
value={statistics.accuracy.toFixed(0)}
|
|
||||||
suffix="%"
|
|
||||||
valueStyle={{ color: '#fa8c16', fontSize: '24px' }}
|
|
||||||
/>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
{/* 题型选择 */}
|
{/* 题型选择 */}
|
||||||
<div className={styles.typeSection}>
|
<div className={styles.typeSection}>
|
||||||
<Title level={4} className={styles.sectionTitle}>
|
<Title level={4} className={styles.sectionTitle}>
|
||||||
@ -448,8 +451,16 @@ const Home: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className={styles.typeIcon} style={{ color: type.color, fontSize: '40px' }}>
|
<div
|
||||||
{type.icon}
|
className={styles.typeIconWrapper}
|
||||||
|
style={{
|
||||||
|
background: `linear-gradient(135deg, ${type.color}15 0%, ${type.color}08 100%)`,
|
||||||
|
borderColor: `${type.color}30`
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className={styles.typeIcon} style={{ color: type.color }}>
|
||||||
|
{type.icon}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Title level={5} className={styles.typeTitle}>{type.title}</Title>
|
<Title level={5} className={styles.typeTitle}>{type.title}</Title>
|
||||||
<Paragraph type="secondary" className={styles.typeDesc}>{type.description}</Paragraph>
|
<Paragraph type="secondary" className={styles.typeDesc}>{type.description}</Paragraph>
|
||||||
@ -472,8 +483,14 @@ const Home: React.FC = () => {
|
|||||||
onClick={() => navigate('/wrong-questions')}
|
onClick={() => navigate('/wrong-questions')}
|
||||||
>
|
>
|
||||||
<Space align="center" size="middle" style={{ width: '100%' }}>
|
<Space align="center" size="middle" style={{ width: '100%' }}>
|
||||||
<div className={styles.quickIcon}>
|
<div
|
||||||
<BookOutlined style={{ fontSize: '32px', color: '#ff4d4f' }} />
|
className={styles.quickIconWrapper}
|
||||||
|
style={{
|
||||||
|
background: 'linear-gradient(135deg, #fff1f0 0%, #ffe7e6 100%)',
|
||||||
|
borderColor: '#ffccc7'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<BookOutlined className={styles.quickIcon} style={{ color: '#ff4d4f' }} />
|
||||||
</div>
|
</div>
|
||||||
<div style={{ flex: 1 }}>
|
<div style={{ flex: 1 }}>
|
||||||
<Title level={5} style={{ margin: 0 }}>错题本</Title>
|
<Title level={5} style={{ margin: 0 }}>错题本</Title>
|
||||||
@ -490,8 +507,14 @@ const Home: React.FC = () => {
|
|||||||
onClick={() => navigate('/question-list')}
|
onClick={() => navigate('/question-list')}
|
||||||
>
|
>
|
||||||
<Space align="center" size="middle" style={{ width: '100%' }}>
|
<Space align="center" size="middle" style={{ width: '100%' }}>
|
||||||
<div className={styles.quickIcon}>
|
<div
|
||||||
<ListOutlined style={{ fontSize: '32px', color: '#1677ff' }} />
|
className={styles.quickIconWrapper}
|
||||||
|
style={{
|
||||||
|
background: 'linear-gradient(135deg, #e6f7ff 0%, #d6f0ff 100%)',
|
||||||
|
borderColor: '#91caff'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ListOutlined className={styles.quickIcon} style={{ color: '#1890ff' }} />
|
||||||
</div>
|
</div>
|
||||||
<div style={{ flex: 1 }}>
|
<div style={{ flex: 1 }}>
|
||||||
<Title level={5} style={{ margin: 0 }}>题目列表</Title>
|
<Title level={5} style={{ margin: 0 }}>题目列表</Title>
|
||||||
@ -510,8 +533,14 @@ const Home: React.FC = () => {
|
|||||||
onClick={() => navigate('/question-management')}
|
onClick={() => navigate('/question-management')}
|
||||||
>
|
>
|
||||||
<Space align="center" size="middle" style={{ width: '100%' }}>
|
<Space align="center" size="middle" style={{ width: '100%' }}>
|
||||||
<div className={styles.quickIcon}>
|
<div
|
||||||
<SettingOutlined style={{ fontSize: '32px', color: '#13c2c2' }} />
|
className={styles.quickIconWrapper}
|
||||||
|
style={{
|
||||||
|
background: 'linear-gradient(135deg, #e6fffb 0%, #d6f5f0 100%)',
|
||||||
|
borderColor: '#87e8de'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SettingOutlined className={styles.quickIcon} style={{ color: '#36cfc9' }} />
|
||||||
</div>
|
</div>
|
||||||
<div style={{ flex: 1 }}>
|
<div style={{ flex: 1 }}>
|
||||||
<Title level={5} style={{ margin: 0 }}>题库管理</Title>
|
<Title level={5} style={{ margin: 0 }}>题库管理</Title>
|
||||||
|
|||||||
@ -48,6 +48,7 @@ export interface Statistics {
|
|||||||
answered_questions: number
|
answered_questions: number
|
||||||
correct_answers: number
|
correct_answers: number
|
||||||
wrong_questions: number
|
wrong_questions: number
|
||||||
|
total_answers: number // 刷题次数
|
||||||
accuracy: number
|
accuracy: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user