@ -1,150 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.personalproject</groupId>
|
||||
<artifactId>math-learning</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>Math Learning Application</name>
|
||||
<description>A desktop application for math learning with exam functionality</description>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<javafx.version>21</javafx.version>
|
||||
<javafx.maven.plugin.version>0.0.8</javafx.maven.plugin.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-controls</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-fxml</artifactId>
|
||||
<version>${javafx.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Jakarta Mail for SMTP -->
|
||||
<dependency>
|
||||
<groupId>com.sun.mail</groupId>
|
||||
<artifactId>jakarta.mail</artifactId>
|
||||
<version>2.0.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Testing Dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-api</artifactId>
|
||||
<version>5.9.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter-engine</artifactId>
|
||||
<version>5.9.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Maven Compiler Plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.11.0</version>
|
||||
<configuration>
|
||||
<source>17</source>
|
||||
<target>17</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- JavaFX Maven Plugin -->
|
||||
<plugin>
|
||||
<groupId>org.openjfx</groupId>
|
||||
<artifactId>javafx-maven-plugin</artifactId>
|
||||
<version>${javafx.maven.plugin.version}</version>
|
||||
<configuration>
|
||||
<mainClass>com.personalproject.ui.MathExamGUI</mainClass>
|
||||
<!-- Specify required JavaFX modules -->
|
||||
<options>
|
||||
<option>--add-modules</option>
|
||||
<option>javafx.controls,javafx.fxml,javafx.graphics</option>
|
||||
</options>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<!-- Default execution for run goal -->
|
||||
<id>default-cli</id>
|
||||
<configuration>
|
||||
<mainClass>com.personalproject.ui.MathExamGUI</mainClass>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!-- Maven Surefire Plugin for testing -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
</plugin>
|
||||
|
||||
<!-- Maven JAR Plugin -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.3.0</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>com.personalproject.ui.MathExamGUI</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- Maven Shade Plugin to create a fat JAR -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>com.personalproject.MathExamApplication</mainClass>
|
||||
</transformer>
|
||||
</transformers>
|
||||
<!-- Include all JavaFX modules in the fat JAR -->
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@ -0,0 +1,62 @@
|
||||
package com.personalproject.auth;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Interface for sending emails with registration codes.
|
||||
*/
|
||||
public final class EmailService {
|
||||
|
||||
private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
private static final int CODE_LENGTH = 6;
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
private EmailService() {
|
||||
// Prevent instantiation of utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random registration code.
|
||||
*
|
||||
* @return A randomly generated registration code
|
||||
*/
|
||||
public static String generateRegistrationCode() {
|
||||
StringBuilder code = new StringBuilder();
|
||||
for (int i = 0; i < CODE_LENGTH; i++) {
|
||||
code.append(CHARACTERS.charAt(RANDOM.nextInt(CHARACTERS.length())));
|
||||
}
|
||||
return code.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a registration code to the specified email address. In a real implementation, this would
|
||||
* connect to an email server.
|
||||
*
|
||||
* @param email The email address to send the code to
|
||||
* @param registrationCode The registration code to send
|
||||
* @return true if successfully sent (in this mock implementation, always true)
|
||||
*/
|
||||
public static boolean sendRegistrationCode(String email, String registrationCode) {
|
||||
// In a real implementation, this would connect to an email server
|
||||
// For the mock implementation, we'll just print to console
|
||||
System.out.println("Sending registration code " + registrationCode + " to " + email);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if an email address has a valid format.
|
||||
*
|
||||
* @param email The email address to validate
|
||||
* @return true if the email has valid format, false otherwise
|
||||
*/
|
||||
public static boolean isValidEmail(String email) {
|
||||
if (email == null || email.trim().isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Simple email validation using regex
|
||||
String emailRegex = "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@"
|
||||
+ "(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$";
|
||||
return email.matches(emailRegex);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package com.personalproject.auth;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Utility class for password validation.
|
||||
*/
|
||||
public final class PasswordValidator {
|
||||
|
||||
private static final Pattern PASSWORD_PATTERN =
|
||||
Pattern.compile("^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]{6,10}$");
|
||||
|
||||
private PasswordValidator() {
|
||||
// Prevent instantiation of utility class
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if a password meets the requirements: - 6-10 characters - Contains at least one
|
||||
* uppercase letter - Contains at least one lowercase letter - Contains at least one digit.
|
||||
*
|
||||
* @param password The password to validate
|
||||
* @return true if password meets requirements, false otherwise
|
||||
*/
|
||||
public static boolean isValidPassword(String password) {
|
||||
if (password == null) {
|
||||
return false;
|
||||
}
|
||||
return PASSWORD_PATTERN.matcher(password).matches();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package com.personalproject.generator;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 生成包含基础四则运算的小学难度题目表达式.
|
||||
*/
|
||||
public final class PrimaryQuestionGenerator implements QuestionGenerator {
|
||||
|
||||
private static final String[] OPERATORS = {"+", "-", "*", "/"};
|
||||
|
||||
@Override
|
||||
public String generateQuestion(Random random) {
|
||||
// 至少生成两个操作数,避免题目退化成单个数字
|
||||
int operandCount = random.nextInt(4) + 2;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int index = 0; index < operandCount; index++) {
|
||||
if (index > 0) {
|
||||
String operator = OPERATORS[random.nextInt(OPERATORS.length)];
|
||||
builder.append(' ').append(operator).append(' ');
|
||||
}
|
||||
int value = random.nextInt(100) + 1;
|
||||
builder.append(value);
|
||||
}
|
||||
String expression = builder.toString();
|
||||
if (operandCount > 1 && random.nextBoolean()) {
|
||||
return '(' + expression + ')';
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
@ -1,119 +0,0 @@
|
||||
package com.personalproject.generator;
|
||||
|
||||
import com.personalproject.model.QuizQuestion;
|
||||
import com.personalproject.service.MathExpressionEvaluator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 生成包含基础四则运算的小学难度题目表达式.
|
||||
*/
|
||||
public final class PrimaryQuestionGenerator implements QuestionGenerator, QuizQuestionGenerator {
|
||||
|
||||
private static final String[] OPERATORS = {"+", "-", "*", "/"};
|
||||
private static final int OPTIONS_COUNT = 4;
|
||||
|
||||
@Override
|
||||
public String generateQuestion(Random random) {
|
||||
// 至少生成两个操作数,避免题目退化成单个数字
|
||||
int operandCount = random.nextInt(4) + 2;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int index = 0; index < operandCount; index++) {
|
||||
if (index > 0) {
|
||||
String operator = OPERATORS[random.nextInt(OPERATORS.length)];
|
||||
builder.append(' ').append(operator).append(' ');
|
||||
}
|
||||
int value = random.nextInt(100) + 1;
|
||||
builder.append(value);
|
||||
}
|
||||
String expression = builder.toString();
|
||||
if (operandCount > 1 && random.nextBoolean()) {
|
||||
return '(' + expression + ')';
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QuizQuestion generateQuizQuestion(Random random) {
|
||||
// 生成数学表达式
|
||||
int operandCount = random.nextInt(4) + 2;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int index = 0; index < operandCount; index++) {
|
||||
if (index > 0) {
|
||||
String operator = OPERATORS[random.nextInt(OPERATORS.length)];
|
||||
builder.append(' ').append(operator).append(' ');
|
||||
}
|
||||
int value = random.nextInt(100) + 1;
|
||||
builder.append(value);
|
||||
}
|
||||
String expression = builder.toString();
|
||||
if (operandCount > 1 && random.nextBoolean()) {
|
||||
expression = '(' + expression + ')';
|
||||
}
|
||||
|
||||
// 直接计算正确答案
|
||||
double correctAnswer;
|
||||
try {
|
||||
correctAnswer = MathExpressionEvaluator.evaluate(expression);
|
||||
} catch (Exception e) {
|
||||
// 如果计算失败则使用兜底值
|
||||
correctAnswer = 0.0;
|
||||
}
|
||||
|
||||
List<String> options = generateOptions(correctAnswer, random);
|
||||
String correctOption = formatOption(correctAnswer);
|
||||
int correctAnswerIndex = options.indexOf(correctOption);
|
||||
if (correctAnswerIndex < 0) {
|
||||
// 兜底逻辑:确保正确答案存在于选项中
|
||||
options.set(0, correctOption);
|
||||
correctAnswerIndex = 0;
|
||||
}
|
||||
|
||||
return new QuizQuestion(expression, options, correctAnswerIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成选择题选项.
|
||||
*/
|
||||
private List<String> generateOptions(double correctAnswer, Random random) {
|
||||
String correctOption = formatOption(correctAnswer);
|
||||
Set<String> optionSet = new LinkedHashSet<>();
|
||||
optionSet.add(correctOption);
|
||||
|
||||
double scale = Math.max(Math.abs(correctAnswer) * 0.2, 1.0);
|
||||
int attempts = 0;
|
||||
while (optionSet.size() < OPTIONS_COUNT && attempts < 100) {
|
||||
double delta = random.nextGaussian() * scale;
|
||||
if (Math.abs(delta) < 0.5) {
|
||||
double direction = random.nextBoolean() ? 1 : -1;
|
||||
delta = direction * (0.5 + random.nextDouble()) * scale;
|
||||
}
|
||||
double candidate = correctAnswer + delta;
|
||||
if (Double.isNaN(candidate) || Double.isInfinite(candidate)) {
|
||||
attempts++;
|
||||
continue;
|
||||
}
|
||||
optionSet.add(formatOption(candidate));
|
||||
attempts++;
|
||||
}
|
||||
|
||||
double step = Math.max(scale, 1.0);
|
||||
while (optionSet.size() < OPTIONS_COUNT) {
|
||||
double candidate = correctAnswer + step * optionSet.size();
|
||||
optionSet.add(formatOption(candidate));
|
||||
step += 1.0;
|
||||
}
|
||||
|
||||
List<String> options = new ArrayList<>(optionSet);
|
||||
Collections.shuffle(options, random);
|
||||
return options;
|
||||
}
|
||||
|
||||
private String formatOption(double value) {
|
||||
return String.format("%.2f", value);
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
package com.personalproject.generator;
|
||||
|
||||
import com.personalproject.model.QuizQuestion;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 负责生成带答案的数学题目.
|
||||
*/
|
||||
public interface QuizQuestionGenerator {
|
||||
|
||||
/**
|
||||
* 基于提供的随机数生成器构造一道带答案的题目.
|
||||
*
|
||||
* @param random 用于生成随机数的实例.
|
||||
* @return 生成的带答案的题目.
|
||||
*/
|
||||
QuizQuestion generateQuizQuestion(Random random);
|
||||
}
|
||||
@ -1,54 +0,0 @@
|
||||
package com.personalproject.ui;
|
||||
|
||||
import com.personalproject.controller.MathLearningController;
|
||||
import com.personalproject.generator.HighSchoolQuestionGenerator;
|
||||
import com.personalproject.generator.MiddleSchoolQuestionGenerator;
|
||||
import com.personalproject.generator.PrimaryQuestionGenerator;
|
||||
import com.personalproject.generator.QuestionGenerator;
|
||||
import com.personalproject.model.DifficultyLevel;
|
||||
import com.personalproject.service.QuestionGenerationService;
|
||||
import com.personalproject.ui.scenes.LoginScene;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import javafx.application.Application;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
/**
|
||||
* 数学学习软件的 JavaFX 图形界面应用程序. 这是图形界面的主要入口点.
|
||||
*/
|
||||
public final class MathExamGui extends Application {
|
||||
|
||||
private MathLearningController controller;
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) {
|
||||
// 使用题目生成器初始化控制器
|
||||
Map<DifficultyLevel, QuestionGenerator> generatorMap = new EnumMap<>(DifficultyLevel.class);
|
||||
generatorMap.put(DifficultyLevel.PRIMARY, new PrimaryQuestionGenerator());
|
||||
generatorMap.put(DifficultyLevel.MIDDLE, new MiddleSchoolQuestionGenerator());
|
||||
generatorMap.put(DifficultyLevel.HIGH, new HighSchoolQuestionGenerator());
|
||||
QuestionGenerationService questionGenerationService = new QuestionGenerationService(
|
||||
generatorMap);
|
||||
this.controller = new MathLearningController(generatorMap, questionGenerationService);
|
||||
|
||||
// 配置主舞台
|
||||
primaryStage.setTitle("数学学习软件");
|
||||
|
||||
// 从登录界面开始
|
||||
LoginScene loginScene = new LoginScene(primaryStage, controller);
|
||||
Scene scene = new Scene(loginScene, 600, 400);
|
||||
|
||||
primaryStage.setScene(scene);
|
||||
primaryStage.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动 JavaFX 应用程序.
|
||||
*
|
||||
* @param args 命令行参数
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
launch(args);
|
||||
}
|
||||
}
|
||||
@ -1,162 +0,0 @@
|
||||
package com.personalproject.ui.scenes;
|
||||
|
||||
import com.personalproject.controller.MathLearningController;
|
||||
import com.personalproject.ui.views.MainMenuView;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.PasswordField;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.scene.text.FontWeight;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
/**
|
||||
* 负责处理用户登录与注册的场景.
|
||||
*/
|
||||
public class LoginScene extends BorderPane {
|
||||
|
||||
private final Stage primaryStage;
|
||||
private final MathLearningController controller;
|
||||
private TextField usernameField;
|
||||
private PasswordField passwordField;
|
||||
private Button loginButton;
|
||||
private Button registerButton;
|
||||
|
||||
/**
|
||||
* LoginScene 的构造函数.
|
||||
*
|
||||
* @param primaryStage 应用程序的主舞台
|
||||
* @param controller 数学学习控制器
|
||||
*/
|
||||
public LoginScene(Stage primaryStage, MathLearningController controller) {
|
||||
this.primaryStage = primaryStage;
|
||||
this.controller = controller;
|
||||
initializeUi();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化界面组件.
|
||||
*/
|
||||
private void initializeUi() {
|
||||
// 创建主布局
|
||||
VBox mainLayout = new VBox(15);
|
||||
mainLayout.setAlignment(Pos.CENTER);
|
||||
mainLayout.setPadding(new Insets(20));
|
||||
|
||||
// 标题
|
||||
final Label titleLabel = new Label("数学学习软件");
|
||||
titleLabel.setFont(Font.font("System", FontWeight.BOLD, 24));
|
||||
|
||||
// 登录表单
|
||||
GridPane loginForm = new GridPane();
|
||||
loginForm.setHgap(10);
|
||||
loginForm.setVgap(10);
|
||||
loginForm.setAlignment(Pos.CENTER);
|
||||
|
||||
final Label usernameLabel = new Label("用户名:");
|
||||
usernameField = new TextField();
|
||||
usernameField.setPrefWidth(200);
|
||||
|
||||
final Label passwordLabel = new Label("密码:");
|
||||
passwordField = new PasswordField();
|
||||
passwordField.setPrefWidth(200);
|
||||
|
||||
loginForm.add(usernameLabel, 0, 0);
|
||||
loginForm.add(usernameField, 1, 0);
|
||||
loginForm.add(passwordLabel, 0, 1);
|
||||
loginForm.add(passwordField, 1, 1);
|
||||
|
||||
// 按钮
|
||||
HBox buttonBox = new HBox(10);
|
||||
buttonBox.setAlignment(Pos.CENTER);
|
||||
|
||||
loginButton = new Button("登录");
|
||||
registerButton = new Button("注册");
|
||||
|
||||
// 设置按钮样式
|
||||
loginButton.setPrefWidth(100);
|
||||
registerButton.setPrefWidth(100);
|
||||
|
||||
buttonBox.getChildren().addAll(loginButton, registerButton);
|
||||
|
||||
// 将组件添加到主布局
|
||||
mainLayout.getChildren().addAll(titleLabel, loginForm, buttonBox);
|
||||
|
||||
// 将主布局放到边界面板中央
|
||||
setCenter(mainLayout);
|
||||
|
||||
// 添加事件处理器
|
||||
addEventHandlers();
|
||||
}
|
||||
|
||||
/**
|
||||
* 为界面组件添加事件处理器.
|
||||
*/
|
||||
private void addEventHandlers() {
|
||||
loginButton.setOnAction(e -> handleLogin());
|
||||
registerButton.setOnAction(e -> handleRegistration());
|
||||
|
||||
// 允许使用回车键登录
|
||||
setOnKeyPressed(event -> {
|
||||
if (event.getCode().toString().equals("ENTER")) {
|
||||
handleLogin();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理登录流程.
|
||||
*/
|
||||
private void handleLogin() {
|
||||
String username = usernameField.getText().trim();
|
||||
String password = passwordField.getText();
|
||||
|
||||
if (username.isEmpty() || password.isEmpty()) {
|
||||
showAlert(Alert.AlertType.WARNING, "警告", "请输入用户名和密码");
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证用户
|
||||
var userAccount = controller.authenticate(username, password);
|
||||
|
||||
if (userAccount.isPresent()) {
|
||||
// 登录成功,跳转到主菜单
|
||||
MainMenuView mainMenuView = new MainMenuView(primaryStage, controller, userAccount.get());
|
||||
primaryStage.getScene().setRoot(mainMenuView);
|
||||
} else {
|
||||
// 登录失败
|
||||
showAlert(Alert.AlertType.ERROR, "登录失败", "用户名或密码错误");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理注册流程.
|
||||
*/
|
||||
private void handleRegistration() {
|
||||
// 切换到注册界面
|
||||
RegistrationScene registrationScene = new RegistrationScene(primaryStage, controller);
|
||||
primaryStage.getScene().setRoot(registrationScene);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示提示对话框.
|
||||
*
|
||||
* @param alertType 提示类型
|
||||
* @param title 对话框标题
|
||||
* @param message 显示的消息
|
||||
*/
|
||||
private void showAlert(Alert.AlertType alertType, String title, String message) {
|
||||
Alert alert = new Alert(alertType);
|
||||
alert.setTitle(title);
|
||||
alert.setHeaderText(null);
|
||||
alert.setContentText(message);
|
||||
alert.showAndWait();
|
||||
}
|
||||
}
|
||||
@ -1,161 +0,0 @@
|
||||
package com.personalproject.ui.views;
|
||||
|
||||
import com.personalproject.controller.MathLearningController;
|
||||
import com.personalproject.model.ExamSession;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.scene.text.FontWeight;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
/**
|
||||
* 用于展示考试结果的界面.
|
||||
*/
|
||||
public class ExamResultsView extends BorderPane {
|
||||
|
||||
private final Stage primaryStage;
|
||||
private final MathLearningController controller;
|
||||
private final ExamSession examSession;
|
||||
private Button continueButton;
|
||||
private Button exitButton;
|
||||
|
||||
/**
|
||||
* ExamResultsView 的构造函数.
|
||||
*
|
||||
* @param primaryStage 应用程序的主舞台
|
||||
* @param controller 数学学习控制器
|
||||
* @param examSession 已完成的考试会话
|
||||
*/
|
||||
public ExamResultsView(Stage primaryStage, MathLearningController controller,
|
||||
ExamSession examSession) {
|
||||
this.primaryStage = primaryStage;
|
||||
this.controller = controller;
|
||||
this.examSession = examSession;
|
||||
initializeUi();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化界面组件.
|
||||
*/
|
||||
private void initializeUi() {
|
||||
// 创建主布局
|
||||
VBox mainLayout = new VBox(20);
|
||||
mainLayout.setAlignment(Pos.CENTER);
|
||||
mainLayout.setPadding(new Insets(20));
|
||||
|
||||
// 结果标题
|
||||
Label titleLabel = new Label("考试结果");
|
||||
titleLabel.setFont(Font.font("System", FontWeight.BOLD, 24));
|
||||
|
||||
// 分数展示
|
||||
double score = examSession.calculateScore();
|
||||
Label scoreLabel = new Label(String.format("您的得分: %.2f", score));
|
||||
scoreLabel.setFont(Font.font("System", FontWeight.BOLD, 18));
|
||||
|
||||
// 成绩明细
|
||||
VBox breakdownBox = new VBox(10);
|
||||
breakdownBox.setAlignment(Pos.CENTER);
|
||||
|
||||
Label totalQuestionsLabel = new Label("总题数: " + examSession.getTotalQuestions());
|
||||
Label correctAnswersLabel = new Label("答对题数: " + examSession.getCorrectAnswersCount());
|
||||
Label incorrectAnswersLabel = new Label("答错题数: " + examSession.getIncorrectAnswersCount());
|
||||
|
||||
breakdownBox.getChildren()
|
||||
.addAll(totalQuestionsLabel, correctAnswersLabel, incorrectAnswersLabel);
|
||||
|
||||
// 按钮区域
|
||||
HBox buttonBox = new HBox(15);
|
||||
buttonBox.setAlignment(Pos.CENTER);
|
||||
|
||||
continueButton = new Button("继续考试");
|
||||
exitButton = new Button("退出");
|
||||
|
||||
// 设置按钮尺寸
|
||||
continueButton.setPrefSize(120, 40);
|
||||
exitButton.setPrefSize(120, 40);
|
||||
|
||||
buttonBox.getChildren().addAll(continueButton, exitButton);
|
||||
|
||||
// 将组件添加到主布局
|
||||
mainLayout.getChildren().addAll(titleLabel, scoreLabel, breakdownBox, buttonBox);
|
||||
|
||||
// 将主布局置于边界面板中央
|
||||
setCenter(mainLayout);
|
||||
|
||||
// 添加事件处理器
|
||||
addEventHandlers();
|
||||
}
|
||||
|
||||
/**
|
||||
* 为界面组件添加事件处理器.
|
||||
*/
|
||||
private void addEventHandlers() {
|
||||
continueButton.setOnAction(e -> handleContinue());
|
||||
exitButton.setOnAction(e -> handleExit());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理继续考试按钮的操作.
|
||||
*/
|
||||
private void handleContinue() {
|
||||
// 返回主菜单以开始新考试
|
||||
controller.getUserAccount(examSession.getUsername())
|
||||
.ifPresentOrElse(
|
||||
userAccount -> {
|
||||
MainMenuView mainMenuView = new MainMenuView(primaryStage, controller, userAccount);
|
||||
primaryStage.getScene().setRoot(mainMenuView);
|
||||
},
|
||||
() -> {
|
||||
// 如果找不到用户信息,则提示错误并返回登录界面
|
||||
showAlert(Alert.AlertType.ERROR, "错误", "用户信息无法找到,请重新登录");
|
||||
// 返回登录场景
|
||||
com.personalproject.ui.scenes.LoginScene loginScene =
|
||||
new com.personalproject.ui.scenes.LoginScene(primaryStage, controller);
|
||||
primaryStage.getScene().setRoot(loginScene);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理退出按钮的操作.
|
||||
*/
|
||||
private void handleExit() {
|
||||
// 返回主菜单
|
||||
controller.getUserAccount(examSession.getUsername())
|
||||
.ifPresentOrElse(
|
||||
userAccount -> {
|
||||
MainMenuView mainMenuView = new MainMenuView(primaryStage, controller, userAccount);
|
||||
primaryStage.getScene().setRoot(mainMenuView);
|
||||
},
|
||||
() -> {
|
||||
// 如果找不到用户信息,则提示错误并返回登录界面
|
||||
showAlert(Alert.AlertType.ERROR, "错误", "用户信息无法找到,请重新登录");
|
||||
// 返回登录场景
|
||||
com.personalproject.ui.scenes.LoginScene loginScene =
|
||||
new com.personalproject.ui.scenes.LoginScene(primaryStage, controller);
|
||||
primaryStage.getScene().setRoot(loginScene);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示提示对话框.
|
||||
*
|
||||
* @param alertType 提示类型
|
||||
* @param title 对话框标题
|
||||
* @param message 显示的消息
|
||||
*/
|
||||
private void showAlert(Alert.AlertType alertType, String title, String message) {
|
||||
Alert alert = new Alert(alertType);
|
||||
alert.setTitle(title);
|
||||
alert.setHeaderText(null);
|
||||
alert.setContentText(message);
|
||||
alert.showAndWait();
|
||||
}
|
||||
}
|
||||
@ -1,157 +0,0 @@
|
||||
package com.personalproject.ui.views;
|
||||
|
||||
import com.personalproject.auth.UserAccount;
|
||||
import com.personalproject.controller.MathLearningController;
|
||||
import com.personalproject.model.DifficultyLevel;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Spinner;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.scene.text.FontWeight;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
/**
|
||||
* 用于选择考试难度和题目数量的界面.
|
||||
*/
|
||||
public class ExamSelectionView extends BorderPane {
|
||||
|
||||
private final Stage primaryStage;
|
||||
private final MathLearningController controller;
|
||||
private final UserAccount userAccount;
|
||||
private ComboBox<DifficultyLevel> difficultyComboBox;
|
||||
private Spinner<Integer> questionCountSpinner;
|
||||
private Button startExamButton;
|
||||
private Button backButton;
|
||||
|
||||
/**
|
||||
* ExamSelectionView 的构造函数.
|
||||
*
|
||||
* @param primaryStage 应用程序的主舞台
|
||||
* @param controller 数学学习控制器
|
||||
* @param userAccount 当前用户账户
|
||||
*/
|
||||
public ExamSelectionView(Stage primaryStage, MathLearningController controller,
|
||||
UserAccount userAccount) {
|
||||
this.primaryStage = primaryStage;
|
||||
this.controller = controller;
|
||||
this.userAccount = userAccount;
|
||||
initializeUi();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化界面组件.
|
||||
*/
|
||||
private void initializeUi() {
|
||||
// 创建主布局
|
||||
VBox mainLayout = new VBox(20);
|
||||
mainLayout.setAlignment(Pos.CENTER);
|
||||
mainLayout.setPadding(new Insets(20));
|
||||
|
||||
// 标题
|
||||
final Label titleLabel = new Label("考试设置");
|
||||
titleLabel.setFont(Font.font("System", FontWeight.BOLD, 24));
|
||||
|
||||
// 考试设置表单
|
||||
GridPane examSettingsForm = new GridPane();
|
||||
examSettingsForm.setHgap(15);
|
||||
examSettingsForm.setVgap(15);
|
||||
examSettingsForm.setAlignment(Pos.CENTER);
|
||||
|
||||
final Label difficultyLabel = new Label("选择难度:");
|
||||
difficultyComboBox = new ComboBox<>();
|
||||
difficultyComboBox.getItems()
|
||||
.addAll(DifficultyLevel.PRIMARY, DifficultyLevel.MIDDLE, DifficultyLevel.HIGH);
|
||||
difficultyComboBox.setValue(userAccount.difficultyLevel()); // 默认选中用户的难度
|
||||
difficultyComboBox.setPrefWidth(200);
|
||||
|
||||
final Label questionCountLabel = new Label("题目数量 (10-30):");
|
||||
questionCountSpinner = new Spinner<>(10, 30, 10); // 最小值、最大值、初始值
|
||||
questionCountSpinner.setPrefWidth(200);
|
||||
|
||||
examSettingsForm.add(difficultyLabel, 0, 0);
|
||||
examSettingsForm.add(difficultyComboBox, 1, 0);
|
||||
examSettingsForm.add(questionCountLabel, 0, 1);
|
||||
examSettingsForm.add(questionCountSpinner, 1, 1);
|
||||
|
||||
// 按钮区域
|
||||
HBox buttonBox = new HBox(15);
|
||||
buttonBox.setAlignment(Pos.CENTER);
|
||||
|
||||
startExamButton = new Button("开始考试");
|
||||
backButton = new Button("返回");
|
||||
|
||||
// 设置按钮尺寸
|
||||
startExamButton.setPrefSize(120, 40);
|
||||
backButton.setPrefSize(120, 40);
|
||||
|
||||
buttonBox.getChildren().addAll(startExamButton, backButton);
|
||||
|
||||
// 将组件添加到主布局
|
||||
mainLayout.getChildren().addAll(titleLabel, examSettingsForm, buttonBox);
|
||||
|
||||
// 将主布局置于边界面板中央
|
||||
setCenter(mainLayout);
|
||||
|
||||
// 添加事件处理器
|
||||
addEventHandlers();
|
||||
}
|
||||
|
||||
/**
|
||||
* 为界面组件添加事件处理器.
|
||||
*/
|
||||
private void addEventHandlers() {
|
||||
startExamButton.setOnAction(e -> handleStartExam());
|
||||
backButton.setOnAction(e -> handleBack());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理开始考试按钮的操作.
|
||||
*/
|
||||
private void handleStartExam() {
|
||||
DifficultyLevel selectedDifficulty = difficultyComboBox.getValue();
|
||||
int questionCount = questionCountSpinner.getValue();
|
||||
|
||||
if (questionCount < 10 || questionCount > 30) {
|
||||
showAlert(Alert.AlertType.WARNING, "无效输入", "题目数量必须在10到30之间");
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建并启动考试会话
|
||||
com.personalproject.model.ExamSession examSession = controller.createExamSession(
|
||||
userAccount.username(), selectedDifficulty, questionCount);
|
||||
|
||||
ExamView examView = new ExamView(primaryStage, controller, examSession);
|
||||
primaryStage.getScene().setRoot(examView);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理返回按钮的操作.
|
||||
*/
|
||||
private void handleBack() {
|
||||
MainMenuView mainMenuView = new MainMenuView(primaryStage, controller, userAccount);
|
||||
primaryStage.getScene().setRoot(mainMenuView);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示提示对话框.
|
||||
*
|
||||
* @param alertType 提示类型
|
||||
* @param title 对话框标题
|
||||
* @param message 显示的消息
|
||||
*/
|
||||
private void showAlert(Alert.AlertType alertType, String title, String message) {
|
||||
Alert alert = new Alert(alertType);
|
||||
alert.setTitle(title);
|
||||
alert.setHeaderText(null);
|
||||
alert.setContentText(message);
|
||||
alert.showAndWait();
|
||||
}
|
||||
}
|
||||
@ -1,267 +0,0 @@
|
||||
package com.personalproject.ui.views;
|
||||
|
||||
import com.personalproject.controller.MathLearningController;
|
||||
import com.personalproject.model.ExamSession;
|
||||
import com.personalproject.model.QuizQuestion;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.RadioButton;
|
||||
import javafx.scene.control.ToggleGroup;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.scene.text.FontWeight;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
/**
|
||||
* 提供答题界面以及题目与选项的呈现.
|
||||
*/
|
||||
public class ExamView extends BorderPane {
|
||||
|
||||
private final Stage primaryStage;
|
||||
private final MathLearningController controller;
|
||||
private final ExamSession examSession;
|
||||
private Label questionNumberLabel;
|
||||
private Label questionTextLabel;
|
||||
private ToggleGroup answerToggleGroup;
|
||||
private VBox optionsBox;
|
||||
private Button nextButton;
|
||||
private Button previousButton;
|
||||
private Button finishButton;
|
||||
private HBox buttonBox;
|
||||
|
||||
/**
|
||||
* ExamView 的构造函数.
|
||||
*
|
||||
* @param primaryStage 应用程序的主舞台
|
||||
* @param controller 数学学习控制器
|
||||
* @param examSession 当前的考试会话
|
||||
*/
|
||||
public ExamView(Stage primaryStage, MathLearningController controller, ExamSession examSession) {
|
||||
this.primaryStage = primaryStage;
|
||||
this.controller = controller;
|
||||
this.examSession = examSession;
|
||||
initializeUi();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化界面组件.
|
||||
*/
|
||||
private void initializeUi() {
|
||||
// 创建主布局
|
||||
VBox mainLayout = new VBox(20);
|
||||
mainLayout.setAlignment(Pos.CENTER);
|
||||
mainLayout.setPadding(new Insets(20));
|
||||
|
||||
// 题号
|
||||
questionNumberLabel = new Label();
|
||||
questionNumberLabel.setFont(Font.font("System", FontWeight.BOLD, 16));
|
||||
|
||||
// 题目文本
|
||||
questionTextLabel = new Label();
|
||||
questionTextLabel.setWrapText(true);
|
||||
questionTextLabel.setFont(Font.font("System", FontWeight.NORMAL, 14));
|
||||
questionTextLabel.setMaxWidth(500);
|
||||
|
||||
// 选项容器
|
||||
optionsBox = new VBox(10);
|
||||
optionsBox.setPadding(new Insets(10));
|
||||
answerToggleGroup = new ToggleGroup();
|
||||
|
||||
// 按钮区域
|
||||
buttonBox = new HBox(15);
|
||||
buttonBox.setAlignment(Pos.CENTER);
|
||||
|
||||
previousButton = new Button("上一题");
|
||||
nextButton = new Button("下一题");
|
||||
finishButton = new Button("完成考试");
|
||||
|
||||
// 设置按钮尺寸
|
||||
previousButton.setPrefSize(100, 35);
|
||||
nextButton.setPrefSize(100, 35);
|
||||
finishButton.setPrefSize(120, 35);
|
||||
|
||||
buttonBox.getChildren().addAll(previousButton, nextButton, finishButton);
|
||||
|
||||
// 将组件添加到主布局
|
||||
mainLayout.getChildren().addAll(questionNumberLabel, questionTextLabel, optionsBox, buttonBox);
|
||||
|
||||
// 将主布局置于边界面板中央
|
||||
setCenter(mainLayout);
|
||||
|
||||
// 加载第一题
|
||||
loadCurrentQuestion();
|
||||
|
||||
// 添加事件处理器
|
||||
addEventHandlers();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前题目加载到界面.
|
||||
*/
|
||||
private void loadCurrentQuestion() {
|
||||
try {
|
||||
// 在加载下一题之前检查考试是否已完成
|
||||
if (examSession.isComplete()) {
|
||||
// 如果考试已完成,则启用“完成考试”按钮
|
||||
updateButtonStates();
|
||||
return;
|
||||
}
|
||||
|
||||
QuizQuestion currentQuestion = examSession.getCurrentQuestion();
|
||||
int currentIndex = examSession.getCurrentQuestionIndex();
|
||||
|
||||
if (currentQuestion == null) {
|
||||
showAlert(Alert.AlertType.ERROR, "错误", "当前题目为空,请重新开始考试");
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新题号与题目文本
|
||||
questionNumberLabel.setText("第 " + (currentIndex + 1) + " 题");
|
||||
questionTextLabel.setText(currentQuestion.getQuestionText());
|
||||
|
||||
// 清空上一题的选项
|
||||
answerToggleGroup.selectToggle(null);
|
||||
answerToggleGroup.getToggles().clear();
|
||||
optionsBox.getChildren().clear();
|
||||
|
||||
// 创建新的选项组件
|
||||
for (int i = 0; i < currentQuestion.getOptions().size(); i++) {
|
||||
String option = currentQuestion.getOptions().get(i);
|
||||
RadioButton optionButton = new RadioButton((i + 1) + ". " + option);
|
||||
optionButton.setToggleGroup(answerToggleGroup);
|
||||
optionButton.setUserData(i); // 存储选项索引
|
||||
|
||||
// 如果该题已有答案则自动选中
|
||||
if (examSession.hasAnswered(currentIndex)
|
||||
&& examSession.getUserAnswer(currentIndex) == i) {
|
||||
optionButton.setSelected(true);
|
||||
}
|
||||
|
||||
optionsBox.getChildren().add(optionButton);
|
||||
}
|
||||
|
||||
// 更新按钮状态
|
||||
updateButtonStates();
|
||||
} catch (Exception e) {
|
||||
showAlert(Alert.AlertType.ERROR, "错误", "加载题目时发生错误: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据当前位置更新导航按钮的状态.
|
||||
*/
|
||||
private void updateButtonStates() {
|
||||
try {
|
||||
int currentIndex = examSession.getCurrentQuestionIndex();
|
||||
int totalQuestions = examSession.getTotalQuestions();
|
||||
|
||||
// 处理潜在极端情况
|
||||
if (totalQuestions <= 0) {
|
||||
// 如果没有题目,则禁用所有导航按钮
|
||||
previousButton.setDisable(true);
|
||||
nextButton.setDisable(true);
|
||||
finishButton.setDisable(false); // 仍允许完成考试
|
||||
return;
|
||||
}
|
||||
|
||||
// “上一题”按钮状态
|
||||
previousButton.setDisable(currentIndex < 0 || currentIndex == 0);
|
||||
|
||||
// “下一题”按钮状态
|
||||
nextButton.setDisable(currentIndex < 0 || currentIndex >= totalQuestions - 1);
|
||||
|
||||
// “完成考试”按钮状态——在考试完成或到达最后一题时启用
|
||||
boolean isExamComplete = examSession.isComplete();
|
||||
boolean isAtLastQuestion = (currentIndex >= totalQuestions - 1);
|
||||
finishButton.setDisable(!(isExamComplete || isAtLastQuestion));
|
||||
} catch (Exception e) {
|
||||
// 若出现异常,禁用导航按钮以避免进一步问题
|
||||
previousButton.setDisable(true);
|
||||
nextButton.setDisable(true);
|
||||
finishButton.setDisable(false); // 仍允许完成考试
|
||||
showAlert(Alert.AlertType.ERROR, "错误", "更新按钮状态时发生错误: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为界面组件添加事件处理器.
|
||||
*/
|
||||
private void addEventHandlers() {
|
||||
nextButton.setOnAction(e -> handleNextQuestion());
|
||||
previousButton.setOnAction(e -> handlePreviousQuestion());
|
||||
finishButton.setOnAction(e -> handleFinishExam());
|
||||
|
||||
// 添加变更监听器,在选项被选择时保存答案
|
||||
answerToggleGroup.selectedToggleProperty().addListener((obs, oldSelection, newSelection) -> {
|
||||
if (newSelection != null) {
|
||||
int selectedIndex = (Integer) newSelection.getUserData();
|
||||
examSession.setAnswer(selectedIndex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理“下一题”按钮的操作.
|
||||
*/
|
||||
private void handleNextQuestion() {
|
||||
try {
|
||||
if (examSession.goToNextQuestion()) {
|
||||
loadCurrentQuestion();
|
||||
} else {
|
||||
// 若无法跳转到下一题,可能已经到达末尾
|
||||
// 检查考试是否完成并据此更新按钮状态
|
||||
updateButtonStates();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
showAlert(Alert.AlertType.ERROR, "错误", "导航到下一题时发生错误: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理“上一题”按钮的操作.
|
||||
*/
|
||||
private void handlePreviousQuestion() {
|
||||
try {
|
||||
if (examSession.goToPreviousQuestion()) {
|
||||
loadCurrentQuestion();
|
||||
} else {
|
||||
// 若无法返回上一题,可能已经位于开头
|
||||
updateButtonStates();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
showAlert(Alert.AlertType.ERROR, "错误", "导航到上一题时发生错误: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示提示对话框.
|
||||
*
|
||||
* @param alertType 提示类型
|
||||
* @param title 对话框标题
|
||||
* @param message 显示的消息
|
||||
*/
|
||||
private void showAlert(Alert.AlertType alertType, String title, String message) {
|
||||
Alert alert = new Alert(alertType);
|
||||
alert.setTitle(title);
|
||||
alert.setHeaderText(null);
|
||||
alert.setContentText(message);
|
||||
alert.showAndWait();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理“完成考试”按钮的操作.
|
||||
*/
|
||||
private void handleFinishExam() {
|
||||
// 保存考试结果
|
||||
controller.saveExamResults(examSession);
|
||||
|
||||
// 展示考试结果
|
||||
ExamResultsView resultsView = new ExamResultsView(primaryStage, controller, examSession);
|
||||
primaryStage.getScene().setRoot(resultsView);
|
||||
}
|
||||
}
|
||||
@ -1,169 +0,0 @@
|
||||
package com.personalproject.ui.views;
|
||||
|
||||
import com.personalproject.auth.UserAccount;
|
||||
import com.personalproject.controller.MathLearningController;
|
||||
import com.personalproject.model.DifficultyLevel;
|
||||
import com.personalproject.ui.scenes.LoginScene;
|
||||
import java.util.Optional;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextInputDialog;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.scene.text.Font;
|
||||
import javafx.scene.text.FontWeight;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
/**
|
||||
* 用户可以开始考试或更改设置的主菜单界面.
|
||||
*/
|
||||
public class MainMenuView extends BorderPane {
|
||||
|
||||
private final Stage primaryStage;
|
||||
private final MathLearningController controller;
|
||||
private final UserAccount userAccount;
|
||||
private Button changePasswordButton;
|
||||
private Button logoutButton;
|
||||
|
||||
/**
|
||||
* MainMenuView 的构造函数.
|
||||
*
|
||||
* @param primaryStage 应用程序的主舞台
|
||||
* @param controller 数学学习控制器
|
||||
* @param userAccount 当前用户账户
|
||||
*/
|
||||
public MainMenuView(Stage primaryStage, MathLearningController controller,
|
||||
UserAccount userAccount) {
|
||||
this.primaryStage = primaryStage;
|
||||
this.controller = controller;
|
||||
this.userAccount = userAccount;
|
||||
initializeUi();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化界面组件.
|
||||
*/
|
||||
private void initializeUi() {
|
||||
// 创建主布局
|
||||
VBox mainLayout = new VBox(20);
|
||||
mainLayout.setAlignment(Pos.CENTER);
|
||||
mainLayout.setPadding(new Insets(20));
|
||||
|
||||
// 欢迎信息
|
||||
Label welcomeLabel = new Label("欢迎, " + userAccount.username());
|
||||
welcomeLabel.setFont(Font.font("System", FontWeight.BOLD, 18));
|
||||
|
||||
// 难度信息
|
||||
Label difficultyLabel = new Label(
|
||||
"当前难度: " + userAccount.difficultyLevel().getDisplayName());
|
||||
difficultyLabel.setFont(Font.font("System", FontWeight.NORMAL, 14));
|
||||
|
||||
Label promptLabel = new Label("请选择考试难度开始答题");
|
||||
promptLabel.setFont(Font.font("System", FontWeight.NORMAL, 14));
|
||||
|
||||
VBox difficultyBox = new VBox(10);
|
||||
difficultyBox.setAlignment(Pos.CENTER);
|
||||
difficultyBox.getChildren().addAll(
|
||||
createDifficultyButton(DifficultyLevel.PRIMARY),
|
||||
createDifficultyButton(DifficultyLevel.MIDDLE),
|
||||
createDifficultyButton(DifficultyLevel.HIGH));
|
||||
|
||||
// 按钮区域
|
||||
VBox buttonBox = new VBox(15);
|
||||
buttonBox.setAlignment(Pos.CENTER);
|
||||
|
||||
changePasswordButton = new Button("修改密码");
|
||||
logoutButton = new Button("退出登录");
|
||||
|
||||
// 设置按钮尺寸
|
||||
changePasswordButton.setPrefSize(150, 40);
|
||||
logoutButton.setPrefSize(150, 40);
|
||||
|
||||
buttonBox.getChildren().addAll(changePasswordButton, logoutButton);
|
||||
|
||||
// 将组件添加到主布局
|
||||
mainLayout.getChildren().addAll(welcomeLabel, difficultyLabel, promptLabel, difficultyBox,
|
||||
buttonBox);
|
||||
|
||||
// 将主布局置于边界面板中央
|
||||
setCenter(mainLayout);
|
||||
|
||||
// 添加事件处理器
|
||||
addEventHandlers();
|
||||
}
|
||||
|
||||
/**
|
||||
* 为界面组件添加事件处理器.
|
||||
*/
|
||||
private void addEventHandlers() {
|
||||
changePasswordButton.setOnAction(e -> handleChangePassword());
|
||||
logoutButton.setOnAction(e -> handleLogout());
|
||||
}
|
||||
|
||||
private Button createDifficultyButton(DifficultyLevel level) {
|
||||
Button button = new Button(level.getDisplayName());
|
||||
button.setPrefSize(150, 40);
|
||||
button.setOnAction(e -> handleDifficultySelection(level));
|
||||
return button;
|
||||
}
|
||||
|
||||
private void handleDifficultySelection(DifficultyLevel level) {
|
||||
TextInputDialog dialog = new TextInputDialog("10");
|
||||
dialog.setTitle("题目数量");
|
||||
dialog.setHeaderText("请选择题目数量");
|
||||
dialog.setContentText("请输入题目数量 (10-30):");
|
||||
|
||||
Optional<String> result = dialog.showAndWait();
|
||||
if (result.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int questionCount;
|
||||
try {
|
||||
questionCount = Integer.parseInt(result.get().trim());
|
||||
} catch (NumberFormatException ex) {
|
||||
showAlert(Alert.AlertType.ERROR, "无效输入", "请输入有效的数字。");
|
||||
return;
|
||||
}
|
||||
|
||||
if (questionCount < 10 || questionCount > 30) {
|
||||
showAlert(Alert.AlertType.WARNING, "题目数量范围错误", "题目数量必须在10到30之间。");
|
||||
return;
|
||||
}
|
||||
|
||||
com.personalproject.model.ExamSession examSession = controller.createExamSession(
|
||||
userAccount.username(), level, questionCount);
|
||||
|
||||
ExamView examView = new ExamView(primaryStage, controller, examSession);
|
||||
primaryStage.getScene().setRoot(examView);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理修改密码按钮的操作.
|
||||
*/
|
||||
private void handleChangePassword() {
|
||||
PasswordChangeView passwordChangeView = new PasswordChangeView(primaryStage, controller,
|
||||
userAccount);
|
||||
primaryStage.getScene().setRoot(passwordChangeView);
|
||||
}
|
||||
|
||||
private void showAlert(Alert.AlertType alertType, String title, String message) {
|
||||
Alert alert = new Alert(alertType);
|
||||
alert.setTitle(title);
|
||||
alert.setHeaderText(null);
|
||||
alert.setContentText(message);
|
||||
alert.showAndWait();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理退出登录按钮的操作.
|
||||
*/
|
||||
private void handleLogout() {
|
||||
// 返回登录界面
|
||||
LoginScene loginScene = new LoginScene(primaryStage, controller);
|
||||
primaryStage.getScene().setRoot(loginScene);
|
||||
}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
|
||||
# 主机不变
|
||||
mail.smtp.host=smtp.126.com
|
||||
|
||||
# 关键修改:端口改为 465
|
||||
mail.smtp.port=465
|
||||
|
||||
# 关键修改:禁用 STARTTLS
|
||||
mail.smtp.starttls.enable=false
|
||||
|
||||
# 关键修改:启用 SSL
|
||||
mail.smtp.ssl.enable=true
|
||||
|
||||
# 以下不变
|
||||
mail.smtp.auth=true
|
||||
mail.username=soloyouth@126.com
|
||||
mail.password=ZYsjxwDXFBsWeQcX
|
||||
mail.from=soloyouth@126.com
|
||||
mail.subject=数学学习软件注册验证码
|
||||
mail.debug=true
|
||||
Loading…
Reference in new issue