**重构前**(旧编号 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 行
15 KiB
坐席端截图 + 拍照功能 PRD
子系统: 04-坐席工作台 模块: 截图拍照
项目信息
| 项 | 内容 |
|---|---|
| 语言 | 中文 |
| 技术栈 | 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> 实现内嵌摄像头预览,支持重拍。截图和拍照结果均插入到聊天输入区作为图片消息等待发送(非立即发送)。
产品定义
产品目标
- 提升沟通效率:客服无需切换到外部截图工具或手机拍照,直接在工作台内完成截图/拍照→编辑→发送的全流程,减少上下文切换。
- 保证信息准确性:通过截图标注(马赛克打码、箭头指引、文字说明)和实时拍照,让客服能够精准传递屏幕画面和实物状况,减少口头描述的歧义。
- 统一图片消息发送体验:截图和拍照结果统一进入输入区待发送队列,客服可在发送前预览、补充文字说明或删除,与现有粘贴图片/文件上传的交互保持一致。
用户故事
-
作为客服坐席,我想直接在工作台点击截图按钮截取屏幕画面,这样我就能把用户报障的屏幕现象快速发给用户确认,而不需要切换到微信截图或系统截图工具。
-
作为客服坐席,我想在截图时用马赛克遮挡敏感信息(如密码、个人信息),这样在发送截图时不会泄露用户隐私。
-
作为客服坐席,我想在截图选区内用箭头和文字标注重点位置,这样用户能快速理解我指的具体是哪里。
-
作为客服坐席,我想通过摄像头拍照来记录实物设备状态(如硬件故障、线缆连接),这样比文字描述更直观。
-
作为客服坐席,我想截图/拍照后的图片先进入输入区等我自己确认后再发送,这样我能在发送前检查标注是否正确、是否打码到位。
技术规范
现有代码分析
通过对 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 ← 会话状态管理(可能需扩展:待发送图片队列)