diff --git a/src/Main.java b/src/Main.java new file mode 100644 index 0000000..b415e36 --- /dev/null +++ b/src/Main.java @@ -0,0 +1,7 @@ +import controller.AppController; + +public class Main { + public static void main(String[] args) { + new AppController(); // 启动控制器 + } +} diff --git a/src/controller/AppController.java b/src/controller/AppController.java new file mode 100644 index 0000000..33da71f --- /dev/null +++ b/src/controller/AppController.java @@ -0,0 +1,111 @@ +package controller; + +import ui.LoginPanel; +import ui.RegisterPanel; +import ui.DifficultySelectionPanel; +import model.User; +import view.QuizPanel; + +import javax.swing.*; +import java.awt.*; + +/** + * 控制器:负责界面切换与核心逻辑 + */ +public class AppController { + private JFrame frame; + private UserManager userManager; + + public AppController() { + userManager = new UserManager(); + + frame = new JFrame("学生数学学习系统"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.setSize(450, 400); + frame.setLocationRelativeTo(null); + frame.setLayout(new BorderLayout()); + + showLoginPanel(); + frame.setVisible(true); + } + + public void showLoginPanel() { + frame.getContentPane().removeAll(); + frame.add(new LoginPanel(this)); + frame.revalidate(); + frame.repaint(); + } + + public void showRegisterPanel() { + frame.getContentPane().removeAll(); + frame.add(new RegisterPanel(this)); + frame.revalidate(); + frame.repaint(); + } + + public void showDifficultySelectionPanel() { + frame.getContentPane().removeAll(); + frame.add(new DifficultySelectionPanel(this)); + frame.revalidate(); + frame.repaint(); + } + + public void showQuizPanel(String level, int count) { + frame.getContentPane().removeAll(); + frame.add(new QuizPanel(this, level, count)); + frame.revalidate(); + frame.repaint(); + } + + public void handleLogin(String email, String password) { + if (userManager.checkLogin(email, password)) { + JOptionPane.showMessageDialog(frame, "登录成功!"); + // 获取用户并显示注册的用户名 + User user = userManager.getUserByEmail(email); + String displayName = user != null ? user.getUsername() : email; + JOptionPane.showMessageDialog(frame, "欢迎回来," + displayName + "!"); + showDifficultySelectionPanel(); + } else { + JOptionPane.showMessageDialog(frame, "邮箱或密码错误!"); + } + } + + public void handleRegister(String email, String password, String username) { + if (userManager.isUserExist(email)) { + JOptionPane.showMessageDialog(frame, "该邮箱已注册!"); + return; + } + // 传递 username 参数 + userManager.addUser(email, password, username); + JOptionPane.showMessageDialog(frame, "注册成功!请登录。"); + showLoginPanel(); + } + + public void handleDifficultySelection(String level) { + String countInput = JOptionPane.showInputDialog( + frame, + "请输入需要生成的题目数量 (1-50):", + "题目数量", + JOptionPane.INFORMATION_MESSAGE + ); + + if (countInput == null) { + return; + } + + try { + int count = Integer.parseInt(countInput); + if (count < 1 || count > 50) { + JOptionPane.showMessageDialog(frame, "题目数量必须在1-50之间!"); + return; + } + showQuizPanel(level, count); + } catch (NumberFormatException e) { + JOptionPane.showMessageDialog(frame, "请输入有效的数字!"); + } + } + + public UserManager getUserManager() { + return userManager; + } +} \ No newline at end of file diff --git a/src/controller/UserManager.java b/src/controller/UserManager.java new file mode 100644 index 0000000..f5fd984 --- /dev/null +++ b/src/controller/UserManager.java @@ -0,0 +1,80 @@ +package controller; + +import model.User; + +import java.io.*; +import java.util.*; + +/** + * 用户信息管理(本地文件,无数据库) + */ +public class UserManager { + private static final String USER_FILE = "users.txt"; + private Map users = new HashMap<>(); // 修改为存储 User 对象 + + public UserManager() { + loadUsers(); + } + + public boolean checkLogin(String email, String password) { + User user = users.get(email); + return user != null && user.getPassword().equals(password); + } + + public boolean isUserExist(String email) { + return users.containsKey(email); + } + + public boolean isUsernameExists(String username) { + for (User user : users.values()) { + if (user.getUsername().equalsIgnoreCase(username)) { + return true; + } + } + return false; + } + // 修改 addUser 方法,接收 username 参数 + public void addUser(String email, String password, String username) { + users.put(email, new User(email, password, username)); + saveUsers(); + } + + // 添加获取用户的方法 + public User getUserByEmail(String email) { + return users.get(email); + } + + private void loadUsers() { + File file = new File(USER_FILE); + if (!file.exists()) return; + try (BufferedReader br = new BufferedReader(new FileReader(file))) { + String line; + while ((line = br.readLine()) != null) { + String[] parts = line.split(","); + if (parts.length >= 2) { + String email = parts[0]; + String password = parts[1]; + String username = email; // 默认用户名为邮箱 + if (parts.length >= 3) { + username = parts[2]; + } + users.put(email, new User(email, password, username)); + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private void saveUsers() { + try (PrintWriter pw = new PrintWriter(new FileWriter(USER_FILE))) { + for (Map.Entry entry : users.entrySet()) { + User user = entry.getValue(); + // 保存 email, password, username 三列 + pw.println(user.getEmail() + "," + user.getPassword() + "," + user.getUsername()); + } + } catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/model/Question.java b/src/model/Question.java new file mode 100644 index 0000000..5b5bc47 --- /dev/null +++ b/src/model/Question.java @@ -0,0 +1,14 @@ +package model; + +public class Question { + private String text; + private String userAnswer; + + public Question(String text) { + this.text = text; + } + + public String getText() { return text; } + public String getUserAnswer() { return userAnswer; } + public void setUserAnswer(String ans) { this.userAnswer = ans; } +} diff --git a/src/model/QuestionGenerator.java b/src/model/QuestionGenerator.java new file mode 100644 index 0000000..c37d3bc --- /dev/null +++ b/src/model/QuestionGenerator.java @@ -0,0 +1,136 @@ +package model; + +import java.util.*; + +/** + * 随机题目生成器 + * 小学:仅 + - * / () + * 初中:含平方、根号 + * 高中:含三角函数 + */ +public class QuestionGenerator { + private static final Random rand = new Random(); + + public static List generateQuestions(String level, int count) { + Set uniqueQuestions = new HashSet<>(); + List list = new ArrayList<>(); + + while (list.size() < count) { + String q; + switch (level) { + case "小学": + q = genPrimary(); + break; + case "初中": + q = genMiddle(); + break; + default: + q = genHigh(); + } + if (!uniqueQuestions.contains(q)) { + uniqueQuestions.add(q); + double correct = calculate(q); + String[] opts = getOptions(q); + // 使用新构造函数传递正确答案 + list.add(new Question(q, opts, String.format("%.2f", correct))); + } + } + return list; + } + + // 小学难度 + private static String genPrimary() { + int a = rand.nextInt(50) + 1; + int b = rand.nextInt(50) + 1; + char op = "+-*/".charAt(rand.nextInt(4)); + return a + " " + op + " " + b; + } + + // 初中难度 + private static String genMiddle() { + int a = rand.nextInt(20) + 1; + int b = rand.nextInt(5) + 1; + String[] ops = {a + "^2", "√" + (a * b), "(" + a + "+" + b + ")^2"}; + return ops[rand.nextInt(ops.length)]; + } + + // 高中难度 + private static String genHigh() { + String[] funcs = {"sin", "cos", "tan"}; + int angle = (rand.nextInt(12) + 1) * 15; + return funcs[rand.nextInt(3)] + "(" + angle + "°)"; + } + + // 模拟生成四个选项(正确答案随机放置) + private static String[] getOptions(String question) { + double correct = calculate(question); + String[] opts = new String[4]; + int correctPos = rand.nextInt(4); + for (int i = 0; i < 4; i++) { + opts[i] = (i == correctPos) + ? String.format("%.2f", correct) + : String.format("%.2f", correct + rand.nextDouble() * 5 - 2.5); + } + return opts; + } + + private static double calculate(String q) { + try { + if (q.contains("sin")) { + int angle = Integer.parseInt(q.replaceAll("\\D", "")); + return Math.sin(Math.toRadians(angle)); + } + if (q.contains("cos")) { + int angle = Integer.parseInt(q.replaceAll("\\D", "")); + return Math.cos(Math.toRadians(angle)); + } + if (q.contains("tan")) { + int angle = Integer.parseInt(q.replaceAll("\\D", "")); + return Math.tan(Math.toRadians(angle)); + } + if (q.contains("√")) { + int num = Integer.parseInt(q.replaceAll("\\D", "")); + return Math.sqrt(num); + } + if (q.contains("^2")) { + int num = Integer.parseInt(q.replaceAll("\\D", "")); + return num * num; + } + // 简单算术 + String[] tokens = q.split(" "); + double a = Double.parseDouble(tokens[0]); + double b = Double.parseDouble(tokens[2]); + return switch (tokens[1]) { + case "+" -> a + b; + case "-" -> a - b; + case "*" -> a * b; + case "/" -> b != 0 ? a / b : 0; + default -> 0; + }; + } catch (Exception e) { + return 0; + } + } + + // Question 内部类 + // 修改Question内部类,添加新的构造函数 + public static class Question { + public String questionText; + public String[] options; + public String correctAnswer; + + public Question(String text, String[] options) { + this.questionText = text; + this.options = options; + // 旧的错误实现:this.correctAnswer = options[0]; + // 改为使用新构造函数 + } + + // 新增构造函数,正确设置正确答案 + public Question(String text, String[] options, String correctAnswer) { + this.questionText = text; + this.options = options; + this.correctAnswer = correctAnswer; + } + } +} diff --git a/src/model/User.java b/src/model/User.java new file mode 100644 index 0000000..0560c64 --- /dev/null +++ b/src/model/User.java @@ -0,0 +1,24 @@ + +package model; + +public class User { + private String email; + private String password; + private String username; + + // 修改构造函数,添加 username 参数 + public User(String email, String password, String username) { + this.email = email; + this.password = password; + this.username = username; + } + + // 保留原有无参数构造函数,用于旧数据兼容 + public User(String email, String password) { + this(email, password, email); // 默认用户名为邮箱 + } + + public String getEmail() { return email; } + public String getPassword() { return password; } + public String getUsername() { return username; } +} \ No newline at end of file diff --git a/src/ui/DifficultySelectionPanel.java b/src/ui/DifficultySelectionPanel.java new file mode 100644 index 0000000..f4f146f --- /dev/null +++ b/src/ui/DifficultySelectionPanel.java @@ -0,0 +1,39 @@ +package ui; + +import controller.AppController; +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class DifficultySelectionPanel extends JPanel { + private AppController controller; + + public DifficultySelectionPanel(AppController controller) { + this.controller = controller; + setLayout(new GridLayout(3, 1, 10, 10)); + setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + + // 创建难度按钮 + JButton elementaryBtn = createDifficultyButton("小学", "小学"); + JButton middleBtn = createDifficultyButton("初中", "初中"); + JButton highBtn = createDifficultyButton("高中", "高中"); + + add(elementaryBtn); + add(middleBtn); + add(highBtn); + } + + private JButton createDifficultyButton(String text, String level) { + JButton btn = new JButton(text); + btn.setFont(new Font("微软雅黑", Font.BOLD, 16)); + btn.setPreferredSize(new Dimension(200, 40)); + btn.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + controller.handleDifficultySelection(level); + } + }); + return btn; + } +} \ No newline at end of file diff --git a/src/ui/LoginPanel.java b/src/ui/LoginPanel.java new file mode 100644 index 0000000..b77d690 --- /dev/null +++ b/src/ui/LoginPanel.java @@ -0,0 +1,57 @@ +package ui; + +import controller.AppController; + +import javax.swing.*; +import java.awt.*; + +/** + * 登录界面 + */ +public class LoginPanel extends JPanel { + private JTextField emailField; + private JPasswordField passwordField; + + public LoginPanel(AppController controller) { + setLayout(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.insets = new Insets(8, 8, 8, 8); + gbc.fill = GridBagConstraints.HORIZONTAL; + + JLabel title = new JLabel("学生数学学习系统", JLabel.CENTER); + title.setFont(new Font("微软雅黑", Font.BOLD, 22)); + gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2; + add(title, gbc); + + gbc.gridwidth = 1; + gbc.gridx = 0; gbc.gridy = 1; + add(new JLabel("邮箱:"), gbc); + + gbc.gridx = 1; + emailField = new JTextField(20); + add(emailField, gbc); + + gbc.gridx = 0; gbc.gridy = 2; + add(new JLabel("密码:"), gbc); + + gbc.gridx = 1; + passwordField = new JPasswordField(20); + add(passwordField, gbc); + + JButton loginButton = new JButton("登录"); + gbc.gridx = 0; gbc.gridy = 3; + add(loginButton, gbc); + + JButton registerButton = new JButton("注册"); + gbc.gridx = 1; + add(registerButton, gbc); + + loginButton.addActionListener(e -> { + String email = emailField.getText().trim(); + String password = new String(passwordField.getPassword()); + controller.handleLogin(email, password); + }); + + registerButton.addActionListener(e -> controller.showRegisterPanel()); + } +} diff --git a/src/ui/RegisterPanel.java b/src/ui/RegisterPanel.java new file mode 100644 index 0000000..a4d68d0 --- /dev/null +++ b/src/ui/RegisterPanel.java @@ -0,0 +1,127 @@ +package ui; + +import controller.AppController; +import controller.UserManager; +import util.EmailUtil; + +import javax.swing.*; +import java.awt.*; + +/** + * 注册界面 + */ +public class RegisterPanel extends JPanel { + private JTextField emailField; + private JTextField codeField; + private JPasswordField passwordField; + private JPasswordField confirmField; + private JTextField usernameField; // 新增用户名输入框 + + private String sentCode = null; + + public RegisterPanel(AppController controller) { + setLayout(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.insets = new Insets(8, 8, 8, 8); + gbc.fill = GridBagConstraints.HORIZONTAL; + + JLabel title = new JLabel("用户注册", JLabel.CENTER); + title.setFont(new Font("微软雅黑", Font.BOLD, 22)); + gbc.gridx = 0; gbc.gridy = 0; gbc.gridwidth = 2; + add(title, gbc); + + gbc.gridwidth = 1; + gbc.gridx = 0; gbc.gridy = 1; + add(new JLabel("邮箱:"), gbc); + + gbc.gridx = 1; + emailField = new JTextField(20); + add(emailField, gbc); + + gbc.gridx = 0; gbc.gridy = 2; + add(new JLabel("用户名:"), gbc); // 修改标签 + gbc.gridx = 1; + usernameField = new JTextField(20); // 新增用户名输入框 + add(usernameField, gbc); + + gbc.gridx = 0; gbc.gridy = 3; + add(new JLabel("验证码:"), gbc); + + gbc.gridx = 1; + codeField = new JTextField(10); + add(codeField, gbc); + + gbc.gridx = 0; gbc.gridy = 4; + add(new JLabel("密码:"), gbc); + + gbc.gridx = 1; + passwordField = new JPasswordField(20); + add(passwordField, gbc); + + gbc.gridx = 0; gbc.gridy = 5; + add(new JLabel("确认密码:"), gbc); + + gbc.gridx = 1; + confirmField = new JPasswordField(20); + add(confirmField, gbc); + + JButton sendCodeButton = new JButton("发送验证码"); + gbc.gridx = 0; gbc.gridy = 6; + add(sendCodeButton, gbc); + + JButton registerButton = new JButton("注册"); + gbc.gridx = 1; + add(registerButton, gbc); + + JButton backButton = new JButton("返回登录"); + gbc.gridx = 0; gbc.gridy = 7; gbc.gridwidth = 2; + add(backButton, gbc); + + sendCodeButton.addActionListener(e -> { + String email = emailField.getText().trim(); + if (!email.matches("^[A-Za-z0-9+_.-]+@(.+)$")) { + JOptionPane.showMessageDialog(this, "邮箱格式不正确!"); + return; + } + sentCode = EmailUtil.sendVerificationCode(email); + JOptionPane.showMessageDialog(this, "验证码已发送(调试模式控制台查看)!"); + }); + + registerButton.addActionListener(e -> { + String email = emailField.getText().trim(); + String code = codeField.getText().trim(); + String password = new String(passwordField.getPassword()); + String confirm = new String(confirmField.getPassword()); + String username = usernameField.getText().trim(); // 获取用户名 + + if (sentCode == null) { + JOptionPane.showMessageDialog(this, "请先获取验证码!"); + return; + } + if (!code.equals(sentCode)) { + JOptionPane.showMessageDialog(this, "验证码错误!"); + return; + } + if (!password.equals(confirm)) { + JOptionPane.showMessageDialog(this, "两次密码不一致!"); + return; + } + if (!password.matches("(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[A-Za-z\\d]{6,10}")) { + JOptionPane.showMessageDialog(this, "密码需包含大小写字母与数字,长度6-10位!"); + return; + } + //防止用户名重复 + if (controller.getUserManager().isUsernameExists(username)) { + JOptionPane.showMessageDialog(this, "用户名已存在,请更换!"); + return; + } + if (username.isEmpty() || password.isEmpty() || email.isEmpty() || code.isEmpty()) { + JOptionPane.showMessageDialog(this, "请填写完整信息!"); + return; + } + controller.handleRegister(email, password, username); // 传递username + }); + + backButton.addActionListener(e -> controller.showLoginPanel()); + } +} \ No newline at end of file diff --git a/src/util/EmailUtil.java b/src/util/EmailUtil.java new file mode 100644 index 0000000..c1d536c --- /dev/null +++ b/src/util/EmailUtil.java @@ -0,0 +1,50 @@ +package util; + +import jakarta.mail.*; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeMessage; +import java.util.Properties; +import java.util.Random; + +/** + * 邮件发送工具(支持 QQ、163 邮箱) + * 使用时请修改 senderEmail / senderAuthCode + */ +public class EmailUtil { + + // 修改为你的发件邮箱和授权码(⚠️不是密码) + private static final String senderEmail = "1193626695@qq.com"; + private static final String senderAuthCode = "ldxzjbnsirnsbacj"; + private static final String smtpHost = "smtp.qq.com"; // 163 改为 smtp.163.com + + public static String sendVerificationCode(String toEmail) { + String code = String.valueOf(100000 + new Random().nextInt(900000)); + + Properties props = new Properties(); + props.put("mail.smtp.auth", "true"); + props.put("mail.smtp.host", smtpHost); + props.put("mail.smtp.port", "587"); + props.put("mail.smtp.starttls.enable", "true"); + + Session session = Session.getInstance(props, new Authenticator() { + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(senderEmail, senderAuthCode); + } + }); + + try { + Message message = new MimeMessage(session); + message.setFrom(new InternetAddress(senderEmail)); + message.setRecipient(Message.RecipientType.TO, new InternetAddress(toEmail)); + message.setSubject("【数学学习系统】注册验证码"); + message.setText("您的验证码是:" + code + "\n(有效期5分钟)"); + + Transport.send(message); + System.out.println("✅ 邮件验证码已发送到:" + toEmail); + } catch (Exception e) { + System.err.println("⚠️ 邮件发送失败:" + e.getMessage()); + } + + return code; + } +} diff --git a/src/util/FileUtil.java b/src/util/FileUtil.java new file mode 100644 index 0000000..fc721f9 --- /dev/null +++ b/src/util/FileUtil.java @@ -0,0 +1,31 @@ +package util; + +import model.User; +import java.io.*; +import java.util.*; + +public class FileUtil { + public static Map loadUsers(String fileName) { + Map users = new HashMap<>(); + try (BufferedReader br = new BufferedReader(new FileReader(fileName))) { + String line; + while ((line = br.readLine()) != null) { + String[] parts = line.split(","); + users.put(parts[0], new User(parts[0], parts[1])); + } + } catch (IOException e) { + // 首次运行时文件可能不存在 + } + return users; + } + + public static void saveUsers(Map users, String fileName) { + try (PrintWriter pw = new PrintWriter(new FileWriter(fileName))) { + for (User u : users.values()) { + pw.println(u.getEmail() + "," + u.getPassword()); + } + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/view/QuizPanel.java b/src/view/QuizPanel.java new file mode 100644 index 0000000..41857b7 --- /dev/null +++ b/src/view/QuizPanel.java @@ -0,0 +1,96 @@ +package view; + +import controller.AppController; +import model.QuestionGenerator; +import model.QuestionGenerator.Question; + +import javax.swing.*; +import java.awt.*; +import java.util.List; + +public class QuizPanel extends JPanel { + private List questions; + private int currentIndex = 0; + private int correctCount = 0; + private ButtonGroup group; + private JLabel questionLabel; + private JRadioButton[] options; + private JButton nextButton; + private AppController controller; + + public QuizPanel(AppController controller, String level, int count) { + this.controller = controller; + this.questions = QuestionGenerator.generateQuestions(level, count); + setLayout(new BorderLayout()); + + questionLabel = new JLabel("", JLabel.CENTER); + questionLabel.setFont(new Font("微软雅黑", Font.BOLD, 18)); + add(questionLabel, BorderLayout.NORTH); + + JPanel optionsPanel = new JPanel(new GridLayout(4, 1, 5, 5)); + group = new ButtonGroup(); + options = new JRadioButton[4]; + for (int i = 0; i < 4; i++) { + options[i] = new JRadioButton(); + group.add(options[i]); + optionsPanel.add(options[i]); + } + add(optionsPanel, BorderLayout.CENTER); + + nextButton = new JButton("下一题"); + add(nextButton, BorderLayout.SOUTH); + + nextButton.addActionListener(e -> checkAndNext()); + showQuestion(); + } + + private void showQuestion() { + if (currentIndex >= questions.size()) { + showResult(); + return; + } + //解决进入下一题后会默认选择前一题的选项问题 + group.clearSelection(); + Question q = questions.get(currentIndex); + questionLabel.setText("第 " + (currentIndex + 1) + " 题:" + q.questionText); + for (int i = 0; i < 4; i++) { + options[i].setText(q.options[i]); + options[i].setSelected(false); + } + } + + private void checkAndNext() { + // 从Question对象中获取正确答案,而不是假设options[0]是正确答案 + String correctAnswer = questions.get(currentIndex).correctAnswer; + for (JRadioButton r : options) { + if (r.isSelected() && r.getText().equals(correctAnswer)) { + correctCount++; + } + } + currentIndex++; + showQuestion(); + } + + private void showResult() { + removeAll(); + setLayout(new GridLayout(3, 1, 10, 10)); + // 添加除以0的检查 + int total = questions.size(); + int score = total > 0 ? (100 * correctCount / total) : 0; + JLabel result = new JLabel("你的得分:" + score + " 分", JLabel.CENTER); + result.setFont(new Font("微软雅黑", Font.BOLD, 22)); + add(result); + + // 其余代码保持不变 + JButton retry = new JButton("再做一份"); + JButton exit = new JButton("退出"); + add(retry); + add(exit); + + retry.addActionListener(e -> controller.showDifficultySelectionPanel()); + exit.addActionListener(e -> System.exit(0)); + + revalidate(); + repaint(); + } +} diff --git a/src/view/ResultPanel.java b/src/view/ResultPanel.java new file mode 100644 index 0000000..4168b08 --- /dev/null +++ b/src/view/ResultPanel.java @@ -0,0 +1,19 @@ +package view; + +import controller.AppController; +import model.User; +import javax.swing.*; +import java.awt.*; + +public class ResultPanel extends JPanel { + public ResultPanel(AppController controller, User user, int score) { + setLayout(new BorderLayout()); + JLabel lbl = new JLabel("用户:" + user.getEmail() + ",得分:" + score + " 分", JLabel.CENTER); + lbl.setFont(new Font("微软雅黑", Font.BOLD, 20)); + add(lbl, BorderLayout.CENTER); + + JButton btnBack = new JButton("返回登录"); + btnBack.addActionListener(e -> controller.showLoginPanel()); + add(btnBack, BorderLayout.SOUTH); + } +}