# 我的(Mine)Tab — 架构文档 > 对应 Gitea issues #5–#13,#39–#41(UI 重设计),#49–#50(编辑资料完整实现) > 参考实现:`im-client-ios-swift-demo` Features/Settings + Features/Profile --- ## 1. 功能范围 | Issue | 功能 | 状态 | |-------|------|------| | #5 | Tab 重命名 & 个人资料卡片 | ✅ 已实现 | | #6 | 编辑个人资料(昵称/bio/头像) | ✅ 全量实现(#49/#50 补全头像上传) | | #7 | 退出登录 | ✅ 已实现 | | #8 | 主题持久化 | ⏳ TODO(解开 ThemeModeNotifier 注释) | | #9 | 语言设置 | ✅ UI 框架(l10n_sdk 待接入) | | #10 | 黑名单管理 | ✅ 页面框架(API 待实现) | | #11 | 聊天文件夹 | ⏳ TODO stub | | #12 | 网络诊断 | ✅ 已实现(4步诊断) | | #13 | 关于 / 版本 | ✅ 已实现 | --- ## 2. 目录结构 ``` features/settings/ ├── di/ │ └── settings_providers.dart # UseCase DI 装配 ├── presentation/ │ ├── settings_view_model.dart # 我的页 ViewModel (NotifierProvider) │ ├── edit_profile_view_model.dart # 编辑资料 ViewModel │ ├── network_diagnostics_view_model.dart # 网络诊断 ViewModel │ └── theme_view_model.dart # 主题 ViewModel (@riverpod) ├── usecases/ │ ├── fetch_profile_usecase.dart # GET /app/api/user/profile │ ├── update_profile_usecase.dart # POST /app/api/user/update-profile │ ├── logout_usecase.dart # POST /app/api/auth/logout + WS + DB │ └── set_theme_usecase.dart # 幂等主题切换 └── view/ ├── settings_page.dart # 我的主页 ├── edit_profile_page.dart # 编辑资料页 ├── language_page.dart # 语言选择页 ├── blocklist_page.dart # 黑名单页(框架) ├── network_diagnostics_page.dart # 网络诊断页 ├── about_page.dart # 关于页 ├── theme_view.dart # 主题选择页 └── widgets/ ├── settings_section_header.dart └── theme_option_tile.dart data/remote/ ├── get_profile_request.dart # GET /app/api/user/profile(已有) └── update_profile_request.dart # POST /app/api/user/update-profile(新增) ``` --- ## 3. 数据流 ### 3.1 资料加载 ``` SettingsPage (build) └─ ref.watch(settingsViewModelProvider) └─ SettingsViewModel.build() └─ Future.microtask(loadProfile) └─ FetchProfileUseCase.execute() └─ NetworksSdkApi.executeRequest(GetProfileRequest()) └─ GET /app/api/user/profile (JWT token in header) └─ ProfileResponse → SettingsState {nickname, avatarUrl, maskedContact, uid} ``` ### 3.2 退出登录 ``` SettingsPage._confirmLogout() └─ AlertDialog confirm └─ SettingsViewModel.logout() ├─ LogoutUseCase.execute() │ ├─ AuthRepository.logout() → POST /app/api/auth/logout │ ├─ SocketManager.disconnect() → 断开 WebSocket │ └─ StorageSdkLifecycle.closeDatabase() └─ AuthNotifier.logout() → go_router 重定向 /login ``` ### 3.3 编辑资料 — 头像上传(#49) ``` EditProfilePage._showAvatarSourceSheet() └─ BottomSheet → 相册 / 拍照 └─ EditProfileViewModel.pickAndUploadAvatar(ImageSource) ├─ ImagePicker.pickImage(source, maxWidth:900, maxHeight:900, quality:85) ├─ ImageCropper.cropImage(1:1, IOSUiSettings / AndroidUiSettings) ├─ state.isUploadingAvatar = true ├─ NetworksSdkApi.executeRequest(UploadFileRequest(filePath)) │ └─ POST /app/api/upload/file (FormData multipart) │ └─ UploadResult.url └─ state.avatarUrl = url (UI 实时预览更新) ``` ### 3.4 编辑资料保存(#50) ``` EditProfilePage → 保存按钮(nickname.isNotEmpty && !isUploadingAvatar) └─ EditProfileViewModel.save() └─ UpdateProfileUseCase.execute(nickname, bio, profilePicUrl) └─ NetworksSdkApi.executeRequest(UpdateProfileRequest) └─ POST /app/api/user/update-profile └─ SettingsViewModel.loadProfile() (刷新资料卡) └─ Navigator.pop() ``` --- ## 4. API 端点 | 操作 | Method | Path | |------|--------|------| | 获取当前用户资料 | GET | `/app/api/user/profile` | | 更新用户资料 | POST | `/app/api/user/update-profile` | | 退出登录 | POST | `/app/api/auth/logout` | | 黑名单列表(待实现) | GET | `/app/api/account/block/list` | | 解除拉黑(待实现) | POST | `/app/api/account/block/remove` | | 聊天文件夹(待实现) | POST | `/app/api/account/store/get-store` | --- ## 5. Provider 设计 所有 Settings ViewModel 使用 `Notifier` + 手动 `NotifierProvider`,**不使用 `@riverpod` 代码生成**,避免额外 build_runner 依赖: ```dart class SettingsViewModel extends Notifier { ... } final settingsViewModelProvider = NotifierProvider( SettingsViewModel.new, ); ``` DI 链路: ``` settingsViewModelProvider └─ fetchProfileUseCaseProvider → networkSdkApiProvider └─ logoutUseCaseProvider ├─ authRepositoryProvider → networkSdkApiProvider ├─ socketManagerProvider └─ storageSdkProvider ``` --- ## 6. 路由 新增 Shell 外全屏路由(`parentNavigatorKey: _rootKey`),TabBar 在子页面隐藏: ``` /settings/edit-profile EditProfilePage /settings/blocklist BlocklistPage /settings/language LanguagePage /settings/network-diagnostics NetworkDiagnosticsPage /settings/about AboutPage /settings/theme ThemeView(原有) ``` 认证守卫 `auth_guard.dart` switch 已补全上述路由。 --- ## 7. UI 重设计(#39 / #40 / #41) ### 7.1 ProfileHeroCard (#39 / #41) | 元素 | 规格 | |------|------| | 头像 | 72pt 圆形;无头像时 8 色渐变占位(uid%8) | | 昵称 | fontWeight w700,titleMedium | | Handle | `@J{uid}`,bodySmall onSurfaceVariant | | 手机号 | 掩码 `+CC ***XXXX`,bodySmall | | Bio | 非空显示,为空显示「添加一句话简介」(斜体,半透明) | | AppBar | compact,右侧:QR 图标 + 编辑铅笔 | 渐变色方案(`_ProfileHeroCard._gradients[uid.abs() % 8]`): ``` 0: [#4776E6, #8E54E9] 1: [#11998E, #38EF7D] 2: [#FC466B, #3F5EFB] 3: [#F7971E, #FFD200] 4: [#56CCF2, #2F80ED] 5: [#EB3349, #F45C43] 6: [#1FA2FF, #12D8FA] 7: [#9D50BB, #6E48AA] ``` ### 7.2 彩色图标行与分组卡片 (#40) `_IconBox`:36pt 圆角正方形(8pt)白色图标,iOS Settings 风格。 | 卡片组 | 菜单项 | 颜色 | |--------|--------|------| | 账户 | 我的钱包 | #FFAA5B | | | 账户安全 | #8A5CF6 | | 工具 | 收藏 | #FFAF45 | | | 最近呼叫 | #4CB050 | | | 链接设备 | #5667FF | | | 聊天文件夹 | #F2994A | | 偏好设置 | 通知和声音 | #FF8B5E | | | 隐私设置 | #0BB8A9 | | | 黑名单 | #FF4B4B | | | 语言 | #5667FF | | | 主题 | #8A5CF6 | | 关于 | 用户协议 | gray | | | 隐私政策 | gray | | | 版本号(静态)| gray,无 chevron | ### 7.3 SettingsState bio 字段 (#41) - `SettingsState.bio: String`(默认 `''`) - `SettingsViewModel.loadProfile()` 赋值 `bio: profile.bio` - 数据来源:`ProfileResponse.bio` → `GET /app/api/user/profile` --- ## 8. 编辑资料 UI 设计(#49 / #50) | 元素 | 规格 | |------|------| | 头像 | 88pt 圆形;无头像时 8 色渐变占位;右下 28pt 相机角标(primary色) | | 上传进度 | CircularProgressIndicator 环绕头像,isUploadingAvatar=true 时显示 | | 选图 Sheet | BottomSheet(从相册选取 / 拍照),16pt 圆角,拖动条 | | 昵称字段 | Card 分组,`x/50` 右侧计数,maxLength:50 | | 简介字段 | Card 分组,`x/200` 右侧计数,maxLines:null(自动展开),maxLength:200 | | 保存按钮 | AppBar 右侧 TextButton;昵称为空或上传中时 disabled | | 错误 Banner | errorContainer 色圆角卡片,⚠️ 图标 + 文本 | --- ## 9. 待完成事项 - **#8 主题持久化**:解开 `ThemeModeNotifier.build()` 和 `setMode()` 中的 TODO - **#10 黑名单 API**:实现 `FetchBlocklistUseCase` + `UnblockUseCase` - **#11 聊天文件夹**:`ChatCategoryViewModel` + `account/store` API