forked from datawhale/whale-town-front
feat:实现登录系统和用户认证功能
- 添加登录场景(login_scene.tscn)和主菜单场景(main_menu_scene.tscn) - 实现认证管理器(AuthManager)用于用户登录和会话管理 - 添加核心服务:加密服务、存储服务、网络服务 - 配置项目主场景为登录场景 - 添加自动加载服务到项目配置 - 添加开发环境配置(VSCode、Claude) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
217
scripts/ui/UI_Login.gd
Normal file
217
scripts/ui/UI_Login.gd
Normal file
@@ -0,0 +1,217 @@
|
||||
extends Control
|
||||
|
||||
## 登录界面控制器
|
||||
## 处理登录界面的用户交互、输入验证、错误提示等
|
||||
|
||||
# UI 节点引用
|
||||
@onready var passwordLoginTab: Button = $LoginPanel/PasswordLoginTab
|
||||
@onready var codeLoginTab: Button = $LoginPanel/CodeLoginTab
|
||||
@onready var usernameInput: LineEdit = $LoginPanel/UsernameInput
|
||||
@onready var passwordInput: LineEdit = $LoginPanel/PasswordInput
|
||||
@onready var togglePasswordButton: Button = $LoginPanel/TogglePasswordButton
|
||||
@onready var rememberPasswordCheckBox: CheckBox = $LoginPanel/RememberPasswordCheckBox
|
||||
@onready var autoLoginCheckBox: CheckBox = $LoginPanel/AutoLoginCheckBox
|
||||
@onready var enterButton: TextureButton = $LoginPanel/EnterButton
|
||||
@onready var forgotPasswordButton: LinkButton = $LoginPanel/ForgotPasswordButton
|
||||
@onready var registerButton: LinkButton = $LoginPanel/RegisterButton
|
||||
@onready var statusLabel: Label = $LoginPanel/StatusLabel
|
||||
@onready var loadingOverlay: ColorRect = $LoadingOverlay
|
||||
@onready var loadingLabel: Label = $LoadingOverlay/LoadingLabel
|
||||
|
||||
# 当前登录模式
|
||||
enum LoginMode {
|
||||
PASSWORD, # 密码登录
|
||||
CODE # 验证码登录
|
||||
}
|
||||
var currentMode: LoginMode = LoginMode.PASSWORD
|
||||
|
||||
func _ready():
|
||||
# 连接Tab按钮信号
|
||||
passwordLoginTab.pressed.connect(_onPasswordTabPressed)
|
||||
codeLoginTab.pressed.connect(_onCodeTabPressed)
|
||||
|
||||
# 连接主要按钮信号
|
||||
enterButton.pressed.connect(_onEnterButtonPressed)
|
||||
togglePasswordButton.pressed.connect(_onTogglePasswordPressed)
|
||||
|
||||
# 连接底部链接按钮
|
||||
forgotPasswordButton.pressed.connect(_onForgotPasswordPressed)
|
||||
registerButton.pressed.connect(_onRegisterPressed)
|
||||
|
||||
# 连接 AuthManager 信号
|
||||
AuthManager.login_success.connect(_onLoginSuccess)
|
||||
AuthManager.login_failed.connect(_onLoginFailed)
|
||||
|
||||
# 连接输入框回车事件
|
||||
usernameInput.text_submitted.connect(_onUsernameSubmitted)
|
||||
passwordInput.text_submitted.connect(_onPasswordSubmitted)
|
||||
|
||||
# 自动填充记住的账号密码
|
||||
_loadSavedAccount()
|
||||
|
||||
# 初始化状态
|
||||
_hideLoading()
|
||||
_clearStatus()
|
||||
_updateLoginMode()
|
||||
|
||||
## Tab切换 - 密码登录
|
||||
func _onPasswordTabPressed() -> void:
|
||||
if currentMode != LoginMode.PASSWORD:
|
||||
currentMode = LoginMode.PASSWORD
|
||||
codeLoginTab.button_pressed = false
|
||||
passwordLoginTab.button_pressed = true
|
||||
_updateLoginMode()
|
||||
|
||||
## Tab切换 - 验证码登录
|
||||
func _onCodeTabPressed() -> void:
|
||||
if currentMode != LoginMode.CODE:
|
||||
currentMode = LoginMode.CODE
|
||||
passwordLoginTab.button_pressed = false
|
||||
codeLoginTab.button_pressed = true
|
||||
_updateLoginMode()
|
||||
|
||||
## 更新登录模式UI
|
||||
func _updateLoginMode() -> void:
|
||||
match currentMode:
|
||||
LoginMode.PASSWORD:
|
||||
passwordInput.placeholder_text = "输入密码"
|
||||
passwordInput.secret = true
|
||||
togglePasswordButton.visible = true
|
||||
LoginMode.CODE:
|
||||
passwordInput.placeholder_text = "输入验证码"
|
||||
passwordInput.secret = false
|
||||
togglePasswordButton.visible = false
|
||||
|
||||
## 切换密码显示/隐藏
|
||||
func _onTogglePasswordPressed() -> void:
|
||||
passwordInput.secret = !passwordInput.secret
|
||||
togglePasswordButton.text = "👁" if passwordInput.secret else "🔒"
|
||||
|
||||
## 加载保存的账号密码
|
||||
func _loadSavedAccount() -> void:
|
||||
var savedAccount = StorageService.loadAccount()
|
||||
|
||||
var username = savedAccount.get("username", "")
|
||||
var password = savedAccount.get("password", "")
|
||||
var rememberPassword = savedAccount.get("remember_password", false)
|
||||
|
||||
# 设置用户名
|
||||
usernameInput.text = username
|
||||
|
||||
if rememberPassword and not password.is_empty():
|
||||
passwordInput.text = password
|
||||
rememberPasswordCheckBox.button_pressed = true
|
||||
else:
|
||||
rememberPasswordCheckBox.button_pressed = false
|
||||
|
||||
## 进入小镇按钮点击事件
|
||||
func _onEnterButtonPressed() -> void:
|
||||
match currentMode:
|
||||
LoginMode.PASSWORD:
|
||||
_handlePasswordLogin()
|
||||
LoginMode.CODE:
|
||||
_handleCodeLogin()
|
||||
|
||||
## 处理密码登录
|
||||
func _handlePasswordLogin() -> void:
|
||||
# 验证输入
|
||||
var validation = _validateInput()
|
||||
if not validation.valid:
|
||||
_showError(validation.error_code)
|
||||
return
|
||||
|
||||
# 显示加载状态
|
||||
_showLoading("登录中...")
|
||||
|
||||
# 获取输入
|
||||
var username = usernameInput.text.strip_edges()
|
||||
var password = passwordInput.text
|
||||
var remember = rememberPasswordCheckBox.button_pressed
|
||||
|
||||
# 调用 AuthManager 登录
|
||||
var result = await AuthManager.login(username, password, remember)
|
||||
|
||||
# 隐藏加载状态
|
||||
_hideLoading()
|
||||
|
||||
## 处理验证码登录
|
||||
func _handleCodeLogin() -> void:
|
||||
# TODO: 实现验证码登录逻辑
|
||||
_showError(ErrorCode.SERVER_ERROR, "验证码登录功能暂未开放")
|
||||
|
||||
## 用户名输入框回车事件
|
||||
func _onUsernameSubmitted(_text: String) -> void:
|
||||
passwordInput.grab_focus()
|
||||
|
||||
## 密码输入框回车事件
|
||||
func _onPasswordSubmitted(_text: String) -> void:
|
||||
_onEnterButtonPressed()
|
||||
|
||||
## 忘记密码按钮点击
|
||||
func _onForgotPasswordPressed() -> void:
|
||||
# TODO: 跳转到忘记密码页面
|
||||
_showError(ErrorCode.SERVER_ERROR, "忘记密码功能暂未开放")
|
||||
|
||||
## 注册按钮点击
|
||||
func _onRegisterPressed() -> void:
|
||||
# TODO: 跳转到注册页面
|
||||
_showError(ErrorCode.SERVER_ERROR, "注册功能暂未开放")
|
||||
|
||||
## 登录成功回调
|
||||
func _onLoginSuccess(userData: UserData) -> void:
|
||||
_hideLoading()
|
||||
_showSuccess("登录成功!")
|
||||
|
||||
# 延迟0.5秒后跳转到主菜单
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
get_tree().change_scene_to_file("res://scenes/main_menu_scene.tscn")
|
||||
|
||||
## 登录失败回调
|
||||
func _onLoginFailed(errorCode: int, errorMessage: String) -> void:
|
||||
_hideLoading()
|
||||
_showError(errorCode, errorMessage)
|
||||
|
||||
## 验证输入
|
||||
func _validateInput() -> Dictionary:
|
||||
var username = usernameInput.text.strip_edges()
|
||||
var password = passwordInput.text
|
||||
|
||||
# 检查是否为空
|
||||
if username.is_empty() or password.is_empty():
|
||||
return {
|
||||
"valid": false,
|
||||
"error_code": ErrorCode.INVALID_INPUT
|
||||
}
|
||||
|
||||
return {"valid": true}
|
||||
|
||||
## 显示加载状态
|
||||
func _showLoading(message: String = "加载中...") -> void:
|
||||
loadingLabel.text = message
|
||||
loadingOverlay.visible = true
|
||||
enterButton.disabled = true
|
||||
|
||||
## 隐藏加载状态
|
||||
func _hideLoading() -> void:
|
||||
loadingOverlay.visible = false
|
||||
enterButton.disabled = false
|
||||
|
||||
## 显示错误提示
|
||||
func _showError(errorCode: int, customMessage: String = "") -> void:
|
||||
var message = customMessage if not customMessage.is_empty() else ErrorCode.getMessage(errorCode)
|
||||
statusLabel.text = message
|
||||
statusLabel.modulate = Color.RED
|
||||
|
||||
# 3秒后自动清除
|
||||
await get_tree().create_timer(3.0).timeout
|
||||
_clearStatus()
|
||||
|
||||
## 显示成功提示
|
||||
func _showSuccess(message: String) -> void:
|
||||
statusLabel.text = message
|
||||
statusLabel.modulate = Color.GREEN
|
||||
|
||||
## 清除状态提示
|
||||
func _clearStatus() -> void:
|
||||
statusLabel.text = " "
|
||||
statusLabel.modulate = Color.WHITE
|
||||
1
scripts/ui/UI_Login.gd.uid
Normal file
1
scripts/ui/UI_Login.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c6f1awwaw1nim
|
||||
37
scripts/ui/UI_MainMenu.gd
Normal file
37
scripts/ui/UI_MainMenu.gd
Normal file
@@ -0,0 +1,37 @@
|
||||
extends Control
|
||||
|
||||
## 主菜单界面控制器
|
||||
## 显示用户信息并提供退出登录功能
|
||||
|
||||
@onready var welcomeLabel: Label = $centerContainer/vboxContainer/welcomeLabel
|
||||
@onready var userInfoLabel: Label = $centerContainer/vboxContainer/userInfoLabel
|
||||
@onready var logoutButton: Button = $centerContainer/vboxContainer/logoutButton
|
||||
|
||||
func _ready():
|
||||
# 连接退出登录按钮
|
||||
logoutButton.pressed.connect(_onLogoutPressed)
|
||||
|
||||
# 连接 AuthManager 信号
|
||||
AuthManager.logout_success.connect(_onLogoutSuccess)
|
||||
|
||||
# 显示用户信息
|
||||
_displayUserInfo()
|
||||
|
||||
## 显示用户信息
|
||||
func _displayUserInfo() -> void:
|
||||
var currentUser = AuthManager.getCurrentUser()
|
||||
|
||||
if currentUser != null:
|
||||
userInfoLabel.text = "用户名:%s\n等级:%d" % [currentUser.username, currentUser.level]
|
||||
else:
|
||||
userInfoLabel.text = "未登录"
|
||||
|
||||
## 退出登录按钮点击事件
|
||||
func _onLogoutPressed() -> void:
|
||||
# 调用 AuthManager 登出
|
||||
AuthManager.logout()
|
||||
|
||||
## 登出成功回调
|
||||
func _onLogoutSuccess() -> void:
|
||||
# 返回登录界面
|
||||
get_tree().change_scene_to_file("res://scenes/login_scene.tscn")
|
||||
1
scripts/ui/UI_MainMenu.gd.uid
Normal file
1
scripts/ui/UI_MainMenu.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cfpv68lwjwove
|
||||
Reference in New Issue
Block a user