// 供应商评价 模板管理新增中的table import React, { useState, useEffect } from 'react'; import { Table, Input, Button, Select, Form, InputNumber, message, Popconfirm, } from 'antd'; import { PlusOutlined, MinusCircleOutlined, PlusCircleOutlined, DeleteOutlined, } from '@ant-design/icons'; import { getDictList, DictItem } from '@/servers/api/dicts'; import { StarLevel, StarLevelText } from '@/dicts/supplierTemplateDict'; import { generateUUID } from '@/utils/utils'; import './EvaluateTemplateTable.less'; const { Option } = Select; const { TextArea } = Input; interface EvaluateTemplateTableProps { value?: any[]; onChange?: (value: any[]) => void; isDetail?: boolean; // 是否详情展示用,如果为true则将input都改为text展示并且将操作列隐藏 isCheck?: boolean; // 是否显示勾选操作,如果为true则在表格最后一列增加勾选操作项 onSelect?: (selectedItems: any[]) => void; // 勾选回调函数 defaultSelectedIds?: string[]; // 默认选中的二级指标ID数组 } // 内部使用的数据结构,扁平化后的行数据 interface TableRowItem { key: string; stId?: string; // 一级指标ID ndId?: string; // 二级指标ID baseIndicator: string; descIndicator?: string; stScore: string; indicatorType?: string; subIndicator: string; ndScore: string; isStar: string; desc?: string; orderBy?: number; ndOrderBy?: number; selected?: boolean; // 是否选中 } const EvaluateTemplateTable: React.FC = ({ value = [], onChange, isDetail = false, isCheck = false, onSelect, defaultSelectedIds = [], }) => { const [dataSource, setDataSource] = useState([]); const [form] = Form.useForm(); const [indicatorTypes, setIndicatorTypes] = useState([]); const [loadingTypes, setLoadingTypes] = useState(false); // 获取指标类型字典 const fetchIndicatorTypes = async () => { setLoadingTypes(true); try { const res = await getDictList('Indicator_type'); if (res.success && res.data) { setIndicatorTypes(res.data); } else { message.error('获取指标类型失败'); } } catch (error) { console.error('获取指标类型失败:', error); message.error('获取指标类型失败'); } finally { setLoadingTypes(false); } }; // 将API数据转换为表格数据 const convertApiDataToTableData = (apiData: any[]): TableRowItem[] => { // 检查数据是否已经是扁平化的表格数据格式 if (apiData.length > 0 && 'subIndicator' in apiData[0]) { return apiData.map((item, index) => ({ ...item, key: item.id || `row-${generateUUID(16)}-${index}`, ndId: item.id, stScore: item.stScore || '0', ndScore: item.score || '0', isStar: item.isStar || StarLevel.NO, orderBy: typeof item.stOrderBy === 'string' ? parseInt(item.stOrderBy) : item.stOrderBy, ndOrderBy: typeof item.orderBy === 'string' ? parseInt(item.orderBy) : item.orderBy, selected: defaultSelectedIds.includes(item.id) // 根据defaultSelectedIds设置选中状态 })); } // 如果是嵌套结构,需要扁平化处理 if (apiData.length > 0 && 'indicatorNdList' in apiData[0]) { const flattenedData: TableRowItem[] = []; apiData.forEach((stItem: any, stIndex: number) => { stItem.indicatorNdList.forEach((ndItem: any, ndIndex: number) => { flattenedData.push({ key: ndItem.id || `${stItem.id}-${ndIndex}`, stId: stItem.id, ndId: ndItem.id, baseIndicator: stItem.baseIndicator, descIndicator: stItem.descIndicator, stScore: stItem.score, indicatorType: stItem.indicatorType, subIndicator: ndItem.subIndicator, ndScore: ndItem.score, isStar: ndItem.isStar || StarLevel.NO, desc: ndItem.desc, orderBy: typeof stItem.orderBy === 'string' ? parseInt(stItem.orderBy) : stItem.orderBy, ndOrderBy: typeof ndItem.orderBy === 'string' ? parseInt(ndItem.orderBy) : ndItem.orderBy, selected: defaultSelectedIds.includes(ndItem.id) // 根据defaultSelectedIds设置选中状态 }); }); }); return flattenedData; } return []; }; // 将表格数据转换回API格式 const convertTableDataToApiData = (tableData: TableRowItem[]): any[] => { // 按一级指标分组 const groupedByLevel1 = tableData.reduce((acc: Record, item: TableRowItem) => { // 为空的baseIndicator也需要分组,使用特殊键标识 const groupKey = item.baseIndicator || `empty-${item.key}`; if (!acc[groupKey]) { acc[groupKey] = []; } acc[groupKey].push(item); return acc; }, {}); // 转换为API需要的格式 return Object.keys(groupedByLevel1).map((groupKey, stIndex) => { const level1Items = groupedByLevel1[groupKey]; const firstItem = level1Items[0]; // 生成唯一的临时ID const tempStId = `temp-st-${generateUUID(16)}-${stIndex}`; return { id: firstItem.stId || tempStId, baseIndicator: firstItem.baseIndicator || '', descIndicator: firstItem.descIndicator || '', score: firstItem.stScore || '0', orderBy: firstItem.orderBy || stIndex + 1, indicatorType: firstItem.indicatorType || '', indicatorNdList: level1Items.map((item, ndIndex) => { // 生成唯一的临时ID const tempNdId = `temp-nd-${generateUUID(16)}-${stIndex}-${ndIndex}`; return { id: item.ndId || tempNdId, subIndicator: item.subIndicator || '', score: item.ndScore || '0', isStar: item.isStar || StarLevel.NO, orderBy: item.ndOrderBy || ndIndex + 1, desc: item.desc || '' }; }) }; }); }; // 初始化数据和获取字典 useEffect(() => { // 获取指标类型字典 fetchIndicatorTypes(); }, []); // 确保数据中没有重复的key const ensureUniqueKeys = (data: TableRowItem[]): TableRowItem[] => { const keyMap = new Map(); return data.map((item, index) => { if (!item.key || item.key.includes('undefined') || keyMap.has(item.key)) { const newKey = `fixed-${generateUUID(32)}-${index}`; return { ...item, key: newKey }; } keyMap.set(item.key, true); return item; }); }; // 单独处理value变化 useEffect(() => { // 初始化表格数据或value变化时更新 if (value && value.length > 0) { // 避免不必要的状态更新,比较新旧数据是否相同 const currentValueStr = JSON.stringify(value); const currentDataSourceApiStr = dataSource.length > 0 ? JSON.stringify(convertTableDataToApiData(dataSource)) : ''; if (currentValueStr !== currentDataSourceApiStr) { const tableData = convertApiDataToTableData(value); // 保留现有项的key,确保稳定性 if (dataSource && dataSource.length > 0) { const updatedTableData = tableData.map((newItem) => { // 尝试查找对应的现有项,通过stId和ndId匹配 const existingItem = dataSource.find(existing => (existing.stId === newItem.stId && existing.ndId === newItem.ndId) || (existing.baseIndicator === newItem.baseIndicator && existing.subIndicator === newItem.subIndicator) ); // 如果找到现有项,保留其key和selected状态 if (existingItem) { return { ...newItem, key: existingItem.key, selected: defaultSelectedIds.includes(newItem.ndId || '') ? true : existingItem.selected }; } return newItem; }); // 确保所有key都是唯一的 setDataSource(ensureUniqueKeys(updatedTableData)); } else { // 确保初始数据的每一项都有唯一的key setDataSource(ensureUniqueKeys(tableData)); } } } else if (value && value.length === 0 && dataSource.length > 0) { // 如果value被清空,也清空dataSource setDataSource([]); } }, [value]); // 处理defaultSelectedIds变化 useEffect(() => { if (defaultSelectedIds.length > 0 && dataSource.length > 0) { const newData = dataSource.map(item => ({ ...item, selected: defaultSelectedIds.includes(item.ndId || '') })); console.log("newData",newData) setDataSource(newData); // 如果有onSelect回调,传递所有选中的项 if (onSelect) { const selectedItems = newData.filter(item => item.selected); // 转换为API格式再传递给父组件 const selectedApiData = convertTableDataToApiData(selectedItems); onSelect(selectedApiData); } } }, [defaultSelectedIds]); // 更新数据源 const updateDataSource = (newData: TableRowItem[]) => { // 确保每行都有唯一稳定的key const finalData = ensureUniqueKeys(newData); setDataSource(finalData); if (onChange) { // 转换回API格式再传递给父组件 const apiData = convertTableDataToApiData(finalData); onChange(apiData); } }; // 处理勾选状态变化 const handleCheckChange = (record: TableRowItem, checked: boolean) => { console.log("handleCheckChange") const newData = [...dataSource]; const index = newData.findIndex((item) => item.key === record.key); if (index > -1) { newData[index] = { ...newData[index], selected: checked }; setDataSource(newData); // 如果有onSelect回调,传递所有选中的项 if (onSelect) { const selectedItems = newData.filter(item => item.selected); // 转换为API格式再传递给父组件 const selectedApiData = convertTableDataToApiData(selectedItems); onSelect(selectedApiData); } } }; // 处理输入变化 const handleInputChange = (val: any, record: TableRowItem, field: string) => { const newData = [...dataSource]; const index = newData.findIndex((item) => item.key === record.key); if (index > -1) { const item = newData[index]; // 特殊处理baseIndicator字段,确保唯一性 if (field === 'baseIndicator') { // 如果之前为空,现在有值,则视为新的一级指标 if (!item.baseIndicator && val) { // 检查是否有重复的baseIndicator const existingNames = newData.map((d) => d.baseIndicator).filter(Boolean); if (existingNames.includes(val)) { message.warning('指标名称已存在,请使用不同的名称'); return; } } // 更新同组内所有行的一级指标信息 if (item.baseIndicator) { const oldName = item.baseIndicator; newData.forEach((row, i) => { if (row.baseIndicator === oldName) { newData[i] = { ...row, baseIndicator: val }; } }); } else { newData[index] = { ...item, [field]: val }; } } // 处理其他一级指标字段,需要同步到同组的所有行 else if (['descIndicator', 'stScore', 'indicatorType'].includes(field)) { const baseIndicator = item.baseIndicator; if (baseIndicator) { newData.forEach((row, i) => { if (row.baseIndicator === baseIndicator) { newData[i] = { ...row, [field]: val }; } }); } else { newData[index] = { ...item, [field]: val }; } } // 处理二级指标字段,只更新当前行 else { newData[index] = { ...item, [field]: val }; } updateDataSource(newData); } }; // 添加一级指标 const addLevel1Indicator = (currentRecord?: TableRowItem) => { // 使用改进后的工具函数生成唯一key const newKey = `level1-${generateUUID(32)}`; const newItem: TableRowItem = { key: newKey, baseIndicator: '', // 初始为空 descIndicator: '', stScore: '0', indicatorType: '', subIndicator: '', // 注意:二级指标需要为空字符串,不能为undefined ndScore: '0', isStar: StarLevel.NO, desc: '', orderBy: dataSource.length + 1, // 确保正确的排序 ndOrderBy: 1, // 设置二级指标初始排序 selected: false, // 默认未选中 }; // 制作数据源的副本,避免直接修改状态 const newData = [...dataSource]; // 找到当前记录所在的一级指标组的最后一行 let insertIndex = -1; if (currentRecord && currentRecord.baseIndicator) { // 找到相同baseIndicator的最后一个元素 const sameGroup = newData.filter((item) => item.baseIndicator === currentRecord.baseIndicator); const lastOfGroup = sameGroup[sameGroup.length - 1]; insertIndex = newData.findIndex((item) => item.key === lastOfGroup.key); } else if (currentRecord && currentRecord.key) { // 如果是新增的空白行,直接在其后插入 insertIndex = newData.findIndex((item) => item.key === currentRecord.key); } // 如果找到了位置,在该位置后插入,否则添加到末尾 if (insertIndex !== -1) { newData.splice(insertIndex + 1, 0, newItem); } else { newData.push(newItem); } updateDataSource(newData); }; // 删除一级指标 const removeLevel1Indicator = (record: TableRowItem) => { // 如果baseIndicator为空,只删除当前行 if (!record.baseIndicator) { const newData = dataSource.filter((item) => item.key !== record.key); updateDataSource(newData); return; } // 删除所有具有相同baseIndicator的行 const newData = dataSource.filter((item) => item.baseIndicator !== record.baseIndicator); updateDataSource(newData); }; // 添加二级指标 const addSubIndicator = (parentKey: string) => { // 使用工具函数生成唯一key const newKey = `level2-${generateUUID(16)}`; const parent = dataSource.find((item) => item.key === parentKey); if (!parent || !parent.baseIndicator) { message.warning('请先填写一级指标名称'); return; } const newItem: TableRowItem = { key: newKey, baseIndicator: parent.baseIndicator, descIndicator: parent.descIndicator, stScore: parent.stScore, indicatorType: parent.indicatorType, subIndicator: '', ndScore: '0', isStar: StarLevel.NO, desc: '', ndOrderBy: dataSource.filter(item => item.baseIndicator === parent.baseIndicator).length + 1, selected: false, // 默认未选中 }; // 找到当前记录所在的位置 const newData = [...dataSource]; const parentIndex = newData.findIndex((item) => item.key === parentKey); // 插入到父级之后 if (parentIndex !== -1) { newData.splice(parentIndex + 1, 0, newItem); } else { newData.push(newItem); } updateDataSource(newData); }; // 删除二级指标 const removeSubIndicator = (key: string) => { const newData = dataSource.filter((item) => item.key !== key); updateDataSource(newData); }; // 获取一级指标的行数 const getLevel1RowSpan = (baseIndicator: string) => { // 如果baseIndicator为空,返回1(不合并) if (!baseIndicator) { return 1; } return dataSource.filter((item) => item.baseIndicator === baseIndicator).length; }; // 处理合并单元格 const renderWithRowSpan = ( content: any, record: TableRowItem, render: (cellContent: any, record: TableRowItem) => React.ReactNode, ) => { // 如果baseIndicator为空,不进行合并 if (!record.baseIndicator) { return { children: render(content, record), props: { rowSpan: 1, }, }; } // 查找相同baseIndicator的所有项 const level1Items = dataSource.filter((item) => item.baseIndicator === record.baseIndicator); const index = level1Items.findIndex((item) => item.key === record.key); if (index === 0) { return { children: render(content, record), props: { rowSpan: level1Items.length, }, }; } return { props: { rowSpan: 0, }, }; }; const columns = [ { title: '一级指标', children: [ { title: '序号', dataIndex: 'index', align: 'center', key: 'index', width: 50, render: (_: any, record: TableRowItem, index: number) => { return <>{index + 1}; }, }, { title: '类型', dataIndex: 'indicatorType', align: 'center', key: 'indicatorType', width: 120, render: (text: string, record: TableRowItem) => { return renderWithRowSpan(text, record, (cellContent) => { if (isDetail) { // 在详情模式下,查找并显示字典中对应的名称 const typeItem = indicatorTypes.find((item) => item.code === cellContent); return typeItem ? typeItem.dicName : cellContent || '-'; } return ( ); }); }, }, { title: '基本指标', align: 'center', dataIndex: 'baseIndicator', key: 'baseIndicator', width: 150, render: (text: string, record: TableRowItem) => { return renderWithRowSpan(text, record, (cellContent) => { if (isDetail) { return cellContent || '-'; } return ( handleInputChange(e.target.value, record, 'baseIndicator')} placeholder="请输入基本指标" /> ); }); }, }, { title: '指标说明', align: 'center', dataIndex: 'descIndicator', key: 'descIndicator', width: 200, render: (text: string, record: TableRowItem) => { return renderWithRowSpan(text, record, (cellContent) => { if (isDetail) { return cellContent || '-'; } return ( handleInputChange(e.target.value, record, 'descIndicator')} placeholder="请输入指标说明" /> ); }); }, }, { title: '分值', align: 'center', dataIndex: 'stScore', key: 'stScore', width: 80, render: (text: string, record: TableRowItem) => { return renderWithRowSpan(text, record, (cellContent) => { if (isDetail) { return cellContent || '-'; } return ( handleInputChange(val?.toString() || '0', record, 'stScore')} style={{ width: '100%' }} /> ); }); }, }, { title: '操作', key: 'level1Action', align: 'center', width: 100, render: (_: any, record: TableRowItem) => { if (isDetail) return null; return renderWithRowSpan(null, record, () => (
)); }, }, ].filter((col) => !(isDetail && col.key === 'level1Action')), }, { title: '二级指标', children: [ { title: '细分指标', dataIndex: 'subIndicator', key: 'subIndicator', align: 'center', width: 200, render: (text: string, record: TableRowItem) => { if (isDetail) { return text || '-'; } return ( handleInputChange(e.target.value, record, 'subIndicator')} placeholder="请输入细分指标" /> ); }, }, { title: '分值', dataIndex: 'ndScore', key: 'ndScore', width: 80, align: 'center', render: (text: string, record: TableRowItem) => { if (isDetail) { return text || '-'; } return ( handleInputChange(val?.toString() || '0', record, 'ndScore')} style={{ width: '100%' }} /> ); }, }, { title: '星号项', dataIndex: 'isStar', key: 'isStar', align: 'center', width: 80, render: (text: string, record: TableRowItem) => { if (isDetail) { return text === StarLevel.YES ? '*' : ''; } return ( ); }, }, { title: '评分说明', dataIndex: 'desc', align: 'center', key: 'desc', render: (text: string, record: TableRowItem) => { if (isDetail) { return text || '-'; } return (