# U8 Chat 2.0 · 消息选择 产品需求文档（PRD）

| 项目 | 内容 |
|---|---|
| 文档版本 | v1.1（2026-10-08） |
| 功能模块 | 聊天 → 长按消息 →「选择」→ 批量转发 / 删除 |
| 适用端 | iOS / Android / Web / Desktop（**同一套规则**，只有触发方式不同） |
| 入口 | 长按消息 → 操作栏最下方「选择」（桌面端＝在消息上点右键） |
| 演示与逐屏 | 同目录 `index.html`（**10 屏**：移动端 8 + 桌面端 2） |
| 参照产品 | Telegram「Select」· **只做 Telegram 有的那套，不额外发明规则**（唯一的例外见 3.6） |

---

## 一、背景与目标

### 1.1 背景

U8 Chat 目前只能一条一条地处理消息：想转发三条要长按三次；想删掉一串刷屏消息，也只能逐条删。

「消息选择」给聊天补上**框选 → 一次处理一批**的能力。

### 1.2 目标

- 4 步完成一批处理：长按 → 选择 → 勾选 → 转发或删除。
- 交互照搬 Telegram，不发明新玩法、不加判定规则。

---

## 二、范围

### 2.1 本期做

| 模块 | 内容 |
|---|---|
| 入口 | 长按消息 → 操作栏最下方「选择」（桌面端＝在消息上点右键） |
| 选择态 | 顶部「✕ / 已选 N 条」；每条消息左侧圆圈；点整条消息切换 |
| 底部操作 | 「转发」「删除」（删除为红色） |
| 转发 | 复用已有的「转发选择页」：可单选会话，也可多选会话后发送 |
| 删除 | 确认弹窗；**只有一种范围**（对所有成员删除），没有范围单选 |
| 权限 | 「删除」**只在两种情况置灰**：一条都没选 / 选中的一条都删不了 |
| 桌面端 | 右键「选择」→ 聊天区顶部计数栏 + 左侧圆圈 + 底部操作栏 |

### 2.2 本期不做

| 不做的事 | 说明 |
|---|---|
| 底部「复制」按钮 | 单条菜单里已有「拷贝」，批量复制本期不做 |
| 保存到相册 / 分享到站外 | 涉及媒体权限，另行排期 |
| 选择模式的「撤回删除」（Undo） | 用删除前的确认弹窗兜底 |
| 跨会话选择 | 选择模式限定在**单个会话内** |
| 红包 / 转账 / 通话记录等卡片消息 | 沿用现有单条菜单，不进选择模式 |
| 桌面端 Shift 连选 | 本期不做 |
| 深色模式、大字号无障碍 | 随全局一起排期 |
| **「仅为我删除」这个范围** | U8 群组和单聊都只有「对所有成员删除」，**不存在**该范围 |

---

## 三、交互

### 3.1 入口

1. 移动端**长按消息 0.3~0.5 秒**、桌面端**在消息上点右键**，弹出**操作栏**。
2. 操作栏菜单项**沿用设计稿**：回复 / 拷贝 / 编辑 / 置顶 / 转发 / 删除；「**选择**」用一条分隔线隔在最下面，其余项一个都不动。
3. 点「**选择**」→ **进入多选模式**，**刚刚点的那条消息已经是选中状态**。
4. 被长按 / 右键的那条消息**加高亮**。

对应演示屏：[`index.html#S1`](index.html#S1) · [`index.html#S2`](index.html#S2)

### 3.2 选择态

进入后三处同时变化：

| 位置 | 变化 |
|---|---|
| 顶部 | 换成「**✕**」+「**已选 N 条**」（**没有「全选」按钮**，Telegram 也没有） |
| 消息列表 | 每条消息**左侧**出现圆圈；已选 = 紫色实心 + 白勾，未选 = 空心圈 |
| 底部 | 输入栏换成「**转发**」「**删除**」 |

规则：

1. **点整条消息**（气泡、头像、时间都算）即可切换勾选，不用精确点到圈上。
2. 自己的消息和别人的消息，**圆圈一律在列表最左侧**；消息本身保持原来的左右对齐。
3. 「✕」= **退出选择模式**，不删除任何东西，**不需要二次确认**。
4. Android 系统返回键 = 同「✕」。
5. 选择模式下**可以正常上下滚动**列表，滚动不改变已选项。
6. 选择模式是**单个会话内**的临时状态，切走会话即退出。

对应演示屏：[`index.html#S2`](index.html#S2) · [`index.html#S3`](index.html#S3)

### 3.3 退出

1. **取消掉最后一条勾选 → 自动退出选择模式**（跟 Telegram）。
2. 一条都没选中时，底部「转发」「删除」**置灰**。
3. 「✕」和 Android 返回键同样退出选择模式，**退出前不需要二次确认**（还没做任何破坏性操作）。

对应演示屏：[`index.html#S2`](index.html#S2)

### 3.4 转发

1. 点底部「转发」→ 打开已有的**转发选择页**（顶部「取消 / 转发 / 选择」+ 搜索 + 会话列表 + 聊天·联系人切换）。
2. 点某个会话 = 立刻把这批消息发给它。
3. 点右上角「选择」= 进入**多选会话**，每个会话左侧出现圆圈，右上角变「**发送 N**」；未选会话时置灰。
4. 转发**按条发**：选了 3 条就发 3 条，不合并成一条。
5. 转发完成后**退出选择模式**，回到会话，提示「**已转发**」。
6. 失败提示：全部失败「转发失败，请检查网络后重试」；部分失败「部分消息转发失败，请重试」，**保留选中态**。

对应演示屏：[`index.html#S4`](index.html#S4) · [`index.html#S5`](index.html#S5)

### 3.5 删除

1. 点底部「删除」→ 弹出**确认弹窗**。**只要「删除」是可点的，就一定弹这个窗**，不做静默删除。
2. **选中的消息全部能删**时，弹窗内容：
   - 标题：**删除 N 条消息？**（N = **选中的总条数**）
   - 正文：**这 N 条消息将会从所有群成员的聊天中删除。**（Telegram 原句 `This will delete them for everyone in this chat.` 直译成「为群里所有成员删除」太绕，U8 改成讲结果的句式）
   - 按钮：**取消** / **删除**（红色）
3. **只能删掉一部分**时（哪些删不了见 3.7），弹窗内容：
   - 标题：**删除 N 条消息？**——**仍是选中的总条数**，不是实际会删掉的条数（否则跟顶部「已选 N 条」对不上）
   - 正文：**其中 X 条不是你发送的，你没有权限删除。你的 Y 条消息将会从所有群成员的聊天中删除。**（N = X + Y）
   - 按钮：**取消** / **删除**（红色）
4. 弹窗里**没有「为所有人删除 / 仅为我删除」的范围单选**：群组和单聊都只有「对所有成员删除」一种范围。
5. 点遮罩或「取消」→ 关闭弹窗，**保留选中态**。
6. 点「删除」→ 执行 → **退出选择模式** → 提示「**已删除 N 条消息**」（N = **实际删掉的条数**）→ 消息从列表消失。
7. **本期不做「撤回删除」**，所以确认弹窗不能省。

对应演示屏：[`index.html#S6`](index.html#S6) · [`index.html#S7`](index.html#S7)

### 3.6 「删除」什么时候置灰

**只有两种情况置灰**：

| # | 情况 | 表现 |
|---|---|---|
| ① | 一条消息都没选中 | 「转发」「删除」都置灰 |
| ② | 选中的消息**一条都删不了** | 「删除」置灰，只剩「转发」能用 |

除此之外「删除」**一律可点**，包括「选中的消息里只有一部分能删」——这时不置灰、不隐藏，点了正常弹确认窗，由弹窗正文说清楚哪部分删不掉（3.5 第 3 条）。

> ⚠️ **这是本功能唯一一处不照抄 Telegram 的地方。** Telegram 的 `canDeleteSelectedMessages()` 只要选中集里有一条没权限删的消息，就把 Delete 整条禁用，语言文件里也没有任何「部分保留」的文案。U8 选择「不置灰 + 弹窗讲清楚」，所以 3.5 第 3 条的文案是**本项目新定**的，不是抄来的。

对应演示屏：[`index.html#S7`](index.html#S7) · [`index.html#S8`](index.html#S8)

### 3.7 删除权限

| 场景 | 能不能删 | 表现 |
|---|---|---|
| 自己发的消息（单聊 / 群聊） | 能 | 「删除」可用，弹确认窗 |
| 群聊里别人的消息 · 我是普通成员 | **不能** | 选中集里**只有**这类时「删除」置灰；跟自己的消息混选时照常可点 |
| 群聊里别人的消息 · 我是群主 / 管理员 | 能 | 「删除」可用，弹确认窗 |
| 单聊里对方的消息 | **不能** | 与群组同一口径 |

- U8 **没有「仅为我删除」**：群组和单聊都只有「对所有成员删除」一种范围。
- 界面上**不额外解释**：能不能删，先看「删除」亮不亮；只删得掉一部分时，由确认弹窗的正文说明。

对应演示屏：[`index.html#S7`](index.html#S7) · [`index.html#S8`](index.html#S8)

### 3.8 桌面端

1. 触发：**在消息上点右键** → 弹出操作栏（与移动端逐项一致）→ 点「选择」进入多选。
2. 进入后：聊天区**顶部**一条「已选 N 条」（右侧「取消」），每条消息**左侧**圆圈，**底部**「转发 / 删除」。
3. **左侧会话列表不变**，只在聊天区内部变化。
4. `Esc` = 退出选择模式。

对应演示屏：[`index.html#D1`](index.html#D1) · [`index.html#D2`](index.html#D2)

---

## 四、边界与异常

| 场景 | 表现 |
|---|---|
| 选择过程中某条消息被对方删除 / 撤回 | 该条从列表消失；若已勾选，自动移出选中集合并刷新计数 |
| 选择过程中对方又发了新消息 | 新消息出现在列表里，**不自动勾选** |
| 选择过程中我被踢出群 / 群解散 | 退出选择模式，回到会话列表 |
| 选择过程中断网 | 仍可勾选；点转发 / 删除时提示网络异常，**保留选中态** |
| 删除时部分消息已被对方删掉 | 删除幂等：已不存在的跳过，其余照删 |
| 删除时权限变了（如管理员被撤） | 以服务端返回为准：能删的删掉、删不掉的保留，提示「部分消息删除失败」 |
| 选择模式开着时收到新消息 | 不清空选择，也不自动勾选 |

**与已有功能的相互影响**（沿用现有规则，本功能不改）：删除置顶消息会同时取消置顶；删除被引用的消息后，引用块显示"该消息已删除"；删除消息会连带删除它上面的表情回复。

---

## 五、验收清单

- [ ] 移动端长按消息，**弹出操作栏**，菜单最下方是「选择」，其余项与设计稿一致。
- [ ] 点「选择」进入多选，**刚点的那条默认已选中**，顶部显示「已选 1 条」。
- [ ] 点整条消息任意位置都能切换勾选。
- [ ] 自己的消息和别人的消息，**圆圈都在最左侧**。
- [ ] 顶部**只有**「✕」和「已选 N 条」，**没有「全选」按钮**。
- [ ] 取消掉最后一条 → **自动退出**选择模式。
- [ ] 一条都没选时，底部「转发」「删除」置灰。
- [ ] 转发 → 复用已有转发选择页；「选择」→ 多选会话 → 右上角「发送 N」。
- [ ] 转发成功提示「已转发」，并退出选择模式。
- [ ] 选中的消息**全部能删** → 弹窗标题是**选中总条数**「删除 N 条消息？」、正文「这 N 条消息将会从所有群成员的聊天中删除。」、按钮「取消 / 删除（红）」。
- [ ] 弹窗里**没有**「为所有人删除 / 仅为我删除」的范围单选。
- [ ] 选中的消息里**只有一部分能删** → 「删除」**不置灰**，点了弹确认窗，标题仍是选中总条数，正文「其中 X 条不是你发送的，你没有权限删除。你的 Y 条消息将会从所有群成员的聊天中删除。」。
- [ ] 上一条点「删除」后，**只删掉自己有权限的 Y 条**，提示「已删除 Y 条消息」。
- [ ] 选中的消息**一条都删不了** → 「删除」**置灰**（唯一置灰场景）。
- [ ] 群主 / 管理员删别人的消息 → 「删除」可用，弹确认窗。
- [ ] 单聊与群组同一套规则：自己发的能删、对方发的删不了，**没有范围单选**。
- [ ] 点遮罩 / 取消 → 弹窗关闭且**保留选中态**。
- [ ] 桌面端右键消息 → 弹出操作栏 → 点「选择」进入多选；顶部有计数栏、底部有操作栏。

---

## 六、关键口径（2026-10-08 已拍板）

| # | 问题 | 结论 |
|---|---|---|
| ① | 删除弹窗里默认选中哪个范围？ | **取消范围单选**——群组只有「对所有群成员删除」一种，没有默认项可选 |
| ② | 普通成员选中别人的消息时，「删除」是**置灰**还是**隐藏**？ | **都不是**：只要有一条能删就保持可点，由确认弹窗说明只删掉哪部分；**只有「一条都删不了」时才置灰** |
| ③ | 单聊要不要保留「仅为我删除」？ | **不保留**，单聊与群组统一为一种范围 |
| ④ | 「只能删一部分」的弹窗文案？ | 标题＝**选中总条数**；正文＝「其中 X 条不是你发送的，你没有权限删除。你的 Y 条消息将会从所有群成员的聊天中删除。」 |

---

## 附：与设计稿的对照

| 设计稿 | 现状 | 本次动作 |
|---|---|---|
| iOS 聊天详情-长按（`802:74269`）：菜单含 … + 分隔线 + **选择** | 已交付 | **不动**，直接作为本功能入口 |
| iOS 全局搜索-未搜索时-链接（多选）（`516:2801`）：左侧勾选圈 | 已交付 | 作为选择态视觉参考 |
| iOS 全局搜索-媒体(长按)-选择-转发（`516:2928`）/ 转发(多选)（`516:3133`） | 已交付 | 转发页**直接复用**，不重画 |
| 聊天详情页的**消息选择态** | **未画** | **本次补齐**（10 屏演示 + 本 PRD） |
