# 2026-08-20 — 医生工作站 AI 问诊对话框布局优化 ## 改动 - 文件: `app/src/doctor_workstation/ui/dialogs/ai_consult.py` - 模块: `AiConsultDialog` 右侧「智能分析」卡片 + 问诊对话卡片密度优化 - 新增 helpers: `_InsightItem`, `_split_insight_segments`, `_split_numbered_list`, `_split_plain_paragraph`, `_split_label_or_sentence`, `_escape_html` - `_set_insights` 改成将稠密段落拆成 lead / list / plain 三类, 每类单独 QLabel 显示 - CSS 新增 `AiConsultInsightMarker` / `AiConsultInsightText` / `AiConsultInsightPlaceholder` - 卡片 padding `10,8,10,8` → `14,12,14,14`; 卡间距 `8` → `10` - 聊天气泡垂直 margin `4` → `6`, chat_layout spacing `10` → `12` ## 拆分策略 - 文本以 `1./2、/3)/4)` 起头 → 视为完整编号列表 - 文本中嵌入 `…:1.…2.…` → 拆出序言 + 编号列表 - 短中文标签 `XX:YY` 开头 → 视为 lead, 标签部分加粗 - 其余按 `。/;/;/` 分句 → 单独成行 ## 测试 - 现有 `tests/test_ai_consult_ui.py` 通过 `widget.objectName() == "AiConsultRecordTitle"` 校验标题仍在, 未受影响 - 暂未运行 UI 测试 (开发环境未装 PySide6); 解析逻辑单独脚本验证通过 ## 注意 - PySide6 QSS 对 `QLabel` 的 `line-height` 支持有限; 主要靠 item 间距 `8px` 与 `padding:1px 0` 提供节奏 - 如果后续接入 `AiConsultInsightText` 还嫌密集, 可再调 `body_host.setSpacing` --- # 接诊台 AI 智能分析详情对话框 (`ReceptionAiAnalysisDialog`) 优化 用户截图(`clipboard-2026-08-20T08-42-40`...)反馈: 「问诊详情 → AI 智能分析」卡片里的诊断建议 / 风险评估 / 治疗建议三段文字太稠密, 不利于阅读。 ## 关键差异 这次的目标文件和上次不同 — 用户截图里出现 "诊断建议 / 风险评估 / 治疗建议" 标题, 而 `ai_consult.py` 用的是 "血糖控制评估 / 并发症风险评估 / 用药合理性评估"。实际是 `app/src/doctor_workstation/ui/pages/reception.py` 中的 `_ReceptionAiAnalysisDialog` 类。 ## 改动 - 文件: `app/src/doctor_workstation/ui/pages/reception.py` - 新增 helpers (与 ai_consult.py 类似但导出名加 `_ai_` 前缀): - `_ai_split_into_segments` / `_ai_split_numbered_list` / `_ai_split_plain_paragraph` - `_ai_segments_to_html` / `_ai_narrative_structured_html` - `_ai_split_label_or_sentence` / `_ai_escape_html` - 正则: `_AI_INSIGHT_LIST_RE`, `_AI_INSIGHT_LIST_LOOKAHEAD_RE`, `_AI_INSIGHT_LABEL_RE` - `add_text_section` 闭包内多挂一个 RichText `_structured_labels[name]` 标签 (`.setObjectName(name + "Structured")`, `property="dialogAiStructured"`) - 原 PlainText `value_label` 仍存在 (`hide()` + `setMaximumHeight(0)`) 用于兼容既有 findChild 测试 - `_render_model` 同步刷新两个结构化标签; 空 HTML 时回退显示原 label 文本 - 风险评估 FlowLayout spacing `7×7` → `9×9` - `body_layout.setSpacing` `12` → `14` - 卡片 contentsMargins `14,13,16,15` → `16,14,18,16` - QSS: `dialogAiBody` 加 `line-height: 1.85`, 新增 `dialogAiStructured` 选择器 (line-height 175%); 风险 pill `min-height` `28` → `30`, padding `4 10` → `5 12`, font 12 → 12.5, border-radius `7` → `8` ## 兼容约束 - `tests/test_reception_parity_ui.py` 严格校验: - `findChild(QLabel, "ReceptionAiAnalysisDialogDiagnosisText")` 必须能找到 - `.text()` 必须等于 `_ai_narrative_text` 归一化结果 - `.textFormat() == Qt.TextFormat.PlainText` - 因此 PlainText 标签必须原样保留, 仅做 `.hide()` + `setMaximumHeight(0)` (避免 QSS `>` 子选择器不被 Qt 支持) ## 验证 - `py_compile` 通过, 无 `SyntaxWarning` - 单独脚本跑过 4 组样本文本 (稠密段落 / 嵌入式编号列表 / 短句 / 纯编号列表), 拆分结果符合预期 - 未在本机跑 UI 测试 (缺 PySide6) --- # 患者列表 AI 分析: 发送完整患者资料给 AI 用户需求: 患者列表 AI 分析时, 每日血糖报告、舌苔、AI 报告、视频录制回放的文字对话都要发给 AI。 ## 改动 - `app/src/doctor_workstation/ui/dialogs/ai_consult.py` - `_load_workspace` 新增 `list_call_records` section (视频问诊记录, 含 `transcript_text`) - 模块级纯函数 (无 Qt 依赖, 便于测试): - `AI_CONTEXT_MAX_CHARS=320`, `AI_PROMPT_LIMIT=500`, `AI_CONTEXT_SEPARATOR="\n\n— 医生提问 —\n"` - `_truncate_for_context` / `_patient_context_blood_sugar` / `_patient_context_tongue` / `_patient_context_reports` / `_patient_context_prescriptions` / `_patient_context_videos` / `build_patient_ai_context` / `_compose_ai_prompt` - `build_patient_ai_context` 返回 `(text, labels)`, 按 每日血糖→舌苔/脉象→视频问诊文字→历史AI报告→处方记录 顺序装入 320 字预算的信封 - `_patient_context_videos` 按 `diagnosis_id` 过滤归属 (`_exact_positive_id`), 只取最近一条有文字的 - `_compose_ai_prompt` 本地强制 500 字上限: 上下文 + "— 医生提问 —" + 问题, 超长时截断问题加 `…` (服务端 `tcm.diagnosis/aiAssistant` prompt 上限 500) - `_ChatBubble.attach_extra()` 支持气泡下方挂载控件 - `_render_patient_context`: 聊天区系统气泡 "AI 已加载本诊单上下文(N 项…)", `_attach_context_detail` 挂 "查看本次发送给 AI 的资料全文" 展开按钮 (QSS: `AiConsultContextToggle`/`AiConsultContextReveal`) - `_ask` 用 `_compose_ai_prompt(text, self._patient_ai_context)` 组装后传给 `_AiStreamWorker`; 医生气泡仍显示原始问题 - `open_for` / `_clear_chat` 重置 `_patient_ai_context*` 三个状态, 防止跨患者残留 ## 测试 - `app/tests/test_ai_consult_ui.py` 新增 5 个测试 (总 15 个全通过): 纯函数: 全 section 信封+视频按诊单过滤 / 仅基本病历返回空 / 长问题截断≤500 UI: 上下文气泡+展开按钮+视频记录计数 / `_ask` 注入上下文 (FakeWorker 捕获 prompt, monkeypatch `QThreadPool`) - `app/scripts/check_ai_context.py`: AST 提取纯函数的冒烟脚本 (无需 Qt) - `tests/test_ai_consult_workspace_ui.py` 18 个测试无回归 ## 环境要点 (重要) - **PySide6 + pytest 在 `app/.venv` 里可用**: 用 `D:/web/zyt/app/.venv/Scripts/python.exe -m pytest` 跑 UI 测试 (QT_QPA_PLATFORM=offscreen)。之前"本机缺 PySide6"的结论只对系统 Python 成立。 - 测试 FakeWorker 需实现 `signals.event/error/finished.connect()` 和 `cancel()` (closeEvent 会调 cancel)