|
|
@@ -0,0 +1,250 @@
|
|
|
+# 高级处理双端保存方案
|
|
|
+
|
|
|
+## 📋 需求说明
|
|
|
+
|
|
|
+在高级处理(`SliderAdjustmentPanel`)中实现**双端保存机制**:
|
|
|
+
|
|
|
+### 核心目标
|
|
|
+**在保存参数时调用后端API保存参数并重建DCM**
|
|
|
+
|
|
|
+### 具体要求
|
|
|
+- 用户通过滑动条调节图像参数时,仅更新前端预览
|
|
|
+- 用户点击"保存参数"按钮或执行特定操作时,触发后端保存
|
|
|
+- 调用 `rebuildDcm` API 进行**异步保存和重建DCM**
|
|
|
+- 实现前端预览 + 后端持久化的**双端同步机制**
|
|
|
+
|
|
|
+## 🔍 当前实现分析
|
|
|
+
|
|
|
+### 1. 当前代码流程
|
|
|
+
|
|
|
+**文件位置**: `src/pages/view/components/SliderAdjustmentPanel.tsx`
|
|
|
+
|
|
|
+```
|
|
|
+用户滑动参数
|
|
|
+ ↓
|
|
|
+handleParameterChange (更新Redux状态)
|
|
|
+ ↓
|
|
|
+debouncedPreview / debouncedWASMPreview (防抖500ms/300ms)
|
|
|
+ ↓
|
|
|
+更新 viewer URL (触发图像重新渲染)
|
|
|
+ ↓
|
|
|
+✅ 预览更新完成
|
|
|
+
|
|
|
+❌ 没有调用后端保存 API,也不需要
|
|
|
+```
|
|
|
+
|
|
|
+### 2. 现有的保存机制
|
|
|
+
|
|
|
+目前只有以下1种情况会调用 `rebuildDcm`:
|
|
|
+
|
|
|
+1. **手动保存** (`handleSave`) - 用户点击"保存参数"按钮
|
|
|
+
|
|
|
+### 3. 相关API
|
|
|
+
|
|
|
+**文件位置**: `src/API/imageActions.ts`
|
|
|
+
|
|
|
+```typescript
|
|
|
+/**
|
|
|
+ * 重建dcm(保存应用增强参数后的dcm)
|
|
|
+ */
|
|
|
+export const rebuildDcm = async (
|
|
|
+ sopInstanceUid: string,
|
|
|
+ params: RebuildDcmRequest
|
|
|
+): Promise<RebuildDcmResponse>
|
|
|
+
|
|
|
+interface RebuildDcmRequest {
|
|
|
+ contrast: number; // 增益
|
|
|
+ detail: number; // 细节
|
|
|
+ latitude: number; // 动态范围
|
|
|
+ noise: number; // 噪声模式
|
|
|
+ ww_coef: number; // 对比度
|
|
|
+ wl_coef: number; // 亮度
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 🔄 参数变化触发场景
|
|
|
+
|
|
|
+参数变化可由多种用户操作和系统行为触发,需要区分哪些场景需要保存到后端:
|
|
|
+
|
|
|
+### 场景1: 初始化加载 ❌ 不保存
|
|
|
+**触发时机**: 组件挂载时自动执行
|
|
|
+```typescript
|
|
|
+useEffect(() => {
|
|
|
+ // 从后端加载当前图像的参数
|
|
|
+ dispatch(loadImageProcessingParams(sopInstanceUid));
|
|
|
+}, [dispatch]);
|
|
|
+```
|
|
|
+**行为**:
|
|
|
+- 从后端读取已保存的参数
|
|
|
+- 更新 Redux 状态
|
|
|
+- 设置 `isInitialLoad = true` 标志
|
|
|
+- **不触发保存**(避免循环调用)
|
|
|
+
|
|
|
+### 场景2: 用户滑动滑块 ❌ 不保存
|
|
|
+**触发时机**: 用户拖动任一参数滑块
|
|
|
+```typescript
|
|
|
+handleParameterChange('contrast', 5.0) // 增益
|
|
|
+handleParameterChange('detail', 8.0) // 细节
|
|
|
+handleParameterChange('latitude', 6.0) // 动态范围
|
|
|
+handleParameterChange('noise', 4.0) // 噪声模式
|
|
|
+handleParameterChange('brightness', 1.2) // 对比度
|
|
|
+handleParameterChange('sharpness', -0.5) // 亮度
|
|
|
+```
|
|
|
+**行为**:
|
|
|
+- 更新 Redux 状态
|
|
|
+- 设置 `isInitialLoad = false`
|
|
|
+- **仅触发防抖预览**
|
|
|
+- 更新前端预览(仅前端状态,不保存到后端)
|
|
|
+
|
|
|
+### 场景3: 切换风格 ❌ 不保存
|
|
|
+**触发时机**: 用户在下拉框中选择风格
|
|
|
+```typescript
|
|
|
+handleStyleChange('高对比') // 均衡 | 高对比 | 锐组织 | 骨骼
|
|
|
+```
|
|
|
+**行为**:
|
|
|
+- 应用风格预设参数
|
|
|
+- 更新 Redux 状态
|
|
|
+- 设置 `isInitialLoad = false`
|
|
|
+- **仅更新前端预览**(参数已批量更新,不保存到后端)
|
|
|
+
|
|
|
+### 场景4: 重置参数 ❌ 不保存
|
|
|
+**触发时机**: 用户点击"重置参数"按钮
|
|
|
+```typescript
|
|
|
+handleReset()
|
|
|
+```
|
|
|
+**行为**:
|
|
|
+- 重置为当前风格的默认参数
|
|
|
+- 更新 Redux 状态
|
|
|
+- 设置 `isInitialLoad = false`
|
|
|
+- **仅更新前端预览**(参数已批量更新,不保存到后端)
|
|
|
+
|
|
|
+### 场景5: 手动保存 ✅ 立即保存
|
|
|
+**触发时机**: 用户点击"保存参数"按钮
|
|
|
+```typescript
|
|
|
+handleSave()
|
|
|
+```
|
|
|
+**行为**:
|
|
|
+- 使用当前 Redux 中的参数
|
|
|
+- **立即调用 saveProcessingParams** → **触发 rebuildDcm**
|
|
|
+- 不更新预览(仅保存)
|
|
|
+
|
|
|
+### 场景6: 算法/LUT切换 ⚠️ 前端状态
|
|
|
+**触发时机**: 用户切换算法或LUT
|
|
|
+```typescript
|
|
|
+handleAlgorithmChange('算法A')
|
|
|
+handleLUTChange('LUT1')
|
|
|
+```
|
|
|
+**行为**:
|
|
|
+- 仅更新前端 Redux 状态
|
|
|
+- **不调用后端API**(算法和LUT是前端渲染配置)
|
|
|
+
|
|
|
+## 🎯 实现方案
|
|
|
+
|
|
|
+### 目标流程
|
|
|
+
|
|
|
+```
|
|
|
+用户滑动参数
|
|
|
+ ↓
|
|
|
+handleParameterChange (更新Redux状态)
|
|
|
+ ↓
|
|
|
+debouncedPreview / debouncedWASMPreview (防抖)
|
|
|
+ ↓
|
|
|
+更新 viewer URL (触发图像重新渲染)
|
|
|
+ ↓
|
|
|
+✅ 预览更新完成(仅前端状态)
|
|
|
+```
|
|
|
+
|
|
|
+### 保存时机
|
|
|
+
|
|
|
+重建DCM仅在以下情况调用:
|
|
|
+
|
|
|
+1. **手动保存**: 用户点击"保存参数"按钮时,保存当前参数到后端并重建DCM
|
|
|
+
|
|
|
+### 实现步骤
|
|
|
+
|
|
|
+**无需修改 `debouncedPreview` 和 `debouncedWASMPreview` 函数**
|
|
|
+
|
|
|
+- 保持原有逻辑,仅用于前端预览更新
|
|
|
+- 不添加自动保存调用
|
|
|
+
|
|
|
+## ⚙️ 技术细节
|
|
|
+
|
|
|
+### 1. 防抖机制
|
|
|
+
|
|
|
+- 传统模式: 500ms 防抖延迟
|
|
|
+- WASM模式: 300ms 防抖延迟
|
|
|
+- 仅用于平滑预览更新,不涉及API调用
|
|
|
+
|
|
|
+### 2. 保存参数映射
|
|
|
+
|
|
|
+```typescript
|
|
|
+Redux State → API Request
|
|
|
+------------------------------
|
|
|
+contrast → contrast
|
|
|
+detail → detail
|
|
|
+latitude → latitude
|
|
|
+noise → noise
|
|
|
+brightness → ww_coef
|
|
|
+sharpness → wl_coef
|
|
|
+```
|
|
|
+
|
|
|
+### 3. 保存错误处理策略
|
|
|
+
|
|
|
+```typescript
|
|
|
+try {
|
|
|
+ await rebuildDcm(...); // 保存到后端
|
|
|
+ console.log('✅ 参数已保存到后端');
|
|
|
+} catch (saveError) {
|
|
|
+ console.error('⚠️ 保存参数失败:', saveError);
|
|
|
+ // 显示错误提示,但不影响其他操作
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## 🔒 注意事项
|
|
|
+
|
|
|
+### 1. 性能考虑
|
|
|
+
|
|
|
+- ✅ 前端预览响应迅速,无API调用阻塞
|
|
|
+- ✅ 保存操作异步执行,不影响UI响应
|
|
|
+- ✅ 防抖机制确保预览流畅
|
|
|
+
|
|
|
+### 2. 用户体验
|
|
|
+
|
|
|
+- ✅ 滑动参数时即时预览,无延迟
|
|
|
+- ✅ 需要时手动保存,控制数据持久化
|
|
|
+- ✅ 保存状态明确反馈
|
|
|
+
|
|
|
+### 3. 与现有功能的兼容性
|
|
|
+
|
|
|
+- ✅ 保留"保存参数"按钮(立即保存)
|
|
|
+- ✅ 风格切换和重置参数仅更新前端预览
|
|
|
+- ✅ 不影响初始加载逻辑
|
|
|
+
|
|
|
+### 4. 两种模式的处理
|
|
|
+
|
|
|
+- **传统模式**: 前端预览 + 按需保存到后端
|
|
|
+- **WASM模式**: 本地WASM预览 + 按需保存到后端
|
|
|
+
|
|
|
+## 📝 测试清单
|
|
|
+
|
|
|
+- [ ] 滑动参数时,仅更新预览,不调用后端API
|
|
|
+- [ ] 防抖生效,快速滑动时预览流畅
|
|
|
+- [ ] 传统模式下预览正常
|
|
|
+- [ ] WASM模式下预览正常
|
|
|
+- [ ] 手动保存按钮可用,点击后调用rebuildDcm
|
|
|
+- [ ] 保存成功时,控制台显示"参数已保存到后端"
|
|
|
+- [ ] 保存失败时,显示错误提示
|
|
|
+- [ ] 切换风格仅更新前端预览,不调用后端API
|
|
|
+- [ ] 重置参数仅更新前端预览,不调用后端API
|
|
|
+
|
|
|
+## 🔧 相关文件
|
|
|
+
|
|
|
+- **组件**: `src/pages/view/components/SliderAdjustmentPanel.tsx`
|
|
|
+- **API**: `src/API/imageActions.ts`
|
|
|
+- **Redux**: `src/states/view/sliderAdjustmentPanelSlice.ts`
|
|
|
+- **类型**: `src/types/imageProcessing.ts`
|
|
|
+
|
|
|
+## 📚 相关文档
|
|
|
+
|
|
|
+- [滑动参数调节面板功能](./滑动参数调节面板功能.md)
|
|
|
+- [滑动参数调节面板-双模式处理流程](./滑动参数调节面板-双模式处理流程.md)
|