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;
|
}
|
}
|