feat(gui): 重构登录界面为现代单窗口布局

- 设计并实现单窗口多视图的登录/注册/找回密码界面- 添加平滑的视图切换动画效果
- 优化输入框和按钮的样式,提升用户体验- 重构部分验证逻辑,提高代码可读性
This commit is contained in:
tzdwindows 7
2025-08-12 21:22:24 +08:00
parent 9d684b310f
commit e4761d34e0
5 changed files with 513 additions and 396 deletions

View File

@@ -7,29 +7,41 @@ import com.formdev.flatlaf.FlatDarculaLaf;
import javax.swing.*;
import javax.swing.border.LineBorder;
import javax.swing.text.JTextComponent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.atomic.AtomicReference;
/**
* 用于登录的主窗口
* @author tzdwindows 7
* 重构后的现代化单窗口登录/注册/找回密码界面(带平滑切换动画)
* 保留原有验证逻辑接口调用OnlineVerification / VerificationService
*
* 说明:
* - 单窗口JDialog内使用滑动动画切换视图仿微软登录体验
* - 所有子界面(登录/注册/找回密码)都在同一容器中切换,不再弹新窗口。
* - 按钮与输入框固定宽度,避免被挤压变形。
*
* 注意:需要 flatlaf 依赖以呈现更现代的外观。
*/
public class LoginWindow {
private static final AtomicReference<UserTags> loginResult = new AtomicReference<>(UserTags.None);
private JDialog dialog;
private JTextField emailField;
private JPasswordField passwordField;
private JCheckBox showPasswordCheckBox;
private static final int FIELD_WIDTH = 300;
private static final int FIELD_HEIGHT = 35;
private final JDialog dialog;
private final JLayeredPane layeredPane;
private final int DIALOG_WIDTH = 460;
private final int DIALOG_HEIGHT = 560;
// 登录面板中的控件需要在类域以便访问
private JTextField loginEmailField;
private JPasswordField loginPasswordField;
public static UserTags createAndShow() throws InterruptedException, InvocationTargetException {
AtomicReference<UserTags> result = new AtomicReference<>(UserTags.None);
SwingUtilities.invokeAndWait(() -> {
LoginWindow loginWindow = new LoginWindow();
loginWindow.dialog.setVisible(true);
LoginWindow window = new LoginWindow();
window.dialog.setVisible(true);
result.set(loginResult.get());
if (result.get() == UserTags.None) {
System.exit(0);
}
@@ -37,171 +49,476 @@ public class LoginWindow {
return result.get();
}
private LoginWindow() {
public LoginWindow() {
setupLookAndFeel();
createMainDialog();
buildLoginUI();
dialog = new JDialog((Frame) null, "AXIS 安全认证", true);
dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
dialog.setSize(DIALOG_WIDTH, DIALOG_HEIGHT);
dialog.setResizable(false);
dialog.setLocationRelativeTo(null);
// 根容器:深色背景并居中卡片
JPanel root = new JPanel(new GridBagLayout());
root.setBackground(new Color(0x202225));
root.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
dialog.setContentPane(root);
// 卡片容器(居中)
JPanel cardWrapper = new JPanel(null) {
@Override
public Dimension getPreferredSize() {
return new Dimension(DIALOG_WIDTH - 40, DIALOG_HEIGHT - 40);
}
};
cardWrapper.setOpaque(false);
cardWrapper.setPreferredSize(new Dimension(DIALOG_WIDTH - 40, DIALOG_HEIGHT - 40));
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
root.add(cardWrapper, gbc);
// 分层面板用于动画
layeredPane = new JLayeredPane();
layeredPane.setBounds(0, 0, DIALOG_WIDTH - 40, DIALOG_HEIGHT - 40);
cardWrapper.add(layeredPane);
// 卡片背景(圆角)
JPanel backgroundCard = new JPanel();
backgroundCard.setBackground(new Color(0x2A2E31));
backgroundCard.setBorder(new RoundBorder(16, new Color(0x3A3F42)));
backgroundCard.setBounds(0, 0, layeredPane.getWidth(), layeredPane.getHeight());
backgroundCard.setLayout(null);
layeredPane.add(backgroundCard, Integer.valueOf(0));
// 创建三个面板宽度等于容器宽度初始将登录面板放置在0位置
JPanel loginPanel = buildLoginPanel();
JPanel registerPanel = buildRegisterPanel();
JPanel forgotPanel = buildForgotPanel();
int w = layeredPane.getWidth();
int h = layeredPane.getHeight();
loginPanel.setBounds(0, 0, w, h);
registerPanel.setBounds(w, 0, w, h); // 右侧预置
forgotPanel.setBounds(w * 2, 0, w, h);
layeredPane.add(loginPanel, Integer.valueOf(1));
layeredPane.add(registerPanel, Integer.valueOf(1));
layeredPane.add(forgotPanel, Integer.valueOf(1));
dialog.pack();
// ensure layered sizes match after pack
SwingUtilities.invokeLater(() -> {
layeredPane.setBounds(0, 0, cardWrapper.getWidth(), cardWrapper.getHeight());
backgroundCard.setBounds(0, 0, layeredPane.getWidth(), layeredPane.getHeight());
int nw = layeredPane.getWidth(), nh = layeredPane.getHeight();
loginPanel.setBounds(0, 0, nw, nh);
registerPanel.setBounds(nw, 0, nw, nh);
forgotPanel.setBounds(nw * 2, 0, nw, nh);
});
}
// 切换动画direction = 1 表示向左滑动进入下一页(当前 -> 右侧),-1 表示向右滑动进入上一页
private void slideTo(int targetIndex) {
Component[] comps = layeredPane.getComponents();
// 每个面板宽度
int w = layeredPane.getWidth();
// 当前最左边的x位置找到最左的那个主要面板
// 我们采用简单方式:目标面板应该位于 x = targetIndex * w 0,1,2
int targetX = -targetIndex * w; // 我们会将所有面板整体平移,使目标显示在 x=0
// 获取当前 offset (使用第一个面板的 x 来代表整体偏移)
int startOffset = 0;
// find current offset by checking bounds of first panel (assume index 0 is login)
if (comps.length > 0) {
startOffset = comps[0].getX();
}
int start = startOffset;
int end = targetX;
int duration = 300; // ms
int fps = 60;
int delay = 1000 / fps;
int steps = Math.max(1, duration / delay);
final int[] step = {0};
Timer timer = new Timer(delay, null);
timer.addActionListener((ActionEvent e) -> {
step[0]++;
double t = (double) step[0] / steps;
// ease in-out cubic
double tt = t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
int cur = (int) Math.round(start + (end - start) * tt);
// 平移所有在 layeredPane 中(除背景)组件
for (Component c : layeredPane.getComponents()) {
if (c instanceof JPanel && c.isVisible()) {
// 计算原始索引根据名字
String name = c.getName();
// 我们之前把panel放在 x = idx * w ; 现在把它设置为 idx*w + cur
int idx = 0;
if ("login".equals(name)) idx = 0;
else if ("register".equals(name)) idx = 1;
else if ("forgot".equals(name)) idx = 2;
c.setLocation(idx * w + cur, 0);
}
}
layeredPane.repaint();
if (step[0] >= steps) {
timer.stop();
}
});
timer.setInitialDelay(0);
timer.start();
}
private JPanel buildLoginPanel() {
JPanel p = new JPanel(null);
p.setOpaque(false);
p.setName("login");
int w = DIALOG_WIDTH - 40;
int h = DIALOG_HEIGHT - 40;
// 标题区
JLabel appTitle = new JLabel("AXIS");
appTitle.setFont(new Font("微软雅黑", Font.BOLD, 28));
appTitle.setForeground(Color.WHITE);
appTitle.setBounds(28, 20, w - 56, 36);
JLabel subtitle = new JLabel("安全认证 — 请登录以继续");
subtitle.setFont(new Font("微软雅黑", Font.PLAIN, 12));
subtitle.setForeground(new Color(0xA7AEB5));
subtitle.setBounds(28, 56, w - 56, 18);
p.add(appTitle);
p.add(subtitle);
// 卡片内控件起始 y
int startY = 96;
int fieldW = Math.min(360, w - 56);
int fieldX = (w - fieldW) / 2;
// Email
JLabel emailLabel = new JLabel("账号");
emailLabel.setForeground(new Color(0x99A0A7));
emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
emailLabel.setBounds(fieldX, startY, fieldW, 18);
loginEmailField = new JTextField();
styleInput(loginEmailField);
loginEmailField.setBounds(fieldX, startY + 22, fieldW, 44);
loginEmailField.putClientProperty("JTextField.placeholderText", "邮箱或手机号");
// Password
JLabel pwdLabel = new JLabel("密码");
pwdLabel.setForeground(new Color(0x99A0A7));
pwdLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
pwdLabel.setBounds(fieldX, startY + 22 + 44 + 12, fieldW, 18);
loginPasswordField = new JPasswordField();
styleInput(loginPasswordField);
loginPasswordField.setBounds(fieldX, startY + 22 + 44 + 12 + 20, fieldW - 48, 44);
loginPasswordField.putClientProperty("JTextField.placeholderText", "请输入密码");
// eye toggle
JToggleButton eyeBtn = new JToggleButton("显示");
eyeBtn.setFont(new Font("微软雅黑", Font.PLAIN, 12));
eyeBtn.setFocusable(false);
eyeBtn.setBorderPainted(false);
eyeBtn.setContentAreaFilled(true);
eyeBtn.setBackground(new Color(0x39424A));
eyeBtn.setForeground(Color.WHITE);
eyeBtn.setBounds(fieldX + fieldW - 44, startY + 22 + 44 + 12 + 20, 44, 44);
eyeBtn.addActionListener(e -> {
if (eyeBtn.isSelected()) loginPasswordField.setEchoChar((char) 0);
else loginPasswordField.setEchoChar('•');
});
// 登录按钮(占满宽度)
JButton loginBtn = new JButton("立即登录");
stylePrimaryButton(loginBtn);
loginBtn.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 22, fieldW, 48);
loginBtn.addActionListener(e -> doLogin());
// 链接区域(注册 / 忘记密码) — 点击切换到对应面板
JButton toRegister = createTextLink("注册账号");
toRegister.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 22 + 60, 120, 20);
toRegister.addActionListener(e -> slideTo(1));
JButton toForgot = createTextLink("忘记密码");
toForgot.setBounds(fieldX + fieldW - 120, startY + 22 + 44 + 12 + 20 + 44 + 22 + 60, 120, 20);
toForgot.addActionListener(e -> slideTo(2));
// footer
JLabel footer = new JLabel("使用你的 AXIS 账户进行登录。");
footer.setForeground(new Color(0x8F969C));
footer.setFont(new Font("微软雅黑", Font.PLAIN, 11));
footer.setBounds(fieldX, h - 44, fieldW, 18);
p.add(emailLabel);
p.add(loginEmailField);
p.add(pwdLabel);
p.add(loginPasswordField);
p.add(eyeBtn);
p.add(loginBtn);
p.add(toRegister);
p.add(toForgot);
p.add(footer);
return p;
}
private JPanel buildRegisterPanel() {
JPanel p = new JPanel(null);
p.setOpaque(false);
p.setName("register");
int w = DIALOG_WIDTH - 40;
int h = DIALOG_HEIGHT - 40;
JLabel title = new JLabel("创建账号");
title.setFont(new Font("微软雅黑", Font.BOLD, 24));
title.setForeground(Color.WHITE);
title.setBounds(28, 20, w - 56, 36);
JLabel desc = new JLabel("快速创建你的 AXIS 帐号");
desc.setFont(new Font("微软雅黑", Font.PLAIN, 12));
desc.setForeground(new Color(0xA7AEB5));
desc.setBounds(28, 56, w - 56, 18);
p.add(title);
p.add(desc);
int startY = 96;
int fieldW = Math.min(360, w - 56);
int fieldX = (w - fieldW) / 2;
// 用户名
JLabel nameLabel = new JLabel("用户名");
nameLabel.setForeground(new Color(0x99A0A7));
nameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
nameLabel.setBounds(fieldX, startY, fieldW, 18);
JTextField nameField = new JTextField();
styleInput(nameField);
nameField.setBounds(fieldX, startY + 22, fieldW, 44);
// 邮箱
JLabel emailLabel = new JLabel("邮箱");
emailLabel.setForeground(new Color(0x99A0A7));
emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
emailLabel.setBounds(fieldX, startY + 22 + 44 + 12, fieldW, 18);
JTextField emailField = new JTextField();
styleInput(emailField);
emailField.setBounds(fieldX, startY + 22 + 44 + 12 + 20, fieldW, 44);
// 密码
JLabel pwdLabel = new JLabel("密码");
pwdLabel.setForeground(new Color(0x99A0A7));
pwdLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
pwdLabel.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 12, fieldW, 18);
JPasswordField pwdField = new JPasswordField();
styleInput(pwdField);
pwdField.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 12 + 20, fieldW, 44);
// 确认密码
JLabel confirmLabel = new JLabel("确认密码");
confirmLabel.setForeground(new Color(0x99A0A7));
confirmLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
confirmLabel.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 12 + 20 + 44 + 12, fieldW, 18);
JPasswordField confirmField = new JPasswordField();
styleInput(confirmField);
confirmField.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 12 + 20 + 44 + 12 + 20, fieldW, 44);
// 注册按钮
JButton regBtn = new JButton("创建账号");
stylePrimaryButton(regBtn);
regBtn.setBounds(fieldX, startY + 22 + 44 + 12 + 20 + 44 + 12 + 20 + 44 + 12 + 20 + 44 + 18, fieldW, 48);
regBtn.addActionListener(e -> {
String name = nameField.getText().trim();
String email = emailField.getText().trim();
String pwd = new String(pwdField.getPassword());
String confirm = new String(confirmField.getPassword());
if (name.isEmpty() || email.isEmpty() || pwd.isEmpty() || confirm.isEmpty()) {
JOptionPane.showMessageDialog(dialog, "请完整填写注册信息", "注册错误", JOptionPane.ERROR_MESSAGE);
return;
}
if (!pwd.equals(confirm)) {
JOptionPane.showMessageDialog(dialog, "两次输入的密码不一致", "注册错误", JOptionPane.ERROR_MESSAGE);
return;
}
boolean success = VerificationService.registerUser(name, email, pwd);
if (success) {
JOptionPane.showMessageDialog(dialog, "注册成功,请登录", "注册成功", JOptionPane.INFORMATION_MESSAGE);
slideTo(0); // 回到登录页面
} else {
JOptionPane.showMessageDialog(dialog, "注册失败,请检查信息", "注册错误", JOptionPane.ERROR_MESSAGE);
}
});
// 返回登录链接
JButton back = createTextLink("返回登录");
back.setBounds(fieldX, regBtn.getY() + regBtn.getHeight() + 12, 120, 20);
back.addActionListener(e -> slideTo(0));
p.add(nameLabel);
p.add(nameField);
p.add(emailLabel);
p.add(emailField);
p.add(pwdLabel);
p.add(pwdField);
p.add(confirmLabel);
p.add(confirmField);
p.add(regBtn);
p.add(back);
return p;
}
private JPanel buildForgotPanel() {
JPanel p = new JPanel(null);
p.setOpaque(false);
p.setName("forgot");
int w = DIALOG_WIDTH - 40;
int h = DIALOG_HEIGHT - 40;
JLabel title = new JLabel("找回密码");
title.setFont(new Font("微软雅黑", Font.BOLD, 24));
title.setForeground(Color.WHITE);
title.setBounds(28, 20, w - 56, 36);
JLabel desc = new JLabel("通过注册邮箱重置密码");
desc.setFont(new Font("微软雅黑", Font.PLAIN, 12));
desc.setForeground(new Color(0xA7AEB5));
desc.setBounds(28, 56, w - 56, 18);
p.add(title);
p.add(desc);
int startY = 110;
int fieldW = Math.min(360, w - 56);
int fieldX = (w - fieldW) / 2;
JLabel emailLabel = new JLabel("注册邮箱");
emailLabel.setForeground(new Color(0x99A0A7));
emailLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
emailLabel.setBounds(fieldX, startY, fieldW, 18);
JTextField emailField = new JTextField();
styleInput(emailField);
emailField.setBounds(fieldX, startY + 22, fieldW, 44);
JButton sendBtn = new JButton("发送重置邮件");
stylePrimaryButton(sendBtn);
sendBtn.setBounds(fieldX, startY + 22 + 44 + 22, fieldW, 48);
sendBtn.addActionListener(e -> {
String email = emailField.getText().trim();
if (email.isEmpty()) {
JOptionPane.showMessageDialog(dialog, "请输入注册邮箱", "错误", JOptionPane.ERROR_MESSAGE);
return;
}
if (VerificationService.sendPasswordReset(email)) {
JOptionPane.showMessageDialog(dialog, "重置邮件已发送,请查收", "成功", JOptionPane.INFORMATION_MESSAGE);
slideTo(0);
} else {
JOptionPane.showMessageDialog(dialog, "发送失败或邮箱未注册", "失败", JOptionPane.ERROR_MESSAGE);
}
});
JButton back = createTextLink("返回登录");
back.setBounds(fieldX, sendBtn.getY() + sendBtn.getHeight() + 12, 120, 20);
back.addActionListener(e -> slideTo(0));
p.add(emailLabel);
p.add(emailField);
p.add(sendBtn);
p.add(back);
return p;
}
private void doLogin() {
String email = loginEmailField.getText().trim();
String password = new String(loginPasswordField.getPassword()).trim();
if (email.isEmpty() || password.isEmpty()) {
JOptionPane.showMessageDialog(dialog, "请输入完整的登录信息", "验证错误", JOptionPane.ERROR_MESSAGE);
return;
}
OnlineVerification onlineVerification = OnlineVerification.validateLogin(email, password);
if (onlineVerification == null) {
String err = OnlineVerification.errorMessage != null && !OnlineVerification.errorMessage.trim().isEmpty()
? OnlineVerification.errorMessage
: "验证失败,请重试";
JOptionPane.showMessageDialog(dialog, err, "验证错误", JOptionPane.ERROR_MESSAGE);
return;
}
loginResult.set(VerificationService.determineUserType(onlineVerification));
dialog.dispose();
}
// 通用输入框样式
private void styleInput(JComponent comp) {
comp.setFont(new Font("微软雅黑", Font.PLAIN, 14));
comp.setBackground(new Color(0x2F3336));
comp.setForeground(new Color(0xE8ECEF));
comp.setBorder(BorderFactory.createCompoundBorder(
new RoundBorder(8, new Color(0x3A3F42)),
BorderFactory.createEmptyBorder(10, 12, 10, 12)
));
if (comp instanceof JTextComponent) ((JTextComponent) comp).setCaretColor(new Color(0x9AA0A6));
}
// 主要操作按钮样式(主色)
private void stylePrimaryButton(AbstractButton b) {
b.setFont(new Font("微软雅黑", Font.BOLD, 14));
b.setForeground(Color.WHITE);
b.setBackground(new Color(0x2B79D0));
b.setBorderPainted(false);
b.setFocusPainted(false);
b.setOpaque(true);
b.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
private JButton createTextLink(String text) {
JButton btn = new JButton(text);
btn.setFont(new Font("微软雅黑", Font.PLAIN, 12));
btn.setForeground(new Color(0x79A6FF));
btn.setBorderPainted(false);
btn.setContentAreaFilled(false);
btn.setFocusPainted(false);
btn.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
return btn;
}
private void setupLookAndFeel() {
try {
UIManager.setLookAndFeel(new FlatDarculaLaf());
// 自定义输入框样式
UIManager.put("Component.arc", 12);
UIManager.put("TextComponent.arc", 12);
UIManager.put("Button.arc", 8);
UIManager.put("ProgressBar.arc", 8);
UIManager.put("TextComponent.background", new Color(0x383838));
UIManager.put("TextField.placeholderForeground", new Color(0x808080));
} catch (UnsupportedLookAndFeelException e) {
e.printStackTrace();
UIManager.put("Button.arc", 10);
UIManager.put("Panel.background", new Color(0x202225));
UIManager.put("TextComponent.background", new Color(0x2F3336));
UIManager.put("TextComponent.foreground", new Color(0xE8ECEF));
} catch (UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
}
private void createMainDialog() {
dialog = new JDialog((Frame) null, "AXIS 安全认证", true);
dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
dialog.setSize(380, 500);
dialog.setLocationRelativeTo(null);
dialog.setResizable(false);
dialog.setLayout(new BorderLayout());
}
private void buildLoginUI() {
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
mainPanel.setBorder(BorderFactory.createEmptyBorder(30, 30, 30, 30));
JLabel titleLabel = new JLabel("欢迎登录");
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
titleLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
mainPanel.add(titleLabel);
mainPanel.add(Box.createRigidArea(new Dimension(0, 30)));
JPanel inputContainer = new JPanel();
inputContainer.setLayout(new BoxLayout(inputContainer, BoxLayout.Y_AXIS));
inputContainer.setOpaque(false);
inputContainer.setAlignmentX(Component.CENTER_ALIGNMENT);
inputContainer.add(createInputField("账号", buildEmailField()));
inputContainer.add(Box.createRigidArea(new Dimension(0, 10)));
inputContainer.add(createInputField("密码", buildPasswordField()));
mainPanel.add(inputContainer);
mainPanel.add(Box.createRigidArea(new Dimension(0, 30)));
JButton loginButton = createLoginButton();
loginButton.setAlignmentX(Component.CENTER_ALIGNMENT);
mainPanel.add(loginButton);
JPanel linksPanel = createUtilityLinks();
linksPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
mainPanel.add(Box.createRigidArea(new Dimension(0, 30)));
mainPanel.add(linksPanel);
mainPanel.add(Box.createVerticalGlue());
dialog.add(mainPanel, BorderLayout.CENTER);
}
private JComponent buildEmailField() {
emailField = new JTextField();
customizeTextField(emailField, "请输入邮箱或手机号");
return emailField;
}
private JComponent buildPasswordField() {
JPanel passwordPanel = new JPanel(new BorderLayout());
passwordPanel.setOpaque(false);
passwordPanel.setBorder(new RoundBorder(8, new Color(0x454545)));
// 密码输入框
passwordField = new JPasswordField();
passwordField.setFont(new Font("微软雅黑", Font.PLAIN, 14));
passwordField.putClientProperty("JTextField.placeholderText", "请输入密码");
passwordField.setMaximumSize(new Dimension(FIELD_WIDTH, FIELD_HEIGHT));
passwordField.setPreferredSize(new Dimension(FIELD_WIDTH, FIELD_HEIGHT));
passwordField.setBorder(BorderFactory.createEmptyBorder(10, 15, 10, 5));
passwordPanel.add(passwordField, BorderLayout.CENTER);
// 显示密码按钮
JPanel togglePanel = new JPanel(new BorderLayout());
togglePanel.setOpaque(false);
togglePanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));
showPasswordCheckBox = new JCheckBox();
showPasswordCheckBox.setOpaque(false);
showPasswordCheckBox.setPreferredSize(new Dimension(32, 32));
showPasswordCheckBox.setBackground(new Color(0x46494B));
showPasswordCheckBox.setIcon(UIManager.getIcon("PasswordView.icon"));
showPasswordCheckBox.setSelectedIcon(UIManager.getIcon("PasswordHide.icon"));
showPasswordCheckBox.addActionListener(e -> togglePasswordVisibility());
togglePanel.add(showPasswordCheckBox, BorderLayout.CENTER);
passwordPanel.add(togglePanel, BorderLayout.EAST);
return passwordPanel;
}
private void customizeTextField(JComponent field, String placeholder) {
field.setPreferredSize(new Dimension(FIELD_WIDTH, FIELD_HEIGHT));
field.setMaximumSize(new Dimension(FIELD_WIDTH, FIELD_HEIGHT));
field.putClientProperty("JComponent.roundRect", true);
field.putClientProperty("JTextField.placeholderText", placeholder);
field.setFont(new Font("微软雅黑", Font.PLAIN, 14));
field.setBorder(BorderFactory.createCompoundBorder(
new RoundBorder(8, new Color(0x454545)),
BorderFactory.createEmptyBorder(10, 15, 10, 15)
));
}
private JPanel createInputField(String label, JComponent field) {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setOpaque(false);
panel.setMaximumSize(new Dimension(280, 62));
JLabel titleLabel = new JLabel(label);
titleLabel.setFont(new Font("微软雅黑", Font.PLAIN, 13));
titleLabel.setForeground(new Color(0x9E9E9E));
titleLabel.setAlignmentX(Component.LEFT_ALIGNMENT);
panel.add(titleLabel);
panel.add(Box.createVerticalStrut(8));
panel.add(field);
return panel;
}
private JButton createLoginButton() {
JButton button = new JButton("立即登录");
button.setFont(new Font("微软雅黑", Font.BOLD, 14));
button.setForeground(Color.WHITE);
button.setBackground(new Color(0x2B579A));
button.setPreferredSize(new Dimension(285, 40));
button.setMaximumSize(new Dimension(285, 40));
button.setAlignmentX(Component.CENTER_ALIGNMENT);
button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
button.addActionListener(e -> attemptLogin());
// 悬停效果
button.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseEntered(java.awt.event.MouseEvent evt) {
button.setBackground(new Color(0x3C6DB0));
}
public void mouseExited(java.awt.event.MouseEvent evt) {
button.setBackground(new Color(0x2B579A));
}
});
return button;
}
// 圆角边框
private static class RoundBorder extends LineBorder {
private final int radius;
@@ -214,222 +531,9 @@ public class LoginWindow {
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.drawRoundRect(x, y, width-1, height-1, radius, radius);
g2.setColor(lineColor);
g2.drawRoundRect(x, y, width - 1, height - 1, radius, radius);
g2.dispose();
}
}
private JPanel createUtilityLinks() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
panel.setOpaque(false);
JButton registerBtn = createTextButton("注册账号", this::showRegister);
JButton forgotBtn = createTextButton("忘记密码", this::showForgotPassword);
panel.add(Box.createHorizontalGlue());
panel.add(registerBtn);
panel.add(Box.createHorizontalStrut(15));
panel.add(forgotBtn);
panel.add(Box.createHorizontalGlue());
return panel;
}
private JButton createTextButton(String text, Runnable action) {
JButton button = new JButton(text);
button.setForeground(new Color(0x8AB4F8));
button.setFont(new Font("微软雅黑", Font.PLAIN, 12));
button.setContentAreaFilled(false);
button.setBorderPainted(false);
button.setFocusPainted(false);
button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
button.addActionListener(e -> action.run());
return button;
}
private void togglePasswordVisibility() {
if (showPasswordCheckBox.isSelected()) {
passwordField.setEchoChar((char) 0);
} else {
passwordField.setEchoChar('•');
}
}
private void attemptLogin() {
String email = emailField.getText().trim();
String password = new String(passwordField.getPassword()).trim();
if (email.isEmpty() || password.isEmpty()) {
showError("请输入完整的登录信息");
return;
}
OnlineVerification onlineVerification = OnlineVerification.validateLogin(email, password);
loginResult.set(VerificationService.determineUserType(onlineVerification));
dialog.dispose();
}
private void showError(String message) {
JOptionPane.showMessageDialog(dialog, message, "验证错误",
JOptionPane.ERROR_MESSAGE);
}
private void showRegister() {
JDialog registerDialog = new JDialog(dialog, "注册新账号", true);
registerDialog.setSize(380, 500);
registerDialog.setLocationRelativeTo(dialog);
registerDialog.setResizable(false);
registerDialog.setLayout(new BorderLayout());
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
mainPanel.setBorder(BorderFactory.createEmptyBorder(30, 30, 30, 30));
// 标题
JLabel titleLabel = new JLabel("新用户注册");
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
titleLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
mainPanel.add(titleLabel);
mainPanel.add(Box.createRigidArea(new Dimension(0, 30)));
// 输入容器
JPanel inputContainer = new JPanel();
inputContainer.setLayout(new BoxLayout(inputContainer, BoxLayout.Y_AXIS));
inputContainer.setOpaque(false);
// 用户名
JTextField usernameField = new JTextField();
customizeTextField(usernameField, "请输入用户名");
inputContainer.add(createInputField("用户名", usernameField));
inputContainer.add(Box.createRigidArea(new Dimension(0, 10)));
// 邮箱
JTextField emailField = new JTextField();
customizeTextField(emailField, "请输入邮箱");
inputContainer.add(createInputField("邮箱", emailField));
inputContainer.add(Box.createRigidArea(new Dimension(0, 10)));
// 密码
JPasswordField passwordField = new JPasswordField();
customizeTextField(passwordField, "请输入密码");
inputContainer.add(createInputField("密码", passwordField));
inputContainer.add(Box.createRigidArea(new Dimension(0, 10)));
// 确认密码
JPasswordField confirmField = new JPasswordField();
customizeTextField(confirmField, "请再次输入密码");
inputContainer.add(createInputField("确认密码", confirmField));
mainPanel.add(inputContainer);
mainPanel.add(Box.createRigidArea(new Dimension(0, 30)));
// 注册按钮
JButton regButton = new JButton("立即注册");
styleButton(regButton);
regButton.addActionListener(e -> {
String pwd = new String(passwordField.getPassword());
String confirm = new String(confirmField.getPassword());
if(!pwd.equals(confirm)) {
JOptionPane.showMessageDialog(registerDialog, "两次输入的密码不一致", "注册错误",
JOptionPane.ERROR_MESSAGE);
return;
}
// 调用验证服务
boolean success = VerificationService.registerUser(
usernameField.getText(),
emailField.getText(),
pwd
);
if(success) {
JOptionPane.showMessageDialog(registerDialog, "注册成功,请登录", "注册成功",
JOptionPane.INFORMATION_MESSAGE);
registerDialog.dispose();
} else {
JOptionPane.showMessageDialog(registerDialog, "注册失败,请检查信息", "注册错误",
JOptionPane.ERROR_MESSAGE);
}
});
mainPanel.add(regButton);
// 返回登录链接
JButton backBtn = createTextButton("返回登录", registerDialog::dispose);
backBtn.setAlignmentX(Component.CENTER_ALIGNMENT);
mainPanel.add(Box.createRigidArea(new Dimension(0, 15)));
mainPanel.add(backBtn);
registerDialog.add(mainPanel, BorderLayout.CENTER);
registerDialog.setVisible(true);
}
private void showForgotPassword() {
JDialog forgotDialog = new JDialog(dialog, "找回密码", true);
forgotDialog.setSize(380, 350);
forgotDialog.setLocationRelativeTo(dialog);
forgotDialog.setResizable(false);
forgotDialog.setLayout(new BorderLayout());
JPanel mainPanel = new JPanel();
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
mainPanel.setBorder(BorderFactory.createEmptyBorder(30, 30, 30, 30));
JLabel titleLabel = new JLabel("找回密码");
titleLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
titleLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
mainPanel.add(titleLabel);
mainPanel.add(Box.createRigidArea(new Dimension(0, 30)));
// 邮箱输入
JTextField emailField = new JTextField();
customizeTextField(emailField, "请输入注册邮箱");
mainPanel.add(createInputField("注册邮箱", emailField));
mainPanel.add(Box.createRigidArea(new Dimension(0, 30)));
// 提交按钮
JButton submitBtn = new JButton("发送重置邮件");
styleButton(submitBtn);
submitBtn.addActionListener(e -> {
if(VerificationService.sendPasswordReset(emailField.getText())) {
JOptionPane.showMessageDialog(forgotDialog, "重置邮件已发送,请查收", "操作成功",
JOptionPane.INFORMATION_MESSAGE);
forgotDialog.dispose();
} else {
JOptionPane.showMessageDialog(forgotDialog, "邮箱未注册或发送失败", "操作失败",
JOptionPane.ERROR_MESSAGE);
}
});
mainPanel.add(submitBtn);
// 返回链接
JButton backBtn = createTextButton("返回登录", forgotDialog::dispose);
backBtn.setAlignmentX(Component.CENTER_ALIGNMENT);
mainPanel.add(Box.createRigidArea(new Dimension(0, 15)));
mainPanel.add(backBtn);
forgotDialog.add(mainPanel, BorderLayout.CENTER);
forgotDialog.setVisible(true);
}
// 通用按钮样式方法
private void styleButton(JButton button) {
button.setFont(new Font("微软雅黑", Font.BOLD, 14));
button.setForeground(Color.WHITE);
button.setBackground(new Color(0x2B579A));
button.setPreferredSize(new Dimension(285, 40));
button.setMaximumSize(new Dimension(285, 40));
button.setAlignmentX(Component.CENTER_ALIGNMENT);
button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
// 悬停效果
button.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseEntered(java.awt.event.MouseEvent evt) {
button.setBackground(new Color(0x3C6DB0));
}
public void mouseExited(java.awt.event.MouseEvent evt) {
button.setBackground(new Color(0x2B579A));
}
});
}
}

View File

@@ -2,32 +2,31 @@ package com.axis.innovators.box.verification;
/**
* 在线验证用户身份
* 用于报错用户的验证信息
* @author tzdwindows 7
*/
public class OnlineVerification {
public String onlineVerification;
public UserTags userTags;
public String password;
OnlineVerification(String onlineVerification, UserTags userTags){
this.onlineVerification = onlineVerification;
this.userTags = userTags;
}
/* 我是错误信息,要返回错误请修改我 */
public static String errorMessage = "用户不存在";
/**
* 验证登录
* @param identifier
* @param password
* @param identifier 账号
* @param password 密码
*/
OnlineVerification(String identifier, String password){
this.onlineVerification = identifier;
this.password = password;
}
public static OnlineVerification validateLogin(String identifier){
return new OnlineVerification(identifier, UserTags.RegularUsers);
}
/**
* 验证登录
* @param identifier 账号
* @param password 密码
* @return 验证结果,如果返回null则表示验证失败使用errorMessage获取验证失败的原因
*/
public static OnlineVerification validateLogin(String identifier, String password){
return new OnlineVerification(identifier, password);
}

View File

@@ -31,6 +31,11 @@ public enum UserTags {
EnterpriseUsers;
private OnlineVerification onlineVerification;
/**
* 设置用户组信息
* @param onlineVerification 用户验证结果信息
*/
void setUser(OnlineVerification onlineVerification) {
this.onlineVerification = onlineVerification;
}

View File

@@ -4,6 +4,12 @@ package com.axis.innovators.box.verification;
* @author tzdwindows 7
*/
public class VerificationService {
/**
* 确定用户类型
* @param identifier 用户
* @return 用户类型
*/
public static UserTags determineUserType(OnlineVerification identifier) {
UserTags userTags = UserTags.RegularUsers;
userTags.setUser(identifier);
@@ -19,6 +25,13 @@ public class VerificationService {
return true;
}
/**
* 注册用户
* @param text
* @param text1
* @param pwd
* @return
*/
public static boolean registerUser(String text, String text1, String pwd) {
return true;
}

View File

@@ -1,4 +0,0 @@
#Updated configuration
#Fri Mar 07 17:43:24 CST 2025
password=safasf
verification=aasfsaf