|
|
@@ -0,0 +1,247 @@
|
|
|
+# 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: (
|
|
|
+ <div style={{ padding: '16px 0' }}>
|
|
|
+ <p style={{ fontSize: '16px', margin: '8px 0' }}>
|
|
|
+ 成功保存 <strong style={{ color: '#52c41a' }}>{count}</strong> 条RIS数据到本地
|
|
|
+ </p>
|
|
|
+ <p style={{ color: '#666', margin: '8px 0' }}>
|
|
|
+ 系统将自动刷新列表显示最新数据
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ ),
|
|
|
+ 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: (
|
|
|
+ <div style={{ padding: '16px 0' }}>
|
|
|
+ <p style={{ margin: '8px 0' }}>{errorMessage}</p>
|
|
|
+ <p style={{ color: '#666', fontSize: '14px' }}>
|
|
|
+ 请检查网络连接或联系技术支持
|
|
|
+ </p>
|
|
|
+ </div>
|
|
|
+ ),
|
|
|
+ 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检查
|
|
|
+- [ ] 单元测试覆盖核心逻辑
|