/** * 风险热力图组件 (V3.1) * * 展示受试者 × 事件 矩阵 * - 绿色圆点:通过 * - 黄色图标:警告(可点击) * - 红色图标:严重(可点击) * - 灰色圆点:未开始 */ import React from 'react'; import { Spin, Tag, Tooltip } from 'antd'; import { ExclamationOutlined, CloseOutlined, } from '@ant-design/icons'; import type { HeatmapData, HeatmapCell } from '../../types/qcCockpit'; interface RiskHeatmapProps { data: HeatmapData; onCellClick: (cell: HeatmapCell) => void; loading?: boolean; } const RiskHeatmap: React.FC = ({ data, onCellClick, loading }) => { const columnLabels = data.columnLabels || {}; const getStatusTag = (status: string) => { switch (status) { case 'enrolled': return 已入组; case 'screening': return 筛查中; case 'completed': return 已完成; case 'withdrawn': return 已退出; default: return {status}; } }; const getColumnLabel = (col: string): string => { if (columnLabels[col]) return columnLabels[col]; if (col.includes('(')) return col.split('(')[0]; return col; }; const renderCell = (cell: HeatmapCell) => { const hasIssues = cell.status === 'warning' || cell.status === 'fail'; const handleClick = () => onCellClick(cell); if (hasIssues) { const iconClass = `qc-heatmap-cell-icon ${cell.status}`; const icon = cell.status === 'fail' ? : ; return (
{icon}
); } const dotClass = `qc-heatmap-cell-dot ${cell.status === 'pass' ? 'pass' : 'pending'}`; const tooltipText = cell.status === 'pass' ? '已通过,点击查看数据' : '未质控,点击查看数据'; return (
); }; return (

受试者风险全景图 (Risk Heatmap)

无异常 疑问 (Query) 严重违规 未开始
{data.columns.map((col, idx) => { const label = getColumnLabel(col); return ( ); })} {data.rows.map((row, rowIdx) => ( {row.cells.map((cell, cellIdx) => ( ))} ))}
受试者 ID 入组状态 {label}
{row.recordId} {getStatusTag(row.status)}
{renderCell(cell)}
); }; export default RiskHeatmap;