From 43680cce2221a39c018473b0254961d0e227fe25 Mon Sep 17 00:00:00 2001 From: yanlongqi Date: Tue, 18 Nov 2025 02:39:48 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=AF=95=E5=8D=B7=E6=89=93?= =?UTF-8?q?=E5=8D=B0=E5=8A=9F=E8=83=BD=E5=B9=B6=E7=A7=BB=E9=99=A4=E6=89=93?= =?UTF-8?q?=E5=8D=B0=E7=AD=94=E6=A1=88=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 移除打印答案按钮及相关功能,简化界面 2. 优化填空题打印效果,使用答案长度计算下划线宽度 3. 改进试卷头部布局,添加日期和成绩栏 4. 更新考试说明,调整考试时间为60分钟 5. 优化打印样式,使用宋体字并减小间距 6. 完善论述题显示,添加用户类型提示 7. 后端支持同时返回两种论述题题目 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- internal/handlers/exam_grading.go | 4 +- internal/handlers/exam_handler.go | 51 +++++- internal/handlers/practice_handler.go | 20 +++ internal/models/practice_question.go | 1 + web/src/pages/ExamManagement.tsx | 10 +- web/src/pages/ExamOnline.tsx | 95 +++++++--- web/src/pages/ExamPrint.module.less | 85 ++++++--- web/src/pages/ExamPrint.tsx | 238 +++++++++++++------------- web/src/types/question.ts | 1 + 9 files changed, 334 insertions(+), 171 deletions(-) diff --git a/internal/handlers/exam_grading.go b/internal/handlers/exam_grading.go index ce53880..87c423f 100644 --- a/internal/handlers/exam_grading.go +++ b/internal/handlers/exam_grading.go @@ -73,8 +73,8 @@ func gradeExam(recordID uint, examID uint, userID uint) { "multiple-choice": 1.0, "multiple-selection": 2.5, "short-answer": 10.0, - "ordinary-essay": 5.0, - "management-essay": 5.0, + "ordinary-essay": 4.5, + "management-essay": 4.5, } // 评分 diff --git a/internal/handlers/exam_handler.go b/internal/handlers/exam_handler.go index 23c0c90..b67ef93 100644 --- a/internal/handlers/exam_handler.go +++ b/internal/handlers/exam_handler.go @@ -44,12 +44,6 @@ func CreateExam(c *gin.Context) { return } - // 根据用户类型决定论述题类型 - essayType := "ordinary-essay" // 默认普通涉密人员论述题 - if user.UserType == "management-person" { - essayType = "management-essay" // 保密管理人员论述题 - } - // 使用固定的题型配置(总分100分) questionTypes := []models.QuestionTypeConfig{ {Type: "fill-in-blank", Count: 10, Score: 2.0}, // 20分 @@ -57,7 +51,8 @@ func CreateExam(c *gin.Context) { {Type: "multiple-choice", Count: 10, Score: 1.0}, // 10分 {Type: "multiple-selection", Count: 10, Score: 2.5}, // 25分 {Type: "short-answer", Count: 2, Score: 10.0}, // 20分 - {Type: essayType, Count: 1, Score: 5.0}, // 5分(根据用户类型选择论述题) + {Type: "ordinary-essay", Count: 1, Score: 4.5}, // 4.5分(普通涉密人员论述题) + {Type: "management-essay", Count: 1, Score: 4.5}, // 4.5分(保密管理人员论述题) } // 按题型配置随机抽取题目 @@ -266,6 +261,39 @@ func GetExamDetail(c *gin.Context) { for _, q := range questions { questionMap[q.ID] = q } + + // 检查是否包含论述题,如果没有则添加两种论述题 + hasOrdinaryEssay := false + hasManagementEssay := false + for _, q := range questions { + if q.Type == "ordinary-essay" { + hasOrdinaryEssay = true + } + if q.Type == "management-essay" { + hasManagementEssay = true + } + } + + // 如果缺少论述题,则补充 + var additionalQuestions []models.PracticeQuestion + if !hasOrdinaryEssay { + var ordinaryEssay models.PracticeQuestion + if err := db.Where("type = ?", "ordinary-essay").First(&ordinaryEssay).Error; err == nil { + additionalQuestions = append(additionalQuestions, ordinaryEssay) + } + } + if !hasManagementEssay { + var managementEssay models.PracticeQuestion + if err := db.Where("type = ?", "management-essay").First(&managementEssay).Error; err == nil { + additionalQuestions = append(additionalQuestions, managementEssay) + } + } + + // 将补充的题目添加到题目映射中 + for _, q := range additionalQuestions { + questionMap[q.ID] = q + } + orderedDTOs := make([]models.PracticeQuestionDTO, 0, len(questionIDs)) for _, id := range questionIDs { if q, ok := questionMap[id]; ok { @@ -278,6 +306,15 @@ func GetExamDetail(c *gin.Context) { } } + // 添加补充的论述题到结果中 + for _, q := range additionalQuestions { + dto := convertToDTO(q) + if !showAnswer { + dto.Answer = nil // 不显示答案 + } + orderedDTOs = append(orderedDTOs, dto) + } + c.JSON(http.StatusOK, gin.H{ "success": true, "data": gin.H{ diff --git a/internal/handlers/practice_handler.go b/internal/handlers/practice_handler.go index c855f0f..012c084 100644 --- a/internal/handlers/practice_handler.go +++ b/internal/handlers/practice_handler.go @@ -574,9 +574,15 @@ func convertToDTO(question models.PracticeQuestion) models.PracticeQuestionDTO { if err := json.Unmarshal([]byte(question.AnswerData), &answer); err != nil { // JSON解析失败,直接使用原始字符串 dto.Answer = question.AnswerData + // 计算答案长度 + dto.AnswerLengths = []int{len(question.AnswerData)} } else { // JSON解析成功 dto.Answer = answer + // 计算答案长度 + if answerStr, ok := answer.(string); ok { + dto.AnswerLengths = []int{len(answerStr)} + } } } else { // 其他题型必须是JSON格式 @@ -586,6 +592,20 @@ func convertToDTO(question models.PracticeQuestion) models.PracticeQuestionDTO { question.ID, question.Type, err) } else { dto.Answer = answer + // 计算填空题答案长度 + if question.Type == "fill-in-blank" { + if answers, ok := answer.([]interface{}); ok { + lengths := make([]int, len(answers)) + for i, ans := range answers { + if ansStr, ok := ans.(string); ok { + lengths[i] = len(ansStr) + } else { + lengths[i] = len(fmt.Sprintf("%v", ans)) + } + } + dto.AnswerLengths = lengths + } + } } } } diff --git a/internal/models/practice_question.go b/internal/models/practice_question.go index 5f0efd0..53db305 100644 --- a/internal/models/practice_question.go +++ b/internal/models/practice_question.go @@ -25,6 +25,7 @@ type PracticeQuestionDTO struct { Options []Option `json:"options"` // 选择题选项数组 Category string `json:"category"` // 题目分类 Answer interface{} `json:"answer"` // 正确答案(用于题目管理编辑) + AnswerLengths []int `json:"answer_lengths,omitempty"` // 答案长度数组(用于打印时计算横线长度) } // PracticeAnswerSubmit 练习题答案提交 diff --git a/web/src/pages/ExamManagement.tsx b/web/src/pages/ExamManagement.tsx index f10f070..5d5caee 100644 --- a/web/src/pages/ExamManagement.tsx +++ b/web/src/pages/ExamManagement.tsx @@ -26,7 +26,8 @@ import { ClockCircleOutlined, CheckCircleOutlined, TrophyOutlined, - HistoryOutlined + HistoryOutlined, + PrinterOutlined } from '@ant-design/icons' import * as examApi from '../api/exam' import styles from './ExamManagement.module.less' @@ -225,6 +226,13 @@ const ExamManagement: React.FC = () => { > 查看答案 , + , - - - - + {/* 打印内容区 */} @@ -422,12 +432,11 @@ const ExamPrint: React.FC = () => { 保密知识模拟考试{showAnswer ? '(答案)' : ''} -
-
- 姓名:__________________ -
-
- 日期:__________________ +
+
+ 日期:{formatDate(new Date())} + 姓名:________________ + 成绩:________________
@@ -437,10 +446,9 @@ const ExamPrint: React.FC = () => { 考试说明
    -
  • 本试卷满分100分,考试时间为90分钟
  • -
  • 请在答题区域内作答,字迹清晰工整
  • -
  • 简答题仅供参考,不计入总分
  • -
  • 论述题从以下2道题目中任选1道作答
  • +
  • 本试卷满分100分,考试时间为60分钟
  • +
  • 填空题每题8分,请在答题区域内作答,字迹清晰工整
  • +
  • 论述题每题9分,从以下2道题目中任选1道作答
)} @@ -455,11 +463,11 @@ const ExamPrint: React.FC = () => { {essayQuestions.length > 0 && (
- + {TYPE_NAME['ordinary-essay']} - (以下2道论述题任选1道作答,共25分) + (以下2道论述题任选1道作答,共9分)
diff --git a/web/src/types/question.ts b/web/src/types/question.ts index 191f3b8..bd50a0a 100644 --- a/web/src/types/question.ts +++ b/web/src/types/question.ts @@ -16,6 +16,7 @@ export interface Question { options: Option[] category: string answer?: any // 正确答案(用于题目管理编辑) + answer_lengths?: number[] // 答案长度数组(用于打印时计算横线长度) } // 提交答案