import React, { useMemo, useState, useEffect, FC } from "react"; import { Badge, Card, Progress, Button, Select, Table, InputNumber, Space, } from "antd"; import { BarChart3, Filter } from "lucide-react"; import type { Dataset } from "@/pages/DataManagement/dataset.model.ts"; const TIME_RANGE_OPTIONS = [ { label: '最近1天', value: 1 }, { label: '最近3天', value: 3 }, { label: '最近7天', value: 7 }, { label: '最近15天', value: 15 }, { label: '最近30天', value: 30 }, ]; interface RatioConfigItem { id: string; name: string; type: "dataset" | "label"; quantity: number; percentage: number; source: string; // dataset id labelFilter?: string; dateRange?: number; } interface RatioConfigProps { ratioType: "dataset" | "label"; selectedDatasets: string[]; datasets: Dataset[]; totalTargetCount: number; distributions: Record>; onChange?: (configs: RatioConfigItem[]) => void; } const genId = (datasetId: string) => `${datasetId}-${Math.random().toString(36).slice(2, 9)}`; const RatioConfig: FC = ({ ratioType, selectedDatasets, datasets, totalTargetCount, distributions, onChange, }) => { const [ratioConfigs, setRatioConfigs] = useState([]); const totalConfigured = useMemo( () => ratioConfigs.reduce((sum, c) => sum + (c.quantity || 0), 0), [ratioConfigs] ); const getDatasetLabels = (datasetId: string): string[] => { const dist = distributions[String(datasetId)] || {}; return Object.keys(dist); }; const addConfig = (datasetId: string) => { const dataset = datasets.find((d) => String(d.id) === datasetId); const newConfig: RatioConfigItem = { id: genId(datasetId), name: dataset?.name || datasetId, type: ratioType, quantity: 0, percentage: 0, source: datasetId, }; const newConfigs = [...ratioConfigs, newConfig]; setRatioConfigs(newConfigs); onChange?.(newConfigs); }; const removeConfig = (configId: string) => { const newConfigs = ratioConfigs.filter((c) => c.id !== configId); const adjusted = recomputePercentages(newConfigs); setRatioConfigs(adjusted); onChange?.(adjusted); }; const updateConfig = ( configId: string, updates: Partial< Pick > ) => { const newConfigs = ratioConfigs.map((c) => c.id === configId ? { ...c, ...updates } : c ); const adjusted = recomputePercentages(newConfigs); setRatioConfigs(adjusted); onChange?.(adjusted); }; const recomputePercentages = (configs: RatioConfigItem[]) => { return configs.map((c) => ({ ...c, percentage: totalTargetCount > 0 ? Math.round((c.quantity / totalTargetCount) * 100) : 0, })); }; const generateAutoRatio = () => { const selectedCount = selectedDatasets.length; if (selectedCount === 0) return; const baseQuantity = Math.floor(totalTargetCount / selectedCount); const remainder = totalTargetCount % selectedCount; let newConfigs: RatioConfigItem[] = ratioConfigs.filter( (c) => !selectedDatasets.includes(c.source) ); selectedDatasets.forEach((datasetId, index) => { const dataset = datasets.find((d) => String(d.id) === datasetId); const quantity = baseQuantity + (index < remainder ? 1 : 0); const config: RatioConfigItem = { id: genId(datasetId), name: dataset?.name || datasetId, type: ratioType, quantity, percentage: Math.round((quantity / totalTargetCount) * 100), source: datasetId, }; newConfigs.push(config); }); setRatioConfigs(newConfigs); onChange?.(newConfigs); }; useEffect(() => { const keep = ratioConfigs.filter((c) => selectedDatasets.includes(c.source) ); if (keep.length !== ratioConfigs.length) { const adjusted = recomputePercentages(keep); setRatioConfigs(adjusted); onChange?.(adjusted); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedDatasets]); return (
配比配置 (已配置:{totalConfigured}/{totalTargetCount}条)
{selectedDatasets.length === 0 ? (

请先选择数据集

) : (
{ratioConfigs.length > 0 && (
总配比数量: {ratioConfigs .reduce((sum, config) => sum + config.quantity, 0) .toLocaleString()}
目标数量: {totalTargetCount.toLocaleString()}
)}
{selectedDatasets.map((datasetId) => { const dataset = datasets.find((d) => String(d.id) === datasetId); if (!dataset) return null; const datasetConfigs = ratioConfigs.filter( (c) => c.source === datasetId ); const labels = getDatasetLabels(datasetId); const usedLabels = datasetConfigs .map((c) => c.labelFilter) .filter(Boolean) as string[]; const columns = [ { title: "配比项", dataIndex: "id", key: "id", render: (_: any, record: RatioConfigItem) => ( {record.name} ), }, { title: "标签筛选", dataIndex: "labelFilter", key: "labelFilter", render: (_: any, record: RatioConfigItem) => { const availableLabels = labels .map((l) => ({ label: l, value: l })) .filter( (opt) => opt.value === record.labelFilter || !usedLabels.includes(opt.value) ); return ( updateConfig(record.id, { dateRange: value || undefined, }) } /> ), }, { title: "数量", dataIndex: "quantity", key: "quantity", render: (_: any, record: RatioConfigItem) => ( updateConfig(record.id, { quantity: Number(v || 0) }) } /> ), }, { title: "占比", dataIndex: "percentage", key: "percentage", render: (_: any, record: RatioConfigItem) => (
{record.percentage ?? 0}%
), }, { title: "操作", dataIndex: "actions", key: "actions", render: (_: any, record: RatioConfigItem) => ( ), }, ]; return (
{dataset.name} {dataset.fileCount}条
{datasetConfigs.reduce((s, c) => s + (c.percentage || 0), 0)}%
); })} )} ); }; export default RatioConfig;