Files
wecom_it_smart_desk/docs/03-测试文档/05-缺陷单/BUG-坐席-002-选项汇总标签缺失-001.md
T
Simon 44e77dcb0e chore(docs): docs/ 目录全面重新编号 + 重组
**重构前**(旧编号 02-11):
- docs/02-产品需求/      → 00 产品规划/PRD
- docs/03-技术架构/      → 01-05 子目录散落
- docs/04-原型设计/      → 01-02 产品设计(HTML 原型)
- docs/05-原型设计/      → screens/
- docs/06-测试素材/      → 02-E2E / 03-功能 / 04-版本测试
- docs/07-项目管理/      → 任务说明书/日报/计划
- docs/08-安全审计/      → 审计报告
- docs/09-堡垒运维/      → toolbox / deploy
- docs/10-项目管理/      → 任务说明书(重复)
- docs/11-历史归档/      → deploy-nas-archived

**重构后**(新编号 00-07,语义化):
- docs/00-产品开发流程与文档管理规范.md
- docs/00-版本迭代总览.md
- docs/01-产品文档/      (PRD/原型/认证/会话/AI 服务/坐席/集成)
- docs/02-技术文档/      (技术方案/架构图/重构记录/前端改造/实现配置)
- docs/03-测试文档/      (E2E/功能用例/版本报告/缺陷单)
- docs/04-运维文档/      (部署运维/运维指南)
- docs/05-运营文档/      (品牌推广/用户手册)
- docs/06-安全审计/      (审计报告)
- docs/07-项目管理/      (任务说明书/日报/计划/看板)

**净收益**:
- 目录编号与产品文档管理规范对齐(按文档阶段 01-07 编号)
- 消除 02-产品需求 与 10-项目管理 的编号重叠
- 子目录按文档类型分组(如 01-产品文档/00-产品规划、01-产品文档/01-认证与登录)
- 把运维/安全/项目管理从 0X 散落改为 04/06/07

合计 494 文件 + 78495 行 / - 14076 行
2026-08-03 18:46:55 +08:00

256 lines
12 KiB
Markdown
Raw 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.
# 缺陷单:坐席端缺"已选:xxx ✓"汇总标签——员工最近一次选项选择不可见
> **缺陷编号**: BUG-坐席-002
> **版本**: v1.0
> **状态**: [已修复]
> **优先级**: P3-Low
> **发现日期**: 2026-07-28
> **发现人**: 宋献
> **指派人**: 宋献
> **修复人**: Duckula (AI助手)
> **关闭日期**: -
> **处理方式**: 自动处理和部署
> **关联需求**: REQ-坐席-002AI 辅助消息框)
> **关联文档**:
> - 故障手册: `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 结构化消息的"推荐选项"(如"卡纸/缺墨/其他"→"提示错误/无法连接/..."→"错误代码/文字提示/..."<br>2. 后端通过 WS 广播 `option_selected` 事件到坐席端<br>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
<div v-if="message.msg_type === 'ai_structured' && message.extra_data?.options?.length" class="ai-structured-options">
<span class="ai-structured-options__label">推荐选项</span>
<span
v-for="(option, idx) in message.extra_data.options"
:key="idx"
class="ai-structured-options__tag"
:class="{ 'ai-structured-options__tag--selected': conversationStore.selectedOptionLabels.includes(option.label || option.value) }"
>
{{ option.label || option.value }}<span v-if="conversationStore.selectedOptionLabels.includes(option.label || option.value)" class="ai-structured-options__check"> </span>
</span>
</div>
```
### 3.2 缺失项
- **无"汇总徽章"设计**:只有"✓ 在被选条目后"的局部标记,缺少整体汇总
- **坐席需扫读多气泡**:决策路径分布在多条 AI 消息的多个 ✓ 标记中,无法一眼看清
---
## 4. 处理办法
| 项目 | 内容 |
|------|------|
| 处理策略 | 自动处理和部署(v2.2 增量) |
| 执行时机 | 立即(影响坐席工作效率) |
| 处理流程 | 1. 新增 `latestSelectedLabel` computed(取 `selectedOptionLabels` 数组最后一项)<br>2. 新增 `messageHasLatestSelected` computed**关键**:判断当前 AI 消息的 options 是否包含最近一次被选 label<br>3. 模板新增"已选:xxx ✓"绿色徽章(`v-if="messageHasLatestSelected"`<br>4. CSS 新增 `.ai-structured-options__summary` 系列样式<br>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<string>(() => {
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<boolean>(() => {
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
<div v-if="message.msg_type === 'ai_structured' && message.extra_data?.options?.length" class="ai-structured-options">
<!-- v2.2: 员工最近一次答案选择汇总仅在该 AI 消息的 options 包含最近一次被选的 label 时显示 -->
<div
v-if="messageHasLatestSelected"
class="ai-structured-options__summary"
>
<span class="ai-structured-options__summary-label">已选</span>
<span class="ai-structured-options__summary-value">{{ latestSelectedLabel }}</span>
<span class="ai-structured-options__summary-check"></span>
</div>
<span class="ai-structured-options__label">推荐选项</span>
...
</div>
```
#### 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-坐席-002AI 辅助消息框)
- **关联技术方案**: `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 修复需同步周边文档 | 文档体系 |
---
> **缺陷状态**: [已修复] — 部署完成,等用户人工扫码验证。验证通过后请将状态改为 `[已验证]`,并补充关闭日期。