package user; import login.EmailCodeSender; import login.PasswordReset; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.border.EmptyBorder; import publicway.buttonset; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; public class ZhaohuiMima extends JDialog { private JTextField emailField; private JTextField codeField; private JPasswordField passwordField; private JPasswordField confirmPasswordField; private JButton getCodeButton; private JButton saveButton; private JLabel tipLabel; private Timer timer; private int countdown = 60; // 主题颜色 (参考 Denglu.java) private final Color THEME_COLOR = new Color(46, 139, 87); private final Color THEME_HOVER_COLOR = new Color(30, 107, 69); public ZhaohuiMima(Frame owner) { super(owner, "找回密码", true); setSize(400, 350); setLocationRelativeTo(owner); setResizable(false); initializeUI(); } private void initializeUI() { JPanel mainPanel = new JPanel(); mainPanel.setLayout(new GridBagLayout()); mainPanel.setBorder(new EmptyBorder(20, 20, 20, 20)); mainPanel.setBackground(Color.WHITE); setContentPane(mainPanel); GridBagConstraints gbc = new GridBagConstraints(); gbc.insets = new Insets(5, 5, 5, 5); gbc.fill = GridBagConstraints.HORIZONTAL; // 邮箱 JLabel emailLabel = new JLabel("邮箱:"); gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 0.1; mainPanel.add(emailLabel, gbc); emailField = new JTextField(); styleTextField(emailField); gbc.gridx = 1; gbc.gridy = 0; gbc.weightx = 0.9; gbc.gridwidth = 2; mainPanel.add(emailField, gbc); // 验证码 JLabel codeLabel = new JLabel("验证码:"); gbc.gridx = 0; gbc.gridy = 1; gbc.gridwidth = 1; gbc.weightx = 0.1; mainPanel.add(codeLabel, gbc); codeField = new JTextField(); styleTextField(codeField); gbc.gridx = 1; gbc.gridy = 1; gbc.weightx = 0.6; mainPanel.add(codeField, gbc); getCodeButton = new JButton("获取验证码"); styleButton(getCodeButton); getCodeButton.setFont(new Font("微软雅黑", Font.PLAIN, 12)); // 调整按钮高度以匹配文本框 getCodeButton.setPreferredSize(new Dimension(100, 38)); gbc.gridx = 2; gbc.gridy = 1; gbc.weightx = 0.3; mainPanel.add(getCodeButton, gbc); // 新密码 JLabel passwordLabel = new JLabel("新密码:"); gbc.gridx = 0; gbc.gridy = 2; gbc.weightx = 0.1; mainPanel.add(passwordLabel, gbc); JPanel passwordPanel = createPasswordPanel(); passwordField = (JPasswordField) passwordPanel.getComponent(0); gbc.gridx = 1; gbc.gridy = 2; gbc.gridwidth = 2; gbc.weightx = 0.9; mainPanel.add(passwordPanel, gbc); // 确认密码 JLabel confirmPasswordLabel = new JLabel("确认密码:"); gbc.gridx = 0; gbc.gridy = 3; gbc.gridwidth = 1; gbc.weightx = 0.1; mainPanel.add(confirmPasswordLabel, gbc); JPanel confirmPasswordPanel = createPasswordPanel(); confirmPasswordField = (JPasswordField) confirmPasswordPanel.getComponent(0); gbc.gridx = 1; gbc.gridy = 3; gbc.gridwidth = 2; gbc.weightx = 0.9; mainPanel.add(confirmPasswordPanel, gbc); // 提示信息 tipLabel = new JLabel("密码6-25个字符"); tipLabel.setForeground(Color.GRAY); tipLabel.setFont(new Font("微软雅黑", Font.PLAIN, 10)); gbc.gridx = 1; gbc.gridy = 4; gbc.gridwidth = 2; mainPanel.add(tipLabel, gbc); // 保存按钮 saveButton = buttonset.createStyledButton("保存密码", THEME_COLOR); gbc.gridx = 0; gbc.gridy = 5; gbc.gridwidth = 3; gbc.insets = new Insets(20, 5, 5, 5); mainPanel.add(saveButton, gbc); // 事件监听 setupEvents(); } private void styleTextField(JTextField field) { field.setPreferredSize(new Dimension(0, 38)); // 宽度由GridBagLayout控制,高度设为38 field.setFont(new Font("PingFang SC", Font.PLAIN, 14)); field.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createLineBorder(new Color(200, 200, 200)), BorderFactory.createEmptyBorder(8, 10, 8, 10) )); field.setForeground(new Color(60, 60, 60)); } private void styleButton(JButton button) { button.setBackground(THEME_COLOR); button.setForeground(Color.WHITE); button.setFocusPainted(false); button.setBorderPainted(false); button.setFont(new Font("微软雅黑", Font.BOLD, 14)); button.setCursor(new Cursor(Cursor.HAND_CURSOR)); button.addMouseListener(new java.awt.event.MouseAdapter() { public void mouseEntered(java.awt.event.MouseEvent evt) { if (button.isEnabled()) { button.setBackground(THEME_HOVER_COLOR); } } public void mouseExited(java.awt.event.MouseEvent evt) { if (button.isEnabled()) { button.setBackground(THEME_COLOR); } } }); } // 邮箱验证方法 private boolean isValidEmail(String email) { // 简单的邮箱格式验证 String emailRegex = "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$"; return email != null && email.matches(emailRegex); } private void setupEvents() { getCodeButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String email = emailField.getText().trim(); if (email.isEmpty()) { JOptionPane.showMessageDialog(ZhaohuiMima.this, "请输入邮箱", "提示", JOptionPane.WARNING_MESSAGE); emailField.requestFocus(); return; } // 验证邮箱格式 if (!isValidEmail(email)) { JOptionPane.showMessageDialog(ZhaohuiMima.this, "请输入有效的邮箱地址", "输入错误", JOptionPane.WARNING_MESSAGE); emailField.requestFocus(); return; } // 禁用按钮,防止重复点击 getCodeButton.setEnabled(false); // 在新线程中调用API,避免阻塞UI new Thread(() -> { try { // 调用邮件验证码发送API EmailCodeSender.EmailCodeResponse response = EmailCodeSender.sendEmailCode(email); // 在EDT线程中更新UI SwingUtilities.invokeLater(() -> { if (response.isSuccess()) { // 发送成功,显示提示并开始倒计时 JOptionPane.showMessageDialog(ZhaohuiMima.this, "验证码已发送到您的邮箱", "提示", JOptionPane.INFORMATION_MESSAGE); // 开始60秒倒计时 startCountdown(); } else { // 发送失败,显示错误信息并恢复按钮 getCodeButton.setEnabled(true); String errorMessage = response.getMessage() != null ? response.getMessage() : "验证码发送失败"; JOptionPane.showMessageDialog(ZhaohuiMima.this, errorMessage, "错误", JOptionPane.ERROR_MESSAGE); } }); } catch (Exception ex) { // 异常处理,恢复按钮并显示错误 SwingUtilities.invokeLater(() -> { getCodeButton.setEnabled(true); JOptionPane.showMessageDialog(ZhaohuiMima.this, "发送验证码时发生错误: " + ex.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); }); } }).start(); } }); // 实时验证密码一致性 DocumentListener passwordListener = new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { checkPasswordMatch(); } @Override public void removeUpdate(DocumentEvent e) { checkPasswordMatch(); } @Override public void changedUpdate(DocumentEvent e) { checkPasswordMatch(); } }; passwordField.getDocument().addDocumentListener(passwordListener); confirmPasswordField.getDocument().addDocumentListener(passwordListener); saveButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { savePassword(); } }); } private void checkPasswordMatch() { String pass = new String(passwordField.getPassword()); String confirm = new String(confirmPasswordField.getPassword()); if (pass.isEmpty()) { tipLabel.setText("密码6-25个字符"); tipLabel.setForeground(Color.GRAY); return; } // 先判断新密码长度 if (pass.length() < 6) { tipLabel.setText("密码长度不能少于6个字符"); tipLabel.setForeground(Color.RED); return; } else if (pass.length() > 25) { tipLabel.setText("密码长度不能超过25个字符"); tipLabel.setForeground(Color.RED); return; } // 如果确认密码为空,提示输入 if (confirm.isEmpty()) { tipLabel.setText("密码6-25个字符"); tipLabel.setForeground(Color.GRAY); return; } // 只有当确认密码长度和新密码长度一致时,才进行比对 if (pass.length() == confirm.length()) { if (!pass.equals(confirm)) { tipLabel.setText("两次输入的密码不一致"); tipLabel.setForeground(Color.RED); } else { tipLabel.setText("密码一致"); tipLabel.setForeground(new Color(46, 139, 87)); // Green } } else { // 长度不一致时,恢复默认提示,避免在输入过程中一直提示不一致 tipLabel.setText("密码6-25个字符"); tipLabel.setForeground(Color.GRAY); } } private void startCountdown() { getCodeButton.setEnabled(false); getCodeButton.setBackground(Color.GRAY); countdown = 60; getCodeButton.setText(countdown + "秒"); timer = new Timer(1000, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { countdown--; if (countdown > 0) { getCodeButton.setText(countdown + "秒"); } else { timer.stop(); getCodeButton.setText("获取验证码"); getCodeButton.setEnabled(true); getCodeButton.setBackground(THEME_COLOR); } } }); timer.start(); } private void savePassword() { String email = emailField.getText().trim(); String code = codeField.getText().trim(); String newPassword = new String(passwordField.getPassword()).trim(); String confirmPassword = new String(confirmPasswordField.getPassword()).trim(); // 输入验证 if (email.isEmpty()) { JOptionPane.showMessageDialog(this, "请输入邮箱", "错误", JOptionPane.ERROR_MESSAGE); emailField.requestFocus(); return; } // 验证邮箱格式 if (!isValidEmail(email)) { JOptionPane.showMessageDialog(this, "请输入有效的邮箱地址", "错误", JOptionPane.ERROR_MESSAGE); emailField.requestFocus(); return; } if (code.isEmpty()) { JOptionPane.showMessageDialog(this, "请输入验证码", "错误", JOptionPane.ERROR_MESSAGE); codeField.requestFocus(); return; } if (newPassword.isEmpty()) { JOptionPane.showMessageDialog(this, "请输入新密码", "错误", JOptionPane.ERROR_MESSAGE); passwordField.requestFocus(); return; } if (newPassword.length() < 6) { JOptionPane.showMessageDialog(this, "密码长度不能少于6个字符", "错误", JOptionPane.ERROR_MESSAGE); passwordField.requestFocus(); return; } if (newPassword.length() > 25) { JOptionPane.showMessageDialog(this, "密码长度不能超过25个字符", "错误", JOptionPane.ERROR_MESSAGE); passwordField.requestFocus(); return; } if (confirmPassword.isEmpty()) { JOptionPane.showMessageDialog(this, "请输入确认密码", "错误", JOptionPane.ERROR_MESSAGE); confirmPasswordField.requestFocus(); return; } if (!newPassword.equals(confirmPassword)) { JOptionPane.showMessageDialog(this, "两次输入的密码不一致", "错误", JOptionPane.ERROR_MESSAGE); confirmPasswordField.requestFocus(); return; } // 禁用保存按钮,防止重复提交 saveButton.setEnabled(false); // 在新线程中调用API,避免阻塞UI new Thread(() -> { try { // 调用密码重置API PasswordReset.ResetPasswordResponse response = PasswordReset.resetPassword( email, code, newPassword, confirmPassword); // 在EDT线程中更新UI SwingUtilities.invokeLater(() -> { saveButton.setEnabled(true); // 显示返回消息 String message = response.getMessage() != null ? response.getMessage() : (response.isSuccess() ? "密码重置成功" : "密码重置失败"); int messageType = response.isSuccess() ? JOptionPane.INFORMATION_MESSAGE : JOptionPane.ERROR_MESSAGE; String title = response.isSuccess() ? "成功" : "失败"; JOptionPane.showMessageDialog(this, message, title, messageType); // 如果成功,关闭窗口 if (response.isSuccess()) { dispose(); } }); } catch (Exception e) { // 异常处理,恢复按钮并显示错误 SwingUtilities.invokeLater(() -> { saveButton.setEnabled(true); JOptionPane.showMessageDialog(this, "重置密码时发生错误: " + e.getMessage(), "错误", JOptionPane.ERROR_MESSAGE); }); } }).start(); } private JPanel createPasswordPanel() { JPanel panel = new JPanel(new BorderLayout()); panel.setBackground(Color.WHITE); JPasswordField pf = new JPasswordField(); styleTextField(pf); // 移除右边框,为了和眼睛按钮无缝连接 pf.setBorder(BorderFactory.createCompoundBorder( BorderFactory.createMatteBorder(1, 1, 1, 0, new Color(200, 200, 200)), BorderFactory.createEmptyBorder(8, 10, 8, 5) )); JToggleButton eyeButton = new JToggleButton("👁"); eyeButton.setPreferredSize(new Dimension(40, 38)); eyeButton.setBackground(Color.WHITE); eyeButton.setBorder(BorderFactory.createMatteBorder(1, 0, 1, 1, new Color(200, 200, 200))); eyeButton.setFocusPainted(false); eyeButton.setCursor(new Cursor(Cursor.HAND_CURSOR)); eyeButton.setFont(new Font("Segoe UI Symbol", Font.PLAIN, 16)); // 确保支持Unicode字符 eyeButton.addActionListener(e -> { if (eyeButton.isSelected()) { pf.setEchoChar((char) 0); // 显示密码 } else { pf.setEchoChar('•'); // 隐藏密码 (默认字符) } }); panel.add(pf, BorderLayout.CENTER); panel.add(eyeButton, BorderLayout.EAST); return panel; } }