Files
2026-07-20 19:01:03 +08:00

630 lines
29 KiB
Markdown
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.
# UI 架构设计
> 依据:`docs/流程/00_总览.md` 及 `docs/流程/01~08` 各流程文件
> 范围:页面布局、卡片设计、全局交互、状态处理、流程映射
---
## 一、架构总览
### 1.1 整体结构:3 页 3 卡
```
页面(Page) 卡片(Card
┌─────────────────────┐ ┌─────────────────────┐
│ 服务器配置页 │ │ 服务器详情卡 │
│ ServerConfigPage │ │ ServerDetailCard │
├─────────────────────┤ │ (底部弹出) │
│ 频道列表页 │ ├─────────────────────┤
│ ChannelListPage │ │ 频道详情卡 │
├─────────────────────┤ │ ChannelDetailCard │
│ 聊天页 │ │ (底部弹出) │
│ ChatPage │ ├─────────────────────┤
└─────────────────────┘ │ 语音卡 │
│ VoiceCard │
│ (底部弹出) │
└─────────────────────┘
```
### 1.2 页面导航关系
```
┌──────────────────────┐
│ 服务器配置页 │
│ (应用启动首屏) │
└──────────┬───────────┘
│ 连接成功
┌──────────────────────┐
┌────→│ 频道列表页 │──────┐
│ │ (主页面) │ │
│ └──────────────────────┘ │
│ │ │
│ │ 点击"中部-底部"当前频道 │
│ ▼ │
│ ┌──────────────────────┐ │
│ │ 聊天页 │ │
│ │ (当前频道消息) │ │
│ └──────────┬───────────┘ │
│ │ 返回按钮 │
└────────────────┘ │
┌───────────────────────────────────┘
│ 头部左侧按钮
服务器详情卡(弹出)
频道列表页 / 聊天页
│ 头部右侧按钮
频道详情卡(弹出)
任意页面(服务器配置页除外)
│ 底部语音控制区 "展开" 按钮
语音卡(弹出)
```
### 1.3 卡片弹出规则
| 卡片 | 可弹出页面 | 弹出方式 | 关闭方式 |
| --- | --- | --- | --- |
| 服务器详情卡 | 频道列表页 | 底部弹出(BottomSheet) | 点击外部 / 下滑 / 断开后自动关闭 |
| 频道详情卡 | 频道列表页、聊天页 | 底部弹出(BottomSheet | 点击外部 / 下滑 |
| 语音卡 | 频道列表页、聊天页 | 底部弹出(BottomSheet),可拖拽为半屏 | 点击外部 / 下滑 / 点击展开按钮 |
---
## 二、页面详细设计
### 2.1 服务器配置页
> 对应流程:01 连接服务器
#### 布局
```
┌──────────────────────────────┐
│ 上:品牌区 │
│ [Logo] │
│ TeamSpeak Mobile │
│ 连接到你的 TeamSpeak 服务器 │
│ [🌙 主题] │ ← 右上角主题切换图标
├──────────────────────────────┤
│ 中:输入区 │
│ │
│ 服务器地址 │
│ ┌──────────────────────────┐│
│ │ ts.example.com ││
│ └──────────────────────────┘│
│ │
│ 昵称 │
│ ┌──────────────────────────┐│
│ │ 我的昵称 ││
│ └──────────────────────────┘│
│ │
│ 密码(可选) │
│ ┌──────────────────────────┐│
│ │ •••••• ││
│ └──────────────────────────┘│
│ │
│ ┌──────────────────────────┐│
│ │ 连接服务器 ││ ← 按钮状态见 5.1
│ └──────────────────────────┘│
├──────────────────────────────┤
│ 下:最近连接 │
│ │
│ 最近连接 │
│ ┌──────────────────────────┐│
│ │ 🟢 ts.myserver.com ││ ← 点击快速连接
│ │ 上次连接:2小时前 ││
│ ├──────────────────────────┤│
│ │ 🔴 ts.other.com ││
│ │ 上次连接:昨天 ││
│ └──────────────────────────┘│
│ │
│ [清空最近记录] │ ← 长按或编辑模式删除单条
└──────────────────────────────┘
```
#### 三段式结构说明
| 段落 | 内容 | 说明 |
| --- | --- | --- |
| 上(品牌区) | Logo + 描述文本 + 主题切换 | 仅展示,不可交互(主题图标除外) |
| 中(输入区) | 地址/昵称/密码输入 + 连接按钮 | 核心交互区,连接成功跳转频道列表页 |
| 下(最近连接) | 历史服务器列表 | 点击直接快速连接(复用已保存的昵称和密码) |
#### 输入验证规则
| 字段 | 必填 | 验证规则 | 错误提示 |
| --- | --- | --- | --- |
| 服务器地址 | 是 | 非空,格式合法(域名/IP/TSDNS) | "请输入有效的服务器地址" |
| 昵称 | 是 | 非空,满足服务器命名规则 | "请输入昵称" |
| 密码 | 否 | 仅当服务器需要密码时必填 | "该服务器需要密码" |
#### 最近连接列表
- 点击列表项 → 自动填充地址/昵称/密码 → 触发连接
- 最多保存 10 条历史记录
- 按最近连接时间倒序排列
- 连接成功的服务器标记为绿色,失败的标记为红色(短暂)
- 长按可删除单条记录
---
### 2.2 频道列表页
> 对应流程:02 浏览频道 + 03 切换频道 + 08 状态同步
#### 布局
```
┌──────────────────────────────────────┐
│ 头部(三段) │
│ ┌────┐ ┌──────────────────┐ ┌─────┐ │
│ │ ☰ │ │ MyServer │ │ 🟢 │ │
│ │服务│ │ 192.168.1.1:9987 │ │连接 │ │ ← 连接状态指示
│ │器卡│ │ 42人在线 │ │状态 │ │
│ └────┘ └──────────────────┘ └─────┘ │
├──────────────────────────────────────┤
│ 中部:频道树 + 成员 │
│ │
│ ▼ 📁 默认频道 │ ← 展开/折叠
│ ▼ 📁 大厅 │
│ 👤 Alice │ ← 点击弹出成员操作菜单
│ 👤 Bob 🔇 │ ← 🔇 = 正在发言
│ 👤 Charlie 🎤 │ ← 🎤 = 正在发言
│ ▶ 📁 游戏区 🔒 │ ← 🔒 = 有密码
│ (3人) │ ← 折叠时显示人数
│ ▶ 📁 音乐区 │
│ (1人) │
│ ▼ 📁 VIP 频道 🔒 │
│ 👤 Admin │
│ │
│ (频道列表支持滚动) │
├──────────────────────────────────────┤
│ 中部-底部:当前频道栏 │
│ ┌──────────────────────────────────┐ │
│ │ 💬 大厅 (5人) Alice🎤 Bob │ │ ← 点击跳转到聊天页
│ └──────────────────────────────────┘ │
├──────────────────────────────────────┤
│ 底部:语音控制 │
│ ┌──────┐ ┌────────────────┐ ┌─────┐ │
│ │ 🎤 │ │ PTT 按住发言 │ │ ⬆ │ │
│ │静音 │ │ │ │语音 │ │ ← 展开语音卡
│ └──────┘ └────────────────┘ └─────┘ │
└──────────────────────────────────────┘
```
#### 频道树交互
| 操作 | 行为 |
| --- | --- |
| 点击频道名 | 无密码 → 直接切换(发送 ClientMove |
| 点击有密码频道 🔒 | 弹出频道密码对话框(见 4.2) |
| 点击折叠频道 ▶ | 展开显示子频道 |
| 点击展开频道 ▼ | 折叠子频道 |
| 长按频道 | 弹出频道操作菜单(频道信息) |
| 点击成员 👤 | 弹出成员操作菜单(见 4.3) |
| 点击当前频道栏 | 跳转到聊天页 |
#### 未读消息指示
| 状态 | 显示 | 说明 |
| --- | --- | --- |
| 无未读 | 正常显示 | — |
| 有未读消息 | 频道名右侧显示红点 ● | 弱未读提示 |
| 有未读 @提及 | 频道名右侧显示数字 badge | 强未读提示 |
| 当前所在频道 | 不显示未读标记 | 已在该频道 |
#### 初始同步加载态
连接成功进入频道列表页时,如果首次同步尚未完成:
```
┌──────────────────────────────┐
│ 头部(正常) │
├──────────────────────────────┤
│ │
│ ⟳ 正在同步服务器数据... │ ← 居中加载指示器
│ │
├──────────────────────────────┤
│ 底部(正常) │
└──────────────────────────────┘
```
同步完成后自动切换为正常频道树视图。
---
### 2.3 聊天页
> 对应流程:04 文本消息
#### 布局
```
┌──────────────────────────────────────┐
│ 头部 │
│ ┌────┐ ┌──────────────────┐ ┌─────┐ │
│ │ ← │ │ 大厅 │ │ ⋮ │ │
│ │返回│ │ 5人 │ │频道 │ │ ← 右侧弹出频道详情卡
│ │ │ │ │ │详情 │ │
│ └────┘ └──────────────────┘ └─────┘ │
├──────────────────────────────────────┤
│ 中部:消息列表 │
│ │
│ ┌──────────────────────────────────┐ │
│ │ Alice 14:30 │ │
│ │ 大家好! │ │
│ ├──────────────────────────────────┤ │
│ │ Bob 14:31 │ │
│ │ 你好 Alice! │ │
│ ├──────────────────────────────────┤ │
│ │ Charlie 14:32 │ │
│ │ 有人一起打游戏吗? │ │
│ ├──────────────────────────────────┤ │
│ │ 我 14:33 │ │
│ │ 我来! │ │ ← 自己的消息靠右 / 不同颜色
│ └──────────────────────────────────┘ │
│ │
│ (消息列表支持滚动,新消息自动滚到底) │
├──────────────────────────────────────┤
│ 中部-底部:消息输入区 │
│ ┌──────────────────────────┐ ┌────┐ │
│ │ 输入消息... │ │ 发送│ │ ← 随输入法上抬
│ └──────────────────────────┘ └────┘ │
├──────────────────────────────────────┤
│ 底部:语音控制 │
│ ┌──────┐ ┌────────────────┐ ┌─────┐ │
│ │ 🎤 │ │ PTT 按住发言 │ │ ⬆ │ │
│ │静音 │ │ │ │语音 │ │
│ └──────┘ └────────────────┘ └─────┘ │
└──────────────────────────────────────┘
```
#### 消息显示格式
```
发送者名称 发送时间
消息内容
```
- 自己的消息靠右对齐或使用不同背景色区分
- 支持长按消息弹出操作菜单(复制 / Poke 发送者)
- 新消息到达时自动滚动到底部
- 如果用户正在查看历史消息,新消息到达时显示 "↓ 新消息" 提示条
#### 消息输入区与键盘联动
- 点击输入框 → 唤起输入法 → 输入区 + 发送按钮随键盘上抬
- 语音控制栏保持固定在底部,不受键盘影响
- 发送消息后输入框清空,保持键盘打开状态
- 输入框为空时发送按钮置灰不可点击
---
## 三、卡片详细设计
### 3.1 服务器详情卡
> 弹出方式:频道列表页头部左侧按钮触发,底部弹出 BottomSheet
```
┌──────────────────────────────┐
│ 服务器详情 ✕ │
├──────────────────────────────┤
│ │
│ 服务器名 │
│ My TeamSpeak Server │
│ │
│ 服务器地址 │
│ 192.168.1.1:9987 │
│ │
│ 在线人数 │
│ 42 / 100 │
│ │
│ ┌──────────────────────────┐│
│ │ 断开服务器 ││ ← 确认弹窗后执行 Disconnect
│ └──────────────────────────┘│
│ │
└──────────────────────────────┘
```
#### 行为
- 断开服务器 → 弹出确认对话框 "确定要断开连接吗?" → 确认后执行断开 → 关闭卡片 → 返回服务器配置页
- 卡片数据来自首次同步结果和实时事件更新
---
### 3.2 频道详情卡
> 弹出方式:聊天页头部右侧按钮触发,底部弹出 BottomSheet
```
┌──────────────────────────────┐
│ 频道详情 ✕ │
├──────────────────────────────┤
│ │
│ 频道名 │
│ 大厅 │
│ │
│ 频道描述 │
│ 欢迎来到大厅频道,请遵守规则 │
│ │
│ 频道人数 │
│ 5人 │
│ │
└──────────────────────────────┘
```
#### 行为
- 如果频道无描述,显示 "暂无描述"
- 有密码频道在频道名旁显示 🔒 图标
---
### 3.3 语音卡
> 弹出方式:频道列表页 / 聊天页底部语音控制区 "展开" 按钮
> 激活时机:进入频道后即激活,除服务器配置页外任意页面可弹出
```
┌──────────────────────────────┐
│ 语音控制 ✕ │
├──────────────────────────────┤
│ │
│ 正在发言 │
│ ┌──────────────────────────┐│
│ │ 🎤 Charlie ▓▓▓▓░░ -12dB││ ← 音量波形指示
│ │ 🎤 Alice ▓▓░░░░ -24dB││
│ └──────────────────────────┘│
│ (无人在发言时显示 "暂无发言")│
│ │
├──────────────────────────────┤
│ │
│ 频道内人员 (5) │
│ ┌──────────────────────────┐│
│ │ 👤 Alice ││ ← 长按弹出操作菜单(Poke)
│ │ 👤 Bob ││
│ │ 👤 Charlie 🎤 发言中 ││ ← 发言中高亮
│ │ 👤 David ││
│ │ 👤 Eve ││
│ └──────────────────────────┘│
│ (人员超出时可滚动) │
│ │
├──────────────────────────────┤
│ 控制 │
│ ┌──────────┐ ┌──────────────┐│
│ │ 🔊 扬声器 │ │ 🎙️ 降噪 ││
│ │ 开 / 关 │ │ 开 / 关 ││
│ └──────────┘ └──────────────┘│
│ │
└──────────────────────────────┘
```
#### 行为
- 发言人栏实时显示正在发送语音帧的成员,附带音量波形指示
- 频道内人员列表支持滚动
- 长按成员 → 弹出操作菜单(Poke)
- 扬声器控制:切换本地音频输出开/关
- 降噪控制:切换本地降噪算法开/关
---
## 四、全局交互
### 4.1 Poke 交互
> 对应流程:02 浏览频道中的 "poke 服务器内成员" 和 "被 poke"
#### 发送 Poke
| 入口 | 操作 |
| --- | --- |
| 频道列表页成员列表 | 长按成员 → 弹出菜单 → Poke |
| 语音卡成员列表 | 长按成员 → 弹出菜单 → Poke |
| 聊天页消息列表 | 长按消息 → 弹出菜单 → Poke 发送者 |
发送后调用 SDK 的 Poke 方法,无需等待服务端确认。
#### 接收 Poke 通知
收到 `OnPoked` 事件时:
```
┌──────────────────────────────┐
│ ┌──────────────────────────┐ │
│ │ 🫴 Alice poke 了你 │ │ ← 顶部 Toast,3秒自动消失
│ │ "起床啦!" │ │
│ └──────────────────────────┘ │
└──────────────────────────────┘
```
- Toast 从顶部滑入,3 秒后自动消失
- 如果在聊天页收到 Poke,同样显示 Toast(不自动跳转)
---
### 4.2 频道密码弹窗
> 对应流程:03 切换频道中的密码检查分支
点击有密码频道 🔒 时弹出:
```
┌──────────────────────────────┐
│ │
│ 该频道需要密码 │
│ │
│ ┌────────────────────────┐ │
│ │ 输入频道密码 │ │
│ └────────────────────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ 取消 │ │ 进入 │ │
│ └──────────┘ └──────────┘ │
│ │
└──────────────────────────────┘
```
- 密码错误 → 按钮变为 "密码错误,重试" → 清空输入框 → 允许重新输入
- 点击取消 → 关闭弹窗,不发送 ClientMove
- 点击进入 → 发送 `ClientMove(selfID, targetID, password)`
---
### 4.3 成员点击行为
在频道列表页和语音卡中,点击/长按成员时弹出操作菜单:
```
┌──────────────────────────────┐
│ 👤 Alice │
│ 频道:大厅 │
├──────────────────────────────┤
│ 🫴 Poke │ → 发送 Poke
│ 📋 复制昵称 │ → 复制到剪贴板
└──────────────────────────────┘
```
---
### 4.4 主题切换
- 入口位置:服务器配置页右上角 🌙 图标
- 切换方式:点击在亮色/暗色主题间切换
- 持久化:选择保存到本地配置,下次启动自动应用
- 影响范围:全局所有页面和卡片
---
## 五、状态处理
### 5.1 连接状态
> 对应流程:01 连接服务器
#### 服务器配置页按钮状态
| 状态 | 按钮外观 | 说明 |
| --- | --- | --- |
| 空闲 | "连接服务器"(正常样式) | 等待用户输入并点击 |
| 连接中 | ⟳ "连接中..."(加载动画 + 禁用) | Connect + WaitConnected 进行中 |
| 连接失败 | "连接失败,点击重试"(红色 + 错误信息) | 显示具体失败原因(密码错误/超时/网络不可达) |
| 超时 | "连接超时,点击重试"(橙色) | WaitConnected 超时 |
#### 错误信息分类
| 错误类型 | 提示信息 | 建议操作 |
| --- | --- | --- |
| 密码错误 | "服务器密码错误" | 重新输入密码 |
| 昵称冲突 | "昵称已被使用,请更换" | 修改昵称 |
| 网络不可达 | "无法连接到服务器,请检查网络" | 检查网络后重试 |
| 地址无效 | "服务器地址无法解析" | 检查地址格式 |
| 超时 | "连接超时" | 检查网络后重试 |
| 服务器满 | "服务器已满" | 稍后重试 |
---
### 5.2 初始同步加载态
> 对应流程:08 状态同步 ① 首次同步
连接成功后进入频道列表页,首次同步期间:
- 频道树区域显示居中加载指示器 + "正在同步服务器数据..."
- 头部正常显示(但在线人数可能显示为 "--"
- 底部语音控制区正常显示
- 同步完成后自动切换到正常频道树视图
- 同步失败 → 显示 "同步失败" + 重试按钮
---
### 5.3 被踢处理
> 对应流程:07 断开连接
被服务器踢出时(`OnKicked` 触发):
```
┌──────────────────────────────┐
│ │
│ 你已被踢出 │
│ │
│ 原因:违反服务器规则 │ ← 显示踢出原因
│ │
│ ┌────────────────────┐ │
│ │ 重新连接 │ │
│ └────────────────────┘ │
│ │
│ ┌────────────────────┐ │
│ │ 返回主页 │ │
│ └────────────────────┘ │
│ │
└──────────────────────────────┘
```
- 全屏覆盖提示,不可通过点击外部关闭
- "重新连接" → 使用相同参数重新连接
- "返回主页" → 回到服务器配置页
- 清理当前会话状态(频道、成员、消息)
---
### 5.4 断线重连
> 对应流程:07 断开连接
网络异常断开时(`OnDisconnected(error)` 触发):
```
┌──────────────────────────────────────┐
│ ┌──────────────────────────────────┐ │
│ │ ⚠️ 连接已断开 │ │ ← 顶部横幅
│ │ 正在尝试重连... (3/5) │ │ ← 自动重连尝试计数
│ │ [手动重连] [放弃] │ │
│ └──────────────────────────────────┘ │
└──────────────────────────────────────┘
```
- 顶部横幅提示,不阻塞底层页面(页面数据冻结)
- 自动重连:最多尝试 5 次,间隔递增(2s → 4s → 8s → 16s → 30s
- 手动重连:用户主动触发立即重连
- 放弃:停止重连 → 显示完整断线提示(类似被踢处理) → 返回主页
- 重连成功 → 执行全量同步(08 状态同步 ⑥)→ 横幅消失 → 恢复正常
---
### 5.5 未读消息指示
> 对应流程:04 文本消息
#### 频道列表页
| 场景 | 显示 | 说明 |
| --- | --- | --- |
| 频道有新消息 | 频道名右侧显示红点 ● | 弱未读(用户不在该频道时) |
| 频道有 @提及 | 频道名右侧显示数字(如 3) | 强未读 |
| 当前所在频道 | 不显示 | 已在该频道,消息直接可见 |
| 用户进入频道后 | 清除该频道的未读标记 | 已读即清 |
---
## 六、页面与流程的对应关系
| 流程文件 | 主要承载页面 | 承载卡片 | 关键交互 |
| --- | --- | --- | --- |
| 01 连接服务器 | 服务器配置页 | — | 输入验证、连接按钮状态、最近连接快速连接 |
| 02 浏览频道 | 频道列表页 | 语音卡 | 频道树展开/折叠、成员列表、Poke、未读指示 |
| 03 切换频道 | 频道列表页 | — | 点击频道切换、密码弹窗、ClientMove 等待确认 |
| 04 文本消息 | 聊天页 | — | 消息列表、发送消息、未读归档 |
| 05 语音通信 | 频道列表页 + 聊天页 | 语音卡 | PTT 按钮、静音控制、发言人指示 |
| 06 文件传输 | — | — | 本应用不实现 |
| 07 断开连接 | 全屏覆盖 | 服务器详情卡 | 断开确认、被踢提示、断线重连横幅 |
| 08 状态同步 | 所有页面 | — | 首次同步加载、增量更新、补偿同步、重连全量同步 |