diff --git a/README.md b/README.md index 98d86a0..0e20bb8 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ mvn exec:java -Dexec.mainClass="com.example.mathsystemtogether.HelloApplication" ## 👤 用户账号 +### 预设测试账号 + 系统预设了9个测试账号: | 用户名 | 密码 | 难度级别 | 题目类型 | @@ -45,12 +47,54 @@ mvn exec:java -Dexec.mainClass="com.example.mathsystemtogether.HelloApplication" | 王五2 | 123 | 高中 | 三角函数运算 | | 王五3 | 123 | 高中 | 三角函数运算 | +### 新用户注册 + +系统支持新用户注册功能: + +1. **点击注册按钮**:在登录界面点击"📝 注册"按钮 +2. **填写注册信息**: + - 用户名(至少3个字符) + - 密码(至少6个字符) + - 确认密码 + - 邮箱地址 + - 难度级别选择 +3. **邮箱验证**: + - 点击"📤 发送验证码"获取验证码 + - 输入收到的6位数字验证码 + - 验证码有效期为5分钟 +4. **完成注册**:点击"🚀 注册"完成账户创建 +5. **自动跳转**:注册成功后自动返回登录界面 + +**注意**: +- 用户名不能重复 +- 邮箱地址格式必须正确 +- 验证码通过真实邮件发送 +- 用户数据保存在本地 `user_data.txt` 文件中 + +### 邮件服务配置 + +系统支持真实的邮箱验证码功能,已配置QQ邮箱: + +- **邮箱地址**:1961004835@qq.com +- **SMTP服务器**:smtp.qq.com:587 +- **配置文件**:`src/main/resources/mail.properties` + +如需修改邮箱配置,请编辑 `mail.properties` 文件。 + ## 🎮 使用流程 -### 1. 用户登录 +### 1. 用户登录或注册 + +**登录现有账户:** - 输入用户名(如:张三1) - 输入密码(123) -- 点击"登录"按钮 +- 点击"🚀 登录"按钮 + +**注册新账户:** +- 点击"📝 注册"按钮 +- 填写注册信息(用户名、密码、邮箱、难度级别) +- 获取并输入邮箱验证码 +- 点击"🚀 注册"完成账户创建 ### 2. 设置考试 - 选择难度级别(小学/初中/高中) @@ -125,10 +169,16 @@ MathSystemTogether/ ├── src/main/java/com/example/mathsystemtogether/ │ ├── HelloApplication.java # 主应用程序入口 │ ├── ExamController.java # 考试系统控制器 +│ ├── RegisterController.java # 注册功能控制器 +│ ├── EmailService.java # 邮件服务类 │ ├── Question.java # 选择题数据模型 │ └── ChoiceQuestionGenerator.java # 选择题生成器 ├── src/main/resources/com/example/mathsystemtogether/ -│ └── exam-view.fxml # 考试界面布局文件 +│ ├── exam-view.fxml # 考试界面布局文件 +│ └── register-view.fxml # 注册界面布局文件 +├── src/main/resources/ +│ └── mail.properties # 邮件服务配置文件 +├── user_data.txt # 用户数据存储文件 ├── papers/ # 题目文件存储目录 │ ├── 张三1/ # 用户文件夹 │ ├── 李四1/ @@ -140,6 +190,12 @@ MathSystemTogether/ ## 🔧 核心功能 +### 用户管理功能 +- **用户注册**:支持新用户注册,包含邮箱验证 +- **用户登录**:支持预设账号和注册账号登录 +- **数据存储**:用户信息保存在本地文件中 +- **数据验证**:用户名唯一性、邮箱格式验证 + ### 题目生成算法 - **小学**:基础四则运算(+、-、×、÷) - **初中**:平方运算(²)、开方运算(√) @@ -156,6 +212,14 @@ MathSystemTogether/ - 进度实时更新 - 结果自动统计 +### 邮箱验证功能 +- **真实邮件发送**:通过SMTP服务器发送验证码邮件 +- **验证码生成**:6位数字随机验证码 +- **时效控制**:验证码5分钟有效期 +- **防重复发送**:60秒倒计时限制 +- **格式验证**:邮箱地址格式检查 +- **HTML邮件**:美观的HTML格式验证码邮件 + ## 📊 考试结果 系统会显示: diff --git a/pom.xml b/pom.xml index 96bff82..7ba6d4d 100644 --- a/pom.xml +++ b/pom.xml @@ -38,6 +38,16 @@ ${junit.version} test + + com.sun.mail + jakarta.mail + 2.0.1 + + + jakarta.activation + jakarta.activation-api + 2.1.3 + @@ -51,6 +61,14 @@ 21 + + org.codehaus.mojo + exec-maven-plugin + 3.1.0 + + com.example.mathsystemtogether.HelloApplication + + org.openjfx javafx-maven-plugin diff --git a/run_gui.bat b/run_gui.bat index c9ec487..c25cf96 100644 --- a/run_gui.bat +++ b/run_gui.bat @@ -7,11 +7,22 @@ echo 系统功能: echo - 支持小学、初中、高中三个难度级别 echo - 选择题形式答题 echo - 自动评分和结果统计 +echo - 用户注册功能(邮箱验证码) +echo - 本地用户数据存储 echo. echo 测试账号: echo - 小学:张三1/123, 张三2/123, 张三3/123 echo - 初中:李四1/123, 李四2/123, 李四3/123 echo - 高中:王五1/123, 王五2/123, 王五3/123 echo. +echo 新用户注册: +echo - 点击"注册"按钮创建新账户 +echo - 使用邮箱获取验证码 +echo - 选择适合的难度级别 +echo. +echo 邮件服务配置: +echo - 已配置QQ邮箱:1961004835@qq.com +echo - 配置文件:src/main/resources/mail.properties +echo. mvn exec:java -Dexec.mainClass="com.example.mathsystemtogether.HelloApplication" pause diff --git a/src/main/java/com/example/mathsystemtogether/ExamController.java b/src/main/java/com/example/mathsystemtogether/ExamController.java index 3bd9d97..cb60676 100644 --- a/src/main/java/com/example/mathsystemtogether/ExamController.java +++ b/src/main/java/com/example/mathsystemtogether/ExamController.java @@ -1,24 +1,30 @@ package com.example.mathsystemtogether; import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.layout.VBox; +import javafx.stage.Stage; import javafx.collections.FXCollections; import javafx.collections.ObservableList; - import java.util.*; +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; /** * 考试系统控制器 */ public class ExamController { - + // 登录界面控件 @FXML private TextField usernameField; @FXML private PasswordField passwordField; @FXML private Button loginButton; + @FXML private Button registerButton; @FXML private Label loginStatusLabel; - + // 考试设置界面控件 @FXML private VBox examSetupPanel; @FXML private Label welcomeLabel; @@ -27,54 +33,27 @@ public class ExamController { @FXML private Button startExamButton; @FXML private Button logoutButton; @FXML private Label statusLabel; - - // 考试界面控件 - @FXML private VBox examPanel; - @FXML private Label questionNumberLabel; - @FXML private Label questionTextLabel; - @FXML private RadioButton optionA; - @FXML private RadioButton optionB; - @FXML private RadioButton optionC; - @FXML private RadioButton optionD; - @FXML private ToggleGroup answerGroup; - @FXML private Button submitButton; - @FXML private Button nextButton; - @FXML private Label progressLabel; - @FXML private Button exitExamButton; - - // 结果界面控件 - @FXML private VBox resultPanel; - @FXML private Label resultTitleLabel; - @FXML private Label scoreLabel; - @FXML private Label correctCountLabel; - @FXML private Label totalCountLabel; - @FXML private TextArea resultDetailsArea; - @FXML private Button restartButton; - @FXML private Button backToLoginButton; - + // 数据成员 private Account currentAccount; private List examQuestions; - private int currentQuestionIndex = 0; - private Map userAnswers = new HashMap<>(); private ChoiceQuestionGenerator questionGenerator; private final Map userMap = new HashMap<>(); - + private static final String USER_DATA_FILE = "user_data.txt"; + // 常量定义 private static final int MIN_QUESTIONS = 5; private static final int MAX_QUESTIONS = 20; - + @FXML public void initialize() { initAccounts(); + loadUserDataFromFile(); setupLevelComboBox(); - setupAnswerGroup(); examSetupPanel.setVisible(false); - examPanel.setVisible(false); - resultPanel.setVisible(false); questionCountField.setText("10"); } - + private void initAccounts() { // 小学三个账号 userMap.put("张三1", new Account("张三1", "123", Level.小学)); @@ -89,32 +68,53 @@ public class ExamController { userMap.put("王五2", new Account("王五2", "123", Level.高中)); userMap.put("王五3", new Account("王五3", "123", Level.高中)); } - + private void setupLevelComboBox() { ObservableList levels = FXCollections.observableArrayList("小学", "初中", "高中"); levelComboBox.setItems(levels); levelComboBox.setValue("小学"); } - - private void setupAnswerGroup() { - answerGroup = new ToggleGroup(); - optionA.setToggleGroup(answerGroup); - optionB.setToggleGroup(answerGroup); - optionC.setToggleGroup(answerGroup); - optionD.setToggleGroup(answerGroup); + + private void loadUserDataFromFile() { + try { + if (!Files.exists(Paths.get(USER_DATA_FILE))) { + return; + } + + List lines = Files.readAllLines(Paths.get(USER_DATA_FILE)); + for (String line : lines) { + String[] parts = line.split("\\|"); + if (parts.length >= 4) { + String username = parts[0]; + String password = parts[1]; + String levelStr = parts[3]; + + Level level; + try { + level = Level.valueOf(levelStr); + } catch (IllegalArgumentException e) { + level = Level.小学; // 默认级别 + } + + userMap.put(username, new Account(username, password, level)); + } + } + } catch (IOException e) { + // 加载用户数据失败,静默处理 + } } - + @FXML private void handleLogin() { String username = usernameField.getText().trim(); String password = passwordField.getText(); - + if (username.isEmpty() || password.isEmpty()) { loginStatusLabel.setText("请输入用户名和密码"); loginStatusLabel.setStyle("-fx-text-fill: red;"); return; } - + Account account = userMap.get(username); if (account != null && account.password.equals(password)) { currentAccount = account; @@ -128,22 +128,36 @@ public class ExamController { loginStatusLabel.setStyle("-fx-text-fill: red;"); } } - + + @FXML + private void handleRegister() { + try { + // 打开注册界面 + FXMLLoader loader = new FXMLLoader(getClass().getResource("register-view.fxml")); + Scene scene = new Scene(loader.load(), 800, 800); + Stage registerStage = new Stage(); + registerStage.setTitle("数学考试系统 - 用户注册"); + registerStage.setScene(scene); + registerStage.setResizable(false); + registerStage.show(); + + } catch (Exception e) { + loginStatusLabel.setText("打开注册界面失败:" + e.getMessage()); + loginStatusLabel.setStyle("-fx-text-fill: red;"); + } + } + @FXML private void handleLogout() { currentAccount = null; examQuestions = null; - currentQuestionIndex = 0; - userAnswers.clear(); examSetupPanel.setVisible(false); - examPanel.setVisible(false); - resultPanel.setVisible(false); usernameField.clear(); passwordField.clear(); loginStatusLabel.setText(""); statusLabel.setText(""); } - + @FXML private void handleStartExam() { if (currentAccount == null) { @@ -151,7 +165,7 @@ public class ExamController { statusLabel.setStyle("-fx-text-fill: red;"); return; } - + try { int count = Integer.parseInt(questionCountField.getText()); if (count < MIN_QUESTIONS || count > MAX_QUESTIONS) { @@ -159,177 +173,71 @@ public class ExamController { statusLabel.setStyle("-fx-text-fill: red;"); return; } - + // 获取选择的级别 String selectedLevel = levelComboBox.getValue(); ChoiceQuestionGenerator.Level level = ChoiceQuestionGenerator.Level.valueOf(selectedLevel); - + // 生成考试题目 questionGenerator = new ChoiceQuestionGenerator(); examQuestions = questionGenerator.generateQuestions(level, count); - + if (examQuestions.isEmpty()) { statusLabel.setText("无法生成题目,请重试"); statusLabel.setStyle("-fx-text-fill: red;"); return; } - + // 开始考试 startExam(); - + } catch (NumberFormatException e) { statusLabel.setText("请输入有效的数字"); statusLabel.setStyle("-fx-text-fill: red;"); } } - + private void startExam() { - currentQuestionIndex = 0; - userAnswers.clear(); - examSetupPanel.setVisible(false); - examPanel.setVisible(true); - resultPanel.setVisible(false); - displayCurrentQuestion(); - } - - private void displayCurrentQuestion() { - if (currentQuestionIndex >= examQuestions.size()) { - showResults(); - return; - } - - Question question = examQuestions.get(currentQuestionIndex); - questionNumberLabel.setText("第 " + (currentQuestionIndex + 1) + " 题 / 共 " + examQuestions.size() + " 题"); - questionTextLabel.setText(question.getQuestionText()); - - optionA.setText("A. " + question.getOptionA()); - optionB.setText("B. " + question.getOptionB()); - optionC.setText("C. " + question.getOptionC()); - optionD.setText("D. " + question.getOptionD()); - - // 清除之前的选择 - answerGroup.selectToggle(null); - - // 更新按钮状态 - if (currentQuestionIndex == examQuestions.size() - 1) { - submitButton.setText("提交并完成考试"); - nextButton.setVisible(false); - } else { - submitButton.setText("提交答案"); - nextButton.setVisible(true); - } - - progressLabel.setText("进度: " + (currentQuestionIndex + 1) + "/" + examQuestions.size()); - } - - @FXML - private void handleSubmitAnswer() { - RadioButton selectedOption = (RadioButton) answerGroup.getSelectedToggle(); - if (selectedOption == null) { - statusLabel.setText("请选择一个答案"); + try { + // 打开专门的考试界面 + FXMLLoader loader = new FXMLLoader(getClass().getResource("exam-taking-view.fxml")); + Scene scene = new Scene(loader.load(), 1000, 950); + Stage examStage = new Stage(); + examStage.setTitle("数学考试 - " + currentAccount.username); + examStage.setScene(scene); + examStage.setResizable(false); + + // 设置考试数据 + ExamTakingController examController = loader.getController(); + examController.setExamData(examQuestions, currentAccount.username, levelComboBox.getValue()); + + // 显示考试窗口 + examStage.show(); + + // 隐藏主窗口 + Stage mainStage = (Stage) startExamButton.getScene().getWindow(); + mainStage.hide(); + + } catch (Exception e) { + statusLabel.setText("启动考试失败:" + e.getMessage()); statusLabel.setStyle("-fx-text-fill: red;"); - return; } - - String answer = selectedOption.getText().substring(0, 1); // 获取A、B、C、D - userAnswers.put(currentQuestionIndex, answer); - - if (currentQuestionIndex == examQuestions.size() - 1) { - // 最后一题,显示结果 - showResults(); - } else { - // 下一题 - currentQuestionIndex++; - displayCurrentQuestion(); - } - } - - @FXML - private void handleNextQuestion() { - currentQuestionIndex++; - displayCurrentQuestion(); } - - @FXML - private void handleExitExam() { - if (showExitConfirmation()) { - examPanel.setVisible(false); - examSetupPanel.setVisible(true); - currentQuestionIndex = 0; - userAnswers.clear(); - } - } - - private boolean showExitConfirmation() { - Alert alert = new Alert(Alert.AlertType.CONFIRMATION); - alert.setTitle("确认退出"); - alert.setHeaderText("确定要退出考试吗?"); - alert.setContentText("退出后当前进度将丢失。"); - return alert.showAndWait().orElse(ButtonType.CANCEL) == ButtonType.OK; - } - - private void showResults() { - examPanel.setVisible(false); - resultPanel.setVisible(true); - - int correctCount = 0; - StringBuilder resultDetails = new StringBuilder(); - - for (int i = 0; i < examQuestions.size(); i++) { - Question question = examQuestions.get(i); - String userAnswer = userAnswers.get(i); - boolean isCorrect = question.isCorrect(userAnswer); - - if (isCorrect) { - correctCount++; - } - - resultDetails.append("第").append(i + 1).append("题: "); - resultDetails.append(question.getQuestionText()).append("\n"); - resultDetails.append("你的答案: ").append(userAnswer != null ? userAnswer : "未作答").append(" "); - resultDetails.append("正确答案: ").append(question.getCorrectAnswer()).append(" "); - resultDetails.append(isCorrect ? "✓" : "✗").append("\n\n"); - } - - int score = (int) Math.round((double) correctCount / examQuestions.size() * 100); - - resultTitleLabel.setText("考试完成!"); - scoreLabel.setText("得分: " + score + "分"); - correctCountLabel.setText("正确: " + correctCount + "题"); - totalCountLabel.setText("总计: " + examQuestions.size() + "题"); - resultDetailsArea.setText(resultDetails.toString()); - } - - @FXML - private void handleRestart() { - resultPanel.setVisible(false); - examSetupPanel.setVisible(true); - currentQuestionIndex = 0; - userAnswers.clear(); - } - - @FXML - private void handleBackToLogin() { - resultPanel.setVisible(false); - examSetupPanel.setVisible(false); - examPanel.setVisible(false); - handleLogout(); - } - + // 内部类 static class Account { final String username; - final String password; + String password; final Level level; - + Account(String username, String password, Level level) { this.username = username; this.password = password; this.level = level; } } - + enum Level { 小学, 初中, 高中 } -} +} \ No newline at end of file diff --git a/src/main/java/com/example/mathsystemtogether/ExamTakingController.java b/src/main/java/com/example/mathsystemtogether/ExamTakingController.java new file mode 100644 index 0000000..be77e36 --- /dev/null +++ b/src/main/java/com/example/mathsystemtogether/ExamTakingController.java @@ -0,0 +1,267 @@ +package com.example.mathsystemtogether; + +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.animation.Timeline; +import javafx.animation.KeyFrame; +import javafx.util.Duration; + +import java.util.*; + +/** + * 考试进行界面控制器 + */ +public class ExamTakingController { + + // 界面控件 + @FXML private Label examInfoLabel; + @FXML private Label questionNumberLabel; + @FXML private Label timeLabel; + @FXML private Label questionTextLabel; + @FXML private RadioButton optionA; + @FXML private RadioButton optionB; + @FXML private RadioButton optionC; + @FXML private RadioButton optionD; + @FXML private ToggleGroup answerGroup; + @FXML private Button submitButton; + @FXML private Button nextButton; + @FXML private Button exitExamButton; + @FXML private ProgressBar progressBar; + @FXML private Label progressLabel; + + // 数据成员 + private List examQuestions; + private int currentQuestionIndex = 0; + private Map userAnswers = new HashMap<>(); + private ChoiceQuestionGenerator questionGenerator; + private String username; + private String level; + private int totalQuestions; + private Timeline timer; + private int remainingSeconds; + + @FXML + public void initialize() { + setupAnswerGroup(); + setupTimer(); + } + + private void setupAnswerGroup() { + answerGroup = new ToggleGroup(); + optionA.setToggleGroup(answerGroup); + optionB.setToggleGroup(answerGroup); + optionC.setToggleGroup(answerGroup); + optionD.setToggleGroup(answerGroup); + } + + private void setupTimer() { + // 设置30分钟倒计时 + remainingSeconds = 30 * 60; // 30分钟 = 1800秒 + timer = new Timeline(new KeyFrame(Duration.seconds(1), e -> updateTimer())); + timer.setCycleCount(Timeline.INDEFINITE); + timer.play(); + } + + private void updateTimer() { + remainingSeconds--; + if (remainingSeconds <= 0) { + timer.stop(); + handleTimeUp(); + return; + } + + int minutes = remainingSeconds / 60; + int seconds = remainingSeconds % 60; + timeLabel.setText(String.format("⏰ 剩余时间: %02d:%02d", minutes, seconds)); + } + + private void handleTimeUp() { + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle("时间到"); + alert.setHeaderText("考试时间已到!"); + alert.setContentText("系统将自动提交您的答案。"); + alert.showAndWait(); + showResults(); + } + + public void setExamData(List questions, String username, String level) { + this.examQuestions = questions; + this.username = username; + this.level = level; + this.totalQuestions = questions.size(); + + examInfoLabel.setText(String.format("👤 %s | 📚 %s | 📝 %d题", username, level, totalQuestions)); + displayCurrentQuestion(); + updateProgress(); + } + + private void displayCurrentQuestion() { + if (currentQuestionIndex >= examQuestions.size()) { + showResults(); + return; + } + + Question question = examQuestions.get(currentQuestionIndex); + questionNumberLabel.setText(String.format("第 %d 题 / 共 %d 题", + currentQuestionIndex + 1, examQuestions.size())); + questionTextLabel.setText(question.getQuestionText()); + + optionA.setText("A. " + question.getOptionA()); + optionB.setText("B. " + question.getOptionB()); + optionC.setText("C. " + question.getOptionC()); + optionD.setText("D. " + question.getOptionD()); + + // 清除之前的选择 + answerGroup.selectToggle(null); + + // 更新按钮状态 + if (currentQuestionIndex == examQuestions.size() - 1) { + submitButton.setText("✅ 提交并完成考试"); + nextButton.setVisible(false); + } else { + submitButton.setText("✅ 提交答案"); + nextButton.setVisible(true); + } + + updateProgress(); + } + + private void updateProgress() { + double progress = (double) currentQuestionIndex / examQuestions.size(); + progressBar.setProgress(progress); + progressLabel.setText(String.format("进度: %d/%d (%.1f%%)", + currentQuestionIndex + 1, examQuestions.size(), progress * 100)); + } + + @FXML + private void handleSubmitAnswer() { + RadioButton selectedOption = (RadioButton) answerGroup.getSelectedToggle(); + if (selectedOption == null) { + showAlert("请选择一个答案", "请先选择答案再提交"); + return; + } + + String answer = getAnswerValue(selectedOption.getText().substring(0, 1)); // 获取答案 + userAnswers.put(currentQuestionIndex, answer); + + if (currentQuestionIndex == examQuestions.size() - 1) { + // 最后一题,显示结果 + timer.stop(); + showResults(); + } else { + // 下一题 + currentQuestionIndex++; + displayCurrentQuestion(); + } + } + // 添加辅助方法获取选项对应的实际值 + private String getAnswerValue(String optionLetter) { + Question currentQuestion = examQuestions.get(currentQuestionIndex); + switch (optionLetter) { + case "A": return currentQuestion.getOptionA(); + case "B": return currentQuestion.getOptionB(); + case "C": return currentQuestion.getOptionC(); + case "D": return currentQuestion.getOptionD(); + default: return ""; + } + } + @FXML + private void handleNextQuestion() { + currentQuestionIndex++; + displayCurrentQuestion(); + } + + @FXML + private void handleExitExam() { + if (showExitConfirmation()) { + timer.stop(); + returnToMainMenu(); + } + } + + private boolean showExitConfirmation() { + Alert alert = new Alert(Alert.AlertType.CONFIRMATION); + alert.setTitle("确认退出"); + alert.setHeaderText("确定要退出考试吗?"); + alert.setContentText("退出后当前进度将丢失。"); + return alert.showAndWait().orElse(ButtonType.CANCEL) == ButtonType.OK; + } + + private void showAlert(String title, String message) { + Alert alert = new Alert(Alert.AlertType.INFORMATION); + alert.setTitle(title); + alert.setHeaderText(null); + alert.setContentText(message); + alert.showAndWait(); + } + + private void showResults() { + try { + // 停止计时器 + timer.stop(); + + // 计算成绩 + int correctCount = 0; + StringBuilder resultDetails = new StringBuilder(); + + for (int i = 0; i < examQuestions.size(); i++) { + Question question = examQuestions.get(i); + String userAnswer = userAnswers.get(i); + boolean isCorrect = question.isCorrect(userAnswer); + + if (isCorrect) { + correctCount++; + } + + resultDetails.append("第").append(i + 1).append("题: "); + resultDetails.append(question.getQuestionText()).append("\n"); + resultDetails.append("你的答案: ").append(userAnswer != null ? userAnswer : "未作答").append(" "); + resultDetails.append("正确答案: ").append(question.getCorrectAnswer()).append(" "); + resultDetails.append(isCorrect ? "✓" : "✗").append("\n\n"); + } + + int score = (int) Math.round((double) correctCount / examQuestions.size() * 100); + + // 显示结果对话框 + Alert resultAlert = new Alert(Alert.AlertType.INFORMATION); + resultAlert.setTitle("考试完成"); + resultAlert.setHeaderText("🎉 考试结果"); + resultAlert.setContentText(String.format( + "得分: %d分\n正确: %d题\n总计: %d题\n正确率: %.1f%%", + score, correctCount, examQuestions.size(), (double) correctCount / examQuestions.size() * 100 + )); + resultAlert.showAndWait(); + + // 返回主菜单 + returnToMainMenu(); + + } catch (Exception e) { + showAlert("错误", "显示结果时出错:" + e.getMessage()); + } + } + + private void returnToMainMenu() { + try { + // 关闭当前窗口 + Stage currentStage = (Stage) submitButton.getScene().getWindow(); + currentStage.close(); + + // 打开主菜单 + FXMLLoader loader = new FXMLLoader(getClass().getResource("exam-view.fxml")); + Scene scene = new Scene(loader.load(), 1000, 900); + Stage stage = new Stage(); + stage.setTitle("数学考试系统"); + stage.setScene(scene); + stage.show(); + + } catch (Exception e) { + showAlert("错误", "返回主菜单时出错:" + e.getMessage()); + } + } +} diff --git a/src/main/java/com/example/mathsystemtogether/HelloApplication.java b/src/main/java/com/example/mathsystemtogether/HelloApplication.java index 9cf2187..91183f8 100644 --- a/src/main/java/com/example/mathsystemtogether/HelloApplication.java +++ b/src/main/java/com/example/mathsystemtogether/HelloApplication.java @@ -11,7 +11,7 @@ public class HelloApplication extends Application { @Override public void start(Stage stage) throws IOException { FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("exam-view.fxml")); - Scene scene = new Scene(fxmlLoader.load(), 900, 800); + Scene scene = new Scene(fxmlLoader.load(), 900, 900); stage.setTitle("数学考试系统"); stage.setResizable(true); stage.setScene(scene); diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 30fc570..9c6a55e 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -1,6 +1,8 @@ module com.example.mathsystemtogether { requires javafx.controls; requires javafx.fxml; + requires jakarta.mail; + requires jakarta.activation; opens com.example.mathsystemtogether to javafx.fxml; diff --git a/src/main/resources/com/example/mathsystemtogether/exam-taking-view.fxml b/src/main/resources/com/example/mathsystemtogether/exam-taking-view.fxml new file mode 100644 index 0000000..41c7385 --- /dev/null +++ b/src/main/resources/com/example/mathsystemtogether/exam-taking-view.fxml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/src/main/resources/com/example/mathsystemtogether/exam-view.fxml b/src/main/resources/com/example/mathsystemtogether/exam-view.fxml index abec30f..7f86954 100644 --- a/src/main/resources/com/example/mathsystemtogether/exam-view.fxml +++ b/src/main/resources/com/example/mathsystemtogether/exam-view.fxml @@ -4,162 +4,112 @@ + + + - + - + - + -
- + - + - + - - - - - - - -