实现RIS数据双击保存功能.md 7.3 KB

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 文件:

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 文件:

// 添加导入
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检查
  • 单元测试覆盖核心逻辑