@@ -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 = 30 0;
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 = 56 0;
// 登录面板中的控件需要在类域以便访问
private JTextField loginEmailField ;
private JPasswordField loginPasswordField ;
public static UserTags createAndShow ( ) throws InterruptedException , InvocationTargetException {
AtomicReference < UserTags > result = new AtomicReference < > ( UserTags . None ) ;
SwingUtilities . invokeAndWait ( ( ) - > {
LoginWindow loginW indow = new LoginWindow ( ) ;
loginW indow. dialog . setVisible ( true ) ;
LoginWindow w indow = new LoginWindow ( ) ;
w indow. 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.placeholderFore ground " , new Color ( 0x808080 ) ) ;
} catch ( UnsupportedLookAndFeelException e ) {
e . printStackTrace ( ) ;
UIManager . put ( " Button.arc " , 10 ) ;
UIManager . put ( " Panel .background" , new Color ( 0x202225 ) ) ;
UIManager . put ( " TextComponent.back ground " , 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 . 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 ) ) ;
}
} ) ;
}
}