# 设计文档 ## 总体架构 应用是一个 Vite 构建的 React SPA。入口文件为 `src/main.tsx`,路由集中在 `src/App.tsx`。页面级功能位于 `src/pages`,公共侧边栏位于 `src/components/Sidebar.tsx`,数据类型集中在 `src/types.ts`,浏览器存储封装在 `src/utils/storage.ts`。 ```text src/ App.tsx 路由定义 main.tsx React 挂载入口 index.css Tailwind 与全局样式 api/ 前端 API client auth/ 登录态、后端用户兼容映射 types.ts 核心数据类型和默认配置 components/Sidebar.tsx 主导航与登出 pages/ 业务页面 utils/ 存储、打印、默认模板内容 ``` ## 路由设计 | 路由 | 页面 | 用途 | | --- | --- | --- | | `/` | `Login` | 登录和首次数据初始化。 | | `/dashboard` | `Dashboard` | 工作台统计和快捷入口。 | | `/report-editor` | `ReportEditor` | 新建报告,或通过 `?id=` 编辑报告。 | | `/report-manage` | `ReportManage` | 报告检索、历史、导出和删除。 | | `/report-view/:id` | `ReportView` | 报告只读查看和打印。 | | `/template-manage` | `TemplateManage` | 模板和字段库维护。 | | `/user-manage` | `UserManage` | 用户、角色、部门和模板权限管理。 | | `/system-settings` | `SystemSettings` | 抽帧、AI、语音和默认模板设置。 | 未匹配路由会重定向到登录页。除登录页外,其余路由由 `RequireAuth` 包裹:优先等待 `AuthProvider` 从后端 `/api/auth/me` 恢复会话;没有后端会话和本地兼容缓存时回到 `/`。 ## 数据设计 核心类型在 `src/types.ts`: - `User`:用户账号、角色、部门、状态、模板权限和签名。 - `Report`:报告元信息、患者信息、HTML 正文、视频、关键帧、作者、状态和历史版本。 - `Template`:模板名称、描述、HTML 内容、作者和字段配置。 - `FormField`:表单字段定义,支持文本、单选、多选、日期、时间、签名、图片等类型。 - `SystemSettings`:抽帧策略、默认模板、AI Provider、语音配置和自动插入设置。 登录认证、报告读写、报告媒体引用、模板读写、字段库、模板图片资源、视频/关键帧文件、用户管理、部门模板授权、系统设置和签名文件已有服务端校验。报告媒体通过 `ReportMedia` 关系表关联 `FileResource`,页面逻辑仍承担一部分迁移期回退约束。 ## 状态与持久化 应用使用 React 本地状态管理页面交互。认证态由后端 Session Cookie 维持,前端 `AuthProvider` 会把当前用户同步到 `localStorage.currentUser` 作为迁移期兼容缓存。报告读写已优先走后端 Reports API,并同步 `localStorage.reports` 作为开发/显式回退缓存;模板读写已优先走后端 Templates API,并同步 `localStorage.templates` 作为开发/显式回退缓存;字段库已优先走 Library API,并同步 `formFieldsConfig` 等兼容缓存;模板图片资源已优先走 Files API,并同步 `imageAssets` 兼容缓存;用户和部门模板授权已优先走 Users/Departments API,并同步 `localStorage.users` 作为兼容缓存;系统设置已优先走 Settings API,并同步 `localStorage.systemSettings` 作为开发/显式回退缓存。`storage.get/set` 对常规键做 JSON 读写,对 `systemSettings` 做简单 XOR + Base64 存储;这不是安全加密,只能降低直接可读性。生产构建默认关闭本地回退。 报告编辑器额外使用 `reportEditorDraft_${username}` 保存未提交草稿,使用 `sessionStorage` 的 `restore_${reportId}` 暂存历史版本恢复内容。 ## 权限设计 当前迁移期权限检查分两层:登录、报告、模板、用户和部门模板授权由后端校验;页面入口和部分业务按钮权限仍保留前端 UI 展示层: - 未登录用户跳回 `/`。 - 医生在报告管理和查看页只能访问本人报告。 - 模板管理不允许医生进入。 - 用户管理只允许超级管理员和管理员进入;后端再次校验可管理范围。 - 管理员只能管理同部门医生或自己,不能创建管理员或跨部门修改用户。 前端入口隐藏不能作为生产级安全边界,所有已后端化 API 都必须以服务端校验为准。 后端化后的目标权限模型以“角色 + 部门 + 模板授权”为主,详细规则见 [权限设计](./permissions.md)。当前已确定规则: - 超级管理员拥有全权限,可以查看和修改所有用户、报告、模板、系统配置和权限配置。 - 管理员只能查看、编辑和删除本部门报告。 - 医生默认只能查看本人报告。 - 医生完成报告后仍允许修改,也可以删除本人已完成报告;完成后的每次修改必须保留历史版本并递增修订版本号。 - 模板按部门授权;管理员可以修改本部门模板;医生不可修改部门模板,但可以复制/新建个人模板。 - 一个部门只能有一个管理员,且只有超级管理员能创建或授权管理员。 - AI 只能接收当前报告内容作为上下文,不读取跨部门报告。 - 后端必须对每个 API 做强权限校验,前端菜单隐藏只作为体验优化。 后端查询层需要把权限规则落到数据过滤: - 报告列表:超级管理员不加部门限制;管理员按 `department_id` 过滤;医生按 `author_id` 过滤。 - 报告详情/编辑/删除:读取报告后再次校验角色、部门和作者关系。 - 模板列表:超级管理员返回全部模板;管理员返回本部门模板和被授权模板;医生返回本部门授权模板和个人模板。 - 用户列表:超级管理员返回全部用户;管理员返回自己和本部门医生;医生只能读取自己。 - 部门模板授权:超级管理员维护部门可用/可管理模板;管理员和医生只读取本人部门。 ## 报告编辑设计 报告正文是一个 `contentEditable` HTML 编辑器。模板中通过 `.field-value[data-bind="fieldKey"]` 标记智能字段,侧边表单更新时同步到正文;正文内字段被编辑时也会反向更新表单状态。 图片占位符通过 `.image-placeholder` 表示: - `data-mode="frame"` 或无 `manual` 标记的占位符可接收关键帧。 - `data-mode="manual"` 用于 Logo、签名等静态图片,不接收视频关键帧拖入。 AI 可编辑区域通过 `.ai-region[data-ai-id]` 和内部 `.ai-content` 定位。AI 返回 `updatedHtml` 后,系统先显示差异确认,再注入目标区域。 ## 打印导出设计 `src/utils/print.ts` 通过隐藏 iframe 写入报告 HTML 和打印样式,然后调用浏览器打印。PDF 导出依赖浏览器“另存为 PDF”,不是服务端生成 PDF。 JSON 导出使用 Blob 下载,报告导出主要包含元信息和 `DEFAULT_FORM_FIELDS` 中定义的字段。 ## 外部服务设计 - AI 服务通过后端 `/api/ai/chat` 和 `/api/ai/models` 代理到 OpenAI 兼容 `/chat/completions` 和 `/models` 接口。 - 语音听写通过后端 `/api/speech/iat` WebSocket 代理到讯飞 IAT 接口,前端只采集 PCM 音频并发送到本系统。 - 当前 AI Key 和讯飞语音密钥均由后端代理使用;普通用户读取系统设置时不会返回真实密钥。