import React, { useState, useEffect } from 'react'; import { AlertTriangle, CheckCircle2, Download, ArrowRight, FileText, Check, RotateCcw, X } from 'lucide-react'; import { ToolBState } from './index'; import * as toolBApi from '../../api/toolB'; interface Step4VerifyProps { state: ToolBState; updateState: (updates: Partial) => void; onComplete: () => void; } interface VerifyRow { id: string; rowIndex: number; text: string; // 原文摘要 fullText: string; // 原文全文 results: Record; status: 'clean' | 'conflict' | 'pending' | 'failed'; // 行状态 } const Step4Verify: React.FC = ({ state, onComplete }) => { const [rows, setRows] = useState([]); const [selectedRowId, setSelectedRowId] = useState(null); const [isLoading, setIsLoading] = useState(false); const hasLoaded = React.useRef(false); // 🔑 防止重复加载 // 从API加载验证数据 useEffect(() => { // 🔑 如果已加载过,跳过 if (hasLoaded.current || !state.taskId) { return; } hasLoaded.current = true; const fetchItems = async () => { setIsLoading(true); try { console.log('Fetching items for taskId:', state.taskId); const { items } = await toolBApi.getTaskItems(state.taskId!); // 🔑 转换后端数据到前端格式 const transformedRows: VerifyRow[] = items.map(item => { const results: Record = {}; // 从resultA和resultB构建results对象 const resultA = item.resultA || {}; const resultB = item.resultB || {}; const finalResult = item.finalResult || {}; // 获取所有字段名(合并两个模型的结果) const allFields = new Set([ ...Object.keys(resultA), ...Object.keys(resultB) ]); allFields.forEach(fieldName => { results[fieldName] = { A: resultA[fieldName] || '未提取', B: resultB[fieldName] || '未提取', chosen: finalResult[fieldName] || null // 如果已有finalResult,使用它 }; }); return { id: item.id, rowIndex: item.rowIndex, text: item.originalText.substring(0, 50) + '...', // 摘要 fullText: item.originalText, results, status: item.status }; }); setRows(transformedRows); console.log('Items loaded successfully:', transformedRows.length, 'rows'); } catch (error) { console.error('Failed to load items:', error); setRows([]); } finally { setIsLoading(false); } }; fetchItems(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [state.taskId]); // 采纳值 const handleAdopt = async (rowId: string, fieldName: string, value: string | null) => { // 先更新本地状态(乐观更新) setRows(prev => prev.map(row => { if (row.id !== rowId) return row; const newResults = { ...row.results }; newResults[fieldName].chosen = value; // 检查该行是否还有未解决的冲突 const hasConflict = Object.values(newResults).some(f => f.chosen === null); return { ...row, results: newResults, status: hasConflict ? 'conflict' : 'clean' }; })); // 如果value不为null,调用API保存 if (value !== null) { try { await toolBApi.resolveConflict(rowId, fieldName, value); console.log('Conflict resolved:', { rowId, fieldName, value }); } catch (error) { console.error('Failed to resolve conflict:', error); // 可以在这里添加错误提示 } } }; // 统计数据 const conflictRowsCount = rows.filter(r => r.status === 'conflict').length; return (
{isLoading && (
加载验证数据中...
)} {!isLoading && ( <> {/* Toolbar */}
总数据: {rows.length}
{conflictRowsCount > 0 ? (
{conflictRowsCount} 条冲突待裁决
) : (
所有冲突已解决
)}
{/* Data Grid */}
{state.fields.map(f => ( ))} {rows.map((row, idx) => ( setSelectedRowId(row.id)} > {/* 动态列 */} {state.fields.map(f => { const cell = row.results[f.name]; if (!cell) return ; const isConflict = cell.A !== cell.B && cell.chosen === null; const isResolved = cell.chosen !== null; if (isConflict) { return ( ); } return ( ); })} ))}
# 原文摘要{f.name}状态
{idx + 1}
{row.text}
-
{isResolved && cell.chosen !== cell.A && cell.chosen !== cell.B ? (
{cell.chosen}
) : (
{cell.chosen || cell.A}
)}
{row.status === 'clean' ? ( 通过 ) : ( 待裁决 )}
{/* Drawer (侧边栏) */}
{selectedRowId && (() => { const row = rows.find(r => r.id === selectedRowId); if (!row) return null; return ( <>

病历原文详情

Row ID: {row.id}

{row.fullText}

快速导航

{Object.entries(row.results).map(([k, v]) => ( {k} ))}
); })()}
)}
); }; export default Step4Verify;