# 急诊拍照功能实现方案 ## 一、功能概述 ### 应用场景 用户急诊进入检查,给患者拍照,避免出现患者离开后急诊得到的 study 对应不上哪个患者。 ### 业务流程 用户点击按钮 → 打开摄像头 → 拍照 → 预览确认 → 发送到服务器 ### UI 设计 - **入口按钮**:位于 ContentAreaLarge 组件的工具栏区域 - **摄像界面**:弹出式 Modal 显示 - **界面元素**: - 实时摄像头画面显示区域 - 拍照按钮 - 预览确认区域 - 重拍/发送按钮 --- ## 二、参与者列表(从粗到细) ### 1. UI 层 #### 主要组件 - **ContentAreaLarge.tsx** - 功能:入口组件,包含触发拍照的按钮 - 职责:触发打开摄像头模态框 - **CameraModal.tsx** *(新建)* - 功能:摄像头模态框主组件 - 职责: - 管理模态框的显示/隐藏 - 协调子组件的切换(预览 ↔ 拍照) - 处理摄像头生命周期 - **CameraPreview.tsx** *(新建)* - 功能:摄像头实时预览子组件 - 职责: - 显示实时摄像头画面 - 提供拍照按钮 - 调用拍照功能 - **PhotoPreview.tsx** *(新建)* - 功能:拍照后预览组件 - 职责: - 显示已拍摄的照片 - 提供重拍/发送按钮 - 处理照片发送 ### 2. State 管理层 #### Redux Slice - **cameraSlice.ts** *(新建)* - 位置:`src/states/exam/cameraSlice.ts` - State 结构: ```typescript interface CameraState { isOpen: boolean; // 摄像头是否开启 stream: MediaStream | null; // 媒体流对象 capturedImage: string | null; // 捕获的图片(base64) isLoading: boolean; // 加载状态 error: string | null; // 错误信息 currentStudyInstanceUid: string | null; // 当前 Study UID } ``` - Actions: - `openCamera()` - 打开摄像头 - `closeCamera()` - 关闭摄像头 - `setStream()` - 设置媒体流 - `setCapturedImage()` - 设置捕获的图片 - `setLoading()` - 设置加载状态 - `setError()` - 设置错误信息 - `resetState()` - 重置状态 ### 3. API 层 #### API 操作 - **cameraActions.ts** *(新建)* - 位置:`src/API/patient/cameraActions.ts` - 方法: - `uploadPatientPhoto(photo: string, studyInstanceUid: string): Promise` - 上传患者照片到后端 - 关联照片到当前 Study - `getPatientPhoto(studyInstanceUid: string): Promise` - 获取已保存的患者照片 - `deletePatientPhoto(photoId: string): Promise` - 删除患者照片 ### 4. Service 层(适配器模式) #### 抽象层 - **CameraService.ts** *(新建)* - 位置:`src/services/camera/CameraService.ts` - 接口定义: ```typescript interface ICameraService { requestPermission(): Promise; getMediaStream(constraints?: MediaStreamConstraints): Promise; capturePhoto(stream: MediaStream): Promise; stopStream(stream: MediaStream): void; } ``` #### 具体实现 - **BrowserCameraService.ts** *(新建)* - 位置:`src/services/camera/BrowserCameraService.ts` - 实现:浏览器环境下的摄像头访问 - 技术:`navigator.mediaDevices.getUserMedia()` - **ElectronCameraService.ts** *(新建)* - 位置:`src/services/camera/ElectronCameraService.ts` - 实现:Electron 环境下的摄像头访问 - 技术:Electron 特定 API + 窗口权限配置 - **CordovaCameraService.ts** *(新建)* - 位置:`src/services/camera/CordovaCameraService.ts` - 实现:Cordova 环境下的摄像头访问 - 技术:Cordova Camera Plugin - **CameraServiceFactory.ts** *(新建)* - 位置:`src/services/camera/CameraServiceFactory.ts` - 职责:根据运行环境创建对应的 CameraService 实例 - 逻辑: ```typescript class CameraServiceFactory { static create(): ICameraService { if (window.cordova) { return new CordovaCameraService(); } else if (window.electronAPI) { return new ElectronCameraService(); } else { return new BrowserCameraService(); } } } ``` ### 5. Domain 层 #### 领域模型 - **patientPhoto.ts** *(新建)* - 位置:`src/domain/patientPhoto.ts` - 模型定义: ```typescript interface PatientPhoto { id: string; studyInstanceUid: string; patientId: string; photoData: string; // base64 timestamp: Date; metadata: PhotoMetadata; } interface PhotoMetadata { width: number; height: number; format: string; size: number; deviceInfo?: string; } ``` ### 6. Utils 层 #### 工具函数 - **imageUtils.ts** *(新建)* - 位置:`src/utils/imageUtils.ts` - 方法: - `base64ToBlob(base64: string): Blob` - base64 转换为 Blob - `compressImage(base64: string, quality: number): Promise` - 图片压缩 - `resizeImage(base64: string, maxWidth: number, maxHeight: number): Promise` - 图片缩放 - `getImageDimensions(base64: string): Promise<{width: number, height: number}>` - 获取图片尺寸 - `validateImageSize(base64: string, maxSize: number): boolean` - 验证图片大小 --- ## 三、交互流程(泳道图) ``` 用户 ContentAreaLarge CameraModal CameraService Redux Store 后端API │ │ │ │ │ │ │──点击摄像头按钮────────>│ │ │ │ │ │ │─dispatch(openCamera)>│ │ │ │ │ │ │ │───────────────────>│ │ │ │ │ │ setIsOpen(true) │ │ │ │ │<────显示Modal──────│ │ │ │ │ │ │ │ │ │ │ │─requestPermission()>│ │ │ │ │ │<─────权限结果──────│ │ │ │ │ │ │ │ │ │ │ │─getMediaStream()──>│ │ │ │ │ │ │─getUserMedia()───>│ │ │ │ │ │ (navigator.mediaDevices) │ │ │ │<─返回MediaStream───│ │ │ │ │ │─dispatch(setStream)>│ │────────────────>│ │ │ │ │ │ stream saved │ │<─────看到实时视频──────────────────────────────│ │ │ │ │ │ │ │ │ │ │──点击拍照按钮────────────────────────────────>│ │ │ │ │ │ │─capturePhoto()────>│ │ │ │ │ │ │─canvas操作────────>│ │ │ │ │ │─toDataURL()───────>│ │ │ │ │<─返回base64────────│ │ │ │ │ │─dispatch(setCapturedImage)>│ │────────────────>│ │ │ │ │ │ image saved │ │<─────看到预览照片──────────────────────────────│ │ │ │ │ │ │ │ │ │ │──点击发送按钮────────────────────────────────>│ │ │ │ │ │ │─dispatch(setLoading)>│ │────────────────>│ │ │ │ │ │ loading=true │ │ │ │─uploadPhoto()──────────────────────────────────────────>│ │ │ │ │ │ │─保存照片 │ │ │ │ │ │─关联Study │ │ │<──────────────────────────────────────────────────────OK│ │ │ │─dispatch(closeCamera)>│ │────────────────>│ │ │ │ │ │ reset state │ │ │ │─stopStream()──────>│ │ │ │ │ │ │─停止MediaStream──>│ │ │<─────提示成功────────────────────────────────│ │ │ │ │ │ │─关闭Modal──────────│ │ │ ``` ### 异常流程 ``` 用户 CameraModal CameraService Redux Store │ │ │ │ │──点击摄像头按钮────>│ │ │ │ │─requestPermission()>│ │ │ │<─────拒绝──────────│ │ │ │─dispatch(setError)>│ │───────────> │<─显示错误提示────────│ │ │ error set │ │ │ │ │──点击重试/取消──────>│ │ │ │ │─关闭Modal或重试────│ │ ``` --- ## 四、数据流 ### 正向数据流(拍照发送) ``` 用户操作 ↓ UI 事件触发 (点击摄像头按钮) ↓ Redux Action (dispatch openCamera) ↓ Redux State 更新 (isOpen: true) ↓ CameraModal 组件渲染 ↓ useEffect 监听 isOpen 变化 ↓ 调用 CameraServiceFactory.create().getMediaStream() ↓ 根据环境选择对应的 Service ├─ 浏览器: BrowserCameraService ├─ Electron: ElectronCameraService └─ Cordova: CordovaCameraService ↓ 获取 MediaStream ↓ 设置到