import React, { useState, useEffect } from 'react'; import { Modal, Radio, Button, Slider, Alert, App, Statistic, Row, Col, Checkbox } from 'antd'; import { Trash2, AlertTriangle } from 'lucide-react'; interface Props { visible: boolean; onClose: () => void; onApply: (newData: any[]) => void; columns: Array<{ id: string; name: string }>; data: any[]; sessionId: string | null; } const DropnaDialog: React.FC = ({ visible, onClose, onApply, columns, data, sessionId, }) => { const { message } = App.useApp(); const [method, setMethod] = useState<'row' | 'column' | 'both'>('row'); const [threshold, setThreshold] = useState(50); // 百分比 const [selectedColumns, setSelectedColumns] = useState([]); const [loading, setLoading] = useState(false); // 计算缺失值统计 const [missingStats, setMissingStats] = useState<{ totalMissing: number; rowsWithMissing: number; colsWithMissing: number; }>({ totalMissing: 0, rowsWithMissing: 0, colsWithMissing: 0 }); useEffect(() => { if (visible && data.length > 0) { // 计算缺失值 let totalMissing = 0; let rowsWithMissing = 0; const colsMissing: Record = {}; data.forEach(row => { let rowHasMissing = false; Object.keys(row).forEach(key => { const value = row[key]; if (value === null || value === undefined || value === '') { totalMissing++; colsMissing[key] = (colsMissing[key] || 0) + 1; rowHasMissing = true; } }); if (rowHasMissing) { rowsWithMissing++; } }); setMissingStats({ totalMissing, rowsWithMissing, colsWithMissing: Object.keys(colsMissing).length, }); } }, [visible, data]); // 执行 const handleApply = async () => { if (!sessionId) { message.error('Session ID不存在'); return; } // 验证 if (method === 'row' && selectedColumns.length === 0) { // 删除所有行:不需要指定列 } 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: 'dropna', params: { method, threshold: threshold / 100, // 转换为0-1 subset: selectedColumns.length > 0 ? selectedColumns : undefined, }, }), }); const result = await response.json(); if (!result.success) { throw new Error(result.error || '删除缺失值失败'); } message.success('删除缺失值成功!'); // 更新父组件数据 if (result.data?.newDataPreview) { onApply(result.data.newDataPreview); } // 成功后关闭 onClose(); } catch (error: any) { message.error(error.message || '执行失败'); } finally { setLoading(false); } }; return ( 删除缺失值 } open={visible} onCancel={onClose} width={700} footer={[ , , ]} >
{/* 缺失值统计 */} } type="info" showIcon className="mb-4" /> {/* 删除方式 */}
setMethod(e.target.value)} className="w-full" >
按行删除
删除包含任何缺失值的行(推荐)
按列删除
删除缺失值比例超过阈值的列
先删列,再删行
先删除缺失列,再删除剩余的缺失行
{/* 缺失率阈值(仅对按列删除和先删列再删行有效) */} {(method === 'column' || method === 'both') && (
删除缺失率超过 {threshold}% 的列
)} {/* 仅检查指定列(仅对按行删除有效) */} {method === 'row' && (
{columns.map((col) => ( {col.name} ))}
留空表示检查所有列
)} {/* 警告提示 */} 重要提示
} description={
• 删除操作不可撤销,请谨慎操作
• 建议先使用AI对话功能探索数据
• 如果删除后数据为空,操作会失败
• 考虑使用"多重插补"代替直接删除
} type="warning" showIcon={false} className="bg-orange-50 border-orange-200" />
); }; export default DropnaDialog;