|
|
|
|
@ -12,14 +12,7 @@ public class Main extends Application {
|
|
|
|
|
private Stage primaryStage;
|
|
|
|
|
private MailSender mailSender = new MailSender("3417398995@qq.com", "zhwytlhmucfxcibe");
|
|
|
|
|
private String generatedCode;
|
|
|
|
|
|
|
|
|
|
// 存储用户信息:邮箱 -> 密码
|
|
|
|
|
private Map<String, String> userDatabase = new HashMap<>();
|
|
|
|
|
// 存储用户名 -> 邮箱 映射
|
|
|
|
|
private Map<String, String> usernameToEmail = new HashMap<>();
|
|
|
|
|
// 存储邮箱 -> 用户名 映射
|
|
|
|
|
private Map<String, String> emailToUsername = new HashMap<>();
|
|
|
|
|
|
|
|
|
|
private Map<String, String> userDatabase = new HashMap<>(); // 存储邮箱 -> 密码
|
|
|
|
|
private Map<String, String> currentUser = new HashMap<>(); // 存储当前登录用户信息
|
|
|
|
|
private List<Question> currentExam = new ArrayList<>();
|
|
|
|
|
private int currentIndex = 0;
|
|
|
|
|
@ -46,29 +39,26 @@ public class Main extends Application {
|
|
|
|
|
Label titleLabel = new Label("数学学习软件 - 登录");
|
|
|
|
|
titleLabel.setStyle("-fx-font-size: 16; -fx-font-weight: bold;");
|
|
|
|
|
|
|
|
|
|
TextField loginField = new TextField();
|
|
|
|
|
loginField.setPromptText("请输入邮箱或用户名");
|
|
|
|
|
TextField emailField = new TextField();
|
|
|
|
|
emailField.setPromptText("请输入邮箱");
|
|
|
|
|
PasswordField passwordField = new PasswordField();
|
|
|
|
|
passwordField.setPromptText("请输入密码");
|
|
|
|
|
|
|
|
|
|
Button loginBtn = new Button("登录");
|
|
|
|
|
Button registerBtn = new Button("前往注册");
|
|
|
|
|
Label infoLabel = new Label();
|
|
|
|
|
Label tipLabel = new Label("提示:可以使用邮箱或用户名登录");
|
|
|
|
|
tipLabel.setStyle("-fx-font-size: 10; -fx-text-fill: gray;");
|
|
|
|
|
|
|
|
|
|
// 登录按钮事件
|
|
|
|
|
loginBtn.setOnAction(e -> {
|
|
|
|
|
String loginInput = loginField.getText().trim();
|
|
|
|
|
String email = emailField.getText().trim();
|
|
|
|
|
String password = passwordField.getText();
|
|
|
|
|
|
|
|
|
|
if (loginInput.isEmpty() || password.isEmpty()) {
|
|
|
|
|
infoLabel.setText("请输入登录信息和密码");
|
|
|
|
|
if (email.isEmpty() || password.isEmpty()) {
|
|
|
|
|
infoLabel.setText("请输入邮箱和密码");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String email = getEmailFromLoginInput(loginInput);
|
|
|
|
|
if (email == null) {
|
|
|
|
|
if (!userDatabase.containsKey(email)) {
|
|
|
|
|
infoLabel.setText("用户不存在");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
@ -80,8 +70,7 @@ public class Main extends Application {
|
|
|
|
|
|
|
|
|
|
// 登录成功
|
|
|
|
|
currentUser.put("email", email);
|
|
|
|
|
currentUser.put("username", emailToUsername.get(email));
|
|
|
|
|
infoLabel.setText("登录成功!欢迎 " + emailToUsername.get(email));
|
|
|
|
|
infoLabel.setText("登录成功!");
|
|
|
|
|
primaryStage.setScene(buildLevelSelectionScene());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
@ -90,21 +79,10 @@ public class Main extends Application {
|
|
|
|
|
primaryStage.setScene(buildRegisterScene());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
root.getChildren().addAll(titleLabel, loginField, passwordField, tipLabel, loginBtn, registerBtn, infoLabel);
|
|
|
|
|
root.getChildren().addAll(titleLabel, emailField, passwordField, loginBtn, registerBtn, infoLabel);
|
|
|
|
|
return new Scene(root, 400, 300);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 根据登录输入获取邮箱
|
|
|
|
|
private String getEmailFromLoginInput(String loginInput) {
|
|
|
|
|
// 如果输入包含@,认为是邮箱
|
|
|
|
|
if (loginInput.contains("@")) {
|
|
|
|
|
return userDatabase.containsKey(loginInput) ? loginInput : null;
|
|
|
|
|
} else {
|
|
|
|
|
// 否则认为是用户名
|
|
|
|
|
return usernameToEmail.get(loginInput);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 注册界面
|
|
|
|
|
private Scene buildRegisterScene() {
|
|
|
|
|
VBox root = new VBox(10);
|
|
|
|
|
@ -112,8 +90,6 @@ public class Main extends Application {
|
|
|
|
|
Label titleLabel = new Label("用户注册");
|
|
|
|
|
titleLabel.setStyle("-fx-font-size: 16; -fx-font-weight: bold;");
|
|
|
|
|
|
|
|
|
|
TextField usernameField = new TextField();
|
|
|
|
|
usernameField.setPromptText("设置用户名 (4-10位字符)");
|
|
|
|
|
TextField emailField = new TextField();
|
|
|
|
|
emailField.setPromptText("请输入邮箱");
|
|
|
|
|
Button sendCodeBtn = new Button("发送验证码");
|
|
|
|
|
@ -128,26 +104,11 @@ public class Main extends Application {
|
|
|
|
|
Label infoLabel = new Label();
|
|
|
|
|
|
|
|
|
|
sendCodeBtn.setOnAction(e -> {
|
|
|
|
|
String username = usernameField.getText().trim();
|
|
|
|
|
String email = emailField.getText().trim();
|
|
|
|
|
|
|
|
|
|
// 验证用户名
|
|
|
|
|
if (!isValidUsername(username)) {
|
|
|
|
|
infoLabel.setText("用户名必须4-10位,只能包含字母、数字和下划线");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (usernameToEmail.containsKey(username)) {
|
|
|
|
|
infoLabel.setText("用户名已存在");
|
|
|
|
|
if (!email.contains("@")) {
|
|
|
|
|
infoLabel.setText("邮箱格式不正确");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 使用新的邮箱验证方法
|
|
|
|
|
if (!isValidEmail(email)) {
|
|
|
|
|
infoLabel.setText("邮箱格式不正确,请使用有效的邮箱地址");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (userDatabase.containsKey(email)) {
|
|
|
|
|
infoLabel.setText("该邮箱已注册");
|
|
|
|
|
return;
|
|
|
|
|
@ -162,29 +123,18 @@ public class Main extends Application {
|
|
|
|
|
if (success) {
|
|
|
|
|
infoLabel.setText("验证码已发送,请查看邮箱");
|
|
|
|
|
} else {
|
|
|
|
|
infoLabel.setText("发送失败,请检查邮箱是否正确");
|
|
|
|
|
infoLabel.setText("发送失败,请检查邮箱");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}).start();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
registerBtn.setOnAction(e -> {
|
|
|
|
|
String username = usernameField.getText().trim();
|
|
|
|
|
String email = emailField.getText().trim();
|
|
|
|
|
String inputCode = codeField.getText().trim();
|
|
|
|
|
String pw = passwordField.getText();
|
|
|
|
|
String pwConfirm = confirmPasswordField.getText();
|
|
|
|
|
|
|
|
|
|
if (!isValidUsername(username)) {
|
|
|
|
|
infoLabel.setText("用户名必须4-10位,只能包含字母、数字和下划线");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (usernameToEmail.containsKey(username)) {
|
|
|
|
|
infoLabel.setText("用户名已存在");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!inputCode.equals(generatedCode)) {
|
|
|
|
|
infoLabel.setText("验证码错误");
|
|
|
|
|
} else if (!pw.equals(pwConfirm)) {
|
|
|
|
|
@ -194,14 +144,10 @@ public class Main extends Application {
|
|
|
|
|
} else if (userDatabase.containsKey(email)) {
|
|
|
|
|
infoLabel.setText("邮箱已注册");
|
|
|
|
|
} else {
|
|
|
|
|
// 注册用户
|
|
|
|
|
userDatabase.put(email, pw);
|
|
|
|
|
usernameToEmail.put(username, email);
|
|
|
|
|
emailToUsername.put(email, username);
|
|
|
|
|
infoLabel.setText("注册成功!用户名: " + username);
|
|
|
|
|
infoLabel.setText("注册成功!");
|
|
|
|
|
// 注册成功后自动登录
|
|
|
|
|
currentUser.put("email", email);
|
|
|
|
|
currentUser.put("username", username);
|
|
|
|
|
primaryStage.setScene(buildLevelSelectionScene());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
@ -210,59 +156,9 @@ public class Main extends Application {
|
|
|
|
|
primaryStage.setScene(buildLoginScene());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
root.getChildren().addAll(titleLabel, usernameField, emailField, sendCodeBtn, codeField,
|
|
|
|
|
root.getChildren().addAll(titleLabel, emailField, sendCodeBtn, codeField,
|
|
|
|
|
passwordField, confirmPasswordField, registerBtn, backBtn, infoLabel);
|
|
|
|
|
return new Scene(root, 400, 450);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 用户名验证方法
|
|
|
|
|
private boolean isValidUsername(String username) {
|
|
|
|
|
if (username.length() < 4 || username.length() > 10) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 只能包含字母、数字、下划线
|
|
|
|
|
return username.matches("^[a-zA-Z0-9_]+$");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 邮箱完整验证方法
|
|
|
|
|
private boolean isValidEmail(String email) {
|
|
|
|
|
if (email == null || email.trim().isEmpty()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String regex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$";
|
|
|
|
|
|
|
|
|
|
// 基本格式检查
|
|
|
|
|
if (!email.matches(regex)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查@符号位置
|
|
|
|
|
int atIndex = email.indexOf('@');
|
|
|
|
|
if (atIndex <= 0 || atIndex == email.length() - 1) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查域名部分
|
|
|
|
|
String domain = email.substring(atIndex + 1);
|
|
|
|
|
if (domain.indexOf('.') <= 0 || domain.endsWith(".")) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 检查常见邮箱服务商
|
|
|
|
|
String[] commonDomains = {"qq.com", "gmail.com", "163.com", "126.com", "sina.com",
|
|
|
|
|
"hotmail.com", "outlook.com", "yahoo.com", "foxmail.com"};
|
|
|
|
|
boolean hasCommonDomain = false;
|
|
|
|
|
for (String commonDomain : commonDomains) {
|
|
|
|
|
if (domain.equalsIgnoreCase(commonDomain)) {
|
|
|
|
|
hasCommonDomain = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
return new Scene(root, 400, 400);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 密码验证方法
|
|
|
|
|
@ -283,18 +179,10 @@ public class Main extends Application {
|
|
|
|
|
return hasUpper && hasLower && hasDigit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 难度选择界面
|
|
|
|
|
private Scene buildLevelSelectionScene() {
|
|
|
|
|
VBox root = new VBox(15);
|
|
|
|
|
root.setStyle("-fx-padding: 20;");
|
|
|
|
|
|
|
|
|
|
// 显示欢迎信息
|
|
|
|
|
String username = currentUser.get("username");
|
|
|
|
|
Label welcomeLabel = new Label("欢迎, " + username + "!");
|
|
|
|
|
welcomeLabel.setStyle("-fx-font-size: 16; -fx-font-weight: bold; -fx-text-fill: #2E8B57;");
|
|
|
|
|
|
|
|
|
|
Label label = new Label("请选择难度");
|
|
|
|
|
label.setStyle("-fx-font-size: 14; -fx-font-weight: bold;");
|
|
|
|
|
|
|
|
|
|
@ -326,7 +214,7 @@ public class Main extends Application {
|
|
|
|
|
HBox bottomBox = new HBox(10, changePwdBtn, logoutBtn);
|
|
|
|
|
bottomBox.setStyle("-fx-alignment: center;");
|
|
|
|
|
|
|
|
|
|
root.getChildren().addAll(welcomeLabel, label, buttonBox, bottomBox);
|
|
|
|
|
root.getChildren().addAll(label, buttonBox, bottomBox);
|
|
|
|
|
Scene scene = new Scene(root, 400, 300);
|
|
|
|
|
primaryStage.setScene(scene);
|
|
|
|
|
return scene;
|
|
|
|
|
@ -339,9 +227,6 @@ public class Main extends Application {
|
|
|
|
|
Label titleLabel = new Label("修改密码");
|
|
|
|
|
titleLabel.setStyle("-fx-font-size: 16; -fx-font-weight: bold;");
|
|
|
|
|
|
|
|
|
|
Label userLabel = new Label("用户: " + currentUser.get("username"));
|
|
|
|
|
userLabel.setStyle("-fx-font-size: 12; -fx-text-fill: gray;");
|
|
|
|
|
|
|
|
|
|
PasswordField oldPwdField = new PasswordField();
|
|
|
|
|
oldPwdField.setPromptText("请输入原密码");
|
|
|
|
|
PasswordField newPwdField = new PasswordField();
|
|
|
|
|
@ -375,9 +260,9 @@ public class Main extends Application {
|
|
|
|
|
primaryStage.setScene(buildLevelSelectionScene());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
root.getChildren().addAll(titleLabel, userLabel, oldPwdField, newPwdField, confirmPwdField,
|
|
|
|
|
root.getChildren().addAll(titleLabel, oldPwdField, newPwdField, confirmPwdField,
|
|
|
|
|
confirmBtn, backBtn, infoLabel);
|
|
|
|
|
return new Scene(root, 400, 350);
|
|
|
|
|
return new Scene(root, 400, 300);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 输入题目数量界面
|
|
|
|
|
@ -487,85 +372,32 @@ public class Main extends Application {
|
|
|
|
|
// 显示分数
|
|
|
|
|
private Scene buildScoreScene() {
|
|
|
|
|
VBox root = new VBox(15);
|
|
|
|
|
root.setStyle("-fx-padding: 30; -fx-alignment: center; -fx-background-color: #f5f5f5;");
|
|
|
|
|
|
|
|
|
|
// 计算分数
|
|
|
|
|
int totalQuestions = currentExam.size();
|
|
|
|
|
double scorePerQuestion = 100.0 / totalQuestions; // 每道题的分值
|
|
|
|
|
int finalScore = (int) Math.round(score * scorePerQuestion); // 最终分数(四舍五入)
|
|
|
|
|
double correctRate = (score * 100.0) / totalQuestions;
|
|
|
|
|
int percentage = (int) Math.round(correctRate);
|
|
|
|
|
|
|
|
|
|
// 标题
|
|
|
|
|
Label titleLabel = new Label("答题结果");
|
|
|
|
|
titleLabel.setStyle("-fx-font-size: 24; -fx-font-weight: bold; -fx-text-fill: #2C3E50;");
|
|
|
|
|
|
|
|
|
|
// 分数卡片
|
|
|
|
|
VBox scoreCard = new VBox(10);
|
|
|
|
|
scoreCard.setStyle("-fx-background-color: white; -fx-padding: 20; -fx-border-color: #ddd; -fx-border-radius: 10; -fx-background-radius: 10;");
|
|
|
|
|
scoreCard.setMaxWidth(300);
|
|
|
|
|
|
|
|
|
|
Label finalScoreLabel = new Label(finalScore + " 分");
|
|
|
|
|
finalScoreLabel.setStyle("-fx-font-size: 36; -fx-font-weight: bold; -fx-text-fill: #E74C3C;");
|
|
|
|
|
root.setStyle("-fx-padding: 30; -fx-alignment: center;");
|
|
|
|
|
|
|
|
|
|
Label scoreTextLabel = new Label("最终得分");
|
|
|
|
|
scoreTextLabel.setStyle("-fx-font-size: 14; -fx-text-fill: #7F8C8D;");
|
|
|
|
|
int percentage = (int) ((score * 100.0) / currentExam.size());
|
|
|
|
|
Label scoreLabel = new Label("答题完成!");
|
|
|
|
|
scoreLabel.setStyle("-fx-font-size: 18; -fx-font-weight: bold;");
|
|
|
|
|
|
|
|
|
|
// 详细信息
|
|
|
|
|
VBox detailBox = new VBox(5);
|
|
|
|
|
detailBox.setStyle("-fx-alignment: center-left;");
|
|
|
|
|
Label percentageLabel = new Label("正确率: " + percentage + "%");
|
|
|
|
|
percentageLabel.setStyle("-fx-font-size: 16;");
|
|
|
|
|
|
|
|
|
|
Label percentageLabel = new Label("• 正确率: " + percentage + "%");
|
|
|
|
|
percentageLabel.setStyle("-fx-font-size: 14;");
|
|
|
|
|
|
|
|
|
|
Label detailLabel = new Label("• 答对: " + score + " 题 / 总共: " + totalQuestions + " 题");
|
|
|
|
|
Label detailLabel = new Label("答对 " + score + " 题 / 总共 " + currentExam.size() + " 题");
|
|
|
|
|
detailLabel.setStyle("-fx-font-size: 14;");
|
|
|
|
|
|
|
|
|
|
Label pointLabel = new Label("• 每题分值: " + String.format("%.1f", scorePerQuestion) + " 分");
|
|
|
|
|
pointLabel.setStyle("-fx-font-size: 14;");
|
|
|
|
|
|
|
|
|
|
detailBox.getChildren().addAll(percentageLabel, detailLabel, pointLabel);
|
|
|
|
|
|
|
|
|
|
// 鼓励语
|
|
|
|
|
Label encouragementLabel = new Label();
|
|
|
|
|
encouragementLabel.setStyle("-fx-font-size: 16; -fx-font-weight: bold; -fx-padding: 10 0 0 0;");
|
|
|
|
|
|
|
|
|
|
if (finalScore == 100) {
|
|
|
|
|
encouragementLabel.setText("🎉 满分!太棒了!");
|
|
|
|
|
encouragementLabel.setStyle("-fx-text-fill: #FFD700; -fx-font-size: 16; -fx-font-weight: bold;");
|
|
|
|
|
} else if (finalScore >= 90) {
|
|
|
|
|
encouragementLabel.setText("👍 优秀!表现很好!");
|
|
|
|
|
encouragementLabel.setStyle("-fx-text-fill: #27AE60; -fx-font-size: 16; -fx-font-weight: bold;");
|
|
|
|
|
} else if (finalScore >= 80) {
|
|
|
|
|
encouragementLabel.setText("👏 很好!继续加油!");
|
|
|
|
|
encouragementLabel.setStyle("-fx-text-fill: #2980B9; -fx-font-size: 16; -fx-font-weight: bold;");
|
|
|
|
|
} else if (finalScore >= 60) {
|
|
|
|
|
encouragementLabel.setText("💪 及格了!还有进步空间!");
|
|
|
|
|
encouragementLabel.setStyle("-fx-text-fill: #F39C12; -fx-font-size: 16; -fx-font-weight: bold;");
|
|
|
|
|
} else {
|
|
|
|
|
encouragementLabel.setText("📚 加油!再多练习一下!");
|
|
|
|
|
encouragementLabel.setStyle("-fx-text-fill: #E74C3C; -fx-font-size: 16; -fx-font-weight: bold;");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scoreCard.getChildren().addAll(finalScoreLabel, scoreTextLabel, new Separator(), detailBox, encouragementLabel);
|
|
|
|
|
|
|
|
|
|
// 按钮区域
|
|
|
|
|
HBox buttonBox = new HBox(20);
|
|
|
|
|
buttonBox.setStyle("-fx-alignment: center; -fx-padding: 20 0 0 0;");
|
|
|
|
|
buttonBox.setStyle("-fx-alignment: center;");
|
|
|
|
|
|
|
|
|
|
Button retryBtn = new Button("继续做题");
|
|
|
|
|
retryBtn.setStyle("-fx-font-size: 14; -fx-background-color: #4CAF50; -fx-text-fill: white; -fx-pref-width: 120;");
|
|
|
|
|
|
|
|
|
|
retryBtn.setStyle("-fx-font-size: 12;");
|
|
|
|
|
Button exitBtn = new Button("退出");
|
|
|
|
|
exitBtn.setStyle("-fx-font-size: 14; -fx-background-color: #95a5a6; -fx-text-fill: white; -fx-pref-width: 120;");
|
|
|
|
|
exitBtn.setStyle("-fx-font-size: 12;");
|
|
|
|
|
|
|
|
|
|
retryBtn.setOnAction(e -> primaryStage.setScene(buildLevelSelectionScene()));
|
|
|
|
|
exitBtn.setOnAction(e -> Platform.exit());
|
|
|
|
|
|
|
|
|
|
buttonBox.getChildren().addAll(retryBtn, exitBtn);
|
|
|
|
|
|
|
|
|
|
root.getChildren().addAll(titleLabel, scoreCard, buttonBox);
|
|
|
|
|
return new Scene(root, 500, 450);
|
|
|
|
|
root.getChildren().addAll(scoreLabel, percentageLabel, detailLabel, buttonBox);
|
|
|
|
|
return new Scene(root, 400, 250);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Question 类
|
|
|
|
|
|