feat(gui,login): 完成登录逻辑
This commit is contained in:
@@ -113,6 +113,7 @@ dependencies {
|
||||
implementation 'jflac:jflac:1.3'
|
||||
implementation 'com.github.axet:TarsosDSP:2.4'
|
||||
implementation 'org.json:json:20231013'
|
||||
implementation 'org.casbin:casdoor-java-sdk:1.37.0'
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
|
||||
@@ -14,8 +14,9 @@ import com.axis.innovators.box.register.RegistrationTopic;
|
||||
import com.axis.innovators.box.tools.*;
|
||||
import com.axis.innovators.box.util.PythonResult;
|
||||
import com.axis.innovators.box.util.Tray;
|
||||
import com.axis.innovators.box.util.UserLocalInformation;
|
||||
import com.axis.innovators.box.verification.UserTags;
|
||||
import com.axis.innovators.box.verification.LoginResult;
|
||||
import com.axis.innovators.box.verification.CasdoorServer;
|
||||
import com.axis.innovators.box.verification.LoginData;
|
||||
import com.formdev.flatlaf.themes.FlatMacDarkLaf;
|
||||
import com.formdev.flatlaf.themes.FlatMacLightLaf;
|
||||
import com.sun.management.HotSpotDiagnosticMXBean;
|
||||
@@ -31,6 +32,7 @@ import org.apache.logging.log4j.core.appender.FileAppender;
|
||||
import org.apache.logging.log4j.core.appender.RollingFileAppender;
|
||||
import org.apache.logging.log4j.core.config.Configuration;
|
||||
import org.api.dog.agent.VirtualMachine;
|
||||
import org.casbin.casdoor.entity.User;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -58,7 +60,8 @@ public class AxisInnovatorsBox {
|
||||
private static final Logger logger = LogManager.getLogger(AxisInnovatorsBox.class);
|
||||
private static final String VERSIONS = "0.1.2";
|
||||
private static final String[] AUTHOR = new String[]{
|
||||
"tzdwindows 7"
|
||||
"tzdwindows 7",
|
||||
"lyxyz5223",
|
||||
};
|
||||
|
||||
/** 我是总任务数 **/
|
||||
@@ -77,10 +80,11 @@ public class AxisInnovatorsBox {
|
||||
private final RegistrationTopic registrationTopic = new RegistrationTopic(this);
|
||||
private final List<WindowsJDialog> windowsJDialogList = new ArrayList<>();
|
||||
private final StateManager stateManager = new StateManager();
|
||||
private UserTags userTags;
|
||||
private final boolean isDebug;
|
||||
private static DebugWindow debugWindow;
|
||||
|
||||
private LoginData loginData;
|
||||
|
||||
public AxisInnovatorsBox(String[] args, boolean isDebug) {
|
||||
this.args = args;
|
||||
this.isDebug = isDebug;
|
||||
@@ -88,6 +92,37 @@ public class AxisInnovatorsBox {
|
||||
organizingCrashReports(throwable instanceof Exception ?
|
||||
(Exception) throwable : new Exception(throwable));
|
||||
});
|
||||
|
||||
// 加载登录信息,如果没有,弹出登录弹窗,后续可以删掉默认弹出
|
||||
// TODO: login window
|
||||
try {
|
||||
StateManager stateManager = new StateManager();
|
||||
String token = stateManager.getState("loginToken");
|
||||
if (token == null || token.isEmpty()) {
|
||||
LoginResult loginResult = CasdoorLoginWindow.showLoginDialogAndGetLoginResult();
|
||||
if (loginResult == null) {
|
||||
// 用户取消登录
|
||||
JOptionPane.showMessageDialog(null, "取消登录", "登录",
|
||||
JOptionPane.INFORMATION_MESSAGE);
|
||||
} else if (loginResult.success()) {
|
||||
loginData = loginResult.loginData();
|
||||
stateManager.saveState("loginToken", loginResult.token());
|
||||
logger.info("Login result: token: " + loginResult.token() + ", user: " + loginResult.user());
|
||||
JOptionPane.showMessageDialog(null, "登录成功", "登录",
|
||||
JOptionPane.INFORMATION_MESSAGE);
|
||||
} else {
|
||||
// 登录失败,弹出错误提醒,这里只是输出登录错误信息
|
||||
logger.error("Login error: " + loginResult.message());
|
||||
JOptionPane.showMessageDialog(null, "登录失败: \n" + loginResult.message(), "登录失败", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
} else {
|
||||
CasdoorServer casdoorServer = new CasdoorServer();
|
||||
User user = casdoorServer.parseJwtToken(token);
|
||||
loginData = new LoginData(token, user);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed to load login information", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -959,14 +994,6 @@ public class AxisInnovatorsBox {
|
||||
|
||||
main.thread = new Thread(() -> {
|
||||
try {
|
||||
UserLocalInformation userLocalInformation = new UserLocalInformation(main);
|
||||
main.userTags = userLocalInformation.getUserTags();
|
||||
if (main.userTags == null) {
|
||||
// 登录窗口
|
||||
main.userTags = LoginWindow.createAndShow();
|
||||
userLocalInformation.setUserTags(main.userTags);
|
||||
}
|
||||
|
||||
// 主任务1:加载插件
|
||||
logger.info("Loaded plugins Started");
|
||||
main.progressBarManager.updateMainProgress(++main.completedTasks);
|
||||
@@ -1092,13 +1119,6 @@ public class AxisInnovatorsBox {
|
||||
return AUTHOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户标签
|
||||
* @return 用户标签
|
||||
*/
|
||||
public UserTags getUserTags() {
|
||||
return userTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态管理器
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
package com.axis.innovators.box.util;
|
||||
|
||||
import com.axis.innovators.box.AxisInnovatorsBox;
|
||||
import com.axis.innovators.box.verification.OnlineVerification;
|
||||
import com.axis.innovators.box.verification.UserTags;
|
||||
import com.axis.innovators.box.verification.VerificationService;
|
||||
|
||||
/**
|
||||
* 用于存储用户信息
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class UserLocalInformation {
|
||||
private final AxisInnovatorsBox main;
|
||||
|
||||
public UserLocalInformation(AxisInnovatorsBox main){
|
||||
this.main = main;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置用户信息
|
||||
* @param userTags 用户信息
|
||||
*/
|
||||
public void setUserTags(UserTags userTags){
|
||||
OnlineVerification onlineVerification = userTags.getUser();
|
||||
main.getStateManager().saveState("password", onlineVerification.password);
|
||||
main.getStateManager().saveState("verification", onlineVerification.onlineVerification);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户信息
|
||||
* @return 用户信息
|
||||
*/
|
||||
public UserTags getUserTags(){
|
||||
String verification = main.getStateManager().getState("verification");
|
||||
String password = main.getStateManager().getState("password");
|
||||
if (verification == null || password == null){
|
||||
return null;
|
||||
}
|
||||
OnlineVerification onlineVerification = OnlineVerification.validateLogin(
|
||||
verification,
|
||||
password);
|
||||
if (onlineVerification == null){
|
||||
return null;
|
||||
}
|
||||
return VerificationService.determineUserType(onlineVerification);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package com.axis.innovators.box.verification;
|
||||
|
||||
/**
|
||||
* 在线验证用户身份
|
||||
* 用于报错用户的验证信息
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public class OnlineVerification {
|
||||
public String onlineVerification;
|
||||
public String password;
|
||||
/* 我是错误信息,要返回错误请修改我 */
|
||||
public static String errorMessage = "用户不存在";
|
||||
|
||||
/**
|
||||
* 验证登录
|
||||
* @param identifier 账号
|
||||
* @param password 密码
|
||||
*/
|
||||
OnlineVerification(String identifier, String password){
|
||||
this.onlineVerification = identifier;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证登录
|
||||
* @param identifier 账号
|
||||
* @param password 密码
|
||||
* @return 验证结果,如果返回null则表示验证失败,使用errorMessage获取验证失败的原因
|
||||
*/
|
||||
public static OnlineVerification validateLogin(String identifier, String password){
|
||||
return new OnlineVerification(identifier, password);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package com.axis.innovators.box.verification;
|
||||
|
||||
/**
|
||||
* 用户标签组
|
||||
* @author tzdwindows 7
|
||||
*/
|
||||
public enum UserTags {
|
||||
/**
|
||||
* 没有登录的标签
|
||||
*/
|
||||
None,
|
||||
/**
|
||||
* 普通用户标签
|
||||
*/
|
||||
RegularUsers,
|
||||
/**
|
||||
* 管理员标签
|
||||
*/
|
||||
AdminUsers,
|
||||
/**
|
||||
* VIP用户标签
|
||||
*/
|
||||
VipUsers,
|
||||
/**
|
||||
* SVip用户标签
|
||||
*/
|
||||
SVipUsers,
|
||||
/**
|
||||
* 企业用户标签
|
||||
*/
|
||||
EnterpriseUsers;
|
||||
|
||||
private OnlineVerification onlineVerification;
|
||||
|
||||
/**
|
||||
* 设置用户组信息
|
||||
* @param onlineVerification 用户验证结果信息
|
||||
*/
|
||||
void setUser(OnlineVerification onlineVerification) {
|
||||
this.onlineVerification = onlineVerification;
|
||||
}
|
||||
|
||||
public OnlineVerification getUser() {
|
||||
return onlineVerification;
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
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);
|
||||
return userTags;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送密码重置链接给用户
|
||||
* @param text
|
||||
* @return
|
||||
*/
|
||||
public static boolean sendPasswordReset(String text) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册用户
|
||||
* @param text
|
||||
* @param text1
|
||||
* @param pwd
|
||||
* @return
|
||||
*/
|
||||
public static boolean registerUser(String text, String text1, String pwd) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,539 +0,0 @@
|
||||
package com.axis.innovators.box.window;
|
||||
|
||||
import com.axis.innovators.box.verification.OnlineVerification;
|
||||
import com.axis.innovators.box.verification.UserTags;
|
||||
import com.axis.innovators.box.verification.VerificationService;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 重构后的现代化单窗口登录/注册/找回密码界面(带平滑切换动画)
|
||||
* 保留原有验证逻辑接口调用(OnlineVerification / VerificationService)
|
||||
*
|
||||
* 说明:
|
||||
* - 单窗口(JDialog)内使用滑动动画切换视图(仿微软登录体验)。
|
||||
* - 所有子界面(登录/注册/找回密码)都在同一容器中切换,不再弹新窗口。
|
||||
* - 按钮与输入框固定宽度,避免被挤压变形。
|
||||
*
|
||||
* 注意:需要 flatlaf 依赖以呈现更现代的外观。
|
||||
*/
|
||||
public class LoginWindow {
|
||||
private static final AtomicReference<UserTags> loginResult = new AtomicReference<>(UserTags.None);
|
||||
|
||||
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 window = new LoginWindow();
|
||||
window.dialog.setVisible(true);
|
||||
result.set(loginResult.get());
|
||||
if (result.get() == UserTags.None) {
|
||||
System.exit(0);
|
||||
}
|
||||
});
|
||||
return result.get();
|
||||
}
|
||||
|
||||
public LoginWindow() {
|
||||
setupLookAndFeel();
|
||||
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", 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 static class RoundBorder extends LineBorder {
|
||||
private final int radius;
|
||||
|
||||
public RoundBorder(int radius, Color color) {
|
||||
super(color, 1);
|
||||
this.radius = radius;
|
||||
}
|
||||
|
||||
@Override
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user