diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..359bb53 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..4f2c764 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +UserService.java \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..aa00ffa --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 2bfdeda..0944a8f 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,14 @@ - + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 8306744..94a25f7 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,7 +1,6 @@ - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 1288078..72cf962 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -1,8 +1,30 @@ + + - - + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/users.json b/data/users.json new file mode 100644 index 0000000..721399b --- /dev/null +++ b/data/users.json @@ -0,0 +1,17 @@ +[ { + "username" : "LXR", + "email" : "2759462569@qq.com", + "passwordHash" : "$2a$12$AiupZKqpZMXvstX6XYNGUuBDZOMdzqXy/ADaYzurUYatM293Lbxii", + "registrationDate" : [ 2025, 10, 11, 19, 40, 59, 177105400 ], + "verificationCode" : "371978", + "verified" : true, + "type" : "高中" +}, { + "username" : "小鱼", + "email" : "1280556515@qq.com", + "passwordHash" : "$2a$12$dbNwBK6NBj7mXU6YzNMAweTMhD9NOxsjPGzW2SfIM.QvGdWt7Lyvy", + "registrationDate" : [ 2025, 10, 10, 11, 7, 5, 853200500 ], + "verificationCode" : "688201", + "verified" : true, + "type" : "高中" +} ] \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..6f1c887 --- /dev/null +++ b/pom.xml @@ -0,0 +1,50 @@ + + + 4.0.0 + + com.mathlearning + math-learning-app + 1.0.0 + + + 11 + 11 + UTF-8 + + + + + + com.sun.mail + javax.mail + 1.6.2 + + + + + com.fasterxml.jackson.core + jackson-core + 2.15.2 + + + com.fasterxml.jackson.core + jackson-databind + 2.15.2 + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + 2.15.2 + + + + + at.favre.lib + bcrypt + 0.9.0 + + + \ No newline at end of file diff --git a/src/main/java/mathlearning/model/User.java b/src/main/java/mathlearning/model/User.java index 6ee96a3..cbf61cd 100644 --- a/src/main/java/mathlearning/model/User.java +++ b/src/main/java/mathlearning/model/User.java @@ -13,7 +13,7 @@ public class User { private boolean verified; private String type; - public User() {} + public User( ) {} public User(String email, String passwordHash, String verificationCode) { this.email = email; diff --git a/src/main/java/mathlearning/service/MultipleChoiceGenerator/MultipleChoiceGenerator.java b/src/main/java/mathlearning/service/MultipleChoiceGenerator/MultipleChoiceGenerator.java index 3a7ba1a..f844f1a 100644 --- a/src/main/java/mathlearning/service/MultipleChoiceGenerator/MultipleChoiceGenerator.java +++ b/src/main/java/mathlearning/service/MultipleChoiceGenerator/MultipleChoiceGenerator.java @@ -14,7 +14,7 @@ public class MultipleChoiceGenerator { public MultipleChoiceGenerator(int count, User nowUser){// 如此声明MultipleChoiceGenerator实例,再调用下面三个接口 this.QuestionList = new String[count]; this.ChoiceList = new String[count]; - this.CorrectAnswerNo = new String[count]; + this.CorrectAnswerNo = new String[count]; this.QuestionList = SetQuestionList(count, nowUser); SetChoiceList(); } @@ -98,4 +98,4 @@ public class MultipleChoiceGenerator { -} +} \ No newline at end of file diff --git a/src/main/java/mathlearning/service/UserService.java b/src/main/java/mathlearning/service/UserService.java index f9c0c95..930e3b1 100644 --- a/src/main/java/mathlearning/service/UserService.java +++ b/src/main/java/mathlearning/service/UserService.java @@ -123,28 +123,32 @@ public class UserService { return result.verified; } - public boolean changePassword(String email, String oldPassword, String newPassword) { + public int changePassword(String email, String oldPassword, String newPassword) { User user = users.get(email); if (user == null) { - return false; + return 1; } // 验证旧密码 BCrypt.Result result = BCrypt.verifyer().verify(oldPassword.toCharArray(), user.getPasswordHash()); if (!result.verified) { - return false; + return 2; } // 验证新密码格式 if (!validatePassword(newPassword)) { - return false; + return 3; + } + + if (oldPassword.equals(newPassword)) { + return 4; } // 更新密码 String newPasswordHash = BCrypt.withDefaults().hashToString(12, newPassword.toCharArray()); user.setPasswordHash(newPasswordHash); saveUsers(); - return true; + return 5; } public boolean setPasswordResetCode(String email, String verificationCode) { @@ -226,6 +230,16 @@ public class UserService { return user != null && user.isVerified(); } + public boolean updateUsername(String email, String newUsername) { + User user = users.get(email); + if (user == null) { + return false; + } + user.setUsername(newUsername); + saveUsers(); + return true; + } + public boolean isValidEmail(String email) { return email.matches("^[A-Za-z0-9+_.-]+@(.+)$"); } diff --git a/src/main/java/mathlearning/ui/ChangeCodeFrame.java b/src/main/java/mathlearning/ui/ChangeCodeFrame.java index 1255136..37ad12b 100644 --- a/src/main/java/mathlearning/ui/ChangeCodeFrame.java +++ b/src/main/java/mathlearning/ui/ChangeCodeFrame.java @@ -36,12 +36,19 @@ public class ChangeCodeFrame extends JFrame { true ); + addWindowListener(new java.awt.event.WindowAdapter() { + @Override + public void windowClosing(java.awt.event.WindowEvent windowEvent) { + loginFrame.setVisible(true); + } + }); + initializeUI(); } private void initializeUI() { setTitle("数学学习软件 - 忘记密码"); - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); setSize(450, 400); setLocationRelativeTo(null); setResizable(false); diff --git a/src/main/java/mathlearning/ui/ChangePasswordFrame.java b/src/main/java/mathlearning/ui/ChangePasswordFrame.java new file mode 100644 index 0000000..96848fe --- /dev/null +++ b/src/main/java/mathlearning/ui/ChangePasswordFrame.java @@ -0,0 +1,120 @@ +package mathlearning.ui; + +import mathlearning.model.User; +import mathlearning.service.UserService; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class ChangePasswordFrame extends JFrame { + private User user; + private UserService userService; + private ProfileFrame profileFrame; + private JPasswordField oldPasswordField; + private JPasswordField newPasswordField; + private JPasswordField confirmPasswordField; + + public ChangePasswordFrame(User user, UserService userService, ProfileFrame profileFrame) { + this.user = user; + this.userService = userService; + this.profileFrame = profileFrame; + addWindowListener(new java.awt.event.WindowAdapter() { + @Override + public void windowClosing(java.awt.event.WindowEvent windowEvent) { + profileFrame.setVisible(true); + } + }); + InitUI(); + } + + private void InitUI() { + setTitle("修改密码"); + setSize(400, 300); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + setLocationRelativeTo(null); + setResizable(false); + + // 创建主面板 + JPanel mainPanel = new JPanel(new BorderLayout(10, 10)); + mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + + // 标题 + JLabel titleLabel = new JLabel("修改密码", JLabel.CENTER); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24)); + mainPanel.add(titleLabel, BorderLayout.NORTH); + + //表单 + JPanel infoPanel = new JPanel(new GridLayout(4, 2, 10, 10)); + JLabel oldPasswordLabel = new JLabel("请输入旧密码:"); + oldPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + oldPasswordField = new JPasswordField(); + infoPanel.add(oldPasswordLabel); + infoPanel.add(oldPasswordField); + JLabel newPasswordLabel = new JLabel("请输入新密码:"); + newPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + newPasswordField = new JPasswordField(); + infoPanel.add(newPasswordLabel); + infoPanel.add(newPasswordField); + JLabel confirmPasswordLabel = new JLabel("请再次输入新密码:"); + confirmPasswordLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + confirmPasswordField = new JPasswordField(); + infoPanel.add(confirmPasswordLabel); + infoPanel.add(confirmPasswordField); + mainPanel.add(infoPanel, BorderLayout.CENTER); + + //按钮 + JPanel buttonPanel = new JPanel(new FlowLayout()); + JButton changeButton = new JButton("修改"); + changeButton.addActionListener(new ChangeButtonListener()); + JButton cancelButton = new JButton("取消并返回"); + cancelButton.addActionListener(e -> returnToProfileFrame()); + + buttonPanel.add(changeButton); + buttonPanel.add(cancelButton); + + mainPanel.add(buttonPanel, BorderLayout.SOUTH); + + add(mainPanel); + } + + private void returnToProfileFrame() { + profileFrame.setVisible(true); + dispose(); + } + + private class ChangeButtonListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + String oldPassword = new String(oldPasswordField.getPassword()); + String newPassword = new String(newPasswordField.getPassword()); + String confirmPassword = new String(confirmPasswordField.getPassword()); + + if (!newPassword.equals(confirmPassword)) { + JOptionPane.showMessageDialog(ChangePasswordFrame.this, "两次输入的密码不相同!", "错误", JOptionPane.ERROR_MESSAGE ); + return; + } + + int changed = userService.changePassword(user.getEmail(), oldPassword, newPassword); + + if (changed == 1) { + JOptionPane.showMessageDialog(ChangePasswordFrame.this, "修改失败!用户账户异常!", "错误", JOptionPane.ERROR_MESSAGE); + return; + } + else if (changed == 2) { + JOptionPane.showMessageDialog(ChangePasswordFrame.this, "修改失败!旧密码输入有误!", "错误", JOptionPane.ERROR_MESSAGE); + return; + } else if (changed == 3) { + JOptionPane.showMessageDialog(ChangePasswordFrame.this, "修改失败!新密码的格式有误!密码必须为6-10位,且包含大小写字母和数字", "错误", JOptionPane.ERROR_MESSAGE); + return; + } else if (changed == 4) { + JOptionPane.showMessageDialog(ChangePasswordFrame.this, "修改失败!旧密码与新密码一致!", "错误", JOptionPane.ERROR_MESSAGE); + return; + } else { + JOptionPane.showMessageDialog(ChangePasswordFrame.this, "修改成功!", "成功", JOptionPane.INFORMATION_MESSAGE); + } + returnToProfileFrame(); + } + } +} diff --git a/src/main/java/mathlearning/ui/LoginFrame.java b/src/main/java/mathlearning/ui/LoginFrame.java index b6d00b0..b2f5a96 100644 --- a/src/main/java/mathlearning/ui/LoginFrame.java +++ b/src/main/java/mathlearning/ui/LoginFrame.java @@ -157,6 +157,12 @@ public class LoginFrame extends JFrame{ "登录成功", JOptionPane.INFORMATION_MESSAGE); } - // openMainApplicationWindow(user); + openMainApplicationWindow(user, userService); + } + + private void openMainApplicationWindow(User user, UserService userService) { + MainFrame mainFrame = new MainFrame(user, userService); + mainFrame.setVisible(true); + dispose(); } } diff --git a/src/main/java/mathlearning/ui/MainFrame.java b/src/main/java/mathlearning/ui/MainFrame.java index abf8743..18b6a0f 100644 --- a/src/main/java/mathlearning/ui/MainFrame.java +++ b/src/main/java/mathlearning/ui/MainFrame.java @@ -1,5 +1,6 @@ package mathlearning.ui; +import com.sun.tools.javac.Main; import mathlearning.model.User; import mathlearning.service.UserService; @@ -12,6 +13,9 @@ public class MainFrame extends JFrame { private User currentUser; private UserService userService; private JLabel welcomeLabel; + private JLabel typeLabel; + private JButton changeTypeButton; + private JPanel mainPanel; public MainFrame(User user, UserService userService) { this.currentUser = user; @@ -28,14 +32,14 @@ public class MainFrame extends JFrame { // 主面板 JPanel titlePanel = new JPanel(new BorderLayout()); - JPanel mainPanel = new JPanel(new BorderLayout(10, 10)); + mainPanel = new JPanel(new BorderLayout(10, 10)); mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); // 欢迎用户与type信息 welcomeLabel = new JLabel("欢迎"+currentUser.getUsername()+"!", JLabel.CENTER); welcomeLabel.setFont(new Font("微软雅黑", Font.BOLD, 24)); - JLabel typeLabel = new JLabel("当前选择的测试题为" + currentUser.getType() + "难度", JLabel.CENTER); + typeLabel = new JLabel("当前选择的测试题为" + currentUser.getType() + "难度", JLabel.CENTER); typeLabel.setFont(new Font("微软雅黑", Font.BOLD, 16)); typeLabel.setForeground(Color.BLUE); @@ -46,8 +50,126 @@ public class MainFrame extends JFrame { // 功能按钮区域 JPanel centerButtonPanel = new JPanel(new GridLayout(2, 1, 15, 15)); centerButtonPanel.setBorder(BorderFactory.createEmptyBorder(30, 50, 30, 50)); + // 切换教育阶段按钮 + changeTypeButton = new JButton("切换教育阶段"); + changeTypeButton.setFont(new Font("微软雅黑", Font.BOLD, 18)); + changeTypeButton.setBackground(new Color(70, 130, 180)); + changeTypeButton.setForeground(Color.WHITE); + changeTypeButton.setPreferredSize(new Dimension(200, 60)); + changeTypeButton.addActionListener(new ChangeTypeListener()); + // 自我测试按钮 + JButton selfTestButton = new JButton("自我测试"); + selfTestButton.setFont(new Font("微软雅黑", Font.BOLD, 18)); + selfTestButton.setBackground(new Color(34, 139, 34)); + selfTestButton.setForeground(Color.WHITE); + selfTestButton.setPreferredSize(new Dimension(200, 60)); + selfTestButton.addActionListener(e -> openSelfTestFrame()); + centerButtonPanel.add(changeTypeButton); + centerButtonPanel.add(selfTestButton); + mainPanel.add(centerButtonPanel, BorderLayout.CENTER); + // 底部按钮面板 - 两个较小按钮 + JPanel bottomButtonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 20, 10)); + + // 退出登录按钮 + JButton logoutButton = new JButton("退出登录"); + logoutButton.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + logoutButton.setBackground(Color.CYAN); + logoutButton.setForeground(Color.WHITE); + logoutButton.addActionListener(new LogoutListener()); + + // 个人资料按钮 + JButton profileButton = new JButton("个人资料"); + profileButton.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + profileButton.setBackground(new Color(100, 149, 237)); + profileButton.setForeground(Color.WHITE); + profileButton.addActionListener(new ProfileListener()); + + bottomButtonPanel.add(logoutButton); + bottomButtonPanel.add(profileButton); + mainPanel.add(bottomButtonPanel, BorderLayout.SOUTH); + + add(mainPanel); + } + + private class ChangeTypeListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + String[] types = {"小学", "初中", "高中" }; + String selectedType = (String) JOptionPane.showInputDialog(MainFrame.this, "请选择需要切换的难度:", "选择难度", JOptionPane.QUESTION_MESSAGE, null, types, currentUser.getType()); + if (selectedType != null) { + boolean updated = userService.updateUserType(currentUser.getEmail(), selectedType); + + if (updated) { + currentUser.setType(selectedType); + JOptionPane.showMessageDialog(MainFrame.this, "当前难度已更改为" + currentUser.getType() , "切换成功", JOptionPane.INFORMATION_MESSAGE); + updateTypeDisplay(); + } else { + JOptionPane.showMessageDialog(MainFrame.this, "切换失败", "错误", JOptionPane.ERROR_MESSAGE); + } + } + } + } + + private void updateTypeDisplay() { + typeLabel.setText("当前选择的测试题为" + currentUser.getType() + "难度"); + } + + private class LogoutListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + int result = JOptionPane.showConfirmDialog(MainFrame.this, "确定要退出登陆吗?", "退出登录", JOptionPane.YES_NO_OPTION); + + if (result == JOptionPane.YES_OPTION) {// 为按钮添加边框和圆角效果 + changeTypeButton.setBorder(BorderFactory.createCompoundBorder( + BorderFactory.createLineBorder(new Color(50, 100, 150), 1), + BorderFactory.createEmptyBorder(10, 20, 10, 20))); + changeTypeButton.setFocusPainted(false); // 去除焦点边框 + + // 为主面板添加渐变背景 + mainPanel.setBackground(new Color(245, 245, 245)); + + LoginFrame loginFrame = new LoginFrame(userService); + loginFrame.setVisible(true); + dispose(); + } + } + } + + private class ProfileListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + ProfileFrame profileFrame = new ProfileFrame(currentUser, userService); + profileFrame.setVisible(true); + dispose(); + } + } + + private void openSelfTestFrame() { + String input = JOptionPane.showInputDialog(MainFrame.this, "请输入题目数量:(10-30)", "题目数量", JOptionPane.QUESTION_MESSAGE); + if (input == null) { + return; + } + + try { + int num = Integer.parseInt(input.trim()); + if (num < 10 || num > 30) { + JOptionPane.showMessageDialog(MainFrame.this, "题目数量应该在10-30之间!", "错误", JOptionPane.ERROR_MESSAGE); + return; + } + SelTestFrame selTestFrame = new SelTestFrame(this, currentUser, userService, num); + selTestFrame.setVisible(true); + this.setVisible(false); + + } catch (NumberFormatException e) { + JOptionPane.showMessageDialog(MainFrame.this, "请输入数字!", "错误", JOptionPane.ERROR_MESSAGE); + } + + } + + public void reTryTest() { + openSelfTestFrame(); } } diff --git a/src/main/java/mathlearning/ui/ProfileFrame.java b/src/main/java/mathlearning/ui/ProfileFrame.java new file mode 100644 index 0000000..8f2b3cf --- /dev/null +++ b/src/main/java/mathlearning/ui/ProfileFrame.java @@ -0,0 +1,130 @@ +package mathlearning.ui; + +import mathlearning.model.User; +import mathlearning.service.UserService; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class ProfileFrame extends JFrame { + private User user; + private UserService userService; + private JPanel mainPanel; + private JLabel usernameValue; + + public ProfileFrame(User user, UserService userService) { + this.user = user; + this.userService = userService; + addWindowListener(new java.awt.event.WindowAdapter() { + @Override + public void windowClosing(java.awt.event.WindowEvent windowEvent) { + MainFrame mainFrame = new MainFrame(user, userService); + mainFrame.setVisible(true); + } + }); + InitUI(); + } + + private void InitUI() { + setTitle("个人资料"); + setSize(400, 300); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + setLocationRelativeTo(null); + setResizable(false); + + // 创建主面板 + mainPanel = new JPanel(new BorderLayout(10, 10)); + mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + + //标题 + JLabel titleLabel = new JLabel("个人资料", JLabel.CENTER); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24)); + mainPanel.add(titleLabel, BorderLayout.NORTH); + + //个人信息(表单) + JPanel infoPanel = new JPanel(new GridLayout(3, 2, 10, 10)); + JLabel usernameLabel = new JLabel("用户名:"); + usernameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + usernameValue = new JLabel(user.getUsername()); + usernameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + infoPanel.add(usernameLabel); + infoPanel.add(usernameValue); + JLabel mailLabel = new JLabel("QQ邮箱:"); + mailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + JLabel mailValue = new JLabel(user.getEmail()); + infoPanel.add(mailLabel); + infoPanel.add(mailValue); + mainPanel.add(infoPanel, BorderLayout.CENTER); + + //三个按钮 + JPanel buttonPanel = new JPanel(new FlowLayout()); + JButton returnToMainButton = new JButton("返回"); + returnToMainButton.addActionListener(new returnToMainButtonListener()); + + JButton changePasswordButton = new JButton("修改密码"); + changePasswordButton.addActionListener(e -> openChangePasswordFrame()); + + JButton changeUsernameButton = new JButton("更改用户名"); + changeUsernameButton.addActionListener(new ChangeUsernameButtonListener()); + + buttonPanel.add(returnToMainButton); + buttonPanel.add(changePasswordButton); + buttonPanel.add(changeUsernameButton); + + mainPanel.add(buttonPanel, BorderLayout.SOUTH); + + add(mainPanel); + } + + private class returnToMainButtonListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + MainFrame mainFrame = new MainFrame(user, userService); + mainFrame.setVisible(true); + dispose(); + } + } + + private void openChangePasswordFrame() { + ChangePasswordFrame changePasswordFrame = new ChangePasswordFrame(user, userService, this); + changePasswordFrame.setVisible(true); + this.setVisible(false); + } + + private class ChangeUsernameButtonListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent e ) { + String newUsername = JOptionPane.showInputDialog(ProfileFrame.this, "请输入您的新用户名:", "修改用户名", JOptionPane.QUESTION_MESSAGE); + + if (newUsername == null ) { + return; + } + + newUsername = newUsername.trim(); + if (newUsername.isEmpty()) { + JOptionPane.showMessageDialog( ProfileFrame.this, "用户名不能为空!", "错误", JOptionPane.ERROR_MESSAGE); + return; + } + if (newUsername.equals(user.getUsername())) { + JOptionPane.showMessageDialog( ProfileFrame.this, "新用户名与原用户名相同!", "提示", JOptionPane.ERROR_MESSAGE); + return; + } + int confirm = JOptionPane.showConfirmDialog(ProfileFrame.this,"确定要修改用户名为\"" + newUsername + "\"吗?", "修改用户名", JOptionPane.YES_NO_OPTION); + if (confirm == JOptionPane.YES_OPTION) { + String oldUsername = user.getUsername(); + + boolean updated = userService.updateUsername(user.getEmail(), newUsername); + if (updated) { + usernameValue.setText(newUsername); + JOptionPane.showMessageDialog(ProfileFrame.this, "修改成功!", "成功", JOptionPane.INFORMATION_MESSAGE); + } + else { + user.setUsername(oldUsername); + JOptionPane.showMessageDialog(ProfileFrame.this, "修改失败!", "错误", JOptionPane.ERROR_MESSAGE); + } + } + } + } +} diff --git a/src/main/java/mathlearning/ui/RegisterFrame.java b/src/main/java/mathlearning/ui/RegisterFrame.java index b5fc993..fb0f5a9 100644 --- a/src/main/java/mathlearning/ui/RegisterFrame.java +++ b/src/main/java/mathlearning/ui/RegisterFrame.java @@ -37,12 +37,19 @@ public class RegisterFrame extends JFrame{ true ); + addWindowListener(new java.awt.event.WindowAdapter() { + @Override + public void windowClosing(java.awt.event.WindowEvent windowEvent) { + loginFrame.setVisible(true); + } + }); + initializeUI(); } private void initializeUI() { setTitle("数学学习软件 - 注册"); - setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); setSize(450, 400); setLocationRelativeTo(null); setResizable(false); diff --git a/src/main/java/mathlearning/ui/SelTestFrame.java b/src/main/java/mathlearning/ui/SelTestFrame.java new file mode 100644 index 0000000..327e3d7 --- /dev/null +++ b/src/main/java/mathlearning/ui/SelTestFrame.java @@ -0,0 +1,324 @@ +package mathlearning.ui; + +import mathlearning.model.User; +import mathlearning.service.MultipleChoiceGenerator.MultipleChoiceGenerator; +import mathlearning.service.UserService; + +import javax.swing.*; +import java.awt.*; + +public class SelTestFrame extends JFrame { + private MainFrame mainFrame; + private User user; + private UserService userService; + private MultipleChoiceGenerator multipleChoiceGenerator; + + private int questionNum; + private int currentQuestionDex; + private String[] answers; + private String[] myAnswers; + + private JLabel titleLabel; + private JButton optionA; + private JButton optionB; + private JButton optionC; + private JButton optionD; + private JButton prevButton; + private JButton nextButton; + private JButton submitButton; + private JLabel question; + private JLabel choice; + + public SelTestFrame(MainFrame mainFrame, User user, UserService userService, int num) { + this.mainFrame = mainFrame; + this.user = user; + this.userService = userService; + this.questionNum = num; + this.currentQuestionDex = 0; + this.myAnswers = new String[questionNum]; + this.multipleChoiceGenerator = new MultipleChoiceGenerator(num, user); + this.answers = multipleChoiceGenerator.GetCorrectAnswerNo(); + InitUI(); + addWindowListener(new java.awt.event.WindowAdapter() { + @Override + public void windowClosing(java.awt.event.WindowEvent windowEvent) { + handleCloseOperation(); + } + }); + } + + private void InitUI() { + setTitle("答题界面"); + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + setSize(800, 600); + setLocationRelativeTo(null); + + //主面板 + JPanel mainPanel = new JPanel(new BorderLayout()); + + //上 + titleLabel = new JLabel("当前为:第" + (currentQuestionDex + 1) + "题"); + + //左侧面板 + JPanel leftPanel = createLeftPanel(); + + //中 + JPanel centerPanel = centerPanel(); + + //右 + JPanel rightPanel = createRightPanel(); + + //底 + JPanel bottomPanel = createButtonPanel(); + + mainPanel.add(titleLabel, BorderLayout.NORTH); + mainPanel.add(leftPanel, BorderLayout.WEST); + mainPanel.add(centerPanel, BorderLayout.CENTER); + mainPanel.add(rightPanel, BorderLayout.EAST); + mainPanel.add(bottomPanel, BorderLayout.SOUTH); + + add(mainPanel); + + updateNavigationButtons(); + } + + private JPanel createLeftPanel() { + JPanel panel = new JPanel(new BorderLayout()); + + JLabel usernameLabel = new JLabel("用户:" + user.getUsername()); + usernameLabel.setFont(new Font("微软雅黑", Font.BOLD, 16)); + usernameLabel.setForeground(Color.BLUE); + panel.add(usernameLabel, BorderLayout.NORTH); + + JList questionList = new JList<>(); + String[] questions = new String[questionNum + 1]; + for (int i = 0; i < questionNum; i++) { + questions[i] = "题目" + (i + 1); + } + questionList.setListData(questions); + panel.add(questionList, BorderLayout.CENTER); + + return panel; + } + + private JPanel centerPanel() { + JPanel panel = new JPanel(new GridLayout(6, 1)); + + JLabel questionLabel = new JLabel("题目:"); + questionLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + question = new JLabel(multipleChoiceGenerator.GetQuestionList()[currentQuestionDex]); + question.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + panel.add(questionLabel); + panel.add(question); + + JLabel choiceLabel = new JLabel("选项:"); + choiceLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14)); + choice = new JLabel(multipleChoiceGenerator.GetChoiceList()[currentQuestionDex]); + choiceLabel.setFont(new Font("微软雅黑", Font.PLAIN, 16)); + panel.add(choiceLabel); + panel.add(choice); + + return panel; + } + + private JPanel createRightPanel() { + JPanel panel = new JPanel(new BorderLayout()); + + JLabel titleLabel = new JLabel("作答区域:"); + titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 16)); + panel.add(titleLabel, BorderLayout.NORTH); + + JPanel buttonPanel = new JPanel(new GridLayout(4, 1)); + + optionA = new JButton("A"); + optionA.setBackground(Color.LIGHT_GRAY); + optionA.setForeground(Color.BLACK); + optionA.addActionListener(e -> saveAnswer("A")); + optionB = new JButton("B"); + optionB.setBackground(Color.LIGHT_GRAY); + optionB.setForeground(Color.BLACK); + optionB.addActionListener(e -> saveAnswer("B")); + optionC = new JButton("C"); + optionC.setBackground(Color.LIGHT_GRAY); + optionC.setForeground(Color.BLACK); + optionC.addActionListener(e -> saveAnswer("C")); + optionD = new JButton("D"); + optionD.setBackground(Color.LIGHT_GRAY); + optionD.setForeground(Color.BLACK); + optionD.addActionListener(e -> saveAnswer("D")); + buttonPanel.add(optionA); + buttonPanel.add(optionB); + buttonPanel.add(optionC); + buttonPanel.add(optionD); + panel.add(buttonPanel, BorderLayout.CENTER); + + return panel; + } + + private JPanel createButtonPanel() { + JPanel panel = new JPanel(new FlowLayout()); + + prevButton = new JButton("上一题"); + prevButton.addActionListener(e -> goToPrevQuestion()); + + nextButton = new JButton("下一题"); + nextButton.addActionListener(e -> goToNextQuestion()); + + submitButton = new JButton("提交"); + submitButton.addActionListener(e -> goToSubmit()); + + panel.add(prevButton); + panel.add(nextButton); + panel.add(submitButton); + return panel; + } + + private void saveAnswer(String answer) { + resetButtonColors(); + + switch (answer) { + case "A": + optionA.setBackground(Color.GREEN); + myAnswers[currentQuestionDex] = answer; + break; + case "B": + optionB.setBackground(Color.GREEN); + myAnswers[currentQuestionDex] = answer; + break; + case "C": + optionC.setBackground(Color.GREEN); + myAnswers[currentQuestionDex] = answer; + break; + case "D": + optionD.setBackground(Color.GREEN); + myAnswers[currentQuestionDex] = answer; + break; + } + + } + + private void resetButtonColors() { + optionA.setBackground(Color.LIGHT_GRAY); + optionB.setBackground(Color.LIGHT_GRAY); + optionC.setBackground(Color.LIGHT_GRAY); + optionD.setBackground(Color.LIGHT_GRAY); + } + + private void goToPrevQuestion() { + if (currentQuestionDex > 0) { + currentQuestionDex--; + updateTitleLabel(); + updateQuestion(); + if (myAnswers[currentQuestionDex] == null) { + resetButtonColors(); + } + else { + saveAnswer(myAnswers[currentQuestionDex]); + } + + updateNavigationButtons(); + } + } + + private void goToNextQuestion() { + if (currentQuestionDex < questionNum - 1) { + currentQuestionDex++; + updateTitleLabel(); + updateQuestion(); + if (myAnswers[currentQuestionDex] == null) { + resetButtonColors(); + } else { + saveAnswer(myAnswers[currentQuestionDex]); + } + + updateNavigationButtons(); + } + } + + private void goToSubmit() { + int confirm = JOptionPane.showConfirmDialog(SelTestFrame.this, "你确定要提交试卷吗?", "提示", JOptionPane.YES_NO_OPTION); + if (confirm == JOptionPane.YES_OPTION) { + int correctCount = 0; + correctCount = getScore(answers, myAnswers); + + double accuracy = Math.round((double) correctCount / myAnswers.length * 10000) / 100.0; + double score = Math.round((double) correctCount * 100 / myAnswers.length * 10) / 10.0; + + // 弹出分数结果 + String[] options = {"返回主菜单", "继续做题"}; + int choice = JOptionPane.showOptionDialog( + SelTestFrame.this, + "您答对了" + correctCount + "道题,正确率为" + String.format("%.2f", accuracy) + "\n您的得分是: " + score + "分", + "测试结果", + JOptionPane.YES_NO_OPTION, + JOptionPane.INFORMATION_MESSAGE, + null, + options, + options[0] + ); + + if (choice == 0) { + mainFrame.setVisible(true); + dispose(); + } else if (choice == 1) { + dispose(); + mainFrame.setVisible(true); + mainFrame.reTryTest(); + } + } + } + + private void updateNavigationButtons() { + if (currentQuestionDex == 0) { + prevButton.setEnabled(false); + prevButton.setVisible(false); + } + else { + prevButton.setEnabled(true); + prevButton.setVisible(true); + } + + if (currentQuestionDex == questionNum - 1) { + nextButton.setEnabled(false); + nextButton.setVisible(false); + submitButton.setEnabled(true); + submitButton.setVisible(true); + } else { + nextButton.setEnabled(true); + nextButton.setVisible(true); + submitButton.setEnabled(false); + submitButton.setVisible(false); + } + } + + private void updateTitleLabel() { + titleLabel.setText("当前为:第" + (currentQuestionDex + 1) + "题"); + } + + private void handleCloseOperation() { + int result = JOptionPane.showConfirmDialog(SelTestFrame.this, "确定要中途退出答题吗?当前答题进度将会丢失!", "确认退出", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); + + if (result == JOptionPane.YES_OPTION) { + mainFrame.setVisible(true); + dispose(); + } + } + + private int getScore(String[] answers, String[] myAnswers) { + if (answers == null || myAnswers == null){ + return 0; + } + int score = 0; + for (int i = 0; i < answers.length; i++) { + if (answers[i] != null && answers[i].equals(myAnswers[i])){ + score++; + } + } + return score; + } + + private void updateQuestion() { + question.setText(multipleChoiceGenerator.GetQuestionList()[currentQuestionDex]); + choice.setText(multipleChoiceGenerator.GetChoiceList()[currentQuestionDex]); + } +}