瀏覽代碼

feat: 优化滑动参数调节面板的双端保存机制

- 在 SliderAdjustmentPanel.tsx 中移除风格切换和重置时的自动保存逻辑,仅前端预览
- 新增高级处理双端保存方案文档

改动文件:
- src/pages/view/components/SliderAdjustmentPanel.tsx
- docs/实现/高级处理双端保存方案.md
dengdx 4 周之前
父節點
當前提交
0214ae8c4e
共有 4 個文件被更改,包括 284 次插入39 次删除
  1. 31 0
      CHANGELOG.md
  2. 250 0
      docs/实现/高级处理双端保存方案.md
  3. 1 1
      package.json
  4. 2 38
      src/pages/view/components/SliderAdjustmentPanel.tsx

+ 31 - 0
CHANGELOG.md

@@ -2,6 +2,37 @@
 
 本项目的所有重要变更都将记录在此文件中。
 
+## [1.11.0] - 2025-12-16 18:10
+
+### 新增 (Added)
+- **高级处理双端保存方案文档** ([docs/实现/高级处理双端保存方案.md](docs/实现/高级处理双端保存方案.md))
+  - 新增高级处理双端保存机制的详细设计文档
+  - 明确双端保存的触发时机和实现策略
+  - 优化前端预览和后端持久化的交互流程
+
+### 变更 (Changed)
+- **滑动参数调节面板双端保存机制优化** ([src/pages/view/components/SliderAdjustmentPanel.tsx](src/pages/view/components/SliderAdjustmentPanel.tsx))
+  - 移除风格切换和重置参数时的自动保存逻辑
+  - 改为仅更新前端预览,不触发后端保存
+  - 保持手动保存按钮的立即保存功能
+  - 提升用户体验,减少不必要的API调用
+
+**核心改进:**
+- 优化参数调节交互:滑动参数时仅预览,不自动保存
+- 明确保存时机:仅在用户点击"保存参数"按钮时调用rebuildDcm
+- 提升性能:减少防抖期间的API调用,提升响应速度
+- 用户控制:给予用户完全的保存控制权
+
+**技术实现:**
+- 修改handleStyleChange:移除setTimeout保存逻辑
+- 修改handleReset:移除setTimeout保存逻辑
+- 保持handleSave:保留立即保存功能
+- 更新注释:明确说明仅前端预览的行为
+
+**改动文件:**
+- src/pages/view/components/SliderAdjustmentPanel.tsx
+- docs/实现/高级处理双端保存方案.md
+
 格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/),
 版本号遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
 

+ 250 - 0
docs/实现/高级处理双端保存方案.md

@@ -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)

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "zsis",
-  "version": "1.10.2",
+  "version": "1.11.0",
   "private": true,
   "description": "医学成像系统",
   "main": "main.js",

+ 2 - 38
src/pages/view/components/SliderAdjustmentPanel.tsx

@@ -234,25 +234,7 @@ const SliderAdjustmentPanel = () => {
    */
   const handleStyleChange = (value: ProcessingStyle) => {
     dispatch(applyPreset(value));
-    // 风格切换后立即保存
-    if (currentImageId) {
-      const preset = parameters; // 应用风格后的参数会在 Redux 中更新
-      setTimeout(() => {
-        dispatch(
-          saveProcessingParams({
-            sopInstanceUid: currentImageId,
-            params: {
-              contrast: preset.contrast,
-              detail: preset.detail,
-              latitude: preset.latitude,
-              noise: preset.noise,
-              ww_coef: preset.brightness,
-              wl_coef: preset.sharpness,
-            },
-          }) as any
-        );
-      }, 100);
-    }
+    // 风格切换仅更新前端预览,不保存到后端
   };
 
   /**
@@ -275,25 +257,7 @@ const SliderAdjustmentPanel = () => {
   const handleReset = () => {
     dispatch(resetToPreset());
     message.success('已重置为当前风格的默认参数');
-
-    // 重置后保存
-    if (currentImageId) {
-      setTimeout(() => {
-        dispatch(
-          saveProcessingParams({
-            sopInstanceUid: currentImageId,
-            params: {
-              contrast: parameters.contrast,
-              detail: parameters.detail,
-              latitude: parameters.latitude,
-              noise: parameters.noise,
-              ww_coef: parameters.brightness,
-              wl_coef: parameters.sharpness,
-            },
-          }) as any
-        );
-      }, 100);
-    }
+    // 重置参数仅更新前端预览,不保存到后端
   };
 
   /**