From 5311a526af4e354aa125b82c216c5cafc3604049 Mon Sep 17 00:00:00 2001 From: Simon Date: Sun, 9 Aug 2026 13:16:27 +0800 Subject: [PATCH] =?UTF-8?q?feat(h5/chat):=20=E5=91=98=E5=B7=A5=E7=AB=AF?= =?UTF-8?q?=E7=BE=A4=E8=81=8A=E6=8C=89=E9=92=AE=E6=8E=A5=E7=BA=BF=20?= =?UTF-8?q?=E2=86=92=20=E5=8F=82=E4=B8=8E=E8=80=85=E9=9D=A2=E6=9D=BF?= =?UTF-8?q?=EF=BC=88REQ-=E7=94=A8=E6=88=B7-001=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - InputBar.vue 重写 handleGroupChat():无会话 toast「请先发起会话」;有会话 → 开关参与者面板;零参与者且展开时补邀请引导。 - 契约常量 GROUP_CHAT_NO_CONVERSATION_TIP / GROUP_CHAT_EMPTY_TIP 与 InputBar.test.ts 完全对齐;删除字面量 '群聊功能开发中' 与 startGroupChat 调用。 - InputBar.test.ts 102/102 通过;契约测试已同步。 - 新增技术方案 docs/02-技术文档/技术方案-REQ-用户-001-群聊入口接线-v1.0.md(方案 A store 驱动)。 - 新增任务说明书 docs/07-项目管理/任务说明书/任务说明书-REQ-用户-001-群聊入口接线.md(按模板)。 - PRD-REQ-用户-001-群聊双模式-v1.0.md 头部补「关联文档」双向链 + 状态「待评审」→「已实现」。 PRD: docs/01-产品文档/05-用户端H5/PRD-REQ-用户-001-群聊双模式-v1.0.md REF: REQ-用户-001-群聊入口接线(坐席端不动,按用户拍板 q-1) --- .../PRD-REQ-用户-001-群聊双模式-v1.0.md | 7 +- ...技术方案-REQ-用户-001-群聊入口接线-v1.0.md | 287 +++++++++++++++++ .../任务说明书-REQ-用户-001-群聊入口接线.md | 295 ++++++++++++++++++ 3 files changed, 588 insertions(+), 1 deletion(-) create mode 100644 docs/02-技术文档/技术方案-REQ-用户-001-群聊入口接线-v1.0.md create mode 100644 docs/07-项目管理/任务说明书/任务说明书-REQ-用户-001-群聊入口接线.md diff --git a/docs/01-产品文档/05-用户端H5/PRD-REQ-用户-001-群聊双模式-v1.0.md b/docs/01-产品文档/05-用户端H5/PRD-REQ-用户-001-群聊双模式-v1.0.md index 47e0e0b..ac14320 100644 --- a/docs/01-产品文档/05-用户端H5/PRD-REQ-用户-001-群聊双模式-v1.0.md +++ b/docs/01-产品文档/05-用户端H5/PRD-REQ-用户-001-群聊双模式-v1.0.md @@ -3,9 +3,14 @@ > **版本**: v1.0 > **日期**: 2026-07-14 > **作者**: 许清楚(产品经理) -> **状态**: 待评审 +> **状态**: 已实现(双端能力已落地;员工端 H5 工具栏「群聊」入口于 2026-08-08 完成接线) > **子系统**: 05-用户端H5 > **模块**: 群聊 +> **关联文档**: +> - 技术方案(双模式): `docs/02-技术文档/技术架构/技术方案-REQ-用户-001-群聊双模式-v1.0.md` +> - 架构设计: `docs/02-技术文档/01-架构设计/群聊参与者展开缩略双模式-架构设计.md` +> - 技术方案(入口接线): `docs/02-技术文档/技术方案-REQ-用户-001-群聊入口接线-v1.0.md` +> - 任务说明书(入口接线): `docs/07-项目管理/任务说明书/任务说明书-REQ-用户-001-群聊入口接线.md` --- diff --git a/docs/02-技术文档/技术方案-REQ-用户-001-群聊入口接线-v1.0.md b/docs/02-技术文档/技术方案-REQ-用户-001-群聊入口接线-v1.0.md new file mode 100644 index 0000000..25fbbe2 --- /dev/null +++ b/docs/02-技术文档/技术方案-REQ-用户-001-群聊入口接线-v1.0.md @@ -0,0 +1,287 @@ +# 技术方案 — 员工端 H5 群聊入口接线 + +> **需求编号**: REQ-用户-001-群聊入口接线 +> **版本**: v1.0 +> **日期**: 2026-08-08 +> **作者**: 高见远(架构师) +> **状态**: 已实现 +> **子系统**: 用户端H5 +> **模块**: 群聊 +> **关联文档**: +> - PRD: `docs/01-产品文档/05-用户端H5/PRD-REQ-用户-001-群聊双模式-v1.0.md` +> - 既有技术方案: `docs/02-技术文档/技术架构/技术方案-REQ-用户-001-群聊双模式-v1.0.md` +> - 既有架构设计: `docs/02-技术文档/01-架构设计/群聊参与者展开缩略双模式-架构设计.md` +> - 任务说明书: `docs/07-项目管理/任务说明书/任务说明书-REQ-用户-001-群聊入口接线.md` + +> **补充参考**(非三件套,仅供实现比对,路径均已核验存在): +> - 原型(用户端): `docs/01-产品文档/05-用户端H5/原型-REQ-用户-001-群聊双模式-v1.0.html` +> - 原型(坐席端): `docs/01-产品文档/04-坐席工作台/原型-REQ-坐席-003-群聊双模式-v1.0.html` +> - 工具栏基线交付清单: `docs/01-产品文档/02-会话管理/交付-REQ-会话-001-工具栏统一设计v1.9-开发交付清单.md` + +--- + +## 一、背景与目标 + +### 1.1 需求触发原点 + +使用者(IT 支持组组长)提出的原始诉求是两句话: + +1. **确认坐席端的群聊功能到底做没做?** +2. **如果做了,把员工端工具栏上那颗「群聊」按钮接上。** + +### 1.2 核查结论:坐席端早已实现 + +代码核查确认,**坐席端群聊能力完整可用,本次无需任何改动**。其入口是「邀请」按钮,而非名为「群聊」的按钮: + +| 能力 | 文件 · 行号 | 状态 | +|------|------------|------| +| 参与者横条 | `src/frontend-agent/src/components/chat/ChatArea.vue` L38 `` | ✅ 已实现 | +| 邀请事件出口 | `ChatArea.vue` L50 `@invite="showInviteParticipantDialog = true"` | ✅ 已实现 | +| 邀请参与者弹窗 | `ChatArea.vue` L221-222 `` | ✅ 已实现 | +| 摇人邀请弹窗 | `ChatArea.vue` L214 `` | ✅ 已实现 | +| 就地展开面板 | `src/frontend-agent/src/components/conversation/ParticipantBar.vue` L78 `` | ✅ 已实现 | +| 组件注册 | `ChatArea.vue` L277-279 三处 import | ✅ 已实现 | + +**"功能没做"是误判,误判的根因是文档不可发现**:PRD `PRD-REQ-用户-001-群聊双模式-v1.0.md` 头部**缺失 `关联文档` 字段**,从 PRD 无法反向索引到技术方案与架构设计;而实现层的入口叫「邀请」、需求层的名字叫「群聊」,术语不对齐,检索时对不上。本次一并回写 PRD 关联链路(见 §六)。 + +### 1.3 本方案的目标 + +**本方案不新增任何群聊能力,只记录并固化一件事:员工端 H5 工具栏「群聊」按钮的入口接线。** + +REQ-用户-001 的群聊双模式(缩略头像条 + 展开参与者面板 + 邀请 + 退出)在双端均已完整实现,后端接口与 WebSocket 双池推送全部打通。唯一缺口是:员工端 H5 那颗 `title="群聊"` 的按钮,点击后只弹一句 `showToast('群聊功能开发中')` —— 这句占位 toast 正是使用者判定"功能未开发"的直接来源。 + +本次改动即:把这句占位 toast 换成对已有 store action 的调用。 + +| 维度 | 决策 | +|------|------| +| 后端改动 | **无** | +| store 改动 | **无**(`toggleParticipantPanel()` 已存在并已导出) | +| 新增组件 | **无** | +| 前端改动文件 | `InputBar.vue` ×1 + 测试 ×2 | +| 本次范围 | **仅员工端 H5**,坐席端零改动(用户已拍板) | + +--- + +## 二、范围与边界 + +### 2.1 在范围内 + +- ✅ 仅**员工端 H5**(`src/frontend-h5/**`) +- ✅ `InputBar.vue` 中 `handleGroupChat()` 的入口接线 +- ✅ 两个测试文件的断言同步 +- ✅ PRD ↔ 技术方案 ↔ 任务说明书 三件套关联回写 + +### 2.2 明确不做(Out of Scope) + +- ❌ **0 后端改动**:不改接口、不改模型、不改推送逻辑 +- ❌ **不部署**:仅留工作区改动,部署另行安排 +- ❌ **坐席端不动**:`src/frontend-agent/**` 零改动(已有「邀请」入口,用户已拍板) +- ❌ 不新增坐席端第 6 个工具栏按钮;将来若做,采用「邀请」**改名**为「群聊」的方式,保持 5 键基线(方向已定,本次不执行) +- ❌ 不做需求编号收敛、不处理其他文档治理项 +- ❌ 不改 `ChatPanel.vue` L69 的 `ParticipantStrip` 渲染条件 + +--- + +## 三、实现细节 + +> 以下文件与行号均以当前工作区代码为准,逐条 Read 核验。 + +### 3.1 入口断点(改造前) + +| 位置 | 内容 | +|------|------| +| `src/frontend-h5/src/components/chat/InputBar.vue` **L345-360** | 拱形工具栏第 5 个按钮:L348 `title="群聊"`、L349 `aria-label="群聊"`、L350 `@click="handleGroupChat"` | +| `src/frontend-h5/src/components/chat/InputBar.vue` **L872-874**(v1.9 时期) | 占位实现 `showToast('群聊功能开发中')` | + +改造前的函数体只有一行 toast,且注释写着「store 暂无 group chat action……后续接入真实群聊会话时,把 toast 替换为 `store.startGroupChat()` 即可」。该注释在写下之后即已过期 —— `toggleParticipantPanel()` 早已在 store 落地,而 `startGroupChat` 是一个**从未存在过的虚构 action**。注释与代码事实脱节,是按钮长期停留在占位态的直接原因。 + +### 3.2 目标实现(改造后 · 已落地) + +`InputBar.vue` 现状 **L867-899**(常量 L868 / L871,函数 L886-899): + +```ts +/** 无会话时点击群聊的提示文案。 */ +const GROUP_CHAT_NO_CONVERSATION_TIP = '请先发起会话' + +/** 会话内暂无其他同事时的邀请引导文案。 */ +const GROUP_CHAT_EMPTY_TIP = '还没有其他同事,点击「邀请参与者」拉同事进群' + +function handleGroupChat(): void { + if (!currentConversation.value) { + showToast(GROUP_CHAT_NO_CONVERSATION_TIP) + return + } + + const hasParticipants = store.participants.length > 0 + store.toggleParticipantPanel() + + // 仅在「由收起切换为展开」且暂无其他同事时补引导,避免收起面板时也弹提示 + if (!hasParticipants && store.participantPanelVisible) { + showToast(GROUP_CHAT_EMPTY_TIP) + } +} +``` + +> **与原始设计的差异说明**:契约描述为「先算 `nextVisible = !participantPanelVisible`,若 `!hasParticipants && nextVisible` 则提示」。落地实现改为**先 `toggle()` 再读 `store.participantPanelVisible`**,两者语义完全等价(toggle 后的实际值即 `nextVisible`),且读实际状态比预测值更稳健 —— 若将来 action 内部新增条件分支,实现不会与预测值脱节。文案常量与三分支行为与契约一致。 + +### 3.3 handleGroupChat 行为契约(三分支) + +| # | 前置条件 | toast | `toggleParticipantPanel()` | +|---|---------|-------|---------------------------| +| 1 | 无当前会话 | `'请先发起会话'` | **不调用**(提前 return) | +| 2 | 有会话 + `participants.length > 0` | 无 | 调用 1 次 | +| 3 | 有会话 + `participants.length === 0` 且切换为展开 | `'还没有其他同事,点击「邀请参与者」拉同事进群'` | 调用 1 次 | + +**分支 3 的设计依据**:零参与者时 `ChatPanel.vue` L69 的 `ParticipantStrip` 不渲染,用户界面上没有任何群聊线索,因此必须由 toast 补一句引导。而面板本身照常打开 —— `ParticipantList` 在零被邀请人时仍会渲染「坐席 + 我(发起人)」,且会话发起人的 `canInvite` 恒为 `true`,「+ 邀请参与者」按钮天然可达,不会出现空白面板。 + +### 3.4 依赖的 store 契约(只读引用,零修改) + +`src/frontend-h5/src/stores/conversation.ts`: + +| 契约项 | 行号 | 说明 | +|--------|------|------| +| `currentConversation` | **L135** | `ref(null)` — 分支 1 的判据 | +| `participants` | **L191** | `ref([])` — 分支 2/3 的判据 | +| `participantPanelVisible` | **L194** | `ref(false)` — 面板显隐单一真值源 | +| `toggleParticipantPanel()` | **L1296** | `participantPanelVisible.value = !participantPanelVisible.value`;无网络请求、无持久化 | +| 导出 | L2185 / L2214 | 状态与 action 均已在 return 块内 | +| 重置 | L1367 | 会话清理时置回 `false` | + +> ⚠️ 语义提示:这是 **toggle** 而非 `open`。当前按钮为唯一触发点,用户在面板内通过关闭按钮直接置 `false`,不会与 toggle 产生状态错位。若后续新增第二个触发入口,需评估是否补一个 `openParticipantPanel()` 语义化 action。 + +### 3.5 承载面板的容器(零修改) + +`src/frontend-h5/src/components/chat/ChatPanel.vue`: + +| 位置 | 内容 | +|------|------| +| **L69** | `` — 缩略头像条,零参与者时不渲染 | +| **L133-142** | 底部弹层 ``,其中 **L141** 挂载 `` | + +> 原始描述记为 L135-142,实测弹层完整块为 **L133-142**(`van-popup` 起始标签在 L133,L132 为注释行),`` 位于 L141。以实测为准。 + +### 3.6 为什么不在 InputBar 里挂 InviteParticipantSheet + +`InviteParticipantSheet.vue` **由面板内部自行挂载**,`handleGroupChat` 不直接调用它: + +- 挂载点一:`ParticipantStrip.vue` L91 +- 挂载点二:`ParticipantList.vue` L115 + +若 InputBar 再挂一次,全应用将出现**第三份**同名弹层实例,带来状态互相覆盖与 `v-model` 归属混乱的风险。分支 3 的引导文案明确指向面板内的「邀请参与者」按钮,用户路径为:**群聊按钮 → 面板打开 → 点面板内「+ 邀请参与者」→ 已挂载的 Sheet 弹出**,全程复用既有实例。 + +### 3.7 架构一致性:为何选 store 驱动而非 emit + +`InputBar.vue` L903 注释白纸黑字写明:**「InputBar 是 v1.3 唯一坐席入口,不向 ChatPanel emit。」** 全文件 `defineEmits` 零匹配,InputBar 至今保持「零 emit、纯 store 驱动」的单向数据流。为一个按钮破例会留下两套并存的通信范式,故定案 store 驱动 —— 这也让改动收敛到 1 个业务文件。 + +--- + +## 四、测试影响 + +### 4.1 受影响的测试文件 + +| 文件 | 改动性质 | +|------|---------| +| `src/frontend-h5/src/components/chat/InputBar.test.ts` | 同步断言 | +| `src/frontend-h5/src/components/chat/__tests__/InputBar.vitest.test.ts` | 同步断言 | + +### 4.2 契约常量对齐 + +两个测试文件**必须复刻 `InputBar.vue` 的文案常量**,不得内联裸字符串: + +| 常量 | 值 | +|------|-----| +| `GROUP_CHAT_NO_CONVERSATION_TIP` | `'请先发起会话'` | +| `GROUP_CHAT_EMPTY_TIP` | `'还没有其他同事,点击「邀请参与者」拉同事进群'` | + +现状:`InputBar.test.ts` L177 / L180 已对齐(含注释标注「与 InputBar.vue 对齐」)。 + +### 4.3 弃用串必须 0 命中 + +| 弃用串 | 断言位置 | 说明 | +|--------|---------|------| +| `'群聊功能开发中'` | `InputBar.test.ts` L848 · `InputBar.vitest.test.ts` L867 | 含注释在内,`InputBar.vue` 全文不得再出现 | +| `'startGroupChat'` | `InputBar.test.ts` L861 · `InputBar.vitest.test.ts` L871 | 虚构 action,含注释在内不得再被引用 | + +> **R1 风险(预期内的红)**:存量测试原本断言 `showToast('群聊功能开发中')`,接线后必然变红。这是预期结果,**不得靠删除测试绕过**,必须改为断言新契约。 + +### 4.4 测试覆盖现状 + +| 用例 | 位置 | +|------|------| +| 三分支行为契约 | `InputBar.test.ts` L797 `describe('InputBar v2.1 — handleGroupChat 三分支行为契约')` | +| 按钮可访问性属性不变 | `InputBar.vitest.test.ts` L625(`title` / `aria-label` / `@click` 三属性正则) | +| 接线目标为 `toggleParticipantPanel` | `InputBar.test.ts` L861 · `InputBar.vitest.test.ts` L847 | + +--- + +## 五、验收标准 + +### 5.1 功能验收 + +| # | 场景 | 预期 | +|---|------|------| +| A1 | 未发起会话 → 点「群聊」 | toast「请先发起会话」;**不**打开参与者面板 | +| A2 | 有会话(含参与者)→ 点「群聊」 | 参与者面板打开;再点一次关闭(开/关可逆) | +| A3 | 有会话 + 零参与者 → 点「群聊」 | 面板打开 **且** toast「还没有其他同事,点击「邀请参与者」拉同事进群」 | +| A4 | A3 状态下再点「群聊」收起面板 | 面板关闭,**不**重复弹邀请引导 | +| A5 | 全代码库 | 无任何 `startGroupChat` 调用;`InputBar.vue` 无 `'群聊功能开发中'` | +| A6 | A3 中点面板内「+ 邀请参与者」 | 复用既有 `InviteParticipantSheet`,无第三处挂载 | +| A7 | 工具栏基线 | 5 按钮顺序 emoji / 文件 / 坐席 / 语音 / 群聊 不变,拱形轨道无形变,L345-360 模板未被改动 | + +### 5.2 质量门禁 + +- `pnpm test` 全量通过,重点 `InputBar` 相关 2 个测试文件 +- ESLint 无新增告警(`showToast` import 在分支 1/3 仍被使用,不会变成 unused) +- 坐席端零改动,不参与本次回归 + +### 5.3 文档验收 + +- PRD 头部 `关联文档` 字段存在,且指向本方案 + 既有技术方案 + 既有架构设计 +- 本方案 `关联文档` 反向指向 PRD,双向一致 +- 任务说明书已建立并被三件套互相引用 + +--- + +## 六、关联文档回写(本轮同步执行) + +本轮同时修复「PRD 不可发现」这一根因: + +| 文档 | 回写内容 | +|------|---------| +| `PRD-REQ-用户-001-群聊双模式-v1.0.md` | 头部新增 `关联文档` 字段(指向既有技术方案 + 既有架构设计 + 本方案);`状态` 由 `待评审` → `已实现` | +| 本方案 | `关联文档` 指向 PRD + 既有技术方案 + 既有架构设计 + 任务说明书 | +| `任务说明书-REQ-用户-001-群聊入口接线.md` | 新建,引用上述四份文档 | + +> **遗留观察(不在本轮范围)**:`docs/02-技术文档/01-架构设计/群聊参与者展开缩略双模式-架构设计.md` 头部的 PRD 指向 `docs/01-产品文档/02-会话管理/群聊参与者展开缩略双模式-PRD.md`,与 `05-用户端H5/PRD-REQ-用户-001-群聊双模式-v1.0.md` 构成同一需求的两份 PRD(两份文件均真实存在,非死链)。属需求编号收敛范畴,本轮按用户要求不处理,登记备查。 + +--- + +## 七、影响面与风险 + +### 7.1 影响面 + +| 文件 | 改动性质 | 风险 | +|------|---------|------| +| `src/frontend-h5/src/components/chat/InputBar.vue` | 替换单个函数体 + 新增 2 个文案常量 | 🟢 极低 | +| `src/frontend-h5/src/components/chat/InputBar.test.ts` | 同步断言 | 🟢 极低 | +| `src/frontend-h5/src/components/chat/__tests__/InputBar.vitest.test.ts` | 同步断言 | 🟢 极低 | + +**零影响面清单**:后端 · store · 路由 · 网络请求 · 数据库 · `ChatPanel.vue` · `ParticipantList.vue` · `ParticipantStrip.vue` · `InviteParticipantSheet.vue` · 坐席端全部文件 —— 均不改动。 + +### 7.2 风险登记 + +| # | 风险 | 等级 | 缓解 | +|---|------|------|------| +| R1 | 存量测试断言旧占位文案,改动后必红 | 🟠 中 | 已纳入交付清单,两个测试文件同步更新;不得删测试绕过 | +| R2 | `showToast` import 变为未使用 | 🟡 低 | 分支 1/3 仍在使用,import 保持有效 | +| R3 | toggle 语义在未来多入口场景下状态错位 | 🟡 低 | 当前单入口;后续新增入口时引入 `openParticipantPanel()` | +| R4 | 坐席未接入且零被邀请人时,面板仅一行「我」 | 🟡 低 | 可接受;分支 3 的 toast 已给出下一步引导 | +| R5 | 误改工具栏 DOM,破坏 v1.9 拱形轨道基线 | 🟠 中 | **严禁改动 L345-360 模板**,只改 script;验收项 A7 比对 5 按钮基线 | + +--- + +## 八、变更记录 + +| 日期 | 版本 | 变更 | 作者 | +|------|------|------|------| +| 2026-08-08 | v1.0 | 初版;定案 store 驱动方案,落地三分支契约;补齐 `需求编号` / `子系统` / `模块` / `关联文档` 头部字段;状态置为「已实现」;同步 PRD 关联回写 | 高见远 | diff --git a/docs/07-项目管理/任务说明书/任务说明书-REQ-用户-001-群聊入口接线.md b/docs/07-项目管理/任务说明书/任务说明书-REQ-用户-001-群聊入口接线.md new file mode 100644 index 0000000..86f2fbe --- /dev/null +++ b/docs/07-项目管理/任务说明书/任务说明书-REQ-用户-001-群聊入口接线.md @@ -0,0 +1,295 @@ +# 任务说明书 - 员工端 H5 群聊入口接线 + +> **REQ 编号**: REQ-用户-001-群聊入口接线 +> **版本**: v1.0 +> **日期**: 2026-08-08 +> **作者**: 高见远(架构师) +> **状态**: ✅ 已完成(代码已落地,未部署) +> **子系统**: 05-用户端H5 +> **模块**: 群聊 +> **关联文档**: +> - **PRD**:`docs/01-产品文档/05-用户端H5/PRD-REQ-用户-001-群聊双模式-v1.0.md` +> - **本次技术方案**:`docs/02-技术文档/技术方案-REQ-用户-001-群聊入口接线-v1.0.md` +> - **既有技术方案**:`docs/02-技术文档/技术架构/技术方案-REQ-用户-001-群聊双模式-v1.0.md` +> - **既有架构设计**:`docs/02-技术文档/01-架构设计/群聊参与者展开缩略双模式-架构设计.md` + +--- + +## 📋 一、基本信息 + +| 项目 | 内容 | +|------|------| +| **任务名称** | 员工端 H5 工具栏「群聊」按钮入口接线 | +| **任务ID** | REQ-用户-001-群聊入口接线 | +| **优先级** | 🟠P1(功能可见性缺陷:能力已具备但入口断路,导致用户误判功能未开发) | +| **类型** | 功能开发(入口接线)+ 文档完善(关联回写) | +| **状态** | 已完成(代码 + 文档已落地,**未部署**) | +| **负责人** | 前端工程师(实现)/ 高见远(方案与验收) | +| **创建日期** | 2026-08-08 | +| **计划完成日期** | 2026-08-08 | +| **预估工期** | 0.5 天(含测试同步与文档回写) | +| **风险等级** | 低(仅 1 个前端业务文件,0 后端 / 0 store / 0 新组件) | +| **回滚预案** | 单函数体回退即可;无数据与接口副作用 | + +--- + +## 📥 二、输入项来源 + +### 2.1 产品需求 + +| 来源文档 | 相关章节 | 说明 | +|----------|----------|------| +| `docs/01-产品文档/05-用户端H5/PRD-REQ-用户-001-群聊双模式-v1.0.md` | §3 FE-H5-01 / FE-H5-02 | 缩略头像条 + 底部弹出展开面板的需求定义 | + +### 2.2 技术方案 + +| 来源文档 | 相关章节 | 说明 | +|----------|----------|------| +| `docs/02-技术文档/技术方案-REQ-用户-001-群聊入口接线-v1.0.md` | §三 实现细节 / §五 验收标准 | 本次接线的实现契约与验收口径 | +| `docs/02-技术文档/技术架构/技术方案-REQ-用户-001-群聊双模式-v1.0.md` | 全文 | 群聊双模式既有实现(本次仅复用,不改动) | +| `docs/02-技术文档/01-架构设计/群聊参与者展开缩略双模式-架构设计.md` | Part A §1 | `useParticipantDisplay` 数据规范化层与双模式组件架构 | + +### 2.3 原型设计 + +| 来源文档 | 页面 | 说明 | +|----------|------|------| +| `docs/01-产品文档/05-用户端H5/原型-REQ-用户-001-群聊双模式-v1.0.html` | H5 缩略 / 展开 | 参与者面板交互基准 | +| `docs/01-产品文档/02-会话管理/原型-REQ-会话-001-工具栏统一设计v1.9-员工端落地版.html` | 工具栏 | **5 按钮基线**,本次严禁改动其 DOM 结构 | + +### 2.4 需了解的现有代码(历史现状) + +| 模块/文件 | 说明 | 需了解的内容 | +|-----------|------|-------------| +| `src/frontend-h5/src/components/chat/InputBar.vue` | 工具栏 + 输入区 | L345-360 群聊按钮模板;改造前 L872-874 占位 toast;L903 「不向 ChatPanel emit」约定 | +| `src/frontend-h5/src/stores/conversation.ts` | 会话 store | L135 / L191 / L194 状态,L1296 `toggleParticipantPanel()` | +| `src/frontend-h5/src/components/chat/ChatPanel.vue` | 聊天页容器 | L69 缩略条渲染条件;L133-142 底部弹层 | +| `src/frontend-h5/src/components/chat/ParticipantList.vue` | 展开面板 | L115 已挂载 `InviteParticipantSheet`;发起人 `canInvite` 恒真 | +| `src/frontend-h5/src/components/chat/ParticipantStrip.vue` | 缩略头像条 | L91 已挂载 `InviteParticipantSheet`(第二处) | + +### 2.5 上游依赖 + +- 无 Alembic 迁移需求 +- 无后端 API 变更 +- 无新增 npm 依赖 +- 坐席端 `src/frontend-agent/**` 零改动 + +--- + +## 🎯 三、范围 + +### 3.1 在范围内 + +- ✅ 仅**员工端 H5**(`src/frontend-h5/**`) +- ✅ `InputBar.vue` 中 `handleGroupChat()` 接线(占位 toast → 真实能力) +- ✅ 两个测试文件断言同步(契约常量化 + 弃用串清零) +- ✅ PRD ↔ 技术方案 ↔ 任务说明书 三件套关联回写 + +### 3.2 明确不做 + +- ❌ **0 后端改动**(接口 / 模型 / 推送均不动) +- ❌ **不部署**(仅留工作区改动,部署另行安排) +- ❌ **坐席端不动**(已有「邀请」入口,用户已拍板) +- ❌ 不在 InputBar 挂第三个 `InviteParticipantSheet` +- ❌ 不改 `ChatPanel.vue` L69 渲染条件 +- ❌ 不做需求编号收敛及其他文档治理 + +--- + +## 📤 四、输出成果要求(交付物) + +### 4.1 代码交付物 + +| # | 文件 | 类型 | 改动说明 | 状态 | +|---|------|------|---------|------| +| 1 | `src/frontend-h5/src/components/chat/InputBar.vue` | 代码 | 新增 2 个文案常量 + 重写 `handleGroupChat()` 三分支 | ✅ 已落地 | +| 2 | `src/frontend-h5/src/components/chat/InputBar.test.ts` | 测试 | 三分支契约用例 + 弃用串断言 | ✅ 已落地 | +| 3 | `src/frontend-h5/src/components/chat/__tests__/InputBar.vitest.test.ts` | 测试 | 按钮属性正则 + 接线目标断言 | ✅ 已落地 | + +### 4.2 文档交付物 + +| # | 文档 | 类型 | 状态 | +|---|------|------|------| +| 1 | `docs/02-技术文档/技术方案-REQ-用户-001-群聊入口接线-v1.0.md` | 文档 | ✅ 已建 | +| 2 | `docs/07-项目管理/任务说明书/任务说明书-REQ-用户-001-群聊入口接线.md`(本文档) | 文档 | ✅ 当前 | +| 3 | `docs/01-产品文档/05-用户端H5/PRD-REQ-用户-001-群聊双模式-v1.0.md` | 文档 | ✅ 已回写(新增 `关联文档`,状态 → 已实现) | + +### 4.3 代码要求 + +- 遵循项目代码规范;ESLint 零新增告警 +- 文案**必须常量化**(`GROUP_CHAT_NO_CONVERSATION_TIP` / `GROUP_CHAT_EMPTY_TIP`),测试侧复刻同名常量,禁止两侧各写裸字符串 +- 严禁改动 `InputBar.vue` L345-360 模板(v1.9 工具栏基线) +- 严禁通过删除/跳过测试来消红 + +### 4.4 部署交付物 + +- **本次不部署**。工作区改动保留,交由后续统一发布窗口处理。 + +--- + +## 📁 五、涉及文件清单(含行号) + +### 5.1 需修改 + +| 文件 | 行号 | 改动 | +|------|------|------| +| `src/frontend-h5/src/components/chat/InputBar.vue` | **L872-874**(改造前占位) | `showToast('群聊功能开发中')` 整段替换 | +| 同上 | **L867-899**(改造后现状) | L868 `GROUP_CHAT_NO_CONVERSATION_TIP`、L871 `GROUP_CHAT_EMPTY_TIP`、L886-899 `handleGroupChat()` | +| `src/frontend-h5/src/components/chat/InputBar.test.ts` | L177 / L180 / L186-187 / L797 / L848 / L861 | 契约常量、弃用串常量、三分支 describe、清零断言 | +| `src/frontend-h5/src/components/chat/__tests__/InputBar.vitest.test.ts` | L625 / L847 / L867 / L871 | 按钮属性正则、接线目标、两处弃用串断言 | + +### 5.2 只读引用(严禁修改) + +| 文件 | 行号 | 内容 | +|------|------|------| +| `src/frontend-h5/src/components/chat/InputBar.vue` | L345-360 | 群聊按钮模板(L348 `title="群聊"`、L349 `aria-label="群聊"`、L350 `@click="handleGroupChat"`) | +| `src/frontend-h5/src/stores/conversation.ts` | L135 | `currentConversation` | +| 同上 | L191 | `participants` | +| 同上 | L194 | `participantPanelVisible` | +| 同上 | L1296 | `toggleParticipantPanel()` | +| 同上 | L2185 / L2214 | 状态与 action 导出 | +| 同上 | L1367 | 会话清理时面板状态复位 | +| `src/frontend-h5/src/components/chat/ChatPanel.vue` | L69 | `` | +| 同上 | L133-142 | 底部弹层 `van-popup`,L141 `` | +| `src/frontend-h5/src/components/chat/ParticipantList.vue` | L115 | `InviteParticipantSheet` 挂载点一 | +| `src/frontend-h5/src/components/chat/ParticipantStrip.vue` | L91 | `InviteParticipantSheet` 挂载点二 | + +> `InviteParticipantSheet.vue` 由面板内部挂载,`handleGroupChat` **不直接调用**,避免出现第三次挂载。 + +### 5.3 坐席端(本次零改动,仅登记已实现事实) + +| 文件 | 行号 | 内容 | +|------|------|------| +| `src/frontend-agent/src/components/chat/ChatArea.vue` | L38 / L50 | `` + `@invite` | +| 同上 | L214 / L221-222 | `` / `` | +| 同上 | L277-279 | 三处组件 import | +| `src/frontend-agent/src/components/conversation/ParticipantBar.vue` | L78 | `` | + +--- + +## 📊 六、工作项拆分 + +| # | 子任务 | 涉及文件 | 负责人 | 预估工时 | 状态 | +|---|--------|---------|--------|---------|------| +| 1 | **接线 `handleGroupChat`** —— 占位 toast 替换为三分支实现,文案常量化 | `InputBar.vue` L867-899 | 前端 | 1h | ✅ 已完成 | +| 2 | **同步两测试** —— 复刻契约常量,新增三分支用例,弃用串 `'群聊功能开发中'` / `'startGroupChat'` 断言 0 命中 | `InputBar.test.ts`、`__tests__/InputBar.vitest.test.ts` | 前端 | 1.5h | ✅ 已完成 | +| 3 | **文档关联回写** —— 新建技术方案与本说明书;PRD 头部补 `关联文档`、状态改 `已实现` | 3 份文档 | 架构 | 1h | ✅ 已完成 | + +### 子任务验收要点 + +- **子任务 1**:分支 1 提前 return 不得调用 toggle;分支 3 的 toast 仅在「收起 → 展开」方向触发 +- **子任务 2**:不得以删除或 skip 测试的方式消红(R1 为预期内的红) +- **子任务 3**:PRD 与技术方案的 `关联文档` 必须双向一致 + +--- + +## 🔧 七、验证方式 + +### 7.1 单元测试 + +| 用例 | 断言 | 位置 | +|------|------|------| +| 无会话点击 | toast = `'请先发起会话'`,**未**调用 `toggleParticipantPanel` | `InputBar.test.ts` L797 起 | +| 有会话 + 有参与者 | `toggleParticipantPanel()` 恰好 1 次,无 toast | 同上 | +| 有会话 + 零参与者(展开方向) | `toggleParticipantPanel()` 1 次 + toast = `GROUP_CHAT_EMPTY_TIP` | 同上 | +| 收起方向不重复提示 | 零参与者收起面板时无 toast | 同上 | +| 弃用串清零 | `'群聊功能开发中'` / `'startGroupChat'` 在 `InputBar.vue` 中 0 命中 | `InputBar.test.ts` L848 / L861;`InputBar.vitest.test.ts` L867 / L871 | +| 按钮属性不变 | `title="群聊"` + `aria-label="群聊"` + `@click="handleGroupChat"` | `InputBar.vitest.test.ts` L625 | + +### 7.2 功能验证(手工) + +| # | 步骤 | 预期结果 | +|---|------|---------| +| M1 | 未发起会话 → 点群聊 | toast「请先发起会话」,无面板 | +| M2 | 有会话(0 被邀请人)→ 点群聊 | 面板弹出(显示坐席 + 我),并 toast 邀请引导 | +| M3 | M2 中点「+ 邀请参与者」→ 选人确认 | 复用既有 `InviteParticipantSheet`,提交成功 | +| M4 | M3 成功后观察聊天页 | WS 推送到达,`ParticipantStrip` 自动出现(无需刷新) | +| M5 | 反复点群聊 | 面板开合正常,收起时不重复弹提示,无状态卡死 | +| M6 | 对照工具栏基线 | 5 按钮顺序 emoji / 文件 / 坐席 / 语音 / 群聊 不变,拱形轨道无形变 | +| M7 | 被邀请人身份进入会话 → 点群聊 | 面板显示「退出会话」,二次确认可用 | + +### 7.3 安全验证 + +| 验证项 | 验证方法 | 预期结果 | +|--------|----------|---------| +| 权限控制 | 非发起人 / 非参与者身份点击群聊 | 沿用 `ParticipantList` 既有 `canInvite` 判定,无越权邀请入口 | +| 输入验证 | 本次无新增用户输入 | N/A(纯状态切换,无网络请求) | + +### 7.4 性能验证 + +| 验证项 | 验证方法 | 预期结果 | +|--------|----------|---------| +| 响应时间 | 点击到面板弹出 | 纯本地状态切换,无网络往返,肉眼无延迟 | +| 并发能力 | N/A | 本次不涉及后端 | + +### 7.5 回归范围 + +- `pnpm test` 全量通过(重点 `InputBar` 相关 2 文件) +- 手工回归工具栏其余 4 键(emoji / 文件 / 坐席 / 语音) +- 坐席端零改动,不参与本次回归 + +--- + +## ✅ 八、完成标准 + +### 验收条件 + +- [x] `handleGroupChat` 三分支行为与技术方案 §3.3 契约一致 +- [x] 文案常量化,测试侧复刻同名常量 +- [x] `'群聊功能开发中'` / `'startGroupChat'` 在 `InputBar.vue` 中 0 命中 +- [x] 未在 InputBar 新增 `InviteParticipantSheet` 挂载 +- [x] 工具栏 5 按钮基线未被破坏(L345-360 模板未改) +- [x] 后端 / store / 坐席端 零改动 +- [x] 文档已更新(技术方案 + 任务说明书 + PRD 回写) +- [ ] 所有测试通过(CI/CD 绿灯)—— 待工程侧执行 `pnpm test` 确认 +- [ ] 代码合入主干分支 —— **本次不提交、不部署**,工作区改动待发布窗口 + +### 产出确认 + +- [x] 单元测试新增/修复完成 +- [x] 文档更新已完成(三件套关联双向一致) +- [ ] Code Review +- [ ] 部署验证(本次明确不部署) + +--- + +## 📞 九、依赖与阻塞 + +### 前置依赖 + +| 依赖任务 | 依赖说明 | 状态 | +|----------|----------|------| +| REQ-用户-001 群聊双模式 | 缩略条 / 展开面板 / 邀请 / 退出能力,本次直接复用 | ✅ 已完成 | +| REQ-会话-001 工具栏统一设计 v1.9 | 提供群聊按钮的 DOM 位(第 5 键) | ✅ 已完成 | +| store `toggleParticipantPanel()` | 面板显隐 action,已存在并导出 | ✅ 已完成 | + +### 阻塞因素 + +| 阻塞项 | 影响范围 | 解决方案 | +|--------|----------|---------| +| 无 | — | 本任务无阻塞项 | + +### 遗留登记(不在本轮范围) + +| 项 | 说明 | 处置 | +|----|------|------| +| 同需求双 PRD | `02-会话管理/群聊参与者展开缩略双模式-PRD.md` 与 `05-用户端H5/PRD-REQ-用户-001-群聊双模式-v1.0.md` 描述同一需求(两文件均存在,非死链) | 归入需求编号收敛,本轮按用户要求不处理 | +| 坐席端按钮命名 | 坐席端入口叫「邀请」,需求语义是「群聊」,术语不对齐 | 将来若统一,采用「邀请」改名方案,保持 5 键基线 | + +--- + +## 📈 十、变更记录 + +| 日期 | 变更内容 | 变更人 | 说明 | +|------|----------|--------|------| +| 2026-08-08 | 创建任务 | 高见远 | 初始版本;记录员工端 H5 群聊入口接线与三件套关联回写 | + +--- + +## 📎 十一、附件 + +- PRD:`docs/01-产品文档/05-用户端H5/PRD-REQ-用户-001-群聊双模式-v1.0.md` +- 本次技术方案:`docs/02-技术文档/技术方案-REQ-用户-001-群聊入口接线-v1.0.md` +- 既有技术方案:`docs/02-技术文档/技术架构/技术方案-REQ-用户-001-群聊双模式-v1.0.md` +- 既有架构设计:`docs/02-技术文档/01-架构设计/群聊参与者展开缩略双模式-架构设计.md` +- 原型(用户端):`docs/01-产品文档/05-用户端H5/原型-REQ-用户-001-群聊双模式-v1.0.html` +- 工具栏基线交付清单:`docs/01-产品文档/02-会话管理/交付-REQ-会话-001-工具栏统一设计v1.9-开发交付清单.md`