- Resample microphone input from the actual browser AudioContext sample rate to 16k before sending it to the speech proxy. - Encode speech input as 16-bit PCM and send it in small 1280-byte frames instead of relying on the browser to create a 16k audio context. - Flush pending audio before sending the standard Xunfei IAT end frame. - Extract PCM helpers and cover downsampling, PCM encoding, base64 conversion, and byte concatenation with unit tests. - Update report editor, testing, and progress documentation for the corrected speech audio pipeline.
5.3 KiB
模块文档:报告编辑器
涉及文件
src/pages/ReportEditor.tsxsrc/api/reports.tssrc/api/ai.tssrc/api/speech.tssrc/api/library.tssrc/api/files.tssrc/utils/defaultContent.tssrc/utils/print.tssrc/types.ts
模块职责
报告编辑器是系统核心模块,负责新建/编辑报告、字段同步、富文本排版、视频关键帧处理、AI 辅助撰写、语音输入、草稿恢复和报告保存。
页面模式
/report-editor:新建报告。/report-editor?id=RPT_xxx:编辑已有报告。/report-editor?id=RPT_xxx&restore=1:从历史版本恢复内容后进入编辑器。
草稿机制
编辑器会在卸载、页面隐藏和部分操作后写入 reportEditorDraft_${username}。草稿包含正文 HTML、表单数据、视频列表、关键帧、当前标签页、模板 ID 和 AI 聊天上下文。
保存报告后会清理草稿。
智能字段
模板中的智能字段形如:
<span class="field-value" data-bind="patientName" contenteditable="true"></span>
编辑器会把侧边表单状态同步到正文,也会在用户直接编辑正文中的绑定字段时反向更新表单。日期和时间字段会按字段配置做格式化和解析。
字段库优先从 /api/library/fields 读取,成功后同步 formFieldsConfig、multiSelectOptions 和 anesthesiaOptions 兼容缓存;只有开发模式或显式开启 VITE_ENABLE_LOCAL_FALLBACK=true 时,API 不可用才继续读取本地缓存。
图片占位符
.image-placeholder 用于在模板或报告中保留图片位置。
- 普通占位符可点击选择图片,也可接收视频关键帧。
data-mode="manual"用于 Logo、签名等静态位置,不允许拖入关键帧。- 已填充图片后会增加
.has-image。
模板图片资源优先从 /api/files?kind=TEMPLATE_ASSET 读取;只有本地回退开启时,API 不可用才继续读取本地 imageAssets。
视频与抽帧
用户上传视频后,系统使用浏览器对象 URL 进行即时预览,同时优先通过 POST /api/files 以 kind = VIDEO 上传后端文件资源。抽帧逻辑按系统设置中的 framePositions 百分比定位视频时间,绘制到 canvas 后转为 JPEG,并优先以 kind = FRAME 上传后端文件资源。实际截图会按视频时间从早到晚执行和展示;自动插入报告图片占位符时按系统设置中的百分比配置顺序插入。
支持:
- 自动抽帧。
- 手动截帧。
- 点击关键帧跳转视频时间。
- 拖拽关键帧到报告占位符。
- 自动把选中的抽帧序号插入空置图片占位符。
报告保存前会等待当前视频和关键帧上传完成,并把 fileId/url、排序和抽帧信息通过 Reports API 同步到后端 ReportMedia 关系表。只有本地回退开启时,API 不可用才保留本地对象 URL/Data URL 回退。
AI 撰写
AI 面板支持两种模式:
- 对话模式:根据当前报告内容和图片上下文回答问题。
- 修改模式:选中
.ai-region后要求模型返回 JSON,其中包含reply和updatedHtml。
编辑器会监听正文中的 .ai-region 变化;用户插入新的 AI 可编辑区域后,AI 撰写面板的目标下拉栏会立即更新,不需要离开页面或刷新。草稿、报告详情、默认模板和切换模板写入正文后也会主动刷新 AI 区域列表,避免已有区域等到下一次 DOM 编辑后才显示。
模型返回 HTML 后,系统会清理换行和 <br>,生成差异预览。用户确认后才写入目标 .ai-content。
当前 AI 调用使用后端代理:
/api/ai/chat- 后端读取全局共用 Provider Key 并代理到 OpenAI 兼容
/chat/completions - 支持图片以
image_url内容传入
语音输入
讯飞听写通过后端 WebSocket 代理:
- 前端连接
/api/speech/iat,不再生成讯飞鉴权 URL,也不读取 APPID/APIKey/APISecret。 - 浏览器采集麦克风音频后按实际
AudioContext.sampleRate重采样为 16k、16bit、单声道 PCM,并按小帧发送音频帧。 - 启动前会检查浏览器是否支持
navigator.mediaDevices.getUserMedia和AudioContext;如果不是localhost或 HTTPS 等安全上下文,浏览器会禁止麦克风能力。Docker 演示环境可使用https://localhost:4443,局域网普通 HTTP 只能通过 Chrome/Edge 演示启动参数临时标记为可信来源。 - 后端读取 Settings API 中的
xfSpeechConfig,连接讯飞 IAT,上游首帧由后端补齐common.app_id和默认business参数。 - 识别结果由后端转发回前端,并追加到 AI 输入框。
保存规则
- 保存草稿不强制患者姓名和住院号。
- 完成报告要求患者姓名和住院号。
- 保存失败时会优先展示后端返回的真实错误;如果 Session 失效,会提示重新登录并返回登录页。
- 保存时优先调用
POST /api/reports或PATCH /api/reports/:id;后端会先做 HTML 白名单清洗,再写入 PostgreSQL 并维护历史版本。 - 编辑已有已完成报告时,后端会把旧内容追加到历史记录并递增
revision。 - 只有本地回退开启时,API 不可用才保留原有本地
localStorage.reports保存逻辑;生产构建默认关闭这条路径。 - 完成报告后跳转到报告管理页。