feat(iit): Implement event-level QC architecture V3.1 with dynamic rule filtering, report deduplication and AI intent enhancement
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* 风险热力图组件
|
||||
*
|
||||
* 展示受试者 × 表单/访视 矩阵
|
||||
* - 绿色圆点:通过
|
||||
* - 黄色图标:警告(可点击)
|
||||
* - 红色图标:严重(可点击)
|
||||
* - 灰色圆点:未开始
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Spin, Tag, Tooltip } from 'antd';
|
||||
import {
|
||||
CheckOutlined,
|
||||
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<RiskHeatmapProps> = ({ data, onCellClick, loading }) => {
|
||||
const getStatusTag = (status: string) => {
|
||||
switch (status) {
|
||||
case 'enrolled':
|
||||
return <Tag color="cyan">已入组</Tag>;
|
||||
case 'screening':
|
||||
return <Tag color="blue">筛查中</Tag>;
|
||||
case 'completed':
|
||||
return <Tag color="green">已完成</Tag>;
|
||||
case 'withdrawn':
|
||||
return <Tag color="default">已退出</Tag>;
|
||||
default:
|
||||
return <Tag>{status}</Tag>;
|
||||
}
|
||||
};
|
||||
|
||||
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'
|
||||
? <CloseOutlined />
|
||||
: <ExclamationOutlined />;
|
||||
|
||||
return (
|
||||
<Tooltip title={`${cell.issueCount} 个问题,点击查看详情`}>
|
||||
<div className={iconClass} onClick={handleClick} style={{ cursor: 'pointer' }}>
|
||||
{icon}
|
||||
</div>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
// 通过或待检查:显示小圆点(也可点击)
|
||||
const dotClass = `qc-heatmap-cell-dot ${cell.status === 'pass' ? 'pass' : 'pending'}`;
|
||||
const tooltipText = cell.status === 'pass'
|
||||
? '已通过,点击查看数据'
|
||||
: '未质控,点击查看数据';
|
||||
|
||||
return (
|
||||
<Tooltip title={tooltipText}>
|
||||
<div
|
||||
className={dotClass}
|
||||
onClick={handleClick}
|
||||
style={{
|
||||
backgroundColor: cell.status === 'pass' ? '#52c41a' : '#d9d9d9',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
/>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="qc-heatmap">
|
||||
{/* 头部 */}
|
||||
<div className="qc-heatmap-header">
|
||||
<h3 className="qc-heatmap-title">受试者风险全景图 (Risk Heatmap)</h3>
|
||||
<div className="qc-heatmap-legend">
|
||||
<span className="qc-heatmap-legend-item">
|
||||
<span className="qc-heatmap-legend-dot pass" />
|
||||
无异常
|
||||
</span>
|
||||
<span className="qc-heatmap-legend-item">
|
||||
<span className="qc-heatmap-legend-dot warning" />
|
||||
疑问 (Query)
|
||||
</span>
|
||||
<span className="qc-heatmap-legend-item">
|
||||
<span className="qc-heatmap-legend-dot fail" />
|
||||
严重违规
|
||||
</span>
|
||||
<span className="qc-heatmap-legend-item">
|
||||
<span className="qc-heatmap-legend-dot pending" />
|
||||
未开始
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 表格内容 */}
|
||||
<div className="qc-heatmap-body">
|
||||
<Spin spinning={loading}>
|
||||
<table className="qc-heatmap-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="subject-header">受试者 ID</th>
|
||||
<th>入组状态</th>
|
||||
{data.columns.map((col, idx) => (
|
||||
<th key={idx}>
|
||||
{col.split('(')[0]}<br />
|
||||
<small style={{ fontWeight: 'normal' }}>
|
||||
{col.includes('(') ? `(${col.split('(')[1]}` : ''}
|
||||
</small>
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.rows.map((row, rowIdx) => (
|
||||
<tr key={rowIdx}>
|
||||
<td className="subject-cell">{row.recordId}</td>
|
||||
<td>{getStatusTag(row.status)}</td>
|
||||
{row.cells.map((cell, cellIdx) => (
|
||||
<td key={cellIdx}>
|
||||
<div className="qc-heatmap-cell">
|
||||
{renderCell(cell)}
|
||||
</div>
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</Spin>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RiskHeatmap;
|
||||
Reference in New Issue
Block a user