diff --git a/src/main/java/com/wsf/mathapp/Main.java b/src/main/java/com/wsf/mathapp/Main.java index 21c2960..b0964a0 100644 --- a/src/main/java/com/wsf/mathapp/Main.java +++ b/src/main/java/com/wsf/mathapp/Main.java @@ -4,13 +4,30 @@ import com.wsf.mathapp.controller.SceneManager; import javafx.application.Application; import javafx.stage.Stage; +/** + * 数学学习软件主应用程序类. + * 负责初始化应用程序并启动JavaFX界面. + */ public class Main extends Application { + + /** + * JavaFX应用程序的入口方法. + * 初始化场景管理器并显示登录界面. + * + * @param primaryStage JavaFX主舞台对象. + */ @Override public void start(Stage primaryStage) { SceneManager sceneManager = new SceneManager(primaryStage); sceneManager.showLoginView(); } + /** + * 应用程序的主方法. + * 启动JavaFX应用程序. + * + * @param args 命令行参数. + */ public static void main(String[] args) { launch(args); } diff --git a/src/main/java/com/wsf/mathapp/controller/SceneManager.java b/src/main/java/com/wsf/mathapp/controller/SceneManager.java index f0088ce..ff4a040 100644 --- a/src/main/java/com/wsf/mathapp/controller/SceneManager.java +++ b/src/main/java/com/wsf/mathapp/controller/SceneManager.java @@ -1,12 +1,22 @@ package com.wsf.mathapp.controller; -import com.wsf.mathapp.view.*; +import com.wsf.mathapp.view.LevelSelectionView; +import com.wsf.mathapp.view.LoginView; +import com.wsf.mathapp.view.MainMenuView; +import com.wsf.mathapp.view.QuestionCountView; +import com.wsf.mathapp.view.QuizView; +import com.wsf.mathapp.view.RegisterView; +import com.wsf.mathapp.view.ResultView; import javafx.stage.Stage; +/** + * 场景管理器类,负责管理应用程序中各个界面之间的切换和导航. + * 维护所有视图实例并提供统一的方法来显示不同的界面. + */ public class SceneManager { private final Stage primaryStage; private final LoginView loginView; - private RegisterView registerView; + private final RegisterView registerView; private final MainMenuView mainMenuView; private final LevelSelectionView levelSelectionView; private final QuestionCountView questionCountView; @@ -14,6 +24,11 @@ public class SceneManager { private final ResultView resultView; private String currentUserName; + /** + * 构造函数,初始化场景管理器. + * + * @param primaryStage JavaFX主舞台,用于显示各个场景. + */ public SceneManager(Stage primaryStage) { this.primaryStage = primaryStage; this.primaryStage.setTitle("数学学习软件"); @@ -29,6 +44,10 @@ public class SceneManager { this.resultView = new ResultView(this); } + /** + * 显示登录界面. + * 清空输入字段并将主舞台的场景设置为登录界面. + */ public void showLoginView() { System.out.println("切换到登录界面"); loginView.clearFields(); @@ -36,6 +55,10 @@ public class SceneManager { primaryStage.show(); } + /** + * 显示注册界面. + * 清空输入字段并将主舞台的场景设置为注册界面. + */ public void showRegisterView() { System.out.println("切换到注册界面"); registerView.clearFields(); @@ -43,32 +66,54 @@ public class SceneManager { primaryStage.show(); } + /** + * 显示主菜单界面. + * 在显示前更新界面上的用户名显示. + */ public void showMainMenuView() { System.out.println("切换到主菜单界面"); // 在显示主菜单前更新用户名 if (mainMenuView != null) { mainMenuView.updateUsername(currentUserName); } - primaryStage.setScene(mainMenuView.getScene()); + if (mainMenuView != null) { + primaryStage.setScene(mainMenuView.getScene()); + } primaryStage.show(); // 添加这行 } + /** + * 显示级别选择界面. + * 在显示前更新界面上的用户名显示. + */ public void showLevelSelectionView() { System.out.println("切换到级别选择界面"); // 在显示级别选择界面前更新用户名 if (levelSelectionView != null) { levelSelectionView.updateUsername(currentUserName); } - primaryStage.setScene(levelSelectionView.getScene()); + if (levelSelectionView != null) { + primaryStage.setScene(levelSelectionView.getScene()); + } primaryStage.show(); // 添加这行 } + /** + * 显示题目数量选择界面. + * 用户可以选择要回答的题目数量. + */ public void showQuestionCountView() { System.out.println("切换到题目数量选择界面"); primaryStage.setScene(questionCountView.getScene()); primaryStage.show(); // 添加这行 } + /** + * 显示答题界面. + * + * @param level 题目难度级别. + * @param count 题目数量. + */ public void showQuizView(String level, int count) { System.out.println("切换到答题界面 - 级别: " + level + ", 题目数量: " + count); // 设置测验参数 @@ -77,6 +122,11 @@ public class SceneManager { primaryStage.show(); } + /** + * 显示结果界面. + + * @param score 用户获得的分数. + */ public void showResultView(double score) { System.out.println("切换到结果界面,分数: " + score); resultView.setScore(score); @@ -84,20 +134,30 @@ public class SceneManager { primaryStage.show(); // 添加这行 } - // Getter methods for views - public LoginView getLoginView() { return loginView; } - public RegisterView getRegisterView() { return registerView; } - public MainMenuView getMainMenuView() { return mainMenuView; } - public LevelSelectionView getLevelSelectionView() { return levelSelectionView; } - public QuestionCountView getQuestionCountView() { return questionCountView; } - public QuizView getQuizView() { return quizView; } - public ResultView getResultView() { return resultView; } + /** + * 获取题目数量选择视图实例. + * + * @return QuestionCountView 题目数量选择视图对象. + */ + public QuestionCountView getQuestionCountView() { + return questionCountView; + } + /** + * 设置当前用户名. + * + * @param currentUserName 当前登录用户的用户名. + */ public void setCurrentUserName(String currentUserName) { this.currentUserName = currentUserName; System.out.println("设置当前用户名: " + currentUserName); } + /** + * 获取当前用户名. + * + * @return String 当前登录用户的用户名. + */ public String getCurrentUserName() { return this.currentUserName; } diff --git a/src/main/java/com/wsf/mathapp/service/QuestionService.java b/src/main/java/com/wsf/mathapp/service/QuestionService.java index ec5c254..2067873 100644 --- a/src/main/java/com/wsf/mathapp/service/QuestionService.java +++ b/src/main/java/com/wsf/mathapp/service/QuestionService.java @@ -1,14 +1,21 @@ package com.wsf.mathapp.service; - -import com.ybw.mathapp.service.PrimarySchoolGenerator; import com.ybw.mathapp.service.JuniorHighGenerator; -import com.ybw.mathapp.service.SeniorHighGenerator; +import com.ybw.mathapp.service.PrimarySchoolGenerator; import com.ybw.mathapp.service.QuestionGenerator; +import com.ybw.mathapp.service.SeniorHighGenerator; - +/** + * 题目服务类,负责根据不同的难度级别创建对应的题目生成器. + */ public class QuestionService { + /** + * 根据难度级别创建对应的题目生成器. + * + * @param level 题目难度级别,可选值:"小学"、"初中"、"高中". + * @return QuestionGenerator 对应难度级别的题目生成器实例,如果级别不匹配则返回null. + */ public static QuestionGenerator createGenerator(String level) { return switch (level) { case "小学" -> new PrimarySchoolGenerator(); diff --git a/src/main/java/com/wsf/mathapp/view/LevelSelectionView.java b/src/main/java/com/wsf/mathapp/view/LevelSelectionView.java index b9a7092..b35c1a3 100644 --- a/src/main/java/com/wsf/mathapp/view/LevelSelectionView.java +++ b/src/main/java/com/wsf/mathapp/view/LevelSelectionView.java @@ -16,6 +16,10 @@ import javafx.scene.text.Font; import javafx.scene.text.FontWeight; import javafx.scene.text.Text; +/** + * 级别选择视图类,提供用户选择题目难度级别的界面. + * 包含小学、初中、高中三个难度级别的选择按钮. + */ public class LevelSelectionView { private Scene scene; private final SceneManager sceneManager; @@ -25,12 +29,20 @@ public class LevelSelectionView { private Label usernameLabel; private Text avatarText; + /** + * 构造函数,初始化级别选择视图. + * + * @param sceneManager 场景管理器,用于界面导航. + */ public LevelSelectionView(SceneManager sceneManager) { this.sceneManager = sceneManager; this.currentUsername = sceneManager.getCurrentUserName(); createScene(); } + /** + * 创建主场景,包含用户信息栏和级别选择内容. + */ private void createScene() { // 创建主容器 VBox mainContainer = new VBox(); @@ -47,31 +59,45 @@ public class LevelSelectionView { scene = new Scene(mainContainer, 450, 550); } + /** + * 创建用户信息栏,包含用户头像和用户名显示. + * + * @return HBox 用户信息栏布局容器. + */ private HBox createUserInfoBar() { HBox userInfoBar = new HBox(15); userInfoBar.setAlignment(Pos.CENTER_LEFT); userInfoBar.setPadding(new Insets(0, 0, 30, 0)); - userInfoBar.setStyle("-fx-border-color: #e0e0e0; -fx-border-width: 0 0 1 0; -fx-padding: 0 0 15 0;"); + userInfoBar.setStyle("-fx-border-color: #e0e0e0; " + + "-fx-border-width: 0 0 1 0; -fx-padding: 0 0 15 0;"); + // 用户名标签 + usernameLabel = new Label(currentUsername != null ? currentUsername : "用户"); + usernameLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16)); + usernameLabel.setStyle("-fx-text-fill: #2c3e50;"); + // 间隔 + Region spacer = new Region(); + HBox.setHgrow(spacer, Priority.ALWAYS); + // 创建头像容器 + VBox avatarContainer = createAvatarContainer(); + userInfoBar.getChildren().addAll(avatarContainer, usernameLabel, spacer); + return userInfoBar; + } - // 创建圆形头像容器 + /** + * 创建头像容器,包含圆形头像背景和用户首字母. + * + * @return VBox 头像容器布局. + */ + private VBox createAvatarContainer() { VBox avatarContainer = new VBox(); avatarContainer.setAlignment(Pos.CENTER); avatarContainer.setPrefSize(50, 50); - - // 创建圆形头像背景 - Circle avatarCircle = new Circle(22); - avatarCircle.setFill(Color.web("#4CAF50")); // 统一的绿色背景 - avatarCircle.setStroke(Color.WHITE); - avatarCircle.setStrokeWidth(2); - - // 添加阴影效果 - avatarCircle.setStyle("-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 5, 0.3, 2, 2);"); - // 添加首字母文本 avatarText = new Text(getFirstLetter()); avatarText.setFill(Color.WHITE); avatarText.setFont(Font.font("Arial", FontWeight.BOLD, 16)); - + // 创建圆形头像背景 + Circle avatarCircle = createAvatarCircle(); // 使用StackPane将文本放在圆形中心 javafx.scene.layout.StackPane avatarStack = new javafx.scene.layout.StackPane(); avatarStack.getChildren().addAll(avatarCircle, avatarText); @@ -79,101 +105,190 @@ public class LevelSelectionView { avatarStack.setAlignment(Pos.CENTER); avatarContainer.getChildren().add(avatarStack); + return avatarContainer; + } - // 用户名标签 - usernameLabel = new Label(currentUsername != null ? currentUsername : "用户"); - usernameLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16)); - usernameLabel.setStyle("-fx-text-fill: #2c3e50;"); - - // 间隔 - Region spacer = new Region(); - HBox.setHgrow(spacer, Priority.ALWAYS); - - userInfoBar.getChildren().addAll(avatarContainer, usernameLabel, spacer); + /** + * 创建圆形头像背景. + * + * @return Circle 圆形头像背景对象. + */ + private Circle createAvatarCircle() { + Circle avatarCircle = new Circle(22); + avatarCircle.setFill(Color.web("#4CAF50")); // 统一的绿色背景 + avatarCircle.setStroke(Color.WHITE); + avatarCircle.setStrokeWidth(2); - return userInfoBar; + // 添加阴影效果 + avatarCircle.setStyle("-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 5, 0.3, 2, 2);"); + return avatarCircle; } + /** + * 创建级别选择内容区域,包含标题和级别选择按钮. + * + * @return VBox 级别选择内容区域布局容器. + */ private VBox createSelectionContent() { VBox selectionContent = new VBox(25); selectionContent.setPadding(new Insets(30, 20, 20, 20)); selectionContent.setAlignment(Pos.CENTER); + // 创建标题区域 + VBox titleSection = createTitleSection(); + + // 创建级别按钮区域 + VBox levelButtonsSection = createLevelButtonsSection(); + + // 创建返回按钮 + Button backButton = createBackButton(); + + selectionContent.getChildren().addAll(titleSection, levelButtonsSection, backButton); + + return selectionContent; + } + + /** + * 创建标题区域,包含主标题和副标题. + * + * @return VBox 标题区域布局容器. + */ + private VBox createTitleSection() { + VBox titleSection = new VBox(5); + titleSection.setAlignment(Pos.CENTER); + Label titleLabel = new Label("选择题目级别"); titleLabel.setFont(Font.font("Arial", FontWeight.BOLD, 26)); titleLabel.setStyle("-fx-text-fill: #2c3e50;"); - // 添加副标题 Label subtitleLabel = new Label("请选择适合您的学习级别"); subtitleLabel.setFont(Font.font("Arial", 14)); subtitleLabel.setStyle("-fx-text-fill: #7f8c8d; -fx-padding: 0 0 10 0;"); + titleSection.getChildren().addAll(titleLabel, subtitleLabel); + return titleSection; + } + + /** + * 创建级别按钮区域,包含三个难度级别的选择按钮. + * + * @return VBox 级别按钮区域布局容器. + */ + private VBox createLevelButtonsSection() { + VBox levelButtonsSection = new VBox(15); + levelButtonsSection.setAlignment(Pos.CENTER); + Button primaryButton = createLevelButton("小学题目", "#4CAF50", "#45a049"); Button juniorButton = createLevelButton("初中题目", "#2196F3", "#1976D2"); Button seniorButton = createLevelButton("高中题目", "#9C27B0", "#7B1FA2"); - Button backButton = new Button("返回主菜单"); - backButton.setStyle("-fx-background-color: #95a5a6; -fx-text-fill: white; -fx-font-size: 14px; -fx-background-radius: 8; -fx-padding: 8 20;"); - backButton.setPrefSize(180, 45); - backButton.setOnMouseEntered(e -> backButton.setStyle("-fx-background-color: #7f8c8d; -fx-text-fill: white; -fx-font-size: 14px; -fx-background-radius: 8; -fx-padding: 8 20;")); - backButton.setOnMouseExited(e -> backButton.setStyle("-fx-background-color: #95a5a6; -fx-text-fill: white; -fx-font-size: 14px; -fx-background-radius: 8; -fx-padding: 8 20;")); + setupLevelButtonAction(primaryButton, "小学"); + setupLevelButtonAction(juniorButton, "初中"); + setupLevelButtonAction(seniorButton, "高中"); - primaryButton.setOnAction(e -> { - String selectedLevel = "小学"; - sceneManager.getQuestionCountView().setLevel(selectedLevel); - sceneManager.showQuestionCountView(); - }); - - juniorButton.setOnAction(e -> { - String selectedLevel = "初中"; - sceneManager.getQuestionCountView().setLevel(selectedLevel); - sceneManager.showQuestionCountView(); - }); + levelButtonsSection.getChildren().addAll(primaryButton, juniorButton, seniorButton); + return levelButtonsSection; + } - seniorButton.setOnAction(e -> { - String selectedLevel = "高中"; - sceneManager.getQuestionCountView().setLevel(selectedLevel); + /** + * 设置级别按钮的点击事件. + * + * @param button 级别按钮. + * @param level 对应的难度级别. + */ + private void setupLevelButtonAction(Button button, String level) { + button.setOnAction(e -> { + sceneManager.getQuestionCountView().setLevel(level); sceneManager.showQuestionCountView(); }); + } - backButton.setOnAction(e -> { - sceneManager.showMainMenuView(); - }); - - selectionContent.getChildren().addAll( - titleLabel, subtitleLabel, primaryButton, juniorButton, seniorButton, backButton - ); + /** + * 创建返回按钮. + * + * @return Button 返回主菜单按钮. + */ + private Button createBackButton() { + Button backButton = new Button("返回主菜单"); + backButton.setStyle("-fx-background-color: #95a5a6; -fx-text-fill: " + + "white; -fx-font-size: 14px; -fx-background-radius: 8; -fx-padding: 8 20;"); + backButton.setPrefSize(180, 45); + setupButtonHoverEffect(backButton, "#95a5a6", "#7f8c8d"); + backButton.setOnAction(e -> sceneManager.showMainMenuView()); - return selectionContent; + return backButton; } + /** + * 创建级别选择按钮. + * + * @param text 按钮显示文本. + * @param color 按钮正常状态颜色. + * @param hoverColor 按钮悬停状态颜色. + * @return Button 配置好的级别选择按钮. + */ private Button createLevelButton(String text, String color, String hoverColor) { Button button = new Button(text); button.setStyle(String.format( - "-fx-background-color: %s; -fx-text-fill: white; -fx-font-size: 16px; -fx-font-weight: bold; " + - "-fx-background-radius: 12; -fx-padding: 12 30; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 8, 0.3, 2, 2);", + "-fx-background-color: %s; -fx-text-fill: " + + "white; -fx-font-size: 16px; -fx-font-weight: bold;-fx-background-radius: 12;" + + "-fx-padding: 12 30;-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 8, 0.3, 2, 2);", color )); button.setPrefSize(220, 60); - button.setOnMouseEntered(e -> button.setStyle(String.format( - "-fx-background-color: %s; -fx-text-fill: white; -fx-font-size: 16px; -fx-font-weight: bold; " + - "-fx-background-radius: 12; -fx-padding: 12 30; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.3), 10, 0.4, 3, 3);", - hoverColor - ))); - button.setOnMouseExited(e -> button.setStyle(String.format( - "-fx-background-color: %s; -fx-text-fill: white; -fx-font-size: 16px; -fx-font-weight: bold; " + - "-fx-background-radius: 12; -fx-padding: 12 30; -fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 8, 0.3, 2, 2);", - color - ))); + + setupButtonHoverEffect(button, color, hoverColor); + return button; } + /** + * 设置按钮的悬停效果. + * + * @param button 需要设置悬停效果的按钮. + * @param normalColor 正常状态颜色. + * @param hoverColor 悬停状态颜色. + */ + private void setupButtonHoverEffect(Button button, String normalColor, String hoverColor) { + String normalStyle = String.format( + "-fx-background-color: %s; -fx-text-fill: white; -fx-font-size: %s;" + + " -fx-background-radius: %s; -fx-padding: %s; -fx-effect: " + + "dropshadow(gaussian, rgba(0,0,0,0.2), 8, 0.3, 2, 2);", + normalColor, + button.getStyle().contains("16px") ? "16px" : "14px", + button.getStyle().contains("12") ? "12" : "8", + button.getStyle().contains("12 30") ? "12 30" : "8 20" + ); + + String hoverStyle = String.format( + "-fx-background-color: %s; -fx-text-fill: white; -fx-font-size: %s;" + + " -fx-background-radius: %s; -fx-padding: %s; -fx-effect:" + + " dropshadow(gaussian, rgba(0,0,0,0.3), 10, 0.4, 3, 3);", + hoverColor, + button.getStyle().contains("16px") ? "16px" : "14px", + button.getStyle().contains("12") ? "12" : "8", + button.getStyle().contains("12 30") ? "12 30" : "8 20" + ); + + button.setOnMouseEntered(e -> button.setStyle(hoverStyle)); + button.setOnMouseExited(e -> button.setStyle(normalStyle)); + } + + /** + * 获取用户名的首字母,用于头像显示. + * + * @return String 用户名的首字母大写,如果用户名为空则返回"U". + */ private String getFirstLetter() { - return currentUsername != null && !currentUsername.isEmpty() ? - currentUsername.substring(0, 1).toUpperCase() : "U"; + return currentUsername != null && !currentUsername.isEmpty() + ? currentUsername.substring(0, 1).toUpperCase() : "U"; } - // 添加更新用户名的方法 + /** + * 更新用户名显示. + * + * @param username 新的用户名. + */ public void updateUsername(String username) { this.currentUsername = username; if (usernameLabel != null) { @@ -184,6 +299,11 @@ public class LevelSelectionView { } } + /** + * 获取当前场景. + * + * @return Scene 级别选择界面的场景对象. + */ public Scene getScene() { return scene; } diff --git a/src/main/java/com/wsf/mathapp/view/LoginView.java b/src/main/java/com/wsf/mathapp/view/LoginView.java index 69afbd7..1810b61 100644 --- a/src/main/java/com/wsf/mathapp/view/LoginView.java +++ b/src/main/java/com/wsf/mathapp/view/LoginView.java @@ -3,14 +3,20 @@ package com.wsf.mathapp.view; import com.wsf.mathapp.controller.SceneManager; import com.ybw.mathapp.util.Login; import com.ybw.mathapp.util.LoginFileUtils; -import com.ybw.mathapp.util.Register; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; -import javafx.scene.control.*; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.PasswordField; +import javafx.scene.control.TextField; import javafx.scene.layout.VBox; import javafx.scene.text.Font; +/** + * 登录视图类,提供用户登录功能界面. + * 支持用户名或邮箱登录,包含输入验证和登录状态提示. + */ public class LoginView { private Scene scene; private final SceneManager sceneManager; @@ -20,90 +26,206 @@ public class LoginView { private PasswordField passwordField; private Label statusLabel; + /** + * 构造函数,初始化登录视图. + * + * @param sceneManager 场景管理器,用于界面导航. + */ public LoginView(SceneManager sceneManager) { this.sceneManager = sceneManager; createScene(); } + /** + * 创建登录界面的主场景. + */ private void createScene() { VBox root = new VBox(20); root.setPadding(new Insets(40)); root.setAlignment(Pos.CENTER); + // 创建用户名/邮箱输入框 + usernameOrEmailField = createUsernameField(); + + // 创建密码输入框 + passwordField = createPasswordField(); + + // 创建登录按钮 + Button loginButton = createLoginButton(); + + // 创建注册按钮 + Button registerButton = createRegisterButton(); + + // 创建状态标签 + statusLabel = new Label(); + + // 设置按钮事件 + setupLoginButtonAction(loginButton); + setupRegisterButtonAction(registerButton); + + // 添加回车键登录支持 + setupEnterKeySupport(loginButton); + // 创建界面标题 + Label titleLabel = createTitleLabel(); + + // 创建副标题 + Label subtitleLabel = createSubtitleLabel(); + root.getChildren().addAll( + titleLabel, subtitleLabel, usernameOrEmailField, passwordField, + loginButton, registerButton, statusLabel + ); + scene = new Scene(root, 400, 500); + } + + /** + * 创建主标题标签. + * + * @return Label 主标题标签对象. + */ + private Label createTitleLabel() { Label titleLabel = new Label("数学学习软件"); titleLabel.setFont(Font.font(24)); + return titleLabel; + } + /** + * 创建副标题标签. + * + * @return Label 副标题标签对象. + */ + private Label createSubtitleLabel() { Label subtitleLabel = new Label("用户登录"); subtitleLabel.setFont(Font.font(18)); + return subtitleLabel; + } - // 修改提示文本 - usernameOrEmailField = new TextField(); - usernameOrEmailField.setPromptText("请输入用户名或邮箱"); - usernameOrEmailField.setMaxWidth(300); - usernameOrEmailField.setPrefHeight(40); + /** + * 创建用户名/邮箱输入框. + * + * @return TextField 用户名/邮箱输入框对象. + */ + private TextField createUsernameField() { + TextField field = new TextField(); + field.setPromptText("请输入用户名或邮箱"); + field.setMaxWidth(300); + field.setPrefHeight(40); + return field; + } - passwordField = new PasswordField(); - passwordField.setPromptText("请输入密码"); - passwordField.setMaxWidth(300); - passwordField.setPrefHeight(40); + /** + * 创建密码输入框. + * + * @return PasswordField 密码输入框对象. + */ + private PasswordField createPasswordField() { + PasswordField field = new PasswordField(); + field.setPromptText("请输入密码"); + field.setMaxWidth(300); + field.setPrefHeight(40); + return field; + } - Button loginButton = new Button("登录"); - loginButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 14px;"); - loginButton.setPrefSize(300, 40); + /** + * 创建登录按钮. + * + * @return Button 登录按钮对象. + */ + private Button createLoginButton() { + Button button = new Button("登录"); + button.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 14px;"); + button.setPrefSize(300, 40); + return button; + } - Button registerButton = new Button("注册账号"); - registerButton.setStyle("-fx-background-color: #2196F3; -fx-text-fill: white; -fx-font-size: 14px;"); - registerButton.setPrefSize(300, 40); + /** + * 创建注册按钮. + * + * @return Button 注册按钮对象. + */ + private Button createRegisterButton() { + Button button = new Button("注册账号"); + button.setStyle("-fx-background-color: #2196F3; -fx-text-fill: white; -fx-font-size: 14px;"); + button.setPrefSize(300, 40); + return button; + } - statusLabel = new Label(); + /** + * 设置登录按钮的点击事件. + * + * @param loginButton 登录按钮对象. + */ + private void setupLoginButtonAction(Button loginButton) { + loginButton.setOnAction(e -> handleLogin()); + } - loginButton.setOnAction(e -> { - String usernameOrEmail = usernameOrEmailField.getText().trim(); - String password = passwordField.getText(); - - if (usernameOrEmail.isEmpty() || password.isEmpty()) { - showError(statusLabel, "用户名/邮箱和密码不能为空!"); - return; - } - - // 调用后端的登录方法,支持用户名或邮箱登录 - boolean success = Login.login(usernameOrEmail, password); - System.out.println(usernameOrEmail); - System.out.println(password); - if (success) { - showSuccess(statusLabel); - String username; - if (LoginFileUtils.emailFindName(usernameOrEmail) == null){ - username = usernameOrEmail; - } - else { - username = LoginFileUtils.emailFindName(usernameOrEmail); - } - sceneManager.setCurrentUserName(username); - sceneManager.showMainMenuView(); - } else { - showError(statusLabel, "用户名/邮箱或密码错误!"); - } - }); - - registerButton.setOnAction(e -> { - sceneManager.showRegisterView(); - }); + /** + * 设置注册按钮的点击事件. + * + * @param registerButton 注册按钮对象. + */ + private void setupRegisterButtonAction(Button registerButton) { + registerButton.setOnAction(e -> sceneManager.showRegisterView()); + } - // 添加回车键登录支持 + /** + * 设置回车键登录支持. + * + * @param loginButton 登录按钮对象. + */ + private void setupEnterKeySupport(Button loginButton) { usernameOrEmailField.setOnAction(e -> loginButton.fire()); passwordField.setOnAction(e -> loginButton.fire()); + } - root.getChildren().addAll( - titleLabel, subtitleLabel, usernameOrEmailField, passwordField, - loginButton, registerButton, statusLabel - ); + /** + * 处理登录逻辑. + */ + private void handleLogin() { + String usernameOrEmail = usernameOrEmailField.getText().trim(); + String password = passwordField.getText(); - scene = new Scene(root, 400, 500); + if (usernameOrEmail.isEmpty() || password.isEmpty()) { + showError(statusLabel, "用户名/邮箱和密码不能为空!"); + return; + } + + // 调用后端的登录方法,支持用户名或邮箱登录 + boolean success = Login.login(usernameOrEmail, password); + System.out.println(usernameOrEmail); + System.out.println(password); + + if (success) { + handleLoginSuccess(usernameOrEmail); + } else { + showError(statusLabel, "用户名/邮箱或密码错误!"); + } + } + + /** + * 处理登录成功后的逻辑. + * + * @param usernameOrEmail 登录使用的用户名或邮箱. + */ + private void handleLoginSuccess(String usernameOrEmail) { + showSuccess(statusLabel); + String username = getUsernameFromInput(usernameOrEmail); + sceneManager.setCurrentUserName(username); + sceneManager.showMainMenuView(); + } + + /** + * 根据输入获取用户名. + * + * @param usernameOrEmail 用户输入的用户名或邮箱. + * @return String 对应的用户名. + */ + private String getUsernameFromInput(String usernameOrEmail) { + String foundName = LoginFileUtils.emailFindName(usernameOrEmail); + return foundName == null ? usernameOrEmail : foundName; } /** - * 清空所有输入字段和状态信息 + * 清空所有输入字段和状态信息. */ public void clearFields() { usernameOrEmailField.clear(); @@ -111,16 +233,32 @@ public class LoginView { statusLabel.setText(""); } + /** + * 显示错误信息. + * + * @param label 状态标签对象. + * @param message 错误信息内容. + */ private void showError(Label label, String message) { label.setText(message); label.setStyle("-fx-text-fill: red;"); } + /** + * 显示成功信息. + * + * @param label 状态标签对象. + */ private void showSuccess(Label label) { label.setText("登录成功!"); label.setStyle("-fx-text-fill: green;"); } + /** + * 获取当前场景. + * + * @return Scene 登录界面的场景对象. + */ public Scene getScene() { return scene; } diff --git a/src/main/java/com/wsf/mathapp/view/MainMenuView.java b/src/main/java/com/wsf/mathapp/view/MainMenuView.java index 7702c36..cad87b8 100644 --- a/src/main/java/com/wsf/mathapp/view/MainMenuView.java +++ b/src/main/java/com/wsf/mathapp/view/MainMenuView.java @@ -20,6 +20,10 @@ import javafx.scene.text.Font; import javafx.scene.text.FontWeight; import javafx.scene.text.Text; +/** + * 主菜单视图类,提供应用程序的主导航界面. + * 包含用户信息显示、开始练习、修改密码和退出登录等功能. + */ public class MainMenuView { private Scene scene; private final SceneManager sceneManager; @@ -30,12 +34,20 @@ public class MainMenuView { private Text avatarText; private Label welcomeLabel; + /** + * 构造函数,初始化主菜单视图. + * + * @param sceneManager 场景管理器,用于界面导航. + */ public MainMenuView(SceneManager sceneManager) { this.sceneManager = sceneManager; this.currentUsername = sceneManager.getCurrentUserName(); createScene(); } + /** + * 创建主场景,包含用户信息栏和菜单内容区域. + */ private void createScene() { // 创建主容器 VBox mainContainer = new VBox(); @@ -52,111 +64,237 @@ public class MainMenuView { scene = new Scene(mainContainer, 450, 550); } + /** + * 创建用户信息栏,包含用户头像和用户名显示. + * + * @return HBox 用户信息栏布局容器. + */ private HBox createUserInfoBar() { HBox userInfoBar = new HBox(15); userInfoBar.setAlignment(Pos.CENTER_LEFT); userInfoBar.setPadding(new Insets(0, 0, 30, 0)); - userInfoBar.setStyle("-fx-border-color: #e0e0e0; -fx-border-width: 0 0 1 0; -fx-padding: 0 0 15 0;"); + userInfoBar.setStyle("-fx-border-color: #e0e0e0; -fx-border-width: 0 0 1 0; " + + "-fx-padding: 0 0 15 0;"); - // 创建圆形头像容器 + // 用户名标签 + usernameLabel = new Label(currentUsername != null ? currentUsername : "用户"); + usernameLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16)); + usernameLabel.setStyle("-fx-text-fill: #2c3e50;"); + + // 间隔 + Region spacer = new Region(); + HBox.setHgrow(spacer, Priority.ALWAYS); + // 创建头像容器 + VBox avatarContainer = createAvatarContainer(); + + userInfoBar.getChildren().addAll(avatarContainer, usernameLabel, spacer); + + return userInfoBar; + } + + /** + * 创建头像容器,包含圆形头像背景和用户首字母. + * + * @return VBox 头像容器布局. + */ + private VBox createAvatarContainer() { VBox avatarContainer = new VBox(); avatarContainer.setAlignment(Pos.CENTER); avatarContainer.setPrefSize(50, 50); - // 创建圆形头像背景 - Circle avatarCircle = new Circle(22); - avatarCircle.setFill(Color.web("#4CAF50")); - avatarCircle.setStroke(Color.WHITE); - avatarCircle.setStrokeWidth(2); - - // 添加阴影效果 - avatarCircle.setStyle("-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), 5, 0.3, 2, 2);"); - // 添加首字母文本 avatarText = new Text(getFirstLetter()); avatarText.setFill(Color.WHITE); avatarText.setFont(Font.font("Arial", FontWeight.BOLD, 16)); + // 创建圆形头像背景 + Circle avatarCircle = createAvatarCircle(); + // 使用StackPane将文本放在圆形中心 - javafx.scene.layout.StackPane avatarStack = new javafx.scene.layout.StackPane(); + javafx.scene.layout.StackPane avatarStack = + new javafx.scene.layout.StackPane(); avatarStack.getChildren().addAll(avatarCircle, avatarText); avatarStack.setPrefSize(44, 44); avatarStack.setAlignment(Pos.CENTER); avatarContainer.getChildren().add(avatarStack); + return avatarContainer; + } - // 用户名标签 - usernameLabel = new Label(currentUsername != null ? currentUsername : "用户"); - usernameLabel.setFont(Font.font("Arial", FontWeight.BOLD, 16)); - usernameLabel.setStyle("-fx-text-fill: #2c3e50;"); - - // 间隔 - Region spacer = new Region(); - HBox.setHgrow(spacer, Priority.ALWAYS); - - userInfoBar.getChildren().addAll(avatarContainer, usernameLabel, spacer); + /** + * 创建圆形头像背景. + * + * @return Circle 圆形头像背景对象. + */ + private Circle createAvatarCircle() { + Circle avatarCircle = new Circle(22); + avatarCircle.setFill(Color.web("#4CAF50")); + avatarCircle.setStroke(Color.WHITE); + avatarCircle.setStrokeWidth(2); - return userInfoBar; + // 添加阴影效果 + avatarCircle.setStyle("-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.2), " + + "5, 0.3, 2, 2);"); + return avatarCircle; } + /** + * 创建主菜单内容区域. + * + * @return VBox 菜单内容区域布局容器. + */ private VBox createMenuContent() { VBox menuContent = new VBox(25); menuContent.setPadding(new Insets(20)); menuContent.setAlignment(Pos.CENTER); + // 创建标题区域 + VBox titleSection = createTitleSection(); + + // 创建功能按钮区域 + VBox buttonSection = createButtonSection(); + + menuContent.getChildren().addAll(titleSection, buttonSection); + + return menuContent; + } + + /** + * 创建标题区域,包含主标题和欢迎信息. + * + * @return VBox 标题区域布局容器. + */ + private VBox createTitleSection() { + VBox titleSection = new VBox(10); + titleSection.setAlignment(Pos.CENTER); + Label titleLabel = new Label("数学学习软件"); titleLabel.setFont(Font.font(28)); titleLabel.setStyle("-fx-text-fill: #2c3e50;"); // 修改欢迎标签,显示用户名 - welcomeLabel = new Label("欢迎," + (currentUsername != null ? currentUsername : "用户") + "!"); + welcomeLabel = new Label("欢迎," + + (currentUsername != null ? currentUsername : "用户") + "!"); welcomeLabel.setFont(Font.font(18)); welcomeLabel.setStyle("-fx-text-fill: #7f8c8d;"); - Button startButton = new Button("开始练习"); - startButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 16px; -fx-background-radius: 10;"); - startButton.setPrefSize(220, 55); - startButton.setOnMouseEntered(e -> startButton.setStyle("-fx-background-color: #45a049; -fx-text-fill: white; -fx-font-size: 16px; -fx-background-radius: 10;")); - startButton.setOnMouseExited(e -> startButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 16px; -fx-background-radius: 10;")); - - Button changePasswordButton = new Button("修改密码"); - changePasswordButton.setStyle("-fx-background-color: #2196F3; -fx-text-fill: white; -fx-font-size: 16px; -fx-background-radius: 10;"); - changePasswordButton.setPrefSize(220, 55); - changePasswordButton.setOnMouseEntered(e -> changePasswordButton.setStyle("-fx-background-color: #1976D2; -fx-text-fill: white; -fx-font-size: 16px; -fx-background-radius: 10;")); - changePasswordButton.setOnMouseExited(e -> changePasswordButton.setStyle("-fx-background-color: #2196F3; -fx-text-fill: white; -fx-font-size: 16px; -fx-background-radius: 10;")); - - Button logoutButton = new Button("退出登录"); - logoutButton.setStyle("-fx-background-color: #f44336; -fx-text-fill: white; -fx-font-size: 16px; -fx-background-radius: 10;"); - logoutButton.setPrefSize(220, 55); - logoutButton.setOnMouseEntered(e -> logoutButton.setStyle("-fx-background-color: #d32f2f; -fx-text-fill: white; -fx-font-size: 16px; -fx-background-radius: 10;")); - logoutButton.setOnMouseExited(e -> logoutButton.setStyle("-fx-background-color: #f44336; -fx-text-fill: white; -fx-font-size: 16px; -fx-background-radius: 10;")); - - startButton.setOnAction(e -> { - sceneManager.showLevelSelectionView(); - }); + titleSection.getChildren().addAll(titleLabel, welcomeLabel); + return titleSection; + } - changePasswordButton.setOnAction(e -> { - showChangePasswordDialog(); - }); + /** + * 创建功能按钮区域. + * + * @return VBox 按钮区域布局容器. + */ + private VBox createButtonSection() { + VBox buttonSection = new VBox(15); + buttonSection.setAlignment(Pos.CENTER); - logoutButton.setOnAction(e -> { - sceneManager.showLoginView(); - }); + Button startButton = createStartButton(); + Button changePasswordButton = createChangePasswordButton(); + Button logoutButton = createLogoutButton(); + + setupButtonActions(startButton, changePasswordButton, logoutButton); - menuContent.getChildren().addAll( - titleLabel, welcomeLabel, startButton, - changePasswordButton, logoutButton + buttonSection.getChildren().addAll( + startButton, changePasswordButton, logoutButton ); - return menuContent; + return buttonSection; + } + + /** + * 创建开始练习按钮. + * + * @return Button 开始练习按钮对象. + */ + private Button createStartButton() { + Button button = new Button("开始练习"); + button.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; " + + "-fx-font-size: 16px; -fx-background-radius: 10;"); + button.setPrefSize(220, 55); + setupButtonHoverEffect(button, "#4CAF50", "#45a049"); + return button; + } + + /** + * 创建修改密码按钮. + * + * @return Button 修改密码按钮对象. + */ + private Button createChangePasswordButton() { + Button button = new Button("修改密码"); + button.setStyle("-fx-background-color: #2196F3; -fx-text-fill: white; " + + "-fx-font-size: 16px; -fx-background-radius: 10;"); + button.setPrefSize(220, 55); + setupButtonHoverEffect(button, "#2196F3", "#1976D2"); + return button; + } + + /** + * 创建退出登录按钮. + * + * @return Button 退出登录按钮对象. + */ + private Button createLogoutButton() { + Button button = new Button("退出登录"); + button.setStyle("-fx-background-color: #f44336; -fx-text-fill: white; " + + "-fx-font-size: 16px; -fx-background-radius: 10;"); + button.setPrefSize(220, 55); + setupButtonHoverEffect(button, "#f44336", "#d32f2f"); + return button; } + /** + * 设置按钮的悬停效果. + * + * @param button 需要设置悬停效果的按钮. + * @param normalColor 正常状态颜色. + * @param hoverColor 悬停状态颜色. + */ + private void setupButtonHoverEffect(Button button, String normalColor, + String hoverColor) { + String normalStyle = String.format( + "-fx-background-color: %s; -fx-text-fill: white; -fx-font-size: 16px; " + + "-fx-background-radius: 10;", normalColor); + String hoverStyle = String.format( + "-fx-background-color: %s; -fx-text-fill: white; -fx-font-size: 16px; " + + "-fx-background-radius: 10;", hoverColor); + + button.setOnMouseEntered(e -> button.setStyle(hoverStyle)); + button.setOnMouseExited(e -> button.setStyle(normalStyle)); + } + + /** + * 设置按钮的点击事件. + * + * @param startButton 开始练习按钮. + * @param changePasswordButton 修改密码按钮. + * @param logoutButton 退出登录按钮. + */ + private void setupButtonActions(Button startButton, + Button changePasswordButton, Button logoutButton) { + startButton.setOnAction(e -> sceneManager.showLevelSelectionView()); + changePasswordButton.setOnAction(e -> showChangePasswordDialog()); + logoutButton.setOnAction(e -> sceneManager.showLoginView()); + } + + /** + * 获取用户名的首字母,用于头像显示. + * + * @return String 用户名的首字母大写,如果用户名为空则返回"U". + */ private String getFirstLetter() { - return currentUsername != null && !currentUsername.isEmpty() ? - currentUsername.substring(0, 1).toUpperCase() : "U"; + return currentUsername != null && !currentUsername.isEmpty() + ? currentUsername.substring(0, 1).toUpperCase() : "U"; } - // 添加更新用户名的方法 + /** + * 更新用户名显示. + * + * @param username 新的用户名. + */ public void updateUsername(String username) { this.currentUsername = username; if (usernameLabel != null) { @@ -170,28 +308,60 @@ public class MainMenuView { } } + /** + * 显示修改密码对话框. + */ private void showChangePasswordDialog() { - // 创建修改密码对话框 - javafx.scene.control.Dialog dialog = new javafx.scene.control.Dialog<>(); + javafx.scene.control.Dialog dialog = + new javafx.scene.control.Dialog<>(); dialog.setTitle("修改密码"); dialog.setHeaderText("请输入密码信息"); dialog.setGraphic(null); // 移除默认图标 // 创建表单 + javafx.scene.layout.GridPane grid = createPasswordForm(); + dialog.getDialogPane().setContent(grid); + + // 添加按钮 + dialog.getDialogPane().getButtonTypes().addAll( + javafx.scene.control.ButtonType.OK, + javafx.scene.control.ButtonType.CANCEL + ); + + dialog.setResultConverter(dialogButton -> { + if (dialogButton == javafx.scene.control.ButtonType.OK) { + handlePasswordChange(grid); + } + return null; + }); + + dialog.showAndWait(); + } + + /** + * 创建密码修改表单. + * + * @return GridPane 密码修改表单布局. + */ + private javafx.scene.layout.GridPane createPasswordForm() { javafx.scene.layout.GridPane grid = new javafx.scene.layout.GridPane(); grid.setHgap(15); grid.setVgap(15); grid.setPadding(new Insets(20, 30, 10, 30)); - javafx.scene.control.PasswordField oldPassword = new javafx.scene.control.PasswordField(); + javafx.scene.control.PasswordField oldPassword = + new javafx.scene.control.PasswordField(); oldPassword.setPromptText("请输入原密码"); oldPassword.setPrefWidth(200); - javafx.scene.control.PasswordField newPassword = new javafx.scene.control.PasswordField(); - newPassword.setPromptText("请输入新密码(密码要6-10位,包含大,小写字母和数字)"); + javafx.scene.control.PasswordField newPassword = + new javafx.scene.control.PasswordField(); + newPassword.setPromptText("请输入新密码(密码要6-10位," + + "包含大,小写字母和数字)"); newPassword.setPrefWidth(200); - javafx.scene.control.PasswordField confirmPassword = new javafx.scene.control.PasswordField(); + javafx.scene.control.PasswordField confirmPassword = + new javafx.scene.control.PasswordField(); confirmPassword.setPromptText("请确认新密码"); confirmPassword.setPrefWidth(200); @@ -202,52 +372,76 @@ public class MainMenuView { grid.add(new Label("确认密码:"), 0, 2); grid.add(confirmPassword, 1, 2); - dialog.getDialogPane().setContent(grid); + return grid; + } - // 添加按钮 - dialog.getDialogPane().getButtonTypes().addAll( - javafx.scene.control.ButtonType.OK, - javafx.scene.control.ButtonType.CANCEL - ); + /** + * 处理密码修改逻辑. + * + * @param grid 包含密码输入框的表单布局. + */ + private void handlePasswordChange(javafx.scene.layout.GridPane grid) { + javafx.scene.control.PasswordField oldPassword = + (javafx.scene.control.PasswordField) grid.getChildren().get(1); + javafx.scene.control.PasswordField newPassword = + (javafx.scene.control.PasswordField) grid.getChildren().get(3); + javafx.scene.control.PasswordField confirmPassword = + (javafx.scene.control.PasswordField) grid.getChildren().get(5); + + String oldPwd = oldPassword.getText(); + String newPwd = newPassword.getText(); + String confirmPwd = confirmPassword.getText(); + + if (!validatePasswordInput(oldPwd, newPwd, confirmPwd)) { + return; + } - dialog.setResultConverter(dialogButton -> { - if (dialogButton == javafx.scene.control.ButtonType.OK) { - // 这里可以添加修改密码的逻辑 - String oldPwd = oldPassword.getText(); - String newPwd = newPassword.getText(); - String confirmPwd = confirmPassword.getText(); - - if (oldPwd.isEmpty() || newPwd.isEmpty() || confirmPwd.isEmpty()) { - showAlert("错误", "请填写所有密码字段!"); - return null; - } - if (!Login.login(currentUsername,oldPwd)){ - showAlert("错误", "原密码错误!"); - return null; - } - if (!newPwd.equals(confirmPwd)) { - showAlert("错误", "两次输入的新密码不一致!"); - return null; - } - if (!Register.isVaildPassword(newPwd)) { - showAlert("错误", "密码要6-10位,包含大,小写字母和数字"); - return null; - } - // 调用修改密码的后端逻辑 - if(!ChangePassword.changePassword(currentUsername, confirmPwd)){ - showAlert("错误", "密码修改失败"); - return null; - } - else{ - showAlert("成功", "密码修改成功!"); - } - } - return null; - }); + if (!Login.login(currentUsername, oldPwd)) { + showAlert("错误", "原密码错误!"); + return; + } - dialog.showAndWait(); + if (!Register.isVaildPassword(newPwd)) { + showAlert("错误", "密码要6-10位,包含大,小写字母和数字"); + return; + } + + if (!ChangePassword.changePassword(currentUsername, confirmPwd)) { + showAlert("错误", "密码修改失败"); + } else { + showAlert("成功", "密码修改成功!"); + } + } + + /** + * 验证密码输入. + * + * @param oldPwd 原密码. + * @param newPwd 新密码. + * @param confirmPwd 确认密码. + * @return boolean 输入是否有效. + */ + private boolean validatePasswordInput(String oldPwd, String newPwd, + String confirmPwd) { + if (oldPwd.isEmpty() || newPwd.isEmpty() || confirmPwd.isEmpty()) { + showAlert("错误", "请填写所有密码字段!"); + return false; + } + + if (!newPwd.equals(confirmPwd)) { + showAlert("错误", "两次输入的新密码不一致!"); + return false; + } + + return true; } + /** + * 显示提示对话框. + * + * @param title 对话框标题. + * @param message 对话框内容. + */ private void showAlert(String title, String message) { Alert alert = new Alert(Alert.AlertType.INFORMATION); alert.setTitle(title); @@ -256,6 +450,11 @@ public class MainMenuView { alert.showAndWait(); } + /** + * 获取当前场景. + * + * @return Scene 主菜单界面的场景对象. + */ public Scene getScene() { return scene; } diff --git a/src/main/java/com/wsf/mathapp/view/QuestionCountView.java b/src/main/java/com/wsf/mathapp/view/QuestionCountView.java index 902476e..3b83f6f 100644 --- a/src/main/java/com/wsf/mathapp/view/QuestionCountView.java +++ b/src/main/java/com/wsf/mathapp/view/QuestionCountView.java @@ -4,82 +4,191 @@ import com.wsf.mathapp.controller.SceneManager; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; -import javafx.scene.control.*; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; import javafx.scene.layout.VBox; import javafx.scene.text.Font; +/** + * 题目数量选择视图类,提供用户选择答题数量的界面. + * 允许用户输入10-30之间的题目数量,并开始答题. + */ public class QuestionCountView { private Scene scene; private final SceneManager sceneManager; private String level; + /** + * 构造函数,初始化题目数量选择视图. + * + * @param sceneManager 场景管理器,用于界面导航. + */ public QuestionCountView(SceneManager sceneManager) { this.sceneManager = sceneManager; createScene(); } + /** + * 创建主场景,包含题目数量选择界面. + */ private void createScene() { VBox root = new VBox(20); root.setPadding(new Insets(40)); root.setAlignment(Pos.CENTER); + // 创建标题标签 + Label titleLabel = createTitleLabel(); + + // 创建级别显示标签 + Label levelLabel = createLevelLabel(); + + // 创建数量输入框 + TextField countField = createCountField(); + + // 创建开始答题按钮 + Button startButton = createStartButton(); + + // 创建返回按钮 + Button backButton = createBackButton(); + + // 创建状态标签 + Label statusLabel = new Label(); + + // 设置按钮事件 + setupStartButtonAction(startButton, countField, statusLabel); + setupBackButtonAction(backButton); + + root.getChildren().addAll( + titleLabel, levelLabel, countField, startButton, backButton, statusLabel + ); + + scene = new Scene(root, 400, 400); + } + + /** + * 创建标题标签. + * + * @return Label 标题标签对象. + */ + private Label createTitleLabel() { Label titleLabel = new Label("选择题目数量"); titleLabel.setFont(Font.font(20)); + return titleLabel; + } + /** + * 创建级别显示标签. + * + * @return Label 级别显示标签对象. + */ + private Label createLevelLabel() { Label levelLabel = new Label(); levelLabel.setFont(Font.font(16)); + return levelLabel; + } + /** + * 创建数量输入框. + * + * @return TextField 数量输入框对象. + */ + private TextField createCountField() { TextField countField = new TextField(); countField.setPromptText("请输入题目数量 (10-30)"); countField.setMaxWidth(250); countField.setPrefHeight(40); + return countField; + } + /** + * 创建开始答题按钮. + * + * @return Button 开始答题按钮对象. + */ + private Button createStartButton() { Button startButton = new Button("开始答题"); - startButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 14px;"); + startButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; " + + "-fx-font-size: 14px;"); startButton.setPrefSize(250, 45); + return startButton; + } + /** + * 创建返回按钮. + * + * @return Button 返回按钮对象. + */ + private Button createBackButton() { Button backButton = new Button("返回"); backButton.setStyle("-fx-background-color: #757575; -fx-text-fill: white;"); backButton.setPrefSize(250, 40); + return backButton; + } - Label statusLabel = new Label(); + /** + * 设置开始答题按钮的点击事件. + * + * @param startButton 开始答题按钮. + * @param countField 数量输入框. + * @param statusLabel 状态标签. + */ + private void setupStartButtonAction(Button startButton, TextField countField, + Label statusLabel) { + startButton.setOnAction(e -> handleStartQuiz(countField, statusLabel)); + } + + /** + * 设置返回按钮的点击事件. + * + * @param backButton 返回按钮. + */ + private void setupBackButtonAction(Button backButton) { + backButton.setOnAction(e -> sceneManager.showLevelSelectionView()); + } - startButton.setOnAction(e -> { - try { - String countText = countField.getText().trim(); - if (countText.isEmpty()) { - showError(statusLabel, "请输入题目数量!"); - return; - } - - int count = Integer.parseInt(countText); - if (count < 10 || count > 30) { - showError(statusLabel, "题目数量必须在10-30之间!"); - return; - } - - // 直接调用 showQuizView 并传递参数 - sceneManager.showQuizView(level, count); - - } catch (NumberFormatException ex) { - showError(statusLabel, "请输入有效的数字!"); + /** + * 处理开始答题逻辑. + * + * @param countField 数量输入框. + * @param statusLabel 状态标签. + */ + private void handleStartQuiz(TextField countField, Label statusLabel) { + try { + String countText = countField.getText().trim(); + if (countText.isEmpty()) { + showError(statusLabel, "请输入题目数量!"); + return; } - }); - backButton.setOnAction(e -> { - sceneManager.showLevelSelectionView(); - }); + int count = Integer.parseInt(countText); + if (count < 10 || count > 30) { + showError(statusLabel, "题目数量必须在10-30之间!"); + return; + } - root.getChildren().addAll( - titleLabel, levelLabel, countField, startButton, backButton, statusLabel - ); + // 直接调用 showQuizView 并传递参数 + sceneManager.showQuizView(level, count); - scene = new Scene(root, 400, 400); + } catch (NumberFormatException ex) { + showError(statusLabel, "请输入有效的数字!"); + } } + /** + * 设置当前选择的题目级别. + * + * @param level 题目难度级别. + */ public void setLevel(String level) { this.level = level; - // 更新界面显示当前选择的级别 + updateLevelDisplay(); + } + + /** + * 更新界面显示当前选择的级别. + */ + private void updateLevelDisplay() { if (scene != null) { VBox root = (VBox) scene.getRoot(); Label levelLabel = (Label) root.getChildren().get(1); @@ -88,11 +197,22 @@ public class QuestionCountView { } } + /** + * 显示错误信息. + * + * @param label 状态标签对象. + * @param message 错误信息内容. + */ private void showError(Label label, String message) { label.setText(message); label.setStyle("-fx-text-fill: red;"); } + /** + * 获取当前场景. + * + * @return Scene 题目数量选择界面的场景对象. + */ public Scene getScene() { return scene; } diff --git a/src/main/java/com/wsf/mathapp/view/QuizView.java b/src/main/java/com/wsf/mathapp/view/QuizView.java index 529b0a9..4382fee 100644 --- a/src/main/java/com/wsf/mathapp/view/QuizView.java +++ b/src/main/java/com/wsf/mathapp/view/QuizView.java @@ -5,18 +5,25 @@ import com.wsf.mathapp.service.QuestionService; import com.ybw.mathapp.entity.QuestionWithOptions; import com.ybw.mathapp.service.MultipleChoiceGenerator; import com.ybw.mathapp.service.QuestionGenerator; +import java.util.List; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; -import javafx.scene.control.*; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.RadioButton; +import javafx.scene.control.ToggleGroup; import javafx.scene.layout.VBox; import javafx.scene.text.Font; -import java.util.List; + +/** + * 答题视图类,提供用户答题的界面. + * 显示题目、选项,并处理用户的答题逻辑和进度跟踪. + */ public class QuizView { private Scene scene; private final SceneManager sceneManager; - private MultipleChoiceGenerator multipleChoiceGenerator = null; private String currentLevel; private int questionCount; private List questions; @@ -29,43 +36,105 @@ public class QuizView { private Label progressLabel; private Label questionNumberLabel; + /** + * 构造函数,初始化答题视图. + * + * @param sceneManager 场景管理器,用于界面导航. + */ public QuizView(SceneManager sceneManager) { this.sceneManager = sceneManager; createScene(); } + /** + * 创建主场景,包含答题界面的所有组件. + */ private void createScene() { VBox root = new VBox(20); root.setPadding(new Insets(30)); root.setAlignment(Pos.TOP_CENTER); - progressLabel = new Label(); - progressLabel.setFont(Font.font(14)); + // 创建界面组件 + progressLabel = createProgressLabel(); + questionNumberLabel = createQuestionNumberLabel(); + questionLabel = createQuestionLabel(); + optionsContainer = createOptionsContainer(); + nextButton = createNextButton(); - questionNumberLabel = new Label(); - questionNumberLabel.setFont(Font.font(16)); - questionNumberLabel.setStyle("-fx-text-fill: #666;"); + root.getChildren().addAll(progressLabel, questionNumberLabel, + questionLabel, optionsContainer, nextButton); - questionLabel = new Label(); - questionLabel.setFont(Font.font(18)); - questionLabel.setWrapText(true); - questionLabel.setStyle("-fx-padding: 10 0 20 0;"); + scene = new Scene(root, 600, 500); + } - optionsGroup = new ToggleGroup(); - optionsContainer = new VBox(15); - optionsContainer.setAlignment(Pos.CENTER_LEFT); - optionsContainer.setPadding(new Insets(10)); + /** + * 创建进度标签. + * + * @return Label 进度标签对象. + */ + private Label createProgressLabel() { + Label label = new Label(); + label.setFont(Font.font(14)); + return label; + } - nextButton = new Button("下一题"); - nextButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 14px;"); - nextButton.setPrefSize(150, 40); - nextButton.setDisable(true); + /** + * 创建题号标签. + * + * @return Label 题号标签对象. + */ + private Label createQuestionNumberLabel() { + Label label = new Label(); + label.setFont(Font.font(16)); + label.setStyle("-fx-text-fill: #666;"); + return label; + } - root.getChildren().addAll(progressLabel, questionNumberLabel, questionLabel, optionsContainer, nextButton); + /** + * 创建题目标签. + * + * @return Label 题目标签对象. + */ + private Label createQuestionLabel() { + Label label = new Label(); + label.setFont(Font.font(18)); + label.setWrapText(true); + label.setStyle("-fx-padding: 10 0 20 0;"); + return label; + } - scene = new Scene(root, 600, 500); + /** + * 创建选项容器. + * + * @return VBox 选项容器布局. + */ + private VBox createOptionsContainer() { + VBox container = new VBox(15); + container.setAlignment(Pos.CENTER_LEFT); + container.setPadding(new Insets(10)); + return container; } + /** + * 创建下一题按钮. + * + * @return Button 下一题按钮对象. + */ + private Button createNextButton() { + Button button = new Button("下一题"); + button.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; " + + "-fx-font-size: 14px;"); + button.setPrefSize(150, 40); + button.setDisable(true); + return button; + } + + /** + * 设置测验参数并开始测验. + * + * @param level 题目难度级别. + * @param count 题目数量. + */ public void setQuizParameters(String level, int count) { System.out.println("设置测验参数 - 级别: " + level + ", 题目数量: " + count); @@ -87,6 +156,9 @@ public class QuizView { showQuestion(0); } + /** + * 生成题目列表. + */ private void generateQuestions() { try { System.out.println("开始生成题目,级别: " + currentLevel + ", 数量: " + questionCount); @@ -98,20 +170,25 @@ public class QuizView { return; } - System.out.println("题目生成器创建成功: " + questionGenerator.getClass().getSimpleName()); + System.out.println("题目生成器创建成功: " + + questionGenerator.getClass().getSimpleName()); - multipleChoiceGenerator = new MultipleChoiceGenerator(questionGenerator, currentLevel); + MultipleChoiceGenerator multipleChoiceGenerator = new MultipleChoiceGenerator( + questionGenerator, currentLevel); questions = multipleChoiceGenerator.generateMultipleChoiceQuestions(questionCount); - System.out.println("题目生成完成,数量: " + (questions != null ? questions.size() : "null")); + System.out.println("题目生成完成,数量: " + + (questions != null ? questions.size() : "null")); } catch (Exception e) { System.err.println("生成题目时出现异常: " + e.getMessage()); - e.printStackTrace(); questions = java.util.Collections.emptyList(); } } + /** + * 显示空状态界面. + */ private void showEmptyState() { VBox root = (VBox) scene.getRoot(); root.getChildren().clear(); @@ -125,6 +202,11 @@ public class QuizView { root.getChildren().addAll(emptyLabel, backButton); } + /** + * 显示指定索引的题目. + * + * @param index 题目索引. + */ private void showQuestion(int index) { if (index >= questions.size()) { // 所有题目已回答,显示结果 @@ -132,20 +214,49 @@ public class QuizView { sceneManager.showResultView(score); return; } + QuestionWithOptions question = questions.get(index); + updateQuestionDisplay(index, question); + setupOptions(question); + setupNextButton(index, question); + } + /** + * 更新题目显示信息. + * + * @param index 题目索引. + * @param question 题目对象. + */ + private void updateQuestionDisplay(int index, QuestionWithOptions question) { // 更新进度和题号 progressLabel.setText("进度: " + (index + 1) + "/" + questions.size()); questionNumberLabel.setText("第 " + (index + 1) + " 题"); // 设置题目内容 - String questionText = question.getQuestionText(); + String questionText = formatQuestionText(question.getQuestionText()); + questionLabel.setText(questionText + " = ?"); + } + + /** + * 格式化题目文本. + * + * @param questionText 原始题目文本. + * @return String 格式化后的题目文本. + */ + private String formatQuestionText(String questionText) { // 移除末尾的 "=" if (questionText.endsWith(" =")) { questionText = questionText.substring(0, questionText.length() - 2); } - questionLabel.setText(questionText + " = ?"); + return questionText; + } + /** + * 设置选项. + * + * @param question 题目对象. + */ + private void setupOptions(QuestionWithOptions question) { // 清空选项容器 optionsContainer.getChildren().clear(); optionsGroup = new ToggleGroup(); @@ -153,15 +264,34 @@ public class QuizView { // 添加选项 List options = question.getOptions(); for (int i = 0; i < options.size(); i++) { - RadioButton radioButton = new RadioButton((char)('A' + i) + ". " + options.get(i)); - radioButton.setToggleGroup(optionsGroup); - radioButton.setFont(Font.font(14)); - radioButton.setWrapText(true); - radioButton.setPrefWidth(500); + RadioButton radioButton = createOptionRadioButton(i, options.get(i)); optionsContainer.getChildren().add(radioButton); } + } - // 设置下一题按钮 + /** + * 创建选项单选按钮. + * + * @param index 选项索引. + * @param optionText 选项文本. + * @return RadioButton 单选按钮对象. + */ + private RadioButton createOptionRadioButton(int index, String optionText) { + RadioButton radioButton = new RadioButton((char) ('A' + index) + ". " + optionText); + radioButton.setToggleGroup(optionsGroup); + radioButton.setFont(Font.font(14)); + radioButton.setWrapText(true); + radioButton.setPrefWidth(500); + return radioButton; + } + + /** + * 设置下一题按钮. + * + * @param index 当前题目索引. + * @param question 题目对象. + */ + private void setupNextButton(int index, QuestionWithOptions question) { nextButton.setDisable(true); nextButton.setText(index == questions.size() - 1 ? "提交答案" : "下一题"); nextButton.setOnAction(e -> { @@ -170,11 +300,22 @@ public class QuizView { }); // 启用下一题按钮当选择了一个选项时 - optionsGroup.selectedToggleProperty().addListener((obs, oldVal, newVal) -> { - nextButton.setDisable(newVal == null); - }); + setupOptionSelectionListener(); } + /** + * 设置选项选择监听器. + */ + private void setupOptionSelectionListener() { + optionsGroup.selectedToggleProperty().addListener((obs, oldVal, newVal) -> + nextButton.setDisable(newVal == null)); + } + + /** + * 检查答案是否正确. + * + * @param question 题目对象. + */ private void checkAnswer(QuestionWithOptions question) { RadioButton selectedRadioButton = (RadioButton) optionsGroup.getSelectedToggle(); if (selectedRadioButton != null) { @@ -185,11 +326,13 @@ public class QuizView { } } + /** + * 获取当前场景. + * + * @return Scene 答题界面的场景对象. + */ public Scene getScene() { return scene; } - public MultipleChoiceGenerator getMultipleChoiceGenerator() { - return multipleChoiceGenerator; - } } \ No newline at end of file diff --git a/src/main/java/com/wsf/mathapp/view/RegisterView.java b/src/main/java/com/wsf/mathapp/view/RegisterView.java index d1a3867..fa515df 100644 --- a/src/main/java/com/wsf/mathapp/view/RegisterView.java +++ b/src/main/java/com/wsf/mathapp/view/RegisterView.java @@ -2,16 +2,24 @@ package com.wsf.mathapp.view; import com.wsf.mathapp.controller.SceneManager; import com.ybw.mathapp.util.EmailService; +import com.ybw.mathapp.util.LoginFileUtils; import com.ybw.mathapp.util.Register; import java.util.regex.Pattern; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; -import javafx.scene.control.*; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.PasswordField; +import javafx.scene.control.TextField; import javafx.scene.layout.VBox; import javafx.scene.text.Font; -import com.ybw.mathapp.util.LoginFileUtils; + +/** + * 注册视图类,提供用户注册功能的界面. + * 包含用户名、邮箱、验证码、密码等输入字段的验证和注册逻辑. + */ public class RegisterView { private Scene scene; private final SceneManager sceneManager; @@ -25,178 +33,342 @@ public class RegisterView { private Label statusLabel; private Button sendCodeButton; + /** + * 构造函数,初始化注册视图. + * + * @param sceneManager 场景管理器,用于界面导航. + */ public RegisterView(SceneManager sceneManager) { this.sceneManager = sceneManager; createScene(); } + /** + * 创建主场景,包含注册界面的所有组件. + */ private void createScene() { VBox root = new VBox(15); root.setPadding(new Insets(30)); root.setAlignment(Pos.CENTER); + // 创建界面组件 + usernameField = createUsernameField(); + emailField = createEmailField(); + sendCodeButton = createSendCodeButton(); + codeField = createCodeField(); + passwordField = createPasswordField(); + confirmPasswordField = createConfirmPasswordField(); + Button registerButton = createRegisterButton(); + statusLabel = new Label(); + Button backButton = createBackButton(); + // 设置按钮事件 + setupSendCodeButtonAction(); + setupRegisterButtonAction(registerButton); + setupBackButtonAction(backButton); + Label titleLabel = createTitleLabel(); + root.getChildren().addAll( + titleLabel, + usernameField, + emailField, + sendCodeButton, + codeField, + passwordField, + confirmPasswordField, + registerButton, + backButton, + statusLabel + ); + + scene = new Scene(root, 400, 550); + } + + /** + * 创建标题标签. + * + * @return Label 标题标签对象. + */ + private Label createTitleLabel() { Label titleLabel = new Label("用户注册"); titleLabel.setFont(Font.font(20)); + return titleLabel; + } - // 添加用户名输入框 - usernameField = new TextField(); - usernameField.setPromptText("请输入用户名(3-20位字母、数字、下划线)"); - usernameField.setMaxWidth(300); - usernameField.setPrefHeight(35); + /** + * 创建用户名输入框. + * + * @return TextField 用户名输入框对象. + */ + private TextField createUsernameField() { + TextField field = new TextField(); + field.setPromptText("请输入用户名(3-20位字母、数字、下划线)"); + field.setMaxWidth(300); + field.setPrefHeight(35); + return field; + } - emailField = new TextField(); - emailField.setPromptText("请输入邮箱"); - emailField.setMaxWidth(300); - emailField.setPrefHeight(35); + /** + * 创建邮箱输入框. + * + * @return TextField 邮箱输入框对象. + */ + private TextField createEmailField() { + TextField field = new TextField(); + field.setPromptText("请输入邮箱"); + field.setMaxWidth(300); + field.setPrefHeight(35); + return field; + } - sendCodeButton = new Button("发送验证码"); - sendCodeButton.setStyle("-fx-background-color: #FF9800; -fx-text-fill: white;"); - sendCodeButton.setPrefSize(120, 35); + /** + * 创建发送验证码按钮. + * + * @return Button 发送验证码按钮对象. + */ + private Button createSendCodeButton() { + Button button = new Button("发送验证码"); + button.setStyle("-fx-background-color: #FF9800; -fx-text-fill: white;"); + button.setPrefSize(120, 35); + return button; + } - codeField = new TextField(); - codeField.setPromptText("请输入验证码"); - codeField.setMaxWidth(300); - codeField.setPrefHeight(35); + /** + * 创建验证码输入框. + * + * @return TextField 验证码输入框对象. + */ + private TextField createCodeField() { + TextField field = new TextField(); + field.setPromptText("请输入验证码"); + field.setMaxWidth(300); + field.setPrefHeight(35); + return field; + } - passwordField = new PasswordField(); - passwordField.setPromptText("请输入密码(6-10位,含大小写字母和数字)"); - passwordField.setMaxWidth(300); - passwordField.setPrefHeight(35); + /** + * 创建密码输入框. + * + * @return PasswordField 密码输入框对象. + */ + private PasswordField createPasswordField() { + PasswordField field = new PasswordField(); + field.setPromptText("请输入密码(6-10位,含大小写字母和数字)"); + field.setMaxWidth(300); + field.setPrefHeight(35); + return field; + } - confirmPasswordField = new PasswordField(); - confirmPasswordField.setPromptText("请再次输入密码"); - confirmPasswordField.setMaxWidth(300); - confirmPasswordField.setPrefHeight(35); + /** + * 创建确认密码输入框. + * + * @return PasswordField 确认密码输入框对象. + */ + private PasswordField createConfirmPasswordField() { + PasswordField field = new PasswordField(); + field.setPromptText("请再次输入密码"); + field.setMaxWidth(300); + field.setPrefHeight(35); + return field; + } - Button registerButton = new Button("注册"); - registerButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 14px;"); - registerButton.setPrefSize(300, 40); + /** + * 创建注册按钮. + * + * @return Button 注册按钮对象. + */ + private Button createRegisterButton() { + Button button = new Button("注册"); + button.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; " + + "-fx-font-size: 14px;"); + button.setPrefSize(300, 40); + return button; + } - Button backButton = new Button("返回登录"); - backButton.setStyle("-fx-background-color: #757575; -fx-text-fill: white;"); - backButton.setPrefSize(300, 35); + /** + * 创建返回登录按钮. + * + * @return Button 返回登录按钮对象. + */ + private Button createBackButton() { + Button button = new Button("返回登录"); + button.setStyle("-fx-background-color: #757575; -fx-text-fill: white;"); + button.setPrefSize(300, 35); + return button; + } - statusLabel = new Label(); + /** + * 设置发送验证码按钮的点击事件. + */ + private void setupSendCodeButtonAction() { + sendCodeButton.setOnAction(e -> handleSendVerificationCode()); + } - sendCodeButton.setOnAction(e -> { - String username = usernameField.getText().trim(); - String email = emailField.getText().trim(); + /** + * 设置注册按钮的点击事件. + * + * @param registerButton 注册按钮对象. + */ + private void setupRegisterButtonAction(Button registerButton) { + registerButton.setOnAction(e -> handleRegistration()); + } - // 验证用户名 - if (!isValidUsername(username)) { - showError(statusLabel, "用户名只包含英文字母和数字)"); - return; - } + /** + * 设置返回按钮的点击事件. + * + * @param backButton 返回按钮对象. + */ + private void setupBackButtonAction(Button backButton) { + backButton.setOnAction(e -> sceneManager.showLoginView()); + } - // 检查用户名是否已存在 - if (LoginFileUtils.isNameRegistered(username)) { - showError(statusLabel, "用户名已存在,请选择其他用户名!"); - return; - } + /** + * 处理发送验证码逻辑. + */ + private void handleSendVerificationCode() { + String username = usernameField.getText().trim(); + String email = emailField.getText().trim(); - if (!EmailService.isValidEmail(email)) { - showError(statusLabel, "请输入有效的邮箱地址!"); - return; - } + if (!validateUsernameForCode(username)) { + return; + } - if (LoginFileUtils.isEmailRegistered(email)){ - showError(statusLabel, "该邮箱已注册,请直接登录!"); - return; - } + if (!validateEmailForCode(email)) { + return; + } - // 生成并发送验证码 - boolean sent = EmailService.sendCode(email); - if (sent) { - showSuccess(statusLabel, "验证码已发送到您的邮箱!"); - // 禁用发送按钮60秒 - sendCodeButton.setDisable(true); - startCountdown(sendCodeButton); - } else { - showError(statusLabel, "发送验证码失败,请稍后重试!"); - } - }); - - registerButton.setOnAction(e -> { - String username = usernameField.getText().trim(); - String email = emailField.getText().trim(); - String code = codeField.getText().trim(); - String password = passwordField.getText(); - String confirmPassword = confirmPasswordField.getText(); - - // 验证用户名 - if (!isValidUsername(username)) { - showError(statusLabel, "用户名只包含英文字母和数字"); - return; - } + // 生成并发送验证码 + boolean sent = EmailService.sendCode(email); + if (sent) { + showSuccess(statusLabel, "验证码已发送到您的邮箱!"); + // 禁用发送按钮60秒 + sendCodeButton.setDisable(true); + startCountdown(sendCodeButton); + } else { + showError(statusLabel, "发送验证码失败,请稍后重试!"); + } + } - // 检查用户名是否已存在 - if (LoginFileUtils.isNameRegistered((username))){ - showError(statusLabel, "用户名已存在,请选择其他用户名!"); - return; - } + /** + * 验证用户名用于发送验证码. + * + * @param username 用户名. + * @return boolean 用户名是否有效. + */ + private boolean validateUsernameForCode(String username) { + if (isInvalidUsername(username)) { + showError(statusLabel, "用户名只包含英文字母和数字)"); + return false; + } - if (!EmailService.isValidEmail(email)) { - showError(statusLabel, "请输入有效的邮箱地址!"); - return; - } + if (LoginFileUtils.isNameRegistered(username)) { + showError(statusLabel, "用户名已存在,请选择其他用户名!"); + return false; + } - if (LoginFileUtils.isEmailRegistered(email)){ - showError(statusLabel, "该邮箱已注册,请直接登录!"); - return; - } + return true; + } - if (username.isEmpty() || email.isEmpty() || code.isEmpty() || password.isEmpty()) { - showError(statusLabel, "请填写所有字段!"); - return; - } + /** + * 验证邮箱用于发送验证码. + * + * @param email 邮箱地址. + * @return boolean 邮箱是否有效. + */ + private boolean validateEmailForCode(String email) { + if (!EmailService.isValidEmail(email)) { + showError(statusLabel, "请输入有效的邮箱地址!"); + return false; + } - // 验证验证码 - if (!EmailService.verifyCode(email, code)) { - showError(statusLabel, "验证码错误或已过期!"); - return; - } + if (LoginFileUtils.isEmailRegistered(email)) { + showError(statusLabel, "该邮箱已注册,请直接登录!"); + return false; + } - if (!Register.isVaildPassword(password)) { - showError(statusLabel, "密码要6-10位,包含大,小写字母和数字"); - return; - } + return true; + } - if (!Register.isEqualPassword(password,confirmPassword)){ - showError(statusLabel, "两次密码不一致"); - return; - } + /** + * 处理用户注册逻辑. + */ + private void handleRegistration() { + String username = usernameField.getText().trim(); + String email = emailField.getText().trim(); + String code = codeField.getText().trim(); + String password = passwordField.getText(); + String confirmPassword = confirmPasswordField.getText(); + + if (!validateRegistrationInput(username, email, code, password, confirmPassword)) { + return; + } - if (Register.register(username, email, password)) { - showSuccess(statusLabel, "注册成功!用户名: " + username); - sceneManager.setCurrentUserName(username); - sceneManager.showMainMenuView(); - } else { - showError(statusLabel, "注册失败,请检查信息!"); - } - }); + // 验证验证码 + if (!EmailService.verifyCode(email, code)) { + showError(statusLabel, "验证码错误或已过期!"); + return; + } - backButton.setOnAction(e -> { - sceneManager.showLoginView(); - }); + if (!Register.isVaildPassword(password)) { + showError(statusLabel, "密码要6-10位,包含大,小写字母和数字"); + return; + } - root.getChildren().addAll( - titleLabel, - usernameField, // 添加用户名输入框 - emailField, - sendCodeButton, - codeField, - passwordField, - confirmPasswordField, - registerButton, - backButton, - statusLabel - ); + if (!Register.isEqualPassword(password, confirmPassword)) { + showError(statusLabel, "两次密码不一致"); + return; + } + + if (Register.register(username, email, password)) { + showSuccess(statusLabel, "注册成功!用户名: " + username); + sceneManager.setCurrentUserName(username); + sceneManager.showMainMenuView(); + } else { + showError(statusLabel, "注册失败,请检查信息!"); + } + } + + /** + * 验证注册输入字段. + * + * @param username 用户名. + * @param email 邮箱. + * @param code 验证码. + * @param password 密码. + * @param confirmPassword 确认密码. + * @return boolean 输入是否有效. + */ + private boolean validateRegistrationInput(String username, String email, + String code, String password, String confirmPassword) { + if (isInvalidUsername(username)) { + showError(statusLabel, "用户名只包含英文字母和数字"); + return false; + } - scene = new Scene(root, 400, 550); // 增加高度以适应新字段 + if (LoginFileUtils.isNameRegistered(username)) { + showError(statusLabel, "用户名已存在,请选择其他用户名!"); + return false; + } + + if (!EmailService.isValidEmail(email)) { + showError(statusLabel, "请输入有效的邮箱地址!"); + return false; + } + + if (LoginFileUtils.isEmailRegistered(email)) { + showError(statusLabel, "该邮箱已注册,请直接登录!"); + return false; + } + + if (username.isEmpty() || email.isEmpty() || code.isEmpty() + || password.isEmpty() || confirmPassword.isEmpty()) { + showError(statusLabel, "请填写所有字段!"); + return false; + } + + return true; } /** - * 清空所有输入字段和状态信息 + * 清空所有输入字段和状态信息. */ public void clearFields() { usernameField.clear(); @@ -211,14 +383,17 @@ public class RegisterView { sendCodeButton.setDisable(false); } + /** + * 开始倒计时. + * + * @param button 需要倒计时的按钮. + */ private void startCountdown(Button button) { new Thread(() -> { try { for (int i = 60; i > 0; i--) { int finalI = i; - javafx.application.Platform.runLater(() -> { - button.setText(finalI + "秒后重发"); - }); + javafx.application.Platform.runLater(() -> button.setText(finalI + "秒后重发")); Thread.sleep(1000); } javafx.application.Platform.runLater(() -> { @@ -226,31 +401,54 @@ public class RegisterView { button.setDisable(false); }); } catch (InterruptedException ex) { - ex.printStackTrace(); + // ex.printStackTrace(); } }).start(); } + /** + * 显示错误信息. + * + * @param label 状态标签对象. + * @param message 错误信息内容. + */ private void showError(Label label, String message) { label.setText(message); label.setStyle("-fx-text-fill: red;"); } + /** + * 显示成功信息. + * + * @param label 状态标签对象. + * @param message 成功信息内容. + */ private void showSuccess(Label label, String message) { label.setText(message); label.setStyle("-fx-text-fill: green;"); } - public Scene getScene() { - return scene; - } - - public static boolean isValidUsername(String username) { + /** + * 验证用户名格式. + * + * @param username 用户名. + * @return boolean 用户名格式是否有效. + */ + public static boolean isInvalidUsername(String username) { if (username == null || username.trim().isEmpty()) { - return false; + return true; } // 用户名规则:只包含英文字母和数字,不限制长度 String usernameRegex = "^[a-zA-Z0-9]+$"; - return Pattern.matches(usernameRegex, username.trim()); + return !Pattern.matches(usernameRegex, username.trim()); + } + + /** + * 获取当前场景. + * + * @return Scene 注册界面的场景对象. + */ + public Scene getScene() { + return scene; } } \ No newline at end of file diff --git a/src/main/java/com/wsf/mathapp/view/ResultView.java b/src/main/java/com/wsf/mathapp/view/ResultView.java index d4af0db..0e3f705 100644 --- a/src/main/java/com/wsf/mathapp/view/ResultView.java +++ b/src/main/java/com/wsf/mathapp/view/ResultView.java @@ -1,32 +1,37 @@ package com.wsf.mathapp.view; import com.wsf.mathapp.controller.SceneManager; -import java.util.Objects; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.Label; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; import javafx.scene.layout.HBox; -import javafx.scene.layout.Priority; -import javafx.scene.layout.Region; import javafx.scene.layout.VBox; import javafx.scene.text.Font; +/** + * 结果视图类,显示用户答题结果的界面. + * 包含分数显示、评语和导航按钮等功能. + */ public class ResultView { private Scene scene; private final SceneManager sceneManager; private double score; - private String currentUsername; + /** + * 构造函数,初始化结果视图. + * + * @param sceneManager 场景管理器,用于界面导航. + */ public ResultView(SceneManager sceneManager) { this.sceneManager = sceneManager; - this.currentUsername = sceneManager.getCurrentUserName(); createScene(); } + /** + * 创建主场景,包含用户信息栏和结果内容区域. + */ private void createScene() { // 创建主容器 VBox mainContainer = new VBox(); @@ -40,9 +45,14 @@ public class ResultView { mainContainer.getChildren().addAll(userInfoBar, resultContent); - scene = new Scene(mainContainer, 500, 550); // 增加宽度和高度以适应新布局 + scene = new Scene(mainContainer, 500, 550); } + /** + * 创建用户信息栏. + * + * @return HBox 用户信息栏布局容器. + */ private HBox createUserInfoBar() { HBox userInfoBar = new HBox(10); userInfoBar.setAlignment(Pos.CENTER_LEFT); @@ -50,13 +60,49 @@ public class ResultView { return userInfoBar; } + /** + * 创建结果内容区域. + * + * @return VBox 结果内容区域布局容器. + */ private VBox createResultContent() { VBox resultContent = new VBox(30); resultContent.setPadding(new Insets(20)); resultContent.setAlignment(Pos.CENTER); + // 创建标题区域 + Label titleLabel = createTitleLabel(); + + // 创建分数显示区域 + VBox scoreSection = createScoreSection(); + + // 创建按钮区域 + VBox buttonSection = createButtonSection(); + + resultContent.getChildren().addAll(titleLabel, scoreSection, buttonSection); + + return resultContent; + } + + /** + * 创建标题标签. + * + * @return Label 标题标签对象. + */ + private Label createTitleLabel() { Label titleLabel = new Label("答题完成"); titleLabel.setFont(Font.font(24)); + return titleLabel; + } + + /** + * 创建分数显示区域. + * + * @return VBox 分数显示区域布局容器. + */ + private VBox createScoreSection() { + VBox scoreSection = new VBox(15); + scoreSection.setAlignment(Pos.CENTER); Label scoreLabel = new Label(); scoreLabel.setFont(Font.font(48)); @@ -64,73 +110,169 @@ public class ResultView { Label messageLabel = new Label(); messageLabel.setFont(Font.font(18)); - Button restartButton = new Button("再次练习"); - restartButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 16px;"); - restartButton.setPrefSize(200, 50); + scoreSection.getChildren().addAll(scoreLabel, messageLabel); + return scoreSection; + } - Button mainMenuButton = new Button("返回主菜单"); - mainMenuButton.setStyle("-fx-background-color: #2196F3; -fx-text-fill: white; -fx-font-size: 16px;"); - mainMenuButton.setPrefSize(200, 50); + /** + * 创建按钮区域. + * + * @return VBox 按钮区域布局容器. + */ + private VBox createButtonSection() { + VBox buttonSection = new VBox(15); + buttonSection.setAlignment(Pos.CENTER); - Button exitButton = new Button("退出系统"); - exitButton.setStyle("-fx-background-color: #757575; -fx-text-fill: white; -fx-font-size: 16px;"); - exitButton.setPrefSize(200, 50); + Button restartButton = createRestartButton(); + Button mainMenuButton = createMainMenuButton(); + Button exitButton = createExitButton(); - restartButton.setOnAction(e -> { - sceneManager.showLevelSelectionView(); - }); + setupButtonActions(restartButton, mainMenuButton, exitButton); - mainMenuButton.setOnAction(e -> { - sceneManager.showMainMenuView(); - }); + buttonSection.getChildren().addAll(restartButton, mainMenuButton, exitButton); + return buttonSection; + } - exitButton.setOnAction(e -> { - System.exit(0); - }); + /** + * 创建再次练习按钮. + * + * @return Button 再次练习按钮对象. + */ + private Button createRestartButton() { + Button button = new Button("再次练习"); + button.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; " + + "-fx-font-size: 16px;"); + button.setPrefSize(200, 50); + return button; + } - resultContent.getChildren().addAll( - titleLabel, scoreLabel, messageLabel, - restartButton, mainMenuButton, exitButton - ); + /** + * 创建返回主菜单按钮. + * + * @return Button 返回主菜单按钮对象. + */ + private Button createMainMenuButton() { + Button button = new Button("返回主菜单"); + button.setStyle("-fx-background-color: #2196F3; -fx-text-fill: white; " + + "-fx-font-size: 16px;"); + button.setPrefSize(200, 50); + return button; + } - return resultContent; + /** + * 创建退出系统按钮. + * + * @return Button 退出系统按钮对象. + */ + private Button createExitButton() { + Button button = new Button("退出系统"); + button.setStyle("-fx-background-color: #757575; -fx-text-fill: white; " + + "-fx-font-size: 16px;"); + button.setPrefSize(200, 50); + return button; + } + + /** + * 设置按钮的点击事件. + * + * @param restartButton 再次练习按钮. + * @param mainMenuButton 返回主菜单按钮. + * @param exitButton 退出系统按钮. + */ + private void setupButtonActions(Button restartButton, Button mainMenuButton, + Button exitButton) { + restartButton.setOnAction(e -> sceneManager.showLevelSelectionView()); + mainMenuButton.setOnAction(e -> sceneManager.showMainMenuView()); + exitButton.setOnAction(e -> System.exit(0)); } + /** + * 设置分数并更新显示. + * + * @param score 用户得分(0-1之间的比例). + */ public void setScore(double score) { this.score = score; updateScoreDisplay(); } - + /** + * 更新分数显示. + */ private void updateScoreDisplay() { if (scene != null) { VBox mainContainer = (VBox) scene.getRoot(); VBox resultContent = (VBox) mainContainer.getChildren().get(1); - Label scoreLabel = (Label) resultContent.getChildren().get(1); - Label messageLabel = (Label) resultContent.getChildren().get(2); - - int percentage = (int) (score * 100); - scoreLabel.setText(percentage + "分"); - - // 根据分数设置不同的颜色和评语 - if (score >= 0.9) { - scoreLabel.setStyle("-fx-text-fill: #4CAF50;"); - messageLabel.setText("优秀!表现非常出色!"); - } else if (score >= 0.7) { - scoreLabel.setStyle("-fx-text-fill: #FF9800;"); - messageLabel.setText("良好!继续加油!"); - } else if (score >= 0.6) { - scoreLabel.setStyle("-fx-text-fill: #FFC107;"); - messageLabel.setText("及格!还有进步空间!"); - } else { - scoreLabel.setStyle("-fx-text-fill: #f44336;"); - messageLabel.setText("需要多加练习!"); - } + VBox scoreSection = (VBox) resultContent.getChildren().get(1); + + Label scoreLabel = (Label) scoreSection.getChildren().get(0); + Label messageLabel = (Label) scoreSection.getChildren().get(1); + + updateScoreLabel(scoreLabel); + updateMessageLabel(messageLabel); } } + /** + * 更新分数标签. + * + * @param scoreLabel 分数标签对象. + */ + private void updateScoreLabel(Label scoreLabel) { + int percentage = (int) (score * 100); + scoreLabel.setText(percentage + "分"); + applyScoreColor(scoreLabel); + } + + /** + * 更新评语标签. + * + * @param messageLabel 评语标签对象. + */ + private void updateMessageLabel(Label messageLabel) { + messageLabel.setText(getScoreMessage()); + } + + /** + * 根据分数应用相应的颜色. + * + * @param scoreLabel 分数标签对象. + */ + private void applyScoreColor(Label scoreLabel) { + if (score >= 0.9) { + scoreLabel.setStyle("-fx-text-fill: #4CAF50;"); + } else if (score >= 0.7) { + scoreLabel.setStyle("-fx-text-fill: #FF9800;"); + } else if (score >= 0.6) { + scoreLabel.setStyle("-fx-text-fill: #FFC107;"); + } else { + scoreLabel.setStyle("-fx-text-fill: #f44336;"); + } + } + + /** + * 根据分数获取相应的评语. + * + * @return String 对应的评语. + */ + private String getScoreMessage() { + if (score >= 0.9) { + return "优秀!表现非常出色!"; + } else if (score >= 0.7) { + return "良好!继续加油!"; + } else if (score >= 0.6) { + return "及格!还有进步空间!"; + } else { + return "需要多加练习!"; + } + } + + /** + * 获取当前场景. + * + * @return Scene 结果界面的场景对象. + */ public Scene getScene() { return scene; } - } \ No newline at end of file