|
|
|
|
@ -0,0 +1,841 @@
|
|
|
|
|
import javax.swing.*;
|
|
|
|
|
import java.awt.*;
|
|
|
|
|
import java.util.*;
|
|
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.HashSet;
|
|
|
|
|
import java.util.Set;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
|
|
// 用户类
|
|
|
|
|
class User {
|
|
|
|
|
private String email;
|
|
|
|
|
private String password;
|
|
|
|
|
private String verificationCode;
|
|
|
|
|
|
|
|
|
|
public User(String email, String password) {
|
|
|
|
|
this.email = email;
|
|
|
|
|
this.password = password;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String getEmail() { return email; }
|
|
|
|
|
public String getPassword() { return password; }
|
|
|
|
|
public void setPassword(String password) { this.password = password; }
|
|
|
|
|
public String getVerificationCode() { return verificationCode; }
|
|
|
|
|
public void setVerificationCode(String code) { this.verificationCode = code; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 题目类
|
|
|
|
|
class Question {
|
|
|
|
|
private String content;
|
|
|
|
|
private String[] options;
|
|
|
|
|
private int correctAnswer;
|
|
|
|
|
|
|
|
|
|
public Question(String content, String[] options, int correctAnswer) {
|
|
|
|
|
this.content = content;
|
|
|
|
|
this.options = options;
|
|
|
|
|
this.correctAnswer = correctAnswer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String getContent() { return content; }
|
|
|
|
|
public String[] getOptions() { return options; }
|
|
|
|
|
public int getCorrectAnswer() { return correctAnswer; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 抽象题目生成器
|
|
|
|
|
abstract class QuestionGenerator {
|
|
|
|
|
protected Random random = new Random();
|
|
|
|
|
|
|
|
|
|
public abstract Question generateQuestion();
|
|
|
|
|
|
|
|
|
|
protected int generateNumber() {
|
|
|
|
|
return random.nextInt(100) + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected String generateOperator() {
|
|
|
|
|
String[] operators = {"+", "-", "*", "/"};
|
|
|
|
|
return operators[random.nextInt(operators.length)];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected String[] generateOptions(int correctAnswer) {
|
|
|
|
|
String[] options = new String[4];
|
|
|
|
|
Set<Integer> usedAnswers = new HashSet<>();
|
|
|
|
|
usedAnswers.add(correctAnswer);
|
|
|
|
|
|
|
|
|
|
options[0] = String.valueOf(correctAnswer);
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i < 4; i++) {
|
|
|
|
|
int wrongAnswer;
|
|
|
|
|
do {
|
|
|
|
|
// 生成有一定随机性但合理的错误答案
|
|
|
|
|
int variation = random.nextInt(20) + 1;
|
|
|
|
|
wrongAnswer = correctAnswer + (random.nextBoolean() ? variation : -variation);
|
|
|
|
|
if (wrongAnswer < 0) wrongAnswer = -wrongAnswer;
|
|
|
|
|
} while (usedAnswers.contains(wrongAnswer));
|
|
|
|
|
|
|
|
|
|
usedAnswers.add(wrongAnswer);
|
|
|
|
|
options[i] = String.valueOf(wrongAnswer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 随机打乱选项顺序
|
|
|
|
|
List<String> optionList = Arrays.asList(options);
|
|
|
|
|
Collections.shuffle(optionList);
|
|
|
|
|
return optionList.toArray(new String[0]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 小学题目生成器
|
|
|
|
|
class PrimaryQuestionGenerator extends QuestionGenerator {
|
|
|
|
|
@Override
|
|
|
|
|
public Question generateQuestion() {
|
|
|
|
|
int operandCount = random.nextInt(2) + 2; // 2-3个操作数
|
|
|
|
|
StringBuilder question = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
boolean hasParentheses = random.nextBoolean() && operandCount >= 3;
|
|
|
|
|
int parenthesesPosition = random.nextInt(operandCount - 1);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < operandCount; i++) {
|
|
|
|
|
if (hasParentheses && i == parenthesesPosition) {
|
|
|
|
|
question.append("(");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
question.append(generateNumber());
|
|
|
|
|
|
|
|
|
|
if (hasParentheses && i == parenthesesPosition + 1) {
|
|
|
|
|
question.append(")");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i < operandCount - 1) {
|
|
|
|
|
question.append(" ").append(generateOperator()).append(" ");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
question.append(" = ?");
|
|
|
|
|
|
|
|
|
|
// 计算正确答案
|
|
|
|
|
int correctAnswer = calculateSimpleExpression(question.toString());
|
|
|
|
|
String[] options = generateOptions(correctAnswer);
|
|
|
|
|
|
|
|
|
|
return new Question(question.toString(), options, Arrays.asList(options).indexOf(String.valueOf(correctAnswer)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private int calculateSimpleExpression(String expression) {
|
|
|
|
|
try {
|
|
|
|
|
// 简化计算,移除括号和问号
|
|
|
|
|
String cleanExpr = expression.replace("(", "").replace(")", "").replace(" = ?", "");
|
|
|
|
|
String[] parts = cleanExpr.split(" ");
|
|
|
|
|
int result = Integer.parseInt(parts[0]);
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i < parts.length; i += 2) {
|
|
|
|
|
String operator = parts[i];
|
|
|
|
|
int num = Integer.parseInt(parts[i + 1]);
|
|
|
|
|
|
|
|
|
|
switch (operator) {
|
|
|
|
|
case "+": result += num; break;
|
|
|
|
|
case "-": result -= num; break;
|
|
|
|
|
case "*": result *= num; break;
|
|
|
|
|
case "/":
|
|
|
|
|
if (num != 0) result /= num;
|
|
|
|
|
else result = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
return random.nextInt(100) + 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 初中题目生成器
|
|
|
|
|
class JuniorQuestionGenerator extends QuestionGenerator {
|
|
|
|
|
@Override
|
|
|
|
|
public Question generateQuestion() {
|
|
|
|
|
StringBuilder question = new StringBuilder();
|
|
|
|
|
|
|
|
|
|
// 随机选择平方根或平方运算
|
|
|
|
|
if (random.nextBoolean()) {
|
|
|
|
|
int num = generateNumber();
|
|
|
|
|
question.append("√").append(num).append(" = ?");
|
|
|
|
|
int correctAnswer = (int) Math.sqrt(num);
|
|
|
|
|
String[] options = generateOptions(correctAnswer);
|
|
|
|
|
return new Question(question.toString(), options, Arrays.asList(options).indexOf(String.valueOf(correctAnswer)));
|
|
|
|
|
} else {
|
|
|
|
|
int num = generateNumber();
|
|
|
|
|
question.append(num).append("² = ?");
|
|
|
|
|
int correctAnswer = num * num;
|
|
|
|
|
String[] options = generateOptions(correctAnswer);
|
|
|
|
|
return new Question(question.toString(), options, Arrays.asList(options).indexOf(String.valueOf(correctAnswer)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 高中题目生成器
|
|
|
|
|
class SeniorQuestionGenerator extends QuestionGenerator {
|
|
|
|
|
private final String[] trigFunctions = {"sin", "cos", "tan"};
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public Question generateQuestion() {
|
|
|
|
|
String trigFunction = trigFunctions[random.nextInt(trigFunctions.length)];
|
|
|
|
|
int angle = random.nextInt(360);
|
|
|
|
|
|
|
|
|
|
String question = trigFunction + "(" + angle + "°) = ?";
|
|
|
|
|
|
|
|
|
|
double result;
|
|
|
|
|
switch (trigFunction) {
|
|
|
|
|
case "sin": result = Math.sin(Math.toRadians(angle)); break;
|
|
|
|
|
case "cos": result = Math.cos(Math.toRadians(angle)); break;
|
|
|
|
|
case "tan":
|
|
|
|
|
result = Math.tan(Math.toRadians(angle));
|
|
|
|
|
// 处理tan函数可能出现的极大值
|
|
|
|
|
if (Math.abs(result) > 100) result = 100 * (result > 0 ? 1 : -1);
|
|
|
|
|
break;
|
|
|
|
|
default: result = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 四舍五入到两位小数
|
|
|
|
|
double roundedResult = Math.round(result * 100.0) / 100.0;
|
|
|
|
|
String[] options = generateTrigOptions(roundedResult);
|
|
|
|
|
|
|
|
|
|
return new Question(question, options, Arrays.asList(options).indexOf(String.format("%.2f", roundedResult)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String[] generateTrigOptions(double correctAnswer) {
|
|
|
|
|
String[] options = new String[4];
|
|
|
|
|
Set<String> usedAnswers = new HashSet<>();
|
|
|
|
|
usedAnswers.add(String.format("%.2f", correctAnswer));
|
|
|
|
|
|
|
|
|
|
options[0] = String.format("%.2f", correctAnswer);
|
|
|
|
|
|
|
|
|
|
for (int i = 1; i < 4; i++) {
|
|
|
|
|
String wrongAnswer;
|
|
|
|
|
do {
|
|
|
|
|
double variation = (random.nextDouble() * 2 - 1) * 0.5; // -0.5 到 0.5 的变化
|
|
|
|
|
double wrongValue = correctAnswer + variation;
|
|
|
|
|
wrongAnswer = String.format("%.2f", wrongValue);
|
|
|
|
|
} while (usedAnswers.contains(wrongAnswer));
|
|
|
|
|
|
|
|
|
|
usedAnswers.add(wrongAnswer);
|
|
|
|
|
options[i] = wrongAnswer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<String> optionList = Arrays.asList(options);
|
|
|
|
|
Collections.shuffle(optionList);
|
|
|
|
|
return optionList.toArray(new String[0]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 模拟邮箱工具类
|
|
|
|
|
class EmailUtil {
|
|
|
|
|
public static boolean sendVerificationCode(String toEmail, String code) {
|
|
|
|
|
// 模拟发送验证码,显示在对话框中
|
|
|
|
|
System.out.println("模拟发送验证码到 " + toEmail + ": " + code);
|
|
|
|
|
|
|
|
|
|
// 在实际项目中,这里应该集成真实的邮箱发送功能
|
|
|
|
|
// 暂时使用模拟成功,显示验证码给用户
|
|
|
|
|
JOptionPane.showMessageDialog(null,
|
|
|
|
|
"验证码已发送到: " + toEmail + "\n验证码: " + code + "\n\n(这是模拟发送,实际项目需配置真实邮箱)",
|
|
|
|
|
"验证码信息",
|
|
|
|
|
JOptionPane.INFORMATION_MESSAGE);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 用户管理类
|
|
|
|
|
class UserManager {
|
|
|
|
|
private Map<String, User> users;
|
|
|
|
|
|
|
|
|
|
public UserManager() {
|
|
|
|
|
users = new HashMap<>();
|
|
|
|
|
// 添加一些测试用户
|
|
|
|
|
users.put("test@test.com", new User("test@test.com", "Test123"));
|
|
|
|
|
users.put("user@example.com", new User("user@example.com", "User123"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean registerUser(String email, String password) {
|
|
|
|
|
if (users.containsKey(email)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
User user = new User(email, password);
|
|
|
|
|
users.put(email, user);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public User login(String email, String password) {
|
|
|
|
|
User user = users.get(email);
|
|
|
|
|
if (user != null && user.getPassword().equals(password)) {
|
|
|
|
|
return user;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean updatePassword(String email, String oldPassword, String newPassword) {
|
|
|
|
|
User user = users.get(email);
|
|
|
|
|
if (user != null && user.getPassword().equals(oldPassword)) {
|
|
|
|
|
user.setPassword(newPassword);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean isEmailRegistered(String email) {
|
|
|
|
|
return users.containsKey(email);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 主应用程序
|
|
|
|
|
public class MathLearningApp {
|
|
|
|
|
private JFrame mainFrame;
|
|
|
|
|
private CardLayout cardLayout;
|
|
|
|
|
private JPanel mainPanel;
|
|
|
|
|
private UserManager userManager;
|
|
|
|
|
private User currentUser;
|
|
|
|
|
private List<Question> currentExam;
|
|
|
|
|
private int currentQuestionIndex;
|
|
|
|
|
private int score;
|
|
|
|
|
|
|
|
|
|
// UI组件
|
|
|
|
|
private JTextField emailField;
|
|
|
|
|
private JPasswordField passwordField;
|
|
|
|
|
private JLabel questionLabel;
|
|
|
|
|
private ButtonGroup optionGroup;
|
|
|
|
|
private JRadioButton[] optionButtons;
|
|
|
|
|
private JLabel scoreLabel;
|
|
|
|
|
|
|
|
|
|
public MathLearningApp() {
|
|
|
|
|
userManager = new UserManager();
|
|
|
|
|
initializeUI();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void initializeUI() {
|
|
|
|
|
mainFrame = new JFrame("数学学习软件");
|
|
|
|
|
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
|
|
|
|
mainFrame.setSize(500, 400);
|
|
|
|
|
mainFrame.setLocationRelativeTo(null);
|
|
|
|
|
|
|
|
|
|
cardLayout = new CardLayout();
|
|
|
|
|
mainPanel = new JPanel(cardLayout);
|
|
|
|
|
|
|
|
|
|
createLoginPanel();
|
|
|
|
|
createRegisterPanel();
|
|
|
|
|
createSetPasswordPanel();
|
|
|
|
|
createLevelSelectionPanel();
|
|
|
|
|
createQuestionCountPanel();
|
|
|
|
|
createExamPanel();
|
|
|
|
|
createScorePanel();
|
|
|
|
|
createChangePasswordPanel();
|
|
|
|
|
|
|
|
|
|
mainFrame.add(mainPanel);
|
|
|
|
|
mainFrame.setVisible(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void createLoginPanel() {
|
|
|
|
|
JPanel panel = new JPanel(new GridBagLayout());
|
|
|
|
|
GridBagConstraints gbc = new GridBagConstraints();
|
|
|
|
|
gbc.insets = new Insets(10, 10, 10, 10);
|
|
|
|
|
gbc.fill = GridBagConstraints.HORIZONTAL;
|
|
|
|
|
|
|
|
|
|
JLabel titleLabel = new JLabel("用户登录", JLabel.CENTER);
|
|
|
|
|
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 20));
|
|
|
|
|
gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2;
|
|
|
|
|
panel.add(titleLabel, gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridwidth = 1;
|
|
|
|
|
gbc.gridy = 1; gbc.gridx = 0;
|
|
|
|
|
panel.add(new JLabel("邮箱:"), gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridx = 1;
|
|
|
|
|
emailField = new JTextField(20);
|
|
|
|
|
panel.add(emailField, gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridy = 2; gbc.gridx = 0;
|
|
|
|
|
panel.add(new JLabel("密码:"), gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridx = 1;
|
|
|
|
|
passwordField = new JPasswordField(20);
|
|
|
|
|
panel.add(passwordField, gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridy = 3; gbc.gridx = 0; gbc.gridwidth = 2;
|
|
|
|
|
JPanel buttonPanel = new JPanel(new FlowLayout());
|
|
|
|
|
|
|
|
|
|
JButton loginButton = new JButton("登录");
|
|
|
|
|
loginButton.addActionListener(e -> handleLogin());
|
|
|
|
|
buttonPanel.add(loginButton);
|
|
|
|
|
|
|
|
|
|
JButton registerButton = new JButton("注册");
|
|
|
|
|
registerButton.addActionListener(e -> cardLayout.show(mainPanel, "Register"));
|
|
|
|
|
buttonPanel.add(registerButton);
|
|
|
|
|
|
|
|
|
|
JButton changePasswordButton = new JButton("修改密码");
|
|
|
|
|
changePasswordButton.addActionListener(e -> cardLayout.show(mainPanel, "ChangePassword"));
|
|
|
|
|
buttonPanel.add(changePasswordButton);
|
|
|
|
|
|
|
|
|
|
panel.add(buttonPanel, gbc);
|
|
|
|
|
|
|
|
|
|
mainPanel.add(panel, "Login");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void createRegisterPanel() {
|
|
|
|
|
JPanel panel = new JPanel(new GridBagLayout());
|
|
|
|
|
GridBagConstraints gbc = new GridBagConstraints();
|
|
|
|
|
gbc.insets = new Insets(10, 10, 10, 10);
|
|
|
|
|
gbc.fill = GridBagConstraints.HORIZONTAL;
|
|
|
|
|
|
|
|
|
|
JLabel titleLabel = new JLabel("用户注册", JLabel.CENTER);
|
|
|
|
|
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 20));
|
|
|
|
|
gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2;
|
|
|
|
|
panel.add(titleLabel, gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridwidth = 1;
|
|
|
|
|
gbc.gridy = 1; gbc.gridx = 0;
|
|
|
|
|
panel.add(new JLabel("邮箱:"), gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridx = 1;
|
|
|
|
|
JTextField registerEmailField = new JTextField(20);
|
|
|
|
|
panel.add(registerEmailField, gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridy = 2; gbc.gridx = 0; gbc.gridwidth = 2;
|
|
|
|
|
JButton sendCodeButton = new JButton("发送验证码");
|
|
|
|
|
panel.add(sendCodeButton, gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridwidth = 1;
|
|
|
|
|
gbc.gridy = 3; gbc.gridx = 0;
|
|
|
|
|
panel.add(new JLabel("验证码:"), gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridx = 1;
|
|
|
|
|
JTextField registerCodeField = new JTextField(20);
|
|
|
|
|
panel.add(registerCodeField, gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridy = 4; gbc.gridx = 0; gbc.gridwidth = 2;
|
|
|
|
|
JButton registerButton = new JButton("注册");
|
|
|
|
|
registerButton.addActionListener(e -> {
|
|
|
|
|
String email = registerEmailField.getText();
|
|
|
|
|
String code = registerCodeField.getText();
|
|
|
|
|
|
|
|
|
|
if (email.isEmpty() || code.isEmpty()) {
|
|
|
|
|
JOptionPane.showMessageDialog(panel, "请填写邮箱和验证码");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 验证验证码(简化处理,只要6位就通过)
|
|
|
|
|
if (code.length() == 6) {
|
|
|
|
|
// 保存邮箱信息用于后续设置密码
|
|
|
|
|
mainPanel.putClientProperty("registerEmail", email);
|
|
|
|
|
cardLayout.show(mainPanel, "SetPassword");
|
|
|
|
|
} else {
|
|
|
|
|
JOptionPane.showMessageDialog(panel, "验证码必须是6位数字");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
panel.add(registerButton, gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridy = 5;
|
|
|
|
|
JButton backButton = new JButton("返回登录");
|
|
|
|
|
backButton.addActionListener(e -> cardLayout.show(mainPanel, "Login"));
|
|
|
|
|
panel.add(backButton, gbc);
|
|
|
|
|
|
|
|
|
|
sendCodeButton.addActionListener(e -> {
|
|
|
|
|
String email = registerEmailField.getText();
|
|
|
|
|
if (isValidEmail(email)) {
|
|
|
|
|
if (userManager.isEmailRegistered(email)) {
|
|
|
|
|
JOptionPane.showMessageDialog(panel, "该邮箱已被注册");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String verificationCode = generateVerificationCode();
|
|
|
|
|
if (EmailUtil.sendVerificationCode(email, verificationCode)) {
|
|
|
|
|
// 保存验证码用于验证
|
|
|
|
|
mainPanel.putClientProperty("verificationCode", verificationCode);
|
|
|
|
|
JOptionPane.showMessageDialog(panel, "验证码已发送,请查看对话框");
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
JOptionPane.showMessageDialog(panel, "请输入有效的邮箱地址");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
mainPanel.add(panel, "Register");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void createSetPasswordPanel() {
|
|
|
|
|
JPanel panel = new JPanel(new GridBagLayout());
|
|
|
|
|
GridBagConstraints gbc = new GridBagConstraints();
|
|
|
|
|
gbc.insets = new Insets(10, 10, 10, 10);
|
|
|
|
|
gbc.fill = GridBagConstraints.HORIZONTAL;
|
|
|
|
|
|
|
|
|
|
JLabel titleLabel = new JLabel("设置密码", JLabel.CENTER);
|
|
|
|
|
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 20));
|
|
|
|
|
gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2;
|
|
|
|
|
panel.add(titleLabel, gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridwidth = 1;
|
|
|
|
|
gbc.gridy = 1; gbc.gridx = 0;
|
|
|
|
|
panel.add(new JLabel("密码:"), gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridx = 1;
|
|
|
|
|
JPasswordField setPasswordField = new JPasswordField(20);
|
|
|
|
|
panel.add(setPasswordField, gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridy = 2; gbc.gridx = 0;
|
|
|
|
|
panel.add(new JLabel("确认密码:"), gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridx = 1;
|
|
|
|
|
JPasswordField confirmSetPasswordField = new JPasswordField(20);
|
|
|
|
|
panel.add(confirmSetPasswordField, gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridy = 3; gbc.gridx = 0; gbc.gridwidth = 2;
|
|
|
|
|
JButton setPasswordButton = new JButton("设置密码");
|
|
|
|
|
setPasswordButton.addActionListener(e -> {
|
|
|
|
|
String password = new String(setPasswordField.getPassword());
|
|
|
|
|
String confirmPassword = new String(confirmSetPasswordField.getPassword());
|
|
|
|
|
String email = (String) mainPanel.getClientProperty("registerEmail");
|
|
|
|
|
|
|
|
|
|
if (validatePassword(password, confirmPassword)) {
|
|
|
|
|
if (userManager.registerUser(email, password)) {
|
|
|
|
|
JOptionPane.showMessageDialog(panel, "注册成功!");
|
|
|
|
|
cardLayout.show(mainPanel, "Login");
|
|
|
|
|
} else {
|
|
|
|
|
JOptionPane.showMessageDialog(panel, "注册失败,用户已存在");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
panel.add(setPasswordButton, gbc);
|
|
|
|
|
|
|
|
|
|
mainPanel.add(panel, "SetPassword");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void createLevelSelectionPanel() {
|
|
|
|
|
JPanel panel = new JPanel(new GridLayout(4, 1, 10, 10));
|
|
|
|
|
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
|
|
|
|
|
|
|
|
|
JLabel titleLabel = new JLabel("选择学习阶段", JLabel.CENTER);
|
|
|
|
|
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 20));
|
|
|
|
|
panel.add(titleLabel);
|
|
|
|
|
|
|
|
|
|
JButton primaryButton = new JButton("小学");
|
|
|
|
|
primaryButton.addActionListener(e -> startExam("小学"));
|
|
|
|
|
panel.add(primaryButton);
|
|
|
|
|
|
|
|
|
|
JButton juniorButton = new JButton("初中");
|
|
|
|
|
juniorButton.addActionListener(e -> startExam("初中"));
|
|
|
|
|
panel.add(juniorButton);
|
|
|
|
|
|
|
|
|
|
JButton seniorButton = new JButton("高中");
|
|
|
|
|
seniorButton.addActionListener(e -> startExam("高中"));
|
|
|
|
|
panel.add(seniorButton);
|
|
|
|
|
|
|
|
|
|
mainPanel.add(panel, "LevelSelection");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void createQuestionCountPanel() {
|
|
|
|
|
JPanel panel = new JPanel(new GridBagLayout());
|
|
|
|
|
GridBagConstraints gbc = new GridBagConstraints();
|
|
|
|
|
gbc.insets = new Insets(10, 10, 10, 10);
|
|
|
|
|
|
|
|
|
|
JLabel titleLabel = new JLabel("请输入题目数量", JLabel.CENTER);
|
|
|
|
|
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 20));
|
|
|
|
|
gbc.gridx = 0; gbc.gridy = 0;
|
|
|
|
|
panel.add(titleLabel, gbc);
|
|
|
|
|
|
|
|
|
|
JTextField countField = new JTextField(10);
|
|
|
|
|
gbc.gridy = 1;
|
|
|
|
|
panel.add(countField, gbc);
|
|
|
|
|
|
|
|
|
|
JButton startButton = new JButton("开始答题");
|
|
|
|
|
startButton.addActionListener(e -> {
|
|
|
|
|
try {
|
|
|
|
|
int count = Integer.parseInt(countField.getText());
|
|
|
|
|
if (count > 0 && count <= 50) {
|
|
|
|
|
generateExamQuestions(count);
|
|
|
|
|
cardLayout.show(mainPanel, "Exam");
|
|
|
|
|
} else {
|
|
|
|
|
JOptionPane.showMessageDialog(panel, "请输入1-50之间的有效题目数量");
|
|
|
|
|
}
|
|
|
|
|
} catch (NumberFormatException ex) {
|
|
|
|
|
JOptionPane.showMessageDialog(panel, "请输入有效的数字");
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
gbc.gridy = 2;
|
|
|
|
|
panel.add(startButton, gbc);
|
|
|
|
|
|
|
|
|
|
mainPanel.add(panel, "QuestionCount");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void createExamPanel() {
|
|
|
|
|
JPanel panel = new JPanel(new BorderLayout());
|
|
|
|
|
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
|
|
|
|
|
|
|
|
|
questionLabel = new JLabel("", JLabel.CENTER);
|
|
|
|
|
questionLabel.setFont(new Font("微软雅黑", Font.PLAIN, 16));
|
|
|
|
|
panel.add(questionLabel, BorderLayout.NORTH);
|
|
|
|
|
|
|
|
|
|
JPanel optionsPanel = new JPanel(new GridLayout(4, 1, 10, 10));
|
|
|
|
|
optionGroup = new ButtonGroup();
|
|
|
|
|
optionButtons = new JRadioButton[4];
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
|
optionButtons[i] = new JRadioButton();
|
|
|
|
|
optionGroup.add(optionButtons[i]);
|
|
|
|
|
optionsPanel.add(optionButtons[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
panel.add(optionsPanel, BorderLayout.CENTER);
|
|
|
|
|
|
|
|
|
|
JButton submitButton = new JButton("提交答案");
|
|
|
|
|
submitButton.addActionListener(e -> handleAnswerSubmission());
|
|
|
|
|
panel.add(submitButton, BorderLayout.SOUTH);
|
|
|
|
|
|
|
|
|
|
mainPanel.add(panel, "Exam");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void createScorePanel() {
|
|
|
|
|
JPanel panel = new JPanel(new BorderLayout());
|
|
|
|
|
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
|
|
|
|
|
|
|
|
|
scoreLabel = new JLabel("", JLabel.CENTER);
|
|
|
|
|
scoreLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
|
|
|
|
|
panel.add(scoreLabel, BorderLayout.CENTER);
|
|
|
|
|
|
|
|
|
|
JPanel buttonPanel = new JPanel(new FlowLayout());
|
|
|
|
|
|
|
|
|
|
JButton continueButton = new JButton("继续做题");
|
|
|
|
|
continueButton.addActionListener(e -> cardLayout.show(mainPanel, "LevelSelection"));
|
|
|
|
|
buttonPanel.add(continueButton);
|
|
|
|
|
|
|
|
|
|
JButton exitButton = new JButton("退出");
|
|
|
|
|
exitButton.addActionListener(e -> {
|
|
|
|
|
currentUser = null;
|
|
|
|
|
cardLayout.show(mainPanel, "Login");
|
|
|
|
|
});
|
|
|
|
|
buttonPanel.add(exitButton);
|
|
|
|
|
|
|
|
|
|
panel.add(buttonPanel, BorderLayout.SOUTH);
|
|
|
|
|
|
|
|
|
|
mainPanel.add(panel, "Score");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void createChangePasswordPanel() {
|
|
|
|
|
JPanel panel = new JPanel(new GridBagLayout());
|
|
|
|
|
GridBagConstraints gbc = new GridBagConstraints();
|
|
|
|
|
gbc.insets = new Insets(10, 10, 10, 10);
|
|
|
|
|
gbc.fill = GridBagConstraints.HORIZONTAL;
|
|
|
|
|
|
|
|
|
|
JLabel titleLabel = new JLabel("修改密码", JLabel.CENTER);
|
|
|
|
|
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 20));
|
|
|
|
|
gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2;
|
|
|
|
|
panel.add(titleLabel, gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridwidth = 1;
|
|
|
|
|
gbc.gridy = 1; gbc.gridx = 0;
|
|
|
|
|
panel.add(new JLabel("邮箱:"), gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridx = 1;
|
|
|
|
|
JTextField changePasswordEmailField = new JTextField(20);
|
|
|
|
|
panel.add(changePasswordEmailField, gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridy = 2; gbc.gridx = 0;
|
|
|
|
|
panel.add(new JLabel("原密码:"), gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridx = 1;
|
|
|
|
|
JPasswordField oldPasswordField = new JPasswordField(20);
|
|
|
|
|
panel.add(oldPasswordField, gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridy = 3; gbc.gridx = 0;
|
|
|
|
|
panel.add(new JLabel("新密码:"), gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridx = 1;
|
|
|
|
|
JPasswordField newPasswordField = new JPasswordField(20);
|
|
|
|
|
panel.add(newPasswordField, gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridy = 4; gbc.gridx = 0;
|
|
|
|
|
panel.add(new JLabel("确认新密码:"), gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridx = 1;
|
|
|
|
|
JPasswordField confirmNewPasswordField = new JPasswordField(20);
|
|
|
|
|
panel.add(confirmNewPasswordField, gbc);
|
|
|
|
|
|
|
|
|
|
gbc.gridy = 5; gbc.gridx = 0; gbc.gridwidth = 2;
|
|
|
|
|
JPanel buttonPanel = new JPanel(new FlowLayout());
|
|
|
|
|
|
|
|
|
|
JButton changeButton = new JButton("修改密码");
|
|
|
|
|
changeButton.addActionListener(e -> {
|
|
|
|
|
String email = changePasswordEmailField.getText();
|
|
|
|
|
String oldPassword = new String(oldPasswordField.getPassword());
|
|
|
|
|
String newPassword = new String(newPasswordField.getPassword());
|
|
|
|
|
String confirmPassword = new String(confirmNewPasswordField.getPassword());
|
|
|
|
|
|
|
|
|
|
if (validatePassword(newPassword, confirmPassword)) {
|
|
|
|
|
if (userManager.updatePassword(email, oldPassword, newPassword)) {
|
|
|
|
|
JOptionPane.showMessageDialog(panel, "密码修改成功");
|
|
|
|
|
cardLayout.show(mainPanel, "Login");
|
|
|
|
|
} else {
|
|
|
|
|
JOptionPane.showMessageDialog(panel, "邮箱或原密码错误");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
buttonPanel.add(changeButton);
|
|
|
|
|
|
|
|
|
|
JButton backButton = new JButton("返回");
|
|
|
|
|
backButton.addActionListener(e -> cardLayout.show(mainPanel, "Login"));
|
|
|
|
|
buttonPanel.add(backButton);
|
|
|
|
|
|
|
|
|
|
panel.add(buttonPanel, gbc);
|
|
|
|
|
|
|
|
|
|
mainPanel.add(panel, "ChangePassword");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 事件处理方法
|
|
|
|
|
private void handleLogin() {
|
|
|
|
|
String email = emailField.getText();
|
|
|
|
|
String password = new String(passwordField.getPassword());
|
|
|
|
|
|
|
|
|
|
if (email.isEmpty() || password.isEmpty()) {
|
|
|
|
|
JOptionPane.showMessageDialog(mainPanel, "请填写邮箱和密码");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentUser = userManager.login(email, password);
|
|
|
|
|
if (currentUser != null) {
|
|
|
|
|
JOptionPane.showMessageDialog(mainPanel, "登录成功!");
|
|
|
|
|
cardLayout.show(mainPanel, "LevelSelection");
|
|
|
|
|
} else {
|
|
|
|
|
JOptionPane.showMessageDialog(mainPanel, "邮箱或密码错误");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void startExam(String level) {
|
|
|
|
|
// 保存当前选择的级别
|
|
|
|
|
mainPanel.putClientProperty("currentLevel", level);
|
|
|
|
|
cardLayout.show(mainPanel, "QuestionCount");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void generateExamQuestions(int count) {
|
|
|
|
|
String level = (String) mainPanel.getClientProperty("currentLevel");
|
|
|
|
|
QuestionGenerator generator;
|
|
|
|
|
|
|
|
|
|
switch (level) {
|
|
|
|
|
case "小学": generator = new PrimaryQuestionGenerator(); break;
|
|
|
|
|
case "初中": generator = new JuniorQuestionGenerator(); break;
|
|
|
|
|
case "高中": generator = new SeniorQuestionGenerator(); break;
|
|
|
|
|
default: generator = new PrimaryQuestionGenerator();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentExam = new ArrayList<>();
|
|
|
|
|
Set<String> generatedQuestions = new HashSet<>();
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
|
Question question;
|
|
|
|
|
int attempts = 0;
|
|
|
|
|
do {
|
|
|
|
|
question = generator.generateQuestion();
|
|
|
|
|
attempts++;
|
|
|
|
|
} while (generatedQuestions.contains(question.getContent()) && attempts < 10);
|
|
|
|
|
|
|
|
|
|
if (attempts < 10) {
|
|
|
|
|
currentExam.add(question);
|
|
|
|
|
generatedQuestions.add(question.getContent());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentQuestionIndex = 0;
|
|
|
|
|
score = 0;
|
|
|
|
|
displayCurrentQuestion();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void displayCurrentQuestion() {
|
|
|
|
|
if (currentQuestionIndex < currentExam.size()) {
|
|
|
|
|
Question question = currentExam.get(currentQuestionIndex);
|
|
|
|
|
questionLabel.setText("题目 " + (currentQuestionIndex + 1) + "/" + currentExam.size() + ": " + question.getContent());
|
|
|
|
|
|
|
|
|
|
String[] options = question.getOptions();
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
|
optionButtons[i].setText((char)('A' + i) + ". " + options[i]);
|
|
|
|
|
optionButtons[i].setSelected(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void handleAnswerSubmission() {
|
|
|
|
|
int selectedIndex = -1;
|
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
|
if (optionButtons[i].isSelected()) {
|
|
|
|
|
selectedIndex = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selectedIndex == -1) {
|
|
|
|
|
JOptionPane.showMessageDialog(mainPanel, "请选择一个答案");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Question currentQuestion = currentExam.get(currentQuestionIndex);
|
|
|
|
|
if (selectedIndex == currentQuestion.getCorrectAnswer()) {
|
|
|
|
|
score++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentQuestionIndex++;
|
|
|
|
|
|
|
|
|
|
if (currentQuestionIndex < currentExam.size()) {
|
|
|
|
|
displayCurrentQuestion();
|
|
|
|
|
} else {
|
|
|
|
|
showScore();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void showScore() {
|
|
|
|
|
double percentage = (double) score / currentExam.size() * 100;
|
|
|
|
|
scoreLabel.setText(String.format("得分: %d/%d (%.1f%%)", score, currentExam.size(), percentage));
|
|
|
|
|
cardLayout.show(mainPanel, "Score");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 工具方法
|
|
|
|
|
private boolean isValidEmail(String email) {
|
|
|
|
|
String emailRegex = "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$";
|
|
|
|
|
Pattern pattern = Pattern.compile(emailRegex);
|
|
|
|
|
return pattern.matcher(email).matches();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean validatePassword(String password, String confirmPassword) {
|
|
|
|
|
if (!password.equals(confirmPassword)) {
|
|
|
|
|
JOptionPane.showMessageDialog(mainPanel, "两次输入的密码不一致");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (password.length() < 6 || password.length() > 10) {
|
|
|
|
|
JOptionPane.showMessageDialog(mainPanel, "密码长度应为6-10位");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
boolean hasUpper = false, hasLower = false, hasDigit = false;
|
|
|
|
|
for (char c : password.toCharArray()) {
|
|
|
|
|
if (Character.isUpperCase(c)) hasUpper = true;
|
|
|
|
|
if (Character.isLowerCase(c)) hasLower = true;
|
|
|
|
|
if (Character.isDigit(c)) hasDigit = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!hasUpper || !hasLower || !hasDigit) {
|
|
|
|
|
JOptionPane.showMessageDialog(mainPanel, "密码必须包含大小写字母和数字");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String generateVerificationCode() {
|
|
|
|
|
Random random = new Random();
|
|
|
|
|
return String.format("%06d", random.nextInt(1000000));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
// 设置界面风格
|
|
|
|
|
try {
|
|
|
|
|
UIManager.setLookAndFeel(UIManager.getLookAndFeel());
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SwingUtilities.invokeLater(() -> new MathLearningApp());
|
|
|
|
|
}
|
|
|
|
|
}
|