# 登录模块 (App/Login) ## 📋 模块概述 用户登录功能模块,包括登录界面 UI 和登录业务逻辑,登录成功后切换到游戏状态。 ## 🎯 核心特性 - ✅ 登录界面 UI - ✅ 账号输入处理 - ✅ 登录按钮交互 - ✅ 网络登录请求 - ✅ 状态切换 - ✅ 错误处理 ## 🗂️ 文件结构 ``` App/Login/ └── UILogin.ts # 登录界面组件 ``` ## 📘 核心类详解 ### UILogin - 登录界面 **职责**: 显示登录 UI,处理用户输入,发送登录请求 ```typescript @ccclass('UILogin') class UILogin extends UIBase { private _inputAccount: EditBox | null; // 账号输入框 private _btnLogin: Node | null; // 登录按钮 private _isLogging: boolean; // 是否正在登录中 // 获取 UI 资源路径 onGetUrl(): string { return 'res/UI/Login/UILogin'; } // UI 初始化 async onStart(params?: any): Promise; // 查找 UI 节点 private findNodes(): void; // 绑定事件 private bindEvents(): void; // 解绑事件 private unbindEvents(): void; // 登录按钮点击 private async onLoginClick(): Promise; // 执行登录 private async login(account: string): Promise; // UI 清理 onEnd(): void; } ``` ## 🎨 UI 结构要求 ### 资源路径 `assets/res/UI/Login/UILogin.prefab` ### 节点结构 ``` UILogin (根节点) ├── mid/ │ └── input_account (EditBox) # 账号输入框 └── btn_login # 登录按钮 ``` ### 组件要求 | 节点路径 | 必需组件 | 说明 | |---------|---------|------| | `mid/input_account` | EditBox | 账号输入框 | | `btn_login` | - | 登录按钮(监听点击事件) | ## 📝 使用指南 ### 1. 创建 UI 预制体 1. 在 Cocos Creator 中创建 UI 预制体 2. 路径: `assets/res/UI/Login/UILogin.prefab` 3. 添加必需节点和组件 4. 保存预制体 ### 2. 在登录状态中使用 ```typescript import { UIMgr } from '../../Framework/UI/UIMgr'; import { UILogin } from '../Login/UILogin'; // 在 AppStatusLogin 中加载登录 UI export class AppStatusLogin extends BaseState { async onEnter(params?: any): Promise { // 加载并显示登录界面 await UIMgr.getInstance().load(UILogin); } onExit(): void { // 隐藏登录界面 UIMgr.getInstance().hide(UILogin); } } ``` ### 3. 登录流程 ``` 用户打开应用 ↓ AppStatusLogin.onEnter() ↓ 加载并显示 UILogin ↓ 用户输入账号 ↓ 点击登录按钮 ↓ onLoginClick() ↓ login(account) ↓ 调用 NetManager.callApi('Login', ...) ↓ 收到服务器响应 ↓ 登录成功? ├─ 是: 切换到 Game 状态 └─ 否: 显示错误提示 ``` ## 📡 网络协议 ### 登录请求 (ReqLogin) ```typescript interface ReqLogin { account: string; // 账号 password?: string; // 密码(可选) } ``` ### 登录响应 (ResLogin) ```typescript interface ResLogin { success: boolean; // 是否成功 message?: string; // 消息 player?: { // 玩家信息 id: string; name: string; position: { x: number; y: number; z: number }; spawnPoint: { x: number; y: number; z: number }; hp: number; maxHp: number; isAlive: boolean; createdAt: number; lastLoginAt: number; }; isNewPlayer?: boolean; // 是否新玩家 } ``` ### 使用示例 ```typescript import { NetManager } from '../../Framework/Net/NetManager'; import { ReqLogin, ResLogin } from '../../Framework/Net/LoginProtocol'; // 发送登录请求 const result = await NetManager.getInstance().callApi('Login', { account: 'player123' }); if (result && result.success) { console.log('登录成功:', result.player); // 切换到游戏状态 AppStatusManager.getInstance().changeState('Game', { player: result.player, isNewPlayer: result.isNewPlayer }); } ``` ## 🔄 完整登录流程 ```typescript // UILogin.ts 中的登录流程 // 1. 用户点击登录按钮 private async onLoginClick(): Promise { // 防止重复点击 if (this._isLogging) { return; } // 获取输入的账号 const account = this._inputAccount?.string?.trim(); if (!account) { console.warn('[UILogin] 请输入账号'); return; } // 标记登录中 this._isLogging = true; try { // 执行登录 await this.login(account); } catch (error) { console.error('[UILogin] 登录失败:', error); } finally { this._isLogging = false; } } // 2. 执行登录逻辑 private async login(account: string): Promise { // 调用登录 API const result = await NetManager.getInstance() .callApi('Login', { account }); if (result && result.success) { // 登录成功,切换状态 AppStatusManager.getInstance().changeState('Game', { player: result.player, isNewPlayer: result.isNewPlayer }); // 隐藏登录界面 this.hide(); } else { // 登录失败,显示错误 console.error('[UILogin] 登录失败:', result?.message); } } ``` ## ⚠️ 注意事项 1. **UI 资源准备**: 必须在 `assets/res/UI/Login/` 创建 UILogin 预制体 2. **节点结构**: 预制体必须包含 `mid/input_account` 和 `btn_login` 节点 3. **EditBox 组件**: `input_account` 节点必须挂载 EditBox 组件 4. **协议同步**: 运行 `npm run sync-shared` 同步服务端协议后,替换临时协议定义 5. **模块归属**: 业务 UI 必须放在 `App/` 对应的业务模块下 6. **防重复点击**: 登录过程中禁用按钮,防止重复请求 ## 🔍 调试技巧 ### 日志输出 ```typescript // UILogin 包含详细日志 // [UILogin] 登录界面初始化 // [UILogin] 已绑定登录按钮事件 // [UILogin] 开始登录,账号: xxx // [UILogin] 登录成功 // [UILogin] 登录界面清理 ``` ### 检查节点 ```typescript // 在 onStart 中检查节点是否找到 private findNodes(): void { const inputNode = this._node.getChildByPath('mid/input_account'); console.log('input_account 节点:', inputNode ? '找到' : '未找到'); const btnNode = this._node.getChildByPath('btn_login'); console.log('btn_login 节点:', btnNode ? '找到' : '未找到'); } ``` ### 常见问题 **问题1**: 节点找不到 ```typescript // 检查预制体中的节点路径是否正确 // 确保节点名称完全匹配(区分大小写) ``` **问题2**: EditBox 组件为 null ```typescript // 确保 input_account 节点挂载了 EditBox 组件 const editBox = inputNode.getComponent(EditBox); if (!editBox) { console.error('未挂载 EditBox 组件'); } ``` **问题3**: 登录请求无响应 ```typescript // 检查网络是否已连接 // 检查服务器地址是否正确 // 检查协议是否已同步 ``` ## 💡 最佳实践 1. **输入验证**: 登录前验证账号格式 2. **加载状态**: 显示加载动画或禁用按钮 3. **错误提示**: 友好的错误提示 UI 4. **记住账号**: 可选的记住账号功能 5. **自动登录**: 可选的自动登录功能 6. **超时处理**: 设置合理的请求超时时间 ## 🎯 扩展功能 ### 添加密码输入 ```typescript // 1. 在 UI 中添加密码输入框 private _inputPassword: EditBox | null = null; // 2. 在 findNodes 中查找 this._inputPassword = this._node.getChildByPath('mid/input_password') ?.getComponent(EditBox) || null; // 3. 登录时传递密码 const result = await NetManager.getInstance() .callApi('Login', { account: account, password: password }); ``` ### 添加记住账号功能 ```typescript import { sys } from 'cc'; // 保存账号 private saveAccount(account: string): void { sys.localStorage.setItem('last_account', account); } // 读取账号 private loadAccount(): string { return sys.localStorage.getItem('last_account') || ''; } // 在 onStart 中自动填充 async onStart(): Promise { // ... 其他初始化 // 自动填充上次的账号 const lastAccount = this.loadAccount(); if (this._inputAccount && lastAccount) { this._inputAccount.string = lastAccount; } } ``` ### 添加加载动画 ```typescript private _loadingNode: Node | null = null; private showLoading(): void { if (this._loadingNode) { this._loadingNode.active = true; } // 禁用登录按钮 if (this._btnLogin) { this._btnLogin.getComponent(Button)!.interactable = false; } } private hideLoading(): void { if (this._loadingNode) { this._loadingNode.active = false; } // 启用登录按钮 if (this._btnLogin) { this._btnLogin.getComponent(Button)!.interactable = true; } } ``` ## 📚 相关文档 - [Framework/UI README](../../Framework/UI/README.md) - UI 系统 - [Framework/Net README](../../Framework/Net/README.md) - 网络通信 - [App/AppStatus README](../AppStatus/README.md) - 应用状态机