189 lines
15 KiB
Markdown
189 lines
15 KiB
Markdown
|
|
# 坐席端截图 + 拍照功能 PRD
|
|||
|
|
|
|||
|
|
## 项目信息
|
|||
|
|
|
|||
|
|
| 项 | 内容 |
|
|||
|
|
|---|---|
|
|||
|
|
| **语言** | 中文 |
|
|||
|
|
| **技术栈** | Vue3 + Element Plus + Tailwind CSS |
|
|||
|
|
| **项目名称** | agent_screenshot_camera |
|
|||
|
|
| **前端目录** | `D:\资料\03-项目开发\wecom_it_smart_desk\frontend-agent` |
|
|||
|
|
| **涉及组件** | `ReplyBox.vue` / `ScreenshotEditor.vue` / `ScreenCapture.vue`(改造)+ 新增 `CameraCapture.vue` |
|
|||
|
|
|
|||
|
|
### 原始需求复述
|
|||
|
|
|
|||
|
|
IT智能服务台坐席端(PC Web 应用)需要增加截图和相机拍照功能。客服在处理工单时经常需要截取屏幕内容或拍照来辅助沟通。截图需支持全屏/窗口/标签页选择(含跨屏),截图时屏幕变暗出现矩形选框 + 工具栏(马赛克、文字标注、箭头、撤销),样式参考微信/企微截图工具。拍照需用 `getUserMedia` + `<video>` 实现内嵌摄像头预览,支持重拍。截图和拍照结果均插入到聊天输入区作为图片消息等待发送(非立即发送)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 产品定义
|
|||
|
|
|
|||
|
|
### 产品目标
|
|||
|
|
|
|||
|
|
1. **提升沟通效率**:客服无需切换到外部截图工具或手机拍照,直接在工作台内完成截图/拍照→编辑→发送的全流程,减少上下文切换。
|
|||
|
|
2. **保证信息准确性**:通过截图标注(马赛克打码、箭头指引、文字说明)和实时拍照,让客服能够精准传递屏幕画面和实物状况,减少口头描述的歧义。
|
|||
|
|
3. **统一图片消息发送体验**:截图和拍照结果统一进入输入区待发送队列,客服可在发送前预览、补充文字说明或删除,与现有粘贴图片/文件上传的交互保持一致。
|
|||
|
|
|
|||
|
|
### 用户故事
|
|||
|
|
|
|||
|
|
1. **作为客服坐席**,我想直接在工作台点击截图按钮截取屏幕画面,这样我就能把用户报障的屏幕现象快速发给用户确认,而不需要切换到微信截图或系统截图工具。
|
|||
|
|
|
|||
|
|
2. **作为客服坐席**,我想在截图时用马赛克遮挡敏感信息(如密码、个人信息),这样在发送截图时不会泄露用户隐私。
|
|||
|
|
|
|||
|
|
3. **作为客服坐席**,我想在截图选区内用箭头和文字标注重点位置,这样用户能快速理解我指的具体是哪里。
|
|||
|
|
|
|||
|
|
4. **作为客服坐席**,我想通过摄像头拍照来记录实物设备状态(如硬件故障、线缆连接),这样比文字描述更直观。
|
|||
|
|
|
|||
|
|
5. **作为客服坐席**,我想截图/拍照后的图片先进入输入区等我自己确认后再发送,这样我能在发送前检查标注是否正确、是否打码到位。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 技术规范
|
|||
|
|
|
|||
|
|
### 现有代码分析
|
|||
|
|
|
|||
|
|
通过对 `frontend-agent/src/components/chat/` 目录的代码审查,发现以下现状:
|
|||
|
|
|
|||
|
|
| 组件 | 现状 | 差距 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `ReplyBox.vue` | 工具栏有表情、文件、邀请、快速回复按钮;已有 `handleScreenshot()`(用 `html2canvas`)和 `handleScreenCapture()`(用 `getDisplayMedia`,但未接入任何按钮) | 缺少截图/拍照入口按钮;截图结果直接上传发送而非插入输入区 |
|
|||
|
|
| `ScreenshotEditor.vue` | 裁剪 + 编辑两步模式,工具有画笔/高亮/矩形/箭头/文字,支持撤销/重做 | 缺少马赛克工具 |
|
|||
|
|
| `ScreenCapture.vue` | 全屏遮罩框选模式,工具有画笔/矩形/箭头/文字 | 缺少马赛克工具;结果直接发送 |
|
|||
|
|
| `upload.ts` | 文件上传 API,支持重试,返回 `{ url, filename, file_size, msg_type }` | 可复用 |
|
|||
|
|
| 拍照功能 | **完全不存在** | 需新建 `CameraCapture.vue` 组件 |
|
|||
|
|
|
|||
|
|
**关键发现**:`ReplyBox.vue` 中已有 `handleScreenCapture()` 方法调用了 `navigator.mediaDevices.getDisplayMedia()`,但该方法未绑定到任何 UI 按钮。当前截图按钮实际走的是 `handleScreenshot()` → `html2canvas`(仅能截取当前页面 DOM,无法截取其他窗口/屏幕)。
|
|||
|
|
|
|||
|
|
### 需求池
|
|||
|
|
|
|||
|
|
#### P0 — 必须实现(Must Have)
|
|||
|
|
|
|||
|
|
| 编号 | 需求 | 验收标准 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| P0-1 | **截图入口按钮**:在 `ReplyBox.vue` 工具栏新增截图按钮(图标裁剪刀或相机图标),与表情、文件等按钮并列 | 点击后触发 `getDisplayMedia()` 屏幕选择流程 |
|
|||
|
|
| P0-2 | **`getDisplayMedia` 屏幕捕获**:调用 `navigator.mediaDevices.getDisplayMedia()` 获取屏幕流,捕获一帧画面传入截图编辑器 | 浏览器弹出屏幕/窗口/标签页选择器;支持选择跨屏显示器;用户取消时静默退出不报错 |
|
|||
|
|
| P0-3 | **截图选区编辑**:全屏遮罩 + 矩形选框拖拽,选区内可编辑,选区外半透明遮罩(对标微信截图) | 屏幕变暗;拖拽出现选框;选框可拖动调整大小;8 个方向手柄 |
|
|||
|
|
| P0-4 | **马赛克工具**:截图编辑工具栏新增马赛克工具,拖拽涂抹区域进行像素化打码 | 选择马赛克工具后在选区内拖拽,拖过区域被马赛克覆盖;支持撤销 |
|
|||
|
|
| P0-5 | **文字标注工具**:点击选区内位置弹出文本输入,输入后渲染为画布上的文字标注 | 点击出现输入框(非 `prompt()`,用内联 input);回车确认渲染到 canvas |
|
|||
|
|
| P0-6 | **箭头工具**:拖拽绘制带箭头的线段 | 起点到终点绘制箭头;箭头头部自动计算角度 |
|
|||
|
|
| P0-7 | **撤销功能**:工具栏撤销按钮,逐步撤销编辑操作 | 每次操作入栈,撤销弹栈;撤销到空时禁用按钮 |
|
|||
|
|
| P0-8 | **截图结果插入输入区**:截图确认后,图片以缩略图形式显示在输入框上方(textarea 之上),作为待发送图片,不自动发送 | 输入区上方出现图片缩略图 + 删除按钮;可继续输入文字;点击发送时图片和文字一起发送 |
|
|||
|
|
| P0-9 | **拍照入口按钮**:在 `ReplyBox.vue` 工具栏新增拍照按钮(摄像头图标) | 点击后弹出摄像头预览弹窗 |
|
|||
|
|
| P0-10 | **`getUserMedia` 摄像头预览**:用 `navigator.mediaDevices.getUserMedia({ video: true })` 获取摄像头流,渲染到 `<video>` 元素实时预览 | 弹窗内嵌视频预览;自动选择前置摄像头;摄像头不可用时显示错误提示 |
|
|||
|
|
| P0-11 | **拍照捕获**:点击拍照按钮,从 `<video>` 当前帧 capture 到 canvas,转为图片 | 拍照后预览切换为静态图片;显示"重拍"和"确认"按钮 |
|
|||
|
|
| P0-12 | **重拍功能**:拍照后点击"重拍"返回实时预览状态 | 重拍后清除上一张照片,恢复 `<video>` 实时预览 |
|
|||
|
|
| P0-13 | **拍照结果插入输入区**:拍照确认后,图片以缩略图形式显示在输入框上方,作为待发送图片 | 与 P0-8 行为一致 |
|
|||
|
|
| P0-14 | **待发送图片发送流程**:发送消息时,如果有待发送图片,先上传图片再发送图片消息(`msg_type: 'image'`),如有文字则额外发送文字消息 | 图片上传复用 `uploadFile()` API;发送成功后清空待发送图片和文字 |
|
|||
|
|
| P0-15 | **待发送图片删除**:输入区待发送图片缩略图上有删除按钮,点击移除 | 点击 × 后缩略图消失,不影响文字内容 |
|
|||
|
|
|
|||
|
|
#### P1 — 应该实现(Should Have)
|
|||
|
|
|
|||
|
|
| 编号 | 需求 | 验收标准 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| P1-1 | **多张待发送图片**:输入区可同时插入多张待发送图片(如先截一张再拍一张) | 支持多张缩略图横向排列;发送时按顺序上传发送;可单独删除某一张 |
|
|||
|
|
| P1-2 | **颜色选择**:截图编辑工具栏提供颜色选择(红/黄/蓝/绿/黑等常用色) | 点击颜色切换标注颜色;当前选中颜色高亮 |
|
|||
|
|
| P1-3 | **矩形框工具**:拖拽绘制矩形边框(用于圈选区域) | 与微信截图矩形工具一致 |
|
|||
|
|
| P1-4 | **截图保存到本地**:截图编辑器提供"保存"按钮,将截图下载到本地 | 下载为 PNG 文件,文件名 `screenshot-{timestamp}.png` |
|
|||
|
|
| P1-5 | **摄像头切换**:如果有多个摄像头,支持切换前后摄像头 | 弹窗内显示切换按钮;切换后预览更新 |
|
|||
|
|
| P1-6 | **ESC 退出**:截图模式和拍照弹窗均支持 ESC 键退出 | 按 ESC 等同于取消 |
|
|||
|
|
| P1-7 | **截图选区尺寸显示**:拖拽选区时实时显示选区宽×高像素值 | 选区右下角或工具栏旁显示 `W×H` 像素值 |
|
|||
|
|
|
|||
|
|
#### P2 — 优先级较低(Nice to Have)
|
|||
|
|
|
|||
|
|
| 编号 | 需求 | 验收标准 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| P2-1 | **画笔自由绘制**:截图编辑器提供自由画笔工具 | 拖拽绘制自由曲线;线宽可调 |
|
|||
|
|
| P2-2 | **高亮工具**:半透明黄色高亮笔 | 拖拽覆盖区域呈半透明高亮 |
|
|||
|
|
| P2-3 | **快捷键截图**:支持 `Ctrl+Shift+A` 快捷键触发截图 | 全局快捷键,焦点不在输入框也能触发 |
|
|||
|
|
| P2-4 | **拍照镜像翻转**:前置摄像头预览水平镜像(符合自拍习惯),但保存的图片不镜像 | 预览镜像、输出正向 |
|
|||
|
|
| P2-5 | **截图延时捕获**:支持 3 秒延时截图(用于捕获鼠标悬停菜单等动态画面) | 选择延时后倒计时 3-2-1 再捕获 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## UI 设计要点
|
|||
|
|
|
|||
|
|
### 1. 工具栏布局(ReplyBox.vue)
|
|||
|
|
|
|||
|
|
现有工具栏顺序:`表情 | 文件 | 分隔线 | 邀请 | 快速回复`
|
|||
|
|
|
|||
|
|
**改造后**:`截图 | 拍照 | 分隔线 | 表情 | 文件 | 分隔线 | 邀请 | 快速回复`
|
|||
|
|
|
|||
|
|
- 截图按钮放在最左侧(最高频使用),图标用裁剪刀 SVG 或 ✂️
|
|||
|
|
- 拍照按钮紧跟截图按钮,图标用摄像头 SVG 或 📷
|
|||
|
|
- 与现有按钮样式保持一致(`.tb-btn` 类,32×28px,hover 高亮 + tooltip)
|
|||
|
|
|
|||
|
|
### 2. 截图模式(全屏覆盖层)
|
|||
|
|
|
|||
|
|
- **触发**:点击截图按钮 → 浏览器弹出 `getDisplayMedia` 选择器(选屏幕/窗口/标签页)
|
|||
|
|
- **选区**:选中后屏幕变暗(`rgba(0,0,0,0.5)` 遮罩),鼠标变为十字光标
|
|||
|
|
- **拖拽框选**:按住鼠标拖拽出现矩形选框(绿色边框 `#07C160`,对标微信),选区内画面清晰,选区外半透明
|
|||
|
|
- **8 方向手柄**:选框四角 + 四边中点各一个手柄,可拖拽调整大小
|
|||
|
|
- **工具栏**:选区下方居中浮动工具栏,从左到右:
|
|||
|
|
- 工具组:`马赛克 | 文字 | 箭头 | 矩形`(P1)
|
|||
|
|
- 颜色组:`🔴 🟡 🔵 🟢 ⚫`(P1)
|
|||
|
|
- 操作组:`撤销 | 保存(P1) | 取消 | 确认✓`
|
|||
|
|
- **尺寸提示**:选区右下角实时显示 `宽×高` 像素值(P1)
|
|||
|
|
- **ESC 退出**:按 ESC 取消截图
|
|||
|
|
|
|||
|
|
### 3. 拍照弹窗(CameraCapture.vue)
|
|||
|
|
|
|||
|
|
- **弹出方式**:Element Plus `el-dialog` 居中弹窗,宽度约 640px
|
|||
|
|
- **预览区**:`<video>` 元素填满弹窗主体,自适应摄像头分辨率
|
|||
|
|
- **控制区**:弹窗底部居中放置拍照按钮(圆形大按钮,图标 📷)
|
|||
|
|
- **拍照后状态**:
|
|||
|
|
- `<video>` 隐藏,显示 `<canvas>` 或 `<img>` 静态画面
|
|||
|
|
- 底部按钮变为:`重拍` (左)| `确认` (右,绿色主按钮)
|
|||
|
|
- **错误状态**:摄像头不可用时显示"无法访问摄像头,请检查权限设置"提示 + 关闭按钮
|
|||
|
|
- **关闭**:右上角 ✕ 或 ESC 关闭弹窗,停止摄像头流
|
|||
|
|
|
|||
|
|
### 4. 待发送图片预览区(输入框上方)
|
|||
|
|
|
|||
|
|
- **位置**:在 `ReplyBox.vue` 的 `textarea` 上方、工具栏下方,新增一个待发送图片预览条
|
|||
|
|
- **布局**:横向排列缩略图,每张缩略图约 80×80px,圆角,右上角有 × 删除按钮
|
|||
|
|
- **样式**:浅色背景容器,与现有引用回复预览条(`.reply-preview`)风格一致
|
|||
|
|
- **多张图片**:横向排列,超出宽度时可横向滚动
|
|||
|
|
- **交互**:
|
|||
|
|
- 鼠标悬停缩略图可放大预览(可选 P2)
|
|||
|
|
- 点击 × 删除该张待发送图片
|
|||
|
|
- 发送后清空所有待发送图片
|
|||
|
|
|
|||
|
|
### 5. 视觉风格
|
|||
|
|
|
|||
|
|
- 主色调:与现有坐席端一致,使用 CSS 变量 `var(--accent)`、`var(--bg-secondary)` 等
|
|||
|
|
- 截图选框颜色:`#07C160`(微信绿,与现有 `ScreenCapture.vue` 一致)
|
|||
|
|
- 工具栏:白色背景 + 阴影,与现有 `ScreenshotEditor.vue` 工具栏风格一致
|
|||
|
|
- 弹窗:Element Plus 默认主题,圆角 8px
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 待确认问题
|
|||
|
|
|
|||
|
|
| 编号 | 问题 | 影响范围 | 建议 |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| Q1 | **企微内嵌浏览器兼容性**:`getDisplayMedia()` 和 `getUserMedia()` 在企微内置浏览器中是否可用?现有代码注释提到"Screen Capture API 在企微桌面端被限制,不稳定"。 | 截图功能核心可行性 | 建议在企微环境下做一次可用性测试;如不可用,保留 `html2canvas` 作为 fallback(仅能截当前页面) |
|
|||
|
|
| Q2 | **HTTPS 要求**:`getDisplayMedia()` 和 `getUserMedia()` 要求 HTTPS 环境(或 localhost)。坐席端部署是否为 HTTPS? | 截图 + 拍照功能可行性 | 确认生产环境域名是否为 HTTPS;如非 HTTPS 则两个功能都无法使用 |
|
|||
|
|
| Q3 | **待发送图片与文字的组合发送**:用户在输入区有待发送图片 + 文字时,发送方式是:(a) 先发图片消息再发文字消息(两条消息)?还是 (b) 合并为一条富文本消息? | 消息发送逻辑 | 建议方案 (a):先发图片再发文字,与现有粘贴图片行为一致。后端如支持图文混排消息可改为 (b) |
|
|||
|
|
| Q4 | **截图工具栏工具优先级**:需求明确提到"马赛克、文字标注、箭头、撤销"四个工具。是否也需要矩形框工具(P1)和高亮/画笔(P2)? | 编辑器功能范围 | 建议 P0 只做需求明确的 4 个工具,矩形框放入 P1 |
|
|||
|
|
| Q5 | **摄像头权限提示**:首次使用拍照功能时浏览器会请求摄像头权限。是否需要自定义权限引导提示? | 用户体验 | 建议在调用 `getUserMedia` 前先显示一个简短提示"即将请求摄像头权限,请在弹窗中允许" |
|
|||
|
|
| Q6 | **InputBox.vue 是否同步改造**:代码库中存在 `InputBox.vue`(旧版输入组件)和 `ReplyBox.vue`(当前使用)。是否需要同步改造 `InputBox.vue`? | 改造范围 | 建议只改 `ReplyBox.vue`,`InputBox.vue` 如已废弃则不动 |
|
|||
|
|
| Q7 | **截图后的编辑器复用**:现有 `ScreenshotEditor.vue`(裁剪+编辑两步)和 `ScreenCapture.vue`(全屏框选+编辑)两个组件功能重叠。新截图功能基于哪个改造? | 架构设计 | 建议基于 `ScreenCapture.vue` 改造(全屏遮罩框选更符合微信截图体验),废弃或保留 `ScreenshotEditor.vue` 供 `html2canvas` fallback 使用 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 附录:现有代码关键路径
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
frontend-agent/src/
|
|||
|
|
├── components/chat/
|
|||
|
|
│ ├── ReplyBox.vue ← 主输入组件(改造:加按钮 + 待发送图片区)
|
|||
|
|
│ ├── InputBox.vue ← 旧版输入组件(待确认是否改造)
|
|||
|
|
│ ├── ScreenshotEditor.vue ← 截图编辑器(改造:加马赛克工具)
|
|||
|
|
│ ├── ScreenCapture.vue ← 全屏框选截图(改造:接入 getDisplayMedia + 加马赛克)
|
|||
|
|
│ └── CameraCapture.vue ← 新建:摄像头拍照组件
|
|||
|
|
├── api/
|
|||
|
|
│ ├── upload.ts ← 文件上传 API(复用)
|
|||
|
|
│ └── message.ts ← 消息发送 API(复用)
|
|||
|
|
└── stores/
|
|||
|
|
└── conversation.ts ← 会话状态管理(可能需扩展:待发送图片队列)
|
|||
|
|
```
|