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