466 lines
15 KiB
TypeScript
466 lines
15 KiB
TypeScript
![]() |
import React, { useState, useEffect, forwardRef, useImperativeHandle, useRef } from 'react';
|
|||
|
import { Card, Button, Space, Form } from 'antd';
|
|||
|
import styles from '../supplierAnnualTaskManageAdd.less';
|
|||
|
import {
|
|||
|
SupplierTable,
|
|||
|
BatchEvaluatorModal,
|
|||
|
SupplierEvaluatorModal,
|
|||
|
WeightSettingModal,
|
|||
|
} from './EvaluatorComponents';
|
|||
|
import { ModalMode } from '@/servers/types/evaluator';
|
|||
|
import type { PersonnelItem, SupplierItem } from '@/servers/types/evaluator';
|
|||
|
import type { DeptWeightItem } from '@/servers/dao/supplierEvaluateTask';
|
|||
|
import type { Dispatch } from 'umi';
|
|||
|
import { connect } from 'umi';
|
|||
|
import type { SupplierTaskModelState } from '@/models/supplierAnnualTaskManage';
|
|||
|
|
|||
|
/**
|
|||
|
* 组件接收的Props接口
|
|||
|
* supplierAnnualTaskManage: Dva model 状态
|
|||
|
* dispatch: Dva 的 dispatch 函数,用于触发 action
|
|||
|
* innerRef: 内部 ref 用于暴露方法给父组件
|
|||
|
*/
|
|||
|
interface EvaluatorSelectStepProps {
|
|||
|
supplierAnnualTaskManage: SupplierTaskModelState;
|
|||
|
dispatch: Dispatch;
|
|||
|
innerRef?: any; // 使用 innerRef 作为属性名
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 评价人员选择步骤组件
|
|||
|
* 主要功能:选择供应商评价人员,设置评分单位权重
|
|||
|
* 数据流转:
|
|||
|
* 1. 从上一步获取供应商数据(通过 model.taskFormData)
|
|||
|
* 2. 组织评价人员列表并维护到本地状态
|
|||
|
* 3. 通过 dispatch 将更新的数据同步回 model
|
|||
|
*/
|
|||
|
const EvaluatorSelectStepComponent = (props: EvaluatorSelectStepProps) => {
|
|||
|
const { supplierAnnualTaskManage, dispatch, innerRef } = props;
|
|||
|
|
|||
|
// 从 model 获取表单数据,避免通过 props 层层传递
|
|||
|
const { taskFormData, mode } = supplierAnnualTaskManage;
|
|||
|
|
|||
|
// 选中的供应商行的key列表,用于批量操作
|
|||
|
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
|||
|
|
|||
|
// 供应商列表数据,包含评价人员信息(从Dva model获取初始数据,维护在本地状态)
|
|||
|
const [suppliers, setSuppliers] = useState<SupplierItem[]>([]);
|
|||
|
|
|||
|
// 批量选择模态框的可见性控制
|
|||
|
const [batchSelectModalVisible, setBatchSelectModalVisible] = useState(false);
|
|||
|
|
|||
|
// 权重设置模态框的可见性控制
|
|||
|
const [weightSettingModalVisible, setWeightSettingModalVisible] = useState(false);
|
|||
|
|
|||
|
// 评价人员选择/查看模态框的可见性控制
|
|||
|
const [evaluatorModalVisible, setEvaluatorModalVisible] = useState(false);
|
|||
|
|
|||
|
// 当前操作的供应商对象,用于评价人员的选择和查看
|
|||
|
const [currentSupplier, setCurrentSupplier] = useState<SupplierItem | null>(null);
|
|||
|
|
|||
|
// 模态框模式:SELECT(选择模式) 或 VIEW(查看模式)
|
|||
|
const [modalMode, setModalMode] = useState<ModalMode>(ModalMode.SELECT);
|
|||
|
|
|||
|
// 权重单位列表,根据评价人员部门动态生成
|
|||
|
const [taskDeptWeightList, setTaskDeptWeightList] = useState<DeptWeightItem[]>([]);
|
|||
|
|
|||
|
// 表单实例,用于权重设置
|
|||
|
const [form] = Form.useForm();
|
|||
|
const [filterUserIds, setFilterUserIds] = useState<string[]>([]);
|
|||
|
useEffect(() => {
|
|||
|
if (mode === 'division' && taskFormData.userList && taskFormData.userList.length > 0) {
|
|||
|
const filterUserIdsaa = taskFormData.userList
|
|||
|
.map((user: PersonnelItem) => (user.isSelected === true ? user.id : null))
|
|||
|
.filter((id: string | null) => id !== null);
|
|||
|
setFilterUserIds(filterUserIdsaa);
|
|||
|
}
|
|||
|
}, [mode, taskFormData.userList]);
|
|||
|
/**
|
|||
|
* 暴露表单方法给父组件
|
|||
|
* 包含验证、获取和设置表单数据的方法
|
|||
|
*/
|
|||
|
useImperativeHandle(innerRef, () => ({
|
|||
|
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();
|
|||
|
}
|
|||
|
}));
|
|||
|
|
|||
|
/**
|
|||
|
* 从上一步获取供应商数据
|
|||
|
* 使用 useEffect 监听 taskFormData 变化,确保任何来自 model 的数据变更都能被捕获
|
|||
|
*/
|
|||
|
useEffect(() => {
|
|||
|
// 从Dva model中获取上一步选择的供应商数据
|
|||
|
if (taskFormData.selectedSuppliers && taskFormData.selectedSuppliers.length > 0) {
|
|||
|
// 转换上一步的供应商数据,添加评价人员数量字段
|
|||
|
const suppliersWithEvaluators = taskFormData.selectedSuppliers.map(
|
|||
|
(supplier) => {
|
|||
|
// 确保evaluators字段存在且为数组
|
|||
|
const evaluators = supplier.evaluators || [];
|
|||
|
return {
|
|||
|
...supplier,
|
|||
|
userDept: supplier.userDept || '采购部', // 设置默认部门
|
|||
|
evaluatorCount: evaluators.length,
|
|||
|
evaluators: evaluators,
|
|||
|
};
|
|||
|
},
|
|||
|
);
|
|||
|
|
|||
|
// 更新本地供应商状态
|
|||
|
setSuppliers(suppliersWithEvaluators);
|
|||
|
} else {
|
|||
|
// 没有选择供应商,清空供应商列表
|
|||
|
setSuppliers([]);
|
|||
|
}
|
|||
|
|
|||
|
}, [taskFormData]); // 依赖于 taskFormData,当 model 数据变化时重新计算
|
|||
|
|
|||
|
/**
|
|||
|
* 当选择的人员变化时,更新权重单位列表
|
|||
|
* 根据所有评价人员的部门信息,动态生成权重单位列表
|
|||
|
*/
|
|||
|
useEffect(() => {
|
|||
|
// 从所有供应商的评价人员中提取部门信息
|
|||
|
const allDepartments: PersonnelItem[] = [];
|
|||
|
|
|||
|
suppliers.forEach((supplier) => {
|
|||
|
if (supplier.evaluators && supplier.evaluators.length > 0) {
|
|||
|
supplier.evaluators.forEach((evaluator) => {
|
|||
|
if (evaluator.userDept) {
|
|||
|
allDepartments.push(evaluator);
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
// 去重部门列表 - 根据部门ID去重
|
|||
|
const departmentMap = new Map<string, PersonnelItem>();
|
|||
|
allDepartments.forEach((dept) => {
|
|||
|
if (dept.userDeptId && !departmentMap.has(dept.userDeptId)) {
|
|||
|
departmentMap.set(dept.userDeptId, dept);
|
|||
|
}
|
|||
|
});
|
|||
|
const uniqueDepartments = Array.from(departmentMap.values());
|
|||
|
|
|||
|
// 如果有部门数据,生成权重单位列表
|
|||
|
if (uniqueDepartments.length > 0) {
|
|||
|
const newTaskDeptWeightList: DeptWeightItem[] = uniqueDepartments.map((dept) => ({
|
|||
|
weightDept: dept.userDeptId || '',
|
|||
|
weightValue: '0', // 默认权重为0
|
|||
|
weightDeptName: dept.userDept || '',
|
|||
|
}));
|
|||
|
|
|||
|
// 更新权重单位列表,保留原有权重值
|
|||
|
setTaskDeptWeightList((prevList) => {
|
|||
|
const prevValuesMap = new Map(prevList.map((item) => [item.weightDept, item.weightValue]));
|
|||
|
|
|||
|
return newTaskDeptWeightList.map((item) => ({
|
|||
|
...item,
|
|||
|
weightValue: prevValuesMap.get(item.weightDept) || '0', // 如果有原来的权重值则保留
|
|||
|
}));
|
|||
|
});
|
|||
|
}
|
|||
|
}, [suppliers]); // 依赖于suppliers,当选择的供应商或其评价人员变化时重新计算
|
|||
|
|
|||
|
/**
|
|||
|
* 更新表单数据
|
|||
|
* 将本地状态同步到 Dva model
|
|||
|
* @param updatedData 要更新的部分数据
|
|||
|
*/
|
|||
|
const updateFormData = (updatedData: any) => {
|
|||
|
// 结构 更新数据中的suppliersWithEvaluators
|
|||
|
const { suppliersWithEvaluators }: { suppliersWithEvaluators: SupplierItem[] } = updatedData;
|
|||
|
// 构建供应商ID列表
|
|||
|
const supplierIds = suppliersWithEvaluators.map((supplier) => ({
|
|||
|
id: supplier.id,
|
|||
|
userIds: supplier.evaluators?.map((e: PersonnelItem) => e.id) || [],
|
|||
|
}));
|
|||
|
|
|||
|
// 构建userList 并去重,确保保留 isSelected 为 true 的对象
|
|||
|
const userMap = new Map();
|
|||
|
suppliersWithEvaluators
|
|||
|
.flatMap((s) => s.evaluators || [])
|
|||
|
.forEach((user) => {
|
|||
|
// 如果已存在此ID的用户且当前用户isSelected为true,或者Map中不存在此用户,则更新/添加
|
|||
|
if (!userMap.has(user.id) || user.isSelected) {
|
|||
|
userMap.set(user.id, user);
|
|||
|
}
|
|||
|
});
|
|||
|
|
|||
|
|
|||
|
// 通过dispatch更新model中的数据
|
|||
|
// 这是组件与Dva model交互的关键
|
|||
|
dispatch({
|
|||
|
type: 'supplierAnnualTaskManage/updateFormData', // action类型
|
|||
|
payload: {
|
|||
|
// action数据
|
|||
|
...updatedData,
|
|||
|
supplierIds,
|
|||
|
selectedSuppliers: suppliersWithEvaluators,
|
|||
|
},
|
|||
|
});
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 处理批量选择按钮点击事件
|
|||
|
* 打开批量选择评价人员模态框
|
|||
|
*/
|
|||
|
const handleBatchSelect = () => {
|
|||
|
if (selectedRowKeys.length === 0) return;
|
|||
|
setBatchSelectModalVisible(true);
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 处理设置评分单位权重按钮点击事件
|
|||
|
* 打开权重设置模态框
|
|||
|
*/
|
|||
|
const handleWeightSetting = () => {
|
|||
|
// 将权重数据转换为表单初始值
|
|||
|
form.setFieldsValue({
|
|||
|
taskDeptWeightList: taskDeptWeightList.reduce((acc, item) => {
|
|||
|
acc[item.weightDept] = parseInt(item.weightValue, 10) || 0;
|
|||
|
return acc;
|
|||
|
}, {} as Record<string, number>),
|
|||
|
});
|
|||
|
|
|||
|
setWeightSettingModalVisible(true);
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 保存权重设置
|
|||
|
* 更新本地权重数据并同步到 Dva model
|
|||
|
*/
|
|||
|
const handleSaveWeights = () => {
|
|||
|
form.validateFields().then((values) => {
|
|||
|
// 将表单值转换回权重列表格式
|
|||
|
const updatedTaskDeptWeightList = taskDeptWeightList.map((item) => ({
|
|||
|
...item,
|
|||
|
weightValue: values.taskDeptWeightList[item.weightDept].toString(),
|
|||
|
}));
|
|||
|
|
|||
|
// 更新本地状态
|
|||
|
setTaskDeptWeightList(updatedTaskDeptWeightList);
|
|||
|
|
|||
|
// 同步到Dva model
|
|||
|
updateFormData({
|
|||
|
suppliersWithEvaluators: suppliers,
|
|||
|
taskDeptWeightList: updatedTaskDeptWeightList,
|
|||
|
});
|
|||
|
|
|||
|
setWeightSettingModalVisible(false);
|
|||
|
});
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 处理选择评价人员按钮点击事件
|
|||
|
* 打开评价人员选择模态框
|
|||
|
* @param supplier 当前选中的供应商
|
|||
|
*/
|
|||
|
const handleSelectEvaluators = (supplier: SupplierItem) => {
|
|||
|
setCurrentSupplier(supplier);
|
|||
|
setModalMode(ModalMode.SELECT);
|
|||
|
setEvaluatorModalVisible(true);
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 处理查看评价人员按钮点击事件
|
|||
|
* 打开评价人员查看模态框
|
|||
|
* @param supplier 当前选中的供应商
|
|||
|
*/
|
|||
|
const handleViewEvaluators = (supplier: SupplierItem) => {
|
|||
|
// 查找完整的供应商数据(包括evaluators)
|
|||
|
const fullSupplier = suppliers.find((s) => s.id === supplier.id);
|
|||
|
if (fullSupplier) {
|
|||
|
setCurrentSupplier(fullSupplier);
|
|||
|
} else {
|
|||
|
setCurrentSupplier(supplier);
|
|||
|
}
|
|||
|
|
|||
|
setModalMode(ModalMode.VIEW);
|
|||
|
setEvaluatorModalVisible(true);
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 处理评价人员选择确认
|
|||
|
* 更新供应商的评价人员列表并同步到 Dva model
|
|||
|
* @param selectedEvaluators 选中的评价人员列表
|
|||
|
*/
|
|||
|
const handleEvaluatorSelect = (selectedEvaluators: PersonnelItem[]) => {
|
|||
|
if (!currentSupplier) return;
|
|||
|
// 更新当前供应商的评价人员列表
|
|||
|
const updatedSuppliers = suppliers.map((supplier) => {
|
|||
|
if (supplier.id === currentSupplier.id) {
|
|||
|
const updated = {
|
|||
|
...supplier,
|
|||
|
evaluators: selectedEvaluators,
|
|||
|
evaluatorCount: selectedEvaluators.length,
|
|||
|
};
|
|||
|
return updated;
|
|||
|
}
|
|||
|
return supplier;
|
|||
|
});
|
|||
|
|
|||
|
// 更新本地状态
|
|||
|
setSuppliers(updatedSuppliers);
|
|||
|
|
|||
|
// 同步到Dva model
|
|||
|
updateFormData({ suppliersWithEvaluators: updatedSuppliers });
|
|||
|
|
|||
|
// 关闭模态框
|
|||
|
setEvaluatorModalVisible(false);
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 处理批量选择评价人员确认
|
|||
|
* 批量更新供应商的评价人员列表并同步到 Dva model
|
|||
|
* @param selectedEvaluators 选中的评价人员列表
|
|||
|
*/
|
|||
|
const handleBatchEvaluatorSelect = (selectedEvaluators: PersonnelItem[]) => {
|
|||
|
if (selectedRowKeys.length === 0) return;
|
|||
|
|
|||
|
// 更新所有选中供应商的评价人员列表
|
|||
|
const updatedSuppliers = suppliers.map((supplier) => {
|
|||
|
if (selectedRowKeys.includes(supplier.id)) {
|
|||
|
return {
|
|||
|
...supplier,
|
|||
|
evaluators: selectedEvaluators,
|
|||
|
evaluatorCount: selectedEvaluators.length,
|
|||
|
};
|
|||
|
}
|
|||
|
return supplier;
|
|||
|
});
|
|||
|
|
|||
|
// 更新本地状态
|
|||
|
setSuppliers(updatedSuppliers);
|
|||
|
|
|||
|
// 同步到Dva model
|
|||
|
updateFormData({ suppliersWithEvaluators: updatedSuppliers });
|
|||
|
|
|||
|
// 关闭模态框
|
|||
|
setBatchSelectModalVisible(false);
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 处理删除供应商
|
|||
|
* 从供应商列表中删除指定供应商并同步到 Dva model
|
|||
|
* @param key 供应商ID
|
|||
|
*/
|
|||
|
const handleDeleteSupplier = (key: string) => {
|
|||
|
// 过滤掉要删除的供应商
|
|||
|
const updatedSuppliers = suppliers.filter((item) => item.id !== key);
|
|||
|
|
|||
|
// 更新本地状态
|
|||
|
setSuppliers(updatedSuppliers);
|
|||
|
|
|||
|
// 同步到Dva model
|
|||
|
updateFormData({ suppliersWithEvaluators: updatedSuppliers });
|
|||
|
|
|||
|
// 更新选中行,移除已删除的供应商
|
|||
|
setSelectedRowKeys((prevKeys) => prevKeys.filter((k) => k !== key));
|
|||
|
};
|
|||
|
|
|||
|
// 渲染组件
|
|||
|
return (
|
|||
|
<div className={styles.evaluatorSelectStep}>
|
|||
|
<Card title="选择评价人员" bordered={false} className="inner-card">
|
|||
|
{/* 工具栏区域 */}
|
|||
|
{mode !== 'division' && (
|
|||
|
<div className={styles.toolbar}>
|
|||
|
<Space>
|
|||
|
<Button
|
|||
|
type="primary"
|
|||
|
onClick={handleBatchSelect}
|
|||
|
disabled={selectedRowKeys.length === 0}
|
|||
|
>
|
|||
|
批量选择评价人员
|
|||
|
</Button>
|
|||
|
<Button onClick={handleWeightSetting} disabled={taskDeptWeightList.length === 0}>
|
|||
|
设置评分单位权重
|
|||
|
</Button>
|
|||
|
</Space>
|
|||
|
</div>
|
|||
|
)}
|
|||
|
|
|||
|
{/* 供应商表格区域 */}
|
|||
|
{suppliers.length === 0 ? (
|
|||
|
<div style={{ textAlign: 'center', padding: '30px 0', color: '#999' }}>
|
|||
|
请先在上一步选择供应商
|
|||
|
</div>
|
|||
|
) : (
|
|||
|
<SupplierTable
|
|||
|
suppliers={suppliers}
|
|||
|
mode={mode}
|
|||
|
selectedRowKeys={selectedRowKeys}
|
|||
|
onSelectChange={setSelectedRowKeys}
|
|||
|
onViewEvaluators={handleViewEvaluators}
|
|||
|
onSelectEvaluators={handleSelectEvaluators}
|
|||
|
onDeleteSupplier={handleDeleteSupplier}
|
|||
|
/>
|
|||
|
)}
|
|||
|
|
|||
|
{/* 批量选择评价人员弹窗 */}
|
|||
|
<BatchEvaluatorModal
|
|||
|
visible={batchSelectModalVisible}
|
|||
|
onCancel={() => setBatchSelectModalVisible(false)}
|
|||
|
filter={mode === 'division'}
|
|||
|
filterUserIds={filterUserIds}
|
|||
|
onSelect={handleBatchEvaluatorSelect}
|
|||
|
/>
|
|||
|
|
|||
|
{/* 单个供应商评价人员弹窗 */}
|
|||
|
<SupplierEvaluatorModal
|
|||
|
visible={evaluatorModalVisible}
|
|||
|
onCancel={() => setEvaluatorModalVisible(false)}
|
|||
|
filter={mode === 'division'}
|
|||
|
filterUserIds={filterUserIds}
|
|||
|
onSelect={handleEvaluatorSelect}
|
|||
|
currentSupplier={currentSupplier}
|
|||
|
mode={modalMode}
|
|||
|
/>
|
|||
|
|
|||
|
{/* 权重设置弹窗 */}
|
|||
|
<WeightSettingModal
|
|||
|
visible={weightSettingModalVisible}
|
|||
|
onCancel={() => setWeightSettingModalVisible(false)}
|
|||
|
onOk={handleSaveWeights}
|
|||
|
taskDeptWeightList={taskDeptWeightList}
|
|||
|
form={form}
|
|||
|
/>
|
|||
|
</Card>
|
|||
|
</div>
|
|||
|
);
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 连接 Dva model
|
|||
|
* 将 model 中的状态映射到组件 props
|
|||
|
*/
|
|||
|
const ConnectedComponent = connect(
|
|||
|
({ supplierAnnualTaskManage }: { supplierAnnualTaskManage: SupplierTaskModelState }) => ({
|
|||
|
supplierAnnualTaskManage,
|
|||
|
}),
|
|||
|
)(EvaluatorSelectStepComponent);
|
|||
|
|
|||
|
/**
|
|||
|
* 外层转发 ref 到 innerRef
|
|||
|
*/
|
|||
|
const EvaluatorSelectStep = forwardRef((props: any, ref) => (
|
|||
|
<ConnectedComponent {...props} innerRef={ref} />
|
|||
|
));
|
|||
|
|
|||
|
export default EvaluatorSelectStep;
|