Files
2026-08-22 08:51:35 +08:00

6.3 KiB
Raw Permalink Blame History

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,814,12,14,14; 卡间距 810
  • 聊天气泡垂直 margin 46, chat_layout spacing 1012

拆分策略

  • 文本以 1./2、/3)/4 起头 → 视为完整编号列表
  • 文本中嵌入 …:1.…2.… → 拆出序言 + 编号列表
  • 短中文标签 XXYY 开头 → 视为 lead, 标签部分加粗
  • 其余按 。//;/ 分句 → 单独成行

测试

  • 现有 tests/test_ai_consult_ui.py 通过 widget.objectName() == "AiConsultRecordTitle" 校验标题仍在, 未受影响
  • 暂未运行 UI 测试 (开发环境未装 PySide6); 解析逻辑单独脚本验证通过

注意

  • PySide6 QSS 对 QLabelline-height 支持有限; 主要靠 item 间距 8pxpadding: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×79×9
  • body_layout.setSpacing 1214
  • 卡片 contentsMargins 14,13,16,1516,14,18,16
  • QSS: dialogAiBodyline-height: 1.85, 新增 dialogAiStructured 选择器 (line-height 175%); 风险 pill min-height 2830, padding 4 105 12, font 12 → 12.5, border-radius 78

兼容约束

  • 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_videosdiagnosis_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)