题型添加
parent
eeebf4bcb1
commit
1aaf69fbdf
|
|
@ -5,18 +5,23 @@ import com.alibaba.fastjson.JSONArray;
|
||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import com.zhangmeng.online.exam.ui.api.ApiUtils;
|
import com.zhangmeng.online.exam.ui.api.ApiUtils;
|
||||||
import com.zhangmeng.online.exam.ui.utils.HttpUtils;
|
import com.zhangmeng.online.exam.ui.utils.HttpUtils;
|
||||||
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.beans.value.WritableValue;
|
import javafx.beans.value.WritableValue;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
import javafx.css.StyleableProperty;
|
import javafx.css.StyleableProperty;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
import javafx.scene.AccessibleRole;
|
import javafx.scene.AccessibleRole;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.ScrollPane;
|
import javafx.scene.control.ScrollPane;
|
||||||
import javafx.scene.layout.FlowPane;
|
import javafx.scene.layout.FlowPane;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.scene.text.Font;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* 考试组件
|
* 考试组件
|
||||||
*
|
*
|
||||||
* @author zm
|
* @author zm
|
||||||
|
|
@ -25,10 +30,33 @@ import java.util.*;
|
||||||
*/
|
*/
|
||||||
public class ExamComponent extends ScrollPane {
|
public class ExamComponent extends ScrollPane {
|
||||||
|
|
||||||
|
//计数器
|
||||||
|
private final SimpleIntegerProperty SINGLE_CHOICE_COUNT = new SimpleIntegerProperty(1);
|
||||||
|
private final SimpleIntegerProperty MULTIPLE_CHOICE_COUNT = new SimpleIntegerProperty(1);
|
||||||
|
private final SimpleIntegerProperty JUDGMENT_COUNT = new SimpleIntegerProperty(1);
|
||||||
|
private final SimpleIntegerProperty NUMERICAL_COUNT = new SimpleIntegerProperty(1);
|
||||||
|
private final SimpleIntegerProperty Fill_IN_THE_BLANKS_COUNT = new SimpleIntegerProperty(1);
|
||||||
|
private final SimpleIntegerProperty SHORT_ANSWER_COUNT = new SimpleIntegerProperty(1);
|
||||||
|
|
||||||
|
// 单选题
|
||||||
|
private final ObservableList<SingleChoiceComponent> singleChoiceComponents = FXCollections.observableArrayList();
|
||||||
|
// 多选题
|
||||||
|
private final ObservableList<MultiChoiceComponent> multiChoiceComponents = FXCollections.observableArrayList();
|
||||||
|
// 判断题
|
||||||
|
private final ObservableList<TrueFalseComponent> judgmentComponents = FXCollections.observableArrayList();
|
||||||
|
// 计算题
|
||||||
|
private final ObservableList<NumericalComponent> numericalComponents = FXCollections.observableArrayList();
|
||||||
|
// 填空题
|
||||||
|
private final ObservableList<FillBlankComponent> fillInBlankComponents = FXCollections.observableArrayList();
|
||||||
|
// 简答题
|
||||||
|
private final ObservableList<ShortAnswerComponent> shortAnswerComponents = FXCollections.observableArrayList();
|
||||||
|
|
||||||
private VBox vBox;
|
private VBox vBox;
|
||||||
|
|
||||||
public ExamComponent() {
|
public ExamComponent() {
|
||||||
|
|
||||||
this.vBox = new VBox(5);
|
this.vBox = new VBox(5);
|
||||||
|
this.vBox.setPadding(new Insets(10));
|
||||||
|
|
||||||
Map<String, Object> params = new HashMap<>();
|
Map<String, Object> params = new HashMap<>();
|
||||||
params.put("pageNum", ApiUtils.PAGE_NUM);
|
params.put("pageNum", ApiUtils.PAGE_NUM);
|
||||||
|
|
@ -39,48 +67,126 @@ public class ExamComponent extends ScrollPane {
|
||||||
JSONArray data = jsonObject.getJSONArray("data");
|
JSONArray data = jsonObject.getJSONArray("data");
|
||||||
int total = jsonObject.getIntValue("total");
|
int total = jsonObject.getIntValue("total");
|
||||||
int index = 1;
|
int index = 1;
|
||||||
|
|
||||||
|
|
||||||
for (Object datum : data) {
|
for (Object datum : data) {
|
||||||
JSONObject question = (JSONObject) datum;
|
JSONObject question = (JSONObject) datum;
|
||||||
String type = question.getString("type");
|
String type = question.getString("type");
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case "单选题" -> {
|
case "单选题" -> singleChoice(question);
|
||||||
String list = question.getString("options");
|
case "多选题" -> multiChoice(question);
|
||||||
List<Map> maps = JSONArray.parseArray(list, Map.class);
|
case "判断题" -> judgment(question);
|
||||||
String correctAnswer = null;
|
case "计算题" -> numerical(question);
|
||||||
List<Map<String,Object>> optionList = new ArrayList<>();
|
case "填空题" -> fillInBlank(question);
|
||||||
for (Map map : maps) {
|
case "简答题" -> shortAnswer(question);
|
||||||
boolean isAnswer = (boolean) map.get("isAnswer");
|
default -> throw new IllegalStateException("Unexpected value: " + type);
|
||||||
|
|
||||||
Map<String,Object> option = new HashMap<>();
|
|
||||||
option.put("id", map.get("id"));
|
|
||||||
option.put("text",map.get("name").toString()+ map.get("content").toString());
|
|
||||||
option.put("isAnswer",isAnswer);
|
|
||||||
optionList.add(option);
|
|
||||||
}
|
}
|
||||||
SingleChoiceComponent singleChoiceComponent = new SingleChoiceComponent(index + "." + question.getString("name"), optionList);
|
|
||||||
singleChoiceComponent.setCorrectAnswer(correctAnswer);
|
|
||||||
Map<String,Object> context = new HashMap<>();
|
|
||||||
context.put("question_id", question.getString("id"));
|
|
||||||
context.put("paper_id",1);
|
|
||||||
singleChoiceComponent.setContext(context);
|
|
||||||
this.vBox.getChildren().add(singleChoiceComponent);
|
|
||||||
index ++;
|
|
||||||
}
|
|
||||||
// case "多选题" -> MULTIPLE_CHOICE_SCORE.set(MULTIPLE_CHOICE_SCORE.get() + score);
|
|
||||||
// case "判断题" -> JUDGMENT_SCORE.set(JUDGMENT_SCORE.get() + score);
|
|
||||||
// case "计算题" -> NUMERICAL_SCORE.set(NUMERICAL_SCORE.get() + score);
|
|
||||||
// case "填空题" -> Fill_IN_THE_BLANKS.set(Fill_IN_THE_BLANKS.get() + score);
|
|
||||||
// case "简答题" -> SHORT_ANSWER_SCORE.set(SHORT_ANSWER_SCORE.get() + score);
|
|
||||||
// default -> throw new IllegalStateException("Unexpected value: " + type.get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Label label = new Label("一. 选择题");
|
||||||
}
|
label.setFont(new Font("黑体", 18));
|
||||||
|
this.vBox.getChildren().add(label);
|
||||||
|
this.vBox.getChildren().addAll(singleChoiceComponents);
|
||||||
|
this.vBox.getChildren().addAll(multiChoiceComponents);
|
||||||
|
this.vBox.getChildren().addAll(judgmentComponents);
|
||||||
|
this.vBox.getChildren().addAll(fillInBlankComponents);
|
||||||
|
this.vBox.getChildren().addAll(shortAnswerComponents);
|
||||||
|
// this.getChildren().addAll(numericalComponents);
|
||||||
|
|
||||||
this.setContent(vBox);
|
this.setContent(vBox);
|
||||||
this.setFitToWidth(true);
|
this.setFitToWidth(true);
|
||||||
this.setFitToHeight(true);
|
this.setFitToHeight(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//简答题
|
||||||
|
private void shortAnswer(JSONObject question) {
|
||||||
|
String list = question.getString("options");
|
||||||
|
List<Map> maps = JSONArray.parseArray(list, Map.class);
|
||||||
|
List<Map<String, Object>> optionList = new ArrayList<>();
|
||||||
|
for (Map map : maps) {
|
||||||
|
boolean isAnswer = (boolean) map.get("isAnswer");
|
||||||
|
Map<String, Object> option = new HashMap<>();
|
||||||
|
option.put("id", map.get("id").toString());
|
||||||
|
option.put("text", map.get("name").toString() + map.get("content").toString());
|
||||||
|
option.put("isAnswer", isAnswer);
|
||||||
|
option.put("option", map.get("name").toString().trim().replace(".", ""));
|
||||||
|
option.put("content", map.get("content").toString());
|
||||||
|
optionList.add(option);
|
||||||
|
}
|
||||||
|
ShortAnswerComponent shortAnswerComponent = new ShortAnswerComponent(SINGLE_CHOICE_COUNT.get() + "." + question.getString("name"), optionList);
|
||||||
|
Map<String, Object> context = new HashMap<>();
|
||||||
|
context.put("question_id", question.getString("id"));
|
||||||
|
context.put("paper_id", 1);
|
||||||
|
shortAnswerComponent.setContext(context);
|
||||||
|
shortAnswerComponents.add(shortAnswerComponent);
|
||||||
|
SHORT_ANSWER_COUNT.set(SHORT_ANSWER_COUNT.get() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//填空题
|
||||||
|
private void fillInBlank(JSONObject question) {
|
||||||
|
}
|
||||||
|
|
||||||
|
//计算题
|
||||||
|
private void numerical(JSONObject question) {
|
||||||
|
}
|
||||||
|
|
||||||
|
//判断题
|
||||||
|
private void judgment(JSONObject question) {
|
||||||
|
|
||||||
|
String list = question.getString("options");
|
||||||
|
List<Map> maps = JSONArray.parseArray(list, Map.class);
|
||||||
|
String correctAnswer = null;
|
||||||
|
List<Map<String, Object>> optionList = new ArrayList<>();
|
||||||
|
for (Map map : maps) {
|
||||||
|
boolean isAnswer = (boolean) map.get("isAnswer");
|
||||||
|
Map<String, Object> option = new HashMap<>();
|
||||||
|
option.put("id", map.get("id").toString());
|
||||||
|
option.put("text", map.get("name").toString() + map.get("content").toString());
|
||||||
|
option.put("isAnswer", isAnswer);
|
||||||
|
option.put("option", map.get("name").toString().trim().replace(".", ""));
|
||||||
|
option.put("content", map.get("content").toString());
|
||||||
|
optionList.add(option);
|
||||||
|
}
|
||||||
|
TrueFalseComponent trueFalseComponent = new TrueFalseComponent(SINGLE_CHOICE_COUNT.get() + "." + question.getString("name"), optionList);
|
||||||
|
Map<String, Object> context = new HashMap<>();
|
||||||
|
context.put("question_id", question.getString("id"));
|
||||||
|
context.put("paper_id", 1);
|
||||||
|
trueFalseComponent.setContext(context);
|
||||||
|
judgmentComponents.add(trueFalseComponent);
|
||||||
|
JUDGMENT_COUNT.set(JUDGMENT_COUNT.get() + 1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//多选题
|
||||||
|
private void multiChoice(JSONObject question) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//单选题
|
||||||
|
private void singleChoice(JSONObject question) {
|
||||||
|
String list = question.getString("options");
|
||||||
|
List<Map> maps = JSONArray.parseArray(list, Map.class);
|
||||||
|
String correctAnswer = null;
|
||||||
|
List<Map<String, Object>> optionList = new ArrayList<>();
|
||||||
|
for (Map map : maps) {
|
||||||
|
boolean isAnswer = (boolean) map.get("isAnswer");
|
||||||
|
Map<String, Object> option = new HashMap<>();
|
||||||
|
option.put("id", map.get("id").toString());
|
||||||
|
option.put("text", map.get("name").toString() + map.get("content").toString());
|
||||||
|
option.put("isAnswer", isAnswer);
|
||||||
|
option.put("option", map.get("name").toString().trim().replace(".", ""));
|
||||||
|
option.put("content", map.get("content").toString());
|
||||||
|
optionList.add(option);
|
||||||
|
}
|
||||||
|
SingleChoiceComponent singleChoiceComponent = new SingleChoiceComponent(SINGLE_CHOICE_COUNT.get() + "." + question.getString("name"), optionList);
|
||||||
|
singleChoiceComponent.setCorrectAnswer(correctAnswer);
|
||||||
|
Map<String, Object> context = new HashMap<>();
|
||||||
|
context.put("question_id", question.getString("id"));
|
||||||
|
context.put("paper_id", 1);
|
||||||
|
singleChoiceComponent.setContext(context);
|
||||||
|
singleChoiceComponents.add(singleChoiceComponent);
|
||||||
|
SINGLE_CHOICE_COUNT.set(SINGLE_CHOICE_COUNT.get() + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,12 @@
|
||||||
package com.zhangmeng.online.exam.ui.components;
|
package com.zhangmeng.online.exam.ui.components;
|
||||||
|
|
||||||
import com.zhangmeng.online.exam.ui.utils.OptionsUtils;
|
import com.zhangmeng.online.exam.ui.utils.OptionsUtils;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
@ -25,6 +27,12 @@ public class MultiChoiceComponent extends VBox {
|
||||||
|
|
||||||
private List<CheckBox> checkBoxes = new ArrayList<>();
|
private List<CheckBox> checkBoxes = new ArrayList<>();
|
||||||
|
|
||||||
|
private HBox hBox = new HBox();
|
||||||
|
|
||||||
|
private int number_of_responses = 0;//作答次数
|
||||||
|
|
||||||
|
private final SimpleStringProperty result_answer = new SimpleStringProperty(); // 最终答案
|
||||||
|
|
||||||
|
|
||||||
public MultiChoiceComponent(String question, Set<String> options) {
|
public MultiChoiceComponent(String question, Set<String> options) {
|
||||||
Set<String> selected = new HashSet<>();
|
Set<String> selected = new HashSet<>();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.zhangmeng.online.exam.ui.components;
|
||||||
|
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author zm
|
||||||
|
* @date 2025/3/21 9:51
|
||||||
|
* @version: 1.0
|
||||||
|
*/
|
||||||
|
public class NumericalComponent extends VBox {
|
||||||
|
}
|
||||||
|
|
@ -359,7 +359,12 @@ public class PaperViewComponent extends SplitPane {
|
||||||
|
|
||||||
private void reloadData(List<Map<String, SimpleStringProperty>> changeList) {
|
private void reloadData(List<Map<String, SimpleStringProperty>> changeList) {
|
||||||
for (Map<String, SimpleStringProperty> chooseTableDatum : changeList) {
|
for (Map<String, SimpleStringProperty> chooseTableDatum : changeList) {
|
||||||
int score = Integer.parseInt(chooseTableDatum.get("score").get());
|
int score = 0;
|
||||||
|
|
||||||
|
if (chooseTableDatum.get("score").get() != null){
|
||||||
|
score = Integer.parseInt(chooseTableDatum.get("score").get());
|
||||||
|
}
|
||||||
|
|
||||||
SimpleStringProperty type = chooseTableDatum.get("type");
|
SimpleStringProperty type = chooseTableDatum.get("type");
|
||||||
|
|
||||||
switch (type.get()) {
|
switch (type.get()) {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
package com.zhangmeng.online.exam.ui.components;
|
package com.zhangmeng.online.exam.ui.components;
|
||||||
|
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.TextArea;
|
import javafx.scene.control.TextArea;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Description: 简答题组件
|
* @Description: 简答题组件
|
||||||
|
|
@ -16,30 +19,55 @@ import java.util.List;
|
||||||
* @version: 1.0
|
* @version: 1.0
|
||||||
*/
|
*/
|
||||||
public class ShortAnswerComponent extends VBox {
|
public class ShortAnswerComponent extends VBox {
|
||||||
// 题目数据模型
|
|
||||||
private static class Question {
|
private Map<String,Object> context = new HashMap<>();
|
||||||
String content; // 题目内容
|
|
||||||
List<String> keywords; // 参考答案关键词(如 ["封装","继承","多态"])
|
public Map<String, Object> getContext() {
|
||||||
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 示例题目
|
public void setContext(Map<String, Object> context) {
|
||||||
private final Question currentQuestion = new Question();
|
this.context = context;
|
||||||
{
|
|
||||||
currentQuestion.content = "简述面向对象编程的三大特性";
|
|
||||||
currentQuestion.keywords = Arrays.asList("封装", "继承", "多态");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShortAnswerComponent() {
|
private String correctAnswer = null; // 预设正确答案
|
||||||
|
|
||||||
|
private int number_of_responses = 0;//作答次数
|
||||||
|
|
||||||
|
private final SimpleStringProperty result_answer = new SimpleStringProperty(); // 最终答案
|
||||||
|
|
||||||
|
private HBox hBox = new HBox();
|
||||||
|
|
||||||
|
private Label questionLabel;
|
||||||
|
|
||||||
|
private TextArea answerArea;
|
||||||
|
|
||||||
|
private List<String> keywords = new ArrayList<>(); // 参考答案关键词(如 ["封装","继承","多态"])
|
||||||
|
|
||||||
|
public ShortAnswerComponent(String question, List<Map<String, Object>> options) {
|
||||||
|
|
||||||
|
this.setStyle("-fx-background-color: white; -fx-border-color: #eadcdc;" +
|
||||||
|
"-fx-border-radius: 10px; -fx-border-width: 1px; -fx-border-style: solid;-fx-background-radius: 10px;");
|
||||||
|
|
||||||
|
for (Map<String, Object> option : options) {
|
||||||
|
keywords.add( option.get("content").toString());
|
||||||
|
}
|
||||||
|
|
||||||
// 题目展示
|
// 题目展示
|
||||||
Label questionLabel = new Label(currentQuestion.content);
|
questionLabel = new Label(question);
|
||||||
questionLabel.setStyle("-fx-font-size: 16px; -fx-font-weight: bold;");
|
|
||||||
|
hBox.getChildren().add(questionLabel);
|
||||||
|
this.getChildren().add(hBox);
|
||||||
|
this.setPrefHeight(240);
|
||||||
|
|
||||||
|
|
||||||
// 输入组件
|
// 输入组件
|
||||||
TextArea answerArea = new TextArea();
|
answerArea = new TextArea();
|
||||||
|
answerArea.setMinHeight(200);
|
||||||
answerArea.setPromptText("在此输入答案(建议不少于50字)");
|
answerArea.setPromptText("在此输入答案(建议不少于50字)");
|
||||||
answerArea.setPrefRowCount(8);
|
|
||||||
answerArea.setWrapText(true); // 自动换行:ml-citation{ref="1" data="citationList"}
|
answerArea.setWrapText(true); // 自动换行:ml-citation{ref="1" data="citationList"}
|
||||||
|
this.getChildren().add(answerArea);
|
||||||
|
|
||||||
|
|
||||||
// 功能组件
|
// 功能组件
|
||||||
Button submitBtn = new Button("提交答案");
|
Button submitBtn = new Button("提交答案");
|
||||||
|
|
@ -57,7 +85,7 @@ public class ShortAnswerComponent extends VBox {
|
||||||
// 关键词匹配验证
|
// 关键词匹配验证
|
||||||
StringBuilder missingKeys = new StringBuilder();
|
StringBuilder missingKeys = new StringBuilder();
|
||||||
int matchCount = 0;
|
int matchCount = 0;
|
||||||
for (String keyword : currentQuestion.keywords) {
|
for (String keyword : keywords) {
|
||||||
if (userAnswer.contains(keyword)) {
|
if (userAnswer.contains(keyword)) {
|
||||||
matchCount++;
|
matchCount++;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -66,22 +94,27 @@ public class ShortAnswerComponent extends VBox {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 反馈逻辑
|
// 反馈逻辑
|
||||||
if (matchCount == currentQuestion.keywords.size()) {
|
if (matchCount == keywords.size()) {
|
||||||
resultLabel.setText("✅ 答案完整,包含所有关键词!");
|
resultLabel.setText("✅ 答案完整,包含所有关键词!");
|
||||||
resultLabel.setStyle("-fx-text-fill: #2ecc71;");
|
resultLabel.setStyle("-fx-text-fill: #2ecc71;");
|
||||||
} else if (matchCount > 0) {
|
} else if (matchCount > 0) {
|
||||||
resultLabel.setText("⚠ 部分正确(缺失:" + missingKeys.substring(1) + ")");
|
resultLabel.setText("⚠ 部分正确(缺失:" + missingKeys.substring(1) + ")");
|
||||||
resultLabel.setStyle("-fx-text-fill: #f1c40f;");
|
resultLabel.setStyle("-fx-text-fill: #f1c40f;");
|
||||||
} else {
|
} else {
|
||||||
resultLabel.setText("❌ 参考答案关键词:" + String.join(",", currentQuestion.keywords));
|
resultLabel.setText("❌ 参考答案关键词:" + String.join(",", keywords));
|
||||||
resultLabel.setStyle("-fx-text-fill: #e74c3c;");
|
resultLabel.setStyle("-fx-text-fill: #e74c3c;");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 布局容器
|
// 布局容器
|
||||||
this.setSpacing(20);
|
// this.setSpacing(20);
|
||||||
this.setPadding(new Insets(20));
|
// this.setPadding(new Insets(20));
|
||||||
this.getChildren().addAll(questionLabel, answerArea, submitBtn, resultLabel);
|
this.getChildren().addAll(submitBtn, resultLabel);
|
||||||
|
|
||||||
|
// 布局容器
|
||||||
|
this.setSpacing(10);
|
||||||
|
this.setPadding(new Insets(15));
|
||||||
|
this.hBox.getChildren().add(1,resultLabel);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,8 @@ package com.zhangmeng.online.exam.ui.components;
|
||||||
import com.zhangmeng.online.exam.ui.api.ApiUtils;
|
import com.zhangmeng.online.exam.ui.api.ApiUtils;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.control.RadioButton;
|
|
||||||
import javafx.scene.control.ToggleGroup;
|
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
@ -45,11 +43,21 @@ public class SingleChoiceComponent extends VBox {
|
||||||
|
|
||||||
private final SimpleStringProperty result_answer = new SimpleStringProperty(); // 最终答案
|
private final SimpleStringProperty result_answer = new SimpleStringProperty(); // 最终答案
|
||||||
|
|
||||||
|
private HBox hBox = new HBox();
|
||||||
|
|
||||||
|
private Label questionLabel;
|
||||||
|
|
||||||
public SingleChoiceComponent(String question, List<Map<String, Object>> options) {
|
public SingleChoiceComponent(String question, List<Map<String, Object>> options) {
|
||||||
|
|
||||||
|
this.setStyle("-fx-background-color: white; -fx-border-color: #eadcdc;" +
|
||||||
|
"-fx-border-radius: 10px; -fx-border-width: 1px; -fx-border-style: solid;-fx-background-radius: 10px;");
|
||||||
|
|
||||||
// 创建题目组件
|
// 创建题目组件
|
||||||
Label questionLabel = new Label(question);
|
questionLabel = new Label(question);
|
||||||
this.getChildren().add(questionLabel);
|
|
||||||
|
hBox.getChildren().add(questionLabel);
|
||||||
|
|
||||||
|
this.getChildren().add(hBox);
|
||||||
ToggleGroup answerGroup = new ToggleGroup(); // 单选按钮组:ml-citation{ref="1,2" data="citationList"}
|
ToggleGroup answerGroup = new ToggleGroup(); // 单选按钮组:ml-citation{ref="1,2" data="citationList"}
|
||||||
// 创建选项按钮
|
// 创建选项按钮
|
||||||
for (Map<String, Object> option : options) {
|
for (Map<String, Object> option : options) {
|
||||||
|
|
@ -105,15 +113,15 @@ public class SingleChoiceComponent extends VBox {
|
||||||
answerGroup.selectedToggleProperty().addListener((ov, old_value, new_value) -> {
|
answerGroup.selectedToggleProperty().addListener((ov, old_value, new_value) -> {
|
||||||
RadioButton selected = (RadioButton) new_value;
|
RadioButton selected = (RadioButton) new_value;
|
||||||
if (selected != null) {
|
if (selected != null) {
|
||||||
String answer = selected.getText().substring(0, 1);
|
|
||||||
result_answer.set(answer);
|
Map<String, Object> option = (Map<String, Object>) selected.getUserData();
|
||||||
number_of_responses++;
|
number_of_responses++;
|
||||||
// resultLabel.setText("你的选择:"+ result_answer.get());
|
context.put("option_id", option.get("id"));
|
||||||
// resultLabel.setStyle("-fx-text-fill: #2ecc71;");
|
result_answer.set(option.get("id").toString());
|
||||||
String id = (String) selected.getUserData();
|
|
||||||
context.put("option_id", id);
|
|
||||||
//提交数据
|
//提交数据
|
||||||
ApiUtils.submitAnswer(context, result_answer.get());
|
ApiUtils.submitAnswer(context);
|
||||||
|
resultLabel.setText("你的选择:"+ option.get("option"));
|
||||||
|
resultLabel.setStyle("-fx-text-fill: #2ecc71;");
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
resultLabel.setText("⚠ 请先选择答案");
|
resultLabel.setText("⚠ 请先选择答案");
|
||||||
|
|
@ -123,7 +131,8 @@ public class SingleChoiceComponent extends VBox {
|
||||||
// 布局容器
|
// 布局容器
|
||||||
this.setSpacing(10);
|
this.setSpacing(10);
|
||||||
this.setPadding(new Insets(15));
|
this.setPadding(new Insets(15));
|
||||||
this.getChildren().addAll(resultLabel);
|
this.hBox.getChildren().add(1,resultLabel);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 封装单选按钮创建方法:ml-citation{ref="2,3" data="citationList"}
|
// 封装单选按钮创建方法:ml-citation{ref="2,3" data="citationList"}
|
||||||
|
|
@ -131,7 +140,7 @@ public class SingleChoiceComponent extends VBox {
|
||||||
RadioButton rb = new RadioButton(option.get("text").toString());
|
RadioButton rb = new RadioButton(option.get("text").toString());
|
||||||
rb.setToggleGroup(group);
|
rb.setToggleGroup(group);
|
||||||
rb.setPadding(new Insets(5));
|
rb.setPadding(new Insets(5));
|
||||||
rb.setUserData(option.get("id"));
|
rb.setUserData(option);
|
||||||
return rb;
|
return rb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,69 +1,134 @@
|
||||||
package com.zhangmeng.online.exam.ui.components;
|
package com.zhangmeng.online.exam.ui.components;
|
||||||
|
|
||||||
|
import com.zhangmeng.online.exam.ui.api.ApiUtils;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.RadioButton;
|
import javafx.scene.control.RadioButton;
|
||||||
import javafx.scene.control.ToggleGroup;
|
import javafx.scene.control.ToggleGroup;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断题组件
|
* 判断题组件
|
||||||
|
*
|
||||||
* @author zm
|
* @author zm
|
||||||
* @date 2025/3/4 15:06
|
* @date 2025/3/4 15:06
|
||||||
* @version: 1.0
|
* @version: 1.0
|
||||||
*/
|
*/
|
||||||
public class TrueFalseComponent extends VBox {
|
public class TrueFalseComponent extends VBox {
|
||||||
|
|
||||||
// 预设正确答案
|
private static final String TRUE_TEXT = "✔ 正确";
|
||||||
private final boolean correctAnswer = true;
|
private static final String FALSE_TEXT = "✘ 错误";
|
||||||
|
|
||||||
|
private Map<String, Object> context = new HashMap<>();
|
||||||
|
|
||||||
|
public Map<String, Object> getContext() {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContext(Map<String, Object> context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String correctAnswer = null; // 预设正确答案
|
||||||
|
|
||||||
|
private int number_of_responses = 0;//作答次数
|
||||||
|
|
||||||
|
private final SimpleStringProperty result_answer = new SimpleStringProperty(); // 最终答案
|
||||||
|
|
||||||
|
private HBox hBox = new HBox();
|
||||||
|
|
||||||
|
private Label questionLabel;
|
||||||
|
|
||||||
|
|
||||||
|
public TrueFalseComponent(String question, List<Map<String, Object>> options) {
|
||||||
|
|
||||||
|
this.setStyle("-fx-background-color: white; -fx-border-color: #eadcdc;" + "-fx-border-radius: 10px; -fx-border-width: 1px; -fx-border-style: solid;-fx-background-radius: 10px;");
|
||||||
|
|
||||||
public TrueFalseComponent() {
|
|
||||||
|
|
||||||
// 题目组件
|
// 题目组件
|
||||||
Label questionLabel = new Label("1. JavaFX 是 Oracle 官方提供的 GUI 框架吗?");
|
questionLabel = new Label(question);
|
||||||
questionLabel.setStyle("-fx-font-size: 16px; -fx-font-weight: bold;");
|
hBox.getChildren().add(questionLabel);
|
||||||
|
this.getChildren().add(hBox);
|
||||||
|
|
||||||
// 单选按钮组
|
// 单选按钮组
|
||||||
ToggleGroup answerGroup = new ToggleGroup();
|
ToggleGroup answerGroup = new ToggleGroup();
|
||||||
RadioButton trueButton = createRadioButton("✔ 正确", answerGroup);
|
|
||||||
RadioButton falseButton = createRadioButton("✘ 错误", answerGroup);
|
// 创建选项按钮
|
||||||
|
for (Map<String, Object> option : options) {
|
||||||
|
boolean isAnswer = (boolean) option.get("isAnswer");
|
||||||
|
if (isAnswer) {
|
||||||
|
RadioButton trueButton = createRadioButton(TRUE_TEXT, answerGroup,option);
|
||||||
|
this.getChildren().add(trueButton);
|
||||||
|
} else {
|
||||||
|
RadioButton falseButton = createRadioButton(FALSE_TEXT, answerGroup,option);
|
||||||
|
this.getChildren().add(falseButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 功能组件
|
// 功能组件
|
||||||
Button submitBtn = new Button("提交答案");
|
// Button submitBtn = new Button("提交答案");
|
||||||
Label resultLabel = new Label();
|
Label resultLabel = new Label();
|
||||||
|
|
||||||
// 提交事件处理
|
// 提交事件处理
|
||||||
submitBtn.setOnAction(e -> {
|
// submitBtn.setOnAction(e -> {
|
||||||
if (answerGroup.getSelectedToggle() == null) {
|
// if (answerGroup.getSelectedToggle() == null) {
|
||||||
resultLabel.setText("⚠ 请先选择答案");
|
// resultLabel.setText("⚠ 请先选择答案");
|
||||||
resultLabel.setStyle("-fx-text-fill: #e67e22;");
|
// resultLabel.setStyle("-fx-text-fill: #e67e22;");
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
|
// boolean userAnswer = ((RadioButton) answerGroup.getSelectedToggle()).getText().contains("正确");
|
||||||
|
// if (userAnswer == correctAnswer) {
|
||||||
|
// resultLabel.setText("✅ 回答正确!");
|
||||||
|
// resultLabel.setStyle("-fx-text-fill: #2ecc71;");
|
||||||
|
// } else {
|
||||||
|
// resultLabel.setText("❌ 正确答案:" + (correctAnswer ? "正确" : "错误"));
|
||||||
|
// resultLabel.setStyle("-fx-text-fill: #e74c3c;");
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
boolean userAnswer = ((RadioButton) answerGroup.getSelectedToggle()).getText().contains("正确");
|
answerGroup.selectedToggleProperty().addListener((ov, old_value, new_value) -> {
|
||||||
if (userAnswer == correctAnswer) {
|
RadioButton selected = (RadioButton) new_value;
|
||||||
resultLabel.setText("✅ 回答正确!");
|
if (selected != null) {
|
||||||
|
|
||||||
|
Map<String, Object> option = (Map<String, Object>) selected.getUserData();
|
||||||
|
number_of_responses++;
|
||||||
|
context.put("option_id", option.get("id"));
|
||||||
|
result_answer.set(option.get("id").toString());
|
||||||
|
//提交数据
|
||||||
|
ApiUtils.submitAnswer(context);
|
||||||
|
boolean isAnswer = (boolean) option.get("isAnswer");
|
||||||
|
if (isAnswer){
|
||||||
|
resultLabel.setText("你的选择:"+ TRUE_TEXT);
|
||||||
|
}else {
|
||||||
|
resultLabel.setText("你的选择:"+ FALSE_TEXT);
|
||||||
|
}
|
||||||
resultLabel.setStyle("-fx-text-fill: #2ecc71;");
|
resultLabel.setStyle("-fx-text-fill: #2ecc71;");
|
||||||
} else {
|
} else {
|
||||||
resultLabel.setText("❌ 正确答案:" + (correctAnswer ? "正确" : "错误"));
|
resultLabel.setText("⚠ 请先选择答案");
|
||||||
resultLabel.setStyle("-fx-text-fill: #e74c3c;");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// 布局容器
|
// 布局容器
|
||||||
this.setSpacing(15);
|
this.setSpacing(10);
|
||||||
this.setPadding(new Insets(20));
|
this.setPadding(new Insets(15));
|
||||||
this.getChildren().addAll(questionLabel, trueButton, falseButton, submitBtn, resultLabel);
|
this.hBox.getChildren().add(1, resultLabel);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建带样式的单选按钮:ml-citation{ref="1,3" data="citationList"}
|
// 创建带样式的单选按钮:ml-citation{ref="1,3" data="citationList"}
|
||||||
private RadioButton createRadioButton(String text, ToggleGroup group) {
|
private RadioButton createRadioButton(String text, ToggleGroup group,Map<String, Object> option) {
|
||||||
RadioButton rb = new RadioButton(text);
|
RadioButton rb = new RadioButton(text);
|
||||||
rb.setToggleGroup(group);
|
rb.setToggleGroup(group);
|
||||||
rb.setStyle("-fx-font-size: 14px; -fx-padding: 5px;");
|
rb.setStyle("-fx-font-size: 14px; -fx-padding: 5px;");
|
||||||
|
rb.setUserData(option);
|
||||||
return rb;
|
return rb;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue