对接评价模板新增和修改
This commit is contained in:
@ -21,15 +21,17 @@ import {
|
||||
} 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';
|
||||
|
||||
// 注意:这里我们使用any类型是为了灵活处理多种数据格式
|
||||
// 实际上,API的类型定义在 src/servers/api/typings.d.ts 中的 SupplierEvaluate 命名空间下
|
||||
// 包含了 IndicatorStItem 和 IndicatorNdItem 类型
|
||||
// 注意:这里我们使用SupplierEvaluate命名空间中的类型
|
||||
// 类型定义已移动到 src/typings.d.ts 文件中
|
||||
|
||||
const { Option } = Select;
|
||||
const { TextArea } = Input;
|
||||
|
||||
// 移除类型定义,使用全局命名空间中的类型
|
||||
|
||||
interface EvaluateTemplateTableProps {
|
||||
value?: any[];
|
||||
onChange?: (value: any[]) => void;
|
||||
@ -86,17 +88,12 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
// 检查数据是否已经是扁平化的表格数据格式
|
||||
if (apiData.length > 0 && 'subIndicator' in apiData[0]) {
|
||||
return apiData.map((item, index) => ({
|
||||
key: item.id || `row-${index}`,
|
||||
stId: item.stId,
|
||||
...item,
|
||||
key: item.id || `row-${generateUUID(16)}-${index}`,
|
||||
ndId: item.id,
|
||||
baseIndicator: item.baseIndicator || '',
|
||||
descIndicator: item.descIndicator || '',
|
||||
stScore: item.stScore || '0',
|
||||
indicatorType: item.indicatorType || '',
|
||||
subIndicator: item.subIndicator || '',
|
||||
ndScore: item.score || '0',
|
||||
isStar: item.isStar || StarLevel.NO,
|
||||
desc: item.desc || '',
|
||||
orderBy: typeof item.stOrderBy === 'string' ? parseInt(item.stOrderBy) : item.stOrderBy,
|
||||
ndOrderBy: typeof item.orderBy === 'string' ? parseInt(item.orderBy) : item.orderBy
|
||||
}));
|
||||
@ -106,7 +103,7 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
if (apiData.length > 0 && 'indicatorNdList' in apiData[0]) {
|
||||
const flattenedData: TableRowItem[] = [];
|
||||
|
||||
apiData.forEach((stItem: any) => {
|
||||
apiData.forEach((stItem: any, stIndex: number) => {
|
||||
stItem.indicatorNdList.forEach((ndItem: any, ndIndex: number) => {
|
||||
flattenedData.push({
|
||||
key: ndItem.id || `${stItem.id}-${ndIndex}`,
|
||||
@ -118,7 +115,7 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
indicatorType: stItem.indicatorType,
|
||||
subIndicator: ndItem.subIndicator,
|
||||
ndScore: ndItem.score,
|
||||
isStar: ndItem.isStar,
|
||||
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
|
||||
@ -129,25 +126,6 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
return flattenedData;
|
||||
}
|
||||
|
||||
// 如果是旧格式,尝试兼容处理
|
||||
if (apiData.length > 0 && 'level1Name' in apiData[0]) {
|
||||
return apiData.map((item, index) => ({
|
||||
key: item.key || `legacy-${index}`,
|
||||
stId: item._originalData?.stItem?.id,
|
||||
ndId: item._originalData?.ndItem?.id,
|
||||
baseIndicator: item.level1Name || '',
|
||||
descIndicator: item.level1Description || '',
|
||||
stScore: item.level1Score?.toString() || '0',
|
||||
indicatorType: item.indicator_type || '',
|
||||
subIndicator: item.level2Name || '',
|
||||
ndScore: item.level2Score?.toString() || '0',
|
||||
isStar: item.isStar || StarLevel.NO,
|
||||
desc: item.desc_score || '',
|
||||
orderBy: typeof item.orderBy === 'string' ? parseInt(item.orderBy) : (item.orderBy || 0),
|
||||
ndOrderBy: typeof item.ndOrderBy === 'string' ? parseInt(item.ndOrderBy) : (item.ndOrderBy || 0)
|
||||
}));
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
|
||||
@ -155,40 +133,46 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
const convertTableDataToApiData = (tableData: TableRowItem[]): any[] => {
|
||||
// 按一级指标分组
|
||||
const groupedByLevel1 = tableData.reduce((acc: Record<string, TableRowItem[]>, item: TableRowItem) => {
|
||||
const baseIndicator = item.baseIndicator;
|
||||
if (!baseIndicator) return acc;
|
||||
// 为空的baseIndicator也需要分组,使用特殊键标识 - 这是关键修复
|
||||
const groupKey = item.baseIndicator || `empty-${item.key}`;
|
||||
|
||||
if (!acc[baseIndicator]) {
|
||||
acc[baseIndicator] = [];
|
||||
if (!acc[groupKey]) {
|
||||
acc[groupKey] = [];
|
||||
}
|
||||
acc[baseIndicator].push(item);
|
||||
acc[groupKey].push(item);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// 转换为API需要的格式
|
||||
return Object.keys(groupedByLevel1).map((baseIndicator, stIndex) => {
|
||||
const level1Items = groupedByLevel1[baseIndicator];
|
||||
return Object.keys(groupedByLevel1).map((groupKey, stIndex) => {
|
||||
const level1Items = groupedByLevel1[groupKey];
|
||||
const firstItem = level1Items[0];
|
||||
|
||||
// 使用any类型避免类型错误
|
||||
const stItem: any = {
|
||||
id: firstItem.stId,
|
||||
baseIndicator,
|
||||
descIndicator: firstItem.descIndicator,
|
||||
score: firstItem.stScore,
|
||||
orderBy: firstItem.orderBy || stIndex + 1,
|
||||
indicatorType: firstItem.indicatorType,
|
||||
indicatorNdList: level1Items.map((item, ndIndex) => ({
|
||||
id: item.ndId,
|
||||
subIndicator: item.subIndicator,
|
||||
score: item.ndScore,
|
||||
isStar: item.isStar,
|
||||
orderBy: item.ndOrderBy || ndIndex + 1,
|
||||
desc: item.desc
|
||||
}))
|
||||
};
|
||||
// 生成唯一的临时ID
|
||||
const tempStId = `temp-st-${generateUUID(16)}-${stIndex}`;
|
||||
|
||||
return stItem;
|
||||
// 简化数据转换,使用可选链和短路评估
|
||||
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 || ''
|
||||
};
|
||||
})
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
@ -196,20 +180,180 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
useEffect(() => {
|
||||
// 获取指标类型字典
|
||||
fetchIndicatorTypes();
|
||||
}, []);
|
||||
|
||||
// 初始化表格数据
|
||||
// 单独处理value变化
|
||||
useEffect(() => {
|
||||
// 初始化表格数据或value变化时更新
|
||||
if (value && value.length > 0) {
|
||||
const tableData = convertApiDataToTableData(value);
|
||||
setDataSource(tableData);
|
||||
console.log('useEffect中接收到的value:', value);
|
||||
|
||||
// 避免不必要的状态更新,比较新旧数据是否相同
|
||||
const currentValueStr = JSON.stringify(value);
|
||||
// 注意:当dataSource为空时,避免调用convertTableDataToApiData
|
||||
const currentDataSourceApiStr = dataSource.length > 0 ?
|
||||
JSON.stringify(convertTableDataToApiData(dataSource)) : '';
|
||||
|
||||
if (currentValueStr !== currentDataSourceApiStr) {
|
||||
const tableData = convertApiDataToTableData(value);
|
||||
console.log('转换后的tableData:', tableData);
|
||||
console.log('转换后的tableData key列表:', tableData.map(item => item.key));
|
||||
|
||||
// 保留现有项的key,确保稳定性
|
||||
if (dataSource && dataSource.length > 0) {
|
||||
console.log('现有dataSource:', dataSource);
|
||||
console.log('现有dataSource key列表:', dataSource.map(item => item.key));
|
||||
|
||||
// 检查现有dataSource中是否有重复的key
|
||||
const existingKeyMap = new Map<string, boolean>();
|
||||
const duplicateKeys = dataSource.filter(item => {
|
||||
if (existingKeyMap.has(item.key)) {
|
||||
return true;
|
||||
}
|
||||
existingKeyMap.set(item.key, true);
|
||||
return false;
|
||||
}).map(item => item.key);
|
||||
|
||||
if (duplicateKeys.length > 0) {
|
||||
console.error('现有dataSource中发现重复的key:', duplicateKeys);
|
||||
}
|
||||
|
||||
const updatedTableData = tableData.map((newItem, index) => {
|
||||
// 尝试查找对应的现有项,通过stId和ndId匹配
|
||||
const existingItem = dataSource.find(existing =>
|
||||
(existing.stId === newItem.stId && existing.ndId === newItem.ndId) ||
|
||||
(existing.baseIndicator === newItem.baseIndicator && existing.subIndicator === newItem.subIndicator)
|
||||
);
|
||||
|
||||
// 如果找到现有项,保留其key
|
||||
if (existingItem) {
|
||||
console.log(`保留现有项的key: ${existingItem.key}`);
|
||||
return { ...newItem, key: existingItem.key };
|
||||
}
|
||||
|
||||
// 生成唯一的key
|
||||
const uniqueKey = `init-${generateUUID(32)}-${index}`;
|
||||
console.log(`为新项生成key: ${uniqueKey}`);
|
||||
return { ...newItem, key: uniqueKey };
|
||||
});
|
||||
|
||||
console.log('更新后的tableData:', updatedTableData);
|
||||
console.log('更新后的tableData key列表:', updatedTableData.map(item => item.key));
|
||||
|
||||
// 再次检查是否有重复的key
|
||||
const finalKeyMap = new Map<string, boolean>();
|
||||
const finalData = updatedTableData.map((item, index) => {
|
||||
if (finalKeyMap.has(item.key)) {
|
||||
// 如果仍然有重复的key,强制生成新的key
|
||||
console.warn(`useEffect中仍然发现重复的key: ${item.key},强制重新生成`);
|
||||
const forceNewKey = `force-${generateUUID(32)}-${index}-${Date.now()}`;
|
||||
return {
|
||||
...item,
|
||||
key: forceNewKey
|
||||
};
|
||||
} else {
|
||||
finalKeyMap.set(item.key, true);
|
||||
return item;
|
||||
}
|
||||
});
|
||||
|
||||
console.log('最终的tableData:', finalData);
|
||||
console.log('最终的tableData key列表:', finalData.map(item => item.key));
|
||||
|
||||
setDataSource(finalData);
|
||||
} else {
|
||||
// 确保初始数据的每一项都有唯一的key
|
||||
const keyMap = new Map<string, boolean>();
|
||||
const dataWithUniqueKeys = tableData.map((item, index) => {
|
||||
const key = item.key || `init-${generateUUID(32)}-${index}`;
|
||||
|
||||
if (keyMap.has(key)) {
|
||||
// 如果key重复,生成新的key
|
||||
const newKey = `unique-${generateUUID(32)}-${index}`;
|
||||
console.log(`初始化时发现重复的key: ${key},生成新key: ${newKey}`);
|
||||
keyMap.set(newKey, true);
|
||||
return { ...item, key: newKey };
|
||||
} else {
|
||||
keyMap.set(key, true);
|
||||
return { ...item, key };
|
||||
}
|
||||
});
|
||||
|
||||
console.log('初始化的dataWithUniqueKeys:', dataWithUniqueKeys);
|
||||
console.log('初始化的key列表:', dataWithUniqueKeys.map(item => item.key));
|
||||
|
||||
setDataSource(dataWithUniqueKeys);
|
||||
}
|
||||
}
|
||||
} else if (value && value.length === 0 && dataSource.length > 0) {
|
||||
// 如果value被清空,也清空dataSource
|
||||
setDataSource([]);
|
||||
}
|
||||
}, [value]);
|
||||
|
||||
// 更新数据源
|
||||
const updateDataSource = (newData: TableRowItem[]) => {
|
||||
setDataSource(newData);
|
||||
console.log('updateDataSource接收到的数据:', newData);
|
||||
console.log('updateDataSource接收到的key列表:', newData.map(item => item.key));
|
||||
|
||||
// 检查是否有重复的key
|
||||
const keyMap = new Map<string, number>();
|
||||
newData.forEach(item => {
|
||||
if (keyMap.has(item.key)) {
|
||||
keyMap.set(item.key, keyMap.get(item.key)! + 1);
|
||||
} else {
|
||||
keyMap.set(item.key, 1);
|
||||
}
|
||||
});
|
||||
|
||||
// 打印重复的key
|
||||
const duplicateKeys = Array.from(keyMap.entries())
|
||||
.filter(([_, count]) => count > 1)
|
||||
.map(([key]) => key);
|
||||
|
||||
if (duplicateKeys.length > 0) {
|
||||
console.error('发现重复的key:', duplicateKeys);
|
||||
console.error('重复key的详细信息:', newData.filter(item => duplicateKeys.includes(item.key)));
|
||||
}
|
||||
|
||||
// 确保每行都有唯一稳定的key
|
||||
const dataWithStableKeys = newData.map((item, index) => {
|
||||
// 如果key不存在、包含undefined或者是重复的key,使用工具函数生成新key
|
||||
if (!item.key || item.key.includes('undefined') || duplicateKeys.includes(item.key)) {
|
||||
console.log(`为项 ${index} 重新生成key,原key: ${item.key}`);
|
||||
const newKey = `fixed-${generateUUID(32)}-${index}`;
|
||||
return {
|
||||
...item,
|
||||
key: newKey
|
||||
};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
|
||||
// 再次检查是否有重复的key
|
||||
const finalKeyMap = new Map<string, boolean>();
|
||||
const finalData = dataWithStableKeys.map((item, index) => {
|
||||
if (finalKeyMap.has(item.key)) {
|
||||
// 如果仍然有重复的key,强制生成新的key
|
||||
console.warn(`仍然发现重复的key: ${item.key},强制重新生成`);
|
||||
const forceNewKey = `force-${generateUUID(32)}-${index}-${Date.now()}`;
|
||||
return {
|
||||
...item,
|
||||
key: forceNewKey
|
||||
};
|
||||
} else {
|
||||
finalKeyMap.set(item.key, true);
|
||||
return item;
|
||||
}
|
||||
});
|
||||
|
||||
console.log('最终的数据源:', finalData);
|
||||
console.log('最终的key列表:', finalData.map(item => item.key));
|
||||
|
||||
setDataSource(finalData);
|
||||
if (onChange) {
|
||||
// 转换回API格式再传递给父组件
|
||||
const apiData = convertTableDataToApiData(newData);
|
||||
const apiData = convertTableDataToApiData(finalData);
|
||||
onChange(apiData);
|
||||
}
|
||||
};
|
||||
@ -269,7 +413,10 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
|
||||
// 添加一级指标
|
||||
const addLevel1Indicator = (currentRecord?: TableRowItem) => {
|
||||
const newKey = `level1-${Date.now()}`;
|
||||
// 使用改进后的工具函数生成唯一key,不再需要额外的时间戳和随机数
|
||||
const newKey = `level1-${generateUUID(32)}`;
|
||||
console.log('添加一级指标,生成的key:', newKey);
|
||||
console.log('当前数据源:', dataSource);
|
||||
|
||||
const newItem: TableRowItem = {
|
||||
key: newKey,
|
||||
@ -277,13 +424,16 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
descIndicator: '',
|
||||
stScore: '0',
|
||||
indicatorType: '',
|
||||
subIndicator: '',
|
||||
subIndicator: '', // 注意:二级指标需要为空字符串,不能为undefined
|
||||
ndScore: '0',
|
||||
isStar: StarLevel.NO,
|
||||
desc: '',
|
||||
orderBy: dataSource.length + 1, // 确保正确的排序
|
||||
ndOrderBy: 1 // 设置二级指标初始排序
|
||||
};
|
||||
console.log('新建的一级指标项:', newItem);
|
||||
|
||||
// 找到当前记录所在的位置
|
||||
// 制作数据源的副本,避免直接修改状态
|
||||
const newData = [...dataSource];
|
||||
|
||||
// 找到当前记录所在的一级指标组的最后一行
|
||||
@ -293,9 +443,11 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
const sameGroup = newData.filter((item) => item.baseIndicator === currentRecord.baseIndicator);
|
||||
const lastOfGroup = sameGroup[sameGroup.length - 1];
|
||||
insertIndex = newData.findIndex((item) => item.key === lastOfGroup.key);
|
||||
console.log('插入位置(相同baseIndicator):', insertIndex);
|
||||
} else if (currentRecord && currentRecord.key) {
|
||||
// 如果是新增的空白行,直接在其后插入
|
||||
insertIndex = newData.findIndex((item) => item.key === currentRecord.key);
|
||||
console.log('插入位置(空白行后):', insertIndex);
|
||||
}
|
||||
|
||||
// 如果找到了位置,在该位置后插入,否则添加到末尾
|
||||
@ -304,6 +456,8 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
} else {
|
||||
newData.push(newItem);
|
||||
}
|
||||
console.log('插入后的数据源:', newData);
|
||||
console.log('插入后的数据源中的key列表:', newData.map(item => item.key));
|
||||
|
||||
updateDataSource(newData);
|
||||
};
|
||||
@ -324,7 +478,9 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
|
||||
// 添加二级指标
|
||||
const addSubIndicator = (parentKey: string) => {
|
||||
const newKey = `level2-${Date.now()}`;
|
||||
// 使用工具函数生成唯一key
|
||||
const newKey = `level2-${generateUUID(16)}`;
|
||||
|
||||
const parent = dataSource.find((item) => item.key === parentKey);
|
||||
|
||||
if (!parent || !parent.baseIndicator) {
|
||||
@ -342,6 +498,7 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
ndScore: '0',
|
||||
isStar: StarLevel.NO,
|
||||
desc: '',
|
||||
ndOrderBy: dataSource.filter(item => item.baseIndicator === parent.baseIndicator).length + 1
|
||||
};
|
||||
|
||||
// 找到当前记录所在的位置
|
||||
@ -532,7 +689,9 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
<Button
|
||||
type="text"
|
||||
icon={<PlusCircleOutlined />}
|
||||
onClick={() => addLevel1Indicator(record)}
|
||||
onClick={() => {
|
||||
addLevel1Indicator(record);
|
||||
}}
|
||||
title="添加一级指标"
|
||||
/>
|
||||
<Button
|
||||
@ -668,6 +827,31 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
|
||||
return (
|
||||
<div className="evaluate-template-table">
|
||||
{console.log('渲染Table前的dataSource:', dataSource)}
|
||||
{console.log('渲染Table前的dataSource中的key列表:', dataSource.map(item => item.key))}
|
||||
{(() => {
|
||||
// 检查是否有重复的key
|
||||
const keyMap = new Map<string, number>();
|
||||
dataSource.forEach(item => {
|
||||
if (keyMap.has(item.key)) {
|
||||
keyMap.set(item.key, keyMap.get(item.key)! + 1);
|
||||
} else {
|
||||
keyMap.set(item.key, 1);
|
||||
}
|
||||
});
|
||||
|
||||
// 打印重复的key
|
||||
const duplicateKeys = Array.from(keyMap.entries())
|
||||
.filter(([_, count]) => count > 1)
|
||||
.map(([key]) => key);
|
||||
|
||||
if (duplicateKeys.length > 0) {
|
||||
console.error('渲染Table前发现重复的key:', duplicateKeys);
|
||||
console.error('重复key的详细信息:', dataSource.filter(item => duplicateKeys.includes(item.key)));
|
||||
}
|
||||
|
||||
return null;
|
||||
})()}
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
@ -678,23 +862,13 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
scroll={{ x: 'max-content' }}
|
||||
locale={{ emptyText: '无数据' }}
|
||||
/>
|
||||
{!isDetail && dataSource.length > 0 && (
|
||||
{!isDetail && (
|
||||
<div className="add-button-row">
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => addLevel1Indicator()}
|
||||
style={{ width: '100%', marginTop: 16 }}
|
||||
icon={<PlusOutlined />}
|
||||
>
|
||||
添加一级指标
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{!isDetail && dataSource.length === 0 && (
|
||||
<div className="add-button-row">
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => addLevel1Indicator()}
|
||||
onClick={() => {
|
||||
addLevel1Indicator();
|
||||
}}
|
||||
style={{ width: '100%', marginTop: 16 }}
|
||||
icon={<PlusOutlined />}
|
||||
>
|
||||
|
Reference in New Issue
Block a user