From b82ae44e0e20f588d5326db02455f03346d4a3d9 Mon Sep 17 00:00:00 2001
From: lsbp <2803234009@qq.com>
Date: Wed, 8 Oct 2025 19:49:26 +0800
Subject: [PATCH 1/5] =?UTF-8?q?=E6=AD=A3=E5=BC=8F=E7=89=881.0.1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 75 +++++++++------
src/main/java/com/pair/MathQuiz/Main.java | 4 -
.../java/com/pair/service/FileIOService.java | 5 +-
.../java/com/pair/service/QuizService.java | 25 +++--
.../java/com/pair/service/UserService.java | 50 +---------
.../java/com/pair/ui/GradeSelectPanel.java | 7 --
src/main/java/com/pair/ui/MainWindow.java | 7 +-
src/main/java/com/pair/ui/ResultPage.java | 1 +
.../pair/util/AsyncRegistrationHelper.java | 16 ++--
src/main/java/com/pair/util/EmailUtil.java | 35 +------
.../java/com/pair/util/PasswordValidator.java | 95 -------------------
11 files changed, 79 insertions(+), 241 deletions(-)
delete mode 100644 src/main/java/com/pair/MathQuiz/Main.java
delete mode 100644 src/main/java/com/pair/ui/GradeSelectPanel.java
diff --git a/pom.xml b/pom.xml
index 0bb799d..0ba7047 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,6 +5,14 @@
http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.1.12
+
+
+
com.mathquiz
MathQuizApp
1.04
@@ -13,22 +21,29 @@
Math Quiz Application
小初高数学学习软件- JavaFX版本
-
jitpack.io
- https://jitpack.io
+ https://jitpack.io
+
UTF-8
21
21
21.0.2
+ 3.1.12
+
+
+ 1.5.13
+ 2.2
+ 6.0.22
+
com.google.code.gson
gson
@@ -56,25 +71,28 @@
${javafx.version}
-
-
com.sun.mail
javax.mail
1.6.2
-
com.sun.activation
javax.activation
1.2.0
-
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
org.apache.maven.plugins
maven-compiler-plugin
@@ -86,6 +104,7 @@
+
org.apache.maven.plugins
maven-resources-plugin
@@ -101,41 +120,39 @@
3.6.1
- copy-dependencies
- package
+ copy-javafx-dependencies
+ compile
copy-dependencies
- ${project.build.directory}/dependencies
- false
- false
- true
+ org.openjfx
+ ${project.build.directory}/javafx-libs
-
- org.codehaus.mojo
- exec-maven-plugin
- 3.1.0
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring-boot.maven.plugin.version}
-
- com.pair.Test
-
-
- --add-modules
- javafx.controls,javafx.fxml,javafx.graphics,javafx.base
-
+ com.pair.Test
+
+
+ --module-path ${project.build.directory}/javafx-libs --add-modules javafx.controls,javafx.fxml,javafx.graphics,javafx.base
+ --add-modules javafx.controls,javafx.fxml,javafx.graphics,javafx.base
+
+
+
+
+ repackage
+
+
+
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/src/main/java/com/pair/MathQuiz/Main.java b/src/main/java/com/pair/MathQuiz/Main.java
deleted file mode 100644
index f94bf8e..0000000
--- a/src/main/java/com/pair/MathQuiz/Main.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package com.pair.MathQuiz;
-
-public class Main {
-}
diff --git a/src/main/java/com/pair/service/FileIOService.java b/src/main/java/com/pair/service/FileIOService.java
index d420587..ed0b0a6 100644
--- a/src/main/java/com/pair/service/FileIOService.java
+++ b/src/main/java/com/pair/service/FileIOService.java
@@ -21,7 +21,6 @@ import java.util.*;
public class FileIOService {
private static final String DATA_DIR = AppDataDirectory.getFullPath("data");
- private static final String USERS_DIR = AppDataDirectory.getFullPath("data/users");
private static final String HISTORY_DIR = AppDataDirectory.getFullPath("data/history");
private static final String REGISTRATION_CODES_FILE = AppDataDirectory.getFullPath("data/registration_codes.json");
@@ -41,7 +40,6 @@ public class FileIOService {
public void initDataDirectory() throws IOException {
FileUtils.createDirectoryIfNotExists(DATA_DIR);
- FileUtils.createDirectoryIfNotExists(USERS_DIR);
FileUtils.createDirectoryIfNotExists(HISTORY_DIR);
FileUtils.ensureFileExists(REGISTRATION_CODES_FILE);
@@ -139,8 +137,9 @@ public class FileIOService {
// ==================== 答题历史操作 ====================
public void saveQuizHistory(QuizHistory history) throws IOException {
+
String filename = HISTORY_DIR + "/" +
- sanitizeFilename(history.getUsername()) + "_" +
+ sanitizeFilename(history.getUsername()) + "/" +
System.currentTimeMillis() + ".txt";
StringBuilder content = new StringBuilder();
diff --git a/src/main/java/com/pair/service/QuizService.java b/src/main/java/com/pair/service/QuizService.java
index 917c91d..ea350f9 100644
--- a/src/main/java/com/pair/service/QuizService.java
+++ b/src/main/java/com/pair/service/QuizService.java
@@ -20,14 +20,6 @@ public class QuizService {
// ==================== 构造方法 ====================
- public QuizService() throws IOException {
- this.fileIOService = new FileIOService();
- this.userService = new UserService(fileIOService);
- this.currentQuestions = new ArrayList<>();
- this.userAnswers = new ArrayList<>();
- this.currentQuestionIndex = 0;
- }
-
public QuizService(FileIOService fileIOService, UserService userService) {
this.fileIOService = fileIOService;
this.userService = userService;
@@ -408,20 +400,27 @@ public class QuizService {
// ==================== 数据持久化 ====================
- public void saveQuizHistory(User user) throws IOException {
+ public void saveQuizHistory() {
QuizResult result = calculateResult();
QuizHistory history = new QuizHistory(
- user.getUsername(),
+ userService.getCurrentUser().getUsername(),
new Date(),
currentQuestions,
userAnswers,
result.getScore()
);
+ try {
+ fileIOService.saveQuizHistory(history);
+ } catch (IOException e) {
+ System.err.println(e);
+ }
- fileIOService.saveQuizHistory(history);
-
- userService.updateUserStatistics(user, result.getScore());
+ try {
+ userService.updateUserStatistics(userService.getCurrentUser(), result.getScore());
+ } catch (IOException e) {
+ System.err.println(e);
+ }
System.out.println("✓ 答题记录已保存");
}
diff --git a/src/main/java/com/pair/service/UserService.java b/src/main/java/com/pair/service/UserService.java
index b5e50fa..f9961b2 100644
--- a/src/main/java/com/pair/service/UserService.java
+++ b/src/main/java/com/pair/service/UserService.java
@@ -1,7 +1,9 @@
package com.pair.service;
+import com.pair.model.ChoiceQuestion;
import com.pair.model.Grade;
+import com.pair.model.QuizHistory;
import com.pair.model.User;
import com.pair.util.EmailUtil;
import com.pair.util.FileUtils;
@@ -495,23 +497,6 @@ public class UserService {
// ==================== 业务逻辑方法===================
-// /**
-// * 从用户名提取真实姓名
-// */
-// public String getRealName(User user) {
-// if (user == null || user.getUsername() == null) {
-// return "";
-// }
-//
-// String username = user.getUsername();
-// int dashIndex = username.indexOf('-');
-//
-// if (dashIndex > 0 && dashIndex < username.length() - 1) {
-// return username.substring(dashIndex + 1);
-// }
-//
-// return username;
-// }
/**
* 获取学段中文名称
@@ -550,35 +535,4 @@ public class UserService {
return sb.toString();
}
- // ==================== 验证工具方法 ====================
-
-// private boolean validateUsername(String username) {
-// if (username == null || username.trim().isEmpty()) {
-// return false;
-// }
-//
-// Matcher matcher = USERNAME_PATTERN.matcher(username);
-// return matcher.matches();
-// }
-
-// private boolean validateEmail(String email) {
-// if (email == null || email.trim().isEmpty()) {
-// return false;
-// }
-//
-// Matcher matcher = EMAIL_PATTERN.matcher(email);
-// return matcher.matches();
-// }
-
-// private Grade extractGradeFromUsername(String username) {
-// if (username.startsWith("小学-")) {
-// return Grade.ELEMENTARY;
-// } else if (username.startsWith("初中-")) {
-// return Grade.MIDDLE;
-// } else if (username.startsWith("高中-")) {
-// return Grade.HIGH;
-// }
-//
-// throw new IllegalArgumentException("无法识别的学段");
-// }
}
\ No newline at end of file
diff --git a/src/main/java/com/pair/ui/GradeSelectPanel.java b/src/main/java/com/pair/ui/GradeSelectPanel.java
deleted file mode 100644
index 83c9d8c..0000000
--- a/src/main/java/com/pair/ui/GradeSelectPanel.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.pair.ui;
-
-import javafx.scene.layout.VBox;
-
-public class GradeSelectPanel extends VBox {
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/pair/ui/MainWindow.java b/src/main/java/com/pair/ui/MainWindow.java
index f44a851..884c90c 100644
--- a/src/main/java/com/pair/ui/MainWindow.java
+++ b/src/main/java/com/pair/ui/MainWindow.java
@@ -2,6 +2,7 @@
package com.pair.ui;
import com.pair.model.User;
+import com.pair.service.FileIOService;
import com.pair.service.QuizService;
import com.pair.service.UserService;
import com.pair.util.AsyncRegistrationHelper;
@@ -15,8 +16,8 @@ import javafx.stage.Stage;
import java.io.IOException;
public class MainWindow extends BorderPane {
- private final UserService userService = new UserService();
- private final QuizService quizService = new QuizService();
+ private final UserService userService;
+ private final QuizService quizService;
private User currentUser;
private final Stage primaryStage;
@@ -24,6 +25,8 @@ public class MainWindow extends BorderPane {
public MainWindow(Stage primaryStage) throws IOException {
this.primaryStage = primaryStage;
+ this.userService = new UserService();
+ this.quizService = new QuizService(new FileIOService(), userService);
showStartPage();
}
diff --git a/src/main/java/com/pair/ui/ResultPage.java b/src/main/java/com/pair/ui/ResultPage.java
index c82b922..1874219 100644
--- a/src/main/java/com/pair/ui/ResultPage.java
+++ b/src/main/java/com/pair/ui/ResultPage.java
@@ -52,6 +52,7 @@ public class ResultPage extends NavigablePanel {
public void updateResult() {
QuizResult result = quizService.calculateResult();
+ quizService.saveQuizHistory();
resultLabel.setText(result.toString());
gradeLabel.setText(quizService.getGrade(result));
}
diff --git a/src/main/java/com/pair/util/AsyncRegistrationHelper.java b/src/main/java/com/pair/util/AsyncRegistrationHelper.java
index b854424..03488d1 100644
--- a/src/main/java/com/pair/util/AsyncRegistrationHelper.java
+++ b/src/main/java/com/pair/util/AsyncRegistrationHelper.java
@@ -4,6 +4,7 @@ package com.pair.util;
import com.pair.service.UserService;
import javafx.application.Platform;
import javafx.concurrent.Task;
+import javafx.concurrent.WorkerStateEvent;
import java.io.IOException;
import java.util.function.Consumer;
@@ -51,13 +52,16 @@ public class AsyncRegistrationHelper {
}).start();
});
- task.setOnFailed(e -> {
+ task.setOnFailed((WorkerStateEvent e) -> {
Throwable ex = task.getException();
- String msg = switch (ex) {
- case IllegalArgumentException iae -> iae.getMessage();
- case IOException ioe -> "系统错误:" + ioe.getMessage();
- default -> "发送失败,请检查网络或稍后重试";
- };
+ String msg;
+ if (ex instanceof IllegalArgumentException iae) {
+ msg = iae.getMessage();
+ } else if (ex instanceof IOException ioe) {
+ msg = "系统错误:" + ioe.getMessage();
+ } else {
+ msg = "发送失败,请检查网络或稍后重试";
+ }
onFailure.accept(msg);
Platform.runLater(onComplete);
});
diff --git a/src/main/java/com/pair/util/EmailUtil.java b/src/main/java/com/pair/util/EmailUtil.java
index c9df9e0..5766db5 100644
--- a/src/main/java/com/pair/util/EmailUtil.java
+++ b/src/main/java/com/pair/util/EmailUtil.java
@@ -61,6 +61,7 @@ public class EmailUtil {
return new PasswordAuthentication(SENDER_EMAIL, SENDER_PASSWORD);
}
});
+ session.setDebug(true);
// 3. 创建邮件
Message message = new MimeMessage(session);
@@ -88,38 +89,4 @@ public class EmailUtil {
}
- /**
- * 发送密码重置邮件(预留接口)
- *
- * @param toEmail 收件人邮箱
- * @param newPassword 新密码
- * @return true表示发送成功
- */
- public static boolean sendPasswordReset(String toEmail, String newPassword) {
- // TODO: 将来如果需要"找回密码"功能,可以在这里实现
-
- System.out.println("【模拟】发送密码重置邮件");
- System.out.println("收件人: " + toEmail);
- System.out.println("新密码: " + newPassword);
-
- return true;
- }
-
}
-
- /**
- * 验证邮箱格式
- * @param email 邮箱地址
- * @return true表示格式正确
- */
-// public static boolean isValidEmail(String email) {
-// if (email == null || email.trim().isEmpty()) {
-// return false;
-// }
-//
-// // 简单的邮箱格式验证
-// String emailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$";
-// return email.matches(emailRegex);
-// }
-//}
-
diff --git a/src/main/java/com/pair/util/PasswordValidator.java b/src/main/java/com/pair/util/PasswordValidator.java
index b183fbf..cd0d972 100644
--- a/src/main/java/com/pair/util/PasswordValidator.java
+++ b/src/main/java/com/pair/util/PasswordValidator.java
@@ -144,99 +144,4 @@ public class PasswordValidator {
return RandomUtils.shuffleString(code.toString());
}
-// /**
-// * 生成固定长度的随机密码
-// *
-// * @param length 密码长度
-// * @param includeSpecialChars 是否包含特殊字符
-// * @return 随机密码
-// */
-// public static String generateRandomPassword(int length, boolean includeSpecialChars) {
-// if (length < MIN_LENGTH) {
-// throw new IllegalArgumentException("密码长度不能少于 " + MIN_LENGTH);
-// }
-//
-// String chars = ALL_CHARS;
-// if (includeSpecialChars) {
-// chars += "!@#$%^&*()_+-=[]{}";
-// }
-//
-// StringBuilder password = new StringBuilder(length);
-//
-// // 确保至少包含一个字母和一个数字
-// password.append(UPPERCASE.charAt(random.nextInt(UPPERCASE.length())));
-// password.append(DIGITS.charAt(random.nextInt(DIGITS.length())));
-//
-// // 填充剩余字符
-// for (int i = 2; i < length; i++) {
-// password.append(chars.charAt(random.nextInt(chars.length())));
-// }
-//
-// return shuffleString(password.toString());
-// }
-
- // ==================== 工具方法 ====================
-//
-// /**
-// * 打乱字符串(使用 Fisher-Yates 算法)
-// *
-// * @param str 原字符串
-// * @return 打乱后的字符串
-// */
-// private static String shuffleString(String str) {
-// if (str == null || str.length() <= 1) {
-// return str;
-// }
-//
-// char[] chars = str.toCharArray();
-//
-// for (int i = chars.length - 1; i > 0; i--) {
-// int j = random.nextInt(i + 1);
-//
-// // 交换
-// char temp = chars[i];
-// chars[i] = chars[j];
-// chars[j] = temp;
-// }
-//
-// return new String(chars);
-// }
-
-// /**
-// * 检查密码是否包含常见弱密码
-// *
-// * @param password 密码
-// * @return true表示是弱密码
-// */
-// public static boolean isWeakPassword(String password) {
-// if (password == null) {
-// return true;
-// }
-//
-// String lowerPassword = password.toLowerCase();
-//
-// // 常见弱密码列表
-// String[] weakPasswords = {
-// "123456", "password", "123456789", "12345678", "12345",
-// "111111", "1234567", "sunshine", "qwerty", "iloveyou",
-// "princess", "admin", "welcome", "666666", "abc123",
-// "football", "123123", "monkey", "654321", "!@#$%^&*",
-// "charlie", "aa123456", "donald", "password1", "qwerty123"
-// };
-//
-// for (String weak : weakPasswords) {
-// if (lowerPassword.equals(weak) || lowerPassword.contains(weak)) {
-// return true;
-// }
-// }
-//
-// // 检查是否是连续数字或字母
-// if (password.matches("^(\\d)\\1+$") || // 全是相同数字
-// password.matches("^(.)\\1+$") || // 全是相同字符
-// password.matches("^(0123456789|123456789|987654321|abcdefghij|qwertyuiop).*")) { // 连续字符
-// return true;
-// }
-//
-// return false;
-// }
}
\ No newline at end of file
--
2.34.1
From 7c3b83f78938019456b3a2b01ca1eb64a30130e8 Mon Sep 17 00:00:00 2001
From: lsbp <2803234009@qq.com>
Date: Wed, 8 Oct 2025 23:36:21 +0800
Subject: [PATCH 2/5] =?UTF-8?q?UI=E4=BF=AE=E6=94=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.idea/workspace.xml | 22 +-
src/main/java/com/pair/ui/InfGenPage.java | 123 ++-----
src/main/java/com/pair/ui/LoginPage.java | 30 +-
src/main/java/com/pair/ui/MainWindow.java | 1 -
src/main/java/com/pair/ui/NavigablePanel.java | 10 +-
.../java/com/pair/ui/PasswordModifyPage.java | 24 +-
src/main/java/com/pair/ui/QuizPage.java | 320 +++++++-----------
src/main/java/com/pair/ui/RegisterPage.java | 31 +-
src/main/java/com/pair/ui/ResultPage.java | 41 +--
src/main/java/com/pair/ui/StartPage.java | 26 +-
src/main/java/com/pair/ui/StyleHelper.java | 26 ++
src/main/java/com/pair/ui/UIConstants.java | 116 ++++---
12 files changed, 317 insertions(+), 453 deletions(-)
create mode 100644 src/main/java/com/pair/ui/StyleHelper.java
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 6851aea..4b04394 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -10,17 +10,18 @@
-
-
+
-
-
-
-
+
-
+
+
+
+
+
+
@@ -68,7 +69,7 @@
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.git.unshallow": "true",
"SHARE_PROJECT_CONFIGURATION_FILES": "true",
- "git-widget-placeholder": "LiangJunYaoBranch",
+ "git-widget-placeholder": "develop",
"kotlin-language-version-configured": "true",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
@@ -112,8 +113,8 @@
-
-
+
+
@@ -135,6 +136,7 @@
+
diff --git a/src/main/java/com/pair/ui/InfGenPage.java b/src/main/java/com/pair/ui/InfGenPage.java
index 3b7dca1..ec8f21c 100644
--- a/src/main/java/com/pair/ui/InfGenPage.java
+++ b/src/main/java/com/pair/ui/InfGenPage.java
@@ -1,4 +1,3 @@
-// com/ui/InfGenPage.java
package com.pair.ui;
import com.pair.model.Grade;
@@ -8,7 +7,6 @@ import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
-import javafx.scene.text.TextAlignment;
public class InfGenPage extends NavigablePanel {
@@ -19,8 +17,8 @@ public class InfGenPage extends NavigablePanel {
private final Spinner questionCountSpinner = new Spinner<>(10, 30, 10);
private final Button passwordModifyButton = new Button("修改密码");
private final Button generateButton = new Button("生成题目");
- private final Button modifyUsernameButton = new Button("修改用户名"); // 新增
- private final Button modifyEmailButton = new Button("修改邮箱"); // 新增
+ private final Button modifyUsernameButton = new Button("修改用户名");
+ private final Button modifyEmailButton = new Button("修改邮箱");
public InfGenPage(Runnable onBack, String currentUsername, String currentEmail, Grade currentGrade) {
super(onBack);
@@ -32,109 +30,58 @@ public class InfGenPage extends NavigablePanel {
@Override
protected void buildContent() {
- // 外层容器:VBox 居中,带内边距和圆角阴影
- VBox form = new VBox(UIConstants.DEFAULT_SPACING * 1.5);
- form.setAlignment(Pos.CENTER);
- form.setPadding(UIConstants.DEFAULT_PADDING);
- form.setStyle(UIConstants.FORM_STYLE);
- form.setMaxWidth(500);
+ VBox card = StyleHelper.createCard();
+ card.setAlignment(Pos.CENTER);
+ card.setMaxWidth(500);
- // 标题居中
- Label titleLabel = new Label("中小学数学答题系统");
- titleLabel.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
- titleLabel.setTextAlignment(TextAlignment.CENTER);
- titleLabel.setMaxWidth(Double.MAX_VALUE);
+ Label title = new Label("中小学数学答题系统");
+ title.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
- // ========== 创建表单项行(关键:标签左对齐 + 固定宽度)==========
- HBox usernameRow = createFormRow("用户名:", usernameField, modifyUsernameButton);
- HBox emailRow = createFormRow("邮箱:", emailField, modifyEmailButton);
- HBox passwordRow = createFormRow("密码:", passwordLabel, passwordModifyButton);
- HBox gradeRow = createFormRow("学段选择:", gradeChoice, null);
- HBox countRow = createFormRow("题目数量:", questionCountSpinner, generateButton);
-
- // ========== 配置控件样式 ==========
- // 用户名输入框
usernameField.setStyle(UIConstants.INPUT_STYLE);
- usernameField.setPrefWidth(200);
- usernameField.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.INPUT_FONT_SIZE));
-
- // 密码标签
- passwordLabel.setStyle("-fx-font-size: " + UIConstants.INPUT_FONT_SIZE + "px;");
-
- // 邮箱输入框
emailField.setStyle(UIConstants.INPUT_STYLE);
- emailField.setPrefWidth(200);
- emailField.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.INPUT_FONT_SIZE));
-
- // 学段选择框
gradeChoice.getItems().addAll("小学", "初中", "高中");
gradeChoice.setValue("小学");
gradeChoice.setStyle(UIConstants.INPUT_STYLE);
- gradeChoice.setPrefWidth(200);
-
- // 题目数量Spinner
questionCountSpinner.setEditable(true);
- questionCountSpinner.setPrefWidth(200);
questionCountSpinner.getEditor().setStyle(UIConstants.INPUT_STYLE);
- questionCountSpinner.getEditor().setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.INPUT_FONT_SIZE));
-
- // 按钮样式统一
- passwordModifyButton.setStyle(UIConstants.BUTTON_STYLE);
- generateButton.setPrefSize(UIConstants.BUTTON_WIDTH, UIConstants.BUTTON_HEIGHT);
- generateButton.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.BUTTON_FONT_SIZE));
- generateButton.setStyle(UIConstants.BUTTON_STYLE);
-
- // 新增按钮样式
- modifyUsernameButton.setStyle(UIConstants.BUTTON_STYLE);
- modifyUsernameButton.setPrefSize(UIConstants.BUTTON_WIDTH, UIConstants.BUTTON_HEIGHT);
- modifyUsernameButton.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.BUTTON_FONT_SIZE));
-
- modifyEmailButton.setStyle(UIConstants.BUTTON_STYLE);
- modifyEmailButton.setPrefSize(UIConstants.BUTTON_WIDTH, UIConstants.BUTTON_HEIGHT);
- modifyEmailButton.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.BUTTON_FONT_SIZE));
-
- // ========== 添加到表单 ==========
- form.getChildren().addAll(
- titleLabel,
- usernameRow,
- emailRow,
- passwordRow,
- gradeRow,
- countRow
+ passwordLabel.setStyle("-fx-font-size: " + UIConstants.BODY_FONT_SIZE + "px;");
+
+ StyleHelper.styleButton(passwordModifyButton);
+ StyleHelper.styleButton(generateButton);
+ StyleHelper.styleButton(modifyUsernameButton);
+ StyleHelper.styleButton(modifyEmailButton);
+
+ VBox.setMargin(title, new javafx.geometry.Insets(0, 0, 20, 0));
+ card.getChildren().addAll(
+ title,
+ createRow("用户名:", usernameField, modifyUsernameButton),
+ createRow("邮箱:", emailField, modifyEmailButton),
+ createRow("密码:", passwordLabel, passwordModifyButton),
+ createRow("学段选择:", gradeChoice, null),
+ createRow("题目数量:", questionCountSpinner, generateButton)
);
-
- this.setCenter(form);
+ setCenter(card);
+ setStyle("-fx-background-color: " + UIConstants.toWeb(UIConstants.COLOR_BG) + ";");
}
- /**
- * 创建表单项行(标签左对齐,固定宽度,避免偏移)
- */
- private HBox createFormRow(String labelText, Control content, Button rightButton) {
- HBox row = new HBox(15); // 间距15
- row.setAlignment(Pos.CENTER_LEFT); // ← 关键:让整行左对齐
- row.setMaxWidth(Double.MAX_VALUE);
-
- // 标签:左对齐,固定宽度,字体统一
- Label label = new Label(labelText);
- label.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.NORMAL, UIConstants.LABEL_ITEM_TITLE_SIZE));
- label.setTextAlignment(TextAlignment.LEFT); // ← 关键:标签文字左对齐
- label.setPrefWidth(120); // 固定宽度,确保所有标签对齐
-
- row.getChildren().addAll(label, content);
- if (rightButton != null) {
- row.getChildren().add(rightButton);
- }
-
+ private HBox createRow(String text, Control field, Button btn) {
+ HBox row = new HBox(15);
+ row.setAlignment(Pos.CENTER_LEFT);
+ Label label = new Label(text);
+ label.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.NORMAL, UIConstants.BODY_FONT_SIZE));
+ label.setPrefWidth(100);
+ row.getChildren().add(label);
+ if (field != null) row.getChildren().add(field);
+ if (btn != null) row.getChildren().add(btn);
return row;
}
- // Getters...
public TextField getUsernameField() { return usernameField; }
public TextField getEmailField() { return emailField; }
public ChoiceBox getGradeChoice() { return gradeChoice; }
public Spinner getQuestionCountSpinner() { return questionCountSpinner; }
public Button getGenerateButton() { return generateButton; }
public Button getPasswordModifyButton() { return passwordModifyButton; }
- public Button getModifyUsernameButton() { return modifyUsernameButton; } // 新增
- public Button getModifyEmailButton() { return modifyEmailButton; } // 新增
+ public Button getModifyUsernameButton() { return modifyUsernameButton; }
+ public Button getModifyEmailButton() { return modifyEmailButton; }
}
\ No newline at end of file
diff --git a/src/main/java/com/pair/ui/LoginPage.java b/src/main/java/com/pair/ui/LoginPage.java
index 0f5d9d8..fc2e588 100644
--- a/src/main/java/com/pair/ui/LoginPage.java
+++ b/src/main/java/com/pair/ui/LoginPage.java
@@ -1,9 +1,7 @@
-// com/ui/LoginPage.java
package com.pair.ui;
import javafx.geometry.Pos;
import javafx.scene.control.*;
-import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
@@ -17,18 +15,16 @@ public class LoginPage extends NavigablePanel {
public LoginPage(Runnable onBack) {
super(onBack);
- initializeContent(); // 字段初始化
+ initializeContent();
}
@Override
protected void buildContent() {
- VBox form = new VBox(UIConstants.DEFAULT_SPACING);
- form.setAlignment(Pos.CENTER);
- form.setPadding(UIConstants.DEFAULT_PADDING);
- form.setStyle(UIConstants.FORM_STYLE);
+ VBox card = StyleHelper.createCard();
+ card.setAlignment(Pos.CENTER);
- Label titleLabel = new Label("中小学数学答题系统");
- titleLabel.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
+ Label title = new Label("中小学数学答题系统");
+ title.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
usernameOrEmailField.setPromptText("邮箱/用户名");
usernameOrEmailField.setStyle(UIConstants.INPUT_STYLE);
@@ -36,19 +32,11 @@ public class LoginPage extends NavigablePanel {
passwordField.setPromptText("密码(6-10位,含大小写字母和数字)");
passwordField.setStyle(UIConstants.INPUT_STYLE);
- loginButton.setPrefSize(UIConstants.BUTTON_WIDTH, UIConstants.BUTTON_HEIGHT);
- loginButton.setStyle(UIConstants.BUTTON_STYLE);
- loginButton.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.BUTTON_FONT_SIZE));
- loginButton.setOnMouseEntered(e -> loginButton.setStyle(UIConstants.BUTTON_STYLE + UIConstants.BUTTON_HOVER_STYLE));
- loginButton.setOnMouseExited(e -> loginButton.setStyle(UIConstants.BUTTON_STYLE));
+ StyleHelper.styleButton(loginButton);
+ registerLink.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) + ";");
- registerLink.setStyle("-fx-text-fill: " + UIConstants.COLOR_ACCENT + ";");
-
- HBox linkBox = new HBox(registerLink);
- linkBox.setAlignment(Pos.CENTER);
-
- form.getChildren().addAll(titleLabel, usernameOrEmailField, passwordField, loginButton, linkBox);
- this.setCenter(form);
+ card.getChildren().addAll(title, usernameOrEmailField, passwordField, loginButton, registerLink);
+ setCenter(card);
}
public TextField getUsernameOrEmailField() { return usernameOrEmailField; }
diff --git a/src/main/java/com/pair/ui/MainWindow.java b/src/main/java/com/pair/ui/MainWindow.java
index f44a851..c49fd63 100644
--- a/src/main/java/com/pair/ui/MainWindow.java
+++ b/src/main/java/com/pair/ui/MainWindow.java
@@ -165,7 +165,6 @@ public class MainWindow extends BorderPane {
infGenPage.getModifyEmailButton().setOnAction(e -> handleEmailModifyAction(infGenPage));
this.setCenter(infGenPage);
- this.setStyle("-fx-background-color: " + UIConstants.COLOR_BACKGROUND + ";");
}
private void handleUsernameModifyAction(InfGenPage infGenPage) {
try {
diff --git a/src/main/java/com/pair/ui/NavigablePanel.java b/src/main/java/com/pair/ui/NavigablePanel.java
index 75ee27d..ed36518 100644
--- a/src/main/java/com/pair/ui/NavigablePanel.java
+++ b/src/main/java/com/pair/ui/NavigablePanel.java
@@ -15,14 +15,14 @@ public abstract class NavigablePanel extends BorderPane {
public NavigablePanel(Runnable onBack) {
Button backButton = new Button("←");
backButton.setOnAction(e -> onBack.run());
- backButton.setPrefSize(UIConstants.BACK_BUTTON_WIDTH, UIConstants.BACK_BUTTON_HEIGHT);
- backButton.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.BUTTON_FONT_SIZE));
+ backButton.setPrefSize(UIConstants.BTN_WIDTH, UIConstants.BTN_HEIGHT);
+ backButton.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.BTN_FONT_SIZE));
+ /* 原代码不变,只把 backButton 样式统一 */
backButton.setStyle(
"-fx-background-radius: 50; " +
- "-fx-background-color: " + UIConstants.COLOR_ACCENT + "; " +
+ "-fx-background-color: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) + "; " +
"-fx-text-fill: white; " +
- "-fx-font-weight: bold;"
- );
+ "-fx-font-weight: bold;");
HBox topBar = new HBox(10);
topBar.setPadding(UIConstants.TOP_BAR_PADDING);
diff --git a/src/main/java/com/pair/ui/PasswordModifyPage.java b/src/main/java/com/pair/ui/PasswordModifyPage.java
index 0288673..c74f317 100644
--- a/src/main/java/com/pair/ui/PasswordModifyPage.java
+++ b/src/main/java/com/pair/ui/PasswordModifyPage.java
@@ -1,4 +1,3 @@
-// com/ui/PasswordModifyPage.java
package com.pair.ui;
import javafx.geometry.Pos;
@@ -21,13 +20,11 @@ public class PasswordModifyPage extends NavigablePanel {
@Override
protected void buildContent() {
- VBox form = new VBox(UIConstants.DEFAULT_SPACING);
- form.setAlignment(Pos.CENTER);
- form.setPadding(UIConstants.DEFAULT_PADDING);
- form.setStyle(UIConstants.FORM_STYLE);
+ VBox card = StyleHelper.createCard();
+ card.setAlignment(Pos.CENTER);
- Label titleLabel = new Label("中小学数学答题系统");
- titleLabel.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
+ Label title = new Label("中小学数学答题系统");
+ title.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
oldPasswordField.setPromptText("旧密码");
oldPasswordField.setStyle(UIConstants.INPUT_STYLE);
@@ -35,14 +32,11 @@ public class PasswordModifyPage extends NavigablePanel {
newPasswordField.setStyle(UIConstants.INPUT_STYLE);
confirmNewPasswordField.setPromptText("确认新密码");
confirmNewPasswordField.setStyle(UIConstants.INPUT_STYLE);
- modifyButton.setStyle(UIConstants.BUTTON_STYLE);
- modifyButton.setPrefSize(UIConstants.BUTTON_WIDTH, UIConstants.BUTTON_HEIGHT);
- modifyButton.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.BUTTON_FONT_SIZE));
-
- form.getChildren().addAll(
- titleLabel, oldPasswordField, newPasswordField, confirmNewPasswordField, modifyButton
- );
- this.setCenter(form);
+
+ StyleHelper.styleButton(modifyButton);
+
+ card.getChildren().addAll(title, oldPasswordField, newPasswordField, confirmNewPasswordField, modifyButton);
+ setCenter(card);
}
public PasswordField getOldPasswordField() { return oldPasswordField; }
diff --git a/src/main/java/com/pair/ui/QuizPage.java b/src/main/java/com/pair/ui/QuizPage.java
index f4ce292..d52d3ac 100644
--- a/src/main/java/com/pair/ui/QuizPage.java
+++ b/src/main/java/com/pair/ui/QuizPage.java
@@ -1,4 +1,3 @@
-// com/ui/QuizPage.java
package com.pair.ui;
import com.pair.model.ChoiceQuestion;
@@ -26,10 +25,9 @@ public class QuizPage extends NavigablePanel {
private final Button nextButton = new Button("下一题");
private final Button submitButton = new Button("交卷");
- // 题目导航矩阵容器
private final GridPane questionNavGrid = new GridPane();
- private int totalQuestions = 10; // 默认10题,由外部设置
- private int currentQuestionIndex = 0; // 当前题号(0-based)
+ private int totalQuestions = 10;
+ private int currentQuestionIndex = 0;
public QuizPage(Runnable onBack, QuizService quizService) {
super(onBack);
@@ -39,257 +37,183 @@ public class QuizPage extends NavigablePanel {
@Override
protected void buildContent() {
- // 设置整体布局:BorderPane
- this.setPadding(new Insets(20));
- this.setStyle("-fx-background-color: " + UIConstants.COLOR_BACKGROUND + ";");
-
- // 顶部标题栏
- HBox topBar = createTopBar();
- this.setTop(topBar);
-
- // 主体内容:左右分栏
- HBox mainContent = new HBox(20);
- mainContent.setAlignment(Pos.CENTER);
- mainContent.setPadding(new Insets(10));
-
- // 左侧:题目内容区
- VBox leftPanel = createLeftPanel();
- leftPanel.setMaxWidth(600);
-
- // 右侧:题目导航矩阵
- VBox rightPanel = createRightPanel();
- rightPanel.setMaxWidth(300);
- rightPanel.setMinWidth(280);
-
- HBox.setHgrow(leftPanel, Priority.ALWAYS);
- HBox.setHgrow(rightPanel, Priority.NEVER);
- mainContent.getChildren().addAll(leftPanel, rightPanel);
- this.setCenter(mainContent);
-
- // 底部按钮
- VBox bottomBarContainer = new VBox(10);
- bottomBarContainer.setAlignment(Pos.CENTER);
- bottomBarContainer.setPadding(new Insets(10));
- nextButton.setStyle(UIConstants.BUTTON_STYLE);
- submitButton.setStyle(UIConstants.BUTTON_STYLE + "-fx-background-color: " + UIConstants.COLOR_ERROR + ";");
- prevButton.setStyle(UIConstants.BUTTON_STYLE);
- nextButton.setPrefSize(UIConstants.BUTTON_WIDTH, UIConstants.BUTTON_HEIGHT);
- submitButton.setPrefSize(UIConstants.BUTTON_WIDTH, UIConstants.BUTTON_HEIGHT);
- prevButton.setPrefSize(UIConstants.BUTTON_WIDTH, UIConstants.BUTTON_HEIGHT);
-
- HBox buttonRow = new HBox(20);
- buttonRow.setAlignment(Pos.CENTER);
- buttonRow.getChildren().addAll(prevButton, nextButton, submitButton);
-
- bottomBarContainer.getChildren().add(buttonRow);
-
- this.setBottom(bottomBarContainer);
-
- // 初始化按钮状态
- updateButtonVisibility();
+ setPadding(new Insets(20));
+ setStyle("-fx-background-color: " + UIConstants.toWeb(UIConstants.COLOR_BG) + ";");
+
+ /* 顶部标题栏卡片 – 整体下移 20px */
+ VBox topWrap = new VBox(createTopBar());
+ topWrap.setPadding(new Insets(0, 0, 20, 0));
+ setTop(topWrap);
+
+ /* 中部:左右两卡片 – 同样下移 20px */
+ HBox center = new HBox(20);
+ center.setAlignment(Pos.CENTER);
+ Pane leftCard = createLeftCard();
+ Pane rightCard = createRightCard();
+ HBox.setHgrow(leftCard, Priority.ALWAYS);
+ center.getChildren().addAll(leftCard, rightCard);
+ VBox centerWrap = new VBox(center);
+ centerWrap.setPadding(new Insets(20, 0, 0, 0));
+ setCenter(centerWrap);
+
+ /* 底部按钮栏卡片 – 下移 20px */
+ VBox bottomWrap = new VBox(createBottomCard());
+ bottomWrap.setPadding(new Insets(20, 0, 0, 0));
+ setBottom(bottomWrap);
}
- /**
- * 创建顶部标题栏
- */
+ /* ---------------- 私有构造区域 ---------------- */
private HBox createTopBar() {
- HBox topBar = new HBox(20);
- topBar.setAlignment(Pos.CENTER_LEFT);
- topBar.setPadding(new Insets(10));
- topBar.setStyle("-fx-background-color: white; -fx-border-width: 0 0 1 0; -fx-border-color: #bdc3c7;");
+ HBox bar = new HBox(20);
+ bar.setAlignment(Pos.CENTER_LEFT);
+ bar.setPadding(UIConstants.CARD_PADDING);
+ bar.setStyle(UIConstants.CARD_STYLE);
+ bar.setEffect(UIConstants.CARD_SHADOW);
titleLabel.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
- progressLabel.setStyle("-fx-font-size: " + UIConstants.HINT_FONT_SIZE + "px; -fx-text-fill: " + UIConstants.COLOR_HINT + ";");
- topBar.getChildren().addAll(titleLabel, progressLabel);
- return topBar;
+ /* 放大、加粗、主色显示进度 */
+ progressLabel.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, 18));
+ progressLabel.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) + ";");
+
+ Region spacer = new Region();
+ HBox.setHgrow(spacer, Priority.ALWAYS);
+
+ bar.getChildren().addAll(titleLabel, spacer, progressLabel);
+ return bar;
}
- /**
- * 创建左侧题目内容面板
- */
- private VBox createLeftPanel() {
- // 直接使用带样式的 VBox 作为内容容器
- VBox content = new VBox(UIConstants.DEFAULT_SPACING);
- content.setPadding(new Insets(20));
- content.setStyle(UIConstants.FORM_STYLE);
- content.setPrefWidth(550);
- content.setMinWidth(550);
- content.setMaxWidth(550);
- content.setPrefHeight(400);
- content.setMinHeight(400);
- content.setMaxHeight(400);
+ private Pane createLeftCard() {
+ VBox card = StyleHelper.createCard();
+ card.setAlignment(Pos.TOP_LEFT);
+ card.setMaxWidth(680); // 稍宽一点
questionLabel.setWrapText(true);
- questionLabel.setPrefWidth(500);
- questionLabel.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.QUIZ_TITLE_FONT_SIZE));
- questionLabel.setStyle("-fx-font-size: " + UIConstants.QUIZ_TITLE_FONT_SIZE + "px; -fx-text-fill: " + UIConstants.COLOR_PRIMARY + ";");
+ questionLabel.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.SUB_TITLE_FONT_SIZE));
+ questionLabel.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_TEXT) + ";");
- VBox optionsBox = new VBox(10);
+ VBox optionsBox = new VBox(12);
optionsBox.setAlignment(Pos.CENTER_LEFT);
for (int i = 0; i < 4; i++) {
- options[i] = new RadioButton("选项 " + (char)('A' + i));
+ options[i] = new RadioButton("选项 " + (char) ('A' + i));
options[i].setToggleGroup(optionGroup);
- options[i].setStyle("-fx-font-size: " + UIConstants.LABEL_FONT_SIZE + "px;");
+ options[i].setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.BODY_FONT_SIZE));
optionsBox.getChildren().add(options[i]);
}
- content.getChildren().addAll(questionLabel, optionsBox);
- return content;
+ card.getChildren().addAll(questionLabel, optionsBox);
+ return card;
}
- /**
- * 创建右侧题目导航矩阵面板
- */
- private VBox createRightPanel() {
- VBox rightPanel = new VBox(10);
- rightPanel.setAlignment(Pos.TOP_CENTER);
- rightPanel.setPadding(new Insets(20));
- rightPanel.setStyle(UIConstants.FORM_STYLE);
+ private Pane createRightCard() {
+ VBox card = StyleHelper.createCard();
+ card.setAlignment(Pos.TOP_CENTER);
+ card.setMaxWidth(300);
Label navTitle = new Label("题目导航");
- navTitle.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.SUBTITLE_FONT_SIZE));
- navTitle.setAlignment(Pos.CENTER);
-
- // 初始化题目导航矩阵
+ navTitle.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.BODY_FONT_SIZE));
initQuestionNavGrid();
+ card.getChildren().addAll(navTitle, questionNavGrid);
+ return card;
+ }
- rightPanel.getChildren().addAll(navTitle, questionNavGrid);
- return rightPanel;
+ private HBox createBottomCard() {
+ HBox card = new HBox(20);
+ card.setAlignment(Pos.CENTER);
+ card.setPadding(UIConstants.CARD_PADDING);
+ card.setStyle(UIConstants.CARD_STYLE);
+ card.setEffect(UIConstants.CARD_SHADOW);
+
+ StyleHelper.styleButton(prevButton);
+ StyleHelper.styleButton(nextButton);
+ StyleHelper.styleButton(submitButton);
+ submitButton.setStyle(submitButton.getStyle() +
+ "-fx-background-color: " + UIConstants.toWeb(UIConstants.COLOR_ERROR) + ";");
+
+ card.getChildren().addAll(prevButton, nextButton, submitButton);
+ return card;
}
- /**
- * 初始化题目导航网格
- */
+ /* ---------------- 导航网格 ---------------- */
private void initQuestionNavGrid() {
questionNavGrid.getChildren().clear();
- questionNavGrid.setHgap(5);
- questionNavGrid.setVgap(5);
+ questionNavGrid.setHgap(6);
+ questionNavGrid.setVgap(6);
questionNavGrid.setAlignment(Pos.CENTER);
int cols = 5;
- int rows = (totalQuestions + cols - 1) / cols;
-
for (int i = 0; i < totalQuestions; i++) {
int row = i / cols;
int col = i % cols;
- String text = String.valueOf(i + 1);
- Button btn = new Button(text);
- btn.setPrefSize(50, 40);
- btn.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.LABEL_FONT_SIZE));
- btn.setStyle(getButtonStyleForStatus(i));
-
+ Button btn = new Button(String.valueOf(i + 1));
+ btn.setPrefSize(48, 40);
+ btn.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.SMALL_FONT_SIZE));
+ btn.setStyle(getNavBtnStyle(i));
final int index = i;
btn.setOnAction(e -> goToQuestion(index));
-
questionNavGrid.add(btn, col, row);
}
}
- /**
- * 根据题目状态返回按钮样式
- * @param index 题号(0-based)
- * @return CSS样式字符串
- */
- private String getButtonStyleForStatus(int index) {
- if (index == currentQuestionIndex) {
- // 当前题
- return "-fx-background-color: " + UIConstants.COLOR_ACCENT + "; -fx-text-fill: white; -fx-font-weight: bold;";
- } else if (quizService.isAnswered(index)) {
- // 已作答
- return "-fx-background-color: #2ecc71; -fx-text-fill: white;";
- } else {
- // 未作答
- return "-fx-background-color: #ecf0f1; -fx-text-fill: #2c3e50;";
- }
+ private String getNavBtnStyle(int index) {
+ if (index == currentQuestionIndex)
+ return "-fx-background-color: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) +
+ "; -fx-text-fill: white; -fx-font-weight: bold;";
+ if (quizService.isAnswered(index))
+ return "-fx-background-color: " + UIConstants.toWeb(UIConstants.COLOR_SECONDARY) +
+ "; -fx-text-fill: white;";
+ return "-fx-background-color: " + UIConstants.toWeb(UIConstants.COLOR_TEXT_SUB) +
+ "; -fx-text-fill: white;";
}
-
- /**
- * 更新按钮可见性(最后一题显示“交卷”,否则显示“下一题”)
- */
- private void updateButtonVisibility() {
- if (currentQuestionIndex == totalQuestions - 1) {
- nextButton.setVisible(false);
- submitButton.setVisible(true);
- } else {
- nextButton.setVisible(true);
- submitButton.setVisible(false);
- }
- }
-
- /**
- * 跳转到指定题目
- * @param index 题号(0-based)
- */
+ /* ---------------- public 供外部调用 ---------------- */
public void goToQuestion(int index) {
currentQuestionIndex = index;
quizService.goToQuestion(index);
- updateProgressLabel();
- updateQuestionNavButtons();
- updateButtonVisibility();
- loadQuestion(index);
+ updateProgress();
+ refreshNavButtons();
+ loadQuestion();
+ updateBottomBtnVisibility();
}
- /**
- * 更新进度标签
- */
- private void updateProgressLabel() {
- int answeredCount = quizService.getAnsweredCount();
- progressLabel.setText("完成 " + answeredCount + "/" + totalQuestions + " 题");
+ private void updateProgress() {
+ int answered = quizService.getAnsweredCount();
+ progressLabel.setText("完成 " + answered + "/" + totalQuestions + " 题");
}
- /**
- * 更新题目导航按钮样式
- */
- private void updateQuestionNavButtons() {
+ private void refreshNavButtons() {
for (Node node : questionNavGrid.getChildren()) {
if (node instanceof Button) {
Button btn = (Button) node;
- int index = Integer.parseInt(btn.getText()) - 1; // 转换为0-based
- btn.setStyle(getButtonStyleForStatus(index));
+ int idx = Integer.parseInt(btn.getText()) - 1;
+ btn.setStyle(getNavBtnStyle(idx));
}
}
}
- /**
- * 加载题目(从 QuizService 获取)
- * @param index 题号
- */
- private void loadQuestion(int index) {
- System.out.println("🔄 加载题目 " + index + ", options[0]=" + options[0]);
- if (options[0] == null) {
- System.err.println("⚠️ RadioButton 未初始化,跳过题目加载");
- return; // 防止 NPE
- }
- ChoiceQuestion question = quizService.getQuestion(index);
- if (question == null) return;
+ private void updateBottomBtnVisibility() {
+ boolean isLast = currentQuestionIndex == totalQuestions - 1;
+ nextButton.setVisible(!isLast);
+ submitButton.setVisible(isLast);
+ }
- // 显示题目
- questionLabel.setText("第 " + (index + 1) + " 题:\n" + question.getQuestionText());
+ private void loadQuestion() {
+ ChoiceQuestion q = quizService.getQuestion(currentQuestionIndex);
+ if (q == null) return;
+ questionLabel.setText("第 " + (currentQuestionIndex + 1) + " 题:\n" + q.getQuestionText());
- List> optionsList = question.getOptions();
+ List> opts = q.getOptions();
for (int i = 0; i < 4; i++) {
- Object option = optionsList.get(i);
- String optionText = option != null ? option.toString() : "未知";
- options[i].setText((char)('A' + i) + ". " + optionText);
+ String txt = opts.get(i) == null ? "未知" : opts.get(i).toString();
+ options[i].setText((char) ('A' + i) + ". " + txt);
}
- Integer userAnswer = quizService.getUserAnswer(index);
- if (userAnswer != null && userAnswer >= 0 && userAnswer < 4) {
- optionGroup.selectToggle(options[userAnswer]);
- } else {
- optionGroup.selectToggle(null);
- }
-
- // ✅ 强制刷新 UI(可选,通常不需要)
- // Platform.runLater(() -> {
- // this.requestLayout(); // 触发重新布局
- // });
+ Integer ans = quizService.getUserAnswer(currentQuestionIndex);
+ optionGroup.selectToggle(ans == null ? null : options[ans]);
}
- // ========== Getter 方法 ==========
+ /* ---------------- Getters ---------------- */
public Label getProgressLabel() { return progressLabel; }
public Label getQuestionLabel() { return questionLabel; }
public RadioButton[] getOptions() { return options; }
@@ -298,17 +222,9 @@ public class QuizPage extends NavigablePanel {
public Button getPrevButton() { return prevButton; }
public Button getSubmitButton() { return submitButton; }
- // ========== 设置器方法 ==========
public void setTotalQuestions(int total) {
this.totalQuestions = total;
- initQuestionNavGrid(); // 重新初始化导航矩阵
- updateProgressLabel();
- }
-
- public void setCurrentQuestionIndex(int index) {
- this.currentQuestionIndex = index;
- updateQuestionNavButtons();
- updateButtonVisibility();
- loadQuestion(index);
+ initQuestionNavGrid();
+ updateProgress();
}
}
\ No newline at end of file
diff --git a/src/main/java/com/pair/ui/RegisterPage.java b/src/main/java/com/pair/ui/RegisterPage.java
index f8ff1a1..2a33b65 100644
--- a/src/main/java/com/pair/ui/RegisterPage.java
+++ b/src/main/java/com/pair/ui/RegisterPage.java
@@ -1,4 +1,3 @@
-// com/ui/RegisterPage.java
package com.pair.ui;
import javafx.geometry.Pos;
@@ -23,13 +22,11 @@ public class RegisterPage extends NavigablePanel {
@Override
protected void buildContent() {
- VBox form = new VBox(UIConstants.DEFAULT_SPACING);
- form.setAlignment(Pos.CENTER);
- form.setPadding(UIConstants.DEFAULT_PADDING);
- form.setStyle(UIConstants.FORM_STYLE);
+ VBox card = StyleHelper.createCard();
+ card.setAlignment(Pos.CENTER);
- Label titleLabel = new Label("中小学数学答题系统");
- titleLabel.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
+ Label title = new Label("中小学数学答题系统");
+ title.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
emailField.setPromptText("邮箱");
emailField.setStyle(UIConstants.INPUT_STYLE);
@@ -39,21 +36,15 @@ public class RegisterPage extends NavigablePanel {
passwordField.setStyle(UIConstants.INPUT_STYLE);
confirmPasswordField.setPromptText("确认密码");
confirmPasswordField.setStyle(UIConstants.INPUT_STYLE);
- sendCodeButton.setPrefSize(UIConstants.BUTTON_WIDTH, UIConstants.BUTTON_HEIGHT);
- sendCodeButton.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.BUTTON_FONT_SIZE));
- sendCodeButton.setStyle(UIConstants.BUTTON_STYLE);
- registerButton.setPrefSize(UIConstants.BUTTON_WIDTH, UIConstants.BUTTON_HEIGHT);
- registerButton.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.BUTTON_FONT_SIZE));
- registerButton.setStyle(UIConstants.BUTTON_STYLE);
-
- form.getChildren().addAll(
- titleLabel, emailField, sendCodeButton, codeField,
- passwordField, confirmPasswordField, registerButton
- );
- this.setCenter(form);
+
+ StyleHelper.styleButton(sendCodeButton);
+ StyleHelper.styleButton(registerButton);
+
+ card.getChildren().addAll(title, emailField, sendCodeButton, codeField,
+ passwordField, confirmPasswordField, registerButton);
+ setCenter(card);
}
- // getters for controller
public TextField getEmailField() { return emailField; }
public TextField getCodeField() { return codeField; }
public PasswordField getPasswordField() { return passwordField; }
diff --git a/src/main/java/com/pair/ui/ResultPage.java b/src/main/java/com/pair/ui/ResultPage.java
index c82b922..e2e07dd 100644
--- a/src/main/java/com/pair/ui/ResultPage.java
+++ b/src/main/java/com/pair/ui/ResultPage.java
@@ -1,4 +1,3 @@
-// com/ui/ResultPage.java
package com.pair.ui;
import com.pair.model.QuizResult;
@@ -20,34 +19,28 @@ public class ResultPage extends NavigablePanel {
public ResultPage(Runnable onBack, QuizService quizService) {
super(onBack);
- initializeContent();
this.quizService = quizService;
+ initializeContent();
}
@Override
protected void buildContent() {
- VBox form = new VBox(UIConstants.DEFAULT_SPACING);
- form.setAlignment(Pos.CENTER);
- form.setPadding(UIConstants.DEFAULT_PADDING);
- form.setStyle(UIConstants.FORM_STYLE);
-
- Label titleLabel = new Label("中小学数学答题系统");
- titleLabel.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
-
- resultLabel.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.TITLE_FONT_SIZE - 4));
- resultLabel.setStyle("-fx-font-size: " + UIConstants.SCORE_FONT_SIZE + "px; -fx-text-fill: " + UIConstants.COLOR_ACCENT + ";");
- gradeLabel.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.SUBTITLE_FONT_SIZE));
- gradeLabel.setStyle("-fx-font-size: " + UIConstants.SUBTITLE_FONT_SIZE + "px; -fx-text-fill: " + UIConstants.COLOR_PRIMARY + ";");
-
- continueButton.setPrefSize(UIConstants.BUTTON_WIDTH, UIConstants.BUTTON_HEIGHT);
- continueButton.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.BUTTON_FONT_SIZE));
- continueButton.setStyle(UIConstants.BUTTON_STYLE);
- exitButton.setPrefSize(UIConstants.BUTTON_WIDTH, UIConstants.BUTTON_HEIGHT);
- exitButton.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.BUTTON_FONT_SIZE));
- exitButton.setStyle(UIConstants.BUTTON_STYLE);
-
- form.getChildren().addAll(titleLabel, resultLabel, gradeLabel, continueButton, exitButton);
- this.setCenter(form);
+ VBox card = StyleHelper.createCard();
+ card.setAlignment(Pos.CENTER);
+
+ Label title = new Label("中小学数学答题系统");
+ title.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
+
+ resultLabel.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.SUB_TITLE_FONT_SIZE));
+ resultLabel.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) + ";");
+ gradeLabel.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.SUB_TITLE_FONT_SIZE));
+ gradeLabel.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_SECONDARY) + ";");
+
+ StyleHelper.styleButton(continueButton);
+ StyleHelper.styleButton(exitButton);
+
+ card.getChildren().addAll(title, resultLabel, gradeLabel, continueButton, exitButton);
+ setCenter(card);
}
public void updateResult() {
diff --git a/src/main/java/com/pair/ui/StartPage.java b/src/main/java/com/pair/ui/StartPage.java
index e0c34ab..ca26628 100644
--- a/src/main/java/com/pair/ui/StartPage.java
+++ b/src/main/java/com/pair/ui/StartPage.java
@@ -1,4 +1,3 @@
-// com/ui/StartPage.java
package com.pair.ui;
import javafx.geometry.Pos;
@@ -13,25 +12,22 @@ public class StartPage extends VBox {
private final Button startButton;
public StartPage(Runnable onStart) {
- this.setAlignment(Pos.CENTER);
- this.setSpacing(UIConstants.DEFAULT_SPACING);
- this.setPadding(UIConstants.DEFAULT_PADDING);
+ setAlignment(Pos.CENTER);
+ setSpacing(UIConstants.DEFAULT_SPACING);
+ setPadding(UIConstants.PAGE_PADDING);
+ setStyle("-fx-background-color: " + UIConstants.toWeb(UIConstants.COLOR_BG) + ";");
- Label titleLabel = new Label("中小学数学答题系统");
- titleLabel.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
+ Label title = new Label("中小学数学答题系统");
+ title.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
- Label subtitleLabel = new Label("HNU@梁峻耀 吴佰轩");
- subtitleLabel.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.SUBTITLE_FONT_SIZE));
- subtitleLabel.setStyle("-fx-text-fill: gray;");
+ Label sub = new Label("HNU@梁峻耀 吴佰轩");
+ sub.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.SMALL_FONT_SIZE));
+ sub.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_TEXT_SUB) + ";");
startButton = new Button("开始");
- startButton.setPrefSize(UIConstants.BUTTON_WIDTH, UIConstants.BUTTON_HEIGHT);
- startButton.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.BUTTON_FONT_SIZE));
+ StyleHelper.styleButton(startButton);
startButton.setOnAction(e -> onStart.run());
- startButton.setStyle(UIConstants.BUTTON_STYLE);
- startButton.setOnMouseEntered(e -> startButton.setStyle(UIConstants.BUTTON_STYLE + UIConstants.BUTTON_HOVER_STYLE));
- startButton.setOnMouseExited(e -> startButton.setStyle(UIConstants.BUTTON_STYLE));
- this.getChildren().addAll(titleLabel, subtitleLabel, startButton);
+ getChildren().addAll(title, sub, startButton);
}
}
\ No newline at end of file
diff --git a/src/main/java/com/pair/ui/StyleHelper.java b/src/main/java/com/pair/ui/StyleHelper.java
new file mode 100644
index 0000000..6b8e37f
--- /dev/null
+++ b/src/main/java/com/pair/ui/StyleHelper.java
@@ -0,0 +1,26 @@
+/* com/ui/StyleHelper.java */
+package com.pair.ui;
+import javafx.scene.control.Button;
+import javafx.scene.layout.*;
+
+public class StyleHelper {
+ /** 给按钮一次性加上通用样式、hover、pressed 动画 */
+ public static void styleButton(Button btn) {
+ btn.setStyle(UIConstants.BTN_NORMAL);
+ // 仅背景深浅变化,字体、圆角始终一致
+ btn.setOnMouseEntered(e -> btn.setStyle(UIConstants.BTN_HOVER));
+ btn.setOnMouseExited(e -> btn.setStyle(UIConstants.BTN_NORMAL));
+ btn.setOnMousePressed(e -> btn.setStyle(UIConstants.BTN_PRESSED));
+ btn.setOnMouseReleased(e -> btn.setStyle(UIConstants.BTN_NORMAL));
+ btn.setPrefSize(UIConstants.BTN_WIDTH, UIConstants.BTN_HEIGHT);
+ }
+
+ /** 快速生成“白色卡片”VBox */
+ public static VBox createCard() {
+ VBox card = new VBox(UIConstants.DEFAULT_SPACING);
+ card.setPadding(UIConstants.CARD_PADDING);
+ card.setStyle(UIConstants.CARD_STYLE);
+ card.setEffect(UIConstants.CARD_SHADOW);
+ return card;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/pair/ui/UIConstants.java b/src/main/java/com/pair/ui/UIConstants.java
index 75527b4..2ea5141 100644
--- a/src/main/java/com/pair/ui/UIConstants.java
+++ b/src/main/java/com/pair/ui/UIConstants.java
@@ -1,69 +1,81 @@
-// UIConstants.java
package com.pair.ui;
import javafx.geometry.Insets;
+import javafx.scene.effect.DropShadow;
+import javafx.scene.paint.Color;
public final class UIConstants {
private UIConstants() {}
- public static final double LABEL_ITEM_TITLE_SIZE = 16.0;
+ /* ====== 间距 & 边距 ====== */
+ public static final double DEFAULT_SPACING = 16;
+ public static final Insets PAGE_PADDING = new Insets(40);
+ public static final Insets CARD_PADDING = new Insets(24);
+ public static final Insets TOP_BAR_PADDING = new Insets(12);
- // 间距与边距
- public static final double DEFAULT_SPACING = 15.0;
- public static final Insets DEFAULT_PADDING = new Insets(40);
- public static final Insets SMALL_PADDING = new Insets(20);
- public static final Insets TOP_BAR_PADDING = new Insets(10);
-
- // 字体
+ /* ====== 字体 ====== */
public static final String FONT_FAMILY = "Microsoft YaHei";
- public static final double TITLE_FONT_SIZE = 26.0;
- public static final double SUBTITLE_FONT_SIZE = 16.0;
- public static final double BUTTON_FONT_SIZE = 15.0;
- public static final double LABEL_FONT_SIZE = 14.0;
- public static final double INPUT_FONT_SIZE = 14.0;
- public static final double HINT_FONT_SIZE = 12.0;
- public static final double ERROR_FONT_SIZE = 12.0;
- public static final double QUIZ_TITLE_FONT_SIZE = 20.0;
- public static final double SCORE_FONT_SIZE = 32.0;
+ public static final double TITLE_FONT_SIZE = 26;
+ public static final double SUB_TITLE_FONT_SIZE = 18;
+ public static final double BODY_FONT_SIZE = 14;
+ public static final double BTN_FONT_SIZE = 14;
+ public static final double SMALL_FONT_SIZE = 12;
+
+ /* ====== 圆角 & 阴影 ====== */
+ public static final double CARD_RADIUS = 12;
+ public static final DropShadow CARD_SHADOW = new DropShadow(15, 0, 4, Color.web("#00000020"));
+
+ /* ====== 主题色(一键换色只改这里) ====== */
+ public static final Color COLOR_PRIMARY = Color.web("#2563eb"); // 主色
+ public static final Color COLOR_SECONDARY = Color.web("#10b981"); // 辅助
+ public static final Color COLOR_ERROR = Color.web("#ef4444");
+ public static final Color COLOR_BG = Color.web("#f3f4f6");
+ public static final Color COLOR_TEXT = Color.web("#1f2937");
+ public static final Color COLOR_TEXT_SUB = Color.web("#6b7280");
- // 按钮尺寸
- public static final double BUTTON_WIDTH = 140.0;
- public static final double BUTTON_HEIGHT = 40.0;
- public static final double BACK_BUTTON_WIDTH = 80.0;
- public static final double BACK_BUTTON_HEIGHT = 30.0;
+ /* ====== 通用按钮 ====== */
+ public static final double BTN_WIDTH = 140;
+ public static final double BTN_HEIGHT = 40;
+ public static final String BTN_NORMAL =
+ "-fx-background-color: " + toWeb(COLOR_PRIMARY) + ";"
+ + "-fx-text-fill: white;"
+ + "-fx-background-radius: 8;" // 圆角
+ + "-fx-font-family: '" + FONT_FAMILY + "';"
+ + "-fx-font-size: " + BTN_FONT_SIZE + "px;"
+ + "-fx-cursor: hand;";
- // 颜色
- public static final String COLOR_PRIMARY = "#2c3e50";
- public static final String COLOR_ACCENT = "#3498db";
- public static final String COLOR_ERROR = "#e74c3c";
- public static final String COLOR_HINT = "#7f8c8d";
- public static final String COLOR_BACKGROUND = "#ecf0f1";
+ public static final String BTN_HOVER =
+ "-fx-background-color: " + toWeb(COLOR_PRIMARY.darker()) + ";"
+ + "-fx-text-fill: white;"
+ + "-fx-background-radius: 8;"; // 保持圆角
- // 按钮样式
- public static final String BUTTON_STYLE =
- "-fx-background-color: " + COLOR_ACCENT + "; " +
- "-fx-text-fill: white; " +
- "-fx-background-radius: 8; " +
- "-fx-font-size: " + BUTTON_FONT_SIZE + "px; " +
- "-fx-font-family: '" + FONT_FAMILY + "'; " +
- "-fx-cursor: hand;";
+ public static final String BTN_PRESSED =
+ "-fx-background-color: " + toWeb(COLOR_PRIMARY.darker().darker()) + ";"
+ + "-fx-text-fill: white;"
+ + "-fx-background-radius: 8;";
- public static final String BUTTON_HOVER_STYLE =
- "-fx-background-color: #2980b9;";
- // 输入框样式
+ /* ====== 输入框 ====== */
public static final String INPUT_STYLE =
- "-fx-background-radius: 8; " +
- "-fx-border-radius: 8; " +
- "-fx-border-color: #bdc3c7; " +
- "-fx-padding: 8; " +
- "-fx-font-size: " + INPUT_FONT_SIZE + "px; " +
- "-fx-font-family: '" + FONT_FAMILY + "';";
+ "-fx-background-radius: 8;"
+ + "-fx-border-radius: 8;"
+ + "-fx-border-color: " + toWeb(COLOR_TEXT_SUB) + ";"
+ + "-fx-padding: 10;"
+ + "-fx-font-family: '" + FONT_FAMILY + "';"
+ + "-fx-font-size: " + BODY_FONT_SIZE + "px;"
+ + "-fx-pref-width: 240;";
+
+ /* ====== 卡片容器 ====== */
+ public static final String CARD_STYLE =
+ "-fx-background-color: white;"
+ + "-fx-background-radius: " + CARD_RADIUS + ";"
+ + "-fx-border-radius: " + CARD_RADIUS + ";";
+
+
- // 表单容器样式
- public static final String FORM_STYLE =
- "-fx-background-color: white; " +
- "-fx-background-radius: 12; " +
- "-fx-border-radius: 12; " +
- "-fx-effect: dropshadow(gaussian, rgba(0,0,0,0.1), 10, 0, 0, 5);";
+ /* ====== 工具 ====== */
+ public static String toWeb(Color c) {
+ return String.format("#%02X%02X%02X", (int) (c.getRed() * 255),
+ (int) (c.getGreen() * 255), (int) (c.getBlue() * 255));
+ }
}
\ No newline at end of file
--
2.34.1
From 7be70fb219545e720ad702861555886043c57163 Mon Sep 17 00:00:00 2001
From: lsbp <2803234009@qq.com>
Date: Thu, 9 Oct 2025 20:41:25 +0800
Subject: [PATCH 3/5] =?UTF-8?q?=E6=AD=A3=E5=BC=8F=E7=89=882.0.0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 91 +++--
src/main/java/com/pair/MathQuiz/Main.java | 4 -
.../java/com/pair/service/FileIOService.java | 14 +-
.../java/com/pair/service/QuizService.java | 33 +-
.../java/com/pair/service/UserService.java | 21 +-
src/main/java/com/pair/ui/InfGenPage.java | 341 ++++++++++++++++--
src/main/java/com/pair/ui/LoginPage.java | 19 +-
src/main/java/com/pair/ui/MainWindow.java | 16 +-
src/main/java/com/pair/ui/NavigablePanel.java | 32 +-
.../java/com/pair/ui/PasswordModifyPage.java | 29 +-
src/main/java/com/pair/ui/QuizPage.java | 132 +++++--
src/main/java/com/pair/ui/RegisterPage.java | 28 +-
src/main/java/com/pair/ui/ResultPage.java | 20 +-
src/main/java/com/pair/ui/StartPage.java | 7 +-
src/main/java/com/pair/ui/StyleHelper.java | 131 ++++++-
src/main/java/com/pair/ui/UIConstants.java | 148 +++++++-
.../pair/util/AsyncRegistrationHelper.java | 13 +-
src/main/java/com/pair/util/EmailUtil.java | 34 --
.../java/com/pair/util/PasswordValidator.java | 2 +-
19 files changed, 906 insertions(+), 209 deletions(-)
delete mode 100644 src/main/java/com/pair/MathQuiz/Main.java
diff --git a/pom.xml b/pom.xml
index f49faa0..2f2e8b5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,19 +1,26 @@
-
+
4.0.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.1.12
+
+
+
com.mathquiz
MathQuizApp
- 1.03
+ 2.0.0
jar
Math Quiz Application
- 小初高数学学习软�?- JavaFX版本
+ 小初高数学学习软件- JavaFX版本
-
jitpack.io
@@ -22,13 +29,21 @@
+
UTF-8
21
21
21.0.2
+ 3.1.12
+
+
+ 1.5.13
+ 2.2
+ 6.0.22
+
com.google.code.gson
gson
@@ -56,18 +71,28 @@
${javafx.version}
+
+ com.sun.mail
+ javax.mail
+ 1.6.2
+
-
-
- com.sun.mail
- javax.mail
- 1.6.2
-
-
+
+ com.sun.activation
+ javax.activation
+ 1.2.0
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+
org.apache.maven.plugins
maven-compiler-plugin
@@ -79,6 +104,7 @@
+
org.apache.maven.plugins
maven-resources-plugin
@@ -94,40 +120,39 @@
3.6.1
- copy-dependencies
- package
+ copy-javafx-dependencies
+ compile
copy-dependencies
- ${project.build.directory}/dependencies
- false
- false
- true
+ org.openjfx
+ ${project.build.directory}/javafx-libs
-
-
-
-
- org.codehaus.mojo
- exec-maven-plugin
- 3.1.0
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring-boot.maven.plugin.version}
-
- com.pair.Test
-
-
- --add-modules
- javafx.controls,javafx.fxml,javafx.graphics,javafx.base
-
+ com.pair.Test
+
+
+ --module-path ${project.build.directory}/javafx-libs --add-modules javafx.controls,javafx.fxml,javafx.graphics,javafx.base
+ --add-modules javafx.controls,javafx.fxml,javafx.graphics,javafx.base
+
+
+
+
+ repackage
+
+
+
-
-
+
\ No newline at end of file
diff --git a/src/main/java/com/pair/MathQuiz/Main.java b/src/main/java/com/pair/MathQuiz/Main.java
deleted file mode 100644
index f94bf8e..0000000
--- a/src/main/java/com/pair/MathQuiz/Main.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package com.pair.MathQuiz;
-
-public class Main {
-}
diff --git a/src/main/java/com/pair/service/FileIOService.java b/src/main/java/com/pair/service/FileIOService.java
index d420587..82aca59 100644
--- a/src/main/java/com/pair/service/FileIOService.java
+++ b/src/main/java/com/pair/service/FileIOService.java
@@ -101,6 +101,10 @@ public class FileIOService {
return null;
}
+ public boolean existsUsername(String username) throws IOException {
+ return findUserByUsername(username) != null;
+ }
+
public User findUserByEmail(String email) throws IOException {
List users = loadAllUsers();
@@ -113,6 +117,10 @@ public class FileIOService {
return null;
}
+ public boolean existsEmail(String email) throws IOException {
+ return findUserByEmail(email) != null;
+ }
+
public boolean isUsernameExists(String username) throws IOException {
return findUserByUsername(username) != null;
}
@@ -140,7 +148,7 @@ public class FileIOService {
public void saveQuizHistory(QuizHistory history) throws IOException {
String filename = HISTORY_DIR + "/" +
- sanitizeFilename(history.getUsername()) + "_" +
+ sanitizeFilename(history.getUsername()) + "/" +
System.currentTimeMillis() + ".txt";
StringBuilder content = new StringBuilder();
@@ -194,9 +202,9 @@ public class FileIOService {
FileUtils.writeStringToFile(filename, content.toString());
}
- public List getHistoryQuestions() throws IOException {
+ public List getHistoryQuestions(String username) throws IOException {
List historyQuestions = new ArrayList<>();
- File[] files = FileUtils.listFiles(HISTORY_DIR);
+ File[] files = FileUtils.listFiles(HISTORY_DIR + "/" + username);
Arrays.sort(files, (f1, f2) -> Long.compare(f2.lastModified(), f1.lastModified()));
diff --git a/src/main/java/com/pair/service/QuizService.java b/src/main/java/com/pair/service/QuizService.java
index 32f976f..4698b1a 100644
--- a/src/main/java/com/pair/service/QuizService.java
+++ b/src/main/java/com/pair/service/QuizService.java
@@ -20,14 +20,6 @@ public class QuizService {
// ==================== 构造方法 ====================
- public QuizService() throws IOException {
- this.fileIOService = new FileIOService();
- this.userService = new UserService(fileIOService);
- this.currentQuestions = new ArrayList<>();
- this.userAnswers = new ArrayList<>();
- this.currentQuestionIndex = 0;
- }
-
public QuizService(FileIOService fileIOService, UserService userService) {
this.fileIOService = fileIOService;
this.userService = userService;
@@ -43,7 +35,7 @@ public class QuizService {
userAnswers.clear();
currentQuestionIndex = 0;
- Set historyQuestions = getRecentHistoryQuestions();
+ Set historyQuestions = getRecentHistoryQuestions(user.getUsername());
Grade grade = user.getGrade();
currentQuestions = QuestionFactoryManager.generateQuestions(
@@ -57,8 +49,8 @@ public class QuizService {
System.out.println("✓ 已生成 " + currentQuestions.size() + " 道 " + grade + " 题目");
}
- private Set getRecentHistoryQuestions() throws IOException {
- List historyList = fileIOService.getHistoryQuestions();
+ private Set getRecentHistoryQuestions(String username) throws IOException {
+ List historyList = fileIOService.getHistoryQuestions(username);
return new HashSet<>(historyList);
}
@@ -407,22 +399,33 @@ public class QuizService {
// ==================== 数据持久化 ====================
- public void saveQuizHistory(User user) throws IOException {
+ public boolean saveQuizHistory() {
QuizResult result = calculateResult();
QuizHistory history = new QuizHistory(
- user.getUsername(),
+ userService.getCurrentUser().getUsername(),
new Date(),
currentQuestions,
userAnswers,
result.getScore()
);
- fileIOService.saveQuizHistory(history);
+ try {
+ fileIOService.saveQuizHistory(history);
+ } catch (IOException e) {
+ System.err.println(e);
+ return false;
+ }
- userService.updateUserStatistics(user, result.getScore());
+ try {
+ userService.updateUserStatistics(userService.getCurrentUser(), result.getScore());
+ } catch (IOException e) {
+ System.err.println(e);
+ return false;
+ }
System.out.println("✓ 答题记录已保存");
+ return true;
}
// ==================== Getters ====================
diff --git a/src/main/java/com/pair/service/UserService.java b/src/main/java/com/pair/service/UserService.java
index 6f8aebe..09f3752 100644
--- a/src/main/java/com/pair/service/UserService.java
+++ b/src/main/java/com/pair/service/UserService.java
@@ -59,7 +59,7 @@ public class UserService {
throw new IllegalArgumentException("邮箱格式错误!");
}
- // 生成6-10位注册码
+ // 生成6位注册码
String code = PasswordValidator.generateRegistrationCode();
long expiryTime = System.currentTimeMillis() + CODE_EXPIRY_TIME;
@@ -424,11 +424,17 @@ public class UserService {
// ==================== 用户信息管理 ====================
- public boolean updateEmail(User user, String newEmail) throws IOException {
+ public void updateEmail(User user, String newEmail) throws IOException {
if (!validateEmail(newEmail)) {
throw new IllegalArgumentException("邮箱格式错误!");
}
-
+ if (user.getEmail().equals(newEmail)) {
+ return ;
+ }
+ boolean exist = fileIOService.isEmailExists(newEmail);
+ if (exist) {
+ throw new IllegalArgumentException("邮箱已存在!");
+ }
user.setEmail(newEmail);
fileIOService.saveUser(user);
@@ -437,14 +443,19 @@ public class UserService {
fileIOService.saveCurrentUser(user);
}
- // System.out.println("✓ 邮箱更新成功");
- return true;
}
public void updateUsername(User user, String newUsername) throws IOException {
if (newUsername.isEmpty()) {
throw new IllegalArgumentException("用户名不为空!");
}
+ if (user.getUsername().equals(newUsername)) {
+ return;
+ }
+ boolean exist = fileIOService.existsUsername(newUsername);
+ if (exist) {
+ throw new IllegalArgumentException("用户名已存在!");
+ }
user.setUsername(newUsername);
fileIOService.saveUser(user);
diff --git a/src/main/java/com/pair/ui/InfGenPage.java b/src/main/java/com/pair/ui/InfGenPage.java
index ec8f21c..1763326 100644
--- a/src/main/java/com/pair/ui/InfGenPage.java
+++ b/src/main/java/com/pair/ui/InfGenPage.java
@@ -1,12 +1,18 @@
package com.pair.ui;
import com.pair.model.Grade;
+import com.pair.service.UserService;
+import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
+import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
+import javafx.scene.layout.Priority;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
+import javafx.scene.effect.DropShadow;
+import javafx.scene.paint.Color;
public class InfGenPage extends NavigablePanel {
@@ -20,68 +26,333 @@ public class InfGenPage extends NavigablePanel {
private final Button modifyUsernameButton = new Button("修改用户名");
private final Button modifyEmailButton = new Button("修改邮箱");
- public InfGenPage(Runnable onBack, String currentUsername, String currentEmail, Grade currentGrade) {
+ // 用户统计信息标签
+ private final Label totalQuizzesLabel = new Label("0");
+ private final Label averageScoreLabel = new Label("0.0");
+
+ public InfGenPage(Runnable onBack, String currentUsername, String currentEmail, Grade currentGrade, int totalQuizzes, double averageScore) {
super(onBack);
initializeContent();
usernameField.setText(currentUsername);
emailField.setText(currentEmail);
gradeChoice.getSelectionModel().select(currentGrade.ordinal());
+
+ // 更新统计信息
+ updateStatistics(totalQuizzes, averageScore);
}
@Override
protected void buildContent() {
- VBox card = StyleHelper.createCard();
- card.setAlignment(Pos.CENTER);
- card.setMaxWidth(500);
+ // 使用新的统一卡片样式
+ VBox card = StyleHelper.createMediumCard();
+ // 增强标题视觉效果
Label title = new Label("中小学数学答题系统");
- title.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
+ title.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.LARGE_TITLE_FONT_SIZE));
+ title.setStyle(
+ "-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) + "; " +
+ "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.3), 8, 0, 0, 3);"
+ );
- usernameField.setStyle(UIConstants.INPUT_STYLE);
- emailField.setStyle(UIConstants.INPUT_STYLE);
+ // 初始化控件样式
gradeChoice.getItems().addAll("小学", "初中", "高中");
gradeChoice.setValue("小学");
- gradeChoice.setStyle(UIConstants.INPUT_STYLE);
- questionCountSpinner.setEditable(true);
- questionCountSpinner.getEditor().setStyle(UIConstants.INPUT_STYLE);
- passwordLabel.setStyle("-fx-font-size: " + UIConstants.BODY_FONT_SIZE + "px;");
-
- StyleHelper.styleButton(passwordModifyButton);
- StyleHelper.styleButton(generateButton);
- StyleHelper.styleButton(modifyUsernameButton);
- StyleHelper.styleButton(modifyEmailButton);
-
- VBox.setMargin(title, new javafx.geometry.Insets(0, 0, 20, 0));
- card.getChildren().addAll(
- title,
+ StyleHelper.styleTextField(usernameField, String.valueOf(UIConstants.INPUT_MEDIUM_WIDTH));
+ StyleHelper.styleTextField(emailField, String.valueOf(UIConstants.INPUT_MEDIUM_WIDTH));
+ StyleHelper.styleChoiceBox(gradeChoice, String.valueOf(UIConstants.INPUT_MEDIUM_WIDTH));
+ StyleHelper.styleSpinner(questionCountSpinner);
+ passwordLabel.setStyle("-fx-font-size: " + UIConstants.BODY_FONT_SIZE + "px; -fx-pref-width: " + UIConstants.INPUT_MEDIUM_WIDTH + "px;");
+
+ // 按钮样式(注意:你类中已有 styleInfoButton / stylePrimaryButton,直接调用)
+ styleInfoButton(passwordModifyButton);
+ stylePrimaryButton(generateButton);
+ styleInfoButton(modifyUsernameButton);
+ styleInfoButton(modifyEmailButton);
+
+ VBox.setMargin(title, new Insets(0, 0, UIConstants.XLARGE_SPACING, 0));
+
+ // ===== 新布局:左侧表单 + 右侧统计 =====
+ HBox mainContent = new HBox(60); // 增加间距避免拥挤
+ mainContent.setAlignment(Pos.TOP_CENTER);
+ mainContent.setMinWidth(Region.USE_PREF_SIZE);
+
+ // ========== 左侧:用户信息 + 答题设置 ==========
+ VBox leftPanel = new VBox(UIConstants.LARGE_SPACING);
+ leftPanel.setAlignment(Pos.TOP_LEFT);
+ leftPanel.setMinWidth(450); // 增加最小宽度
+ leftPanel.setMaxWidth(500); // 增加最大宽度
+ leftPanel.setStyle("-fx-background-color: white; -fx-border-color: #e0e0e0; -fx-border-radius: 8; -fx-padding: 25;");
+
+ // --- 用户信息 ---
+ Label userInfoTitle = new Label("用户信息");
+ userInfoTitle.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, 18));
+ userInfoTitle.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) + ";");
+
+ VBox userForm = new VBox(UIConstants.MEDIUM_SPACING);
+ userForm.getChildren().addAll(
createRow("用户名:", usernameField, modifyUsernameButton),
createRow("邮箱:", emailField, modifyEmailButton),
- createRow("密码:", passwordLabel, passwordModifyButton),
+ createRow("密码:", passwordLabel, passwordModifyButton)
+ );
+
+ // --- 答题设置 ---
+ Label quizSettingsTitle = new Label("答题设置");
+ quizSettingsTitle.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, 18));
+ quizSettingsTitle.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) + ";");
+
+ VBox quizSettings = new VBox(UIConstants.MEDIUM_SPACING);
+ quizSettings.getChildren().addAll(
createRow("学段选择:", gradeChoice, null),
createRow("题目数量:", questionCountSpinner, generateButton)
);
+
+ // 组装左侧
+ leftPanel.getChildren().addAll(userInfoTitle, userForm, new Separator(), quizSettingsTitle, quizSettings);
+
+ // ========== 右侧:仅学习统计 ==========
+ VBox rightPanel = new VBox();
+ rightPanel.setAlignment(Pos.CENTER); // 统计区域垂直+水平居中
+ rightPanel.setMinWidth(340); // 增加最小宽度
+ rightPanel.setMaxWidth(400); // 增加最大宽度
+
+ VBox statsArea = createStatsArea();
+ rightPanel.getChildren().add(statsArea);
+
+ // 组装主内容
+ mainContent.getChildren().addAll(leftPanel, rightPanel);
+
+ // 组装完整卡片
+ card.getChildren().addAll(title, mainContent);
setCenter(card);
setStyle("-fx-background-color: " + UIConstants.toWeb(UIConstants.COLOR_BG) + ";");
}
private HBox createRow(String text, Control field, Button btn) {
- HBox row = new HBox(15);
+ HBox row = new HBox(20); // 间距适中
row.setAlignment(Pos.CENTER_LEFT);
+ row.setPadding(new Insets(8, 0, 8, 0));
+
+ // 左侧标签容器 - 确保宽度足够
+ VBox labelContainer = new VBox();
+ labelContainer.setPrefWidth(130); // 足够显示“用户名:”等标签
+ labelContainer.setAlignment(Pos.CENTER_RIGHT);
Label label = new Label(text);
- label.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.NORMAL, UIConstants.BODY_FONT_SIZE));
- label.setPrefWidth(100);
- row.getChildren().add(label);
- if (field != null) row.getChildren().add(field);
- if (btn != null) row.getChildren().add(btn);
+ label.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.BODY_FONT_SIZE));
+ label.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_TEXT) + ";");
+ labelContainer.getChildren().add(label);
+
+ // 中间字段容器 - 不再强制拉伸,避免挤压按钮
+ VBox fieldContainer = new VBox();
+ fieldContainer.setAlignment(Pos.CENTER_LEFT);
+ if (field != null) {
+ fieldContainer.getChildren().add(field);
+ if (field instanceof TextField || field instanceof PasswordField) {
+ field.setStyle(field.getStyle() + "-fx-padding: 8 12 8 12;");
+ } else if (field instanceof ChoiceBox) {
+ field.setStyle(field.getStyle() + "-fx-pref-width: 180;");
+ } else if (field instanceof Spinner) {
+ field.setStyle(field.getStyle() + "-fx-pref-width: 180;");
+ }
+ }
+
+ // 右侧按钮容器 - 关键:移除左边距,让按钮自然对齐
+ VBox buttonContainer = new VBox();
+ buttonContainer.setAlignment(Pos.CENTER_LEFT);
+ if (btn != null) {
+ buttonContainer.getChildren().add(btn);
+ // 👇 移除这个!它会导致按钮错位
+ // VBox.setMargin(btn, new Insets(0, 0, 0, 20)); ← 删除这行!
+ }
+
+ // ⚠️ 重要:不要对任何容器设置 Hgrow,避免布局挤压
+ HBox.setHgrow(labelContainer, Priority.NEVER);
+ HBox.setHgrow(fieldContainer, Priority.NEVER);
+ HBox.setHgrow(buttonContainer, Priority.NEVER);
+
+ row.getChildren().addAll(labelContainer, fieldContainer, buttonContainer);
return row;
}
- public TextField getUsernameField() { return usernameField; }
- public TextField getEmailField() { return emailField; }
- public ChoiceBox getGradeChoice() { return gradeChoice; }
- public Spinner getQuestionCountSpinner() { return questionCountSpinner; }
- public Button getGenerateButton() { return generateButton; }
- public Button getPasswordModifyButton() { return passwordModifyButton; }
- public Button getModifyUsernameButton() { return modifyUsernameButton; }
- public Button getModifyEmailButton() { return modifyEmailButton; }
+ public TextField getUsernameField() {
+ return usernameField;
+ }
+
+ public TextField getEmailField() {
+ return emailField;
+ }
+
+ public ChoiceBox getGradeChoice() {
+ return gradeChoice;
+ }
+
+ public Spinner getQuestionCountSpinner() {
+ return questionCountSpinner;
+ }
+
+ public Button getGenerateButton() {
+ return generateButton;
+ }
+
+ public Button getPasswordModifyButton() {
+ return passwordModifyButton;
+ }
+
+ public Button getModifyUsernameButton() {
+ return modifyUsernameButton;
+ }
+
+ public Button getModifyEmailButton() {
+ return modifyEmailButton;
+ }
+
+ /**
+ * 新的按钮样式 - 使用更现代的颜色渐变,修复悬停变形问题
+ */
+ private void styleInfoButton(Button btn) {
+ String baseStyle =
+ "-fx-background-color: linear-gradient(to bottom, #4CAF50, #45a049); " +
+ "-fx-text-fill: white; " +
+ "-fx-font-weight: bold; " +
+ "-fx-background-radius: 8; " +
+ "-fx-border-radius: 8; " +
+ "-fx-border-color: transparent; " +
+ "-fx-border-width: 2; " +
+ "-fx-font-family: '" + UIConstants.FONT_FAMILY + "'; " +
+ "-fx-font-size: " + UIConstants.BTN_FONT_SIZE + "px; " +
+ "-fx-cursor: hand; " +
+ "-fx-padding: 8 16 8 16; "; // 固定内边距,让文字有呼吸空间
+
+ String normalStyle = baseStyle +
+ "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.2), 6, 0, 0, 2);";
+
+ String hoverStyle = baseStyle +
+ "-fx-background-color: linear-gradient(to bottom, #45a049, #3d8b40); " +
+ "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.3), 8, 0, 0, 3);";
+
+ String pressedStyle = baseStyle +
+ "-fx-background-color: linear-gradient(to bottom, #3d8b40, #357a38); " +
+ "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.1), 4, 0, 0, 1);";
+
+ btn.setStyle(normalStyle);
+ btn.setOnMouseEntered(e -> btn.setStyle(hoverStyle));
+ btn.setOnMouseExited(e -> btn.setStyle(normalStyle));
+ btn.setOnMousePressed(e -> btn.setStyle(pressedStyle));
+ btn.setOnMouseReleased(e -> btn.setStyle(normalStyle));
+
+ // ✅ 关键修复:不要硬编码宽度!让按钮自动适应内容
+ btn.setMinWidth(Region.USE_PREF_SIZE);
+ btn.setMaxWidth(Double.MAX_VALUE);
+ btn.setPrefWidth(Region.USE_COMPUTED_SIZE);
+ }
+
+ /**
+ * 创建统计信息区域
+ */
+ private VBox createStatsArea() {
+ VBox statsBox = new VBox(20); // 增加间距
+ statsBox.setAlignment(Pos.CENTER);
+ statsBox.setPadding(new Insets(25)); // 增加内边距
+ statsBox.setStyle(
+ "-fx-background-color: linear-gradient(to bottom, " + UIConstants.toWeb(UIConstants.COLOR_LIGHT) + ", " + UIConstants.toWeb(UIConstants.COLOR_BG) + "); " +
+ "-fx-background-radius: 15; " +
+ "-fx-border-radius: 15; " +
+ "-fx-border-color: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) + "; " +
+ "-fx-border-width: 2; " +
+ "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.15), 12, 0, 0, 6);"
+ );
+ statsBox.setMinWidth(250);
+ statsBox.setMaxWidth(300);
+
+ // 标题
+ Label statsTitle = new Label("📊 学习统计");
+ statsTitle.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, 18));
+ statsTitle.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) + ";");
+
+ // 总答题次数
+ VBox quizzesBox = new VBox(8);
+ quizzesBox.setAlignment(Pos.CENTER);
+ quizzesBox.setStyle(
+ "-fx-background-color: " + UIConstants.toWeb(UIConstants.COLOR_SUCCESS) + "20; " +
+ "-fx-background-radius: 10; " +
+ "-fx-padding: 15;"
+ );
+ Label quizzesTitle = new Label("总答题次数");
+ quizzesTitle.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.BODY_FONT_SIZE));
+ quizzesTitle.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_TEXT_SUB) + ";");
+ totalQuizzesLabel.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, 28));
+ totalQuizzesLabel.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_SUCCESS) + ";");
+ quizzesBox.getChildren().addAll(quizzesTitle, totalQuizzesLabel);
+
+ // 平均分
+ VBox scoreBox = new VBox(8);
+ scoreBox.setAlignment(Pos.CENTER);
+ scoreBox.setStyle(
+ "-fx-background-color: " + UIConstants.toWeb(UIConstants.COLOR_WARNING) + "20; " +
+ "-fx-background-radius: 10; " +
+ "-fx-padding: 15;"
+ );
+ Label scoreTitle = new Label("平均分");
+ scoreTitle.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.BODY_FONT_SIZE));
+ scoreTitle.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_TEXT_SUB) + ";");
+ averageScoreLabel.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, 28));
+ averageScoreLabel.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_WARNING) + ";");
+ scoreBox.getChildren().addAll(scoreTitle, averageScoreLabel);
+
+ // 添加学习建议标签
+ Label suggestionLabel = new Label("继续加油,提升数学能力!");
+ suggestionLabel.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.SMALL_FONT_SIZE));
+ suggestionLabel.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_TEXT_SUB) + "; " +
+ "-fx-font-style: italic;");
+
+ statsBox.getChildren().addAll(statsTitle, quizzesBox, scoreBox, suggestionLabel);
+ return statsBox;
+ }
+
+ /**
+ * 更新统计信息
+ */
+ public void updateStatistics(int totalQuizzes, double averageScore) {
+ totalQuizzesLabel.setText(String.valueOf(totalQuizzes));
+ averageScoreLabel.setText(String.format("%.1f", averageScore));
+ }
+
+ /**
+ * 主要按钮样式 - 使用橙色渐变突出显示,修复悬停变形问题
+ */
+ private void stylePrimaryButton(Button btn) {
+ String baseStyle =
+ "-fx-background-color: linear-gradient(to bottom, #FF9800, #F57C00); " +
+ "-fx-text-fill: white; " +
+ "-fx-font-weight: bold; " +
+ "-fx-background-radius: 8; " +
+ "-fx-border-radius: 8; " +
+ "-fx-border-color: transparent; " +
+ "-fx-border-width: 2; " +
+ "-fx-font-family: '" + UIConstants.FONT_FAMILY + "'; " +
+ "-fx-font-size: " + (UIConstants.BTN_FONT_SIZE + 1) + "px; " +
+ "-fx-cursor: hand; " +
+ "-fx-padding: 10 20 10 20; ";
+
+ String normalStyle = baseStyle +
+ "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.3), 8, 0, 0, 3);";
+
+ String hoverStyle = baseStyle +
+ "-fx-background-color: linear-gradient(to bottom, #F57C00, #EF6C00); " +
+ "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.4), 10, 0, 0, 4);";
+
+ String pressedStyle = baseStyle +
+ "-fx-background-color: linear-gradient(to bottom, #EF6C00, #E65100); " +
+ "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.2), 6, 0, 0, 2);";
+
+ btn.setStyle(normalStyle);
+ btn.setOnMouseEntered(e -> btn.setStyle(hoverStyle));
+ btn.setOnMouseExited(e -> btn.setStyle(normalStyle));
+ btn.setOnMousePressed(e -> btn.setStyle(pressedStyle));
+ btn.setOnMouseReleased(e -> btn.setStyle(normalStyle));
+
+ // ✅ 关键修复:让“生成题目”按钮也自适应宽度
+ btn.setMinWidth(Region.USE_PREF_SIZE);
+ btn.setMaxWidth(Double.MAX_VALUE);
+ btn.setPrefWidth(Region.USE_COMPUTED_SIZE);
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/pair/ui/LoginPage.java b/src/main/java/com/pair/ui/LoginPage.java
index fc2e588..f8c9c53 100644
--- a/src/main/java/com/pair/ui/LoginPage.java
+++ b/src/main/java/com/pair/ui/LoginPage.java
@@ -20,19 +20,26 @@ public class LoginPage extends NavigablePanel {
@Override
protected void buildContent() {
- VBox card = StyleHelper.createCard();
- card.setAlignment(Pos.CENTER);
+ // 使用新的统一卡片样式
+ VBox card = StyleHelper.createMediumCard();
+ // 增强标题视觉效果
Label title = new Label("中小学数学答题系统");
- title.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
+ title.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.LARGE_TITLE_FONT_SIZE)); // 使用更大的字体
+ title.setStyle(
+ "-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) + "; " +
+ "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.3), 8, 0, 0, 3);" // 添加阴影效果
+ );
+ // 使用统一的输入框样式
usernameOrEmailField.setPromptText("邮箱/用户名");
- usernameOrEmailField.setStyle(UIConstants.INPUT_STYLE);
+ StyleHelper.styleInputField(usernameOrEmailField);
passwordField.setPromptText("密码(6-10位,含大小写字母和数字)");
- passwordField.setStyle(UIConstants.INPUT_STYLE);
+ StyleHelper.stylePasswordField(passwordField);
- StyleHelper.styleButton(loginButton);
+ // 使用统一的按钮样式
+ StyleHelper.stylePrimaryButton(loginButton);
registerLink.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) + ";");
card.getChildren().addAll(title, usernameOrEmailField, passwordField, loginButton, registerLink);
diff --git a/src/main/java/com/pair/ui/MainWindow.java b/src/main/java/com/pair/ui/MainWindow.java
index c49fd63..d0502fb 100644
--- a/src/main/java/com/pair/ui/MainWindow.java
+++ b/src/main/java/com/pair/ui/MainWindow.java
@@ -2,6 +2,7 @@
package com.pair.ui;
import com.pair.model.User;
+import com.pair.service.FileIOService;
import com.pair.service.QuizService;
import com.pair.service.UserService;
import com.pair.util.AsyncRegistrationHelper;
@@ -15,14 +16,16 @@ import javafx.stage.Stage;
import java.io.IOException;
public class MainWindow extends BorderPane {
- private final UserService userService = new UserService();
- private final QuizService quizService = new QuizService();
+ private final UserService userService;
+ private final QuizService quizService;
private User currentUser;
private final Stage primaryStage;
private Panel currentPanel;
public MainWindow(Stage primaryStage) throws IOException {
+ userService = new UserService();
+ quizService = new QuizService(new FileIOService(), userService);
this.primaryStage = primaryStage;
showStartPage();
}
@@ -139,8 +142,13 @@ public class MainWindow extends BorderPane {
}
private void initInfGenPage() {
- InfGenPage infGenPage = new InfGenPage(() -> navigateTo(Panel.LOGIN), userService.getCurrentUser().getUsername(),
- userService.getCurrentUser().getEmail(), userService.getCurrentUser().getGrade());
+ User currentUser = userService.getCurrentUser();
+ InfGenPage infGenPage = new InfGenPage(() -> navigateTo(Panel.LOGIN),
+ currentUser.getUsername(),
+ currentUser.getEmail(),
+ currentUser.getGrade(),
+ currentUser.getTotalQuizzes(),
+ currentUser.getAverageScore());
infGenPage.getGenerateButton().setOnAction(e -> {
int count = infGenPage.getQuestionCountSpinner().getValue();
try {
diff --git a/src/main/java/com/pair/ui/NavigablePanel.java b/src/main/java/com/pair/ui/NavigablePanel.java
index ed36518..8abea3a 100644
--- a/src/main/java/com/pair/ui/NavigablePanel.java
+++ b/src/main/java/com/pair/ui/NavigablePanel.java
@@ -2,6 +2,7 @@
package com.pair.ui;
import javafx.application.Platform;
+import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
@@ -15,21 +16,32 @@ public abstract class NavigablePanel extends BorderPane {
public NavigablePanel(Runnable onBack) {
Button backButton = new Button("←");
backButton.setOnAction(e -> onBack.run());
- backButton.setPrefSize(UIConstants.BTN_WIDTH, UIConstants.BTN_HEIGHT);
- backButton.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.BTN_FONT_SIZE));
- /* 原代码不变,只把 backButton 样式统一 */
+ backButton.setPrefSize(50, 50); // 调整为正方形按钮
+ backButton.setFont(Font.font(UIConstants.FONT_FAMILY, 18)); // 增大字体
+ /* 创建悬浮按钮样式 - 半透明圆形按钮 */
backButton.setStyle(
"-fx-background-radius: 50; " +
- "-fx-background-color: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) + "; " +
+ "-fx-background-color: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) + "E6; " + // 添加透明度
"-fx-text-fill: white; " +
- "-fx-font-weight: bold;");
+ "-fx-font-weight: bold; " +
+ "-fx-cursor: hand; " +
+ "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.3), 10, 0, 0, 2);"); // 添加阴影效果
- HBox topBar = new HBox(10);
- topBar.setPadding(UIConstants.TOP_BAR_PADDING);
- topBar.setAlignment(Pos.CENTER_LEFT);
- topBar.getChildren().add(backButton);
+ /* 添加悬停效果 */
+ backButton.setOnMouseEntered(e ->
+ backButton.setStyle(backButton.getStyle().replace("E6;", "FF;")) // 鼠标悬停时不透明
+ );
+ backButton.setOnMouseExited(e ->
+ backButton.setStyle(backButton.getStyle().replace("FF;", "E6;")) // 鼠标离开时半透明
+ );
- this.setTop(topBar);
+ /* 将返回按钮悬浮在左上角,带有边距 */
+ HBox leftBar = new HBox(10);
+ leftBar.setPadding(new Insets(20, 0, 0, 20)); // 顶部20px,左侧20px边距
+ leftBar.setAlignment(Pos.TOP_LEFT);
+ leftBar.getChildren().add(backButton);
+
+ this.setLeft(leftBar);
}
protected static void showErrorAlert(String title, String message) {
diff --git a/src/main/java/com/pair/ui/PasswordModifyPage.java b/src/main/java/com/pair/ui/PasswordModifyPage.java
index c74f317..de09f5a 100644
--- a/src/main/java/com/pair/ui/PasswordModifyPage.java
+++ b/src/main/java/com/pair/ui/PasswordModifyPage.java
@@ -20,20 +20,29 @@ public class PasswordModifyPage extends NavigablePanel {
@Override
protected void buildContent() {
- VBox card = StyleHelper.createCard();
- card.setAlignment(Pos.CENTER);
-
- Label title = new Label("中小学数学答题系统");
- title.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
-
+ // 使用新的统一卡片样式
+ VBox card = StyleHelper.createMediumCard();
+
+ // 增强标题视觉效果
+ Label title = new Label("修改密码");
+ title.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.LARGE_TITLE_FONT_SIZE)); // 使用更大的字体
+ title.setStyle(
+ "-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_WARNING) + "; " + // 使用警告色(橙色)突出显示
+ "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.3), 8, 0, 0, 3);" // 添加阴影效果
+ );
+
+ // 使用统一的输入框样式
oldPasswordField.setPromptText("旧密码");
- oldPasswordField.setStyle(UIConstants.INPUT_STYLE);
+ StyleHelper.stylePasswordField(oldPasswordField);
+
newPasswordField.setPromptText("新密码(6-10位)");
- newPasswordField.setStyle(UIConstants.INPUT_STYLE);
+ StyleHelper.stylePasswordField(newPasswordField);
+
confirmNewPasswordField.setPromptText("确认新密码");
- confirmNewPasswordField.setStyle(UIConstants.INPUT_STYLE);
+ StyleHelper.stylePasswordField(confirmNewPasswordField);
- StyleHelper.styleButton(modifyButton);
+ // 使用统一的按钮样式
+ StyleHelper.stylePrimaryButton(modifyButton);
card.getChildren().addAll(title, oldPasswordField, newPasswordField, confirmNewPasswordField, modifyButton);
setCenter(card);
diff --git a/src/main/java/com/pair/ui/QuizPage.java b/src/main/java/com/pair/ui/QuizPage.java
index d52d3ac..a17ad79 100644
--- a/src/main/java/com/pair/ui/QuizPage.java
+++ b/src/main/java/com/pair/ui/QuizPage.java
@@ -45,20 +45,39 @@ public class QuizPage extends NavigablePanel {
topWrap.setPadding(new Insets(0, 0, 20, 0));
setTop(topWrap);
- /* 中部:左右两卡片 – 同样下移 20px */
- HBox center = new HBox(20);
- center.setAlignment(Pos.CENTER);
- Pane leftCard = createLeftCard();
- Pane rightCard = createRightCard();
- HBox.setHgrow(leftCard, Priority.ALWAYS);
- center.getChildren().addAll(leftCard, rightCard);
- VBox centerWrap = new VBox(center);
- centerWrap.setPadding(new Insets(20, 0, 0, 0));
+ /* 中部:左右两卡片 – 调整布局创造参差美感 */
+ // 创建主内容区域,使用BorderPane实现更灵活的布局
+ BorderPane mainContent = new BorderPane();
+
+ /* 左侧题目卡片 - 占据主要区域 */
+ Pane leftCard = createLeftCard();
+ mainContent.setCenter(leftCard);
+
+ /* 右侧导航区域 - 放置题目导航 */
+ VBox rightArea = new VBox(20);
+ rightArea.setAlignment(Pos.TOP_CENTER);
+ rightArea.setPadding(new Insets(0, 0, 100, 0)); // 增加底部内边距,让导航下移
+
+ // 创建题目导航标题
+ Label navTitle = new Label("题目导航");
+ navTitle.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.BODY_FONT_SIZE));
+
+ // 创建题目导航网格
+ initQuestionNavGrid();
+
+ // 添加标题和导航到右侧区域
+ rightArea.getChildren().addAll(navTitle, questionNavGrid);
+
+ mainContent.setRight(rightArea);
+
+ /* 将主内容区域添加到中心,减少上移间距 */
+ VBox centerWrap = new VBox(mainContent);
+ centerWrap.setPadding(new Insets(10, 0, 0, 0)); // 减少上移间距
setCenter(centerWrap);
- /* 底部按钮栏卡片 – 下移 20px */
+ /* 底部按钮栏卡片 – 位置上移,更接近导航 */
VBox bottomWrap = new VBox(createBottomCard());
- bottomWrap.setPadding(new Insets(20, 0, 0, 0));
+ bottomWrap.setPadding(new Insets(10, 0, 0, 0)); // 减少间距,让导航更接近底部
setBottom(bottomWrap);
}
@@ -70,7 +89,12 @@ public class QuizPage extends NavigablePanel {
bar.setStyle(UIConstants.CARD_STYLE);
bar.setEffect(UIConstants.CARD_SHADOW);
- titleLabel.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
+ // 增强标题视觉效果
+ titleLabel.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.LARGE_TITLE_FONT_SIZE)); // 使用更大的字体
+ titleLabel.setStyle(
+ "-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) + "; " +
+ "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.3), 8, 0, 0, 3);" // 添加阴影效果
+ );
/* 放大、加粗、主色显示进度 */
progressLabel.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, 18));
@@ -87,17 +111,27 @@ public class QuizPage extends NavigablePanel {
VBox card = StyleHelper.createCard();
card.setAlignment(Pos.TOP_LEFT);
card.setMaxWidth(680); // 稍宽一点
+ card.setMinHeight(450); // 增加最小高度,让题目区域更大
+ card.setStyle(card.getStyle() + "-fx-padding: 30;"); // 增加内边距,让内容更舒展
questionLabel.setWrapText(true);
questionLabel.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.SUB_TITLE_FONT_SIZE));
questionLabel.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_TEXT) + ";");
- VBox optionsBox = new VBox(12);
+ VBox optionsBox = new VBox(15); // 增加选项间距
optionsBox.setAlignment(Pos.CENTER_LEFT);
+ optionsBox.setPadding(new Insets(20, 0, 0, 0)); // 增加选项区域的上边距
for (int i = 0; i < 4; i++) {
options[i] = new RadioButton("选项 " + (char) ('A' + i));
options[i].setToggleGroup(optionGroup);
options[i].setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.BODY_FONT_SIZE));
+ // 添加选项变化监听器,实现实时保存
+ final int optionIndex = i;
+ options[i].selectedProperty().addListener((obs, wasSelected, isSelected) -> {
+ if (isSelected) {
+ saveCurrentAnswer(optionIndex);
+ }
+ });
optionsBox.getChildren().add(options[i]);
}
@@ -109,10 +143,15 @@ public class QuizPage extends NavigablePanel {
VBox card = StyleHelper.createCard();
card.setAlignment(Pos.TOP_CENTER);
card.setMaxWidth(300);
+ card.setMinHeight(350); // 设置比左侧稍小的高度,创造参差美感
+ card.setStyle(card.getStyle() + "-fx-padding: 20;"); // 增加内边距
Label navTitle = new Label("题目导航");
navTitle.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.BODY_FONT_SIZE));
+ navTitle.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) + ";");
+
initQuestionNavGrid();
+
card.getChildren().addAll(navTitle, questionNavGrid);
return card;
}
@@ -124,11 +163,29 @@ public class QuizPage extends NavigablePanel {
card.setStyle(UIConstants.CARD_STYLE);
card.setEffect(UIConstants.CARD_SHADOW);
- StyleHelper.styleButton(prevButton);
- StyleHelper.styleButton(nextButton);
- StyleHelper.styleButton(submitButton);
- submitButton.setStyle(submitButton.getStyle() +
- "-fx-background-color: " + UIConstants.toWeb(UIConstants.COLOR_ERROR) + ";");
+ // 使用统一的按钮样式
+ StyleHelper.styleButton(prevButton); // 使用默认样式
+ StyleHelper.styleButton(nextButton); // 使用默认样式
+ StyleHelper.styleErrorButton(submitButton); // 提交按钮使用错误样式(红色)突出显示
+
+ // 为上一题按钮添加保存逻辑
+ prevButton.setOnAction(e -> {
+ saveCurrentAnswer(); // 保存当前答案
+ if (currentQuestionIndex > 0) {
+ goToQuestion(currentQuestionIndex - 1);
+ }
+ });
+
+ // 为下一题按钮添加保存逻辑
+ nextButton.setOnAction(e -> {
+ saveCurrentAnswer(); // 保存当前答案
+ if (currentQuestionIndex < totalQuestions - 1) {
+ goToQuestion(currentQuestionIndex + 1);
+ }
+ });
+
+ // 提交按钮不需要保存,直接交卷
+ // submitButton的点击事件由外部处理
card.getChildren().addAll(prevButton, nextButton, submitButton);
return card;
@@ -137,8 +194,8 @@ public class QuizPage extends NavigablePanel {
/* ---------------- 导航网格 ---------------- */
private void initQuestionNavGrid() {
questionNavGrid.getChildren().clear();
- questionNavGrid.setHgap(6);
- questionNavGrid.setVgap(6);
+ questionNavGrid.setHgap(8); // 增加水平间距
+ questionNavGrid.setVgap(8); // 增加垂直间距
questionNavGrid.setAlignment(Pos.CENTER);
int cols = 5;
@@ -147,7 +204,7 @@ public class QuizPage extends NavigablePanel {
int col = i % cols;
Button btn = new Button(String.valueOf(i + 1));
- btn.setPrefSize(48, 40);
+ btn.setPrefSize(52, 44); // 稍大一点的按钮
btn.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.SMALL_FONT_SIZE));
btn.setStyle(getNavBtnStyle(i));
final int index = i;
@@ -167,8 +224,41 @@ public class QuizPage extends NavigablePanel {
"; -fx-text-fill: white;";
}
+ /* ---------------- 答案保存功能 ---------------- */
+ private void saveCurrentAnswer(int selectedOptionIndex) {
+ // 保存当前选择的答案
+ quizService.submitAnswer(currentQuestionIndex, selectedOptionIndex);
+ // 更新进度显示
+ updateProgress();
+ // 刷新导航按钮状态
+ refreshNavButtons();
+ }
+
+ private void saveCurrentAnswer() {
+ // 获取当前选中的选项
+ Toggle selectedToggle = optionGroup.getSelectedToggle();
+ if (selectedToggle != null) {
+ for (int i = 0; i < 4; i++) {
+ if (options[i] == selectedToggle) {
+ saveCurrentAnswer(i);
+ break;
+ }
+ }
+ } else {
+ // 如果没有选中任何选项,清除当前答案
+ // QuizService没有直接的清除方法,我们保存一个无效索引来表示清除
+ // 或者使用submitCurrentAnswer(-1)来表示清除(如果支持)
+ // 暂时不处理清除逻辑,保持原有状态
+ updateProgress();
+ refreshNavButtons();
+ }
+ }
+
/* ---------------- public 供外部调用 ---------------- */
public void goToQuestion(int index) {
+ // 在切换题目之前,先保存当前题目的答案
+ saveCurrentAnswer();
+
currentQuestionIndex = index;
quizService.goToQuestion(index);
updateProgress();
diff --git a/src/main/java/com/pair/ui/RegisterPage.java b/src/main/java/com/pair/ui/RegisterPage.java
index 2a33b65..c95866b 100644
--- a/src/main/java/com/pair/ui/RegisterPage.java
+++ b/src/main/java/com/pair/ui/RegisterPage.java
@@ -22,23 +22,33 @@ public class RegisterPage extends NavigablePanel {
@Override
protected void buildContent() {
- VBox card = StyleHelper.createCard();
- card.setAlignment(Pos.CENTER);
+ // 使用新的统一卡片样式
+ VBox card = StyleHelper.createMediumCard();
+ // 增强标题视觉效果
Label title = new Label("中小学数学答题系统");
- title.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
+ title.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.LARGE_TITLE_FONT_SIZE)); // 使用更大的字体
+ title.setStyle(
+ "-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) + "; " +
+ "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.3), 8, 0, 0, 3);" // 添加阴影效果
+ );
+ // 使用统一的输入框样式
emailField.setPromptText("邮箱");
- emailField.setStyle(UIConstants.INPUT_STYLE);
+ StyleHelper.styleInputField(emailField);
+
codeField.setPromptText("注册码");
- codeField.setStyle(UIConstants.INPUT_STYLE);
+ StyleHelper.styleInputField(codeField);
+
passwordField.setPromptText("密码(6-10位)");
- passwordField.setStyle(UIConstants.INPUT_STYLE);
+ StyleHelper.stylePasswordField(passwordField);
+
confirmPasswordField.setPromptText("确认密码");
- confirmPasswordField.setStyle(UIConstants.INPUT_STYLE);
+ StyleHelper.stylePasswordField(confirmPasswordField);
- StyleHelper.styleButton(sendCodeButton);
- StyleHelper.styleButton(registerButton);
+ // 使用统一的按钮样式
+ StyleHelper.styleSecondaryButton(sendCodeButton); // 次要按钮样式
+ StyleHelper.stylePrimaryButton(registerButton); // 主要按钮样式
card.getChildren().addAll(title, emailField, sendCodeButton, codeField,
passwordField, confirmPasswordField, registerButton);
diff --git a/src/main/java/com/pair/ui/ResultPage.java b/src/main/java/com/pair/ui/ResultPage.java
index e2e07dd..0f9b24f 100644
--- a/src/main/java/com/pair/ui/ResultPage.java
+++ b/src/main/java/com/pair/ui/ResultPage.java
@@ -25,19 +25,25 @@ public class ResultPage extends NavigablePanel {
@Override
protected void buildContent() {
- VBox card = StyleHelper.createCard();
- card.setAlignment(Pos.CENTER);
+ // 使用新的统一卡片样式
+ VBox card = StyleHelper.createMediumCard();
+ // 增强标题视觉效果
Label title = new Label("中小学数学答题系统");
- title.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
+ title.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.LARGE_TITLE_FONT_SIZE)); // 使用更大的字体
+ title.setStyle(
+ "-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) + "; " +
+ "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.3), 8, 0, 0, 3);" // 添加阴影效果
+ );
resultLabel.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.SUB_TITLE_FONT_SIZE));
resultLabel.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) + ";");
gradeLabel.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.SUB_TITLE_FONT_SIZE));
gradeLabel.setStyle("-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_SECONDARY) + ";");
- StyleHelper.styleButton(continueButton);
- StyleHelper.styleButton(exitButton);
+ // 使用统一的按钮样式
+ StyleHelper.stylePrimaryButton(continueButton); // 继续答题使用主要按钮样式
+ StyleHelper.styleSecondaryButton(exitButton); // 退出使用次要按钮样式
card.getChildren().addAll(title, resultLabel, gradeLabel, continueButton, exitButton);
setCenter(card);
@@ -45,6 +51,10 @@ public class ResultPage extends NavigablePanel {
public void updateResult() {
QuizResult result = quizService.calculateResult();
+ boolean isSaveData = quizService.saveQuizHistory();
+ if (!isSaveData) {
+ NavigablePanel.showErrorAlert("保存失败","数据持久化失败!");
+ }
resultLabel.setText(result.toString());
gradeLabel.setText(quizService.getGrade(result));
}
diff --git a/src/main/java/com/pair/ui/StartPage.java b/src/main/java/com/pair/ui/StartPage.java
index ca26628..6513f1d 100644
--- a/src/main/java/com/pair/ui/StartPage.java
+++ b/src/main/java/com/pair/ui/StartPage.java
@@ -17,8 +17,13 @@ public class StartPage extends VBox {
setPadding(UIConstants.PAGE_PADDING);
setStyle("-fx-background-color: " + UIConstants.toWeb(UIConstants.COLOR_BG) + ";");
+ // 增强标题视觉效果 - StartPage使用超大字体作为主页面
Label title = new Label("中小学数学答题系统");
- title.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.TITLE_FONT_SIZE));
+ title.setFont(Font.font(UIConstants.FONT_FAMILY, FontWeight.BOLD, UIConstants.XLARGE_TITLE_FONT_SIZE)); // 使用超大字体
+ title.setStyle(
+ "-fx-text-fill: " + UIConstants.toWeb(UIConstants.COLOR_PRIMARY) + "; " +
+ "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.3), 10, 0, 0, 4);" // 添加更强的阴影效果
+ );
Label sub = new Label("HNU@梁峻耀 吴佰轩");
sub.setFont(Font.font(UIConstants.FONT_FAMILY, UIConstants.SMALL_FONT_SIZE));
diff --git a/src/main/java/com/pair/ui/StyleHelper.java b/src/main/java/com/pair/ui/StyleHelper.java
index 6b8e37f..97a4a4a 100644
--- a/src/main/java/com/pair/ui/StyleHelper.java
+++ b/src/main/java/com/pair/ui/StyleHelper.java
@@ -1,10 +1,15 @@
/* com/ui/StyleHelper.java */
package com.pair.ui;
+import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.layout.*;
+import javafx.scene.control.TextField;
+import javafx.scene.control.PasswordField;
+import javafx.scene.control.ChoiceBox;
+import javafx.scene.control.Spinner;
public class StyleHelper {
- /** 给按钮一次性加上通用样式、hover、pressed 动画 */
+ /** 给按钮一次性加上通用样式、hover、pressed 动画 - 兼容方法 */
public static void styleButton(Button btn) {
btn.setStyle(UIConstants.BTN_NORMAL);
// 仅背景深浅变化,字体、圆角始终一致
@@ -15,7 +20,62 @@ public class StyleHelper {
btn.setPrefSize(UIConstants.BTN_WIDTH, UIConstants.BTN_HEIGHT);
}
- /** 快速生成“白色卡片”VBox */
+ /** 新的统一按钮样式方法 - 支持不同类型按钮 */
+ public static void styleButton(Button btn, String type) {
+ btn.setStyle(UIConstants.getButtonNormalStyle(type));
+ btn.setOnMouseEntered(e -> btn.setStyle(UIConstants.getButtonHoverStyle(type)));
+ btn.setOnMouseExited(e -> btn.setStyle(UIConstants.getButtonNormalStyle(type)));
+ btn.setOnMousePressed(e -> btn.setStyle(UIConstants.getButtonPressedStyle(type)));
+ btn.setOnMouseReleased(e -> btn.setStyle(UIConstants.getButtonNormalStyle(type)));
+
+ // 根据按钮类型设置尺寸
+ switch (type) {
+ case UIConstants.BTN_TYPE_PRIMARY:
+ case UIConstants.BTN_TYPE_WARNING:
+ btn.setPrefSize(UIConstants.BTN_LARGE_WIDTH, UIConstants.BTN_LARGE_HEIGHT);
+ break;
+ case UIConstants.BTN_TYPE_SUCCESS:
+ case UIConstants.BTN_TYPE_ERROR:
+ case UIConstants.BTN_TYPE_INFO:
+ btn.setPrefSize(UIConstants.BTN_MEDIUM_WIDTH, UIConstants.BTN_MEDIUM_HEIGHT);
+ break;
+ default:
+ btn.setPrefSize(UIConstants.BTN_SMALL_WIDTH, UIConstants.BTN_SMALL_HEIGHT);
+ break;
+ }
+ }
+
+ /** 主要按钮 - 使用主色调 */
+ public static void stylePrimaryButton(Button btn) {
+ styleButton(btn, UIConstants.BTN_TYPE_PRIMARY);
+ }
+
+ /** 次要按钮 - 使用辅助色调 */
+ public static void styleSecondaryButton(Button btn) {
+ styleButton(btn, UIConstants.BTN_TYPE_SECONDARY);
+ }
+
+ /** 成功按钮 - 使用绿色调 */
+ public static void styleSuccessButton(Button btn) {
+ styleButton(btn, UIConstants.BTN_TYPE_SUCCESS);
+ }
+
+ /** 警告按钮 - 使用橙色调 */
+ public static void styleWarningButton(Button btn) {
+ styleButton(btn, UIConstants.BTN_TYPE_WARNING);
+ }
+
+ /** 错误按钮 - 使用红色调 */
+ public static void styleErrorButton(Button btn) {
+ styleButton(btn, UIConstants.BTN_TYPE_ERROR);
+ }
+
+ /** 信息按钮 - 使用蓝色调 */
+ public static void styleInfoButton(Button btn) {
+ styleButton(btn, UIConstants.BTN_TYPE_INFO);
+ }
+
+ /** 快速生成"白色卡片"VBox - 兼容方法 */
public static VBox createCard() {
VBox card = new VBox(UIConstants.DEFAULT_SPACING);
card.setPadding(UIConstants.CARD_PADDING);
@@ -23,4 +83,71 @@ public class StyleHelper {
card.setEffect(UIConstants.CARD_SHADOW);
return card;
}
+
+ /** 新的卡片生成方法 - 支持不同尺寸 */
+ public static VBox createCard(String size) {
+ VBox card = new VBox(UIConstants.MEDIUM_SPACING);
+ card.setPadding(UIConstants.CARD_PADDING);
+ card.setAlignment(Pos.CENTER);
+
+ switch (size) {
+ case "SMALL":
+ card.setStyle(UIConstants.CARD_STYLE_SMALL);
+ break;
+ case "LARGE":
+ card.setStyle(UIConstants.CARD_STYLE_LARGE);
+ break;
+ default: // MEDIUM
+ card.setStyle(UIConstants.CARD_STYLE_MEDIUM);
+ break;
+ }
+
+ return card;
+ }
+
+ /** 创建小型卡片 */
+ public static VBox createSmallCard() {
+ return createCard("SMALL");
+ }
+
+ /** 创建中型卡片 */
+ public static VBox createMediumCard() {
+ return createCard("MEDIUM");
+ }
+
+ /** 创建大型卡片 */
+ public static VBox createLargeCard() {
+ return createCard("LARGE");
+ }
+
+ /** 统一输入框样式 - TextField */
+ public static void styleTextField(TextField textField, String width) {
+ textField.setStyle(UIConstants.getInputStyle(width));
+ }
+
+ /** 统一输入框样式 - PasswordField */
+ public static void stylePasswordField(PasswordField passwordField, String width) {
+ passwordField.setStyle(UIConstants.getInputStyle(width));
+ }
+
+ /** 统一输入框样式 - ChoiceBox */
+ public static void styleChoiceBox(ChoiceBox> choiceBox, String width) {
+ choiceBox.setStyle(UIConstants.getInputStyle(width));
+ }
+
+ /** 统一输入框样式 - Spinner */
+ public static void styleSpinner(Spinner> spinner) {
+ spinner.getEditor().setStyle(UIConstants.getInputStyle(String.valueOf(UIConstants.INPUT_MEDIUM_WIDTH)));
+ spinner.setStyle("-fx-background-radius: 8; -fx-border-radius: 8;");
+ }
+
+ /** 快速设置输入框为中等宽度 */
+ public static void styleInputField(TextField field) {
+ styleTextField(field, String.valueOf(UIConstants.INPUT_MEDIUM_WIDTH));
+ }
+
+ /** 快速设置密码框为中等宽度 */
+ public static void stylePasswordField(PasswordField field) {
+ stylePasswordField(field, String.valueOf(UIConstants.INPUT_MEDIUM_WIDTH));
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/pair/ui/UIConstants.java b/src/main/java/com/pair/ui/UIConstants.java
index 2ea5141..f04c276 100644
--- a/src/main/java/com/pair/ui/UIConstants.java
+++ b/src/main/java/com/pair/ui/UIConstants.java
@@ -13,6 +13,31 @@ public final class UIConstants {
public static final Insets CARD_PADDING = new Insets(24);
public static final Insets TOP_BAR_PADDING = new Insets(12);
+ // 新增:统一间距规范
+ public static final double SMALL_SPACING = 8;
+ public static final double MEDIUM_SPACING = 16;
+ public static final double LARGE_SPACING = 24;
+ public static final double XLARGE_SPACING = 32;
+
+ // 新增:卡片尺寸规范
+ public static final double CARD_SMALL_WIDTH = 400;
+ public static final double CARD_MEDIUM_WIDTH = 600;
+ public static final double CARD_LARGE_WIDTH = 800;
+ public static final double CARD_MIN_HEIGHT = 500;
+
+ // 新增:输入框宽度规范
+ public static final double INPUT_SMALL_WIDTH = 200;
+ public static final double INPUT_MEDIUM_WIDTH = 300;
+ public static final double INPUT_LARGE_WIDTH = 400;
+
+ // 新增:按钮尺寸规范
+ public static final double BTN_SMALL_WIDTH = 100;
+ public static final double BTN_MEDIUM_WIDTH = 120;
+ public static final double BTN_LARGE_WIDTH = 140;
+ public static final double BTN_SMALL_HEIGHT = 32;
+ public static final double BTN_MEDIUM_HEIGHT = 36;
+ public static final double BTN_LARGE_HEIGHT = 40;
+
/* ====== 字体 ====== */
public static final String FONT_FAMILY = "Microsoft YaHei";
public static final double TITLE_FONT_SIZE = 26;
@@ -21,21 +46,97 @@ public final class UIConstants {
public static final double BTN_FONT_SIZE = 14;
public static final double SMALL_FONT_SIZE = 12;
+ // 新增:更大的标题字体
+ public static final double LARGE_TITLE_FONT_SIZE = 32;
+ public static final double XLARGE_TITLE_FONT_SIZE = 40;
+
/* ====== 圆角 & 阴影 ====== */
public static final double CARD_RADIUS = 12;
public static final DropShadow CARD_SHADOW = new DropShadow(15, 0, 4, Color.web("#00000020"));
/* ====== 主题色(一键换色只改这里) ====== */
- public static final Color COLOR_PRIMARY = Color.web("#2563eb"); // 主色
- public static final Color COLOR_SECONDARY = Color.web("#10b981"); // 辅助
- public static final Color COLOR_ERROR = Color.web("#ef4444");
- public static final Color COLOR_BG = Color.web("#f3f4f6");
- public static final Color COLOR_TEXT = Color.web("#1f2937");
- public static final Color COLOR_TEXT_SUB = Color.web("#6b7280");
+ public static final Color COLOR_PRIMARY = Color.web("#2563eb"); // 主色 - 蓝色
+ public static final Color COLOR_SECONDARY = Color.web("#10b981"); // 辅助 - 绿色
+ public static final Color COLOR_ERROR = Color.web("#ef4444"); // 错误 - 红色
+ public static final Color COLOR_BG = Color.web("#f3f4f6"); // 背景 - 浅灰
+ public static final Color COLOR_TEXT = Color.web("#1f2937"); // 文字 - 深灰
+ public static final Color COLOR_TEXT_SUB = Color.web("#6b7280"); // 副文字 - 中灰
+
+ // 新增:扩展配色方案
+ public static final Color COLOR_SUCCESS = Color.web("#22c55e"); // 成功 - 亮绿
+ public static final Color COLOR_WARNING = Color.web("#f59e0b"); // 警告 - 橙色
+ public static final Color COLOR_INFO = Color.web("#3b82f6"); // 信息 - 亮蓝
+ public static final Color COLOR_DARK = Color.web("#374151"); // 深色 - 深灰
+ public static final Color COLOR_LIGHT = Color.web("#ffffff"); // 浅色 - 白色
/* ====== 通用按钮 ====== */
public static final double BTN_WIDTH = 140;
public static final double BTN_HEIGHT = 40;
+
+ // 新增:按钮类型常量
+ public static final String BTN_TYPE_PRIMARY = "PRIMARY";
+ public static final String BTN_TYPE_SECONDARY = "SECONDARY";
+ public static final String BTN_TYPE_SUCCESS = "SUCCESS";
+ public static final String BTN_TYPE_WARNING = "WARNING";
+ public static final String BTN_TYPE_ERROR = "ERROR";
+ public static final String BTN_TYPE_INFO = "INFO";
+
+ // 新增:统一按钮样式生成方法
+ public static String getButtonNormalStyle(String type) {
+ Color bgColor;
+ switch (type) {
+ case BTN_TYPE_PRIMARY: bgColor = COLOR_PRIMARY; break;
+ case BTN_TYPE_SECONDARY: bgColor = COLOR_SECONDARY; break;
+ case BTN_TYPE_SUCCESS: bgColor = COLOR_SUCCESS; break;
+ case BTN_TYPE_WARNING: bgColor = COLOR_WARNING; break;
+ case BTN_TYPE_ERROR: bgColor = COLOR_ERROR; break;
+ case BTN_TYPE_INFO: bgColor = COLOR_INFO; break;
+ default: bgColor = COLOR_PRIMARY; break;
+ }
+ return "-fx-background-color: " + toWeb(bgColor) + ";"
+ + "-fx-text-fill: white;"
+ + "-fx-background-radius: 8;"
+ + "-fx-font-family: '" + FONT_FAMILY + "';"
+ + "-fx-font-size: " + BTN_FONT_SIZE + "px;"
+ + "-fx-cursor: hand;"
+ + "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.2), 6, 0, 0, 2);";
+ }
+
+ public static String getButtonHoverStyle(String type) {
+ Color bgColor;
+ switch (type) {
+ case BTN_TYPE_PRIMARY: bgColor = COLOR_PRIMARY.darker(); break;
+ case BTN_TYPE_SECONDARY: bgColor = COLOR_SECONDARY.darker(); break;
+ case BTN_TYPE_SUCCESS: bgColor = COLOR_SUCCESS.darker(); break;
+ case BTN_TYPE_WARNING: bgColor = COLOR_WARNING.darker(); break;
+ case BTN_TYPE_ERROR: bgColor = COLOR_ERROR.darker(); break;
+ case BTN_TYPE_INFO: bgColor = COLOR_INFO.darker(); break;
+ default: bgColor = COLOR_PRIMARY.darker(); break;
+ }
+ return "-fx-background-color: " + toWeb(bgColor) + ";"
+ + "-fx-text-fill: white;"
+ + "-fx-background-radius: 8;"
+ + "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.3), 8, 0, 0, 3);";
+ }
+
+ public static String getButtonPressedStyle(String type) {
+ Color bgColor;
+ switch (type) {
+ case BTN_TYPE_PRIMARY: bgColor = COLOR_PRIMARY.darker().darker(); break;
+ case BTN_TYPE_SECONDARY: bgColor = COLOR_SECONDARY.darker().darker(); break;
+ case BTN_TYPE_SUCCESS: bgColor = COLOR_SUCCESS.darker().darker(); break;
+ case BTN_TYPE_WARNING: bgColor = COLOR_WARNING.darker().darker(); break;
+ case BTN_TYPE_ERROR: bgColor = COLOR_ERROR.darker().darker(); break;
+ case BTN_TYPE_INFO: bgColor = COLOR_INFO.darker().darker(); break;
+ default: bgColor = COLOR_PRIMARY.darker().darker(); break;
+ }
+ return "-fx-background-color: " + toWeb(bgColor) + ";"
+ + "-fx-text-fill: white;"
+ + "-fx-background-radius: 8;"
+ + "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.1), 4, 0, 0, 1);";
+ }
+
+ // 保留原有样式用于兼容
public static final String BTN_NORMAL =
"-fx-background-color: " + toWeb(COLOR_PRIMARY) + ";"
+ "-fx-text-fill: white;"
@@ -56,6 +157,25 @@ public final class UIConstants {
/* ====== 输入框 ====== */
+ // 新增:输入框样式生成方法
+ public static String getInputStyle(String width) {
+ return "-fx-background-radius: 8;"
+ + "-fx-border-radius: 8;"
+ + "-fx-border-color: " + toWeb(COLOR_TEXT_SUB) + ";"
+ + "-fx-padding: 12;"
+ + "-fx-font-family: '" + FONT_FAMILY + "';"
+ + "-fx-font-size: " + BODY_FONT_SIZE + "px;"
+ + "-fx-pref-width: " + width + ";"
+ + "-fx-background-color: " + toWeb(COLOR_LIGHT) + ";"
+ + "-fx-effect: innershadow(gaussian, rgba(0,0,0,0.05), 2, 0, 0, 1);";
+ }
+
+ // 新增:输入框宽度预设
+ public static final String INPUT_STYLE_SMALL = getInputStyle(String.valueOf(INPUT_SMALL_WIDTH));
+ public static final String INPUT_STYLE_MEDIUM = getInputStyle(String.valueOf(INPUT_MEDIUM_WIDTH));
+ public static final String INPUT_STYLE_LARGE = getInputStyle(String.valueOf(INPUT_LARGE_WIDTH));
+
+ // 保留原有样式用于兼容
public static final String INPUT_STYLE =
"-fx-background-radius: 8;"
+ "-fx-border-radius: 8;"
@@ -66,6 +186,22 @@ public final class UIConstants {
+ "-fx-pref-width: 240;";
/* ====== 卡片容器 ====== */
+ // 新增:卡片容器样式生成方法
+ public static String getCardStyle(double width, double minHeight) {
+ return "-fx-background-color: " + toWeb(COLOR_LIGHT) + ";"
+ + "-fx-background-radius: " + CARD_RADIUS + ";"
+ + "-fx-border-radius: " + CARD_RADIUS + ";"
+ + "-fx-pref-width: " + width + ";"
+ + "-fx-min-height: " + minHeight + ";"
+ + "-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.1), 15, 0, 4, 4);";
+ }
+
+ // 新增:卡片尺寸预设
+ public static final String CARD_STYLE_SMALL = getCardStyle(CARD_SMALL_WIDTH, CARD_MIN_HEIGHT);
+ public static final String CARD_STYLE_MEDIUM = getCardStyle(CARD_MEDIUM_WIDTH, CARD_MIN_HEIGHT);
+ public static final String CARD_STYLE_LARGE = getCardStyle(CARD_LARGE_WIDTH, CARD_MIN_HEIGHT);
+
+ // 保留原有样式用于兼容
public static final String CARD_STYLE =
"-fx-background-color: white;"
+ "-fx-background-radius: " + CARD_RADIUS + ";"
diff --git a/src/main/java/com/pair/util/AsyncRegistrationHelper.java b/src/main/java/com/pair/util/AsyncRegistrationHelper.java
index b854424..a658fb1 100644
--- a/src/main/java/com/pair/util/AsyncRegistrationHelper.java
+++ b/src/main/java/com/pair/util/AsyncRegistrationHelper.java
@@ -53,11 +53,14 @@ public class AsyncRegistrationHelper {
task.setOnFailed(e -> {
Throwable ex = task.getException();
- String msg = switch (ex) {
- case IllegalArgumentException iae -> iae.getMessage();
- case IOException ioe -> "系统错误:" + ioe.getMessage();
- default -> "发送失败,请检查网络或稍后重试";
- };
+ String msg;
+ if (ex instanceof IllegalArgumentException iae) {
+ msg = iae.getMessage(); // 17 支持 instanceof 模式变量
+ } else if (ex instanceof IOException ioe) {
+ msg = "系统错误:" + ioe.getMessage();
+ } else {
+ msg = "发送失败,请检查网络或稍后重试";
+ }
onFailure.accept(msg);
Platform.runLater(onComplete);
});
diff --git a/src/main/java/com/pair/util/EmailUtil.java b/src/main/java/com/pair/util/EmailUtil.java
index e38f235..7cdcd15 100644
--- a/src/main/java/com/pair/util/EmailUtil.java
+++ b/src/main/java/com/pair/util/EmailUtil.java
@@ -86,38 +86,4 @@ public class EmailUtil {
}
- /**
- * 发送密码重置邮件(预留接口)
- *
- * @param toEmail 收件人邮箱
- * @param newPassword 新密码
- * @return true表示发送成功
- */
- public static boolean sendPasswordReset(String toEmail, String newPassword) {
- // TODO: 将来如果需要"找回密码"功能,可以在这里实现
-
- System.out.println("【模拟】发送密码重置邮件");
- System.out.println("收件人: " + toEmail);
- System.out.println("新密码: " + newPassword);
-
- return true;
- }
-
}
-
- /**
- * 验证邮箱格式
- * @param email 邮箱地址
- * @return true表示格式正确
- */
-// public static boolean isValidEmail(String email) {
-// if (email == null || email.trim().isEmpty()) {
-// return false;
-// }
-//
-// // 简单的邮箱格式验证
-// String emailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$";
-// return email.matches(emailRegex);
-// }
-//}
-
diff --git a/src/main/java/com/pair/util/PasswordValidator.java b/src/main/java/com/pair/util/PasswordValidator.java
index 3dd9d5c..b183fbf 100644
--- a/src/main/java/com/pair/util/PasswordValidator.java
+++ b/src/main/java/com/pair/util/PasswordValidator.java
@@ -136,7 +136,7 @@ public class PasswordValidator {
StringBuilder code = new StringBuilder(codeLength);
// 填充剩余字符
- for (int i = 3; i < codeLength; i++) {
+ for (int i = 0; i < codeLength; i++) {
code.append(DIGITS.charAt(RandomUtils.nextInt(0, DIGITS.length() - 1)));
}
--
2.34.1
From b8d46f4c41e9368b3a937acfe044ffbf1e923cfe Mon Sep 17 00:00:00 2001
From: lsbp <2803234009@qq.com>
Date: Thu, 9 Oct 2025 20:48:25 +0800
Subject: [PATCH 4/5] feat: ignore package.txt
---
.gitignore | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.gitignore b/.gitignore
index 5ff6309..8df4325 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,4 +35,4 @@ build/
.vscode/
### Mac OS ###
-.DS_Store
\ No newline at end of file
+.DS_Store.idea/workspace.xml
--
2.34.1
From 26e22b72f530cf3aff55a066524c5dd1ac291559 Mon Sep 17 00:00:00 2001
From: lsbp <2803234009@qq.com>
Date: Thu, 9 Oct 2025 20:49:48 +0800
Subject: [PATCH 5/5] feat: ignore package.txt
---
.gitignore | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.gitignore b/.gitignore
index 8df4325..347d0c9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@ target/
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
+.idea/workspace.xml
*.iws
*.iml
*.ipr
@@ -36,3 +37,4 @@ build/
### Mac OS ###
.DS_Store.idea/workspace.xml
+package.txt
--
2.34.1