import React, { useState, useCallback, useMemo } from 'react'; import { Button, Input, InputNumber, Checkbox, Typography, Popconfirm, } from '@douyinfe/semi-ui'; import { IconPlus, IconDelete } from '@douyinfe/semi-icons'; import { useTranslation } from 'react-i18next'; import CardTable from '../../../../components/common/ui/CardTable'; const { Text } = Typography; let _idCounter = 0; const uid = () => `gr_${++_idCounter}`; function parseJSON(str, fallback) { if (!str || !str.trim()) return fallback; try { return JSON.parse(str); } catch { return fallback; } } function buildRows(groupRatioStr, userUsableGroupsStr) { const ratioMap = parseJSON(groupRatioStr, {}); const usableMap = parseJSON(userUsableGroupsStr, {}); const allNames = new Set([ ...Object.keys(ratioMap), ...Object.keys(usableMap), ]); return Array.from(allNames).map((name) => ({ _id: uid(), name, ratio: ratioMap[name] ?? 1, selectable: name in usableMap, description: usableMap[name] ?? '', })); } export function serializeGroupTable(rows) { const groupRatio = {}; const userUsableGroups = {}; rows.forEach((row) => { if (!row.name) return; groupRatio[row.name] = row.ratio; if (row.selectable) { userUsableGroups[row.name] = row.description; } }); return { GroupRatio: JSON.stringify(groupRatio, null, 2), UserUsableGroups: JSON.stringify(userUsableGroups, null, 2), }; } export default function GroupTable({ groupRatio, userUsableGroups, onChange, }) { const { t } = useTranslation(); const [rows, setRows] = useState(() => buildRows(groupRatio, userUsableGroups), ); const emitChange = useCallback( (newRows) => { setRows(newRows); onChange?.(serializeGroupTable(newRows)); }, [onChange], ); const updateRow = useCallback( (id, field, value) => { const next = rows.map((r) => r._id === id ? { ...r, [field]: value } : r, ); emitChange(next); }, [rows, emitChange], ); const addRow = useCallback(() => { const existingNames = new Set(rows.map((r) => r.name)); let counter = 1; let newName = `group_${counter}`; while (existingNames.has(newName)) { counter++; newName = `group_${counter}`; } emitChange([ ...rows, { _id: uid(), name: newName, ratio: 1, selectable: true, description: '', }, ]); }, [rows, emitChange]); const removeRow = useCallback( (id) => { emitChange(rows.filter((r) => r._id !== id)); }, [rows, emitChange], ); const groupNames = useMemo(() => rows.map((r) => r.name), [rows]); const duplicateNames = useMemo(() => { const counts = {}; groupNames.forEach((n) => { counts[n] = (counts[n] || 0) + 1; }); return new Set(Object.keys(counts).filter((k) => counts[k] > 1)); }, [groupNames]); const columns = useMemo( () => [ { title: t('分组名称'), dataIndex: 'name', key: 'name', width: 180, render: (_, record) => ( updateRow(record._id, 'name', v)} /> ), }, { title: t('倍率'), dataIndex: 'ratio', key: 'ratio', width: 120, render: (_, record) => ( updateRow(record._id, 'ratio', v ?? 0)} /> ), }, { title: t('用户可选'), dataIndex: 'selectable', key: 'selectable', width: 90, align: 'center', render: (_, record) => ( updateRow(record._id, 'selectable', e.target.checked) } /> ), }, { title: t('描述'), dataIndex: 'description', key: 'description', render: (_, record) => record.selectable ? ( updateRow(record._id, 'description', v)} /> ) : ( - ), }, { title: '', key: 'actions', width: 50, render: (_, record) => ( removeRow(record._id)} position='left' > {duplicateNames.size > 0 && ( {t('存在重复的分组名称:')}{Array.from(duplicateNames).join(', ')} )} ); }