diff --git a/pom.xml b/pom.xml
index 56d7799..67d77d6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -156,14 +156,14 @@
0.0.8
- com.wsf.mathapp.App
+ com.wsf.mathapp.Main
default-cli
- com.wsf.mathapp.App
+ com.wsf.mathapp.Main
diff --git a/src/main/java/com/wsf/mathapp/App.java b/src/main/java/com/wsf/mathapp/App.java
deleted file mode 100644
index 8df2322..0000000
--- a/src/main/java/com/wsf/mathapp/App.java
+++ /dev/null
@@ -1,97 +0,0 @@
-// src/main/java/com/wsf/mathapp/App.java
-package com.wsf.mathapp;
-
-import com.wsf.mathapp.controller.LoginController;
-import javafx.application.Application;
-import javafx.fxml.FXMLLoader;
-import javafx.scene.Parent;
-import javafx.scene.Scene;
-import javafx.stage.Stage;
-import java.io.IOException;
-
-public class App extends Application {
-
- private Stage primaryStage;
-
- @Override
- public void start(Stage primaryStage) throws IOException {
- this.primaryStage = primaryStage;
- this.primaryStage.setTitle("数学学习软件");
-
- showLoginScene();
- }
-
- public void showLoginScene() throws IOException {
- FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/wsf/mathapp/view/fxml/login.fxml"));
- Parent root = loader.load();
- LoginController controller = loader.getController();
- controller.setApp(this);
-
- Scene scene = new Scene(root);
- scene.getStylesheets().add(getClass().getResource("/com/wsf/mathapp/view/css/styles.css").toExternalForm());
- primaryStage.setScene(scene);
- primaryStage.show();
- }
-
- public void showRegisterScene() throws IOException {
- FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/wsf/mathapp/view/fxml/register.fxml"));
- Parent root = loader.load();
-
- Scene scene = new Scene(root);
- scene.getStylesheets().add(getClass().getResource("/com/wsf/mathapp/view/css/styles.css").toExternalForm());
- primaryStage.setScene(scene);
- }
-
- public void showSetPasswordScene(String email) throws IOException {
- FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/wsf/mathapp/view/fxml/set_password.fxml"));
- Parent root = loader.load();
- // Pass email to SetPasswordController if needed
- // SetPasswordController controller = loader.getController();
- // controller.setEmail(email);
-
- Scene scene = new Scene(root);
- scene.getStylesheets().add(getClass().getResource("/com/wsf/mathapp/view/css/styles.css").toExternalForm());
- primaryStage.setScene(scene);
- }
-
- public void showGradeSelectionScene() throws IOException {
- FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/wsf/mathapp/view/fxml/grade_selection.fxml"));
- Parent root = loader.load();
-
- Scene scene = new Scene(root);
- scene.getStylesheets().add(getClass().getResource("/com/wsf/mathapp/view/css/styles.css").toExternalForm());
- primaryStage.setScene(scene);
- }
-
- public void showQuestionScene(java.util.List questions) throws IOException {
- FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/wsf/mathapp/view/fxml/question.fxml"));
- Parent root = loader.load();
- com.wsf.mathapp.controller.QuestionController controller = loader.getController();
- controller.setApp(this);
- controller.setQuestions(questions);
-
- Scene scene = new Scene(root);
- scene.getStylesheets().add(getClass().getResource("/com/wsf/mathapp/view/css/styles.css").toExternalForm());
- primaryStage.setScene(scene);
- }
-
- public void showResultScene(com.wsf.mathapp.model.QuizResult result) throws IOException {
- FXMLLoader loader = new FXMLLoader(getClass().getResource("/com/wsf/mathapp/view/fxml/result.fxml"));
- Parent root = loader.load();
- com.wsf.mathapp.controller.ResultController controller = loader.getController();
- controller.setApp(this);
- controller.setResult(result);
-
- Scene scene = new Scene(root);
- scene.getStylesheets().add(getClass().getResource("/com/wsf/mathapp/view/css/styles.css").toExternalForm());
- primaryStage.setScene(scene);
- }
-
- public Stage getPrimaryStage() {
- return primaryStage;
- }
-
- public static void main(String[] args) {
- launch();
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/Main.java b/src/main/java/com/wsf/mathapp/Main.java
new file mode 100644
index 0000000..21c2960
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/Main.java
@@ -0,0 +1,17 @@
+package com.wsf.mathapp;
+
+import com.wsf.mathapp.controller.SceneManager;
+import javafx.application.Application;
+import javafx.stage.Stage;
+
+public class Main extends Application {
+ @Override
+ public void start(Stage primaryStage) {
+ SceneManager sceneManager = new SceneManager(primaryStage);
+ sceneManager.showLoginView();
+ }
+
+ public static void main(String[] args) {
+ launch(args);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/controller/Login.fxml b/src/main/java/com/wsf/mathapp/controller/Login.fxml
deleted file mode 100644
index 14309c8..0000000
--- a/src/main/java/com/wsf/mathapp/controller/Login.fxml
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/main/java/com/wsf/mathapp/controller/LoginController.java b/src/main/java/com/wsf/mathapp/controller/LoginController.java
deleted file mode 100644
index c4cbbad..0000000
--- a/src/main/java/com/wsf/mathapp/controller/LoginController.java
+++ /dev/null
@@ -1,75 +0,0 @@
-// src/main/java/com/wsf/mathapp/controller/LoginController.java
-package com.wsf.mathapp.controller;
-
-import com.wsf.mathapp.App;
-import com.wsf.mathapp.model.User;
-import com.wsf.mathapp.service.BackendService; // 需要后端实现
-import com.wsf.mathapp.util.ValidationUtils;
-import javafx.fxml.FXML;
-import javafx.scene.control.Button;
-import javafx.scene.control.Label;
-import javafx.scene.control.PasswordField;
-import javafx.scene.control.TextField;
-
-public class LoginController {
-
- @FXML private TextField emailField;
- @FXML private PasswordField passwordField;
- @FXML private Button loginButton;
- @FXML private Button registerButton;
- @FXML private Label messageLabel;
-
- private App app;
- private BackendService backendService; // 需要注入后端服务实现
-
- public void setApp(App app) {
- this.app = app;
- // 示例:注入后端服务实现
- // this.backendService = new MyBackendServiceImpl(); // 你需要后端同学提供实现
- }
-
- @FXML
- private void handleLogin() {
- String email = emailField.getText().trim();
- String password = passwordField.getText();
-
- // 1. 验证邮箱格式 (需求:对注册邮箱进行格式校验)
- if (!ValidationUtils.isValidEmail(email)) {
- messageLabel.setText("请输入有效的邮箱地址");
- return;
- }
-
- // 2. 验证密码是否为空 (需求:不输入密码也能直接进入主功能页面 -> 修复)
- if (password.isEmpty()) {
- messageLabel.setText("请输入密码");
- return;
- }
-
- // 3. 调用后端服务进行登录
- User user = backendService.loginUser(email, password);
- if (user != null) {
- messageLabel.setText("登录成功");
- try {
- // 登录成功后跳转到年级选择界面
- app.showGradeSelectionScene();
- } catch (Exception e) {
- e.printStackTrace();
- messageLabel.setText("界面跳转失败");
- }
- } else {
- // 4. 登录失败,显示错误信息 (需求:无法使用已注册用户登陆)
- messageLabel.setText("邮箱或密码错误");
- }
- }
-
- @FXML
- private void handleRegister() {
- try {
- // 点击注册按钮,跳转到注册界面
- app.showRegisterScene();
- } catch (Exception e) {
- e.printStackTrace();
- messageLabel.setText("界面跳转失败");
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/controller/SceneManager.java b/src/main/java/com/wsf/mathapp/controller/SceneManager.java
new file mode 100644
index 0000000..137578d
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/controller/SceneManager.java
@@ -0,0 +1,69 @@
+package com.wsf.mathapp.controller;
+
+import com.wsf.mathapp.view.*;
+import javafx.stage.Stage;
+
+public class SceneManager {
+ private Stage primaryStage;
+ private LoginView loginView;
+ private RegisterView registerView;
+ private MainMenuView mainMenuView;
+ private LevelSelectionView levelSelectionView;
+ private QuestionCountView questionCountView;
+ private QuizView quizView;
+ private ResultView resultView;
+
+ public SceneManager(Stage primaryStage) {
+ this.primaryStage = primaryStage;
+ this.primaryStage.setTitle("数学学习软件");
+ this.primaryStage.setResizable(false);
+
+ // 初始化所有视图
+ this.loginView = new LoginView(this);
+ this.registerView = new RegisterView(this);
+ this.mainMenuView = new MainMenuView(this);
+ this.levelSelectionView = new LevelSelectionView(this);
+ this.questionCountView = new QuestionCountView(this);
+ this.quizView = new QuizView(this);
+ this.resultView = new ResultView(this);
+ }
+
+ public void showLoginView() {
+ primaryStage.setScene(loginView.getScene());
+ primaryStage.show();
+ }
+
+ public void showRegisterView() {
+ primaryStage.setScene(registerView.getScene());
+ }
+
+ public void showMainMenuView() {
+ primaryStage.setScene(mainMenuView.getScene());
+ }
+
+ public void showLevelSelectionView() {
+ primaryStage.setScene(levelSelectionView.getScene());
+ }
+
+ public void showQuestionCountView() {
+ primaryStage.setScene(questionCountView.getScene());
+ }
+
+ public void showQuizView() {
+ primaryStage.setScene(quizView.getScene());
+ }
+
+ public void showResultView(double score) {
+ resultView.setScore(score);
+ primaryStage.setScene(resultView.getScene());
+ }
+
+ // 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; }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/service/EmailService.java b/src/main/java/com/wsf/mathapp/service/EmailService.java
new file mode 100644
index 0000000..84baaaf
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/service/EmailService.java
@@ -0,0 +1,79 @@
+package com.wsf.mathapp.service;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 前端的模拟邮件服务
+ * 在实际部署时,可以替换为真实的邮件服务
+ */
+public class EmailService {
+ private static Map verificationCodes = new HashMap<>();
+
+ private static class VerificationCodeInfo {
+ String code;
+ long timestamp;
+
+ VerificationCodeInfo(String code, long timestamp) {
+ this.code = code;
+ this.timestamp = timestamp;
+ }
+ }
+
+ // 生成6位随机验证码
+ public static String generateVerificationCode() {
+ //Random random = new Random();
+ //int code = 100000 + random.nextInt(900000);
+ //return String.valueOf(code);
+ return "1";
+ }
+
+ // 模拟发送验证码
+ public static boolean sendVerificationCode(String recipientEmail, String code) {
+ try {
+ // 在实际环境中,这里会发送真实的邮件
+ // 现在只是模拟发送,将验证码存储起来
+ System.out.println("模拟发送验证码到: " + recipientEmail + ", 验证码: " + code);
+
+ // 存储验证码信息
+ verificationCodes.put(recipientEmail,
+ new VerificationCodeInfo(code, System.currentTimeMillis()));
+
+ // 模拟网络延迟
+ Thread.sleep(1000);
+
+ return true;
+
+ } catch (Exception e) {
+ System.err.println("发送邮件失败: " + e.getMessage());
+ // 在开发环境中,即使失败也存储验证码用于测试
+ verificationCodes.put(recipientEmail,
+ new VerificationCodeInfo(code, System.currentTimeMillis()));
+ return true;
+ }
+ }
+
+ // 验证验证码
+ public static boolean verifyCode(String email, String inputCode) {
+ VerificationCodeInfo codeInfo = verificationCodes.get(email);
+ if (codeInfo == null) {
+ return false;
+ }
+
+ // 检查验证码是否过期(5分钟)
+ long currentTime = System.currentTimeMillis();
+ if (currentTime - codeInfo.timestamp > 5 * 60 * 1000) {
+ verificationCodes.remove(email);
+ return false;
+ }
+
+ return codeInfo.code.equals(inputCode);
+ }
+
+ // 清理过期的验证码
+ public static void cleanupExpiredCodes() {
+ long currentTime = System.currentTimeMillis();
+ verificationCodes.entrySet().removeIf(entry ->
+ currentTime - entry.getValue().timestamp > 5 * 60 * 1000);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/service/QuestionService.java b/src/main/java/com/wsf/mathapp/service/QuestionService.java
new file mode 100644
index 0000000..cdbcc2b
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/service/QuestionService.java
@@ -0,0 +1,34 @@
+package com.wsf.mathapp.service;
+
+import com.ybw.mathapp.service.ChoiceGenerator;
+import com.ybw.mathapp.service.PrimarySchoolGenerator;
+import com.ybw.mathapp.service.JuniorHighGenerator;
+import com.ybw.mathapp.service.SeniorHighGenerator;
+import com.ybw.mathapp.service.QuestionGenerator;
+import java.util.List;
+
+public class QuestionService {
+
+ public QuestionGenerator createGenerator(String level) {
+ switch (level) {
+ case "小学":
+ return new PrimarySchoolGenerator();
+ case "初中":
+ return new JuniorHighGenerator();
+ case "高中":
+ return new SeniorHighGenerator();
+ default:
+ return null;
+ }
+ }
+
+ public List generateQuestions(String level, int count) {
+ QuestionGenerator generator = createGenerator(level);
+ if (generator == null) {
+ throw new IllegalArgumentException("不支持的题目级别: " + level);
+ }
+
+ ChoiceGenerator choiceGenerator = new ChoiceGenerator(generator);
+ return choiceGenerator.generateMultipleChoiceQuestions(count);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/service/UserService.java b/src/main/java/com/wsf/mathapp/service/UserService.java
new file mode 100644
index 0000000..df20881
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/service/UserService.java
@@ -0,0 +1,81 @@
+package com.wsf.mathapp.service;
+
+import com.ybw.mathapp.util.LoginFileUtils;
+
+public class UserService {
+
+ public boolean register(String email, String password, String confirmPassword) {
+ // 验证邮箱格式
+ if (!isValidEmail(email)) {
+ return false;
+ }
+
+ // 检查邮箱是否已注册
+ if (LoginFileUtils.isEmailRegistered(email)) {
+ return false;
+ }
+
+ // 验证密码格式和匹配
+ if (!isValidPassword(password, confirmPassword)) {
+ return false;
+ }
+
+ // 创建用户并保存
+ com.ybw.mathapp.entity.User user = new com.ybw.mathapp.entity.User(email, password);
+ LoginFileUtils.saveUser(user);
+ return true;
+ }
+
+ public boolean login(String email, String password) {
+ return LoginFileUtils.validateUser(email, password);
+ }
+
+ public boolean changePassword(String email, String oldPassword, String newPassword, String confirmPassword) {
+ // 验证旧密码
+ if (!LoginFileUtils.validateUser(email, oldPassword)) {
+ return false;
+ }
+
+ // 验证新密码格式和匹配
+ if (!isValidPassword(newPassword, confirmPassword)) {
+ return false;
+ }
+
+ // 更新密码逻辑
+ return updateUserPassword(email, newPassword);
+ }
+
+ private boolean updateUserPassword(String email, String newPassword) {
+ // 实现密码更新逻辑
+ // 这里需要扩展后端的LoginFileUtils来支持密码更新
+ return true;
+ }
+
+ private boolean isValidEmail(String email) {
+ if (email == null || email.trim().isEmpty()) {
+ return false;
+ }
+ return email.contains("@") && email.contains(".");
+ }
+
+ private boolean isValidPassword(String password, String confirmPassword) {
+ if (password == null || password.length() < 6 || password.length() > 10) {
+ return false;
+ }
+
+ // 验证密码格式:必须包含大小写字母和数字
+ boolean hasLower = false, hasUpper = false, hasDigit = false;
+ for (char c : password.toCharArray()) {
+ if (Character.isLowerCase(c)) hasLower = true;
+ if (Character.isUpperCase(c)) hasUpper = true;
+ if (Character.isDigit(c)) hasDigit = true;
+ }
+
+ if (!hasLower || !hasUpper || !hasDigit) {
+ return false;
+ }
+
+ // 验证两次输入是否一致
+ return password.equals(confirmPassword);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/view/LevelSelectionView.java b/src/main/java/com/wsf/mathapp/view/LevelSelectionView.java
new file mode 100644
index 0000000..1fcd7c4
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/view/LevelSelectionView.java
@@ -0,0 +1,77 @@
+package com.wsf.mathapp.view;
+
+import com.wsf.mathapp.controller.SceneManager;
+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.layout.VBox;
+import javafx.scene.text.Font;
+
+public class LevelSelectionView {
+ private Scene scene;
+ private SceneManager sceneManager;
+
+ public LevelSelectionView(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 = new Label("选择题目级别");
+ titleLabel.setFont(Font.font(24));
+
+ Button primaryButton = new Button("小学题目");
+ primaryButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 14px;");
+ primaryButton.setPrefSize(200, 50);
+
+ Button juniorButton = new Button("初中题目");
+ juniorButton.setStyle("-fx-background-color: #2196F3; -fx-text-fill: white; -fx-font-size: 14px;");
+ juniorButton.setPrefSize(200, 50);
+
+ Button seniorButton = new Button("高中题目");
+ seniorButton.setStyle("-fx-background-color: #9C27B0; -fx-text-fill: white; -fx-font-size: 14px;");
+ seniorButton.setPrefSize(200, 50);
+
+ Button backButton = new Button("返回");
+ backButton.setStyle("-fx-background-color: #757575; -fx-text-fill: white;");
+ backButton.setPrefSize(200, 40);
+
+ primaryButton.setOnAction(e -> {
+ String selectedLevel = "小学";
+ sceneManager.getQuestionCountView().setLevel(selectedLevel);
+ sceneManager.showQuestionCountView();
+ });
+
+ juniorButton.setOnAction(e -> {
+ String selectedLevel = "初中";
+ sceneManager.getQuestionCountView().setLevel(selectedLevel);
+ sceneManager.showQuestionCountView();
+ });
+
+ seniorButton.setOnAction(e -> {
+ String selectedLevel = "高中";
+ sceneManager.getQuestionCountView().setLevel(selectedLevel);
+ sceneManager.showQuestionCountView();
+ });
+
+ backButton.setOnAction(e -> {
+ sceneManager.showMainMenuView();
+ });
+
+ root.getChildren().addAll(
+ titleLabel, primaryButton, juniorButton, seniorButton, backButton
+ );
+
+ scene = new Scene(root, 400, 500);
+ }
+
+ public Scene getScene() {
+ return scene;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/view/LoginView.java b/src/main/java/com/wsf/mathapp/view/LoginView.java
new file mode 100644
index 0000000..aa5f659
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/view/LoginView.java
@@ -0,0 +1,97 @@
+package com.wsf.mathapp.view;
+
+import com.wsf.mathapp.controller.SceneManager;
+import com.wsf.mathapp.service.UserService;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.*;
+import javafx.scene.layout.VBox;
+import javafx.scene.text.Font;
+
+public class LoginView {
+ private Scene scene;
+ private SceneManager sceneManager;
+ private UserService userService;
+
+ public LoginView(SceneManager sceneManager) {
+ this.sceneManager = sceneManager;
+ this.userService = new UserService();
+ createScene();
+ }
+
+ private void createScene() {
+ VBox root = new VBox(20);
+ root.setPadding(new Insets(40));
+ root.setAlignment(Pos.CENTER);
+
+ Label titleLabel = new Label("数学学习软件");
+ titleLabel.setFont(Font.font(24));
+
+ Label subtitleLabel = new Label("用户登录");
+ subtitleLabel.setFont(Font.font(18));
+
+ TextField emailField = new TextField();
+ emailField.setPromptText("请输入邮箱");
+ emailField.setMaxWidth(300);
+ emailField.setPrefHeight(40);
+
+ PasswordField passwordField = new PasswordField();
+ passwordField.setPromptText("请输入密码");
+ passwordField.setMaxWidth(300);
+ passwordField.setPrefHeight(40);
+
+ Button loginButton = new Button("登录");
+ loginButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 14px;");
+ loginButton.setPrefSize(300, 40);
+
+ Button registerButton = new Button("注册账号");
+ registerButton.setStyle("-fx-background-color: #2196F3; -fx-text-fill: white; -fx-font-size: 14px;");
+ registerButton.setPrefSize(300, 40);
+
+ Label statusLabel = new Label();
+
+ loginButton.setOnAction(e -> {
+ String email = emailField.getText().trim();
+ String password = passwordField.getText();
+
+ if (email.isEmpty() || password.isEmpty()) {
+ showError(statusLabel, "邮箱和密码不能为空!");
+ return;
+ }
+
+ boolean success = userService.login(email, password);
+ if (success) {
+ showSuccess(statusLabel, "登录成功!");
+ sceneManager.showMainMenuView();
+ } else {
+ showError(statusLabel, "邮箱或密码错误!");
+ }
+ });
+
+ registerButton.setOnAction(e -> {
+ sceneManager.showRegisterView();
+ });
+
+ root.getChildren().addAll(
+ titleLabel, subtitleLabel, emailField, passwordField,
+ loginButton, registerButton, statusLabel
+ );
+
+ scene = new Scene(root, 400, 500);
+ }
+
+ private void showError(Label label, String message) {
+ label.setText(message);
+ label.setStyle("-fx-text-fill: red;");
+ }
+
+ private void showSuccess(Label label, String message) {
+ label.setText(message);
+ label.setStyle("-fx-text-fill: green;");
+ }
+
+ public Scene getScene() {
+ return scene;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/view/MainMenuView.java b/src/main/java/com/wsf/mathapp/view/MainMenuView.java
new file mode 100644
index 0000000..67d6af2
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/view/MainMenuView.java
@@ -0,0 +1,112 @@
+package com.wsf.mathapp.view;
+
+import com.wsf.mathapp.controller.SceneManager;
+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.layout.VBox;
+import javafx.scene.text.Font;
+
+public class MainMenuView {
+ private Scene scene;
+ private SceneManager sceneManager;
+
+ public MainMenuView(SceneManager sceneManager) {
+ this.sceneManager = sceneManager;
+ createScene();
+ }
+
+ private void createScene() {
+ VBox root = new VBox(30);
+ root.setPadding(new Insets(50));
+ root.setAlignment(Pos.CENTER);
+
+ Label titleLabel = new Label("数学学习软件");
+ titleLabel.setFont(Font.font(28));
+
+ Label welcomeLabel = new Label("欢迎使用数学学习软件");
+ welcomeLabel.setFont(Font.font(18));
+
+ Button startButton = new Button("开始练习");
+ startButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 16px;");
+ startButton.setPrefSize(200, 50);
+
+ Button changePasswordButton = new Button("修改密码");
+ changePasswordButton.setStyle("-fx-background-color: #2196F3; -fx-text-fill: white; -fx-font-size: 16px;");
+ changePasswordButton.setPrefSize(200, 50);
+
+ Button logoutButton = new Button("退出登录");
+ logoutButton.setStyle("-fx-background-color: #f44336; -fx-text-fill: white; -fx-font-size: 16px;");
+ logoutButton.setPrefSize(200, 50);
+
+ startButton.setOnAction(e -> {
+ sceneManager.showLevelSelectionView();
+ });
+
+ changePasswordButton.setOnAction(e -> {
+ showChangePasswordDialog();
+ });
+
+ logoutButton.setOnAction(e -> {
+ sceneManager.showLoginView();
+ });
+
+ root.getChildren().addAll(
+ titleLabel, welcomeLabel, startButton,
+ changePasswordButton, logoutButton
+ );
+
+ scene = new Scene(root, 400, 500);
+ }
+
+ private void showChangePasswordDialog() {
+ // 创建修改密码对话框
+ javafx.scene.control.Dialog dialog = new javafx.scene.control.Dialog<>();
+ dialog.setTitle("修改密码");
+ dialog.setHeaderText("请输入密码信息");
+
+ // 创建表单
+ javafx.scene.layout.GridPane grid = new javafx.scene.layout.GridPane();
+ grid.setHgap(10);
+ grid.setVgap(10);
+ grid.setPadding(new Insets(20, 150, 10, 10));
+
+ javafx.scene.control.PasswordField oldPassword = new javafx.scene.control.PasswordField();
+ oldPassword.setPromptText("原密码");
+ javafx.scene.control.PasswordField newPassword = new javafx.scene.control.PasswordField();
+ newPassword.setPromptText("新密码");
+ javafx.scene.control.PasswordField confirmPassword = new javafx.scene.control.PasswordField();
+ confirmPassword.setPromptText("确认新密码");
+
+ grid.add(new Label("原密码:"), 0, 0);
+ grid.add(oldPassword, 1, 0);
+ grid.add(new Label("新密码:"), 0, 1);
+ grid.add(newPassword, 1, 1);
+ grid.add(new Label("确认密码:"), 0, 2);
+ grid.add(confirmPassword, 1, 2);
+
+ 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) {
+ // 这里可以添加修改密码的逻辑
+ System.out.println("修改密码功能待实现");
+ }
+ return null;
+ });
+
+ dialog.showAndWait();
+ }
+
+ public Scene getScene() {
+ return scene;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/view/QuestionCountView.java b/src/main/java/com/wsf/mathapp/view/QuestionCountView.java
new file mode 100644
index 0000000..60f8ae3
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/view/QuestionCountView.java
@@ -0,0 +1,100 @@
+package com.wsf.mathapp.view;
+
+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.layout.VBox;
+import javafx.scene.text.Font;
+
+public class QuestionCountView {
+ private Scene scene;
+ private SceneManager sceneManager;
+ private String level;
+
+ 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 = new Label("选择题目数量");
+ titleLabel.setFont(Font.font(20));
+
+ Label levelLabel = new Label();
+ levelLabel.setFont(Font.font(16));
+
+ TextField countField = new TextField();
+ countField.setPromptText("请输入题目数量 (10-30)");
+ countField.setMaxWidth(250);
+ countField.setPrefHeight(40);
+
+ Button startButton = new Button("开始答题");
+ startButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 14px;");
+ startButton.setPrefSize(250, 45);
+
+ Button backButton = new Button("返回");
+ backButton.setStyle("-fx-background-color: #757575; -fx-text-fill: white;");
+ backButton.setPrefSize(250, 40);
+
+ Label statusLabel = new Label();
+
+ 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;
+ }
+
+ // 设置题目数量和级别,然后开始答题
+ sceneManager.getQuizView().setQuizParameters(level, count);
+ sceneManager.showQuizView();
+
+ } catch (NumberFormatException ex) {
+ showError(statusLabel, "请输入有效的数字!");
+ }
+ });
+
+ backButton.setOnAction(e -> {
+ sceneManager.showLevelSelectionView();
+ });
+
+ root.getChildren().addAll(
+ titleLabel, levelLabel, countField, startButton, backButton, statusLabel
+ );
+
+ scene = new Scene(root, 400, 400);
+ }
+
+ public void setLevel(String level) {
+ this.level = level;
+ // 更新界面显示当前选择的级别
+ if (scene != null) {
+ VBox root = (VBox) scene.getRoot();
+ Label levelLabel = (Label) root.getChildren().get(1);
+ levelLabel.setText("当前级别: " + level);
+ levelLabel.setStyle("-fx-text-fill: #2196F3;");
+ }
+ }
+
+ private void showError(Label label, String message) {
+ label.setText(message);
+ label.setStyle("-fx-text-fill: red;");
+ }
+
+ public Scene getScene() {
+ return scene;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/wsf/mathapp/view/QuizView.java b/src/main/java/com/wsf/mathapp/view/QuizView.java
new file mode 100644
index 0000000..b25a7c4
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/view/QuizView.java
@@ -0,0 +1,148 @@
+package com.wsf.mathapp.view;
+
+import com.wsf.mathapp.controller.SceneManager;
+import com.wsf.mathapp.service.QuestionService;
+import com.ybw.mathapp.service.ChoiceGenerator;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.*;
+import javafx.scene.layout.VBox;
+import javafx.scene.text.Font;
+import java.util.List;
+
+public class QuizView {
+ private Scene scene;
+ private SceneManager sceneManager;
+ private QuestionService questionService;
+ private String currentLevel;
+ private int questionCount;
+ private List questions;
+ private int currentQuestionIndex = 0;
+ private int correctAnswers = 0;
+
+ private Label questionLabel;
+ private ToggleGroup optionsGroup;
+ private VBox optionsContainer;
+ private Button nextButton;
+ private Label progressLabel;
+ private Label questionNumberLabel;
+
+ public QuizView(SceneManager sceneManager) {
+ this.sceneManager = sceneManager;
+ this.questionService = new QuestionService();
+ 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));
+
+ questionNumberLabel = new Label();
+ questionNumberLabel.setFont(Font.font(16));
+ questionNumberLabel.setStyle("-fx-text-fill: #666;");
+
+ questionLabel = new Label();
+ questionLabel.setFont(Font.font(18));
+ questionLabel.setWrapText(true);
+ questionLabel.setStyle("-fx-padding: 10 0 20 0;");
+
+ optionsGroup = new ToggleGroup();
+ optionsContainer = new VBox(15);
+ optionsContainer.setAlignment(Pos.CENTER_LEFT);
+ optionsContainer.setPadding(new Insets(10));
+
+ nextButton = new Button("下一题");
+ nextButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 14px;");
+ nextButton.setPrefSize(150, 40);
+ nextButton.setDisable(true);
+
+ root.getChildren().addAll(progressLabel, questionNumberLabel, questionLabel, optionsContainer, nextButton);
+
+ scene = new Scene(root, 600, 500);
+ }
+
+ public void setQuizParameters(String level, int count) {
+ this.currentLevel = level;
+ this.questionCount = count;
+ this.currentQuestionIndex = 0;
+ this.correctAnswers = 0;
+
+ // 生成题目
+ generateQuestions();
+ showQuestion(0);
+ }
+
+ private void generateQuestions() {
+ questions = questionService.generateQuestions(currentLevel, questionCount);
+ }
+
+ private void showQuestion(int index) {
+ if (index >= questions.size()) {
+ // 所有题目已回答,显示结果
+ double score = (double) correctAnswers / questions.size();
+ sceneManager.showResultView(score);
+ return;
+ }
+
+ ChoiceGenerator.MultipleChoiceQuestion question = questions.get(index);
+
+ // 更新进度和题号
+ progressLabel.setText("进度: " + (index + 1) + "/" + questions.size());
+ questionNumberLabel.setText("第 " + (index + 1) + " 题");
+
+ // 设置题目内容
+ String questionText = question.getQuestion();
+ // 移除末尾的 "="
+ if (questionText.endsWith(" =")) {
+ questionText = questionText.substring(0, questionText.length() - 2);
+ }
+ questionLabel.setText(questionText + " = ?");
+
+ // 清空选项容器
+ optionsContainer.getChildren().clear();
+ optionsGroup = new ToggleGroup();
+
+ // 添加选项
+ 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);
+ optionsContainer.getChildren().add(radioButton);
+ }
+
+ // 设置下一题按钮
+ nextButton.setDisable(true);
+ nextButton.setText(index == questions.size() - 1 ? "提交答案" : "下一题");
+ nextButton.setOnAction(e -> {
+ checkAnswer(question);
+ showQuestion(index + 1);
+ });
+
+ // 启用下一题按钮当选择了一个选项时
+ optionsGroup.selectedToggleProperty().addListener((obs, oldVal, newVal) -> {
+ nextButton.setDisable(newVal == null);
+ });
+ }
+
+ private void checkAnswer(ChoiceGenerator.MultipleChoiceQuestion question) {
+ RadioButton selectedRadioButton = (RadioButton) optionsGroup.getSelectedToggle();
+ if (selectedRadioButton != null) {
+ int selectedIndex = optionsContainer.getChildren().indexOf(selectedRadioButton);
+ if (selectedIndex == question.getCorrectIndex()) {
+ correctAnswers++;
+ }
+ }
+ }
+
+ public Scene getScene() {
+ return scene;
+ }
+}
\ 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
new file mode 100644
index 0000000..0b2a1a2
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/view/RegisterView.java
@@ -0,0 +1,160 @@
+package com.wsf.mathapp.view;
+
+import com.wsf.mathapp.controller.SceneManager;
+import com.wsf.mathapp.service.UserService;
+import com.wsf.mathapp.service.EmailService;
+import javafx.geometry.Insets;
+import javafx.geometry.Pos;
+import javafx.scene.Scene;
+import javafx.scene.control.*;
+import javafx.scene.layout.VBox;
+import javafx.scene.text.Font;
+
+public class RegisterView {
+ private Scene scene;
+ private SceneManager sceneManager;
+ private UserService userService;
+
+ public RegisterView(SceneManager sceneManager) {
+ this.sceneManager = sceneManager;
+ this.userService = new UserService();
+ createScene();
+ }
+
+ private void createScene() {
+ VBox root = new VBox(15);
+ root.setPadding(new Insets(30));
+ root.setAlignment(Pos.CENTER);
+
+ Label titleLabel = new Label("用户注册");
+ titleLabel.setFont(Font.font(20));
+
+ TextField emailField = new TextField();
+ emailField.setPromptText("请输入邮箱");
+ emailField.setMaxWidth(300);
+ emailField.setPrefHeight(35);
+
+ Button sendCodeButton = new Button("发送验证码");
+ sendCodeButton.setStyle("-fx-background-color: #FF9800; -fx-text-fill: white;");
+ sendCodeButton.setPrefSize(120, 35);
+
+ TextField codeField = new TextField();
+ codeField.setPromptText("请输入验证码");
+ codeField.setMaxWidth(300);
+ codeField.setPrefHeight(35);
+
+ PasswordField passwordField = new PasswordField();
+ passwordField.setPromptText("请输入密码(6-10位,含大小写字母和数字)");
+ passwordField.setMaxWidth(300);
+ passwordField.setPrefHeight(35);
+
+ PasswordField confirmPasswordField = new PasswordField();
+ confirmPasswordField.setPromptText("请再次输入密码");
+ confirmPasswordField.setMaxWidth(300);
+ confirmPasswordField.setPrefHeight(35);
+
+ Button registerButton = new Button("注册");
+ registerButton.setStyle("-fx-background-color: #4CAF50; -fx-text-fill: white; -fx-font-size: 14px;");
+ registerButton.setPrefSize(300, 40);
+
+ Button backButton = new Button("返回登录");
+ backButton.setStyle("-fx-background-color: #757575; -fx-text-fill: white;");
+ backButton.setPrefSize(300, 35);
+
+ Label statusLabel = new Label();
+
+ sendCodeButton.setOnAction(e -> {
+ String email = emailField.getText().trim();
+ if (email.isEmpty() || !email.contains("@") || !email.contains(".")) {
+ showError(statusLabel, "请输入有效的邮箱地址!");
+ return;
+ }
+
+ // 生成并发送验证码
+ String verificationCode = EmailService.generateVerificationCode();
+ boolean sent = EmailService.sendVerificationCode(email, verificationCode);
+
+ if (sent) {
+ showSuccess(statusLabel, "验证码已发送到您的邮箱!");
+
+ // 禁用发送按钮60秒
+ sendCodeButton.setDisable(true);
+ startCountdown(sendCodeButton);
+ } else {
+ showError(statusLabel, "发送验证码失败,请稍后重试!");
+ }
+ });
+
+ registerButton.setOnAction(e -> {
+ String email = emailField.getText().trim();
+ String code = codeField.getText().trim();
+ String password = passwordField.getText();
+ String confirmPassword = confirmPasswordField.getText();
+
+ if (email.isEmpty() || code.isEmpty() || password.isEmpty()) {
+ showError(statusLabel, "请填写所有字段!");
+ return;
+ }
+
+ // 验证验证码
+ if (!EmailService.verifyCode(email, code)) {
+ showError(statusLabel, "验证码错误或已过期!");
+ return;
+ }
+
+ boolean success = userService.register(email, password, confirmPassword);
+ if (success) {
+ showSuccess(statusLabel, "注册成功!");
+ sceneManager.showLoginView();
+ } else {
+ showError(statusLabel, "注册失败,请检查信息!");
+ }
+ });
+
+ backButton.setOnAction(e -> {
+ sceneManager.showLoginView();
+ });
+
+ root.getChildren().addAll(
+ titleLabel, emailField, sendCodeButton, codeField,
+ passwordField, confirmPasswordField, registerButton,
+ backButton, statusLabel
+ );
+
+ scene = new Scene(root, 400, 500);
+ }
+
+ 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 + "秒后重发");
+ });
+ Thread.sleep(1000);
+ }
+ javafx.application.Platform.runLater(() -> {
+ button.setText("发送验证码");
+ button.setDisable(false);
+ });
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ }
+ }).start();
+ }
+
+ private void showError(Label label, String message) {
+ label.setText(message);
+ label.setStyle("-fx-text-fill: red;");
+ }
+
+ private void showSuccess(Label label, String message) {
+ label.setText(message);
+ label.setStyle("-fx-text-fill: green;");
+ }
+
+ 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
new file mode 100644
index 0000000..0b71a26
--- /dev/null
+++ b/src/main/java/com/wsf/mathapp/view/ResultView.java
@@ -0,0 +1,102 @@
+package com.wsf.mathapp.view;
+
+import com.wsf.mathapp.controller.SceneManager;
+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.layout.VBox;
+import javafx.scene.text.Font;
+
+public class ResultView {
+ private Scene scene;
+ private SceneManager sceneManager;
+ private double score;
+
+ public ResultView(SceneManager sceneManager) {
+ this.sceneManager = sceneManager;
+ createScene();
+ }
+
+ private void createScene() {
+ VBox root = new VBox(30);
+ root.setPadding(new Insets(50));
+ root.setAlignment(Pos.CENTER);
+
+ Label titleLabel = new Label("答题完成");
+ titleLabel.setFont(Font.font(24));
+
+ Label scoreLabel = new Label();
+ scoreLabel.setFont(Font.font(48));
+
+ 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);
+
+ Button mainMenuButton = new Button("返回主菜单");
+ mainMenuButton.setStyle("-fx-background-color: #2196F3; -fx-text-fill: white; -fx-font-size: 16px;");
+ mainMenuButton.setPrefSize(200, 50);
+
+ Button exitButton = new Button("退出系统");
+ exitButton.setStyle("-fx-background-color: #757575; -fx-text-fill: white; -fx-font-size: 16px;");
+ exitButton.setPrefSize(200, 50);
+
+ restartButton.setOnAction(e -> {
+ sceneManager.showLevelSelectionView();
+ });
+
+ mainMenuButton.setOnAction(e -> {
+ sceneManager.showMainMenuView();
+ });
+
+ exitButton.setOnAction(e -> {
+ System.exit(0);
+ });
+
+ root.getChildren().addAll(
+ titleLabel, scoreLabel, messageLabel,
+ restartButton, mainMenuButton, exitButton
+ );
+
+ scene = new Scene(root, 400, 500);
+ }
+
+ public void setScore(double score) {
+ this.score = score;
+ updateScoreDisplay();
+ }
+
+ private void updateScoreDisplay() {
+ if (scene != null) {
+ VBox root = (VBox) scene.getRoot();
+ Label scoreLabel = (Label) root.getChildren().get(1);
+ Label messageLabel = (Label) root.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("需要多加练习!");
+ }
+ }
+ }
+
+ public Scene getScene() {
+ return scene;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/ybw/mathapp/entity/User.java b/src/main/java/com/ybw/mathapp/entity/User.java
index 91e46c0..0baf6d7 100644
--- a/src/main/java/com/ybw/mathapp/entity/User.java
+++ b/src/main/java/com/ybw/mathapp/entity/User.java
@@ -13,7 +13,7 @@ package com.ybw.mathapp.entity;
public class User {
/** 用户名,不可修改。 */
- // private final String name;
+ private final String name;
/** 邮箱,不可修改。 */
private final String email;
@@ -30,7 +30,8 @@ public class User {
* @param email 邮箱,不能为空 用户名,不能为空
* @param password 用户密码,不能为空
*/
- public User(String email, String password) {
+ public User(String name,String email, String password) {
+ this.name = name;
this.password = password;
this.email = email;
}
@@ -92,4 +93,8 @@ public class User {
}
return null;
}
+
+ public String getName() {
+ return this.name;
+ }
}
diff --git a/src/main/java/com/ybw/mathapp/service/FileHandler.java b/src/main/java/com/ybw/mathapp/service/FileHandler.java
index 8cd486a..c41c4d4 100644
--- a/src/main/java/com/ybw/mathapp/service/FileHandler.java
+++ b/src/main/java/com/ybw/mathapp/service/FileHandler.java
@@ -10,7 +10,7 @@ import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
-import mathpuzzle.entity.User;
+import com.ybw.mathapp.entity.User;
/**
* 文件处理器,负责创建用户目录和保存试卷文件。
diff --git a/src/main/java/com/ybw/mathapp/service/QuestionDeduplicator.java b/src/main/java/com/ybw/mathapp/service/QuestionDeduplicator.java
index b4319ee..cf51e49 100644
--- a/src/main/java/com/ybw/mathapp/service/QuestionDeduplicator.java
+++ b/src/main/java/com/ybw/mathapp/service/QuestionDeduplicator.java
@@ -6,7 +6,7 @@ import java.io.FileReader;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
-import mathpuzzle.entity.User;
+import com.ybw.mathapp.entity.User;
/**
* 题目查重器,负责加载历史题目并检查新题目是否重复。