/** * Pivot面板:长表→宽表 * 从PivotDialog.tsx提取,作为TransformDialog的子组件 */ import React, { useState } from 'react'; import { Select, Button, Alert, Checkbox, Radio, App } from 'antd'; import { ArrowLeftRight, Info } from 'lucide-react'; interface Props { columns: Array<{ id: string; name: string }>; sessionId: string | null; onApply: (newData: any[]) => void; onClose: () => void; } const PivotPanel: React.FC = ({ columns, sessionId, onApply, onClose, }) => { 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); const [keepUnusedColumns, setKeepUnusedColumns] = useState(false); const [unusedAggMethod, setUnusedAggMethod] = useState<'first' | 'mode' | 'mean'>('first'); // 执行 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, keepUnusedColumns, unusedAggMethod, }, }), }); 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 (
{/* 说明 */}
• 将"一人多行"的纵向数据转为"一人一行"的横向数据
• 典型场景:将随访数据(基线、随访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)}>
取第一个值 (推荐)
取最后一个值 求平均值 求和
{/* 高级选项 */}
⚙️ 高级选项
setKeepUnusedColumns(e.target.checked)} > 保留未选择的列 {keepUnusedColumns && (
setUnusedAggMethod(e.target.value)} >
取第一个值 (默认)
取众数
取均值 (仅数值列)
)}
{/* 警告 */}
• Pivot操作会显著改变数据结构(行列转换)
• 转换后列数可能大幅增加
} type="warning" showIcon={false} className="bg-orange-50 border-orange-200" /> {/* 底部按钮 */}
); }; export default PivotPanel;