Files
Mdeical_Sur_Report/AGENTS.md

15 KiB
Raw Blame History

AGENTS.md

本文档面向 AI 编程助手。阅读者应假定对项目一无所知。所有信息均基于项目实际代码和开发历史,不做假设性推断。


项目概述

手术图文病历报告系统(版本 1.3是一款纯前端、单页应用SPA面向医院手术室场景用于

  • 通过富文本编辑器撰写结构化手术图文报告
  • 上传手术视频并自动/手动抽取关键帧,拖拽插入报告
  • 管理报告模板、用户权限、系统设置
  • 导出 PDF / JSON 格式的报告和模板

核心特征

  • 无后端服务器:所有数据(用户、报告、模板、设置、素材)持久化在浏览器 localStorage
  • 离线可用:部署后即为静态文件,无需网络 API
  • A4 打印优先:编辑器按 A4 尺寸210mm × 297mm排版支持浏览器打印转 PDF
  • 角色权限控制:三级角色 super(超级管理员)/admin(科室管理员)/user(医生)

技术栈

层级 技术
框架 React 19 + TypeScript 5.8
构建工具 Vite 6
样式 Tailwind CSS v4使用 @theme@import "tailwindcss" 新语法)
路由 React Router DOM v7
图标 lucide-react
动画 motion
AI SDK @google/genai(依赖已安装,但当前源码中未实际调用任何 LLM API
运行时 纯浏览器客户端;express 仅在依赖列表中,未被源码使用

项目结构

├── public/                 # 静态资源favicon、logo_square.png
├── src/
│   ├── components/
│   │   └── Sidebar.tsx     # 左侧导航栏(角色过滤、自动折叠)
│   ├── pages/
│   │   ├── Login.tsx       # 登录页 + 全局初始化(默认用户/模板/字段/素材)
│   │   ├── Dashboard.tsx   # 工作台统计卡片、SVG 趋势图)
│   │   ├── ReportEditor.tsx# 核心报告编辑器2,200+ 行,最大文件)
│   │   ├── ReportManage.tsx# 报告列表(搜索、筛选、批量操作、历史回溯)
│   │   ├── ReportView.tsx  # 报告只读查看 + 打印
│   │   ├── TemplateManage.tsx # 模板编辑器1,600+ 行,自定义 Undo/Redo
│   │   ├── UserManage.tsx  # 用户管理RBAC、签名上传、模板权限
│   │   └── SystemSettings.tsx # 系统设置抽帧配置、AI API、默认模板
│   ├── utils/
│   │   ├── storage.ts      # localStorage / sessionStorage 封装
│   │   ├── print.ts        # iframe 打印工具A4 样式、@page 边距)
│   │   └── defaultContent.ts # 默认模板 HTML腹腔镜胆囊切除术报告
│   ├── App.tsx             # BrowserRouter + 路由表
│   ├── main.tsx            # React 根挂载StrictMode
│   ├── types.ts            # 核心 TypeScript 类型User/Report/Template/FormField 等)
│   └── index.css           # Tailwind 入口 + @theme 变量 + 打印媒体查询
├── Dockerfile              # 多阶段构建node builder → nginx alpine
├── docker-compose.yaml     # 映射宿主机 4002 → 容器 80
├── nginx.conf              # SPA 回退、Gzip、静态缓存
├── vite.config.ts          # Vite + Tailwind 插件、GEMINI_API_KEY 注入
├── tsconfig.json           # ES2022、react-jsx、路径别名 `@/*`
├── package.json
├── .env.example            # GEMINI_API_KEY、APP_URL
├── index.html              # 入口 HTML标题 "My Google AI Studio App"
└── 过往经验/               # 开发经验记录(经验记录-1.md / 经验记录-2.md

构建与部署命令

# 开发
npm run dev              # vite --port=3000 --host=0.0.0.0

# 生产构建
npm run build            # vite build → dist/
npm run preview          # vite preview默认端口 4173

# 清理
npm run clean            # rm -rf dist

# 类型检查(唯一 lint 手段,无 ESLint
npm run lint             # tsc --noEmit

Docker 部署

docker-compose up -d --build
# 宿主机访问 http://localhost:4002

无 Docker 环境部署Windows 等):

npm run build
npm run preview
# 或任何静态文件服务器托管 dist/

数据持久化与状态管理

  • 无全局状态库(无 Redux、Zustand、Context API
  • 每个页面独立通过 useState + useEffect 管理状态
  • localStorage 即数据库。关键键名:
    • users — 用户列表
    • currentUser — 当前登录用户
    • reports — 报告列表
    • templates — 模板列表
    • systemSettings — 系统设置
    • formFieldsConfig — 动态字段配置
    • imageAssets — 系统素材库Base64 图片)
    • reportEditorDraft_${username} — 每用户报告草稿
    • customTimeFormats — 用户自定义时间格式缓存

⚠️ localStorage 容量约 5MB。关键帧图片采用 Canvas 压缩(最大宽度 800px、JPEG 质量 0.6)以避免超限。storage.ts 中的异常已改为 console.error 输出,不再静默吞掉。


核心模块说明

1. 富文本编辑器ReportEditor.tsx / TemplateManage.tsx

  • 底层:原生 contentEditable + document.execCommand
  • 智能字段Smart Field:三层嵌套结构
    <span class="smart-field-wrapper" contenteditable="false">
      <span class="field-label">标签:</span>
      <span class="field-value" contenteditable="true" data-bind="key"></span>
    </span>&#8203;
    
    • 外层 contenteditable="false" 保护标签不被逐字删除
    • 输入层 data-bind 实现与右侧表单的双向绑定
    • 末尾追加 &#8203;(零宽空格)防止排版换行异常
  • 图片占位符<span class="image-placeholder">,支持 data-mode="frame|manual" 分类隔离
  • 自定义 Undo/RedoTemplateManage 中已实现基于 HTML 字符串快照的自定义历史栈(undoStack/redoStack),取代不可靠的浏览器原生 undo

2. 视频分析ReportEditor.tsx

  • 上传本地视频 → 生成 object URL
  • 自动抽帧:按 systemSettings.framePositions 百分比位置逐帧截图
  • 手动截图:点击按钮从当前播放时间捕获
  • 图片压缩Canvas 等比缩放至最大 800px 宽JPEG 质量 0.6
  • 拖拽/一键插入:填充到第一个空置的 image-placeholder:not([data-mode="manual"])
  • 自动帧插入:非阻塞 setTimeout 队列式插入,避免阻塞抽帧循环

3. 打印系统src/utils/print.ts

  • 创建隐藏 iframe写入带 A4 打印样式的 HTML
  • @page { margin: 15mm 10mm; }每一页纸张独立分配边距
  • body { padding: 0 } — 不可用 body padding 代替 @page margin否则第二页及后续页边距失效
  • 打印前临时设置 document.title 并注入 iframe <title>,确保 PDF 默认文件名正确
  • 打印后恢复原始标题

4. 角色权限RBAC

角色 权限
super 全部页面、全部数据
admin 仅管理本科室用户;可管理模板;不能看系统设置中的 AI 配置
user 仅创建/查看/编辑自己的报告;可见被分配模板
  • Sidebar.tsx 根据 currentUser.role 过滤导航项
  • UserManage.tsx 中 admin 只能管理 department 与自己相同的用户

开发规范与约定

代码风格

  • TypeScript 严格模式未开启;skipLibCheck: true
  • 路径别名 @/ 映射到项目根目录(src/ 同级)
  • 无 ESLint、无 Prettier、无格式化配置
  • 所有字符串插值、UI 文案、注释均以中文为主

关键开发教训(必读)

以下经验来自 过往经验/ 中的 40+ 条记录,是修改本项目时最容易踩的坑

A. contentEditable 与 DOM 操作

  1. 插入 HTML 必须为紧凑单行:使用 document.execCommand('insertHTML', ...)Range.insertNode() 时,多行模板字符串中的缩进/换行会被浏览器解析为额外文本节点,破坏排版和光标行为。
  2. 删除字段用 Range.selectNode + execCommand('delete'):直接 target.remove() 会绕过浏览器撤销栈,且可能在 WebKit 中误删父级 <p>
  3. 任何直接操作 DOM 修改编辑器内容后,必须紧跟
    contentRef.current = editorRef.current.innerHTML;
    
  4. 在表格 <td> 内插入复杂 inline 元素时:优先使用块级 <div> 作为外层容器,execCommand('insertHTML')<td> 内的 inline-flex 嵌套会自动"拍平"结构。
  5. 对齐操作弃用 execCommand('justifyLeft'...):改用直接设置 block.style.textAlign = align,避免浏览器对混合排版(文字 + 智能字段/占位符)的肢解。

B. 自动保存与 Ref/State 同步

  1. 永远不要将 useRef 作为自动保存的唯一数据源React 18 StrictMode 的"挂载 → 卸载 → 重挂载"会导致 ref 在首次卸载时仍保持初始空值,从而用空数据覆盖有效的 localStorage draft。
  2. 自动保存函数应直接从最新的 React state 和 DOM 读取数据,通过 useCallback + 完整 dependency 数组保证闭包新鲜;或从 stateRef / contentRef 读取稳定快照,但必须在所有数据恢复路径中同步 ref。
  3. setState 是异步的setCapturedFrames(next); saveDraftToStorage(); 的写法会导致闭包读到旧值。若需即时保存,应在 setState 回调中触发保存,或从 ref 读取。
  4. 组件卸载时 DOM 可能已失效editorRef.current?.innerHTML 在卸载阶段可能为空,应优先使用 contentRef.current(内存引用)。
  5. 异步循环中不要 await 阻塞主流程:自动帧插入使用 setTimeout 推入事件队列,而非 await new Promise(...)

C. 图片占位符体系

  1. 占位符涉及三处必须同步修改defaultContent.ts(静态模板)、ReportEditor.tsx(运行时插入/填充/删除恢复)、TemplateManage.tsx(模板管理)。
  2. 占位符创建时需写入 max-width / max-height;填充后改为 width:auto; height:auto,让图片 shrink-wrap删除恢复时需回读 maxWidth/maxHeight 重置尺寸。
  3. 提示文字使用 position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); text-align:center;,要求父容器带 position:relative
  4. 占位符分类隔离:通过 data-mode="frame|manual" 区分;自动插入和拖拽填充时必须用 :not([data-mode="manual"]) 过滤。

D. 时间/日期字段格式系统

  1. 存储格式与显示格式分离:YYYY-MM-DD / HH:mm 存储;YYYY年MM月DD日 / hh:mm A 显示。
  2. 必须同时实现正向格式化(存储 → 显示)和反向解析(显示 → 存储),否则编辑器内直接编辑 smart field 会导致数据混乱。
  3. 12h/24h 判断使用包含性判断:field.timeFormat.includes('hh') || field.timeFormat.includes('A'),避免精确匹配无法覆盖自定义格式。
  4. 默认值策略:「固定时间」/specific 与「当前时间」/current。自动填充必须加「仅当值为空时触发」保护,防止编辑已有报告时覆盖用户数据。
  5. 时间格式 token 体系:YYYYMMDDHHhhmmA。避免使用简写别名如 '24h''12h' 作为存储值。

E. 事件与交互

  1. 工具栏/字段库按钮必须加 onMouseDown={(e) => e.preventDefault()}:防止点击时编辑器失焦导致 Selection/Range 丢失。
  2. 插入操作前恢复 savedRangeRef:作为焦点流失后的兜底保险。
  3. 双向联动高亮:通过 activeFieldKey 状态 + useEffect 直接操作 DOM stylebackgroundColoroutline),避免触发组件重渲染导致光标丢失。点击非字段区域时清空高亮。
  4. 打印样式必须通过 @media print 强制抹除所有交互高亮内联样式(outline: none !important; box-shadow: none !important;)。

F. 数据初始化与默认值

  1. Login.tsxinitData() 是全局唯一初始化入口默认用户、默认模板、默认字段配置、默认设置、素材预加载logo均应在此处完成。
  2. 新增 localStorage key 时需提供合理的默认值或降级处理。
  3. resetToDefault / 恢复出厂设置函数必须包含所有 SystemSettings 字段,不能遗漏新增配置项。
  4. 修改 DEFAULT_FORM_FIELDS 默认值后,已有用户的 localStorage 中旧配置不会自动更新;若变更影响核心功能,应考虑启动时做配置迁移或版本校验。
  5. 批量操作后必须同步清理 selectedIds 和当前选中状态,避免选中已删除项。

测试策略

当前状态:零自动化测试。

  • 无单元测试、无集成测试、无 E2E 测试
  • 无 Jest、Vitest、Playwright、Cypress 配置
  • 唯一类型检查:npm run linttsc --noEmit

建议补充方向(如用户要求):

  • storage.ts 的 JSON 序列化/反序列化
  • types.ts 中日期/时间格式化与解析函数的正反向一致性
  • 报告编辑器的草稿保存/恢复逻辑

安全注意事项

  1. 密码明文存储:用户密码以明文形式保存在 localStorageusers 数组中。这是纯前端架构的固有限制,不适合生产环境处理真实敏感数据
  2. 无 HTTPS 强制Docker 部署默认 HTTP 80 端口。
  3. 无 API 鉴权:无后端,因此无 Token、Session、CSRF 防护概念。
  4. XSS 风险:报告和模板内容直接以 HTML 字符串存储并在 innerHTML 中渲染。当前通过 contentEditable 限制输入来源,但若导入外部 JSON 模板/报告,需警惕恶意脚本。
  5. Gemini API Key:通过 Vite define 注入客户端,构建后 key 会暴露在静态 JS 中(当前源码未实际调用)。

部署环境变量

复制 .env.example.env

GEMINI_API_KEY="YOUR_KEY"   # Google Gemini API Key当前未在业务代码中使用
APP_URL="YOUR_APP_URL"      # 应用托管 URL

Vite 构建时仅将 GEMINI_API_KEY 注入 process.env.GEMINI_API_KEY,其余变量不自动暴露给客户端。


默认账号(首次登录或清空数据后)

账号 密码 角色
admin 123456 super
manager 123456 admin
doctor / 0001 123456 user

修改前必读检查清单

在修改任何涉及以下内容的功能前,请先搜索并同步检查所有相关文件:

  • 智能字段结构types.tsdefaultContent.tsReportEditor.tsxTemplateManage.tsxindex.cssprint.ts
  • 图片占位符(创建/填充/删除恢复)defaultContent.tsReportEditor.tsxTemplateManage.tsx
  • 打印样式print.tsindex.css@media print
  • 时间/日期格式types.tsReportEditor.tsxTemplateManage.tsx
  • 数据初始化/默认值Login.tsxSystemSettings.tsx
  • 自动保存/草稿ReportEditor.tsx 中的 saveDraftToStoragestateRefcontentRef