import React, { useState, useEffect } from 'react'; import { Modal, Select, Button, Alert, Checkbox, Radio, App, Tag } from 'antd'; import { ArrowLeftRight, Info } from 'lucide-react'; interface Props { visible: boolean; onClose: () => void; onApply: (newData: any[]) => void; columns: Array<{ id: string; name: string }>; sessionId: string | null; } const PivotDialog: React.FC = ({ visible, onClose, onApply, columns, sessionId, }) => { const { message } = App.useApp(); const [indexColumn, setIndexColumn] = useState(''); const [pivotColumn, setPivotColumn] = useState(''); const [valueColumns, setValueColumns] = useState([]); const [aggfunc, setAggfunc] = useState<'first' | 'last' | 'mean' | 'sum'>('first'); const [loading, setLoading] = useState(false); // 重置状态 useEffect(() => { if (visible) { setIndexColumn(''); setPivotColumn(''); setValueColumns([]); setAggfunc('first'); } }, [visible]); // 执行 const handleApply = async () => { if (!sessionId) { message.error('Session ID不存在'); return; } // 验证 if (!indexColumn) { message.warning('请选择索引列(唯一标识列)'); return; } if (!pivotColumn) { message.warning('请选择透视列(要变成列名的列)'); return; } if (valueColumns.length === 0) { message.warning('请至少选择一个值列'); return; } setLoading(true); try { const response = await fetch('/api/v1/dc/tool-c/quick-action', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ sessionId, action: 'pivot', params: { indexColumn, pivotColumn, valueColumns, aggfunc, }, }), }); const result = await response.json(); if (!result.success) { throw new Error(result.error || 'Pivot转换失败'); } message.success('Pivot转换成功!'); // 更新父组件数据 if (result.data?.newDataPreview) { onApply(result.data.newDataPreview); } // 成功后关闭 onClose(); } catch (error: any) { message.error(error.message || '执行失败'); } finally { setLoading(false); } }; return ( 长表转宽表(Pivot) } open={visible} onCancel={onClose} width={700} footer={[ , , ]} >
{/* 说明 */}
• 将"一人多行"的纵向数据转为"一人一行"的横向数据
• 典型场景:将随访数据(基线、随访2周、随访1个月)展开为独立列
• 示例:Event_Name列的"基线"、"随访2周" → FMA_基线、FMA_随访2周
} type="info" showIcon icon={} className="mb-4" /> {/* 索引列 */}
(option?.label ?? '').toLowerCase().includes(input.toLowerCase()) } options={columns .filter(col => col.id !== indexColumn) .map(col => ({ label: col.name, value: col.id }))} />
此列的不同值将成为新的列名后缀
{/* 值列 */}
{columns .filter(col => col.id !== indexColumn && col.id !== pivotColumn) .map((col) => ( {col.name} ))}
可多选。每个值列会生成多个新列(如:FMA_基线、FMA_随访2周)
{/* 聚合方式 */}
setAggfunc(e.target.value)}>
取第一个值 (推荐)
取最后一个值 求平均值 求和
如果同一个索引+透视组合出现多次,按此方式处理
{/* 警告 */}
• Pivot操作会显著改变数据结构(行列转换)
• 转换后列数可能大幅增加
• 建议先用AI对话探索数据结构
• 索引列应该是真正的唯一标识(如患者ID)
} type="warning" showIcon={false} className="bg-orange-50 border-orange-200" />
); }; export default PivotDialog;