提交一下 换git仓库了
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useCallback, useMemo } from 'react';
|
||||
import { Input, Button, Badge, Avatar, Space, Tabs, Checkbox, Row, Col, Card, Empty, Divider, Alert } from 'antd';
|
||||
import { SearchOutlined, UserOutlined, TeamOutlined, CheckCircleOutlined } from '@ant-design/icons';
|
||||
import './EvaluateTaskPersonnelSelector.less';
|
||||
@ -75,33 +75,19 @@ const EvaluateTaskPersonnelSelector: React.FC<EvaluateTaskPersonnelSelectorProps
|
||||
* 部门数据列表
|
||||
* 实际项目中可通过API获取
|
||||
*/
|
||||
const departments: Department[] = [
|
||||
const departments: Department[] = useMemo(() => [
|
||||
{ id: '1', name: '采购部' },
|
||||
{ id: '2', name: '技术部' },
|
||||
{ id: '3', name: '质量部' },
|
||||
{ id: '4', name: '生产部' },
|
||||
{ id: '5', name: '财务部' },
|
||||
];
|
||||
|
||||
/**
|
||||
* 初始化人员数据和已选状态
|
||||
*/
|
||||
useEffect(() => {
|
||||
// 加载人员数据(模拟API请求)
|
||||
fetchPersonnelData();
|
||||
|
||||
// 设置已选人员
|
||||
if (selectedPersonnel && selectedPersonnel.length > 0) {
|
||||
const selectedIds = selectedPersonnel.map(item => item.id);
|
||||
setSelectedKeys(selectedIds);
|
||||
}
|
||||
}, [selectedPersonnel]);
|
||||
], []);
|
||||
|
||||
/**
|
||||
* 模拟获取人员数据
|
||||
* 实际项目中替换为API调用
|
||||
*/
|
||||
const fetchPersonnelData = () => {
|
||||
const fetchPersonnelData = useCallback(() => {
|
||||
// 模拟API调用获取评价人员
|
||||
const mockPersonnel: PersonnelItem[] = [
|
||||
{ id: '1', name: '张三', department: '采购部', position: '采购经理' },
|
||||
@ -119,46 +105,59 @@ const EvaluateTaskPersonnelSelector: React.FC<EvaluateTaskPersonnelSelectorProps
|
||||
];
|
||||
|
||||
setPersonnel(mockPersonnel);
|
||||
};
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* 初始化人员数据和已选状态
|
||||
*/
|
||||
useEffect(() => {
|
||||
// 加载人员数据(模拟API请求)
|
||||
fetchPersonnelData();
|
||||
|
||||
// 设置已选人员
|
||||
if (selectedPersonnel && selectedPersonnel.length > 0) {
|
||||
const selectedIds = selectedPersonnel.map(item => item.id);
|
||||
setSelectedKeys(selectedIds);
|
||||
}
|
||||
}, [fetchPersonnelData, selectedPersonnel]);
|
||||
|
||||
/**
|
||||
* 处理搜索
|
||||
* @param {string} value - 搜索关键词
|
||||
*/
|
||||
const handleSearch = (value: string) => {
|
||||
const handleSearch = useCallback((value: string) => {
|
||||
setKeyword(value);
|
||||
};
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* 处理Tab切换
|
||||
* @param {string} key - 选中的Tab键值
|
||||
*/
|
||||
const handleTabChange = (key: string) => {
|
||||
const handleTabChange = useCallback((key: string) => {
|
||||
setActiveTab(key);
|
||||
};
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* 处理单个人员选择
|
||||
* @param {string} personnelId - 人员ID
|
||||
* @param {boolean} checked - 是否选中
|
||||
*/
|
||||
const handleSelect = (personnelId: string, checked: boolean) => {
|
||||
let newSelectedKeys = [...selectedKeys];
|
||||
if (checked) {
|
||||
newSelectedKeys.push(personnelId);
|
||||
} else {
|
||||
newSelectedKeys = newSelectedKeys.filter(id => id !== personnelId);
|
||||
}
|
||||
|
||||
setSelectedKeys(newSelectedKeys);
|
||||
};
|
||||
const handleSelect = useCallback((personnelId: string, checked: boolean) => {
|
||||
setSelectedKeys(prevKeys => {
|
||||
if (checked) {
|
||||
return [...prevKeys, personnelId];
|
||||
} else {
|
||||
return prevKeys.filter(id => id !== personnelId);
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* 处理部门人员全选
|
||||
* @param {string} deptId - 部门ID
|
||||
* @param {boolean} checked - 是否全选
|
||||
*/
|
||||
const handleSelectAll = (deptId: string, checked: boolean) => {
|
||||
const handleSelectAll = useCallback((deptId: string, checked: boolean) => {
|
||||
const deptName = departments.find(d => d.id === deptId)?.name || '';
|
||||
const deptPersonnel = personnel.filter(p =>
|
||||
p.department === deptName &&
|
||||
@ -166,38 +165,64 @@ const EvaluateTaskPersonnelSelector: React.FC<EvaluateTaskPersonnelSelectorProps
|
||||
);
|
||||
const deptPersonnelIds = deptPersonnel.map(p => p.id);
|
||||
|
||||
let newSelectedKeys = [...selectedKeys];
|
||||
|
||||
if (checked) {
|
||||
// 添加该部门所有人员
|
||||
deptPersonnelIds.forEach(id => {
|
||||
if (!newSelectedKeys.includes(id)) {
|
||||
newSelectedKeys.push(id);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 移除该部门所有人员
|
||||
newSelectedKeys = newSelectedKeys.filter(id => !deptPersonnelIds.includes(id));
|
||||
}
|
||||
|
||||
setSelectedKeys(newSelectedKeys);
|
||||
};
|
||||
setSelectedKeys(prevKeys => {
|
||||
if (checked) {
|
||||
// 添加该部门所有人员
|
||||
const newKeys = new Set([...prevKeys, ...deptPersonnelIds]);
|
||||
return Array.from(newKeys);
|
||||
} else {
|
||||
// 移除该部门所有人员
|
||||
return prevKeys.filter(id => !deptPersonnelIds.includes(id));
|
||||
}
|
||||
});
|
||||
}, [departments, keyword, personnel]);
|
||||
|
||||
/**
|
||||
* 确认选择
|
||||
* 将选中的人员ID列表转换为人员对象列表
|
||||
*/
|
||||
const handleConfirm = () => {
|
||||
const handleConfirm = useCallback(() => {
|
||||
const selectedItems = personnel.filter(item => selectedKeys.includes(item.id));
|
||||
onSelect(selectedItems);
|
||||
};
|
||||
}, [onSelect, personnel, selectedKeys]);
|
||||
|
||||
/**
|
||||
* 计算各部门已选人数
|
||||
* @param {string} deptId - 部门ID
|
||||
* @returns {number} - 已选人数
|
||||
*/
|
||||
const getSelectedCountByDept = useCallback((deptId: string) => {
|
||||
const deptName = departments.find(d => d.id === deptId)?.name || '';
|
||||
const deptPersonnel = personnel.filter(p => p.department === deptName);
|
||||
const selectedCount = deptPersonnel.filter(p => selectedKeys.includes(p.id)).length;
|
||||
return selectedCount;
|
||||
}, [departments, personnel, selectedKeys]);
|
||||
|
||||
/**
|
||||
* 渲染Tab标签
|
||||
* @param {Department} dept - 部门信息
|
||||
* @returns {React.ReactNode} - 渲染结果
|
||||
*/
|
||||
const renderTabTitle = useCallback((dept: Department) => {
|
||||
const selectedCount = getSelectedCountByDept(dept.id);
|
||||
return (
|
||||
<Badge
|
||||
count={selectedCount > 0 ? selectedCount : 0}
|
||||
size="small"
|
||||
offset={[5, -3]}
|
||||
style={{ backgroundColor: selectedCount > 0 ? '#1890ff' : '#d9d9d9' }}
|
||||
>
|
||||
<span>{dept.name}</span>
|
||||
</Badge>
|
||||
);
|
||||
}, [getSelectedCountByDept]);
|
||||
|
||||
/**
|
||||
* 渲染部门下的人员列表
|
||||
* @param {string} deptId - 部门ID
|
||||
* @returns {React.ReactNode} - 渲染结果
|
||||
*/
|
||||
const renderDepartmentPersonnel = (deptId: string) => {
|
||||
const renderDepartmentPersonnel = useCallback((deptId: string) => {
|
||||
// 获取部门名称
|
||||
const deptName = departments.find(d => d.id === deptId)?.name || '';
|
||||
|
||||
@ -273,38 +298,19 @@ const EvaluateTaskPersonnelSelector: React.FC<EvaluateTaskPersonnelSelectorProps
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}, [departments, handleSelect, handleSelectAll, keyword, personnel, selectedKeys]);
|
||||
|
||||
/**
|
||||
* 计算各部门已选人数
|
||||
* @param {string} deptId - 部门ID
|
||||
* @returns {number} - 已选人数
|
||||
*/
|
||||
const getSelectedCountByDept = (deptId: string) => {
|
||||
const deptName = departments.find(d => d.id === deptId)?.name || '';
|
||||
const deptPersonnel = personnel.filter(p => p.department === deptName);
|
||||
const selectedCount = deptPersonnel.filter(p => selectedKeys.includes(p.id)).length;
|
||||
return selectedCount;
|
||||
};
|
||||
|
||||
/**
|
||||
* 渲染Tab标签
|
||||
* @param {Department} dept - 部门信息
|
||||
* @returns {React.ReactNode} - 渲染结果
|
||||
*/
|
||||
const renderTabTitle = (dept: Department) => {
|
||||
const selectedCount = getSelectedCountByDept(dept.id);
|
||||
return (
|
||||
<Badge
|
||||
count={selectedCount > 0 ? selectedCount : 0}
|
||||
size="small"
|
||||
offset={[5, -3]}
|
||||
style={{ backgroundColor: selectedCount > 0 ? '#1890ff' : '#d9d9d9' }}
|
||||
// 使用useMemo优化部门Tab的渲染
|
||||
const departmentTabs = useMemo(() => {
|
||||
return departments.map(dept => (
|
||||
<TabPane
|
||||
tab={renderTabTitle(dept)}
|
||||
key={dept.id}
|
||||
>
|
||||
<span>{dept.name}</span>
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
{renderDepartmentPersonnel(dept.id)}
|
||||
</TabPane>
|
||||
));
|
||||
}, [departments, renderDepartmentPersonnel, renderTabTitle]);
|
||||
|
||||
return (
|
||||
<div className="evaluate-task-personnel-selector">
|
||||
@ -346,14 +352,7 @@ const EvaluateTaskPersonnelSelector: React.FC<EvaluateTaskPersonnelSelectorProps
|
||||
onChange={handleTabChange}
|
||||
className="department-tabs"
|
||||
>
|
||||
{departments.map(dept => (
|
||||
<TabPane
|
||||
tab={renderTabTitle(dept)}
|
||||
key={dept.id}
|
||||
>
|
||||
{renderDepartmentPersonnel(dept.id)}
|
||||
</TabPane>
|
||||
))}
|
||||
{departmentTabs}
|
||||
</Tabs>
|
||||
|
||||
{/* 底部确认按钮区域 */}
|
||||
|
@ -7,15 +7,11 @@ import {
|
||||
Select,
|
||||
Form,
|
||||
InputNumber,
|
||||
Upload,
|
||||
message,
|
||||
Divider,
|
||||
Switch,
|
||||
} from 'antd';
|
||||
import {
|
||||
PlusOutlined,
|
||||
MinusCircleOutlined,
|
||||
UploadOutlined,
|
||||
PlusCircleOutlined,
|
||||
DeleteOutlined,
|
||||
} from '@ant-design/icons';
|
||||
@ -24,14 +20,9 @@ import { StarLevel, StarLevelText } from '@/dicts/supplierTemplateDict';
|
||||
import { generateUUID } from '@/utils/utils';
|
||||
import './EvaluateTemplateTable.less';
|
||||
|
||||
// 注意:这里我们使用SupplierEvaluate命名空间中的类型
|
||||
// 类型定义已移动到 src/typings.d.ts 文件中
|
||||
|
||||
const { Option } = Select;
|
||||
const { TextArea } = Input;
|
||||
|
||||
// 移除类型定义,使用全局命名空间中的类型
|
||||
|
||||
interface EvaluateTemplateTableProps {
|
||||
value?: any[];
|
||||
onChange?: (value: any[]) => void;
|
||||
@ -133,7 +124,7 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
const convertTableDataToApiData = (tableData: TableRowItem[]): any[] => {
|
||||
// 按一级指标分组
|
||||
const groupedByLevel1 = tableData.reduce((acc: Record<string, TableRowItem[]>, item: TableRowItem) => {
|
||||
// 为空的baseIndicator也需要分组,使用特殊键标识 - 这是关键修复
|
||||
// 为空的baseIndicator也需要分组,使用特殊键标识
|
||||
const groupKey = item.baseIndicator || `empty-${item.key}`;
|
||||
|
||||
if (!acc[groupKey]) {
|
||||
@ -151,7 +142,6 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
// 生成唯一的临时ID
|
||||
const tempStId = `temp-st-${generateUUID(16)}-${stIndex}`;
|
||||
|
||||
// 简化数据转换,使用可选链和短路评估
|
||||
return {
|
||||
id: firstItem.stId || tempStId,
|
||||
baseIndicator: firstItem.baseIndicator || '',
|
||||
@ -182,43 +172,36 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
fetchIndicatorTypes();
|
||||
}, []);
|
||||
|
||||
// 确保数据中没有重复的key
|
||||
const ensureUniqueKeys = (data: TableRowItem[]): TableRowItem[] => {
|
||||
const keyMap = new Map<string, boolean>();
|
||||
|
||||
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) {
|
||||
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) => {
|
||||
const updatedTableData = tableData.map((newItem) => {
|
||||
// 尝试查找对应的现有项,通过stId和ndId匹配
|
||||
const existingItem = dataSource.find(existing =>
|
||||
(existing.stId === newItem.stId && existing.ndId === newItem.ndId) ||
|
||||
@ -227,62 +210,17 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
|
||||
// 如果找到现有项,保留其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 };
|
||||
return newItem;
|
||||
});
|
||||
|
||||
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);
|
||||
// 确保所有key都是唯一的
|
||||
setDataSource(ensureUniqueKeys(updatedTableData));
|
||||
} 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);
|
||||
setDataSource(ensureUniqueKeys(tableData));
|
||||
}
|
||||
}
|
||||
} else if (value && value.length === 0 && dataSource.length > 0) {
|
||||
@ -293,62 +231,8 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
|
||||
// 更新数据源
|
||||
const updateDataSource = (newData: TableRowItem[]) => {
|
||||
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));
|
||||
const finalData = ensureUniqueKeys(newData);
|
||||
|
||||
setDataSource(finalData);
|
||||
if (onChange) {
|
||||
@ -413,10 +297,8 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
|
||||
// 添加一级指标
|
||||
const addLevel1Indicator = (currentRecord?: TableRowItem) => {
|
||||
// 使用改进后的工具函数生成唯一key,不再需要额外的时间戳和随机数
|
||||
// 使用改进后的工具函数生成唯一key
|
||||
const newKey = `level1-${generateUUID(32)}`;
|
||||
console.log('添加一级指标,生成的key:', newKey);
|
||||
console.log('当前数据源:', dataSource);
|
||||
|
||||
const newItem: TableRowItem = {
|
||||
key: newKey,
|
||||
@ -431,7 +313,6 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
orderBy: dataSource.length + 1, // 确保正确的排序
|
||||
ndOrderBy: 1 // 设置二级指标初始排序
|
||||
};
|
||||
console.log('新建的一级指标项:', newItem);
|
||||
|
||||
// 制作数据源的副本,避免直接修改状态
|
||||
const newData = [...dataSource];
|
||||
@ -443,11 +324,9 @@ 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);
|
||||
}
|
||||
|
||||
// 如果找到了位置,在该位置后插入,否则添加到末尾
|
||||
@ -456,8 +335,6 @@ const EvaluateTemplateTable: React.FC<EvaluateTemplateTableProps> = ({
|
||||
} else {
|
||||
newData.push(newItem);
|
||||
}
|
||||
console.log('插入后的数据源:', newData);
|
||||
console.log('插入后的数据源中的key列表:', newData.map(item => item.key));
|
||||
|
||||
updateDataSource(newData);
|
||||
};
|
||||
@ -827,31 +704,6 @@ 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}
|
||||
|
@ -1,45 +1,69 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Form, Input, Select, DatePicker, Row, Col, Card } from 'antd';
|
||||
import React, { useState, useEffect, useCallback, forwardRef, useImperativeHandle } from 'react';
|
||||
import { Form, Input, Select, DatePicker, Row, Col, Card, Radio } from 'antd';
|
||||
import moment from 'moment';
|
||||
import { getAllTemplates } from '@/servers/api/supplierEvaluate';
|
||||
import CategorySelector from '@/components/CategorySelector';
|
||||
import styles from '../supplierTaskManageAdd.less';
|
||||
import { CategoryLimitationType } from '@/dicts/supplierTemplateDict';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
interface BasicInfoStepProps {
|
||||
formData: any;
|
||||
onFormDataChange: (data: any) => void;
|
||||
formData: Partial<SupplierEvaluate.TaskAddRequest>;
|
||||
onFormDataChange: (data: Partial<SupplierEvaluate.TaskAddRequest>) => void;
|
||||
}
|
||||
|
||||
interface TemplateItem {
|
||||
id: string;
|
||||
templateName: string;
|
||||
isUnlimitedCategory?: boolean;
|
||||
categoryLimitation?: string;
|
||||
}
|
||||
|
||||
const BasicInfoStep: React.FC<BasicInfoStepProps> = ({ formData, onFormDataChange }) => {
|
||||
const BasicInfoStep = forwardRef<any, BasicInfoStepProps>(({ formData, onFormDataChange }, ref) => {
|
||||
const [form] = Form.useForm();
|
||||
const [templates, setTemplates] = useState<TemplateItem[]>([]);
|
||||
const [categoryTemplates, setCategoryTemplates] = useState<any[]>([]);
|
||||
const [selectedCategoryId, setSelectedCategoryId] = useState<string | undefined>(undefined);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
|
||||
// 暴露表单方法给父组件
|
||||
useImperativeHandle(ref, () => ({
|
||||
validateFields: () => form.validateFields(),
|
||||
getFieldsValue: () => form.getFieldsValue(),
|
||||
setFieldsValue: (values: any) => form.setFieldsValue(values),
|
||||
}));
|
||||
|
||||
// 获取模板列表
|
||||
const fetchTemplates = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const response = await getAllTemplates();
|
||||
if (response.success && response.data) {
|
||||
setTemplates(response.data);
|
||||
} else {
|
||||
setTemplates([
|
||||
{ id: '1', templateName: '不限品类模板', categoryLimitation: '0' },
|
||||
{ id: '2', templateName: '限制品类模板', categoryLimitation: '1' },
|
||||
]);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取模板列表失败:', error);
|
||||
// 使用模拟数据
|
||||
setTemplates([
|
||||
{ id: '1', templateName: '不限品类模板', categoryLimitation: '0' },
|
||||
{ id: '2', templateName: '限制品类模板', categoryLimitation: '1' },
|
||||
]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取评价模板和初始化表单数据
|
||||
useEffect(() => {
|
||||
// 模拟API调用获取模板列表
|
||||
const mockTemplates = [
|
||||
{ id: '1', templateName: '不限品类', isUnlimitedCategory: true },
|
||||
{ id: '2', templateName: '按品类', isUnlimitedCategory: false },
|
||||
];
|
||||
setTemplates(mockTemplates);
|
||||
fetchTemplates();
|
||||
|
||||
// 初始化表单数据
|
||||
if (formData) {
|
||||
const initialValues = {
|
||||
...formData,
|
||||
evaluateStartTime: formData.evaluateStartTime ? moment(formData.evaluateStartTime) : undefined,
|
||||
evaluateEndTime: formData.evaluateEndTime ? moment(formData.evaluateEndTime) : undefined,
|
||||
};
|
||||
form.setFieldsValue(initialValues);
|
||||
form.setFieldsValue(formData);
|
||||
|
||||
// 设置已选品类
|
||||
if (formData.categoryId) {
|
||||
@ -48,73 +72,61 @@ const BasicInfoStep: React.FC<BasicInfoStepProps> = ({ formData, onFormDataChang
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 获取品类对应的模板数据
|
||||
useEffect(() => {
|
||||
if (selectedCategoryId) {
|
||||
// 模拟API调用,根据品类ID获取对应的模板列表
|
||||
const mockCategoryTemplates = [
|
||||
{ id: 'ct1', templateName: '硬件设备评价模板' },
|
||||
{ id: 'ct2', templateName: '备件评价模板' },
|
||||
{ id: 'ct3', templateName: '通用备件评价模板' },
|
||||
];
|
||||
setCategoryTemplates(mockCategoryTemplates);
|
||||
} else {
|
||||
setCategoryTemplates([]);
|
||||
}
|
||||
}, [selectedCategoryId]);
|
||||
|
||||
// 检查当前选择的模板是否限制品类
|
||||
const isTemplateUnlimitedCategory = () => {
|
||||
const templateId = form.getFieldValue('templateId');
|
||||
const currentTemplate = templates.find(t => t.id === templateId);
|
||||
return currentTemplate?.isUnlimitedCategory;
|
||||
};
|
||||
|
||||
// 表单值变化时触发
|
||||
const handleValuesChange = (changedValues: any, allValues: any) => {
|
||||
// 处理模板变更
|
||||
if (changedValues.templateId) {
|
||||
// 如果选择了不限品类的模板,清空品类选择和品类模板选择
|
||||
if (isTemplateUnlimitedCategory()) {
|
||||
const template = templates.find((t) => t.id === changedValues.templateId);
|
||||
if (template) {
|
||||
form.setFieldsValue({
|
||||
categoryId: undefined,
|
||||
categoryTemplateId: undefined
|
||||
categoryLimitation: template.categoryLimitation || '0',
|
||||
});
|
||||
setSelectedCategoryId(undefined);
|
||||
|
||||
// 如果选择了不限品类的模板,清空品类选择
|
||||
if (template.categoryLimitation === '0') {
|
||||
form.setFieldsValue({
|
||||
categoryId: undefined,
|
||||
});
|
||||
setSelectedCategoryId(undefined);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 处理品类变更
|
||||
if (changedValues.categoryId) {
|
||||
setSelectedCategoryId(changedValues.categoryId?.[0]);
|
||||
form.setFieldsValue({ categoryTemplateId: undefined });
|
||||
}
|
||||
|
||||
// 将moment对象转换为字符串再传递
|
||||
// 默认设置weightStatus为0
|
||||
const formattedValues = {
|
||||
...allValues,
|
||||
evaluateStartTime: allValues.evaluateStartTime?.format('YYYY-MM-DD'),
|
||||
evaluateEndTime: allValues.evaluateEndTime?.format('YYYY-MM-DD'),
|
||||
weightStatus: 0,
|
||||
};
|
||||
|
||||
onFormDataChange(formattedValues);
|
||||
};
|
||||
|
||||
// 年度选项生成
|
||||
const yearOptions = () => {
|
||||
const yearOptions = useCallback(() => {
|
||||
const currentYear = new Date().getFullYear();
|
||||
return Array.from({ length: 11 }, (_, i) => currentYear - 5 + i).map(year => (
|
||||
<Option key={year} value={year.toString()}>{year}</Option>
|
||||
return Array.from({ length: 11 }, (_, i) => currentYear - 5 + i).map((year) => (
|
||||
<Option key={year} value={year.toString()}>
|
||||
{year}
|
||||
</Option>
|
||||
));
|
||||
};
|
||||
}, []);
|
||||
|
||||
// 判断是否显示品类选择器
|
||||
const shouldShowCategorySelector = () => {
|
||||
const templateId = form.getFieldValue('templateId');
|
||||
if (!templateId) return false;
|
||||
const shouldShowCategorySelector = useCallback(() => {
|
||||
const categoryLimitation = form.getFieldValue('categoryLimitation');
|
||||
return categoryLimitation === CategoryLimitationType.LIMITED;
|
||||
}, [form]);
|
||||
|
||||
const currentTemplate = templates.find(t => t.id === templateId);
|
||||
return currentTemplate && !currentTemplate.isUnlimitedCategory;
|
||||
};
|
||||
// 处理品类选择变化
|
||||
const handleCategoryChange = useCallback((value: string | string[]) => {
|
||||
const categoryId = Array.isArray(value) ? value[0] : value;
|
||||
setSelectedCategoryId(categoryId);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={styles.basicInfoStep}>
|
||||
@ -123,7 +135,10 @@ const BasicInfoStep: React.FC<BasicInfoStepProps> = ({ formData, onFormDataChang
|
||||
form={form}
|
||||
layout="vertical"
|
||||
onValuesChange={handleValuesChange}
|
||||
initialValues={formData}
|
||||
initialValues={{
|
||||
...formData,
|
||||
categoryLimitation: formData.categoryLimitation || '0',
|
||||
}}
|
||||
>
|
||||
<Row gutter={24}>
|
||||
<Col span={12}>
|
||||
@ -141,7 +156,7 @@ const BasicInfoStep: React.FC<BasicInfoStepProps> = ({ formData, onFormDataChang
|
||||
name="evaluateYear"
|
||||
rules={[{ required: true, message: '请选择评价年度' }]}
|
||||
>
|
||||
<Select placeholder="2025" style={{ width: '100%' }}>
|
||||
<Select placeholder="请选择年度" style={{ width: '100%' }}>
|
||||
{yearOptions()}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
@ -152,49 +167,42 @@ const BasicInfoStep: React.FC<BasicInfoStepProps> = ({ formData, onFormDataChang
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label="评价开始时间"
|
||||
name="evaluateStartTime"
|
||||
name="startTime"
|
||||
rules={[{ required: true, message: '请选择评价开始时间' }]}
|
||||
getValueProps={(value) => ({
|
||||
value: value ? moment(value) : undefined,
|
||||
})}
|
||||
normalize={(value) => value && value.format('YYYY-MM-DD')}
|
||||
>
|
||||
<DatePicker
|
||||
style={{ width: '100%' }}
|
||||
format="YYYY-MM-DD"
|
||||
placeholder="年/月/日"
|
||||
/>
|
||||
<DatePicker style={{ width: '100%' }} format="YYYY-MM-DD" placeholder="年/月/日" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label="评价结束时间"
|
||||
name="evaluateEndTime"
|
||||
name="endTime"
|
||||
rules={[{ required: true, message: '请选择评价结束时间' }]}
|
||||
getValueProps={(value) => ({
|
||||
value: value ? moment(value) : undefined,
|
||||
})}
|
||||
normalize={(value) => value && value.format('YYYY-MM-DD')}
|
||||
>
|
||||
<DatePicker
|
||||
style={{ width: '100%' }}
|
||||
format="YYYY-MM-DD"
|
||||
placeholder="年/月/日"
|
||||
/>
|
||||
<DatePicker style={{ width: '100%' }} format="YYYY-MM-DD" placeholder="年/月/日" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row gutter={24} align="middle">
|
||||
<Row gutter={24}>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label="适用评价模板"
|
||||
name="templateId"
|
||||
rules={[{ required: true, message: '请选择适用评价模板' }]}
|
||||
label="品类限制类型"
|
||||
name="categoryLimitation"
|
||||
rules={[{ required: true, message: '请选择品类限制类型' }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择"
|
||||
style={{ width: '100%' }}
|
||||
allowClear
|
||||
>
|
||||
{templates.map(template => (
|
||||
<Option key={template.id} value={template.id}>
|
||||
{template.templateName}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
<Radio.Group>
|
||||
<Radio value={CategoryLimitationType.UNIVERSAL}>通用不限品类</Radio>
|
||||
<Radio value={CategoryLimitationType.LIMITED}>限制品类</Radio>
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
{shouldShowCategorySelector() && (
|
||||
@ -204,16 +212,7 @@ const BasicInfoStep: React.FC<BasicInfoStepProps> = ({ formData, onFormDataChang
|
||||
name="categoryId"
|
||||
rules={[{ required: true, message: '请选择品类' }]}
|
||||
>
|
||||
<CategorySelector
|
||||
onChange={(values: string[]) => {
|
||||
const categoryId = values?.[0];
|
||||
setSelectedCategoryId(categoryId);
|
||||
|
||||
if (!categoryId) {
|
||||
form.setFieldsValue({ categoryTemplateId: undefined });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<CategorySelector onChange={handleCategoryChange} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
)}
|
||||
@ -223,15 +222,11 @@ const BasicInfoStep: React.FC<BasicInfoStepProps> = ({ formData, onFormDataChang
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label="选择模板"
|
||||
name="categoryTemplateId"
|
||||
rules={[{ required: true, message: '请选择模板' }]}
|
||||
name="templateId"
|
||||
rules={[{ required: true, message: '请选择适用评价模板' }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择"
|
||||
style={{ width: '100%' }}
|
||||
allowClear
|
||||
>
|
||||
{categoryTemplates.map(template => (
|
||||
<Select placeholder="请选择" style={{ width: '100%' }} allowClear loading={loading}>
|
||||
{templates.map((template) => (
|
||||
<Option key={template.id} value={template.id}>
|
||||
{template.templateName}
|
||||
</Option>
|
||||
@ -244,6 +239,6 @@ const BasicInfoStep: React.FC<BasicInfoStepProps> = ({ formData, onFormDataChang
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default BasicInfoStep;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
|
||||
import { Card, Button, Table, Space, Modal, Tag, Tooltip, Input, Form, InputNumber } from 'antd';
|
||||
import { UserOutlined, SettingOutlined } from '@ant-design/icons';
|
||||
import styles from '../supplierTaskManageAdd.less';
|
||||
@ -25,7 +25,7 @@ interface WeightUnit {
|
||||
weight: number;
|
||||
}
|
||||
|
||||
const EvaluatorSelectStep: React.FC<EvaluatorSelectStepProps> = ({ formData, onFormDataChange }) => {
|
||||
const EvaluatorSelectStep = forwardRef<any, EvaluatorSelectStepProps>(({ formData, onFormDataChange }, ref) => {
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||
const [suppliers, setSuppliers] = useState<SupplierItem[]>([]);
|
||||
const [batchSelectModalVisible, setBatchSelectModalVisible] = useState(false);
|
||||
@ -41,6 +41,57 @@ const EvaluatorSelectStep: React.FC<EvaluatorSelectStepProps> = ({ formData, onF
|
||||
]);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
// 暴露表单方法给父组件
|
||||
useImperativeHandle(ref, () => ({
|
||||
validateFields: () => {
|
||||
// 自定义验证逻辑
|
||||
if (suppliers.length === 0) {
|
||||
return Promise.reject('请至少添加一个供应商');
|
||||
}
|
||||
|
||||
// 检查是否每个供应商都有评价人员
|
||||
const hasNoEvaluator = suppliers.some(supplier =>
|
||||
!supplier.evaluators || supplier.evaluators.length === 0
|
||||
);
|
||||
|
||||
if (hasNoEvaluator) {
|
||||
return Promise.reject('存在未分配评价人员的供应商');
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
getFieldsValue: () => {
|
||||
// 构建评价人员列表
|
||||
const indicatorList = suppliers.flatMap(supplier =>
|
||||
supplier.evaluators?.map(evaluator => ({
|
||||
userId: evaluator.id,
|
||||
type: 0, // 假设按评价单评价
|
||||
indicatorIds: []
|
||||
})) || []
|
||||
);
|
||||
|
||||
// 构建供应商ID列表
|
||||
const supplierIds = suppliers.map(supplier => ({
|
||||
id: supplier.key,
|
||||
userIds: supplier.evaluators?.map(e => e.id) || []
|
||||
}));
|
||||
|
||||
return {
|
||||
indicatorList,
|
||||
supplierIds,
|
||||
suppliersWithEvaluators: suppliers
|
||||
};
|
||||
},
|
||||
setFieldsValue: (values: any) => {
|
||||
if (values.suppliersWithEvaluators) {
|
||||
setSuppliers(values.suppliersWithEvaluators);
|
||||
}
|
||||
if (values.weightUnits) {
|
||||
setWeightUnits(values.weightUnits);
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
// 从上一步获取供应商数据
|
||||
useEffect(() => {
|
||||
if (formData.selectedSuppliers) {
|
||||
@ -75,9 +126,25 @@ const EvaluatorSelectStep: React.FC<EvaluatorSelectStepProps> = ({ formData, onF
|
||||
|
||||
// 更新表单数据
|
||||
const updateFormData = (updatedData: any) => {
|
||||
// 构建评价人员列表
|
||||
const indicatorList = suppliers.flatMap(supplier =>
|
||||
supplier.evaluators?.map(evaluator => ({
|
||||
userId: evaluator.id,
|
||||
type: 0, // 假设按评价单评价
|
||||
indicatorIds: []
|
||||
})) || []
|
||||
);
|
||||
|
||||
// 构建供应商ID列表
|
||||
const supplierIds = suppliers.map(supplier => ({
|
||||
id: supplier.key,
|
||||
userIds: supplier.evaluators?.map(e => e.id) || []
|
||||
}));
|
||||
|
||||
onFormDataChange({
|
||||
...formData,
|
||||
...updatedData
|
||||
...updatedData,
|
||||
indicatorList,
|
||||
supplierIds
|
||||
});
|
||||
};
|
||||
|
||||
@ -252,122 +319,103 @@ const EvaluatorSelectStep: React.FC<EvaluatorSelectStepProps> = ({ formData, onF
|
||||
},
|
||||
];
|
||||
|
||||
// 权重表格列定义
|
||||
const weightColumns = [
|
||||
{
|
||||
title: '序号',
|
||||
dataIndex: 'id',
|
||||
key: 'id',
|
||||
width: 80,
|
||||
render: (_: string, __: any, index: number) => index + 1
|
||||
},
|
||||
{
|
||||
title: '评价单位',
|
||||
dataIndex: 'name',
|
||||
key: 'name'
|
||||
},
|
||||
{
|
||||
title: <span style={{ color: 'red' }}>*权重 (%)</span>,
|
||||
dataIndex: 'weight',
|
||||
key: 'weight',
|
||||
render: (_: number, record: WeightUnit) => (
|
||||
<Form.Item
|
||||
name={['weightUnits', record.id]}
|
||||
rules={[
|
||||
{ required: true, message: '请输入权重' },
|
||||
{ type: 'number', min: 0, max: 100, message: '权重范围为0-100' },
|
||||
]}
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
<InputNumber min={0} max={100} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={styles.evaluatorSelectStep}>
|
||||
<div className={styles.toolBar}>
|
||||
<Space>
|
||||
{selectedRowKeys.length > 0 && (
|
||||
<Card title="选择评价人员" bordered={false} className="inner-card">
|
||||
<div className={styles.toolbar}>
|
||||
<Space>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<UserOutlined />}
|
||||
onClick={handleBatchSelect}
|
||||
disabled={selectedRowKeys.length === 0}
|
||||
>
|
||||
批量选择
|
||||
批量选择评价人员
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
icon={<SettingOutlined />}
|
||||
onClick={handleWeightSetting}
|
||||
>
|
||||
设置评分单位权重
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
<Button
|
||||
icon={<SettingOutlined />}
|
||||
onClick={handleWeightSetting}
|
||||
>
|
||||
设置评分单位权重
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<Card title="已选供应商列表" bordered={false} className="inner-card">
|
||||
<Table
|
||||
rowSelection={rowSelection}
|
||||
columns={columns}
|
||||
dataSource={suppliers}
|
||||
pagination={false}
|
||||
size="middle"
|
||||
rowKey="key"
|
||||
/>
|
||||
</Card>
|
||||
|
||||
{/* 批量选择模态框 */}
|
||||
<Modal
|
||||
title="批量选择评价人员"
|
||||
visible={batchSelectModalVisible}
|
||||
footer={null}
|
||||
onCancel={() => setBatchSelectModalVisible(false)}
|
||||
width={800}
|
||||
destroyOnClose
|
||||
>
|
||||
<EvaluateTaskPersonnelSelector
|
||||
onSelect={handleBatchEvaluatorSelect}
|
||||
selectedPersonnel={currentSupplier?.evaluators}
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
{/* 设置评分单位权重模态框 */}
|
||||
<Modal
|
||||
title="评分单位权重设置"
|
||||
visible={weightSettingModalVisible}
|
||||
onOk={handleSaveWeights}
|
||||
onCancel={() => setWeightSettingModalVisible(false)}
|
||||
width={600}
|
||||
>
|
||||
<Form form={form} layout="vertical">
|
||||
<Table
|
||||
columns={weightColumns}
|
||||
dataSource={weightUnits}
|
||||
pagination={false}
|
||||
rowKey="id"
|
||||
bordered
|
||||
{/* 批量选择评价人员弹窗 */}
|
||||
<Modal
|
||||
title="批量选择评价人员"
|
||||
visible={batchSelectModalVisible}
|
||||
onCancel={() => setBatchSelectModalVisible(false)}
|
||||
footer={null}
|
||||
width={700}
|
||||
>
|
||||
<EvaluateTaskPersonnelSelector
|
||||
onSelect={handleBatchEvaluatorSelect}
|
||||
selectedPersonnel={[]}
|
||||
/>
|
||||
</Form>
|
||||
</Modal>
|
||||
</Modal>
|
||||
|
||||
{/* 评价人员选择/查看模态框 */}
|
||||
<Modal
|
||||
title={currentSupplier?.supplierName + ' - 评价人员选择'}
|
||||
visible={evaluatorModalVisible}
|
||||
footer={null}
|
||||
onCancel={() => setEvaluatorModalVisible(false)}
|
||||
width={800}
|
||||
destroyOnClose
|
||||
>
|
||||
<EvaluateTaskPersonnelSelector
|
||||
onSelect={handleEvaluatorSelect}
|
||||
selectedPersonnel={currentSupplier?.evaluators}
|
||||
/>
|
||||
</Modal>
|
||||
{/* 单个供应商选择评价人员弹窗 */}
|
||||
<Modal
|
||||
title={`选择评价人员 - ${currentSupplier?.supplierName || ''}`}
|
||||
visible={evaluatorModalVisible}
|
||||
onCancel={() => setEvaluatorModalVisible(false)}
|
||||
footer={null}
|
||||
width={700}
|
||||
>
|
||||
<EvaluateTaskPersonnelSelector
|
||||
onSelect={handleEvaluatorSelect}
|
||||
selectedPersonnel={currentSupplier?.evaluators || []}
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
{/* 权重设置弹窗 */}
|
||||
<Modal
|
||||
title="设置评分单位权重"
|
||||
visible={weightSettingModalVisible}
|
||||
onOk={handleSaveWeights}
|
||||
onCancel={() => setWeightSettingModalVisible(false)}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
>
|
||||
<Form.Item label="权重设置" required>
|
||||
<p>请设置各评分单位的权重比例,总和需为100%</p>
|
||||
{weightUnits.map(unit => (
|
||||
<Form.Item
|
||||
key={unit.id}
|
||||
label={unit.name}
|
||||
name={['weightUnits', unit.id]}
|
||||
rules={[{ required: true, message: '请输入权重值' }]}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
max={100}
|
||||
formatter={value => `${value}%`}
|
||||
parser={(value: string | undefined) => {
|
||||
if (!value) return 0;
|
||||
const num = Number(value.replace('%', ''));
|
||||
return num;
|
||||
}}
|
||||
style={{ width: '100%' }}
|
||||
/>
|
||||
</Form.Item>
|
||||
))}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default EvaluatorSelectStep;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, forwardRef, useImperativeHandle } from 'react';
|
||||
import { Card, Row, Col, Input, Select, Radio, Table, Button, Space, Pagination, Form } from 'antd';
|
||||
import { SearchOutlined, ArrowRightOutlined } from '@ant-design/icons';
|
||||
import styles from '../supplierTaskManageAdd.less';
|
||||
@ -17,7 +17,7 @@ interface SupplierItem {
|
||||
category: string;
|
||||
}
|
||||
|
||||
const SupplierSelectStep: React.FC<SupplierSelectStepProps> = ({ formData, onFormDataChange }) => {
|
||||
const SupplierSelectStep = forwardRef<any, SupplierSelectStepProps>(({ formData, onFormDataChange }, ref) => {
|
||||
const [filterForm] = Form.useForm();
|
||||
const [categoryKeyword, setCategoryKeyword] = useState<string>('');
|
||||
const [selectedCategory, setSelectedCategory] = useState<string | undefined>(undefined);
|
||||
@ -33,6 +33,25 @@ const SupplierSelectStep: React.FC<SupplierSelectStepProps> = ({ formData, onFor
|
||||
const [pendingSelectedRowKeys, setPendingSelectedRowKeys] = useState<React.Key[]>([]);
|
||||
const [selectedSelectedRowKeys, setSelectedSelectedRowKeys] = useState<React.Key[]>([]);
|
||||
|
||||
// 暴露表单方法给父组件
|
||||
useImperativeHandle(ref, () => ({
|
||||
validateFields: () => {
|
||||
// 这里可以添加自定义验证逻辑
|
||||
return Promise.resolve();
|
||||
},
|
||||
getFieldsValue: () => {
|
||||
return {
|
||||
selectedSuppliers,
|
||||
supplierIds: selectedSuppliers.map(supplier => ({ id: supplier.key }))
|
||||
};
|
||||
},
|
||||
setFieldsValue: (values: any) => {
|
||||
if (values.selectedSuppliers) {
|
||||
setSelectedSuppliers(values.selectedSuppliers);
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
// 初始化数据
|
||||
useEffect(() => {
|
||||
// 从formData中恢复已选供应商
|
||||
@ -87,7 +106,10 @@ const SupplierSelectStep: React.FC<SupplierSelectStepProps> = ({ formData, onFor
|
||||
|
||||
// 更新表单数据
|
||||
const updateFormData = (suppliers: SupplierItem[]) => {
|
||||
onFormDataChange({ selectedSuppliers: suppliers });
|
||||
onFormDataChange({
|
||||
selectedSuppliers: suppliers,
|
||||
supplierIds: suppliers.map(supplier => ({ id: supplier.key }))
|
||||
});
|
||||
};
|
||||
|
||||
// 处理筛选条件变化
|
||||
@ -242,7 +264,7 @@ const SupplierSelectStep: React.FC<SupplierSelectStepProps> = ({ formData, onFor
|
||||
onValuesChange={handleFilterChange}
|
||||
>
|
||||
<Row gutter={24}>
|
||||
<Col span={12}>
|
||||
<Col span={8}>
|
||||
<Form.Item label="选择类别" name="category">
|
||||
<Select
|
||||
placeholder="请选择"
|
||||
@ -254,19 +276,17 @@ const SupplierSelectStep: React.FC<SupplierSelectStepProps> = ({ formData, onFor
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Col span={8}>
|
||||
<Form.Item label="品类关键字" name="categoryKeyword">
|
||||
<Input placeholder="请输入" allowClear />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={24}>
|
||||
<Col span={12}>
|
||||
<Col span={8}>
|
||||
<Form.Item label="品类范围" name="categoryRange">
|
||||
<Input placeholder="请输入" allowClear />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Col span={8}>
|
||||
<Form.Item label="近一年有付款" name="hasPaymentLastYear" initialValue="是">
|
||||
<Radio.Group>
|
||||
<Radio value="是">是</Radio>
|
||||
@ -274,9 +294,7 @@ const SupplierSelectStep: React.FC<SupplierSelectStepProps> = ({ formData, onFor
|
||||
</Radio.Group>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={24}>
|
||||
<Col span={12}>
|
||||
<Col span={8}>
|
||||
<Form.Item label="准入部门" name="department">
|
||||
<Select
|
||||
placeholder="请选择"
|
||||
@ -288,7 +306,7 @@ const SupplierSelectStep: React.FC<SupplierSelectStepProps> = ({ formData, onFor
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Col span={8}>
|
||||
<Form.Item wrapperCol={{ offset: 6, span: 18 }}>
|
||||
<Button type="primary" onClick={() => filterForm.submit()}>
|
||||
查询
|
||||
@ -391,6 +409,6 @@ const SupplierSelectStep: React.FC<SupplierSelectStepProps> = ({ formData, onFor
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
});
|
||||
|
||||
export default SupplierSelectStep;
|
||||
|
@ -25,15 +25,13 @@ import {
|
||||
} from '@ant-design/icons';
|
||||
import { history } from 'umi';
|
||||
import { TaskStatus, TaskType, TaskStatusText, TaskStatusColor, TaskTypeText } from '@/dicts/supplierTaskDict';
|
||||
import { getTaskList, deleteTask } from '@/servers/api/supplierEvaluate';
|
||||
import styles from './supplierTaskManage.less';
|
||||
|
||||
const { Option } = Select;
|
||||
const { RangePicker } = DatePicker;
|
||||
|
||||
// 删除本地接口定义,使用全局类型定义
|
||||
|
||||
const SupplierTaskManage: React.FC = () => {
|
||||
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [form] = Form.useForm();
|
||||
const [modalVisible, setModalVisible] = useState<boolean>(false);
|
||||
@ -54,7 +52,7 @@ const SupplierTaskManage: React.FC = () => {
|
||||
});
|
||||
const [searchParams, setSearchParams] = useState<SupplierEvaluate.TaskSearchParams>({});
|
||||
|
||||
// 模拟获取任务列表
|
||||
// 获取任务列表
|
||||
const fetchTaskList = async (
|
||||
current = 1,
|
||||
pageSize = 10,
|
||||
@ -67,64 +65,62 @@ const SupplierTaskManage: React.FC = () => {
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
// 模拟API请求
|
||||
setTimeout(() => {
|
||||
// 模拟数据
|
||||
const mockData: SupplierEvaluate.TaskRecord[] = Array.from({ length: 35 }).map((_, index) => {
|
||||
const id = `${index + 1}`;
|
||||
const status = Object.values(TaskStatus)[Math.floor(Math.random() * 3)];
|
||||
const taskType = Math.random() > 0.5 ? TaskType.REGULAR : TaskType.SPECIAL;
|
||||
// 构造请求参数
|
||||
const requestParams: SupplierEvaluate.TaskRequest = {
|
||||
basePageRequest: {
|
||||
pageNo: current,
|
||||
pageSize: pageSize,
|
||||
},
|
||||
};
|
||||
|
||||
// 生成随机单位名称
|
||||
const units = ['中山市合创展包装材料有限公司', '广州市科技发展有限公司', '深圳市创新科技有限公司', '东莞市制造业有限公司'];
|
||||
const unitIndex = Math.floor(Math.random() * units.length);
|
||||
// 添加搜索条件
|
||||
if (params.evaluateTheme) {
|
||||
requestParams.evaluateTheme = params.evaluateTheme;
|
||||
}
|
||||
if (params.status) {
|
||||
requestParams.status = params.status;
|
||||
}
|
||||
if (params.dateRange && params.dateRange.length === 2) {
|
||||
requestParams.startTime = params.dateRange[0];
|
||||
requestParams.endTime = params.dateRange[1];
|
||||
}
|
||||
|
||||
return {
|
||||
id,
|
||||
key: id,
|
||||
taskName: `供应商评价任务${index + 1}`,
|
||||
taskCode: `TASK${String(index + 1).padStart(4, '0')}`,
|
||||
taskType,
|
||||
templateName: `${TaskTypeText[taskType]}模板${Math.floor(Math.random() * 5) + 1}`,
|
||||
status,
|
||||
startTime: '2023-01-01',
|
||||
endTime: '2023-12-31',
|
||||
createBy: units[unitIndex],
|
||||
createTime: '2023-01-01 12:00:00',
|
||||
};
|
||||
});
|
||||
const response = await getTaskList(requestParams);
|
||||
|
||||
// 根据搜索条件过滤
|
||||
let filteredData = [...mockData];
|
||||
if (params.taskName) {
|
||||
filteredData = filteredData.filter(item =>
|
||||
item.taskName.includes(params.taskName || '')
|
||||
);
|
||||
}
|
||||
if (params.status) {
|
||||
filteredData = filteredData.filter(item =>
|
||||
item.status === params.status
|
||||
);
|
||||
}
|
||||
if (response.success) {
|
||||
// 处理返回的数据
|
||||
const { records, total, current: currentPage, size } = response.data;
|
||||
|
||||
// 分页
|
||||
const startIndex = (current - 1) * pageSize;
|
||||
const endIndex = startIndex + pageSize;
|
||||
const paginatedData = filteredData.slice(startIndex, endIndex);
|
||||
// 转换数据结构以适应组件
|
||||
const formattedData = records.map(item => ({
|
||||
id: item.id || '',
|
||||
key: item.id || '',
|
||||
taskName: item.evaluateTheme || '',
|
||||
taskCode: item.id || '', // 使用id作为临时的taskCode
|
||||
status: item.status || '',
|
||||
startTime: item.startTime || '',
|
||||
endTime: item.endTime || '',
|
||||
createBy: item.tenantName || '',
|
||||
// 添加必要的字段,以符合TaskRecord类型
|
||||
taskType: TaskType.REGULAR, // 默认值,实际应从API获取
|
||||
templateName: '评价模板', // 默认值,实际应从API获取
|
||||
createTime: item.startTime || '', // 使用开始时间作为创建时间
|
||||
}));
|
||||
|
||||
setTaskData(paginatedData);
|
||||
setTaskData(formattedData);
|
||||
setPagination({
|
||||
...pagination,
|
||||
current,
|
||||
pageSize,
|
||||
total: filteredData.length,
|
||||
current: currentPage,
|
||||
pageSize: size,
|
||||
total: total,
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
}, 500);
|
||||
} else {
|
||||
message.error(response.message || '获取任务列表失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取任务列表失败:', error);
|
||||
message.error('获取任务列表失败');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
@ -163,11 +159,13 @@ const SupplierTaskManage: React.FC = () => {
|
||||
maskClosable: false,
|
||||
onOk: async () => {
|
||||
try {
|
||||
// 模拟删除请求
|
||||
setTimeout(() => {
|
||||
const response = await deleteTask(record.id);
|
||||
if (response.success) {
|
||||
message.success('删除成功');
|
||||
fetchTaskList(pagination.current, pagination.pageSize, searchParams);
|
||||
}, 500);
|
||||
} else {
|
||||
message.error(response.message || '删除任务失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('删除任务失败:', error);
|
||||
message.error('删除任务失败');
|
||||
@ -190,11 +188,9 @@ const SupplierTaskManage: React.FC = () => {
|
||||
maskClosable: false,
|
||||
onOk: async () => {
|
||||
try {
|
||||
// 模拟请求
|
||||
setTimeout(() => {
|
||||
message.success(`${actionText}成功`);
|
||||
fetchTaskList(pagination.current, pagination.pageSize, searchParams);
|
||||
}, 500);
|
||||
// 这里需要添加实际的API调用
|
||||
message.success(`${actionText}成功`);
|
||||
fetchTaskList(pagination.current, pagination.pageSize, searchParams);
|
||||
} catch (error) {
|
||||
console.error(`${actionText}失败:`, error);
|
||||
message.error(`${actionText}失败`);
|
||||
@ -282,52 +278,12 @@ const SupplierTaskManage: React.FC = () => {
|
||||
},
|
||||
];
|
||||
|
||||
const onSelectChange = (newSelectedRowKeys: React.Key[]) => {
|
||||
setSelectedRowKeys(newSelectedRowKeys);
|
||||
};
|
||||
|
||||
const rowSelection = {
|
||||
selectedRowKeys,
|
||||
onChange: onSelectChange,
|
||||
};
|
||||
|
||||
const hasSelected = selectedRowKeys.length > 0;
|
||||
|
||||
// 处理添加
|
||||
const handleAdd = () => {
|
||||
// 跳转到新增页面
|
||||
history.push('/supplierTaskManage/supplierTaskManageAdd');
|
||||
};
|
||||
|
||||
// 处理批量删除
|
||||
const handleBatchDelete = () => {
|
||||
Modal.confirm({
|
||||
title: '确认批量删除',
|
||||
icon: <ExclamationCircleOutlined />,
|
||||
content: '确定要删除选中的任务吗?此操作不可恢复。',
|
||||
okText: '删除',
|
||||
okType: 'danger',
|
||||
cancelText: '取消',
|
||||
maskClosable: false,
|
||||
onOk: async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 模拟批量删除
|
||||
setTimeout(() => {
|
||||
setSelectedRowKeys([]);
|
||||
message.success('批量删除成功');
|
||||
fetchTaskList(pagination.current, pagination.pageSize, searchParams);
|
||||
setLoading(false);
|
||||
}, 1000);
|
||||
} catch (error) {
|
||||
console.error('批量删除失败:', error);
|
||||
message.error('批量删除失败');
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 处理搜索
|
||||
const handleSearch = (values: any) => {
|
||||
const { dateRange, ...rest } = values;
|
||||
@ -508,7 +464,7 @@ const SupplierTaskManage: React.FC = () => {
|
||||
layout="inline"
|
||||
className={styles.filterForm}
|
||||
>
|
||||
<Form.Item name="taskName" label="评价主题">
|
||||
<Form.Item name="evaluateTheme" label="评价主题">
|
||||
<Input placeholder="请输入评价主题" allowClear />
|
||||
</Form.Item>
|
||||
<Form.Item name="status" label="评价状态">
|
||||
@ -522,7 +478,7 @@ const SupplierTaskManage: React.FC = () => {
|
||||
<RangePicker />
|
||||
</Form.Item>
|
||||
<Form.Item className={styles.filterBtns}>
|
||||
<Button type="primary" htmlType="submit" icon={<SearchOutlined />} onClick={() => form.submit()}>
|
||||
<Button type="primary" htmlType="submit" icon={<SearchOutlined />}>
|
||||
搜索
|
||||
</Button>
|
||||
<Button
|
||||
@ -542,21 +498,11 @@ const SupplierTaskManage: React.FC = () => {
|
||||
<Button type="primary" ghost icon={<PlusOutlined />} onClick={handleAdd}>
|
||||
新增
|
||||
</Button>
|
||||
<Button
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={handleBatchDelete}
|
||||
disabled={!hasSelected}
|
||||
loading={loading}
|
||||
>
|
||||
批量删除
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.contentArea}>
|
||||
<Table
|
||||
rowSelection={rowSelection}
|
||||
columns={columns}
|
||||
dataSource={taskData}
|
||||
pagination={pagination}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Card, Steps, Button, message, Space, Row, Col } from 'antd';
|
||||
import React, { useState, useRef } from 'react';
|
||||
import { Card, Steps, Button, message, Space, Row, Col, Form } from 'antd';
|
||||
import { history } from 'umi';
|
||||
import { ArrowLeftOutlined, SaveOutlined } from '@ant-design/icons';
|
||||
import { addTask } from '@/servers/api/supplierEvaluate';
|
||||
import BasicInfoStep from './components/BasicInfoStep';
|
||||
import SupplierSelectStep from './components/SupplierSelectStep';
|
||||
import EvaluatorSelectStep from './components/EvaluatorSelectStep';
|
||||
@ -10,26 +11,31 @@ import styles from './supplierTaskManageAdd.less';
|
||||
const { Step } = Steps;
|
||||
|
||||
const SupplierTaskManageAdd: React.FC = () => {
|
||||
const [currentStep, setCurrentStep] = useState<number>(2);
|
||||
const [currentStep, setCurrentStep] = useState<number>(0);
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [formData, setFormData] = useState<any>({});
|
||||
const [formData, setFormData] = useState<Partial<SupplierEvaluate.TaskAddRequest>>({});
|
||||
|
||||
// 创建表单引用
|
||||
const basicFormRef = useRef<any>(null);
|
||||
const supplierFormRef = useRef<any>(null);
|
||||
const evaluatorFormRef = useRef<any>(null);
|
||||
|
||||
// 步骤配置
|
||||
const steps = [
|
||||
{
|
||||
title: '基本信息',
|
||||
description: '请填写基本信息',
|
||||
content: <BasicInfoStep formData={formData} onFormDataChange={handleFormDataChange} />,
|
||||
content: <BasicInfoStep formData={formData} onFormDataChange={handleFormDataChange} ref={basicFormRef} />,
|
||||
},
|
||||
{
|
||||
title: '选择供应商',
|
||||
description: '请选择参加评价的供应商',
|
||||
content: <SupplierSelectStep formData={formData} onFormDataChange={handleFormDataChange} />,
|
||||
content: <SupplierSelectStep formData={formData} onFormDataChange={handleFormDataChange} ref={supplierFormRef} />,
|
||||
},
|
||||
{
|
||||
title: '选择评价人员',
|
||||
description: '请选择供应商评价人员',
|
||||
content: <EvaluatorSelectStep formData={formData} onFormDataChange={handleFormDataChange} />,
|
||||
content: <EvaluatorSelectStep formData={formData} onFormDataChange={handleFormDataChange} ref={evaluatorFormRef} />,
|
||||
},
|
||||
{
|
||||
title: '设置评价分工',
|
||||
@ -54,54 +60,31 @@ const SupplierTaskManageAdd: React.FC = () => {
|
||||
};
|
||||
|
||||
// 下一步
|
||||
const handleNext = () => {
|
||||
// 验证当前步骤表单
|
||||
if (currentStep === 0) {
|
||||
// 验证基本信息
|
||||
if (!formData.evaluateTheme) {
|
||||
message.error('请填写评价主题');
|
||||
return;
|
||||
const handleNext = async () => {
|
||||
try {
|
||||
// 验证当前步骤表单
|
||||
if (currentStep === 0 && basicFormRef.current) {
|
||||
// 验证基本信息
|
||||
await basicFormRef.current.validateFields();
|
||||
} else if (currentStep === 1 && supplierFormRef.current) {
|
||||
// 验证供应商选择
|
||||
await supplierFormRef.current.validateFields();
|
||||
} else if (currentStep === 2 && evaluatorFormRef.current) {
|
||||
// 验证评价人员选择
|
||||
await evaluatorFormRef.current.validateFields();
|
||||
}
|
||||
if (!formData.evaluateStartTime) {
|
||||
message.error('请选择评价开始时间');
|
||||
return;
|
||||
}
|
||||
if (!formData.evaluateEndTime) {
|
||||
message.error('请选择评价结束时间');
|
||||
return;
|
||||
}
|
||||
if (!formData.templateId) {
|
||||
message.error('请选择适用评价模板');
|
||||
return;
|
||||
}
|
||||
if (!formData.categoryId && formData.templateId !== '1') { // 如果不是"不限品类"模板,则需要选择品类
|
||||
message.error('请选择品类');
|
||||
return;
|
||||
}
|
||||
} else if (currentStep === 1) {
|
||||
// 验证供应商选择
|
||||
if (!formData.selectedSuppliers || formData.selectedSuppliers.length === 0) {
|
||||
message.error('请至少选择一个供应商');
|
||||
return;
|
||||
}
|
||||
} else if (currentStep === 2) {
|
||||
// 验证评价人员选择
|
||||
if (!formData.suppliersWithEvaluators) {
|
||||
message.error('请为供应商分配评价人员');
|
||||
return;
|
||||
}
|
||||
// 检查是否每个供应商都有评价人员
|
||||
const hasNoEvaluator = formData.suppliersWithEvaluators.some((supplier: any) =>
|
||||
supplier.evaluatorCount === 0
|
||||
);
|
||||
if (hasNoEvaluator) {
|
||||
message.error('存在未分配评价人员的供应商,请为所有供应商分配评价人员');
|
||||
return;
|
||||
|
||||
// 验证通过,进入下一步
|
||||
setCurrentStep(currentStep + 1);
|
||||
} catch (errorInfo: any) {
|
||||
// 表单验证失败
|
||||
console.log('表单验证失败:', errorInfo);
|
||||
if (typeof errorInfo === 'string') {
|
||||
message.error(errorInfo);
|
||||
} else if (errorInfo && errorInfo.errorFields) {
|
||||
message.error('请完成必填项');
|
||||
}
|
||||
}
|
||||
|
||||
// 进入下一步
|
||||
setCurrentStep(currentStep + 1);
|
||||
};
|
||||
|
||||
// 上一步
|
||||
@ -113,17 +96,34 @@ const SupplierTaskManageAdd: React.FC = () => {
|
||||
const handleSubmit = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
// 模拟提交
|
||||
console.log('提交数据:', formData);
|
||||
setTimeout(() => {
|
||||
// 构建提交数据
|
||||
const submitData: SupplierEvaluate.TaskAddRequest = {
|
||||
evaluateTheme: formData.evaluateTheme || '',
|
||||
startTime: formData.startTime || '',
|
||||
endTime: formData.endTime || '',
|
||||
templateId: formData.templateId || '',
|
||||
categoryLimitation: formData.categoryLimitation || '0',
|
||||
evaluateYear: formData.evaluateYear || '',
|
||||
supplierIds: formData.supplierIds || [],
|
||||
indicatorList: formData.indicatorList || [],
|
||||
taskDeptWeightList: formData.taskDeptWeightList || [],
|
||||
weightStatus: formData.weightStatus || 0
|
||||
};
|
||||
|
||||
// 调用API提交数据
|
||||
const response = await addTask(submitData);
|
||||
|
||||
if (response.success) {
|
||||
message.success('任务创建成功');
|
||||
setLoading(false);
|
||||
// 显示最后一步(创建成功)
|
||||
setCurrentStep(steps.length - 1);
|
||||
}, 1000);
|
||||
} else {
|
||||
message.error(response.message || '提交失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error);
|
||||
message.error('提交失败');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
@ -123,3 +123,74 @@ export async function getDepartmentList() {
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取评价任务列表
|
||||
* @param params 查询参数
|
||||
* @returns Promise
|
||||
*/
|
||||
export async function getTaskList(params: SupplierEvaluate.TaskRequest) {
|
||||
return request<SupplierEvaluate.TaskResponse>('/coscoEvaluate/task/getPage', {
|
||||
method: 'POST',
|
||||
data: params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取评价任务详情
|
||||
* @param id 任务ID
|
||||
* @returns Promise
|
||||
*/
|
||||
export async function getTaskDetail(id: string) {
|
||||
return request<API.APIResponse<SupplierEvaluate.TaskRecord>>(`/coscoEvaluate/task/${id}`, {
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增评价任务
|
||||
* @param params 任务数据
|
||||
* @returns Promise
|
||||
*/
|
||||
export async function addTask(params: SupplierEvaluate.TaskAddRequest) {
|
||||
return request<API.APIResponse<any>>('/coscoEvaluate/task/addTask', {
|
||||
method: 'POST',
|
||||
data: params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新评价任务
|
||||
* @param params 任务数据
|
||||
* @returns Promise
|
||||
*/
|
||||
export async function updateTask(params: SupplierEvaluate.TaskUpdateRequest) {
|
||||
return request<API.APIResponse<any>>('/coscoEvaluate/task', {
|
||||
method: 'PUT',
|
||||
data: params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除评价任务
|
||||
* @param id 任务ID
|
||||
* @returns Promise
|
||||
*/
|
||||
export async function deleteTask(id: string) {
|
||||
return request<API.APIResponse<any>>('/coscoEvaluate/task/delete', {
|
||||
method: 'POST',
|
||||
data: { id },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除评价任务
|
||||
* @param ids 任务ID数组
|
||||
* @returns Promise
|
||||
*/
|
||||
export async function batchDeleteTask(ids: string[]) {
|
||||
return request<API.APIResponse<any>>('/coscoEvaluate/task/batchDelete', {
|
||||
method: 'POST',
|
||||
data: { ids },
|
||||
});
|
||||
}
|
||||
|
143
src/typings.d.ts
vendored
143
src/typings.d.ts
vendored
@ -108,12 +108,17 @@ declare namespace SupplierEvaluate {
|
||||
createTime: string;
|
||||
updateBy?: string;
|
||||
updateTime?: string;
|
||||
// 接口返回的字段
|
||||
evaluateTheme?: string;
|
||||
statusName?: string;
|
||||
tenantName?: string;
|
||||
};
|
||||
|
||||
type TaskSearchParams = {
|
||||
taskName?: string;
|
||||
status?: string;
|
||||
dateRange?: string[];
|
||||
evaluateTheme?: string;
|
||||
};
|
||||
|
||||
// 评价结果相关类型
|
||||
@ -365,6 +370,144 @@ declare namespace SupplierEvaluate {
|
||||
templateType: string;
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
// 评价任务请求和响应类型
|
||||
interface BasePageRequest {
|
||||
pageNo: number;
|
||||
pageSize: number;
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
interface TaskRequest {
|
||||
basePageRequest: BasePageRequest;
|
||||
isAnswer?: number;
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
interface TaskResponse {
|
||||
code: number;
|
||||
data: {
|
||||
countId: null;
|
||||
current: number;
|
||||
hitCount: boolean;
|
||||
maxLimit: null;
|
||||
optimizeCountSql: boolean;
|
||||
orders: string[];
|
||||
pages: number;
|
||||
records: {
|
||||
basePageRequest?: null;
|
||||
endTime?: string;
|
||||
evaluateTheme?: string;
|
||||
id?: string;
|
||||
startTime?: string;
|
||||
status?: string;
|
||||
statusName?: string;
|
||||
tenantName?: string;
|
||||
[property: string]: any;
|
||||
}[];
|
||||
searchCount: boolean;
|
||||
size: number;
|
||||
total: number;
|
||||
[property: string]: any;
|
||||
};
|
||||
message: string;
|
||||
success: boolean;
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
// 评价任务新增接口类型
|
||||
interface TaskIndicatorList {
|
||||
/**
|
||||
* 指标id集合
|
||||
*/
|
||||
indicatorIds?: string[];
|
||||
/**
|
||||
* 评价类型(0 按评价单 1 按指标)
|
||||
*/
|
||||
type: number;
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
userId: string;
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
interface TaskSupplierId {
|
||||
/**
|
||||
* 供应商id
|
||||
*/
|
||||
id: string;
|
||||
/**
|
||||
* 用户集合
|
||||
*/
|
||||
userIds: string[];
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
interface TaskDeptWeight {
|
||||
/**
|
||||
* 权重分配部门
|
||||
*/
|
||||
weightDept?: string;
|
||||
/**
|
||||
* 权重比例值
|
||||
*/
|
||||
weightValue?: string;
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
interface TaskAddRequest {
|
||||
/**
|
||||
* 品类限制类型(0.通用不限品类、1.限制品类)
|
||||
*/
|
||||
categoryLimitation: string;
|
||||
/**
|
||||
* 评价结束时间
|
||||
*/
|
||||
endTime: string;
|
||||
/**
|
||||
* 评价主题
|
||||
*/
|
||||
evaluateTheme: string;
|
||||
/**
|
||||
* 评价年度
|
||||
*/
|
||||
evaluateYear: string;
|
||||
/**
|
||||
* 指标分配列表
|
||||
*/
|
||||
indicatorList: TaskIndicatorList[];
|
||||
/**
|
||||
* 评价开始时间
|
||||
*/
|
||||
startTime: string;
|
||||
/**
|
||||
* 供应商列表
|
||||
*/
|
||||
supplierIds: TaskSupplierId[];
|
||||
/**
|
||||
* 部门权重列表
|
||||
*/
|
||||
taskDeptWeightList: TaskDeptWeight[];
|
||||
/**
|
||||
* 评价表模板id(cosco_evaluate_template表主键)
|
||||
*/
|
||||
templateId: string;
|
||||
/**
|
||||
* 权重启用状态(0.不启用、1.启用)
|
||||
*/
|
||||
weightStatus: number;
|
||||
[property: string]: any;
|
||||
}
|
||||
|
||||
interface TaskUpdateRequest {
|
||||
id: string;
|
||||
evaluateTheme: string;
|
||||
templateId: string;
|
||||
startTime: string;
|
||||
endTime: string;
|
||||
[property: string]: any;
|
||||
}
|
||||
}
|
||||
|
||||
declare module '*.css';
|
||||
|
@ -38,31 +38,28 @@ export const validateFileSize = (file: File, maxSize: number,type: string[]) =>
|
||||
export const generateUUID = (length: number) => {
|
||||
// 使用更复杂的字符集
|
||||
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
const charactersLength = characters.length;
|
||||
|
||||
// 使用加密安全的随机数生成(如果可用)
|
||||
let array;
|
||||
// 使用加密安全的随机数生成
|
||||
let result = '';
|
||||
|
||||
// 添加时间戳前缀,确保唯一性
|
||||
const timestamp = Date.now().toString(36);
|
||||
result += timestamp.substring(timestamp.length - Math.min(6, length / 4));
|
||||
|
||||
// 获取随机字符
|
||||
const randomPart = [];
|
||||
if (typeof window !== 'undefined' && window.crypto && window.crypto.getRandomValues) {
|
||||
array = new Uint8Array(length);
|
||||
const array = new Uint8Array(length);
|
||||
window.crypto.getRandomValues(array);
|
||||
for (let i = 0; i < length - result.length; i++) {
|
||||
randomPart.push(characters.charAt(array[i] % charactersLength));
|
||||
}
|
||||
} else {
|
||||
array = new Uint8Array(length);
|
||||
for (let i = 0; i < length; i++) {
|
||||
array[i] = Math.floor(Math.random() * 256);
|
||||
for (let i = 0; i < length - result.length; i++) {
|
||||
randomPart.push(characters.charAt(Math.floor(Math.random() * charactersLength)));
|
||||
}
|
||||
}
|
||||
|
||||
// 生成UUID
|
||||
const timestamp = Date.now().toString(36);
|
||||
let result = '';
|
||||
|
||||
// 添加时间戳前缀(最多6个字符)
|
||||
result += timestamp.substring(timestamp.length - Math.min(6, length / 3));
|
||||
|
||||
// 添加随机字符
|
||||
for (let i = 0; i < length - result.length; i++) {
|
||||
const randomIndex = array[i % array.length] % characters.length;
|
||||
result += characters.charAt(randomIndex);
|
||||
}
|
||||
|
||||
return result;
|
||||
return result + randomPart.join('');
|
||||
};
|
||||
|
Reference in New Issue
Block a user