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 1/5] 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 Date: Sat, 11 Oct 2025 19:31:36 +0800 Subject: [PATCH 2/5] v2.0 --- src/.idea/.name | 1 + .../inspectionProfiles/Project_Default.xml | 6 + src/.idea/misc.xml | 2 +- src/.idea/vcs.xml | 1 + src/src/MathLearningApp.java | 292 ++++++++++++++---- 5 files changed, 240 insertions(+), 62 deletions(-) create mode 100644 src/.idea/.name create mode 100644 src/.idea/inspectionProfiles/Project_Default.xml diff --git a/src/.idea/.name b/src/.idea/.name new file mode 100644 index 0000000..696aa06 --- /dev/null +++ b/src/.idea/.name @@ -0,0 +1 @@ +MathLearningApp.java \ No newline at end of file diff --git a/src/.idea/inspectionProfiles/Project_Default.xml b/src/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..a374ccf --- /dev/null +++ b/src/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/src/.idea/misc.xml b/src/.idea/misc.xml index 6f29fee..28c56bd 100644 --- a/src/.idea/misc.xml +++ b/src/.idea/misc.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/src/.idea/vcs.xml b/src/.idea/vcs.xml index 94a25f7..288b36b 100644 --- a/src/.idea/vcs.xml +++ b/src/.idea/vcs.xml @@ -1,6 +1,7 @@ + \ No newline at end of file diff --git a/src/src/MathLearningApp.java b/src/src/MathLearningApp.java index 7b7e167..64c9b95 100644 --- a/src/src/MathLearningApp.java +++ b/src/src/MathLearningApp.java @@ -11,15 +11,18 @@ import java.util.Map; // 用户类 class User { + private String username; private String email; private String password; private String verificationCode; - public User(String email, String password) { + public User(String username, String email, String password) { + this.username = username; this.email = email; this.password = password; } + public String getUsername() { return username; } public String getEmail() { return email; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @@ -245,27 +248,36 @@ class EmailUtil { // 用户管理类 class UserManager { - private Map users; + private Map usersByEmail; + private Map usersByUsername; public UserManager() { - users = new HashMap<>(); + usersByEmail = new HashMap<>(); + usersByUsername = new HashMap<>(); // 添加一些测试用户 - users.put("test@test.com", new User("test@test.com", "Test123")); - users.put("user@example.com", new User("user@example.com", "User123")); + addUser("测试用户", "test@test.com", "Test123"); + addUser("示例用户", "user@example.com", "User123"); } - public boolean registerUser(String email, String password) { - if (users.containsKey(email)) { + private void addUser(String username, String email, String password) { + User user = new User(username, email, password); + usersByEmail.put(email, user); + usersByUsername.put(username, user); + } + + public boolean registerUser(String username, String email, String password) { + if (usersByEmail.containsKey(email) || usersByUsername.containsKey(username)) { return false; } - User user = new User(email, password); - users.put(email, user); + User user = new User(username, email, password); + usersByEmail.put(email, user); + usersByUsername.put(username, user); return true; } public User login(String email, String password) { - User user = users.get(email); + User user = usersByEmail.get(email); if (user != null && user.getPassword().equals(password)) { return user; } @@ -273,7 +285,7 @@ class UserManager { } public boolean updatePassword(String email, String oldPassword, String newPassword) { - User user = users.get(email); + User user = usersByEmail.get(email); if (user != null && user.getPassword().equals(oldPassword)) { user.setPassword(newPassword); return true; @@ -282,7 +294,11 @@ class UserManager { } public boolean isEmailRegistered(String email) { - return users.containsKey(email); + return usersByEmail.containsKey(email); + } + + public boolean isUsernameRegistered(String username) { + return usersByUsername.containsKey(username); } } @@ -304,6 +320,7 @@ public class MathLearningApp { private ButtonGroup optionGroup; private JRadioButton[] optionButtons; private JLabel scoreLabel; + private JLabel welcomeLabel; public MathLearningApp() { userManager = new UserManager(); @@ -313,7 +330,7 @@ public class MathLearningApp { private void initializeUI() { mainFrame = new JFrame("数学学习软件"); mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - mainFrame.setSize(500, 400); + mainFrame.setSize(600, 500); mainFrame.setLocationRelativeTo(null); cardLayout = new CardLayout(); @@ -335,46 +352,66 @@ public class MathLearningApp { private void createLoginPanel() { JPanel panel = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); - gbc.insets = new Insets(10, 10, 10, 10); + gbc.insets = new Insets(15, 15, 15, 15); gbc.fill = GridBagConstraints.HORIZONTAL; - JLabel titleLabel = new JLabel("用户登录", JLabel.CENTER); - titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 20)); + JLabel titleLabel = new JLabel("数学学习软件", JLabel.CENTER); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24)); + titleLabel.setForeground(new Color(0, 100, 200)); gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2; panel.add(titleLabel, gbc); + JLabel subtitleLabel = new JLabel("用户登录", JLabel.CENTER); + subtitleLabel.setFont(new Font("微软雅黑", Font.PLAIN, 16)); + subtitleLabel.setForeground(Color.GRAY); + gbc.gridy = 1; + panel.add(subtitleLabel, gbc); + gbc.gridwidth = 1; - gbc.gridy = 1; gbc.gridx = 0; - panel.add(new JLabel("邮箱:"), gbc); + gbc.gridy = 2; gbc.gridx = 0; + JLabel emailLabel = new JLabel("邮箱:"); + emailLabel.setFont(new Font("微软雅黑", Font.BOLD, 14)); + panel.add(emailLabel, gbc); gbc.gridx = 1; emailField = new JTextField(20); + emailField.setFont(new Font("微软雅黑", Font.PLAIN, 14)); panel.add(emailField, gbc); - gbc.gridy = 2; gbc.gridx = 0; - panel.add(new JLabel("密码:"), gbc); + gbc.gridy = 3; gbc.gridx = 0; + JLabel passwordLabel = new JLabel("密码:"); + passwordLabel.setFont(new Font("微软雅黑", Font.BOLD, 14)); + panel.add(passwordLabel, gbc); gbc.gridx = 1; passwordField = new JPasswordField(20); + passwordField.setFont(new Font("微软雅黑", Font.PLAIN, 14)); panel.add(passwordField, gbc); - gbc.gridy = 3; gbc.gridx = 0; gbc.gridwidth = 2; + gbc.gridy = 4; gbc.gridx = 0; gbc.gridwidth = 2; JPanel buttonPanel = new JPanel(new FlowLayout()); - JButton loginButton = new JButton("登录"); + JButton loginButton = createStyledButton("登录", new Color(70, 130, 180)); loginButton.addActionListener(e -> handleLogin()); buttonPanel.add(loginButton); - JButton registerButton = new JButton("注册"); + JButton registerButton = createStyledButton("注册", new Color(60, 179, 113)); registerButton.addActionListener(e -> cardLayout.show(mainPanel, "Register")); buttonPanel.add(registerButton); - JButton changePasswordButton = new JButton("修改密码"); + JButton changePasswordButton = createStyledButton("修改密码", new Color(218, 165, 32)); changePasswordButton.addActionListener(e -> cardLayout.show(mainPanel, "ChangePassword")); buttonPanel.add(changePasswordButton); panel.add(buttonPanel, gbc); + // 添加测试账号提示 + gbc.gridy = 5; + JLabel testAccountLabel = new JLabel("测试账号: test@test.com / Test123", JLabel.CENTER); + testAccountLabel.setFont(new Font("微软雅黑", Font.ITALIC, 12)); + testAccountLabel.setForeground(Color.GRAY); + panel.add(testAccountLabel, gbc); + mainPanel.add(panel, "Login"); } @@ -391,38 +428,52 @@ public class MathLearningApp { gbc.gridwidth = 1; gbc.gridy = 1; gbc.gridx = 0; + panel.add(new JLabel("用户名:"), gbc); + + gbc.gridx = 1; + JTextField usernameField = new JTextField(20); + panel.add(usernameField, gbc); + + gbc.gridy = 2; 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("发送验证码"); + gbc.gridy = 3; gbc.gridx = 0; gbc.gridwidth = 2; + JButton sendCodeButton = createStyledButton("发送验证码", new Color(70, 130, 180)); panel.add(sendCodeButton, gbc); gbc.gridwidth = 1; - gbc.gridy = 3; gbc.gridx = 0; + gbc.gridy = 4; 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("注册"); + gbc.gridy = 5; gbc.gridx = 0; gbc.gridwidth = 2; + JButton registerButton = createStyledButton("注册", new Color(60, 179, 113)); registerButton.addActionListener(e -> { + String username = usernameField.getText(); String email = registerEmailField.getText(); String code = registerCodeField.getText(); - if (email.isEmpty() || code.isEmpty()) { - JOptionPane.showMessageDialog(panel, "请填写邮箱和验证码"); + if (username.isEmpty() || email.isEmpty() || code.isEmpty()) { + JOptionPane.showMessageDialog(panel, "请填写所有信息"); + return; + } + + if (userManager.isUsernameRegistered(username)) { + JOptionPane.showMessageDialog(panel, "用户名已被使用"); return; } // 验证验证码(简化处理,只要6位就通过) if (code.length() == 6) { - // 保存邮箱信息用于后续设置密码 + // 保存用户信息用于后续设置密码 + mainPanel.putClientProperty("registerUsername", username); mainPanel.putClientProperty("registerEmail", email); cardLayout.show(mainPanel, "SetPassword"); } else { @@ -431,13 +482,25 @@ public class MathLearningApp { }); panel.add(registerButton, gbc); - gbc.gridy = 5; - JButton backButton = new JButton("返回登录"); + gbc.gridy = 6; + JButton backButton = createStyledButton("返回登录", new Color(169, 169, 169)); backButton.addActionListener(e -> cardLayout.show(mainPanel, "Login")); panel.add(backButton, gbc); sendCodeButton.addActionListener(e -> { + String username = usernameField.getText(); String email = registerEmailField.getText(); + + if (username.isEmpty() || email.isEmpty()) { + JOptionPane.showMessageDialog(panel, "请填写用户名和邮箱"); + return; + } + + if (userManager.isUsernameRegistered(username)) { + JOptionPane.showMessageDialog(panel, "用户名已被使用"); + return; + } + if (isValidEmail(email)) { if (userManager.isEmailRegistered(email)) { JOptionPane.showMessageDialog(panel, "该邮箱已被注册"); @@ -485,14 +548,15 @@ public class MathLearningApp { panel.add(confirmSetPasswordField, gbc); gbc.gridy = 3; gbc.gridx = 0; gbc.gridwidth = 2; - JButton setPasswordButton = new JButton("设置密码"); + JButton setPasswordButton = createStyledButton("设置密码", new Color(60, 179, 113)); setPasswordButton.addActionListener(e -> { String password = new String(setPasswordField.getPassword()); String confirmPassword = new String(confirmSetPasswordField.getPassword()); + String username = (String) mainPanel.getClientProperty("registerUsername"); String email = (String) mainPanel.getClientProperty("registerEmail"); if (validatePassword(password, confirmPassword)) { - if (userManager.registerUser(email, password)) { + if (userManager.registerUser(username, email, password)) { JOptionPane.showMessageDialog(panel, "注册成功!"); cardLayout.show(mainPanel, "Login"); } else { @@ -506,24 +570,37 @@ public class MathLearningApp { } private void createLevelSelectionPanel() { - JPanel panel = new JPanel(new GridLayout(4, 1, 10, 10)); - panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + JPanel panel = new JPanel(new BorderLayout()); + panel.setBorder(BorderFactory.createEmptyBorder(30, 30, 30, 30)); + panel.setBackground(new Color(240, 248, 255)); JLabel titleLabel = new JLabel("选择学习阶段", JLabel.CENTER); - titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 20)); - panel.add(titleLabel); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24)); + titleLabel.setForeground(new Color(0, 100, 200)); + panel.add(titleLabel, BorderLayout.NORTH); + + welcomeLabel = new JLabel("", JLabel.CENTER); + welcomeLabel.setFont(new Font("微软雅黑", Font.PLAIN, 16)); + welcomeLabel.setForeground(Color.DARK_GRAY); + panel.add(welcomeLabel, BorderLayout.CENTER); - JButton primaryButton = new JButton("小学"); + JPanel buttonPanel = new JPanel(new GridLayout(3, 1, 20, 20)); + buttonPanel.setBorder(BorderFactory.createEmptyBorder(20, 50, 20, 50)); + buttonPanel.setBackground(new Color(240, 248, 255)); + + JButton primaryButton = createLevelButton("小学", new Color(135, 206, 250)); primaryButton.addActionListener(e -> startExam("小学")); - panel.add(primaryButton); + buttonPanel.add(primaryButton); - JButton juniorButton = new JButton("初中"); + JButton juniorButton = createLevelButton("初中", new Color(100, 149, 237)); juniorButton.addActionListener(e -> startExam("初中")); - panel.add(juniorButton); + buttonPanel.add(juniorButton); - JButton seniorButton = new JButton("高中"); + JButton seniorButton = createLevelButton("高中", new Color(65, 105, 225)); seniorButton.addActionListener(e -> startExam("高中")); - panel.add(seniorButton); + buttonPanel.add(seniorButton); + + panel.add(buttonPanel, BorderLayout.SOUTH); mainPanel.add(panel, "LevelSelection"); } @@ -531,7 +608,7 @@ public class MathLearningApp { private void createQuestionCountPanel() { JPanel panel = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); - gbc.insets = new Insets(10, 10, 10, 10); + gbc.insets = new Insets(15, 15, 15, 15); JLabel titleLabel = new JLabel("请输入题目数量", JLabel.CENTER); titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 20)); @@ -539,10 +616,17 @@ public class MathLearningApp { panel.add(titleLabel, gbc); JTextField countField = new JTextField(10); + countField.setFont(new Font("微软雅黑", Font.PLAIN, 16)); gbc.gridy = 1; panel.add(countField, gbc); - JButton startButton = new JButton("开始答题"); + JLabel hintLabel = new JLabel("(建议5-20题)", JLabel.CENTER); + hintLabel.setFont(new Font("微软雅黑", Font.ITALIC, 12)); + hintLabel.setForeground(Color.GRAY); + gbc.gridy = 2; + panel.add(hintLabel, gbc); + + JButton startButton = createStyledButton("开始答题", new Color(60, 179, 113)); startButton.addActionListener(e -> { try { int count = Integer.parseInt(countField.getText()); @@ -556,7 +640,7 @@ public class MathLearningApp { JOptionPane.showMessageDialog(panel, "请输入有效的数字"); } }); - gbc.gridy = 2; + gbc.gridy = 3; panel.add(startButton, gbc); mainPanel.add(panel, "QuestionCount"); @@ -565,24 +649,31 @@ public class MathLearningApp { private void createExamPanel() { JPanel panel = new JPanel(new BorderLayout()); panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + panel.setBackground(new Color(240, 248, 255)); questionLabel = new JLabel("", JLabel.CENTER); - questionLabel.setFont(new Font("微软雅黑", Font.PLAIN, 16)); + questionLabel.setFont(new Font("微软雅黑", Font.PLAIN, 18)); + questionLabel.setBorder(BorderFactory.createEmptyBorder(10, 10, 20, 10)); panel.add(questionLabel, BorderLayout.NORTH); - JPanel optionsPanel = new JPanel(new GridLayout(4, 1, 10, 10)); + JPanel optionsPanel = new JPanel(new GridLayout(4, 1, 15, 15)); + optionsPanel.setBorder(BorderFactory.createEmptyBorder(20, 50, 20, 50)); + optionsPanel.setBackground(new Color(240, 248, 255)); + optionGroup = new ButtonGroup(); optionButtons = new JRadioButton[4]; for (int i = 0; i < 4; i++) { optionButtons[i] = new JRadioButton(); + optionButtons[i].setFont(new Font("微软雅黑", Font.PLAIN, 16)); + optionButtons[i].setBackground(new Color(240, 248, 255)); optionGroup.add(optionButtons[i]); optionsPanel.add(optionButtons[i]); } panel.add(optionsPanel, BorderLayout.CENTER); - JButton submitButton = new JButton("提交答案"); + JButton submitButton = createStyledButton("提交答案", new Color(70, 130, 180)); submitButton.addActionListener(e -> handleAnswerSubmission()); panel.add(submitButton, BorderLayout.SOUTH); @@ -591,19 +682,26 @@ public class MathLearningApp { private void createScorePanel() { JPanel panel = new JPanel(new BorderLayout()); - panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + panel.setBorder(BorderFactory.createEmptyBorder(40, 40, 40, 40)); + panel.setBackground(new Color(240, 248, 255)); + + JLabel titleLabel = new JLabel("答题完成", JLabel.CENTER); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 28)); + titleLabel.setForeground(new Color(0, 100, 200)); + panel.add(titleLabel, BorderLayout.NORTH); scoreLabel = new JLabel("", JLabel.CENTER); - scoreLabel.setFont(new Font("微软雅黑", Font.BOLD, 24)); + scoreLabel.setFont(new Font("微软雅黑", Font.BOLD, 36)); + scoreLabel.setForeground(new Color(220, 20, 60)); panel.add(scoreLabel, BorderLayout.CENTER); JPanel buttonPanel = new JPanel(new FlowLayout()); - JButton continueButton = new JButton("继续做题"); + JButton continueButton = createStyledButton("继续做题", new Color(60, 179, 113)); continueButton.addActionListener(e -> cardLayout.show(mainPanel, "LevelSelection")); buttonPanel.add(continueButton); - JButton exitButton = new JButton("退出"); + JButton exitButton = createStyledButton("退出登录", new Color(205, 92, 92)); exitButton.addActionListener(e -> { currentUser = null; cardLayout.show(mainPanel, "Login"); @@ -658,7 +756,7 @@ public class MathLearningApp { gbc.gridy = 5; gbc.gridx = 0; gbc.gridwidth = 2; JPanel buttonPanel = new JPanel(new FlowLayout()); - JButton changeButton = new JButton("修改密码"); + JButton changeButton = createStyledButton("修改密码", new Color(60, 179, 113)); changeButton.addActionListener(e -> { String email = changePasswordEmailField.getText(); String oldPassword = new String(oldPasswordField.getPassword()); @@ -676,7 +774,7 @@ public class MathLearningApp { }); buttonPanel.add(changeButton); - JButton backButton = new JButton("返回"); + JButton backButton = createStyledButton("返回", new Color(169, 169, 169)); backButton.addActionListener(e -> cardLayout.show(mainPanel, "Login")); buttonPanel.add(backButton); @@ -697,6 +795,7 @@ public class MathLearningApp { currentUser = userManager.login(email, password); if (currentUser != null) { + welcomeLabel.setText("欢迎, " + currentUser.getUsername() + "!"); JOptionPane.showMessageDialog(mainPanel, "登录成功!"); cardLayout.show(mainPanel, "LevelSelection"); } else { @@ -746,7 +845,8 @@ public class MathLearningApp { private void displayCurrentQuestion() { if (currentQuestionIndex < currentExam.size()) { Question question = currentExam.get(currentQuestionIndex); - questionLabel.setText("题目 " + (currentQuestionIndex + 1) + "/" + currentExam.size() + ": " + question.getContent()); + questionLabel.setText("
题目 " + (currentQuestionIndex + 1) + "/" + + currentExam.size() + "
" + question.getContent() + "
"); String[] options = question.getOptions(); for (int i = 0; i < 4; i++) { @@ -785,12 +885,82 @@ public class MathLearningApp { } private void showScore() { - double percentage = (double) score / currentExam.size() * 100; - scoreLabel.setText(String.format("得分: %d/%d (%.1f%%)", score, currentExam.size(), percentage)); + // 计算百分制得分 + int totalQuestions = currentExam.size(); + int percentScore = (int) Math.round((double) score / totalQuestions * 100); + + // 根据得分设置不同的颜色和评语 + String comment; + Color color; + if (percentScore >= 90) { + comment = "优秀!"; + color = new Color(0, 128, 0); // 绿色 + } else if (percentScore >= 80) { + comment = "良好!"; + color = new Color(0, 100, 0); // 深绿色 + } else if (percentScore >= 60) { + comment = "及格!"; + color = new Color(218, 165, 32); // 金色 + } else { + comment = "加油!"; + color = new Color(220, 20, 60); // 红色 + } + + scoreLabel.setText("
" + + "得分: " + percentScore + "分
" + + "(" + score + "/" + totalQuestions + ")
" + + "" + comment + "
"); cardLayout.show(mainPanel, "Score"); } // 工具方法 + private JButton createStyledButton(String text, Color color) { + JButton button = new JButton(text); + button.setFont(new Font("微软雅黑", Font.BOLD, 14)); + button.setBackground(color); + button.setForeground(Color.WHITE); + button.setFocusPainted(false); + button.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20)); + button.setCursor(new Cursor(Cursor.HAND_CURSOR)); + + // 添加鼠标悬停效果 + button.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseEntered(java.awt.event.MouseEvent evt) { + button.setBackground(color.darker()); + } + public void mouseExited(java.awt.event.MouseEvent evt) { + button.setBackground(color); + } + }); + + return button; + } + + private JButton createLevelButton(String text, Color color) { + JButton button = new JButton(text); + button.setFont(new Font("微软雅黑", Font.BOLD, 18)); + button.setBackground(color); + button.setForeground(Color.WHITE); + button.setFocusPainted(false); + button.setBorder(BorderFactory.createEmptyBorder(15, 0, 15, 0)); + button.setCursor(new Cursor(Cursor.HAND_CURSOR)); + + button.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseEntered(java.awt.event.MouseEvent evt) { + button.setBackground(color.darker()); + } + public void mouseExited(java.awt.event.MouseEvent evt) { + button.setBackground(color); + } + }); + + return button; + } + + private String getHexColor(Color color) { + return String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue()); + } + 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); -- 2.34.1 From 5bb8da09f4a86f71c46595f6e2a29352ad411bfe Mon Sep 17 00:00:00 2001 From: luoyuehang <2830398107@qq.com> Date: Sat, 11 Oct 2025 19:45:04 +0800 Subject: [PATCH 3/5] v3.0 --- src/src/MathLearningApp.java | 634 ++++++++++++++++++++++++++--------- 1 file changed, 468 insertions(+), 166 deletions(-) diff --git a/src/src/MathLearningApp.java b/src/src/MathLearningApp.java index 7b7e167..e6bf77e 100644 --- a/src/src/MathLearningApp.java +++ b/src/src/MathLearningApp.java @@ -11,15 +11,18 @@ import java.util.Map; // 用户类 class User { + private String username; private String email; private String password; private String verificationCode; - public User(String email, String password) { + public User(String username, String email, String password) { + this.username = username; this.email = email; this.password = password; } + public String getUsername() { return username; } public String getEmail() { return email; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @@ -87,40 +90,55 @@ abstract class QuestionGenerator { } // 小学题目生成器 +// 修改 PrimaryQuestionGenerator 类 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); +public Question generateQuestion() { + // 修改操作数数量为 1-5 个,确保至少为1 + int operandCount = Math.max(1, random.nextInt(5) + 1); + StringBuilder question = new StringBuilder(); + + // 只有当操作数大于等于3时才可能添加括号 + boolean hasParentheses = random.nextBoolean() && operandCount >= 3; + int parenthesesPosition = 0; + if (hasParentheses && operandCount >= 3) { + parenthesesPosition = random.nextInt(operandCount - 2); // 确保不会越界 + } - for (int i = 0; i < operandCount; i++) { - if (hasParentheses && i == parenthesesPosition) { - question.append("("); - } + for (int i = 0; i < operandCount; i++) { + if (hasParentheses && i == parenthesesPosition) { + question.append("("); + } - question.append(generateNumber()); + question.append(generateNumber()); - if (hasParentheses && i == parenthesesPosition + 1) { - question.append(")"); - } + if (hasParentheses && i == parenthesesPosition + 1) { + question.append(")"); + } - if (i < operandCount - 1) { - question.append(" ").append(generateOperator()).append(" "); - } + if (i < operandCount - 1) { + question.append(" ").append(generatePrimaryOperator()).append(" "); } + } - question.append(" = ?"); + question.append(" = ?"); - // 计算正确答案 - int correctAnswer = calculateSimpleExpression(question.toString()); - String[] options = generateOptions(correctAnswer); + // 计算正确答案 + int correctAnswer = calculateSimpleExpression(question.toString()); + String[] options = generateOptions(correctAnswer); - return new Question(question.toString(), options, Arrays.asList(options).indexOf(String.valueOf(correctAnswer))); + return new Question(question.toString(), options, Arrays.asList(options).indexOf(String.valueOf(correctAnswer))); +} + + + // 添加专门用于小学级别的操作符生成方法 + private String generatePrimaryOperator() { + // 小学只包含 +, -, *, 不包含除法 + String[] operators = {"+", "-", "*","/"}; + return operators[random.nextInt(operators.length)]; } + // 保持原有的 calculateSimpleExpression 方法不变 private int calculateSimpleExpression(String expression) { try { // 简化计算,移除括号和问号 @@ -149,59 +167,147 @@ class PrimaryQuestionGenerator extends QuestionGenerator { } } + // 初中题目生成器 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))); + if (random.nextBoolean()) { + // 平方运算 + String question = num + "² = ?"; + int correctAnswer = num * num; + String[] options = generateOptions(correctAnswer); + return new Question(question, options, Arrays.asList(options).indexOf(String.valueOf(correctAnswer))); + } else { + // 开根号运算 + String question = "√" + num + " = ?"; + int correctAnswer = (int) Math.sqrt(num); + String[] options = generateOptions(correctAnswer); + return new Question(question, options, Arrays.asList(options).indexOf(String.valueOf(correctAnswer))); + } } else { - int num = generateNumber(); - question.append(num).append("² = ?"); - int correctAnswer = num * num; + // 生成包含基本运算的表达式,操作数为1-5个 + int operandCount = random.nextInt(5) + 1; + StringBuilder question = new StringBuilder(); + + for (int i = 0; i < operandCount; i++) { + question.append(generateNumber()); + if (i < operandCount - 1) { + question.append(" ").append(generateJuniorOperator()).append(" "); + } + } + + question.append(" = ?"); + + // 计算正确答案 + double result = calculateExpression(question.toString()); + int correctAnswer = (int) Math.round(result); String[] options = generateOptions(correctAnswer); return new Question(question.toString(), options, Arrays.asList(options).indexOf(String.valueOf(correctAnswer))); } } + + // 为初中级别生成操作符(可以包含除法) + private String generateJuniorOperator() { + String[] operators = {"+", "-", "*", "/"}; + return operators[random.nextInt(operators.length)]; + } + + // 简化的表达式计算方法 + private double calculateExpression(String expression) { + try { + String cleanExpr = expression.replace(" = ?", ""); + // 添加类型转换 + Object result = new javax.script.ScriptEngineManager().getEngineByName("JavaScript").eval(cleanExpr); + if (result instanceof Number) { + return ((Number) result).doubleValue(); + } else { + return random.nextInt(100) + 1; + } + } catch (Exception e) { + return random.nextInt(100) + 1; + } +} + } + // 高中题目生成器 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; + // 高中题目至少包含一个三角函数 + if (random.nextBoolean()) { + // 生成三角函数题目 + 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))); + } else { + // 生成包含基本运算和三角函数的复合表达式 + return generateComplexExpression(); } + } - // 四舍五入到两位小数 - double roundedResult = Math.round(result * 100.0) / 100.0; - String[] options = generateTrigOptions(roundedResult); + private Question generateComplexExpression() { + // 生成一个包含三角函数的复合表达式 + int operandCount = random.nextInt(4) + 2; // 2-5个操作数 + StringBuilder question = new StringBuilder(); + + for (int i = 0; i < operandCount; i++) { + if (random.nextBoolean() && i < operandCount - 1) { + // 添加三角函数 + String trigFunction = trigFunctions[random.nextInt(trigFunctions.length)]; + int angle = random.nextInt(360); + question.append(trigFunction).append("(").append(angle).append("°)"); + } else { + // 添加普通数字 + question.append(generateNumber()); + } - return new Question(question, options, Arrays.asList(options).indexOf(String.format("%.2f", roundedResult))); + if (i < operandCount - 1) { + question.append(" ").append(generateSeniorOperator()).append(" "); + } + } + + question.append(" = ?"); + + // 对于复杂表达式,简化处理,生成一个合理的结果 + int correctAnswer = random.nextInt(200) - 100; // 结果范围 -100 到 100 + String[] options = generateOptions(correctAnswer); + return new Question(question.toString(), options, Arrays.asList(options).indexOf(String.valueOf(correctAnswer))); + } + + private String generateSeniorOperator() { + String[] operators = {"+", "-", "*", "/"}; + return operators[random.nextInt(operators.length)]; } + // 保持原有的 generateTrigOptions 方法不变 private String[] generateTrigOptions(double correctAnswer) { String[] options = new String[4]; Set usedAnswers = new HashSet<>(); @@ -227,6 +333,7 @@ class SeniorQuestionGenerator extends QuestionGenerator { } } + // 模拟邮箱工具类 class EmailUtil { public static boolean sendVerificationCode(String toEmail, String code) { @@ -245,35 +352,55 @@ class EmailUtil { // 用户管理类 class UserManager { - private Map users; + private Map usersByEmail; + private Map usersByUsername; public UserManager() { - users = new HashMap<>(); + usersByEmail = new HashMap<>(); + usersByUsername = new HashMap<>(); // 添加一些测试用户 - users.put("test@test.com", new User("test@test.com", "Test123")); - users.put("user@example.com", new User("user@example.com", "User123")); + addUser("测试用户", "test@test.com", "Test123"); + addUser("示例用户", "user@example.com", "User123"); } - public boolean registerUser(String email, String password) { - if (users.containsKey(email)) { + private void addUser(String username, String email, String password) { + User user = new User(username, email, password); + usersByEmail.put(email, user); + usersByUsername.put(username, user); + } + // 在UserManager类中添加新的updatePasswordByUsername方法 + public boolean updatePasswordByUsername(String username, String oldPassword, String newPassword) { + User user = usersByUsername.get(username); + if (user != null && user.getPassword().equals(oldPassword)) { + user.setPassword(newPassword); + return true; + } + return false; + } + + public boolean registerUser(String username, String email, String password) { + if (usersByEmail.containsKey(email) || usersByUsername.containsKey(username)) { return false; } - User user = new User(email, password); - users.put(email, user); + User user = new User(username, email, password); + usersByEmail.put(email, user); + usersByUsername.put(username, user); return true; } - public User login(String email, String password) { - User user = users.get(email); + + public User login(String username, String password) { + User user = usersByUsername.get(username); 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); + User user = usersByEmail.get(email); if (user != null && user.getPassword().equals(oldPassword)) { user.setPassword(newPassword); return true; @@ -282,7 +409,11 @@ class UserManager { } public boolean isEmailRegistered(String email) { - return users.containsKey(email); + return usersByEmail.containsKey(email); + } + + public boolean isUsernameRegistered(String username) { + return usersByUsername.containsKey(username); } } @@ -304,6 +435,7 @@ public class MathLearningApp { private ButtonGroup optionGroup; private JRadioButton[] optionButtons; private JLabel scoreLabel; + private JLabel welcomeLabel; public MathLearningApp() { userManager = new UserManager(); @@ -313,7 +445,7 @@ public class MathLearningApp { private void initializeUI() { mainFrame = new JFrame("数学学习软件"); mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - mainFrame.setSize(500, 400); + mainFrame.setSize(600, 500); mainFrame.setLocationRelativeTo(null); cardLayout = new CardLayout(); @@ -332,52 +464,74 @@ public class MathLearningApp { 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.insets = new Insets(15, 15, 15, 15); gbc.fill = GridBagConstraints.HORIZONTAL; - JLabel titleLabel = new JLabel("用户登录", JLabel.CENTER); - titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 20)); + JLabel titleLabel = new JLabel("数学学习软件", JLabel.CENTER); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24)); + titleLabel.setForeground(new Color(0, 100, 200)); gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2; panel.add(titleLabel, gbc); + JLabel subtitleLabel = new JLabel("用户登录", JLabel.CENTER); + subtitleLabel.setFont(new Font("微软雅黑", Font.PLAIN, 16)); + subtitleLabel.setForeground(Color.GRAY); + gbc.gridy = 1; + panel.add(subtitleLabel, gbc); + gbc.gridwidth = 1; - gbc.gridy = 1; gbc.gridx = 0; - panel.add(new JLabel("邮箱:"), gbc); + gbc.gridy = 2; gbc.gridx = 0; + JLabel usernameLabel = new JLabel("用户名:"); + usernameLabel.setFont(new Font("微软雅黑", Font.BOLD, 14)); + panel.add(usernameLabel, gbc); gbc.gridx = 1; - emailField = new JTextField(20); + emailField = new JTextField(20); // 重命名为usernameField更合适,但为了减少修改量保留原名 + emailField.setFont(new Font("微软雅黑", Font.PLAIN, 14)); panel.add(emailField, gbc); - gbc.gridy = 2; gbc.gridx = 0; - panel.add(new JLabel("密码:"), gbc); + gbc.gridy = 3; gbc.gridx = 0; + JLabel passwordLabel = new JLabel("密码:"); + passwordLabel.setFont(new Font("微软雅黑", Font.BOLD, 14)); + panel.add(passwordLabel, gbc); gbc.gridx = 1; passwordField = new JPasswordField(20); + passwordField.setFont(new Font("微软雅黑", Font.PLAIN, 14)); panel.add(passwordField, gbc); - gbc.gridy = 3; gbc.gridx = 0; gbc.gridwidth = 2; + gbc.gridy = 4; gbc.gridx = 0; gbc.gridwidth = 2; JPanel buttonPanel = new JPanel(new FlowLayout()); - JButton loginButton = new JButton("登录"); + JButton loginButton = createStyledButton("登录", new Color(70, 130, 180)); loginButton.addActionListener(e -> handleLogin()); buttonPanel.add(loginButton); - JButton registerButton = new JButton("注册"); + JButton registerButton = createStyledButton("注册", new Color(60, 179, 113)); registerButton.addActionListener(e -> cardLayout.show(mainPanel, "Register")); buttonPanel.add(registerButton); - JButton changePasswordButton = new JButton("修改密码"); + JButton changePasswordButton = createStyledButton("修改密码", new Color(218, 165, 32)); changePasswordButton.addActionListener(e -> cardLayout.show(mainPanel, "ChangePassword")); buttonPanel.add(changePasswordButton); panel.add(buttonPanel, gbc); + // 添加测试账号提示 + gbc.gridy = 5; + JLabel testAccountLabel = new JLabel("测试账号: 测试用户 / Test123", JLabel.CENTER); + testAccountLabel.setFont(new Font("微软雅黑", Font.ITALIC, 12)); + testAccountLabel.setForeground(Color.GRAY); + panel.add(testAccountLabel, gbc); + mainPanel.add(panel, "Login"); } + private void createRegisterPanel() { JPanel panel = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); @@ -391,38 +545,52 @@ public class MathLearningApp { gbc.gridwidth = 1; gbc.gridy = 1; gbc.gridx = 0; + panel.add(new JLabel("用户名:"), gbc); + + gbc.gridx = 1; + JTextField usernameField = new JTextField(20); + panel.add(usernameField, gbc); + + gbc.gridy = 2; 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("发送验证码"); + gbc.gridy = 3; gbc.gridx = 0; gbc.gridwidth = 2; + JButton sendCodeButton = createStyledButton("发送验证码", new Color(70, 130, 180)); panel.add(sendCodeButton, gbc); gbc.gridwidth = 1; - gbc.gridy = 3; gbc.gridx = 0; + gbc.gridy = 4; 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("注册"); + gbc.gridy = 5; gbc.gridx = 0; gbc.gridwidth = 2; + JButton registerButton = createStyledButton("注册", new Color(60, 179, 113)); registerButton.addActionListener(e -> { + String username = usernameField.getText(); String email = registerEmailField.getText(); String code = registerCodeField.getText(); - if (email.isEmpty() || code.isEmpty()) { - JOptionPane.showMessageDialog(panel, "请填写邮箱和验证码"); + if (username.isEmpty() || email.isEmpty() || code.isEmpty()) { + JOptionPane.showMessageDialog(panel, "请填写所有信息"); + return; + } + + if (userManager.isUsernameRegistered(username)) { + JOptionPane.showMessageDialog(panel, "用户名已被使用"); return; } // 验证验证码(简化处理,只要6位就通过) if (code.length() == 6) { - // 保存邮箱信息用于后续设置密码 + // 保存用户信息用于后续设置密码 + mainPanel.putClientProperty("registerUsername", username); mainPanel.putClientProperty("registerEmail", email); cardLayout.show(mainPanel, "SetPassword"); } else { @@ -431,13 +599,25 @@ public class MathLearningApp { }); panel.add(registerButton, gbc); - gbc.gridy = 5; - JButton backButton = new JButton("返回登录"); + gbc.gridy = 6; + JButton backButton = createStyledButton("返回登录", new Color(169, 169, 169)); backButton.addActionListener(e -> cardLayout.show(mainPanel, "Login")); panel.add(backButton, gbc); sendCodeButton.addActionListener(e -> { + String username = usernameField.getText(); String email = registerEmailField.getText(); + + if (username.isEmpty() || email.isEmpty()) { + JOptionPane.showMessageDialog(panel, "请填写用户名和邮箱"); + return; + } + + if (userManager.isUsernameRegistered(username)) { + JOptionPane.showMessageDialog(panel, "用户名已被使用"); + return; + } + if (isValidEmail(email)) { if (userManager.isEmailRegistered(email)) { JOptionPane.showMessageDialog(panel, "该邮箱已被注册"); @@ -459,71 +639,93 @@ public class MathLearningApp { } 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, "注册失败,用户已存在"); - } + 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); + + // 添加密码要求说明 + JLabel requirementLabel = new JLabel("密码要求:6-10位,包含大小写字母和数字", JLabel.CENTER); + requirementLabel.setFont(new Font("微软雅黑", Font.ITALIC, 12)); + requirementLabel.setForeground(Color.RED); + gbc.gridy = 1; gbc.gridwidth = 2; + panel.add(requirementLabel, gbc); + + gbc.gridwidth = 1; + gbc.gridy = 2; gbc.gridx = 0; + panel.add(new JLabel("密码:"), gbc); + + gbc.gridx = 1; + JPasswordField setPasswordField = new JPasswordField(20); + panel.add(setPasswordField, gbc); + + gbc.gridy = 3; gbc.gridx = 0; + panel.add(new JLabel("确认密码:"), gbc); + + gbc.gridx = 1; + JPasswordField confirmSetPasswordField = new JPasswordField(20); + panel.add(confirmSetPasswordField, gbc); + + gbc.gridy = 4; gbc.gridx = 0; gbc.gridwidth = 2; + JButton setPasswordButton = createStyledButton("设置密码", new Color(60, 179, 113)); + setPasswordButton.addActionListener(e -> { + String password = new String(setPasswordField.getPassword()); + String confirmPassword = new String(confirmSetPasswordField.getPassword()); + String username = (String) mainPanel.getClientProperty("registerUsername"); + String email = (String) mainPanel.getClientProperty("registerEmail"); + + if (validatePassword(password, confirmPassword)) { + if (userManager.registerUser(username, email, password)) { + JOptionPane.showMessageDialog(panel, "注册成功!"); + cardLayout.show(mainPanel, "Login"); + } else { + JOptionPane.showMessageDialog(panel, "注册失败,用户已存在"); } - }); - panel.add(setPasswordButton, gbc); + } + }); + panel.add(setPasswordButton, gbc); + + mainPanel.add(panel, "SetPassword"); +} - 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)); + JPanel panel = new JPanel(new BorderLayout()); + panel.setBorder(BorderFactory.createEmptyBorder(30, 30, 30, 30)); + panel.setBackground(new Color(240, 248, 255)); JLabel titleLabel = new JLabel("选择学习阶段", JLabel.CENTER); - titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 20)); - panel.add(titleLabel); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24)); + titleLabel.setForeground(new Color(0, 100, 200)); + panel.add(titleLabel, BorderLayout.NORTH); + + welcomeLabel = new JLabel("", JLabel.CENTER); + welcomeLabel.setFont(new Font("微软雅黑", Font.PLAIN, 16)); + welcomeLabel.setForeground(Color.DARK_GRAY); + panel.add(welcomeLabel, BorderLayout.CENTER); + + JPanel buttonPanel = new JPanel(new GridLayout(3, 1, 20, 20)); + buttonPanel.setBorder(BorderFactory.createEmptyBorder(20, 50, 20, 50)); + buttonPanel.setBackground(new Color(240, 248, 255)); - JButton primaryButton = new JButton("小学"); + JButton primaryButton = createLevelButton("小学", new Color(135, 206, 250)); primaryButton.addActionListener(e -> startExam("小学")); - panel.add(primaryButton); + buttonPanel.add(primaryButton); - JButton juniorButton = new JButton("初中"); + JButton juniorButton = createLevelButton("初中", new Color(100, 149, 237)); juniorButton.addActionListener(e -> startExam("初中")); - panel.add(juniorButton); + buttonPanel.add(juniorButton); - JButton seniorButton = new JButton("高中"); + JButton seniorButton = createLevelButton("高中", new Color(65, 105, 225)); seniorButton.addActionListener(e -> startExam("高中")); - panel.add(seniorButton); + buttonPanel.add(seniorButton); + + panel.add(buttonPanel, BorderLayout.SOUTH); mainPanel.add(panel, "LevelSelection"); } @@ -531,7 +733,7 @@ public class MathLearningApp { private void createQuestionCountPanel() { JPanel panel = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); - gbc.insets = new Insets(10, 10, 10, 10); + gbc.insets = new Insets(15, 15, 15, 15); JLabel titleLabel = new JLabel("请输入题目数量", JLabel.CENTER); titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 20)); @@ -539,24 +741,32 @@ public class MathLearningApp { panel.add(titleLabel, gbc); JTextField countField = new JTextField(10); + countField.setFont(new Font("微软雅黑", Font.PLAIN, 16)); gbc.gridy = 1; panel.add(countField, gbc); - JButton startButton = new JButton("开始答题"); + JLabel hintLabel = new JLabel("(建议5-20题)", JLabel.CENTER); + hintLabel.setFont(new Font("微软雅黑", Font.ITALIC, 12)); + hintLabel.setForeground(Color.GRAY); + gbc.gridy = 2; + panel.add(hintLabel, gbc); + + JButton startButton = createStyledButton("开始答题", new Color(60, 179, 113)); startButton.addActionListener(e -> { try { int count = Integer.parseInt(countField.getText()); - if (count > 0 && count <= 50) { + // 修改验证范围从 1-50 为 10-30 + if (count >= 10 && count <= 30) { generateExamQuestions(count); cardLayout.show(mainPanel, "Exam"); } else { - JOptionPane.showMessageDialog(panel, "请输入1-50之间的有效题目数量"); + JOptionPane.showMessageDialog(panel, "请输入10-30之间的有效题目数量"); } } catch (NumberFormatException ex) { JOptionPane.showMessageDialog(panel, "请输入有效的数字"); } }); - gbc.gridy = 2; + gbc.gridy = 3; panel.add(startButton, gbc); mainPanel.add(panel, "QuestionCount"); @@ -565,24 +775,31 @@ public class MathLearningApp { private void createExamPanel() { JPanel panel = new JPanel(new BorderLayout()); panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + panel.setBackground(new Color(240, 248, 255)); questionLabel = new JLabel("", JLabel.CENTER); - questionLabel.setFont(new Font("微软雅黑", Font.PLAIN, 16)); + questionLabel.setFont(new Font("微软雅黑", Font.PLAIN, 18)); + questionLabel.setBorder(BorderFactory.createEmptyBorder(10, 10, 20, 10)); panel.add(questionLabel, BorderLayout.NORTH); - JPanel optionsPanel = new JPanel(new GridLayout(4, 1, 10, 10)); + JPanel optionsPanel = new JPanel(new GridLayout(4, 1, 15, 15)); + optionsPanel.setBorder(BorderFactory.createEmptyBorder(20, 50, 20, 50)); + optionsPanel.setBackground(new Color(240, 248, 255)); + optionGroup = new ButtonGroup(); optionButtons = new JRadioButton[4]; for (int i = 0; i < 4; i++) { optionButtons[i] = new JRadioButton(); + optionButtons[i].setFont(new Font("微软雅黑", Font.PLAIN, 16)); + optionButtons[i].setBackground(new Color(240, 248, 255)); optionGroup.add(optionButtons[i]); optionsPanel.add(optionButtons[i]); } panel.add(optionsPanel, BorderLayout.CENTER); - JButton submitButton = new JButton("提交答案"); + JButton submitButton = createStyledButton("提交答案", new Color(70, 130, 180)); submitButton.addActionListener(e -> handleAnswerSubmission()); panel.add(submitButton, BorderLayout.SOUTH); @@ -591,19 +808,26 @@ public class MathLearningApp { private void createScorePanel() { JPanel panel = new JPanel(new BorderLayout()); - panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + panel.setBorder(BorderFactory.createEmptyBorder(40, 40, 40, 40)); + panel.setBackground(new Color(240, 248, 255)); + + JLabel titleLabel = new JLabel("答题完成", JLabel.CENTER); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 28)); + titleLabel.setForeground(new Color(0, 100, 200)); + panel.add(titleLabel, BorderLayout.NORTH); scoreLabel = new JLabel("", JLabel.CENTER); - scoreLabel.setFont(new Font("微软雅黑", Font.BOLD, 24)); + scoreLabel.setFont(new Font("微软雅黑", Font.BOLD, 36)); + scoreLabel.setForeground(new Color(220, 20, 60)); panel.add(scoreLabel, BorderLayout.CENTER); JPanel buttonPanel = new JPanel(new FlowLayout()); - JButton continueButton = new JButton("继续做题"); + JButton continueButton = createStyledButton("继续做题", new Color(60, 179, 113)); continueButton.addActionListener(e -> cardLayout.show(mainPanel, "LevelSelection")); buttonPanel.add(continueButton); - JButton exitButton = new JButton("退出"); + JButton exitButton = createStyledButton("退出登录", new Color(205, 92, 92)); exitButton.addActionListener(e -> { currentUser = null; cardLayout.show(mainPanel, "Login"); @@ -615,6 +839,7 @@ public class MathLearningApp { mainPanel.add(panel, "Score"); } + // 修改createChangePasswordPanel方法中的邮箱字段提示 private void createChangePasswordPanel() { JPanel panel = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); @@ -628,7 +853,7 @@ public class MathLearningApp { gbc.gridwidth = 1; gbc.gridy = 1; gbc.gridx = 0; - panel.add(new JLabel("邮箱:"), gbc); + panel.add(new JLabel("用户名:"), gbc); // 改为用户名 gbc.gridx = 1; JTextField changePasswordEmailField = new JTextField(20); @@ -658,25 +883,26 @@ public class MathLearningApp { gbc.gridy = 5; gbc.gridx = 0; gbc.gridwidth = 2; JPanel buttonPanel = new JPanel(new FlowLayout()); - JButton changeButton = new JButton("修改密码"); + JButton changeButton = createStyledButton("修改密码", new Color(60, 179, 113)); changeButton.addActionListener(e -> { - String email = changePasswordEmailField.getText(); + String username = changePasswordEmailField.getText(); // 改为username 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)) { + // 需要修改UserManager中的updatePassword方法以支持用户名 + if (userManager.updatePasswordByUsername(username, oldPassword, newPassword)) { JOptionPane.showMessageDialog(panel, "密码修改成功"); cardLayout.show(mainPanel, "Login"); } else { - JOptionPane.showMessageDialog(panel, "邮箱或原密码错误"); + JOptionPane.showMessageDialog(panel, "用户名或原密码错误"); } } }); buttonPanel.add(changeButton); - JButton backButton = new JButton("返回"); + JButton backButton = createStyledButton("返回", new Color(169, 169, 169)); backButton.addActionListener(e -> cardLayout.show(mainPanel, "Login")); buttonPanel.add(backButton); @@ -685,25 +911,29 @@ public class MathLearningApp { mainPanel.add(panel, "ChangePassword"); } + // 事件处理方法 + private void handleLogin() { - String email = emailField.getText(); + String username = emailField.getText(); // 这里改为username更语义化 String password = new String(passwordField.getPassword()); - if (email.isEmpty() || password.isEmpty()) { - JOptionPane.showMessageDialog(mainPanel, "请填写邮箱和密码"); + if (username.isEmpty() || password.isEmpty()) { + JOptionPane.showMessageDialog(mainPanel, "请填写用户名和密码"); return; } - currentUser = userManager.login(email, password); + currentUser = userManager.login(username, password); if (currentUser != null) { + welcomeLabel.setText("欢迎, " + currentUser.getUsername() + "!"); JOptionPane.showMessageDialog(mainPanel, "登录成功!"); cardLayout.show(mainPanel, "LevelSelection"); } else { - JOptionPane.showMessageDialog(mainPanel, "邮箱或密码错误"); + JOptionPane.showMessageDialog(mainPanel, "用户名或密码错误"); } } + private void startExam(String level) { // 保存当前选择的级别 mainPanel.putClientProperty("currentLevel", level); @@ -746,13 +976,15 @@ public class MathLearningApp { private void displayCurrentQuestion() { if (currentQuestionIndex < currentExam.size()) { Question question = currentExam.get(currentQuestionIndex); - questionLabel.setText("题目 " + (currentQuestionIndex + 1) + "/" + currentExam.size() + ": " + question.getContent()); + 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); } + optionGroup.clearSelection(); } } @@ -785,12 +1017,82 @@ public class MathLearningApp { } private void showScore() { - double percentage = (double) score / currentExam.size() * 100; - scoreLabel.setText(String.format("得分: %d/%d (%.1f%%)", score, currentExam.size(), percentage)); + // 计算百分制得分 + int totalQuestions = currentExam.size(); + int percentScore = (int) Math.round((double) score / totalQuestions * 100); + + // 根据得分设置不同的颜色和评语 + String comment; + Color color; + if (percentScore >= 90) { + comment = "优秀!"; + color = new Color(0, 128, 0); // 绿色 + } else if (percentScore >= 80) { + comment = "良好!"; + color = new Color(0, 100, 0); // 深绿色 + } else if (percentScore >= 60) { + comment = "及格!"; + color = new Color(218, 165, 32); // 金色 + } else { + comment = "加油!"; + color = new Color(220, 20, 60); // 红色 + } + + scoreLabel.setText("
" + + "得分: " + percentScore + "分
" + + "(" + score + "/" + totalQuestions + ")
" + + "" + comment + "
"); cardLayout.show(mainPanel, "Score"); } // 工具方法 + private JButton createStyledButton(String text, Color color) { + JButton button = new JButton(text); + button.setFont(new Font("微软雅黑", Font.BOLD, 14)); + button.setBackground(color); + button.setForeground(Color.WHITE); + button.setFocusPainted(false); + button.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20)); + button.setCursor(new Cursor(Cursor.HAND_CURSOR)); + + // 添加鼠标悬停效果 + button.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseEntered(java.awt.event.MouseEvent evt) { + button.setBackground(color.darker()); + } + public void mouseExited(java.awt.event.MouseEvent evt) { + button.setBackground(color); + } + }); + + return button; + } + + private JButton createLevelButton(String text, Color color) { + JButton button = new JButton(text); + button.setFont(new Font("微软雅黑", Font.BOLD, 18)); + button.setBackground(color); + button.setForeground(Color.WHITE); + button.setFocusPainted(false); + button.setBorder(BorderFactory.createEmptyBorder(15, 0, 15, 0)); + button.setCursor(new Cursor(Cursor.HAND_CURSOR)); + + button.addMouseListener(new java.awt.event.MouseAdapter() { + public void mouseEntered(java.awt.event.MouseEvent evt) { + button.setBackground(color.darker()); + } + public void mouseExited(java.awt.event.MouseEvent evt) { + button.setBackground(color); + } + }); + + return button; + } + + private String getHexColor(Color color) { + return String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue()); + } + 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); -- 2.34.1 From d6edcc46775cd0a802ff3b96a3abca32c2ea6377 Mon Sep 17 00:00:00 2001 From: ZHW <1941286652@qq.com> Date: Sat, 11 Oct 2025 20:31:42 +0800 Subject: [PATCH 4/5] v4.0 --- src/src/MathLearningApp.java | 547 +++++++++++++++++++++++++++-------- 1 file changed, 423 insertions(+), 124 deletions(-) diff --git a/src/src/MathLearningApp.java b/src/src/MathLearningApp.java index 64c9b95..525a026 100644 --- a/src/src/MathLearningApp.java +++ b/src/src/MathLearningApp.java @@ -8,9 +8,12 @@ import java.util.HashSet; import java.util.Set; import java.util.HashMap; import java.util.Map; +import java.io.*; +import java.nio.file.*; -// 用户类 -class User { +// 用户类 - 实现序列化接口 +class User implements Serializable { + private static final long serialVersionUID = 1L; private String username; private String email; private String password; @@ -30,6 +33,109 @@ class User { public void setVerificationCode(String code) { this.verificationCode = code; } } +// 表达式计算工具类 +class ExpressionCalculator { + + // 计算表达式结果(支持加减乘除和括号) + public static double calculate(String expression) { + try { + // 移除问号和其他非表达式字符 + String cleanExpr = expression.replace(" = ?", "").trim(); + return evaluateExpression(cleanExpr); + } catch (Exception e) { + return 0; + } + } + + // 递归计算表达式 + private static double evaluateExpression(String expr) { + // 处理括号 + while (expr.contains("(")) { + int start = expr.lastIndexOf("("); + int end = expr.indexOf(")", start); + if (end == -1) break; + + String subExpr = expr.substring(start + 1, end); + double subResult = evaluateExpression(subExpr); + expr = expr.substring(0, start) + subResult + expr.substring(end + 1); + } + + return evaluateWithoutParentheses(expr); + } + + // 计算没有括号的表达式(先乘除后加减) + private static double evaluateWithoutParentheses(String expr) { + // 分割表达式为数字和操作符 + List numbers = new ArrayList<>(); + List operators = new ArrayList<>(); + + parseExpression(expr, numbers, operators); + + // 先处理乘除 + for (int i = 0; i < operators.size(); i++) { + char op = operators.get(i); + if (op == '*' || op == '/') { + double left = numbers.get(i); + double right = numbers.get(i + 1); + double result = performOperation(left, right, op); + + numbers.set(i, result); + numbers.remove(i + 1); + operators.remove(i); + i--; // 调整索引 + } + } + + // 再处理加减 + double result = numbers.get(0); + for (int i = 0; i < operators.size(); i++) { + char op = operators.get(i); + double right = numbers.get(i + 1); + result = performOperation(result, right, op); + } + + return result; + } + + // 解析表达式为数字和操作符列表 + private static void parseExpression(String expr, List numbers, List operators) { + StringBuilder currentNumber = new StringBuilder(); + + for (int i = 0; i < expr.length(); i++) { + char c = expr.charAt(i); + + if (c == '+' || c == '-' || c == '*' || c == '/') { + if (currentNumber.length() > 0) { + numbers.add(Double.parseDouble(currentNumber.toString())); + currentNumber.setLength(0); + } + operators.add(c); + } else if (Character.isDigit(c) || c == '.') { + currentNumber.append(c); + } + // 忽略空格和其他字符 + } + + // 添加最后一个数字 + if (currentNumber.length() > 0) { + numbers.add(Double.parseDouble(currentNumber.toString())); + } + } + + // 执行单个运算 + private static double performOperation(double left, double right, char operator) { + switch (operator) { + case '+': return left + right; + case '-': return left - right; + case '*': return left * right; + case '/': + if (right == 0) return 0; // 避免除零 + return left / right; + default: return 0; + } + } +} + // 题目类 class Question { private String content; @@ -57,47 +163,54 @@ abstract class QuestionGenerator { return random.nextInt(100) + 1; } - protected String generateOperator() { - String[] operators = {"+", "-", "*", "/"}; - return operators[random.nextInt(operators.length)]; - } - - protected String[] generateOptions(int correctAnswer) { + protected String[] generateOptionsWithCorrectAnswer(int correctAnswer) { String[] options = new String[4]; - Set usedAnswers = new HashSet<>(); - usedAnswers.add(correctAnswer); + Set usedAnswers = new HashSet<>(); options[0] = String.valueOf(correctAnswer); + usedAnswers.add(options[0]); for (int i = 1; i < 4; i++) { - int wrongAnswer; + String wrongAnswer; do { - // 生成有一定随机性但合理的错误答案 int variation = random.nextInt(20) + 1; - wrongAnswer = correctAnswer + (random.nextBoolean() ? variation : -variation); - if (wrongAnswer < 0) wrongAnswer = -wrongAnswer; + int wrongValue = correctAnswer + (random.nextBoolean() ? variation : -variation); + if (wrongValue < 0) wrongValue = -wrongValue; + wrongAnswer = String.valueOf(wrongValue); } while (usedAnswers.contains(wrongAnswer)); usedAnswers.add(wrongAnswer); - options[i] = String.valueOf(wrongAnswer); + options[i] = wrongAnswer; } - // 随机打乱选项顺序 - List optionList = Arrays.asList(options); + List optionList = new ArrayList<>(Arrays.asList(options)); Collections.shuffle(optionList); return optionList.toArray(new String[0]); } + + protected int findCorrectAnswerIndex(String[] options, String correctAnswer) { + for (int i = 0; i < options.length; i++) { + if (options[i].equals(correctAnswer)) { + return i; + } + } + return 0; + } } // 小学题目生成器 class PrimaryQuestionGenerator extends QuestionGenerator { @Override public Question generateQuestion() { - int operandCount = random.nextInt(2) + 2; // 2-3个操作数 + int operandCount = Math.max(2, random.nextInt(3) + 2); // 2-4个操作数 StringBuilder question = new StringBuilder(); + // 随机决定是否添加括号(只在有3个以上操作数时) boolean hasParentheses = random.nextBoolean() && operandCount >= 3; - int parenthesesPosition = random.nextInt(operandCount - 1); + int parenthesesPosition = 0; + if (hasParentheses && operandCount >= 3) { + parenthesesPosition = random.nextInt(operandCount - 1); + } for (int i = 0; i < operandCount; i++) { if (hasParentheses && i == parenthesesPosition) { @@ -111,44 +224,30 @@ class PrimaryQuestionGenerator extends QuestionGenerator { } if (i < operandCount - 1) { - question.append(" ").append(generateOperator()).append(" "); + question.append(" ").append(generatePrimaryOperator()).append(" "); } } question.append(" = ?"); - // 计算正确答案 - int correctAnswer = calculateSimpleExpression(question.toString()); - String[] options = generateOptions(correctAnswer); + // 使用新的计算器计算正确答案 + double result = ExpressionCalculator.calculate(question.toString()); + int correctAnswer = (int) Math.round(result); + + // 确保答案合理 + if (correctAnswer < 0) correctAnswer = Math.abs(correctAnswer); + if (correctAnswer > 10000) correctAnswer = correctAnswer % 1000; - return new Question(question.toString(), options, Arrays.asList(options).indexOf(String.valueOf(correctAnswer))); + String[] options = generateOptionsWithCorrectAnswer(correctAnswer); + int correctIndex = findCorrectAnswerIndex(options, String.valueOf(correctAnswer)); + + return new Question(question.toString(), options, correctIndex); } - 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; - } + // 小学只使用加减乘,避免复杂除法 + private String generatePrimaryOperator() { + String[] operators = {"+", "-", "*"}; + return operators[random.nextInt(operators.length)]; } } @@ -156,23 +255,58 @@ class PrimaryQuestionGenerator extends QuestionGenerator { 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))); + if (random.nextBoolean()) { + // 平方运算 + String question = num + "² = ?"; + int correctAnswer = num * num; + String[] options = generateOptionsWithCorrectAnswer(correctAnswer); + int correctIndex = findCorrectAnswerIndex(options, String.valueOf(correctAnswer)); + return new Question(question, options, correctIndex); + } else { + // 开根号运算 - 确保是完全平方数 + int squared = num * num; + String question = "√" + squared + " = ?"; + int correctAnswer = num; + String[] options = generateOptionsWithCorrectAnswer(correctAnswer); + int correctIndex = findCorrectAnswerIndex(options, String.valueOf(correctAnswer)); + return new Question(question, options, correctIndex); + } } 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))); + // 混合运算题目 + int operandCount = random.nextInt(3) + 2; // 2-4个操作数 + StringBuilder question = new StringBuilder(); + + for (int i = 0; i < operandCount; i++) { + question.append(generateNumber()); + if (i < operandCount - 1) { + question.append(" ").append(generateJuniorOperator()).append(" "); + } + } + + question.append(" = ?"); + + // 使用新的计算器 + double result = ExpressionCalculator.calculate(question.toString()); + int correctAnswer = (int) Math.round(result); + + // 确保答案合理 + if (Math.abs(correctAnswer) > 10000) { + correctAnswer = correctAnswer % 1000; + } + + String[] options = generateOptionsWithCorrectAnswer(correctAnswer); + int correctIndex = findCorrectAnswerIndex(options, String.valueOf(correctAnswer)); + return new Question(question.toString(), options, correctIndex); } } + + private String generateJuniorOperator() { + String[] operators = {"+", "-", "*", "/"}; + return operators[random.nextInt(operators.length)]; + } } // 高中题目生成器 @@ -181,41 +315,105 @@ class SeniorQuestionGenerator extends QuestionGenerator { @Override public Question generateQuestion() { + if (random.nextBoolean()) { + // 三角函数题目 + return generateTrigQuestion(); + } else { + // 复合表达式题目 + return generateComplexExpression(); + } + } + + private Question generateTrigQuestion() { String trigFunction = trigFunctions[random.nextInt(trigFunctions.length)]; - int angle = random.nextInt(360); + // 使用特殊角度确保结果合理 + int[] specialAngles = {0, 30, 45, 60, 90, 120, 135, 150, 180, 210, 225, 240, 270, 300, 315, 330, 360}; + int angle = specialAngles[random.nextInt(specialAngles.length)]; 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 "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); + // 处理tan函数在90°和270°的无穷大情况 + if (angle % 180 == 90 && angle % 360 != 270) { + result = Double.POSITIVE_INFINITY; + } break; - default: result = 0; + 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))); + // 处理无穷大情况 + if (Double.isInfinite(roundedResult)) { + roundedResult = 1000; // 用一个大的数值表示无穷大 + } + + String[] options = generateTrigOptionsWithCorrectAnswer(roundedResult); + int correctIndex = findCorrectAnswerIndex(options, String.format("%.2f", roundedResult)); + return new Question(question, options, correctIndex); + } + + private Question generateComplexExpression() { + // 生成包含三角函数和基本运算的表达式 + int operandCount = random.nextInt(2) + 2; // 2-3个操作数 + StringBuilder question = new StringBuilder(); + + for (int i = 0; i < operandCount; i++) { + if (random.nextBoolean() && i < operandCount - 1) { + // 添加三角函数 + String trigFunction = trigFunctions[random.nextInt(trigFunctions.length)]; + int[] specialAngles = {0, 30, 45, 60, 90, 180, 270, 360}; + int angle = specialAngles[random.nextInt(specialAngles.length)]; + question.append(trigFunction).append("(").append(angle).append("°)"); + } else { + // 添加普通数字 + question.append(generateNumber()); + } + + if (i < operandCount - 1) { + question.append(" ").append(generateSeniorOperator()).append(" "); + } + } + + question.append(" = ?"); + + // 对于复杂表达式,简化处理 + double result = ExpressionCalculator.calculate(question.toString()); + int correctAnswer = (int) Math.round(result); + + String[] options = generateOptionsWithCorrectAnswer(correctAnswer); + int correctIndex = findCorrectAnswerIndex(options, String.valueOf(correctAnswer)); + return new Question(question.toString(), options, correctIndex); } - private String[] generateTrigOptions(double correctAnswer) { + private String generateSeniorOperator() { + String[] operators = {"+", "-", "*", "/"}; + return operators[random.nextInt(operators.length)]; + } + + private String[] generateTrigOptionsWithCorrectAnswer(double correctAnswer) { String[] options = new String[4]; Set usedAnswers = new HashSet<>(); - usedAnswers.add(String.format("%.2f", correctAnswer)); - options[0] = String.format("%.2f", correctAnswer); + String correctAnswerStr = String.format("%.2f", correctAnswer); + options[0] = correctAnswerStr; + usedAnswers.add(correctAnswerStr); for (int i = 1; i < 4; i++) { String wrongAnswer; do { - double variation = (random.nextDouble() * 2 - 1) * 0.5; // -0.5 到 0.5 的变化 + double variation = (random.nextDouble() * 2 - 1) * 0.5; double wrongValue = correctAnswer + variation; wrongAnswer = String.format("%.2f", wrongValue); } while (usedAnswers.contains(wrongAnswer)); @@ -224,7 +422,7 @@ class SeniorQuestionGenerator extends QuestionGenerator { options[i] = wrongAnswer; } - List optionList = Arrays.asList(options); + List optionList = new ArrayList<>(Arrays.asList(options)); Collections.shuffle(optionList); return optionList.toArray(new String[0]); } @@ -250,13 +448,67 @@ class EmailUtil { class UserManager { private Map usersByEmail; private Map usersByUsername; + private final String USER_DATA_DIR = "user_data"; + private final String USER_DATA_FILE = "users.dat"; public UserManager() { usersByEmail = new HashMap<>(); usersByUsername = new HashMap<>(); - // 添加一些测试用户 - addUser("测试用户", "test@test.com", "Test123"); - addUser("示例用户", "user@example.com", "User123"); + + // 创建用户数据目录 + createUserDataDirectory(); + + // 从文件加载用户数据 + loadUsersFromFile(); + + // 如果没有用户数据,添加一些测试用户 + if (usersByEmail.isEmpty()) { + addUser("测试用户", "test@test.com", "Test123"); + addUser("示例用户", "user@example.com", "User123"); + saveUsersToFile(); + } + } + + private void createUserDataDirectory() { + try { + Files.createDirectories(Paths.get(USER_DATA_DIR)); + } catch (IOException e) { + System.err.println("创建用户数据目录失败: " + e.getMessage()); + } + } + + @SuppressWarnings("unchecked") + private void loadUsersFromFile() { + File userFile = new File(USER_DATA_DIR, USER_DATA_FILE); + if (!userFile.exists()) { + return; + } + + try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(userFile))) { + List userList = (List) ois.readObject(); + for (User user : userList) { + usersByEmail.put(user.getEmail(), user); + usersByUsername.put(user.getUsername(), user); + } + System.out.println("成功加载 " + userList.size() + " 个用户数据"); + } catch (IOException | ClassNotFoundException e) { + System.err.println("加载用户数据失败: " + e.getMessage()); + } + } + + private void saveUsersToFile() { + File userFile = new File(USER_DATA_DIR, USER_DATA_FILE); + try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(userFile))) { + List userList = new ArrayList<>(usersByEmail.values()); + oos.writeObject(userList); + System.out.println("成功保存 " + userList.size() + " 个用户数据"); + } catch (IOException e) { + System.err.println("保存用户数据失败: " + e.getMessage()); + JOptionPane.showMessageDialog(null, + "用户数据保存失败: " + e.getMessage(), + "错误", + JOptionPane.ERROR_MESSAGE); + } } private void addUser(String username, String email, String password) { @@ -265,6 +517,17 @@ class UserManager { usersByUsername.put(username, user); } + // 在UserManager类中添加新的updatePasswordByUsername方法 + public boolean updatePasswordByUsername(String username, String oldPassword, String newPassword) { + User user = usersByUsername.get(username); + if (user != null && user.getPassword().equals(oldPassword)) { + user.setPassword(newPassword); + saveUsersToFile(); // 保存更改到文件 + return true; + } + return false; + } + public boolean registerUser(String username, String email, String password) { if (usersByEmail.containsKey(email) || usersByUsername.containsKey(username)) { return false; @@ -273,11 +536,12 @@ class UserManager { User user = new User(username, email, password); usersByEmail.put(email, user); usersByUsername.put(username, user); + saveUsersToFile(); // 保存新用户到文件 return true; } - public User login(String email, String password) { - User user = usersByEmail.get(email); + public User login(String username, String password) { + User user = usersByUsername.get(username); if (user != null && user.getPassword().equals(password)) { return user; } @@ -288,6 +552,7 @@ class UserManager { User user = usersByEmail.get(email); if (user != null && user.getPassword().equals(oldPassword)) { user.setPassword(newPassword); + saveUsersToFile(); // 保存更改到文件 return true; } return false; @@ -369,9 +634,9 @@ public class MathLearningApp { gbc.gridwidth = 1; gbc.gridy = 2; gbc.gridx = 0; - JLabel emailLabel = new JLabel("邮箱:"); - emailLabel.setFont(new Font("微软雅黑", Font.BOLD, 14)); - panel.add(emailLabel, gbc); + JLabel usernameLabel = new JLabel("用户名:"); + usernameLabel.setFont(new Font("微软雅黑", Font.BOLD, 14)); + panel.add(usernameLabel, gbc); gbc.gridx = 1; emailField = new JTextField(20); @@ -407,7 +672,7 @@ public class MathLearningApp { // 添加测试账号提示 gbc.gridy = 5; - JLabel testAccountLabel = new JLabel("测试账号: test@test.com / Test123", JLabel.CENTER); + JLabel testAccountLabel = new JLabel("测试账号: 测试用户 / Test123", JLabel.CENTER); testAccountLabel.setFont(new Font("微软雅黑", Font.ITALIC, 12)); testAccountLabel.setForeground(Color.GRAY); panel.add(testAccountLabel, gbc); @@ -532,22 +797,29 @@ public class MathLearningApp { gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2; panel.add(titleLabel, gbc); + // 添加密码要求说明 + JLabel requirementLabel = new JLabel("密码要求:6-10位,包含大小写字母和数字", JLabel.CENTER); + requirementLabel.setFont(new Font("微软雅黑", Font.ITALIC, 12)); + requirementLabel.setForeground(Color.RED); + gbc.gridy = 1; gbc.gridwidth = 2; + panel.add(requirementLabel, gbc); + gbc.gridwidth = 1; - gbc.gridy = 1; gbc.gridx = 0; + gbc.gridy = 2; 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; + gbc.gridy = 3; 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; + gbc.gridy = 4; gbc.gridx = 0; gbc.gridwidth = 2; JButton setPasswordButton = createStyledButton("设置密码", new Color(60, 179, 113)); setPasswordButton.addActionListener(e -> { String password = new String(setPasswordField.getPassword()); @@ -620,7 +892,7 @@ public class MathLearningApp { gbc.gridy = 1; panel.add(countField, gbc); - JLabel hintLabel = new JLabel("(建议5-20题)", JLabel.CENTER); + JLabel hintLabel = new JLabel("(建议10-30题)", JLabel.CENTER); hintLabel.setFont(new Font("微软雅黑", Font.ITALIC, 12)); hintLabel.setForeground(Color.GRAY); gbc.gridy = 2; @@ -630,11 +902,11 @@ public class MathLearningApp { startButton.addActionListener(e -> { try { int count = Integer.parseInt(countField.getText()); - if (count > 0 && count <= 50) { + if (count >= 10 && count <= 30) { generateExamQuestions(count); cardLayout.show(mainPanel, "Exam"); } else { - JOptionPane.showMessageDialog(panel, "请输入1-50之间的有效题目数量"); + JOptionPane.showMessageDialog(panel, "请输入10-30之间的有效题目数量"); } } catch (NumberFormatException ex) { JOptionPane.showMessageDialog(panel, "请输入有效的数字"); @@ -726,7 +998,7 @@ public class MathLearningApp { gbc.gridwidth = 1; gbc.gridy = 1; gbc.gridx = 0; - panel.add(new JLabel("邮箱:"), gbc); + panel.add(new JLabel("用户名:"), gbc); gbc.gridx = 1; JTextField changePasswordEmailField = new JTextField(20); @@ -754,57 +1026,66 @@ public class MathLearningApp { panel.add(confirmNewPasswordField, gbc); gbc.gridy = 5; gbc.gridx = 0; gbc.gridwidth = 2; - JPanel buttonPanel = new JPanel(new FlowLayout()); - - JButton changeButton = createStyledButton("修改密码", new Color(60, 179, 113)); - changeButton.addActionListener(e -> { - String email = changePasswordEmailField.getText(); + JButton changePasswordButton = createStyledButton("修改密码", new Color(218, 165, 32)); + changePasswordButton.addActionListener(e -> { + String username = changePasswordEmailField.getText(); String oldPassword = new String(oldPasswordField.getPassword()); String newPassword = new String(newPasswordField.getPassword()); - String confirmPassword = new String(confirmNewPasswordField.getPassword()); + String confirmNewPassword = 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, "邮箱或原密码错误"); - } + if (username.isEmpty() || oldPassword.isEmpty() || newPassword.isEmpty() || confirmNewPassword.isEmpty()) { + JOptionPane.showMessageDialog(panel, "请填写所有信息"); + return; + } + + if (!newPassword.equals(confirmNewPassword)) { + JOptionPane.showMessageDialog(panel, "新密码和确认密码不匹配"); + return; + } + + if (!validatePasswordFormat(newPassword)) { + JOptionPane.showMessageDialog(panel, "密码必须是6-10位,包含大小写字母和数字"); + return; + } + + if (userManager.updatePasswordByUsername(username, oldPassword, newPassword)) { + JOptionPane.showMessageDialog(panel, "密码修改成功"); + cardLayout.show(mainPanel, "Login"); + } else { + JOptionPane.showMessageDialog(panel, "用户名或原密码错误"); } }); - buttonPanel.add(changeButton); + panel.add(changePasswordButton, gbc); - JButton backButton = createStyledButton("返回", new Color(169, 169, 169)); + gbc.gridy = 6; + JButton backButton = createStyledButton("返回登录", new Color(169, 169, 169)); backButton.addActionListener(e -> cardLayout.show(mainPanel, "Login")); - buttonPanel.add(backButton); - - panel.add(buttonPanel, gbc); + panel.add(backButton, gbc); mainPanel.add(panel, "ChangePassword"); } // 事件处理方法 private void handleLogin() { - String email = emailField.getText(); + String username = emailField.getText(); String password = new String(passwordField.getPassword()); - if (email.isEmpty() || password.isEmpty()) { - JOptionPane.showMessageDialog(mainPanel, "请填写邮箱和密码"); + if (username.isEmpty() || password.isEmpty()) { + JOptionPane.showMessageDialog(mainPanel, "请填写用户名和密码"); return; } - currentUser = userManager.login(email, password); + currentUser = userManager.login(username, password); if (currentUser != null) { welcomeLabel.setText("欢迎, " + currentUser.getUsername() + "!"); JOptionPane.showMessageDialog(mainPanel, "登录成功!"); cardLayout.show(mainPanel, "LevelSelection"); } else { - JOptionPane.showMessageDialog(mainPanel, "邮箱或密码错误"); + JOptionPane.showMessageDialog(mainPanel, "用户名或密码错误"); } } private void startExam(String level) { - // 保存当前选择的级别 mainPanel.putClientProperty("currentLevel", level); cardLayout.show(mainPanel, "QuestionCount"); } @@ -839,6 +1120,9 @@ public class MathLearningApp { currentQuestionIndex = 0; score = 0; + + // 开始考试时重置选项状态 + resetOptionButtons(); displayCurrentQuestion(); } @@ -851,7 +1135,22 @@ public class MathLearningApp { String[] options = question.getOptions(); for (int i = 0; i < 4; i++) { optionButtons[i].setText((char)('A' + i) + ". " + options[i]); - optionButtons[i].setSelected(false); + } + + // 重置选项状态 + resetOptionButtons(); + } + } + + private void resetOptionButtons() { + if (optionGroup != null) { + optionGroup.clearSelection(); + } + if (optionButtons != null) { + for (JRadioButton button : optionButtons) { + if (button != null) { + button.setSelected(false); + } } } } @@ -885,25 +1184,23 @@ public class MathLearningApp { } private void showScore() { - // 计算百分制得分 int totalQuestions = currentExam.size(); int percentScore = (int) Math.round((double) score / totalQuestions * 100); - // 根据得分设置不同的颜色和评语 String comment; Color color; if (percentScore >= 90) { comment = "优秀!"; - color = new Color(0, 128, 0); // 绿色 + color = new Color(0, 128, 0); } else if (percentScore >= 80) { comment = "良好!"; - color = new Color(0, 100, 0); // 深绿色 + color = new Color(0, 100, 0); } else if (percentScore >= 60) { comment = "及格!"; - color = new Color(218, 165, 32); // 金色 + color = new Color(218, 165, 32); } else { comment = "加油!"; - color = new Color(220, 20, 60); // 红色 + color = new Color(220, 20, 60); } scoreLabel.setText("
" + @@ -923,7 +1220,6 @@ public class MathLearningApp { button.setBorder(BorderFactory.createEmptyBorder(10, 20, 10, 20)); button.setCursor(new Cursor(Cursor.HAND_CURSOR)); - // 添加鼠标悬停效果 button.addMouseListener(new java.awt.event.MouseAdapter() { public void mouseEntered(java.awt.event.MouseEvent evt) { button.setBackground(color.darker()); @@ -973,6 +1269,10 @@ public class MathLearningApp { return false; } + return validatePasswordFormat(password); + } + + private boolean validatePasswordFormat(String password) { if (password.length() < 6 || password.length() > 10) { JOptionPane.showMessageDialog(mainPanel, "密码长度应为6-10位"); return false; @@ -999,7 +1299,6 @@ public class MathLearningApp { } public static void main(String[] args) { - // 设置界面风格 try { UIManager.setLookAndFeel(UIManager.getLookAndFeel()); } catch (Exception e) { -- 2.34.1 From 7a4920060c75946abebb3b21526bb71a03ab623a Mon Sep 17 00:00:00 2001 From: ZHW <1941286652@qq.com> Date: Sun, 12 Oct 2025 13:10:44 +0800 Subject: [PATCH 5/5] final --- README.md | 2 - doc/README.md | 188 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+), 2 deletions(-) delete mode 100644 README.md create mode 100644 doc/README.md diff --git a/README.md b/README.md deleted file mode 100644 index 8101d5a..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# mathlearningapp - diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..f6bc7fe --- /dev/null +++ b/doc/README.md @@ -0,0 +1,188 @@ +# mathlearningapp + +# mathlearningapp + +# 数学学习软件 - 版本演进文档 + +## 项目概述 + +数学学习软件是一个基于Java Swing开发的桌面应用程序,旨在为不同学习阶段(小学、初中、高中)的学生提供数学题目练习和测试功能。软件采用模块化设计,包含用户管理、题目生成、答题测试等核心功能。 + +## 版本演进历史 + +### 版本1.0 - 基础版本 + +#### 核心功能 +- **用户管理**:邮箱注册、登录、密码修改 +- **题目生成**:小学、初中、高中三个级别的题目生成器 +- **答题系统**:选择题形式,支持自定义题目数量 +- **成绩统计**:显示得分和正确率 + +#### 技术特点 +- 使用CardLayout实现多面板切换 +- 基础的用户验证系统 +- 简单的题目生成算法 + +#### 主要类结构 +``` +User - 用户类 +Question - 题目类 +QuestionGenerator - 题目生成器抽象类 +PrimaryQuestionGenerator - 小学题目生成器 +JuniorQuestionGenerator - 初中题目生成器 +SeniorQuestionGenerator - 高中题目生成器 +UserManager - 用户管理类 +MathLearningApp - 主应用程序类 +``` + +### 版本2.0 - 功能增强版 + +#### 新增功能 +- **用户名系统**:引入用户名概念,不再仅依赖邮箱 +- **界面美化**:改进UI设计,添加颜色和样式 +- **验证码系统**:注册时发送验证码 +- **欢迎界面**:登录后显示个性化欢迎信息 + +#### 技术改进 +- 改进的用户管理,支持用户名和邮箱双重索引 +- 增强的题目生成逻辑 +- 更友好的用户界面 + +### 版本3.0 - 算法优化版 + +#### 核心改进 +- **题目生成优化**: + - 小学:操作数数量扩展为1-5个 + - 初中:支持混合运算和平方/开方运算 + - 高中:三角函数使用特殊角度,确保结果合理 +- **表达式计算**:引入JavaScript引擎计算复杂表达式 +- **输入验证**:题目数量限制为10-30题 + +#### 技术升级 +- 更健壮的题目生成算法 +- 改进的错误处理机制 +- 优化的用户交互体验 + +### 版本4.0 - 企业级版本 + +#### 重大改进 +- **数据持久化**:用户数据序列化保存到文件 +- **表达式计算器**:自定义表达式计算引擎 +- **包管理**:添加com.hnu包结构 +- **代码重构**:大幅优化代码结构和算法 + +#### 新增特性 +- **文件存储**:用户数据自动保存和加载 +- **高级计算**:支持括号和运算优先级 +- **健壮性提升**:完善的异常处理和输入验证 + +## 核心功能详解 + +### 1. 用户管理系统 + +#### 版本演进 +- **1.0**:基于邮箱的简单用户管理 +- **2.0**:引入用户名系统,双重索引 +- **4.0**:数据持久化,文件存储 + +#### 功能特性 +- 用户注册(邮箱验证) +- 用户登录 +- 密码修改 +- 数据持久化(4.0+) + +### 2. 题目生成系统 + +#### 小学题目 +- **运算符**:+、-、*、/ +- **特点**:支持括号,2-4个操作数 +- **演进**:从简单运算到支持复杂表达式 + +#### 初中题目 +- **类型**:基本运算、平方、开方 +- **特点**:混合运算,确保完全平方数 +- **演进**:算法优化,结果更合理 + +#### 高中题目 +- **类型**:三角函数、复合表达式 +- **特点**:使用特殊角度,避免极端值 +- **演进**:从简单三角函数到复合表达式 + +### 3. 答题与评分系统 + +#### 答题流程 +1. 选择学习阶段 +2. 输入题目数量 +3. 逐题作答 +4. 查看成绩 + +#### 评分机制 +- 实时计分 +- 百分比显示 +- 成绩评级(优秀、良好、及格、加油) + +## 技术架构 + +### 设计模式应用 +- **工厂模式**:题目生成器 +- **单例模式**:用户管理 +- **策略模式**:不同级别的题目生成 + +### 核心算法 + +#### 表达式计算(版本4.0) +```java +// 支持括号和运算优先级 +public static double calculate(String expression) { + // 递归处理括号 + // 先乘除后加减 + // 完善的错误处理 +} +``` + +#### 题目生成 +```java +// 确保题目多样性和合理性 +public Question generateQuestion() { + // 操作数数量随机 + // 运算符随机选择 + // 错误答案生成算法 + // 选项随机排序 +} +``` + +## 部署与使用 + +### 环境要求 +- Java 8+ +- 支持Swing的Java环境 + +### 运行方式 +```bash +javac MathLearningApp.java +java MathLearningApp +``` + +### 测试账户 +- 用户名:测试用户 +- 密码:Test123 + +## 版本对比总结 + +| 特性 | 1.0 | 2.0 | 3.0 | 4.0 | +|------|-----|-----|-----|-----| +| 用户管理 | 基础 | 增强 | 增强 | 持久化 | +| 题目生成 | 简单 | 改进 | 优化 | 高级 | +| 界面设计 | 基础 | 美化 | 优化 | 专业 | +| 数据存储 | 内存 | 内存 | 内存 | 文件 | +| 计算能力 | 基础 | 基础 | 引擎 | 自定义 | +| 代码结构 | 简单 | 模块化 | 优化 | 企业级 | + +## 未来发展建议 + +1. **数据库集成**:替换文件存储为数据库 +2. **网络功能**:添加在线排名和题目分享 +3. **移动端**:开发Android和iOS版本 +4. **AI辅助**:集成智能题目推荐 +5. **多媒体**:添加题目解析视频功能 + -- 2.34.1