# RIS数据双击保存功能实现方案
## 需求描述
在worklist页面中,实现通过双击列表中的数据行来触发"RIS数据保存到本地"功能的需求。具体要求:
- 双击RIS数据行(具备entry_id字段)时,自动触发保存到本地的功能
- 双击本地study数据行(无entry_id字段)时,保持原有进入检查的逻辑
- 保存成功后显示成功提示并自动刷新列表
- 保存失败时显示错误提示
## 当前状态分析
### 现有实现
1. **双击逻辑**:`src/pages/patient/worklist.tsx` 中的 `handleRowDoubleClick` 函数目前对所有数据行都调用 `worklistToExam(record)` 进入检查
2. **保存逻辑**:`src/pages/patient/components/ActionPanel.tsx` 中的 `handleRisSave` 函数实现了"RIS数据保存到本地"的完整功能
3. **数据区分**:RIS数据具有 `entry_id` 字段,本地study数据没有此字段
### 存在问题
- 双击事件没有区分数据类型,所有双击都进入检查
- 保存逻辑集中在UI组件中,缺乏业务逻辑分离
## 实现思路
### 架构设计
采用分层架构,将业务逻辑从UI层分离:
1. **Domain层**:`src/domain/patient/risSaveLogic.ts`
- 封装RIS保存的核心业务逻辑
- 负责API调用、消息处理、列表刷新等
- 与 `worklistToExam.ts` 保持一致的架构风格
2. **UI层**:`src/pages/patient/worklist.tsx`
- 负责数据类型判断
- 调用相应的domain函数处理业务逻辑
### 技术方案
- 在双击事件中通过 `record.entry_id` 字段判断数据类型
- RIS数据:调用domain层的保存函数
- 本地数据:调用原有的 `worklistToExam` 函数
- 保持与ActionPanel中保存逻辑的一致性
## 具体实现步骤
### 步骤1:创建Domain层逻辑
创建 `src/domain/patient/risSaveLogic.ts` 文件:
```typescript
import { saveRisBatch } from '@/API/patient/risActions';
import { Modal, message } from 'antd';
import store from '@/states/store';
import { setPage, setPageSize } from '@/states/patient/worklist/slices/searchSlice';
import { fetchWorkThunk } from '@/states/patient/worklist/slices/workSlice';
import { WorkFilter } from '@/states/patient/worklist/types/workfilter';
/**
* RIS数据保存成功弹框
*/
const showSaveSuccessModal = (count: number) => {
Modal.success({
title: '🎉 保存成功',
content: (
成功保存 {count} 条RIS数据到本地
系统将自动刷新列表显示最新数据
),
okText: '确定',
centered: true,
afterClose: () => {
// 弹框自动消失后执行列表刷新
triggerSearch();
}
});
};
/**
* 触发搜索刷新列表(复用SearchPanel的逻辑)
*/
const triggerSearch = () => {
const dispatch = store.dispatch;
const searchState = store.getState().search;
// 重置分页
dispatch(setPage(1));
dispatch(setPageSize(10));
// 构建过滤条件
const filters: WorkFilter = {
patient_id: searchState.id,
patient_name: searchState.name,
access_number: searchState.acc_no,
start_time: searchState.start_time,
end_time: searchState.end_time,
status: 'Arrived,InProgress',
};
// 调用搜索
dispatch(fetchWorkThunk({ page: 1, pageSize: 10, filters }));
};
/**
* 保存单个RIS数据到本地
* @param entryId RIS条目ID
*/
export const saveRisData = async (entryId: string) => {
try {
console.log('RIS双击保存本地参数:', entryId);
// 调用RIS批量保存API(传递单个entryId的数组)
await saveRisBatch([entryId]);
console.log('RIS双击保存本地成功');
// 显示成功弹框(暂逝后自动刷新)
showSaveSuccessModal(1);
} catch (error) {
console.error('RIS双击保存本地失败:', error);
const errorMessage = error instanceof Error ? error.message : '保存过程中发生未知错误';
Modal.error({
title: '❌ 保存失败',
content: (
{errorMessage}
请检查网络连接或联系技术支持
),
okText: '确定',
centered: true
});
}
};
```
### 步骤2:修改UI层逻辑
修改 `src/pages/patient/worklist.tsx` 文件:
```typescript
// 添加导入
import { saveRisData } from '@/domain/patient/risSaveLogic';
// 修改handleRowDoubleClick函数
const handleRowDoubleClick = (record: Task) => {
console.log(
'[WorklistTable] Row double-clicked:',
JSON.stringify(record, null, 2)
);
// 判断是否为RIS数据
if (record.entry_id) {
// RIS数据:触发保存到本地
saveRisData(record.entry_id);
} else {
// 本地study数据:进入检查
worklistToExam(record);
}
};
```
## 测试方案
### 功能测试
1. **RIS数据双击保存测试**:
- 前置条件:worklist中存在有entry_id的RIS数据
- 操作步骤:
1. 在worklist页面找到一条有entry_id的RIS数据行
2. 双击该数据行
- 预期结果:
1. 触发RIS保存API调用
2. 显示保存成功弹框
3. 弹框自动关闭后列表自动刷新
4. 控制台显示相应的日志信息
2. **本地数据双击进入检查测试**:
- 前置条件:worklist中存在无entry_id的本地study数据
- 操作步骤:
1. 在worklist页面找到一条无entry_id的本地数据行
2. 双击该数据行
- 预期结果:
1. 正常进入检查流程
2. 页面跳转到exam或process页面
### 异常测试
1. **保存失败测试**:
- 前置条件:模拟网络异常或API错误
- 操作步骤:双击RIS数据行
- 预期结果:显示保存失败的错误弹框
2. **边界情况测试**:
- 测试entry_id为空字符串的情况
- 测试entry_id为undefined的情况
- 测试网络超时的情况
### 回归测试
1. **原有功能验证**:
- 验证单选/多选功能正常
- 验证其他按钮功能正常
- 验证分页、搜索功能正常
2. **性能测试**:
- 测试大量数据下的双击响应速度
- 测试连续双击的处理能力
## 风险评估
### 技术风险
1. **API兼容性**:确保 `saveRisBatch` API能正确处理单个entryId的数组
2. **状态管理**:确保列表刷新后Redux状态正确更新
3. **错误处理**:确保异常情况下的用户体验友好
### 业务风险
1. **数据一致性**:确保保存后的数据状态正确显示
2. **用户体验**:确保操作反馈及时且明确
## 实施计划
1. **开发阶段**:
- 创建 `risSaveLogic.ts` 文件
- 修改 `worklist.tsx` 文件
- 本地测试验证
2. **测试阶段**:
- 功能测试
- 异常测试
- 回归测试
3. **部署阶段**:
- 代码审查
- 生产环境部署
- 监控验证
## 验收标准
- [ ] 双击RIS数据行能正确触发保存功能
- [ ] 双击本地数据行能正常进入检查
- [ ] 保存成功/失败有适当的用户反馈
- [ ] 列表在保存成功后自动刷新
- [ ] 所有原有功能正常工作
- [ ] 代码通过ESLint检查
- [ ] 单元测试覆盖核心逻辑