# 缺陷单:坐席端缺"已选:xxx ✓"汇总标签——员工最近一次选项选择不可见
> **缺陷编号**: BUG-坐席-002
> **版本**: v1.0
> **状态**: [已修复]
> **优先级**: P3-Low
> **发现日期**: 2026-07-28
> **发现人**: 宋献
> **指派人**: 宋献
> **修复人**: Duckula (AI助手)
> **关闭日期**: -
> **处理方式**: 自动处理和部署
> **关联需求**: REQ-坐席-002(AI 辅助消息框)
> **关联文档**:
> - 故障手册: `docs/04-运维文档/部署运维/00-标准故障排查手册.md`(CASE-20260728-06)
> - 技术方案: `docs/02-技术文档/技术架构/技术方案-REQ-坐席-002-AI辅助消息框-v1.0.md`
---
## 1. 基本信息
| 字段 | 内容 |
|------|------|
| 缺陷标题 | 坐席端缺"已选:xxx ✓"汇总标签——员工最近一次选项选择不可见 |
| 影响范围 | 坐席端所有"AI 结构化消息"渲染(涉及 `ai_structured` 类型消息的"推荐选项"区) |
| 涉及模块 | 坐席端(frontend-agent) |
| 涉及文件 | `src/frontend-agent/src/components/chat/MessageBubble.vue`(ai-structured-options 渲染区) |
| 触发条件 | 1. 员工在 H5 端依次点选 AI 结构化消息的"推荐选项"(如"卡纸/缺墨/其他"→"提示错误/无法连接/..."→"错误代码/文字提示/...")
2. 后端通过 WS 广播 `option_selected` 事件到坐席端
3. 坐席端 store `conversationStore.selectedOptionLabels` 累加被选 label |
| 预期行为 | 在**最近一次被选的那条 AI 消息**(即选项区里包含最近一次被选 label 的那条)的"推荐选项"区域**顶部**,加一个明显的"已选:错误代码 ✓"绿色徽章,让坐席一眼看到员工最终选了哪个。前几条历史 AI 消息的选项区不显示这个徽章(保持现状的"✓ 在被选条目后"设计) |
| 实际行为(修复前) | 坐席端只能看到每个 AI 消息选项区里已选条目后面带 `✓` 标记,但**无法一眼看出员工最终选了哪个**。需要逐个气泡阅读"已选 ✓"标记才能拼出完整决策路径。 |
---
## 2. 复现步骤
1. 员工在 H5 端进入"打印机"会话
2. 员工依次点选 AI 提供的选项:
- 第一条 AI 消息"已刷卡但打不出?...":点"卡纸"
- 后续交互再点"其他"等
- 第二条 AI 消息"其他问题?...":点"提示错误"
- 第三条 AI 消息"提示错误?...":点"错误代码"
3. 坐席端 store 收到 WS 广播 `option_selected` 事件,`selectedOptionLabels` 数组累加 `["卡纸", "其他", "提示错误", "错误代码"]`
4. 坐席端打开该会话,滚动到 3 条 AI 消息
5. **观察**:每条 AI 消息的"推荐选项"区只在自己被选条目后带 ✓ 标记
6. **问题**:坐席需要扫读 3 个气泡的 ✓ 标记才能知道员工最终选了"错误代码"(第 3 条)
### 影响截图
- 页面:坐席端 → "打印机"会话
- 元素:3 条 AI 消息下方的"推荐选项"区
- 期望:在第 3 条 AI 消息选项区顶部显示"已选:错误代码 ✓"绿色徽章
---
## 3. 根因分析
### 3.1 现状(修复前)
`src/frontend-agent/src/components/chat/MessageBubble.vue` 第 55-65 行(v2.1 版本)只渲染了选项列表:
```vue
推荐选项:
{{ option.label || option.value }} ✓
```
### 3.2 缺失项
- **无"汇总徽章"设计**:只有"✓ 在被选条目后"的局部标记,缺少整体汇总
- **坐席需扫读多气泡**:决策路径分布在多条 AI 消息的多个 ✓ 标记中,无法一眼看清
---
## 4. 处理办法
| 项目 | 内容 |
|------|------|
| 处理策略 | 自动处理和部署(v2.2 增量) |
| 执行时机 | 立即(影响坐席工作效率) |
| 处理流程 | 1. 新增 `latestSelectedLabel` computed(取 `selectedOptionLabels` 数组最后一项)
2. 新增 `messageHasLatestSelected` computed(**关键**:判断当前 AI 消息的 options 是否包含最近一次被选 label)
3. 模板新增"已选:xxx ✓"绿色徽章(`v-if="messageHasLatestSelected"`)
4. CSS 新增 `.ai-structured-options__summary` 系列样式
5. 本地构建 → 上传 → 服务器解压 → docker restart nginx |
| 验证方式 | 部署层验证:curl 200 OK + grep 确认新代码生效 + Workspace-B-lO49Vu.js 部署到容器 |
| 回滚方案 | 服务器保留 `dist.bak.option-summary.v2.2b/` 备份目录;紧急回滚 `mv dist.bak.option-summary.v2.2b dist && docker restart wecom_it_nginx` |
---
## 5. 修复方案
### 5.1 代码修复(`MessageBubble.vue`)
#### 5.1.1 新增计算属性 `latestSelectedLabel`
```typescript
/**
* 员工最近一次答案选择(v2.2 新增:坐席端"已选:xxx ✓"汇总标签)
*
* 做什么:从 conversationStore.selectedOptionLabels 数组中取最后一个值(最近一次被选的)
* 为什么:让坐席在每条 AI 消息的选项区顶部一眼看到员工最终选了哪个
* 边界:空数组时返回空字符串(不显示"已选"标签)
*/
const latestSelectedLabel = computed(() => {
const labels = conversationStore.selectedOptionLabels
if (!labels || labels.length === 0) return ''
return labels[labels.length - 1]
})
```
#### 5.1.2 新增计算属性 `messageHasLatestSelected`(**关键**)
```typescript
/**
* 当前 AI 消息是否包含最近一次被选的选项(v2.2 配合"已选"标签使用)
*
* 做什么:判断 props.message.extra_data.options 中是否包含 latestSelectedLabel
* 为什么:用户期望"已选:xxx ✓"只显示在"最近一次被选的那条 AI 消息"的选项区
* 前几条历史 AI 消息的选项区不包含最近一次被选的 label,所以不显示
* 边界:latestSelectedLabel 为空时返回 false(不显示"已选"标签)
*/
const messageHasLatestSelected = computed(() => {
if (!latestSelectedLabel.value) return false
const options = props.message.extra_data?.options || []
return options.some(
(opt: any) => (opt?.label || opt?.value) === latestSelectedLabel.value
)
})
```
#### 5.1.3 模板新增"已选:xxx ✓"绿色徽章
```vue
已选:
{{ latestSelectedLabel }}
✓
推荐选项:
...
```
#### 5.1.4 CSS 样式
```css
/* v2.2 新增:员工最近一次答案选择汇总(绿色徽章,位于选项区顶部) */
.ai-structured-options__summary {
display: inline-flex;
align-items: center;
gap: 4px;
margin-right: 4px;
padding: 3px 10px;
background: #07C160;
color: #fff;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
line-height: 1.4;
/* 占据整行宽度,让"推荐选项:"换到下一行更清晰 */
flex-basis: 100%;
margin-bottom: 4px;
}
.ai-structured-options__summary-label { font-weight: 500; opacity: 0.9; }
.ai-structured-options__summary-value { font-weight: 700; }
.ai-structured-options__summary-check { font-weight: 700; margin-left: 2px; }
```
### 5.2 关键设计
- **条件渲染下沉到 message 级**:`messageHasLatestSelected` 把"是否显示徽章"的判断**下沉到每条消息**——只有**包含最近一次被选 label 的那条 AI 消息**才显示"已选"徽章
- **前几条历史 AI 消息的选项区不显示**:避免无差别地所有 AI 消息都显示"已选"造成误导
- **`flex-basis: 100%` 让徽章独占一行**:避免和"推荐选项:"label 挤在同一行造成视觉混乱
### 5.3 首次实现的 Bug 与修正
首次实现的 v2.2a 版(`Workspace-CdGky81t.js`)**未做"message 级判断"**——`v-if="latestSelectedLabel"` 会让**所有** AI 消息的选项区都显示"已选"徽章(前两条历史 AI 消息也会显示"已选:错误代码 ✓")。
修正后 v2.2b 版(`Workspace-B-lO49Vu.js`)加 `messageHasLatestSelected` 判断,**只在最近一次被选的那条 AI 消息显示**,符合用户期望的"只显示最近一次的答案选择"。
---
## 6. 验证结果
### 6.1 部署层验证
| 验证项 | 命令/方式 | 结果 |
|--------|----------|------|
| 本地构建 | `npm run build` | ✅ 5.21s |
| 构建产物 | `dist/assets/Workspace-B-lO49Vu.js`(hash 变化) | ✅ 157.30 kB |
| 打包 zip | `dist-agent-option-summary-v2.2b.zip` | ✅ 1,621,981 B |
| 上传校验 | `v2_ops.py upload` md5 校验 | ✅ 通过 |
| 服务器解压 | `unzip /tmp/dist-agent-option-summary-v2.2b.zip` | ✅ 成功 |
| Nginx 重启 | `docker restart wecom_it_nginx` | ✅ Up 3 seconds |
| 外部访问 | `curl -I https://itsupport.servyou.com.cn/itagent/assets/Workspace-B-lO49Vu.js` | ✅ HTTP 200 |
| 新代码生效 | `grep -c "messageHasLatestSelected" .../Workspace-B-lO49Vu.js` | ✅ 1(命中) |
| 容器内文件 | `docker exec wecom_it_nginx ls /usr/share/nginx/html/itagent/assets/ \| grep Workspace` | ✅ 命中 `Workspace-B-lO49Vu.js` |
### 6.2 中文标签验证
```powershell
PS> $content = Get-Content "Workspace-B-lO49Vu.js" -Raw
PS> ([regex]::Matches($content, "已选")).Count
4
```
`已选` 出现 4 次(在 Vue 模板字符串中),字符编码正常,无 GBK 误读。
### 6.3 真实浏览器验证
⏳ **待用户在坐席端人工验证**(企微扫码登录后):
1. 打开"打印机"会话
2. 观察 3 条 AI 消息的选项区
3. **预期看到**:第 3 条 AI 消息("提示错误?...")的选项区**顶部**有**绿色徽章** "已选:错误代码 ✓"
4. **预期看到**:前两条 AI 消息的选项区**不**显示"已选"徽章(保持现有 ✓ 在被选条目后的设计)
5. 验证通过后,将本文档状态改为 `[已验证]`
### 6.4 浏览器测试硬性限制
坐席端是**企微扫码登录**,agent-browser 无法脚本模拟扫码。**真实浏览器测试必须由用户人工扫码完成**。
---
## 7. 关联信息
- **关联需求**: REQ-坐席-002(AI 辅助消息框)
- **关联技术方案**: `docs/02-技术文档/技术架构/技术方案-REQ-坐席-002-AI辅助消息框-v1.0.md`
- **关联故障手册**: `docs/04-运维文档/部署运维/00-标准故障排查手册.md`(v3.1 新增 CASE-20260728-06)
- **修复代码文件**: `src/frontend-agent/src/components/chat/MessageBubble.vue`
- **部署包**: `deploy-temp/dist-agent-option-summary-v2.2b.zip`(保留作为部署物证)
---
## 8. 变更记录
| 日期 | 版本 | 变更内容 | 变更人 | 变更原因 | 影响范围 |
|------|------|----------|--------|----------|----------|
| 2026-07-28 | v1.0 | 创建缺陷单,记录坐席端缺"已选:xxx ✓"汇总标签 | 宋献 / Duckula | 用户反馈坐席无法一眼看清员工最终选项 | 坐席端所有 AI 结构化消息渲染 |
| 2026-07-28 | v1.0 | 修复 MessageBubble.vue:新增 `latestSelectedLabel` + `messageHasLatestSelected` 两个 computed | Duckula | 缺少"消息级判断"导致 v2.2a 误显示 | 坐席端 MessageBubble 组件 |
| 2026-07-28 | v1.0 | v2.2a 部署后修正为 v2.2b:补 `messageHasLatestSelected` 避免前几条历史 AI 消息也显示徽章 | Duckula | 用户要求"只显示最近一次",避免误导 | 坐席端 MessageBubble 组件 |
| 2026-07-28 | v1.0 | 本地构建 + 服务器部署 + nginx 重启 + curl/grep 验证 | Duckula | 部署层确认修复实际生效 | 坐席端 dist 资产 |
| 2026-07-28 | v1.0 | 文档规范化:故障手册 v3.0→v3.1 + 缺陷单 README 追加清单 | Duckula | 规范要求 BUG 修复需同步周边文档 | 文档体系 |
---
> **缺陷状态**: [已修复] — 部署完成,等用户人工扫码验证。验证通过后请将状态改为 `[已验证]`,并补充关闭日期。