v1.0 大概的框架已经写好,未与后端对接

pull/2/head
玖兮冉 4 months ago
parent 997acd73ec
commit e2e0d99656

@ -156,14 +156,14 @@
<version>0.0.8</version>
<configuration>
<!-- 根据你的实际主类修改 -->
<mainClass>com.wsf.mathapp.App</mainClass>
<mainClass>com.wsf.mathapp.Main</mainClass>
<!-- 移除了 preview 选项 -->
</configuration>
<executions>
<execution>
<id>default-cli</id>
<configuration>
<mainClass>com.wsf.mathapp.App</mainClass>
<mainClass>com.wsf.mathapp.Main</mainClass>
</configuration>
</execution>
</executions>

@ -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<com.wsf.mathapp.model.Question> 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();
}
}

@ -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);
}
}

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?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?>
<VBox alignment="CENTER" spacing="20.0" xmlns="http://javafx.com/javafx/17.0.2" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.wsf.mathapp.controller.LoginController">
<children>
<Label text="登录" />
<TextField fx:id="emailField" promptText="邮箱" />
<PasswordField fx:id="passwordField" promptText="密码" />
<Button fx:id="loginButton" onAction="#handleLogin" text="登录" />
<Button fx:id="registerButton" onAction="#handleRegister" text="注册" />
<Label fx:id="messageLabel" textFill="RED" />
</children>
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>
</VBox>

@ -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("界面跳转失败");
}
}
}

@ -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; }
}

@ -0,0 +1,79 @@
package com.wsf.mathapp.service;
import java.util.HashMap;
import java.util.Map;
/**
*
*
*/
public class EmailService {
private static Map<String, VerificationCodeInfo> 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);
}
}

@ -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<ChoiceGenerator.MultipleChoiceQuestion> 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);
}
}

@ -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);
}
}

@ -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;
}
}

@ -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;
}
}

@ -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<Void> 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;
}
}

@ -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;
}
}

@ -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<ChoiceGenerator.MultipleChoiceQuestion> 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<Double> 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;
}
}

@ -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;
}
}

@ -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;
}
}

@ -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;
}
}

@ -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;
/**
*

@ -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;
/**
*

Loading…
Cancel
Save