Files
whale-town-front/scenes/ui/AuthScene.gd
王浩 6e70aac0b9 fix: 修复聊天消息显示问题
- AuthScene: 修复节点路径错误 (WhaleFrame, UsernameInput)
- ChatManager: 修复 timestamp 类型转换 (String -> float)
- ChatMessage: 修复节点引用获取方式和 UI 显示
- ChatUI: 优化消息列表布局对齐
2026-01-14 17:10:48 +08:00

616 lines
22 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
extends Control
# ============================================================================
# AuthScene.gd - 认证场景视图控制器
# ============================================================================
# 认证系统的视图层控制器只负责UI相关的操作
#
# 核心职责:
# - UI控件的显示和隐藏
# - 用户输入的收集和传递
# - 错误提示的显示
# - 按钮状态的管理
# - Toast消息的显示
#
# 业务逻辑:
# - 所有业务逻辑都委托给AuthManager处理
# - 通过信号与AuthManager通信
# - 不包含任何验证逻辑或网络请求
#
# 注意事项:
# - 这是纯视图层,不处理业务逻辑
# - 通过事件系统与业务层解耦
# - 专注于用户体验和界面交互
# ============================================================================
# ============ 信号定义 ============
# 登录成功信号 - 传递给上层场景
signal login_success(username: String)
# ============ UI节点引用 ============
# 主要容器和背景
@onready var background_image: TextureRect = $BackgroundImage # 背景图片
@onready var login_panel: Panel = $CenterContainer/LoginPanel # 登录面板
@onready var register_panel: Panel = $CenterContainer/RegisterPanel # 注册面板
@onready var title_label: Label = $CenterContainer/LoginPanel/VBoxContainer/TitleLabel # 标题标签
@onready var subtitle_label: Label = $CenterContainer/LoginPanel/VBoxContainer/SubtitleLabel # 副标题标签
@onready var whale_frame: TextureRect = $CenterContainer/WhaleFrame # 鲸鱼装饰框
# 登录表单输入控件
@onready var login_username: LineEdit = $CenterContainer/LoginPanel/VBoxContainer/LoginForm/UsernameInput
@onready var login_password: LineEdit = $CenterContainer/LoginPanel/VBoxContainer/LoginForm/PasswordContainer/PasswordInput
@onready var login_verification: LineEdit = $CenterContainer/LoginPanel/VBoxContainer/LoginForm/VerificationContainer/VerificationInputContainer/VerificationInput
# 登录表单错误提示标签
@onready var login_username_error: Label = $CenterContainer/LoginPanel/VBoxContainer/LoginForm/UsernameContainer/UsernameLabelContainer/UsernameError
@onready var login_password_error: Label = $CenterContainer/LoginPanel/VBoxContainer/LoginForm/PasswordContainer/PasswordLabelContainer/PasswordError
@onready var login_verification_error: Label = $CenterContainer/LoginPanel/VBoxContainer/LoginForm/VerificationContainer/VerificationLabelContainer/VerificationError
# 登录表单容器(用于显示/隐藏)
@onready var password_container: VBoxContainer = $CenterContainer/LoginPanel/VBoxContainer/LoginForm/PasswordContainer
@onready var verification_container: VBoxContainer = $CenterContainer/LoginPanel/VBoxContainer/LoginForm/VerificationContainer
# 登录相关按钮
@onready var get_code_btn: Button = $CenterContainer/LoginPanel/VBoxContainer/LoginForm/VerificationContainer/VerificationInputContainer/GetCodeBtn
@onready var main_btn: Button = $CenterContainer/LoginPanel/VBoxContainer/MainButton
@onready var login_btn: Button = $CenterContainer/LoginPanel/VBoxContainer/ButtonContainer/LoginBtn
@onready var forgot_password_btn: Button = $CenterContainer/LoginPanel/VBoxContainer/BottomLinks/ForgotPassword
@onready var register_link_btn: Button = $CenterContainer/LoginPanel/VBoxContainer/BottomLinks/RegisterLink
# 注册表单输入控件
@onready var register_username: LineEdit = $CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/UsernameContainer/UsernameInput
@onready var register_email: LineEdit = $CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/EmailContainer/EmailInput
@onready var register_password: LineEdit = $CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/PasswordContainer/PasswordInput
@onready var register_confirm: LineEdit = $CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/ConfirmContainer/ConfirmInput
@onready var verification_input: LineEdit = $CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/VerificationContainer/VerificationInputContainer/VerificationInput
# 注册相关按钮
@onready var send_code_btn: Button = $CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/VerificationContainer/VerificationInputContainer/SendCodeBtn
@onready var register_btn: Button = $CenterContainer/RegisterPanel/VBoxContainer/ButtonContainer/RegisterBtn
@onready var to_login_btn: Button = $CenterContainer/RegisterPanel/VBoxContainer/ButtonContainer/ToLoginBtn
# 注册表单错误提示标签
@onready var register_username_error: Label = $CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/UsernameContainer/UsernameLabelContainer/UsernameError
@onready var register_email_error: Label = $CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/EmailContainer/EmailLabelContainer/EmailError
@onready var register_password_error: Label = $CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/PasswordContainer/PasswordLabelContainer/PasswordError
@onready var register_confirm_error: Label = $CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/ConfirmContainer/ConfirmLabelContainer/ConfirmError
@onready var verification_error: Label = $CenterContainer/RegisterPanel/VBoxContainer/RegisterForm/VerificationContainer/VerificationLabelContainer/VerificationError
# Toast消息系统
@onready var toast_container: Control = $ToastContainer # Toast消息容器
# ============ 成员变量 ============
# 业务逻辑管理器
var auth_manager: AuthManager
# Toast消息管理器
var toast_manager: ToastManager
# 验证码冷却计时器
var cooldown_timer: Timer = null
# 当前登录模式(从管理器同步)
var current_login_mode: AuthManager.LoginMode = AuthManager.LoginMode.PASSWORD
# ============ 生命周期方法 ============
# 初始化认证场景
func _ready():
_setup_controllers() # 初始化控制器
_connect_signals() # 连接信号
_setup_ui() # 设置UI初始状态
print("认证场景视图已加载")
# 测试网络连接
auth_manager.test_network_connection()
# 设置控制器
func _setup_controllers():
# 创建业务逻辑管理器
auth_manager = AuthManager.new()
# 创建Toast管理器
toast_manager = ToastManager.new()
toast_manager.setup(toast_container)
# 连接管理器信号
_connect_controller_signals()
# 连接管理器信号
func _connect_controller_signals():
# 登录相关信号
auth_manager.login_success.connect(_on_controller_login_success)
auth_manager.login_failed.connect(_on_controller_login_failed)
# 注册相关信号
auth_manager.register_success.connect(_on_controller_register_success)
auth_manager.register_failed.connect(_on_controller_register_failed)
# 验证码相关信号
auth_manager.verification_code_sent.connect(_on_controller_verification_code_sent)
auth_manager.verification_code_failed.connect(_on_controller_verification_code_failed)
# 表单验证信号
auth_manager.form_validation_failed.connect(_on_controller_form_validation_failed)
# 网络状态信号
auth_manager.network_status_changed.connect(_on_controller_network_status_changed)
# 按钮状态信号
auth_manager.button_state_changed.connect(_on_controller_button_state_changed)
# Toast消息信号
auth_manager.show_toast_message.connect(_on_controller_show_toast_message)
# 设置UI初始状态
func _setup_ui():
show_login_panel()
_update_login_mode_ui()
# 连接UI信号
func _connect_signals():
# 主要按钮信号
main_btn.pressed.connect(_on_main_button_pressed)
# 登录界面按钮信号
login_btn.pressed.connect(_on_login_mode_toggle_pressed)
forgot_password_btn.pressed.connect(_on_forgot_password_pressed)
register_link_btn.pressed.connect(_on_register_link_pressed)
get_code_btn.pressed.connect(_on_get_login_code_pressed)
# 注册界面按钮信号
register_btn.pressed.connect(_on_register_pressed)
to_login_btn.pressed.connect(_on_to_login_pressed)
send_code_btn.pressed.connect(_on_send_code_pressed)
# 回车键快捷登录
login_password.text_submitted.connect(_on_login_enter)
# 表单失焦验证事件
_connect_validation_signals()
# 实时输入验证事件
_connect_input_change_signals()
# 连接表单验证信号
func _connect_validation_signals():
# 登录表单
login_username.focus_exited.connect(_on_login_username_focus_exited)
login_password.focus_exited.connect(_on_login_password_focus_exited)
login_verification.focus_exited.connect(_on_login_verification_focus_exited)
# 注册表单
register_username.focus_exited.connect(_on_register_username_focus_exited)
register_email.focus_exited.connect(_on_register_email_focus_exited)
register_password.focus_exited.connect(_on_register_password_focus_exited)
register_confirm.focus_exited.connect(_on_register_confirm_focus_exited)
verification_input.focus_exited.connect(_on_verification_focus_exited)
# 连接输入变化信号
func _connect_input_change_signals():
register_username.text_changed.connect(_on_register_username_text_changed)
register_email.text_changed.connect(_on_register_email_text_changed)
register_password.text_changed.connect(_on_register_password_text_changed)
register_confirm.text_changed.connect(_on_register_confirm_text_changed)
verification_input.text_changed.connect(_on_verification_text_changed)
# ============ UI面板管理 ============
# 显示登录面板
func show_login_panel():
login_panel.visible = true
register_panel.visible = false
login_username.grab_focus()
# 显示注册面板
func show_register_panel():
login_panel.visible = false
register_panel.visible = true
register_username.grab_focus()
# ============ 登录模式管理 ============
# 更新登录模式UI
func _update_login_mode_ui():
current_login_mode = auth_manager.get_current_login_mode()
if current_login_mode == AuthManager.LoginMode.PASSWORD:
# 密码登录模式
login_btn.text = "验证码登录"
forgot_password_btn.text = "忘记密码"
# 显示密码输入框,隐藏验证码输入框
password_container.visible = true
verification_container.visible = false
# 清空验证码输入框和错误提示
login_verification.text = ""
_hide_field_error(login_verification_error)
else: # VERIFICATION mode
# 验证码登录模式
login_btn.text = "密码登录"
forgot_password_btn.text = "获取验证码"
# 隐藏密码输入框,显示验证码输入框
password_container.visible = false
verification_container.visible = true
# 清空密码输入框和错误提示
login_password.text = ""
_hide_field_error(login_password_error)
# ============ 按钮事件处理 ============
# 主按钮点击事件
func _on_main_button_pressed():
if current_login_mode == AuthManager.LoginMode.PASSWORD:
_execute_password_login()
else:
_execute_verification_login()
# 执行密码登录
func _execute_password_login():
var username = login_username.text.strip_edges()
var password = login_password.text
auth_manager.execute_password_login(username, password)
# 执行验证码登录
func _execute_verification_login():
var identifier = login_username.text.strip_edges()
var verification_code = login_verification.text.strip_edges()
auth_manager.execute_verification_login(identifier, verification_code)
# 登录模式切换按钮
func _on_login_mode_toggle_pressed():
auth_manager.toggle_login_mode()
_update_login_mode_ui()
# 清空输入框和错误提示
login_username.text = ""
login_password.text = ""
login_verification.text = ""
_hide_field_error(login_username_error)
_hide_field_error(login_password_error)
_hide_field_error(login_verification_error)
# 注册按钮点击事件
func _on_register_pressed():
var username = register_username.text.strip_edges()
var email = register_email.text.strip_edges()
var password = register_password.text
var confirm_password = register_confirm.text
var verification_code = verification_input.text.strip_edges()
auth_manager.execute_register(username, email, password, confirm_password, verification_code)
# 发送邮箱验证码按钮
func _on_send_code_pressed():
var email = register_email.text.strip_edges()
auth_manager.send_email_verification_code(email)
# 开始冷却计时器
_start_cooldown_timer(email)
# 获取登录验证码按钮
func _on_get_login_code_pressed():
var identifier = login_username.text.strip_edges()
auth_manager.send_login_verification_code(identifier)
# 忘记密码按钮
func _on_forgot_password_pressed():
var identifier = login_username.text.strip_edges()
if current_login_mode == AuthManager.LoginMode.PASSWORD:
# 密码登录模式:发送密码重置验证码
auth_manager.send_password_reset_code(identifier)
else:
# 验证码登录模式:发送登录验证码
auth_manager.send_login_verification_code(identifier)
# 跳转到注册面板
func _on_register_link_pressed():
show_register_panel()
# 返回登录面板
func _on_to_login_pressed():
show_login_panel()
# 回车键登录
func _on_login_enter(_text: String):
_on_main_button_pressed()
# ============ 控制器信号处理 ============
# 登录成功处理
func _on_controller_login_success(username: String) -> void:
# 清空表单
login_username.text = ""
login_password.text = ""
login_verification.text = ""
_hide_field_error(login_username_error)
_hide_field_error(login_password_error)
_hide_field_error(login_verification_error)
# 设置 token 给 ChatManager用于 WebSocket 聊天认证)
var token: String = auth_manager.get_access_token()
if not token.is_empty():
ChatManager.set_game_token(token)
print("✅ 已设置 ChatManager token: ", token.substr(0, 20) + "...")
# 发送登录成功信号给上层
login_success.emit(username)
# 登录失败处理
func _on_controller_login_failed(_message: String):
# 登录失败时不需要额外处理Toast已经显示了错误信息
pass
# 注册成功处理
func _on_controller_register_success(_message: String):
# 清空注册表单
_clear_register_form()
# 切换到登录面板
show_login_panel()
# 将注册时的用户名填入登录框
login_username.text = register_username.text.strip_edges()
# 注册失败处理
func _on_controller_register_failed(_message: String):
# 注册失败时不需要额外处理Toast已经显示了错误信息
pass
# 验证码发送成功处理
func _on_controller_verification_code_sent(_message: String):
# 验证码发送成功,冷却计时器已经在按钮点击时启动
pass
# 验证码发送失败处理
func _on_controller_verification_code_failed(_message: String):
# 重置验证码按钮状态
_reset_verification_button()
# 表单验证失败处理
func _on_controller_form_validation_failed(field: String, message: String):
match field:
"username":
_show_field_error(login_username_error, message)
login_username.grab_focus()
"password":
_show_field_error(login_password_error, message)
login_password.grab_focus()
"verification":
if current_login_mode == AuthManager.LoginMode.VERIFICATION:
_show_field_error(login_verification_error, message)
login_verification.grab_focus()
else:
_show_field_error(verification_error, message)
verification_input.grab_focus()
"email":
_show_field_error(register_email_error, message)
register_email.grab_focus()
"confirm":
_show_field_error(register_confirm_error, message)
register_confirm.grab_focus()
# 网络状态变化处理
func _on_controller_network_status_changed(network_connected: bool, message: String):
# 可以在这里添加网络状态指示器
print("网络状态: ", "连接" if network_connected else "断开", " - ", message)
# 按钮状态变化处理
func _on_controller_button_state_changed(button_name: String, is_loading: bool, text: String):
match button_name:
"main_btn":
_set_button_state(main_btn, is_loading, text)
"register_btn":
_set_button_state(register_btn, is_loading, text)
"get_code_btn":
_set_button_state(get_code_btn, is_loading, text)
"forgot_password_btn":
_set_button_state(forgot_password_btn, is_loading, text)
# Toast消息处理
func _on_controller_show_toast_message(message: String, is_success: bool):
toast_manager.show_toast(message, is_success)
# ============ 验证码冷却管理 ============
# 开始冷却计时器
func _start_cooldown_timer(_email: String):
if cooldown_timer != null:
cooldown_timer.queue_free()
send_code_btn.disabled = true
send_code_btn.text = "重新发送(60)"
cooldown_timer = Timer.new()
add_child(cooldown_timer)
cooldown_timer.wait_time = 1.0
cooldown_timer.timeout.connect(_on_cooldown_timer_timeout)
cooldown_timer.start()
# 冷却计时器超时处理
func _on_cooldown_timer_timeout():
var remaining_time = auth_manager.get_remaining_cooldown_time(register_email.text.strip_edges())
if remaining_time > 0:
send_code_btn.text = "重新发送(%d)" % remaining_time
else:
send_code_btn.text = "重新发送"
send_code_btn.disabled = false
if cooldown_timer != null:
cooldown_timer.queue_free()
cooldown_timer = null
# 重置验证码按钮状态
func _reset_verification_button():
if cooldown_timer != null:
cooldown_timer.queue_free()
cooldown_timer = null
send_code_btn.disabled = false
send_code_btn.text = "发送验证码"
# ============ UI工具方法 ============
# 设置按钮状态
func _set_button_state(button: Button, is_loading: bool, text: String):
button.disabled = is_loading
button.text = text
# 显示字段错误
func _show_field_error(error_label: Label, message: String):
error_label.text = message
error_label.visible = true
# 隐藏字段错误
func _hide_field_error(error_label: Label):
error_label.visible = false
# 清空注册表单
func _clear_register_form():
register_username.text = ""
register_email.text = ""
register_password.text = ""
register_confirm.text = ""
verification_input.text = ""
_reset_verification_button()
_hide_field_error(register_username_error)
_hide_field_error(register_email_error)
_hide_field_error(register_password_error)
_hide_field_error(register_confirm_error)
_hide_field_error(verification_error)
# ============ 表单验证事件处理 ============
# 登录用户名失焦验证
func _on_login_username_focus_exited():
var username = login_username.text.strip_edges()
if username.is_empty():
_show_field_error(login_username_error, "用户名不能为空")
else:
_hide_field_error(login_username_error)
# 登录密码失焦验证
func _on_login_password_focus_exited():
var password = login_password.text
if password.is_empty():
_show_field_error(login_password_error, "密码不能为空")
else:
_hide_field_error(login_password_error)
# 登录验证码失焦验证
func _on_login_verification_focus_exited():
var verification_code = login_verification.text.strip_edges()
var validation = auth_manager.validate_verification_code(verification_code)
if not validation.valid:
_show_field_error(login_verification_error, validation.message)
else:
_hide_field_error(login_verification_error)
# 注册用户名失焦验证
func _on_register_username_focus_exited():
var username = register_username.text.strip_edges()
var validation = auth_manager.validate_username(username)
if not validation.valid:
_show_field_error(register_username_error, validation.message)
else:
_hide_field_error(register_username_error)
# 注册邮箱失焦验证
func _on_register_email_focus_exited():
var email = register_email.text.strip_edges()
var validation = auth_manager.validate_email(email)
if not validation.valid:
_show_field_error(register_email_error, validation.message)
else:
_hide_field_error(register_email_error)
# 注册密码失焦验证
func _on_register_password_focus_exited():
var password = register_password.text
var validation = auth_manager.validate_password(password)
if not validation.valid:
_show_field_error(register_password_error, validation.message)
else:
_hide_field_error(register_password_error)
# 如果确认密码不为空,重新验证确认密码
if not register_confirm.text.is_empty():
_on_register_confirm_focus_exited()
# 注册确认密码失焦验证
func _on_register_confirm_focus_exited():
var password = register_password.text
var confirm = register_confirm.text
var validation = auth_manager.validate_confirm_password(password, confirm)
if not validation.valid:
_show_field_error(register_confirm_error, validation.message)
else:
_hide_field_error(register_confirm_error)
# 验证码失焦验证
func _on_verification_focus_exited():
var code = verification_input.text.strip_edges()
var validation = auth_manager.validate_verification_code(code)
if not validation.valid:
_show_field_error(verification_error, validation.message)
else:
_hide_field_error(verification_error)
# ============ 实时输入验证事件 ============
# 注册用户名输入变化
func _on_register_username_text_changed(new_text: String):
if register_username_error.visible and not new_text.is_empty():
_hide_field_error(register_username_error)
# 注册邮箱输入变化
func _on_register_email_text_changed(new_text: String):
if register_email_error.visible and not new_text.is_empty():
_hide_field_error(register_email_error)
# 注册密码输入变化
func _on_register_password_text_changed(new_text: String):
if register_password_error.visible and not new_text.is_empty():
_hide_field_error(register_password_error)
# 注册确认密码输入变化
func _on_register_confirm_text_changed(new_text: String):
if register_confirm_error.visible and not new_text.is_empty():
_hide_field_error(register_confirm_error)
# 验证码输入变化
func _on_verification_text_changed(new_text: String):
if verification_error.visible and not new_text.is_empty():
_hide_field_error(verification_error)
# ============ 资源清理 ============
# 节点销毁时清理资源
func _exit_tree():
# 清理控制器
if auth_manager:
auth_manager.cleanup()
# 清理Toast管理器
if toast_manager:
toast_manager.clear_all_toasts()
# 清理计时器
if cooldown_timer != null:
cooldown_timer.queue_free()
cooldown_timer = null
# 处理ESC键退出
func _input(event):
if event.is_action_pressed("ui_cancel"):
get_tree().quit()