extends Control # 信号定义 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 = $WhaleFrame # 登录表单 @onready var login_username: LineEdit = $CenterContainer/LoginPanel/VBoxContainer/LoginForm/UsernameContainer/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 active_toasts: Array = [] var toast_counter: int = 0 # 验证码状态 var verification_codes_sent: Dictionary = {} var code_cooldown: float = 60.0 var cooldown_timer: Timer = null var current_email: String = "" # 登录模式枚举 enum LoginMode { PASSWORD, # 密码登录模式 VERIFICATION # 验证码登录模式 } # 当前登录模式 var current_login_mode: LoginMode = LoginMode.PASSWORD # 网络请求管理 var active_request_ids: Array = [] func _ready(): connect_signals() show_login_panel() update_login_mode_ui() # 初始化登录模式UI await get_tree().process_frame print("认证系统已加载") test_network_connection() func test_network_connection(): print("=== 测试网络连接 ===") var request_id = NetworkManager.get_app_status(_on_network_test_response) if request_id != "": active_request_ids.append(request_id) print("网络测试请求已发送,ID: ", request_id) else: print("网络连接测试失败") func connect_signals(): # 主要按钮 main_btn.pressed.connect(_on_main_button_pressed) # 登录界面按钮 login_btn.pressed.connect(_on_login_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) # 登录表单失焦验证 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) # 实时输入验证 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) 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(): if current_login_mode == 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) # 这里需要根据实际UI结构调整 # 切换登录模式 func toggle_login_mode(): if current_login_mode == LoginMode.PASSWORD: current_login_mode = LoginMode.VERIFICATION else: current_login_mode = LoginMode.PASSWORD update_login_mode_ui() # 清空输入框 login_username.text = "" login_password.text = "" hide_field_error(login_username_error) hide_field_error(login_password_error) # ============ 按钮事件处理 ============ func _on_main_button_pressed(): # 根据当前登录模式执行不同的登录逻辑 if current_login_mode == LoginMode.PASSWORD: _execute_password_login() else: _execute_verification_login() func _execute_password_login(): if not validate_login_form(): return var username = login_username.text.strip_edges() var password = login_password.text show_loading(main_btn, "登录中...") show_toast('正在验证登录信息...', true) var request_id = NetworkManager.login(username, password, _on_login_response) if request_id != "": active_request_ids.append(request_id) else: restore_button(main_btn, "进入小镇") show_toast('网络请求失败', false) func _execute_verification_login(): var identifier = login_username.text.strip_edges() var verification_code = login_verification.text.strip_edges() if identifier.is_empty(): show_field_error(login_username_error, "请输入用户名/手机/邮箱") login_username.grab_focus() return if verification_code.is_empty(): show_field_error(login_verification_error, "请输入验证码") login_verification.grab_focus() return show_loading(main_btn, "登录中...") show_toast('正在验证验证码...', true) var request_id = NetworkManager.verification_code_login(identifier, verification_code, _on_verification_login_response) if request_id != "": active_request_ids.append(request_id) else: restore_button(main_btn, "进入小镇") show_toast('网络请求失败', false) func _on_login_pressed(): # 现在这个按钮用于切换登录模式 toggle_login_mode() func _on_register_pressed(): print("注册按钮被点击") if not validate_register_form(): print("注册表单验证失败") show_toast('请检查并完善注册信息', false) return print("注册表单验证通过,开始注册流程") var username = register_username.text.strip_edges() var email = register_email.text.strip_edges() var password = register_password.text var verification_code = verification_input.text.strip_edges() show_loading(register_btn, "注册中...") show_toast('正在创建账户...', true) # 直接调用注册接口,让服务器端处理验证码验证 send_register_request(username, email, password, verification_code) func _on_send_code_pressed(): var email = register_email.text.strip_edges() var email_validation = validate_email(email) if not email_validation.valid: show_toast(email_validation.message, false) register_email.grab_focus() return hide_field_error(register_email_error) # 检查冷却时间 var current_time = Time.get_time_dict_from_system() var current_timestamp = current_time.hour * 3600 + current_time.minute * 60 + current_time.second if verification_codes_sent.has(email): var email_data = verification_codes_sent[email] if email_data.sent and (current_timestamp - email_data.time) < code_cooldown: var remaining = code_cooldown - (current_timestamp - email_data.time) show_toast('该邮箱请等待 %d 秒后再次发送' % remaining, false) return if current_email != email: stop_current_cooldown() current_email = email if not verification_codes_sent.has(email): verification_codes_sent[email] = {} verification_codes_sent[email].sent = true verification_codes_sent[email].time = current_timestamp start_cooldown_timer(email) var request_id = NetworkManager.send_email_verification(email, _on_send_code_response) if request_id != "": active_request_ids.append(request_id) else: show_toast('网络请求失败', false) reset_verification_button() func _on_register_link_pressed(): show_register_panel() func _on_get_login_code_pressed(): var identifier = login_username.text.strip_edges() if identifier.is_empty(): show_field_error(login_username_error, "请先输入用户名/手机/邮箱") login_username.grab_focus() return show_loading(get_code_btn, "发送中...") show_toast('正在发送登录验证码...', true) var request_id = NetworkManager.send_login_verification_code(identifier, _on_send_login_code_response) if request_id != "": active_request_ids.append(request_id) else: restore_button(get_code_btn, "获取验证码") show_toast('网络请求失败', false) func _on_to_login_pressed(): show_login_panel() func _on_login_enter(_text: String): _on_login_pressed() func _on_forgot_password_pressed(): var identifier = login_username.text.strip_edges() if identifier.is_empty(): show_toast('请先输入邮箱或手机号', false) login_username.grab_focus() return if not is_valid_email(identifier) and not is_valid_phone(identifier): show_toast('请输入有效的邮箱或手机号', false) login_username.grab_focus() return if current_login_mode == LoginMode.PASSWORD: # 密码登录模式:发送密码重置验证码 show_loading(forgot_password_btn, "发送中...") show_toast('正在发送密码重置验证码...', true) var request_id = NetworkManager.forgot_password(identifier, _on_forgot_password_response) if request_id != "": active_request_ids.append(request_id) else: restore_button(forgot_password_btn, "忘记密码") show_toast('网络请求失败', false) else: # 验证码登录模式:发送登录验证码 show_loading(forgot_password_btn, "发送中...") show_toast('正在发送登录验证码...', true) var request_id = NetworkManager.send_login_verification_code(identifier, _on_send_login_code_response) if request_id != "": active_request_ids.append(request_id) else: restore_button(forgot_password_btn, "获取验证码") show_toast('网络请求失败', false) # ============ 网络响应处理 ============ func send_register_request(username: String, email: String, password: String, verification_code: String = ""): var request_id = NetworkManager.register(username, password, username, email, verification_code, _on_register_response) if request_id != "": active_request_ids.append(request_id) else: show_toast('网络请求失败', false) restore_button(register_btn, "注册") func _on_network_test_response(success: bool, data: Dictionary, error_info: Dictionary): print("=== 网络测试响应处理 ===") print("成功: ", success) print("数据: ", data) var result = ResponseHandler.handle_network_test_response(success, data, error_info) if result.should_show_toast: show_toast(result.message, result.success) func _on_login_response(success: bool, data: Dictionary, error_info: Dictionary): print("=== 登录响应处理 ===") print("成功: ", success) print("数据: ", data) print("错误信息: ", error_info) restore_button(main_btn, "进入小镇") var result = ResponseHandler.handle_login_response(success, data, error_info) if result.should_show_toast: show_toast(result.message, result.success) if result.success: var username = login_username.text.strip_edges() if data.has("data") and data.data.has("user") and data.data.user.has("username"): username = data.data.user.username login_username.text = "" login_password.text = "" hide_field_error(login_username_error) hide_field_error(login_password_error) await get_tree().create_timer(1.0).timeout login_success.emit(username) func _on_send_code_response(success: bool, data: Dictionary, error_info: Dictionary): print("=== 发送验证码响应处理 ===") print("成功: ", success) print("数据: ", data) var result = ResponseHandler.handle_send_verification_code_response(success, data, error_info) if result.should_show_toast: show_toast(result.message, result.success) if not result.success: reset_verification_button() func _on_send_login_code_response(success: bool, data: Dictionary, error_info: Dictionary): print("=== 发送登录验证码响应处理 ===") print("成功: ", success) print("数据: ", data) # 恢复按钮状态 if current_login_mode == LoginMode.PASSWORD: restore_button(forgot_password_btn, "忘记密码") else: restore_button(forgot_password_btn, "获取验证码") restore_button(get_code_btn, "获取验证码") var result = ResponseHandler.handle_send_login_code_response(success, data, error_info) if result.should_show_toast: show_toast(result.message, result.success) func _on_verification_login_response(success: bool, data: Dictionary, error_info: Dictionary): print("=== 验证码登录响应处理 ===") print("成功: ", success) print("数据: ", data) print("错误信息: ", error_info) restore_button(main_btn, "进入小镇") var result = ResponseHandler.handle_verification_code_login_response(success, data, error_info) if result.should_show_toast: show_toast(result.message, result.success) if result.success: var username = login_username.text.strip_edges() if data.has("data") and data.data.has("user") and data.data.user.has("username"): username = data.data.user.username login_username.text = "" hide_field_error(login_username_error) await get_tree().create_timer(1.0).timeout login_success.emit(username) func _on_forgot_password_response(success: bool, data: Dictionary, error_info: Dictionary): print("=== 忘记密码响应处理 ===") print("成功: ", success) print("数据: ", data) restore_button(forgot_password_btn, "忘记密码") # 使用通用的发送验证码响应处理 var result = ResponseHandler.handle_send_login_code_response(success, data, error_info) if result.should_show_toast: show_toast(result.message, result.success) if result.success: show_toast("密码重置验证码已发送,请查收邮件", true) func _on_register_response(success: bool, data: Dictionary, error_info: Dictionary): print("=== 注册响应处理 ===") print("成功: ", success) print("数据: ", data) restore_button(register_btn, "注册") var result = ResponseHandler.handle_register_response(success, data, error_info) if result.should_show_toast: show_toast(result.message, result.success) if result.success: clear_register_form() show_login_panel() login_username.text = register_username.text.strip_edges() # 使用注册时的用户名 # ============ 验证码冷却管理 ============ func start_cooldown_timer(email: String): if cooldown_timer != null: cooldown_timer.queue_free() current_email = email 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 input_email = register_email.text.strip_edges() if input_email != current_email: stop_current_cooldown() return if verification_codes_sent.has(current_email): var current_time = Time.get_time_dict_from_system() var current_timestamp = current_time.hour * 3600 + current_time.minute * 60 + current_time.second var email_data = verification_codes_sent[current_email] var remaining = code_cooldown - (current_timestamp - email_data.time) if remaining > 0: send_code_btn.text = "重新发送(%d)" % remaining else: send_code_btn.text = "重新发送" send_code_btn.disabled = false if cooldown_timer != null: cooldown_timer.queue_free() cooldown_timer = null current_email = "" func stop_current_cooldown(): if cooldown_timer != null: cooldown_timer.queue_free() cooldown_timer = null send_code_btn.disabled = false send_code_btn.text = "发送验证码" current_email = "" func reset_verification_button(): if current_email != "" and verification_codes_sent.has(current_email): verification_codes_sent[current_email].sent = false stop_current_cooldown() func clear_register_form(): register_username.text = "" register_email.text = "" register_password.text = "" register_confirm.text = "" verification_input.text = "" stop_current_cooldown() verification_codes_sent.clear() 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) # ============ Toast消息系统 ============ # 检测文本是否包含中文字符 func contains_chinese(text: String) -> bool: for i in range(text.length()): var char_code = text.unicode_at(i) # 中文字符的Unicode范围 if (char_code >= 0x4E00 and char_code <= 0x9FFF) or \ (char_code >= 0x3400 and char_code <= 0x4DBF) or \ (char_code >= 0x20000 and char_code <= 0x2A6DF): return true return false func show_toast(message: String, is_success: bool = true): print("显示Toast消息: ", message, " 成功: ", is_success) if toast_container == null: print("错误: toast_container 节点不存在") return # 异步创建Toast实例 create_toast_instance(message, is_success) func create_toast_instance(message: String, is_success: bool): toast_counter += 1 # Web平台字体处理 var is_web = OS.get_name() == "Web" # 1. 创建Toast Panel(方框UI) var toast_panel = Panel.new() toast_panel.name = "Toast_" + str(toast_counter) # 设置Toast样式 var style = StyleBoxFlat.new() if is_success: style.bg_color = Color(0.15, 0.7, 0.15, 0.95) style.border_color = Color(0.2, 0.9, 0.2, 0.9) else: style.bg_color = Color(0.7, 0.15, 0.15, 0.95) style.border_color = Color(0.9, 0.2, 0.2, 0.9) style.border_width_left = 3 style.border_width_top = 3 style.border_width_right = 3 style.border_width_bottom = 3 style.corner_radius_top_left = 12 style.corner_radius_top_right = 12 style.corner_radius_bottom_left = 12 style.corner_radius_bottom_right = 12 style.shadow_color = Color(0, 0, 0, 0.3) style.shadow_size = 4 style.shadow_offset = Vector2(2, 2) toast_panel.add_theme_stylebox_override("panel", style) # 设置Toast基本尺寸 var toast_width = 320 toast_panel.size = Vector2(toast_width, 60) # 2. 创建VBoxContainer var vbox = VBoxContainer.new() vbox.add_theme_constant_override("separation", 0) vbox.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) vbox.alignment = BoxContainer.ALIGNMENT_CENTER # 3. 创建CenterContainer var center_container = CenterContainer.new() center_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL center_container.size_flags_vertical = Control.SIZE_SHRINK_CENTER # 4. 创建Label(文字控件) var text_label = Label.new() text_label.text = message text_label.add_theme_color_override("font_color", Color(1, 1, 1, 1)) text_label.add_theme_font_size_override("font_size", 14) # 平台特定的字体处理 if is_web: print("Web平台Toast字体处理") # Web平台使用主题文件 var chinese_theme = load("res://assets/ui/chinese_theme.tres") if chinese_theme: text_label.theme = chinese_theme print("Web平台应用中文主题") else: print("Web平台中文主题加载失败") else: print("桌面平台Toast字体处理") # 桌面平台直接加载中文字体 var desktop_chinese_font = load("res://assets/fonts/msyh.ttc") if desktop_chinese_font: text_label.add_theme_font_override("font", desktop_chinese_font) print("桌面平台使用中文字体") text_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART text_label.custom_minimum_size = Vector2(280, 0) text_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER text_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER # 组装控件层级 center_container.add_child(text_label) vbox.add_child(center_container) toast_panel.add_child(vbox) # 计算位置 var margin = 20 var start_x = get_viewport().get_visible_rect().size.x var final_x = get_viewport().get_visible_rect().size.x - toast_width - margin # 计算Y位置 var y_position = margin for existing_toast in active_toasts: if is_instance_valid(existing_toast): y_position += existing_toast.size.y + 15 # 设置初始位置 toast_panel.position = Vector2(start_x, y_position) # 添加到容器 toast_container.add_child(toast_panel) active_toasts.append(toast_panel) # 等待一帧让布局系统计算尺寸 await get_tree().process_frame # 让Toast高度自适应内容 var content_size = vbox.get_combined_minimum_size() var final_height = max(60, content_size.y + 20) # 最小60,加20像素边距 toast_panel.size.y = final_height # 重新排列所有Toast rearrange_toasts() # 开始动画 animate_toast_in(toast_panel, final_x) func animate_toast_in(toast_panel: Panel, final_x: float): var tween = create_tween() tween.set_ease(Tween.EASE_OUT) tween.set_trans(Tween.TRANS_BACK) tween.parallel().tween_property(toast_panel, "position:x", final_x, 0.6) tween.parallel().tween_property(toast_panel, "modulate:a", 1.0, 0.4) toast_panel.modulate.a = 0.0 await get_tree().create_timer(3.0).timeout animate_toast_out(toast_panel) func animate_toast_out(toast_panel: Panel): if not is_instance_valid(toast_panel): return var tween = create_tween() tween.set_ease(Tween.EASE_IN) tween.set_trans(Tween.TRANS_QUART) var end_x = get_viewport().get_visible_rect().size.x + 50 tween.parallel().tween_property(toast_panel, "position:x", end_x, 0.4) tween.parallel().tween_property(toast_panel, "modulate:a", 0.0, 0.3) await tween.finished cleanup_toast(toast_panel) func cleanup_toast(toast_panel: Panel): if not is_instance_valid(toast_panel): return active_toasts.erase(toast_panel) rearrange_toasts() toast_panel.queue_free() func rearrange_toasts(): var margin = 20 var current_y = margin for i in range(active_toasts.size()): var toast = active_toasts[i] if is_instance_valid(toast): var tween = create_tween() tween.tween_property(toast, "position:y", current_y, 0.2) current_y += toast.size.y + 15 # ============ UI工具方法 ============ func show_loading(button: Button, loading_text: String): button.disabled = true button.text = loading_text func restore_button(button: Button, original_text: String): button.disabled = false button.text = original_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 is_valid_email(email: String) -> bool: return StringUtils.is_valid_email(email) func is_valid_phone(phone: String) -> bool: var regex = RegEx.new() regex.compile("^\\+?[1-9]\\d{1,14}$") return regex.search(phone) != null # ============ 表单验证方法 ============ func validate_username(username: String) -> Dictionary: var result = {"valid": false, "message": ""} if username.is_empty(): result.message = "用户名不能为空" return result if not StringUtils.is_valid_username(username): if username.length() > 50: result.message = "用户名长度不能超过50字符" else: result.message = "用户名只能包含字母、数字和下划线" return result result.valid = true return result func validate_email(email: String) -> Dictionary: var result = {"valid": false, "message": ""} if email.is_empty(): result.message = "邮箱不能为空" return result if not StringUtils.is_valid_email(email): result.message = "请输入有效的邮箱地址" return result result.valid = true return result func validate_password(password: String) -> Dictionary: return StringUtils.validate_password_strength(password) func validate_confirm_password(password: String, confirm: String) -> Dictionary: var result = {"valid": false, "message": ""} if confirm.is_empty(): result.message = "确认密码不能为空" return result if password != confirm: result.message = "两次输入的密码不一致" return result result.valid = true return result func validate_verification_code(code: String) -> Dictionary: var result = {"valid": false, "message": ""} if code.is_empty(): result.message = "验证码不能为空" return result if code.length() != 6: result.message = "验证码必须是6位数字" return result for i in range(code.length()): var character = code[i] if not (character >= '0' and character <= '9'): result.message = "验证码必须是6位数字" return result result.valid = true return result # ============ 表单验证事件 ============ 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() if verification_code.is_empty(): show_field_error(login_verification_error, "验证码不能为空") elif verification_code.length() != 6: show_field_error(login_verification_error, "验证码必须是6位数字") elif not verification_code.is_valid_int(): show_field_error(login_verification_error, "验证码只能包含数字") else: hide_field_error(login_verification_error) func _on_register_username_focus_exited(): var username = register_username.text.strip_edges() var validation = 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 = 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 = 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 = 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 = 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 validate_login_form() -> bool: var is_valid = true var username = login_username.text.strip_edges() var password = login_password.text if username.is_empty(): show_field_error(login_username_error, "用户名不能为空") is_valid = false else: hide_field_error(login_username_error) if password.is_empty(): show_field_error(login_password_error, "密码不能为空") is_valid = false else: hide_field_error(login_password_error) return is_valid func validate_register_form() -> bool: print("开始验证注册表单") var is_valid = true var username = register_username.text.strip_edges() var email = register_email.text.strip_edges() var password = register_password.text var confirm = register_confirm.text var verification_code = verification_input.text.strip_edges() print("表单数据: 用户名='%s', 邮箱='%s', 密码长度=%d, 确认密码长度=%d, 验证码='%s'" % [username, email, password.length(), confirm.length(), verification_code]) var username_validation = validate_username(username) if not username_validation.valid: print("用户名验证失败: ", username_validation.message) show_field_error(register_username_error, username_validation.message) is_valid = false else: hide_field_error(register_username_error) var email_validation = validate_email(email) if not email_validation.valid: print("邮箱验证失败: ", email_validation.message) show_field_error(register_email_error, email_validation.message) is_valid = false else: hide_field_error(register_email_error) var password_validation = validate_password(password) if not password_validation.valid: print("密码验证失败: ", password_validation.message) show_field_error(register_password_error, password_validation.message) is_valid = false else: hide_field_error(register_password_error) var confirm_validation = validate_confirm_password(password, confirm) if not confirm_validation.valid: print("确认密码验证失败: ", confirm_validation.message) show_field_error(register_confirm_error, confirm_validation.message) is_valid = false else: hide_field_error(register_confirm_error) var code_validation = validate_verification_code(verification_code) if not code_validation.valid: print("验证码格式验证失败: ", code_validation.message) show_field_error(verification_error, code_validation.message) is_valid = false else: hide_field_error(verification_error) var current_email_input = register_email.text.strip_edges() var has_sent_code = false if verification_codes_sent.has(current_email_input): var email_data = verification_codes_sent[current_email_input] has_sent_code = email_data.get("sent", false) if not has_sent_code: print("当前邮箱验证码未发送,email = ", current_email_input) show_toast("请先获取邮箱验证码", false) is_valid = false print("表单验证结果: ", is_valid) return is_valid # ============ 资源清理 ============ func _exit_tree(): for request_id in active_request_ids: NetworkManager.cancel_request(request_id) active_request_ids.clear() if cooldown_timer != null: cooldown_timer.queue_free() cooldown_timer = null func _input(event): if event.is_action_pressed("ui_cancel"): get_tree().quit()