From 44aaaa6fd77f8679d4d5026dd8d844caf303b0c8 Mon Sep 17 00:00:00 2001 From: luoyuehang <2830398107@qq.com> Date: Sat, 11 Oct 2025 19:21:29 +0800 Subject: [PATCH] v1.0 --- src/.gitignore | 30 ++ src/.idea/.gitignore | 8 + src/.idea/misc.xml | 6 + src/.idea/modules.xml | 8 + src/.idea/vcs.xml | 6 + src/MathExamGenerator.iml | 11 + src/src/MathLearningApp.java | 841 +++++++++++++++++++++++++++++++++++ src/user_data/users.dat | Bin 0 -> 294 bytes src/users.txt | 3 + 9 files changed, 913 insertions(+) create mode 100644 src/.gitignore create mode 100644 src/.idea/.gitignore create mode 100644 src/.idea/misc.xml create mode 100644 src/.idea/modules.xml create mode 100644 src/.idea/vcs.xml create mode 100644 src/MathExamGenerator.iml create mode 100644 src/src/MathLearningApp.java create mode 100644 src/user_data/users.dat create mode 100644 src/users.txt diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..13275f1 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,30 @@ +### IntelliJ IDEA ### +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ +.kotlin + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/src/.idea/.gitignore b/src/.idea/.gitignore new file mode 100644 index 0000000..35410ca --- /dev/null +++ b/src/.idea/.gitignore @@ -0,0 +1,8 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/src/.idea/misc.xml b/src/.idea/misc.xml new file mode 100644 index 0000000..6f29fee --- /dev/null +++ b/src/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/.idea/modules.xml b/src/.idea/modules.xml new file mode 100644 index 0000000..49b3317 --- /dev/null +++ b/src/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/.idea/vcs.xml b/src/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/src/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/MathExamGenerator.iml b/src/MathExamGenerator.iml new file mode 100644 index 0000000..c90834f --- /dev/null +++ b/src/MathExamGenerator.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/src/MathLearningApp.java b/src/src/MathLearningApp.java new file mode 100644 index 0000000..7b7e167 --- /dev/null +++ b/src/src/MathLearningApp.java @@ -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 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 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 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 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 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 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 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()); + } +} \ No newline at end of file diff --git a/src/user_data/users.dat b/src/user_data/users.dat new file mode 100644 index 0000000000000000000000000000000000000000..209d73f9cd9343fe6f52df7f1f109ba38a5cbef2 GIT binary patch literal 294 zcmZ4UmVvdnh(S0ju`E%qv?Mb}&#|Z|vC=2AxTK=-lI+amiF2757(E$SiZiQHD+(AG z7?{ghKqOETOK5Rw5s(c6j7$tHJ`Aj>xrv!MB@9A7AoKKd67$magG-7s^U|$-7&rKK@T{8FIyyu{p8C|{r~wJ0+!GdZy&Ge6HcKLsLQQBcAl08;6YT9KGr zkdvyHoS$35zz(w8(8#!if#>;>T~GFPKcBMVS;zK*Vvu1>B@DbJsl_D@AOfmB1jvM_ jf3~&z#rmn}>Va}V