From bf19b5340291b21247dbe7e03367393ddf0e530c Mon Sep 17 00:00:00 2001 From: linxd <544554903@qq.com> Date: Fri, 27 Jun 2025 19:55:19 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=B9=B4=E5=AE=A1=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dicts/supplierAnnualTaskManageDict.ts | 39 ++ src/models/supplierAnnualTaskManage.ts | 381 ++++++++++++ .../supplierAnnualResult.less | 55 -- .../supplierAnnualResult.tsx | 1 - .../supplierAnnualReview.less | 1 - .../components/BasicInfoStep.tsx | 231 +++++++ .../components/Detail/BasicInfo.tsx | 45 ++ .../components/Detail/EvaluatorInfo.tsx | 88 +++ .../components/Detail/EvaluatorModal.tsx | 42 ++ .../components/Detail/IndicatorModal.tsx | 99 +++ .../components/Detail/SupplierInfo.tsx | 95 +++ .../components/Detail/WeightInfo.tsx | 48 ++ .../components/Detail/index.ts | 15 + .../BatchEvaluatorModal.tsx | 43 ++ .../SupplierEvaluatorModal.tsx | 114 ++++ .../EvaluatorComponents/SupplierTable.tsx | 116 ++++ .../WeightSettingModal.tsx | 50 ++ .../components/EvaluatorComponents/index.ts | 15 + .../components/EvaluatorSelectStep.tsx | 465 ++++++++++++++ .../components/SupplierSelectStep.tsx | 88 +++ .../supplierAnnualTaskManage.less | 44 ++ .../supplierAnnualTaskManage.tsx | 278 ++++++++- .../supplierAnnualTaskManageAdd.less | 85 +++ .../supplierAnnualTaskManageAdd.tsx | 343 +++++++++++ .../supplierAnnualTaskManageDetail.less | 100 +++ .../supplierAnnualTaskManageDetail.tsx | 153 +++++ .../supplierTaskManageDetail.tsx | 3 +- src/servers/api/supplierAnnual.ts | 49 ++ .../supplierAnnualTaskManage.d.ts | 172 ++++++ src/servers/dao/supplierAnnualTaskManag.ts | 567 ++++++++++++++++++ src/utils/componentStyle.less | 3 + 31 files changed, 3764 insertions(+), 64 deletions(-) create mode 100644 src/dicts/supplierAnnualTaskManageDict.ts create mode 100644 src/models/supplierAnnualTaskManage.ts create mode 100644 src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/BasicInfoStep.tsx create mode 100644 src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/BasicInfo.tsx create mode 100644 src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/EvaluatorInfo.tsx create mode 100644 src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/EvaluatorModal.tsx create mode 100644 src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/IndicatorModal.tsx create mode 100644 src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/SupplierInfo.tsx create mode 100644 src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/WeightInfo.tsx create mode 100644 src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/index.ts create mode 100644 src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/BatchEvaluatorModal.tsx create mode 100644 src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/SupplierEvaluatorModal.tsx create mode 100644 src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/SupplierTable.tsx create mode 100644 src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/WeightSettingModal.tsx create mode 100644 src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/index.ts create mode 100644 src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorSelectStep.tsx create mode 100644 src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/SupplierSelectStep.tsx create mode 100644 src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManage.less create mode 100644 src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManageAdd.less create mode 100644 src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManageDetail.less create mode 100644 src/servers/dao/supplierAnnualManage/supplierAnnualTaskManage.d.ts create mode 100644 src/servers/dao/supplierAnnualTaskManag.ts diff --git a/src/dicts/supplierAnnualTaskManageDict.ts b/src/dicts/supplierAnnualTaskManageDict.ts new file mode 100644 index 0000000..45b8227 --- /dev/null +++ b/src/dicts/supplierAnnualTaskManageDict.ts @@ -0,0 +1,39 @@ +// 供应商年度任务管理字典 + +/** + * 年度任务状态 + */ +export enum AnnualTaskStatus { + PENDING = '0', // 待执行 + RUNNING = '1', // 执行中 + COMPLETED = '2', // 已完成 + TERMINATED = '3' // 已终止 +} + +/** + * 年度任务状态文本 + */ +export const AnnualTaskStatusText: Record = { + [AnnualTaskStatus.PENDING]: '待执行', + [AnnualTaskStatus.RUNNING]: '执行中', + [AnnualTaskStatus.COMPLETED]: '已完成', + [AnnualTaskStatus.TERMINATED]: '已终止' +}; + +/** + * 年度任务状态颜色 + */ +export const AnnualTaskStatusColor: Record = { + [AnnualTaskStatus.PENDING]: 'default', + [AnnualTaskStatus.RUNNING]: 'processing', + [AnnualTaskStatus.COMPLETED]: 'success', + [AnnualTaskStatus.TERMINATED]: 'error' +}; + +/** + * 年度任务状态选项(用于下拉框) + */ +export const AnnualTaskStatusOptions = Object.keys(AnnualTaskStatusText).map(key => ({ + label: AnnualTaskStatusText[key], + value: key +})); diff --git a/src/models/supplierAnnualTaskManage.ts b/src/models/supplierAnnualTaskManage.ts new file mode 100644 index 0000000..14f9d00 --- /dev/null +++ b/src/models/supplierAnnualTaskManage.ts @@ -0,0 +1,381 @@ +import { message } from 'antd'; +import { addTask, getTaskDetail } from '@/servers/api/supplierEvaluate'; +import type { + TaskDetailData, + TaskAddRequest, + TaskDetailResponse +} from '@/servers/dao/supplierAnnualTaskManag'; +import type { TaskNotifyLowerUnits } from '@/dicts/supplierTaskDict'; +import type { PersonnelItem, IndicatorItem, SupplierItem } from '@/servers/dao/supplierAnnualTaskManag'; + +// Define the types for dva effects and reducers +type Effect = (action: { payload: any }, effects: { call: any; put: any; select: any }) => Generator; +type Reducer = (state: S, action: { payload: any }) => S; + +/** + * 供应商任务管理模型状态接口 + * 定义了整个模型的状态结构 + */ +export interface SupplierTaskModelState { + currentStep: number; // 当前步骤索引,从0开始 + loading: boolean; // 提交加载状态 + detailLoading: boolean; // 详情数据加载状态 + taskFormData: Partial; // 任务表单数据 + taskDetail: TaskDetailData | null; // 任务详情数据 + mode: 'add' | 'edit' | 'division'; // 模式 +} + +/** + * 供应商任务管理模型类型接口 + * 定义了模型的命名空间、状态、副作用和reducers + */ +export interface SupplierTaskModelType { + namespace: 'supplierAnnualTaskManage'; // 模型命名空间 + state: SupplierTaskModelState; // 模型状态 + effects: { + fetchTaskDetail: Effect; // 获取任务详情 + submitTaskData: Effect; // 提交任务数据 + updateFormData: Effect; // 更新表单数据 + nextStep: Effect; // 下一步 + prevStep: Effect; // 上一步 + resetState: Effect; // 重置状态 + setCurrentStep: Effect; // 设置当前步骤 + setMode: Effect; // 设置模式 + deleteUser: Effect; // 删除用户 + }; + reducers: { + saveCurrentStep: Reducer; // 保存当前步骤 + saveLoading: Reducer; // 保存加载状态 + saveDetailLoading: Reducer; // 保存详情加载状态 + saveTaskFormData: Reducer; // 保存任务表单数据 + saveTaskDetail: Reducer; // 保存任务详情数据 + saveMode: Reducer; // 保存模式 + }; +} + +/** + * 供应商任务管理模型 + * 用于管理供应商评价任务的添加、编辑流程和数据 + */ +const supplierAnnualTaskManage: SupplierTaskModelType = { + namespace: 'supplierAnnualTaskManage', + + // 初始状态 + state: { + currentStep: 0, // 当前步骤,默认为第一步 + loading: false, // 提交加载状态 + detailLoading: false, // 详情数据加载状态 + taskFormData: {}, // 任务表单数据 + taskDetail: null, // 任务详情数据 + mode: 'add', // 模式 + }, + + // 副作用处理函数 + effects: { + /** + * 获取任务详情 + * @param payload.taskId 任务ID + */ + *fetchTaskDetail({ payload }: { payload: { taskId: string } }, { call, put }: { call: any; put: any }) { + const { taskId } = payload; + yield put({ type: 'saveDetailLoading', payload: true }); + + try { + // 调用API获取任务详情 + const response = (yield call(getTaskDetail, taskId)) as TaskDetailResponse; + + if (response.success) { + const detail: TaskDetailData = response.data; + + // 转换任务详情数据为表单数据结构 + const taskFormData: Partial = { + id: detail.id || '', + evaluateTheme: detail.evaluateTheme || '', + startTime: detail.startTime || '', + endTime: detail.endTime || '', + templateId: detail.templateId || '', + categoryLimitation: detail.categoryLimitation || '0', + evaluateYear: detail.evaluateYear || '', + categoryId: detail.categoryId || undefined, + + + // 供应商数据转换,添加id和name字段用于UI展示 + selectedSuppliers: detail.blackSupplierVos.map((item) => ({ + ...item, + id: item.supplierId, + name: item.supplierName, + evaluators: item.userList, + })), + + // 指标列表 + indicatorList: detail.indicatorList || [], + + // 供应商IDs与用户IDs + supplierIds: detail.supplierIds || [], + + //设置评价分工table回显 需处理 + userList: detail.userList.map((user) => { + const matchedIndicator = detail.indicatorList?.find( + (indicator) => indicator.userId === user.userId + ); + return { + ...user, + name: user.userName, + id: user.userId, + indicatorIds: matchedIndicator?.indicatorIds || [], + }; + }), + + + }; + + // 保存数据到状态 + yield put({ type: 'saveTaskFormData', payload: taskFormData }); + yield put({ type: 'saveTaskDetail', payload: detail }); + } else { + message.error(response.message || '获取任务详情失败'); + } + } catch (error) { + console.error('获取任务详情失败:', error); + message.error('获取任务详情失败'); + } finally { + yield put({ type: 'saveDetailLoading', payload: false }); + } + }, + + /** + * 提交任务数据 + * @param payload.taskStatus 任务状态 + * @param payload.isEditMode 是否为编辑模式 + * @param payload.taskId 任务ID(编辑模式必须) + * @param payload.onSuccess 成功回调函数 + */ + *submitTaskData( + { payload }: { payload: { taskStatus: TaskNotifyLowerUnits | null; isEditMode: boolean; taskId: string; onSuccess?: () => void } }, + { call, put, select }: { call: any; put: any; select: any } + ) { + const { taskStatus, isEditMode, taskId, onSuccess } = payload; + yield put({ type: 'saveLoading', payload: true }); + + try { + // 获取表单数据 + const { taskFormData } = (yield select((state: any) => state.supplierAnnualTaskManage)) as { taskFormData: TaskAddRequest }; + + // 构建提交数据 + let submitData: TaskAddRequest = { + evaluateTheme: taskFormData.evaluateTheme || '', + startTime: taskFormData.startTime || '', + endTime: taskFormData.endTime || '', + templateId: taskFormData.templateId || '', + categoryLimitation: taskFormData.categoryLimitation || '0', + evaluateYear: taskFormData.evaluateYear || '', + categoryId: taskFormData.categoryId || '', + taskStatus: taskStatus || null, + + // 供应商IDs与评价人员IDs + supplierIds: + taskFormData.selectedSuppliers?.map((supplier: any) => { + // 从供应商的evaluators中提取用户ID + const userIds = supplier.evaluators?.map((evaluator: any) => evaluator.id) || []; + return { + id: supplier.id, + userIds, + }; + }) || [], + + // 指标列表 + indicatorList: + taskFormData.indicatorList?.map((item: any) => ({ + userId: item.userId, + // 评价类型:如果用户关联了指标则为1(按指标),否则为0(按评价单) + type: item.indicatorIds && item.indicatorIds.length > 0 ? 1 : 0, + indicatorIds: item.indicatorIds || [], + })) || [], + + }; + + + if (isEditMode) { + // 编辑模式,添加ID字段 + submitData = { + ...submitData, + id: taskId, + }; + } + const response = (yield call(addTask, submitData)) as API.Response; + if (response.success) { + message.success(isEditMode ? '任务更新成功' : '任务创建成功'); + if (onSuccess) { + onSuccess(); + } + } else { + message.error(response.message || '提交失败'); + } + } catch (error) { + console.error('提交失败:', error); + message.error('提交失败'); + } finally { + yield put({ type: 'saveLoading', payload: false }); + } + }, + + /** + * 更新表单数据 + * 处理不同步骤表单数据的变化,保持数据一致性 + * @param payload 表单数据 + */ + *updateFormData({ payload }: { payload: any }, { put, select }: { put: any; select: any }) { + // 获取当前表单数据 + const { taskFormData } = (yield select((state: any) => state.supplierAnnualTaskManage)) as { taskFormData: TaskAddRequest }; + // 合并新的表单数据 + const updatedFormData = { ...taskFormData, ...payload }; + // 处理供应商选择更新 + if (payload.selectedSuppliers) { + // 确保每个供应商都有evaluators字段 + const suppliersWithEvaluators = payload.selectedSuppliers.map((supplier: any) => ({ + ...supplier, + evaluators: supplier.evaluators || [], // 确保evaluators字段存在 + })); + updatedFormData.selectedSuppliers = suppliersWithEvaluators; + } + // 处理评价人员更新 - 更新后要保留原有对象中的indicatorIds + + console.log(updatedFormData) + + // 保存更新后的表单数据 + yield put({ type: 'saveTaskFormData', payload: updatedFormData }); + }, + /** + * 删除用户 + * @param payload.userId 用户ID + */ + *deleteUser({ payload }: { payload: { userIds: string[] } }, { put, select }: { put: any; select: any }) { + const { userIds } = payload; + if (!userIds || userIds.length === 0) return; + + const userIdSet = new Set(userIds.filter(Boolean).map(String)); + + const { taskFormData } = (yield select((state: any) => state.supplierAnnualTaskManage)) as { + taskFormData: TaskAddRequest; + }; + + const updatedFormData = { ...taskFormData }; + + // 1. 删除 userList 中的用户 + updatedFormData.userList = (updatedFormData.userList || []).filter( + (user: PersonnelItem) => !userIdSet.has(String(user.id || user.userId)) + ); + + // 2. 删除 indicatorList(指标对象) 中该用户的指标 + updatedFormData.indicatorList = (updatedFormData.indicatorList || []).filter( + (item: IndicatorItem) => !userIdSet.has(String(item.userId)) + ); + + // 3. 删除 selectedSuppliers(供应商对象) 中 evaluator(评价人员对象) 对象 + updatedFormData.selectedSuppliers = (updatedFormData.selectedSuppliers || []) + .map((supplier: SupplierItem) => { + const newEvaluators = (supplier.evaluators || []).filter( + (evaluator: PersonnelItem) => !userIdSet.has(String(evaluator.id)) + ); + return { + ...supplier, + evaluators: newEvaluators, + }; + }) + .filter((supplier) => (supplier.evaluators || []).length > 0); + + // 保存更新 + yield put({ type: 'saveTaskFormData', payload: updatedFormData }); + }, + + + /** + * 下一步 + * 将当前步骤索引加1 + */ + *nextStep(_: any, { put, select }: { put: any; select: any }) { + const { currentStep } = (yield select((state: any) => state.supplierAnnualTaskManage)) as { currentStep: number }; + yield put({ type: 'saveCurrentStep', payload: currentStep + 1 }); + }, + + /** + * 上一步 + * 将当前步骤索引减1 + */ + *prevStep(_: any, { put, select }: { put: any; select: any }) { + const { currentStep } = (yield select((state: any) => state.supplierAnnualTaskManage)) as { currentStep: number }; + yield put({ type: 'saveCurrentStep', payload: currentStep - 1 }); + }, + /* + 设置当前步骤 + */ + *setCurrentStep({ payload }: { payload: number }, { put }: { put: any }) { + yield put({ type: 'saveCurrentStep', payload }); + }, + + /** + * 重置状态 + * 清空表单数据并将步骤重置为第一步 + */ + *resetState(_: any, { put }: { put: any }) { + // 重置表单数据 + yield put({ + type: 'saveTaskFormData', + payload: {} + }); + // 重置步骤到第一步 + yield put({ + type: 'saveCurrentStep', + payload: 0 + }); + }, + /** + * 设置模式 + */ + *setMode({ payload }: { payload: 'add' | 'edit' | 'division' }, { put }: { put: any }) { + yield put({ type: 'saveMode', payload }); + }, + }, + + // reducers用于更新状态 + reducers: { + /** + * 保存当前步骤 + */ + saveCurrentStep(state, { payload }) { + return { ...state, currentStep: payload }; + }, + /** + * 保存模式 + */ + saveMode(state, { payload }) { + return { ...state, mode: payload }; + }, + /** + * 保存加载状态 + */ + saveLoading(state, { payload }) { + return { ...state, loading: payload }; + }, + /** + * 保存详情加载状态 + */ + saveDetailLoading(state, { payload }) { + return { ...state, detailLoading: payload }; + }, + /** + * 保存任务表单数据 + */ + saveTaskFormData(state, { payload }) { + return { ...state, taskFormData: payload }; + }, + /** + * 保存任务详情数据 + */ + saveTaskDetail(state, { payload }) { + return { ...state, taskDetail: payload }; + }, + }, +}; + +export default supplierAnnualTaskManage; diff --git a/src/pages/supplierAnnualManage/supplierAnnualResult/supplierAnnualResult.less b/src/pages/supplierAnnualManage/supplierAnnualResult/supplierAnnualResult.less index e1e55c1..ace86ac 100644 --- a/src/pages/supplierAnnualManage/supplierAnnualResult/supplierAnnualResult.less +++ b/src/pages/supplierAnnualManage/supplierAnnualResult/supplierAnnualResult.less @@ -1,6 +1,4 @@ .common-container { - padding: 24px; - background-color: #f0f2f5; } .page-header { @@ -27,56 +25,3 @@ text-align: center; } -/* 公共样式类,与supplierEvaluateResult.less一致 */ -:global { - .common-container { - background-color: #f0f2f5; - min-height: 100%; - padding: 16px; - } - - .filter-action-row { - background-color: #fff; - padding: 16px; - border-radius: 2px; - margin-bottom: 16px; - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05); - } - - .filter-form { - display: flex; - flex-wrap: wrap; - gap: 16px; - - .ant-form-item { - margin-right: 16px; - margin-bottom: 12px; - - &.filter-btns { - margin-left: auto; - - button { - margin-left: 8px; - } - } - } - } - - .content-area { - background-color: #fff; - padding: 16px; - border-radius: 2px; - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05); - } - - .ant-card { - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05); - } - - .table-top-row { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 16px; - } -} diff --git a/src/pages/supplierAnnualManage/supplierAnnualResult/supplierAnnualResult.tsx b/src/pages/supplierAnnualManage/supplierAnnualResult/supplierAnnualResult.tsx index 5c0187b..9aa9af8 100644 --- a/src/pages/supplierAnnualManage/supplierAnnualResult/supplierAnnualResult.tsx +++ b/src/pages/supplierAnnualManage/supplierAnnualResult/supplierAnnualResult.tsx @@ -3,7 +3,6 @@ import { Card, Table, Button, Input, Row, Col, message, Space, Form, DatePicker, import { history } from 'umi'; import { SearchOutlined, DeleteOutlined } from '@ant-design/icons'; import { getAnnualResultTaskList } from '@/servers/api/supplierAnnual'; -import styles from './supplierAnnualResult.less'; const { RangePicker } = DatePicker; const { Option } = Select; diff --git a/src/pages/supplierAnnualManage/supplierAnnualReview/supplierAnnualReview.less b/src/pages/supplierAnnualManage/supplierAnnualReview/supplierAnnualReview.less index ea349bc..e88b9df 100644 --- a/src/pages/supplierAnnualManage/supplierAnnualReview/supplierAnnualReview.less +++ b/src/pages/supplierAnnualManage/supplierAnnualReview/supplierAnnualReview.less @@ -16,7 +16,6 @@ } .content-area { - margin-top: 16px; } .status-tag { diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/BasicInfoStep.tsx b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/BasicInfoStep.tsx new file mode 100644 index 0000000..5e80fbf --- /dev/null +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/BasicInfoStep.tsx @@ -0,0 +1,231 @@ +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 '../supplierAnnualTaskManageAdd.less'; +import { CategoryLimitationType } from '@/dicts/supplierTemplateDict'; +import type { Dispatch } from 'umi'; +import { connect } from 'umi'; +import type { SupplierTaskModelState } from '@/models/supplierAnnualTaskManage'; + +const { Option } = Select; + +interface TemplateItem { + id: string; + templateName: string; + categoryLimitation?: string; +} + +interface BasicInfoStepProps { + supplierAnnualTaskManage: SupplierTaskModelState; + dispatch: Dispatch; + innerRef?: any; // 使用 innerRef 作为属性名 +} + +// 定义组件,使用 innerRef 代替直接的 ref +const BasicInfoStepComponent = (props: BasicInfoStepProps) => { + const { supplierAnnualTaskManage, dispatch, innerRef } = props; + const [form] = Form.useForm(); + const [templates, setTemplates] = useState([]); + const [loading, setLoading] = useState(false); + + // 从props中获取状态 + const { taskFormData } = supplierAnnualTaskManage; + + // 暴露表单方法给父组件,使用 innerRef + useImperativeHandle(innerRef, () => ({ + validateFields: () => form.validateFields(), + // 删除不必要的方法,因为现在使用Dva管理数据 + })); + + // 获取模板列表 + const fetchTemplates = async () => { + setLoading(true); + try { + const response = await getAllTemplates(); + if (response.success && response.data) { + setTemplates(response.data); + } + } catch (error) { + console.error('获取模板列表失败:', error); + } finally { + setLoading(false); + } + }; + + // 获取评价模板和初始化表单数据 + useEffect(() => { + fetchTemplates(); + + // 初始化表单数据 + if (taskFormData) { + form.setFieldsValue(taskFormData); + } + }, []); + + // 当taskFormData变化时更新表单值 + useEffect(() => { + if (taskFormData) { + // 处理日期字段,确保正确设置 + const formValues = { + ...taskFormData, + startTime: taskFormData.startTime ? moment(taskFormData.startTime) : undefined, + endTime: taskFormData.endTime ? moment(taskFormData.endTime) : undefined, + }; + form.setFieldsValue(formValues); + } + }, [taskFormData, form]); + + // 表单值变化时触发 + const handleValuesChange = (changedValues: any, allValues: any) => { + // 使用dispatch更新model中的数据 + dispatch({ + type: 'supplierAnnualTaskManage/updateFormData', + payload: allValues, + }); + }; + + // 年度选项生成 + const yearOptions = useCallback(() => { + const currentYear = new Date().getFullYear(); + return Array.from({ length: 11 }, (_, i) => currentYear - 5 + i).map((year) => ( + + )); + }, []); + + // 判断是否显示品类选择器 + const shouldShowCategorySelector = useCallback(() => { + const categoryLimitation = taskFormData.categoryLimitation; + return categoryLimitation === CategoryLimitationType.LIMITED; + }, [taskFormData]); + + + return ( +
+ +
+ + + + + + + + + + + + + + + + ({ + value: value ? moment(value) : undefined, + })} + normalize={(value) => value && value.format('YYYY-MM-DD')} + > + + + + + ({ + value: value ? moment(value) : undefined, + })} + normalize={(value) => value && value.format('YYYY-MM-DD')} + > + + + + + + + + + + 通用不限品类 + 限制品类 + + + + {shouldShowCategorySelector() && ( + + + + + + )} + + + + + + + + + +
+
+
+ ); +}; + +// 连接 Dva model +const ConnectedComponent = connect( + ({ supplierAnnualTaskManage }: { supplierAnnualTaskManage: SupplierTaskModelState }) => ({ + supplierAnnualTaskManage, + }) +)(BasicInfoStepComponent); + +// 外层转发 ref 到 innerRef +const BasicInfoStep = forwardRef((props: any, ref) => ( + +)); + +export default BasicInfoStep; diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/BasicInfo.tsx b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/BasicInfo.tsx new file mode 100644 index 0000000..21d8840 --- /dev/null +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/BasicInfo.tsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { Card, Descriptions, Tag } from 'antd'; +import { TaskStatusColor, TaskStatusText } from '@/dicts/supplierTaskDict'; +import styles from '../../supplierAnnualTaskManageDetail.less'; +import { CategoryLimitationType, CategoryLimitationTypeText } from '@/dicts/supplierTemplateDict'; + +interface BasicInfoProps { + taskData: SupplierEvaluate.TaskDetailData; +} + +const BasicInfo: React.FC = ({ taskData }) => { + // 获取状态标签 + const getStatusTag = (status: string) => { + const color = TaskStatusColor[status as keyof typeof TaskStatusColor] || 'default'; + const text = TaskStatusText[status as keyof typeof TaskStatusText] || '未知状态'; + return {text}; + }; + + return ( + + + {taskData.evaluateTheme || '--'} + + {getStatusTag(taskData.status || '')} + + {taskData.evaluateYear || '--'} + {taskData.startTime || '--'} + {taskData.endTime || '--'} + {taskData.templateName || '--'} + + { + CategoryLimitationTypeText[ + taskData.categoryLimitation as keyof typeof CategoryLimitationTypeText + ] + } + + {taskData.categoryId && ( + {taskData.categoryName} + )} + + + ); +}; + +export default BasicInfo; diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/EvaluatorInfo.tsx b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/EvaluatorInfo.tsx new file mode 100644 index 0000000..a1fa9cc --- /dev/null +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/EvaluatorInfo.tsx @@ -0,0 +1,88 @@ +import React from 'react'; +import { Card, Table, Button, message } from 'antd'; +import { UnorderedListOutlined } from '@ant-design/icons'; +import styles from '../../supplierAnnualTaskManageDetail.less'; +import type { TaskDetailData, IndicatorList, User } from '@/servers/types/supplierEvaluateTask'; + +interface EvaluatorInfoProps { + taskData: TaskDetailData; + onViewIndicators: (record: IndicatorList) => void; +} + +const EvaluatorInfo: React.FC = ({ taskData, onViewIndicators }) => { + const userList = taskData.userList; + // 查看评价人员分工 + const handleViewIndicators = (record: IndicatorList) => { + if (record.indicatorIds && record.indicatorIds.length > 0) { + onViewIndicators(record); + } else { + message.info('该评价人员没有分配指标'); + } + }; + + const columns = [ + { + title: '序号', + dataIndex: 'index', + key: 'index', + render: (_: any, __: any, index: number) => index + 1, + width: 80, + }, + { + title: '评价人员', + dataIndex: 'userId', + key: 'userId', + render: (userId: string) => { + try { + const user = userList.find((userItem: User) => userItem.userId === userId); + return user?.userName; + } catch (error) { + console.error('获取评价人员信息失败:', error); + return userId; + } + }, + }, + { + title: '评价类型', + dataIndex: 'type', + key: 'type', + render: (type: string) => (type === '1' ? '按指标评价' : '按评价单评价'), + }, + { + title: '指标数量', + dataIndex: 'indicatorIds', + key: 'indicatorCount', + render: (indicatorIds: string[]) => indicatorIds?.length || 0, + }, + { + title: '操作', + key: 'action', + render: (record: any) => ( + + ), + }, + ]; + + if (!taskData || !taskData.indicatorList || taskData.indicatorList.length === 0) { + return
暂无评价人员数据
; + } + + return ( + + + + ); +}; + +export default EvaluatorInfo; diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/EvaluatorModal.tsx b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/EvaluatorModal.tsx new file mode 100644 index 0000000..641a9d8 --- /dev/null +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/EvaluatorModal.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { Modal, Button, Tag } from 'antd'; +import styles from '../../supplierAnnualTaskManageDetail.less'; +import type {TaskDetailData} from '@/servers/supplierEvaluateTask' + +interface EvaluatorModalProps { + visible: boolean; + supplier: TaskDetailData; + onCancel: () => void; +} + +const EvaluatorModal: React.FC = ({ visible, supplier, onCancel }) => { + return ( + + 关闭 + + ]} + width={600} + > + {supplier?.userList?.length > 0 ? ( +
+
+ {supplier.userList.map((user) => ( + + {user.userDept} - {user.userName} + + ))} +
+
+ ) : ( +
暂无评价人员数据
+ )} +
+ ); +}; + +export default EvaluatorModal; diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/IndicatorModal.tsx b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/IndicatorModal.tsx new file mode 100644 index 0000000..1096836 --- /dev/null +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/IndicatorModal.tsx @@ -0,0 +1,99 @@ +import React, { useState, useEffect } from 'react'; +import { Modal, Button, Spin } from 'antd'; +import styles from '../../supplierAnnualTaskManageDetail.less'; +import EvaluateTemplateTable from '@/components/EvaluateTemplateTable/EvaluateTemplateTable'; +import { getTemplateDetail } from '@/servers/api/supplierEvaluate'; + +interface IndicatorModalProps { + visible: boolean; + evaluatorId: string; + indicators: string[]; + templateId?: string; + onCancel: () => void; +} + +const IndicatorModal: React.FC = ({ + visible, + evaluatorId, + indicators, + templateId, + onCancel +}) => { + const [loading, setLoading] = useState(false); + const [filteredIndicators, setFilteredIndicators] = useState([]); + + // 获取模板详情数据 + useEffect(() => { + const fetchTemplateData = async () => { + if (visible && templateId) { + setLoading(true); + try { + const res = await getTemplateDetail(templateId); + if (res.success && res.data) { + + // 筛选出当前评价人员负责的指标 + if (res.data.indicatorStList && indicators.length > 0) { + // 扁平化处理模板数据,提取出所有二级指标 + const allIndicators = []; + for (const stItem of res.data.indicatorStList) { + if (stItem.indicatorNdList) { + for (const ndItem of stItem.indicatorNdList) { + if (indicators.includes(ndItem.id as string)) { + allIndicators.push({ + ...ndItem, + baseIndicator: stItem.baseIndicator, + descIndicator: stItem.descIndicator, + stScore: stItem.score, + indicatorType: stItem.indicatorType, + id: ndItem.id, + score: ndItem.score, + }); + } + } + } + } + setFilteredIndicators(allIndicators); + } + } + } catch (error) { + console.error('获取模板详情失败:', error); + } finally { + setLoading(false); + } + } + }; + + fetchTemplateData(); + }, [visible, templateId, indicators]); + + return ( + + 关闭 + + ]} + width={1200} // 增加宽度以适应表格 + > + {loading ? ( +
+ +
+ ) : indicators.length > 0 && filteredIndicators.length > 0 ? ( +
+ +
+ ) : ( +
暂无指标分工数据
+ )} +
+ ); +}; + +export default IndicatorModal; diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/SupplierInfo.tsx b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/SupplierInfo.tsx new file mode 100644 index 0000000..57707f6 --- /dev/null +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/SupplierInfo.tsx @@ -0,0 +1,95 @@ +import React from 'react'; +import { Card, Table, Button, message } from 'antd'; +import { TeamOutlined } from '@ant-design/icons'; +import styles from '../../supplierAnnualTaskManageDetail.less'; +import type { TaskDetailData,User } from '@/servers/types/supplierEvaluateTask'; + +interface SupplierInfoProps { + taskData: TaskDetailData; + onViewEvaluators: (supplier: any) => void; +} + +const SupplierInfo: React.FC = ({ taskData, onViewEvaluators }) => { + // 查看供应商评价人员 + const handleViewSupplierEvaluators = (record: TaskDetailData) => { + if (!taskData || !taskData.supplierIds) { + message.error('无法获取供应商评价人员信息'); + return; + } + + // 根据供应商ID查找对应的userIds + const supplierData = taskData.supplierIds.find((item) => item.id === record.supplierId); + let userList: User[] = []; + try { + userList = taskData.userList.filter((item) => supplierData?.userIds.includes(item.userId)); + } catch (error) { + console.error('获取供应商评价人员信息失败:', error); + } + + if (supplierData) { + onViewEvaluators({ + ...record, + userIds: supplierData.userIds, + userList: userList, + }); + } else { + message.error('未找到该供应商的评价人员信息'); + } + }; + + const columns = [ + { + title: '序号', + dataIndex: 'index', + key: 'index', + render: (_: any, __: any, index: number) => index + 1, + width: 80, + }, + { + title: '供应商名称', + dataIndex: 'supplierName', + key: 'supplierName', + }, + { + title: '部门', + dataIndex: 'deptName', + key: 'deptName', + }, + { + title: '品类', + dataIndex: 'categoryName', + key: 'categoryName', + render: (text: string) => text || '--', + }, + { + title: '操作', + key: 'action', + render: (record: any) => ( + + ), + }, + ]; + + if (!taskData || !taskData.blackSupplierVos || taskData.blackSupplierVos.length === 0) { + return
暂无供应商数据
; + } + + return ( + +
+ + ); +}; + +export default SupplierInfo; diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/WeightInfo.tsx b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/WeightInfo.tsx new file mode 100644 index 0000000..3f1e01e --- /dev/null +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/WeightInfo.tsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { Card, Table } from 'antd'; +import styles from '../../supplierAnnualTaskManageDetail.less'; + +interface WeightInfoProps { + taskData: SupplierEvaluate.TaskDetailData; +} + +const WeightInfo: React.FC = ({ taskData }) => { + const columns = [ + { + title: '序号', + dataIndex: 'index', + key: 'index', + render: (_: any, __: any, index: number) => index + 1, + width: 80, + }, + { + title: '部门名称', + dataIndex: 'weightDept', + key: 'weightDept', + }, + { + title: '权重值', + dataIndex: 'weightValue', + key: 'weightValue', + render: (value: number) => `${value}%`, + }, + ]; + + if (!taskData || !taskData.taskDeptWeightList || taskData.taskDeptWeightList.length === 0) { + return
暂无权重设置数据
; + } + + return ( + +
+ + ); +}; + +export default WeightInfo; diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/index.ts b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/index.ts new file mode 100644 index 0000000..2a6c7df --- /dev/null +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/Detail/index.ts @@ -0,0 +1,15 @@ +import BasicInfo from './BasicInfo'; +import SupplierInfo from './SupplierInfo'; +import EvaluatorInfo from './EvaluatorInfo'; +import WeightInfo from './WeightInfo'; +import EvaluatorModal from './EvaluatorModal'; +import IndicatorModal from './IndicatorModal'; + +export { + BasicInfo, + SupplierInfo, + EvaluatorInfo, + WeightInfo, + EvaluatorModal, + IndicatorModal +}; diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/BatchEvaluatorModal.tsx b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/BatchEvaluatorModal.tsx new file mode 100644 index 0000000..99f8036 --- /dev/null +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/BatchEvaluatorModal.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { Modal } from 'antd'; +import EvaluateTaskPersonnelSelector from '@/components/EvaluateTaskPersonnelSelector/EvaluateTaskPersonnelSelector'; +import type { PersonnelItem } from '@/servers/types/evaluator'; + +interface BatchEvaluatorModalProps { + visible: boolean; + onCancel: () => void; + onSelect: (selectedEvaluators: PersonnelItem[]) => void; + selectedPersonnel?: PersonnelItem[]; + filter?: boolean; + filterUserIds?: string[]; +} + +const BatchEvaluatorModal: React.FC = ({ + visible, + onCancel, + onSelect, + selectedPersonnel = [], + filter, + filterUserIds, +}) => { + return ( + + + + ); +}; + +export default BatchEvaluatorModal; diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/SupplierEvaluatorModal.tsx b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/SupplierEvaluatorModal.tsx new file mode 100644 index 0000000..d1a9e3d --- /dev/null +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/SupplierEvaluatorModal.tsx @@ -0,0 +1,114 @@ +import React, { useEffect, useState } from 'react'; +import { Modal, Button, List, message } from 'antd'; +import EvaluateTaskPersonnelSelector from '@/components/EvaluateTaskPersonnelSelector/EvaluateTaskPersonnelSelector'; +import { ModalMode } from '@/servers/types/evaluator'; +import type { PersonnelItem, SupplierItem } from '@/servers/types/evaluator'; + +interface SupplierEvaluatorModalProps { + visible: boolean; // 控制弹窗显示/隐藏 + onCancel: () => void; // 取消按钮回调函数 + onSelect: (personnel: PersonnelItem[]) => void; // 选择人员后的回调函数 + currentSupplier: SupplierItem | null; // 当前操作的供应商对象 + mode: ModalMode; // 弹窗模式:SELECT(选择) 或 VIEW(查看) + filter?: boolean; // 是否过滤评价人员 + filterUserIds?: string[]; // 过滤评价人员ID列表 +} + +const SupplierEvaluatorModal: React.FC = ({ + visible, + onCancel, + onSelect, + currentSupplier, + mode, + filter, + filterUserIds, +}) => { + // 本地保存当前选中的人员,确保在弹窗打开/关闭时能正确处理数据 + const [localSelectedPersonnel, setLocalSelectedPersonnel] = useState([]); + + // 当currentSupplier变化时,更新本地的选中人员 + useEffect(() => { + if (currentSupplier && currentSupplier.evaluators) { + setLocalSelectedPersonnel(currentSupplier.evaluators); + } else { + setLocalSelectedPersonnel([]); + } + }, [currentSupplier]); + + // 处理选择人员的回调 + const handleSelect = (personnel: PersonnelItem[]) => { + if (personnel.length === 0) { + message.warning('请至少选择一名评价人员'); + return; + } + setLocalSelectedPersonnel(personnel); + onSelect(personnel); + }; + + // 渲染评价人员列表(用于查看模式) + const renderEvaluatorList = () => { + + if (!currentSupplier) { + return
无供应商数据
; + } + + if (!currentSupplier.evaluators || currentSupplier.evaluators.length === 0) { + return ( +
+ 当前供应商暂无评价人员,请点击"编辑人员"按钮添加 +
+ ); + } + + return ( + ( + + + + )} + /> + ); + }; + + return ( + + 关闭 + , + ] + : null + } + width={700} + destroyOnClose={false} + > + {mode === ModalMode.SELECT ? ( + + ) : ( + renderEvaluatorList() + )} + + ); +}; + +export default SupplierEvaluatorModal; diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/SupplierTable.tsx b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/SupplierTable.tsx new file mode 100644 index 0000000..ed5d013 --- /dev/null +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/SupplierTable.tsx @@ -0,0 +1,116 @@ +import React, { useEffect } from 'react'; +import { Table, Space, Button, Tag, Modal, Tooltip } from 'antd'; +import type { SupplierItem } from '@/servers/types/evaluator'; + +interface SupplierTableProps { + suppliers: SupplierItem[]; // 供应商数据列表 + selectedRowKeys: React.Key[]; // 选中的行keys + onSelectChange: (selectedRowKeys: React.Key[]) => void; // 选择行变化的回调 + onViewEvaluators: (supplier: SupplierItem) => void; // 查看评价人员的回调 + onSelectEvaluators: (supplier: SupplierItem) => void; // 选择评价人员的回调 + onDeleteSupplier: (key: string) => void; // 删除供应商的回调 + mode?: string; // 模式 +} + +const SupplierTable: React.FC = ({ + suppliers, + selectedRowKeys, + onSelectChange, + onViewEvaluators, + onSelectEvaluators, + onDeleteSupplier, + mode, +}) => { + // 表格行选择配置 + const rowSelection = { + selectedRowKeys, + onChange: onSelectChange, + }; + + // 删除确认对话框 + const showDeleteConfirm = (record: SupplierItem) => { + Modal.confirm({ + title: '确定要删除此供应商吗?', + content: `供应商: ${record.supplierName}${ + record.evaluatorCount > 0 ? `,将同时删除 ${record.evaluatorCount} 名关联的评价人员` : '' + }`, + okText: '确定', + okType: 'danger', + cancelText: '取消', + onOk: () => onDeleteSupplier(record.id), + }); + }; + + // 表格列定义 + const columns = [ + { + title: '供应商名称', // 列标题 + dataIndex: 'supplierName', // 数据字段名 + key: 'supplierName', // 列的唯一标识 + }, + { + title: '统一社会信用代码', + dataIndex: 'socialCreditCode', + key: 'socialCreditCode', + }, + { + title: '准入品类', + dataIndex: 'categoryName', + key: 'categoryName', + render: (categoryName: string) => ( + + {categoryName || '-'} + + ), + }, + { + title: '准入部门', + dataIndex: 'deptName', + key: 'deptName', + }, + { + title: '评价人员数', + align: 'center' as const, // 列对齐方式 + dataIndex: 'evaluatorCount', // 评价人员数量字段 + key: 'evaluatorCount', + }, + { + title: '操作', + key: 'action', + render: (_: any, record: SupplierItem) => ( + + + + {mode !== 'division' && ( + <> + + + )} + + ), + }, + ]; + + return ( +
+ ); +}; + +export default SupplierTable; diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/WeightSettingModal.tsx b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/WeightSettingModal.tsx new file mode 100644 index 0000000..2e0e57a --- /dev/null +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/WeightSettingModal.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import { Modal, Form, Row, Col, InputNumber } from 'antd'; +import type { DeptWeightItem } from '@/servers/types/supplierEvaluateTask'; + +interface WeightSettingModalProps { + visible: boolean; + onCancel: () => void; + onOk: () => void; + taskDeptWeightList: DeptWeightItem[]; + form: any; +} + +const WeightSettingModal: React.FC = ({ + visible, + onCancel, + onOk, + taskDeptWeightList, + form, +}) => { + return ( + +
+ + {taskDeptWeightList.map((item) => ( +
+ + + + + ))} + + + + ); +}; + +export default WeightSettingModal; diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/index.ts b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/index.ts new file mode 100644 index 0000000..4283746 --- /dev/null +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorComponents/index.ts @@ -0,0 +1,15 @@ +import SupplierTable from './SupplierTable'; +import BatchEvaluatorModal from './BatchEvaluatorModal'; +import SupplierEvaluatorModal from './SupplierEvaluatorModal'; +import WeightSettingModal from './WeightSettingModal'; + +// 日志组件版本 +console.log('EvaluatorComponents version: 1.0.1'); + +export { + SupplierTable, + BatchEvaluatorModal, + SupplierEvaluatorModal, + WeightSettingModal, + // 导出的所有组件 +}; diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorSelectStep.tsx b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorSelectStep.tsx new file mode 100644 index 0000000..e552649 --- /dev/null +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/EvaluatorSelectStep.tsx @@ -0,0 +1,465 @@ +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([]); + + // 供应商列表数据,包含评价人员信息(从Dva model获取初始数据,维护在本地状态) + const [suppliers, setSuppliers] = useState([]); + + // 批量选择模态框的可见性控制 + const [batchSelectModalVisible, setBatchSelectModalVisible] = useState(false); + + // 权重设置模态框的可见性控制 + const [weightSettingModalVisible, setWeightSettingModalVisible] = useState(false); + + // 评价人员选择/查看模态框的可见性控制 + const [evaluatorModalVisible, setEvaluatorModalVisible] = useState(false); + + // 当前操作的供应商对象,用于评价人员的选择和查看 + const [currentSupplier, setCurrentSupplier] = useState(null); + + // 模态框模式:SELECT(选择模式) 或 VIEW(查看模式) + const [modalMode, setModalMode] = useState(ModalMode.SELECT); + + // 权重单位列表,根据评价人员部门动态生成 + const [taskDeptWeightList, setTaskDeptWeightList] = useState([]); + + // 表单实例,用于权重设置 + const [form] = Form.useForm(); + const [filterUserIds, setFilterUserIds] = useState([]); + 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(); + 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), + }); + + 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 ( +
+ + {/* 工具栏区域 */} + {mode !== 'division' && ( +
+ + + + +
+ )} + + {/* 供应商表格区域 */} + {suppliers.length === 0 ? ( +
+ 请先在上一步选择供应商 +
+ ) : ( + + )} + + {/* 批量选择评价人员弹窗 */} + setBatchSelectModalVisible(false)} + filter={mode === 'division'} + filterUserIds={filterUserIds} + onSelect={handleBatchEvaluatorSelect} + /> + + {/* 单个供应商评价人员弹窗 */} + setEvaluatorModalVisible(false)} + filter={mode === 'division'} + filterUserIds={filterUserIds} + onSelect={handleEvaluatorSelect} + currentSupplier={currentSupplier} + mode={modalMode} + /> + + {/* 权重设置弹窗 */} + setWeightSettingModalVisible(false)} + onOk={handleSaveWeights} + taskDeptWeightList={taskDeptWeightList} + form={form} + /> +
+
+ ); +}; + +/** + * 连接 Dva model + * 将 model 中的状态映射到组件 props + */ +const ConnectedComponent = connect( + ({ supplierAnnualTaskManage }: { supplierAnnualTaskManage: SupplierTaskModelState }) => ({ + supplierAnnualTaskManage, + }), +)(EvaluatorSelectStepComponent); + +/** + * 外层转发 ref 到 innerRef + */ +const EvaluatorSelectStep = forwardRef((props: any, ref) => ( + +)); + +export default EvaluatorSelectStep; diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/SupplierSelectStep.tsx b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/SupplierSelectStep.tsx new file mode 100644 index 0000000..8e0634b --- /dev/null +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/components/SupplierSelectStep.tsx @@ -0,0 +1,88 @@ +import React, { forwardRef, useImperativeHandle, useEffect, useState } from 'react'; +import { Card } from 'antd'; +import styles from '../supplierAnnualTaskManageAdd.less'; +import SupplierSelector from '@/components/SupplierSelector'; +import type { SupplierItem } from '@/servers/dao/supplierAnnualTaskManag'; +import type { Dispatch } from 'umi'; +import { connect } from 'umi'; +import type { SupplierTaskModelState } from '@/models/supplierAnnualTaskManage'; + +interface SupplierSelectStepProps { + supplierAnnualTaskManage: SupplierTaskModelState; + dispatch: Dispatch; + innerRef?: any; // 使用 innerRef 作为属性名 +} + +// 定义组件,使用 innerRef 代替直接的 ref +const SupplierSelectStepComponent = (props: SupplierSelectStepProps) => { + const { supplierAnnualTaskManage, dispatch, innerRef } = props; + + // 从model获取数据 + const { taskFormData } = supplierAnnualTaskManage; + + // 内部状态,避免直接操作formData导致循环更新 + const [selectedSuppliers, setSelectedSuppliers] = useState([]); + + // 当taskFormData.selectedSuppliers更新时,同步到本地状态 + useEffect(() => { + if (taskFormData.selectedSuppliers && taskFormData.selectedSuppliers.length > 0) { + setSelectedSuppliers(taskFormData.selectedSuppliers); + } + }, [taskFormData.selectedSuppliers]); // 只在表单ID变化时更新(编辑模式加载时) + + // 暴露表单方法给父组件,使用 innerRef + useImperativeHandle(innerRef, () => ({ + validateFields: () => { + // 这里可以添加自定义验证逻辑 + return Promise.resolve(); + }, + // 删除不必要的方法,因为现在使用Dva管理数据 + })); + + // 处理供应商选择 + const handleSupplierSelect = (suppliers: SupplierItem[]) => { + // 确保每个供应商都有evaluators字段 + const suppliersWithEvaluators = suppliers.map(supplier => ({ + ...supplier, + evaluators: supplier.evaluators || [], // 确保evaluators字段存在,即使是空数组 + evaluatorCount: supplier.evaluators?.length || 0 // 计算评价人员数量 + })); + + // 更新本地状态 + setSelectedSuppliers(suppliersWithEvaluators); + + // 通过dispatch更新model数据 + dispatch({ + type: 'supplierAnnualTaskManage/updateFormData', + payload: { + selectedSuppliers: suppliersWithEvaluators, + supplierIds: suppliersWithEvaluators.map(supplier => ({ id: supplier.id })) + } + }); + }; + + return ( +
+ + + +
+ ); +}; + +// 连接 Dva model +const ConnectedComponent = connect( + ({ supplierAnnualTaskManage }: { supplierAnnualTaskManage: SupplierTaskModelState }) => ({ + supplierAnnualTaskManage, + }) +)(SupplierSelectStepComponent); + +// 外层转发 ref 到 innerRef +const SupplierSelectStep = forwardRef((props: any, ref) => ( + +)); + +export default SupplierSelectStep; diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManage.less b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManage.less new file mode 100644 index 0000000..ec027da --- /dev/null +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManage.less @@ -0,0 +1,44 @@ +.card-container { + margin-bottom: 24px; +} + +.filter-container { + margin-bottom: 24px; + padding: 24px 24px 0; + + .filter-item { + display: flex; + + :global { + .ant-form-item-label { + min-width: 80px; + text-align: right; + } + } + } +} + + +.table-container { + .table-top { + display: flex; + justify-content: flex-end; + margin-bottom: 16px; + } +} + +// 详情页样式 +.page-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 24px; +} + +.detail-card { + margin-bottom: 24px; + + .ant-card-head { + border-bottom: 1px solid #f0f0f0; + } +} diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManage.tsx b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManage.tsx index 8c11536..5e92f93 100644 --- a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManage.tsx +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManage.tsx @@ -1,11 +1,279 @@ -import React from 'react'; -import { Card } from 'antd'; +import React, { useState, useEffect } from 'react'; +import { + Card, + Table, + Button, + Input, + Row, + Col, + message, + Space, + Form, + DatePicker, + Select, + Tag, + Tooltip, + Modal +} from 'antd'; +import { history } from 'umi'; +import { SearchOutlined, DeleteOutlined, PlusOutlined, EditOutlined, EyeOutlined } from '@ant-design/icons'; +import { getAnnualTaskList } from '@/servers/api/supplierAnnual'; +import { + AnnualTaskStatus, + AnnualTaskStatusText, + AnnualTaskStatusColor, + AnnualTaskStatusOptions +} from '@/dicts/supplierAnnualTaskManageDict'; +import moment from 'moment'; +import styles from './supplierAnnualTaskManage.less'; + +const { RangePicker } = DatePicker; +const { Option } = Select; const SupplierAnnualTaskManage: React.FC = () => { + const [form] = Form.useForm(); + const [loading, setLoading] = useState(false); + const [data, setData] = useState([]); + const [pagination, setPagination] = useState({ + current: 1, + pageSize: 10, + total: 0, + showTotal: (total: number) => `共 ${total} 条记录`, + showSizeChanger: true, + showQuickJumper: true, + }); + const [searchParams, setSearchParams] = useState({}); + + // 获取年度任务列表 + const fetchList = async (params: any = {}) => { + try { + setLoading(true); + const { current, pageSize, ...restParams } = params; + const res = await getAnnualTaskList({ + basePageRequest: { + pageNo: current, + pageSize, + }, + ...restParams, + ...searchParams, + }); + + if (res.success) { + setData(res.data?.records || []); + setPagination({ + ...pagination, + current, + pageSize, + total: res.data?.total || 0, + }); + } else { + message.error(res.message || '获取列表失败'); + } + } catch (error) { + console.error('获取列表失败:', error); + message.error('获取列表失败'); + } finally { + setLoading(false); + } + }; + + // 首次加载获取数据 + useEffect(() => { + fetchList({ current: 1, pageSize: 10 }); + }, []); + + // 表格变化处理 + const handleTableChange = (paginationParams: any) => { + fetchList({ + current: paginationParams.current, + pageSize: paginationParams.pageSize, + }); + }; + + // 搜索 + const handleSearch = (values: any) => { + const params = { ...values }; + + // 处理日期范围 + if (params.timeRange && params.timeRange.length === 2) { + params.startTime = params.timeRange[0].format('YYYY-MM-DD'); + params.endTime = params.timeRange[1].format('YYYY-MM-DD'); + delete params.timeRange; + } + + setSearchParams(params); + fetchList({ current: 1, pageSize: pagination.pageSize, ...params }); + }; + + // 重置搜索 + const handleReset = () => { + form.resetFields(); + setSearchParams({}); + fetchList({ current: 1, pageSize: pagination.pageSize }); + }; + + // 新增任务 + const handleAdd = () => { + history.push({ + pathname: 'supplierAnnualTaskManageAdd', + }); + }; + + // 编辑任务 + const handleEdit = (record: supplierAnnualTaskManage.TaskRecord) => { + if (record.status !== AnnualTaskStatus.PENDING) { + message.warning('只有待执行状态的任务才能编辑!'); + return; + } + + history.push({ + pathname: 'supplierAnnualTaskManageAdd', + state: { id: record.id, mode: 'edit' } + }); + }; + + // 查看详情 + const handleView = (record: supplierAnnualTaskManage.TaskRecord) => { + history.push({ + pathname: 'supplierAnnualTaskManageDetail', + state: { id: record.id } + }); + }; + + // 获取状态标签 + const getStatusTag = (status: string, statusName: string) => { + const color = AnnualTaskStatusColor[status] || 'default'; + return {statusName || AnnualTaskStatusText[status] || '未知状态'}; + }; + + const columns = [ + { + title: '序号', + dataIndex: 'index', + key: 'index', + width: 80, + render: (_: any, __: any, index: number) => index + 1 + (pagination.current - 1) * pagination.pageSize, + }, + { + title: '评价主题', + dataIndex: 'annualreviewTheme', + key: 'annualreviewTheme', + ellipsis: { + showTitle: false, + }, + render: (text: string) => ( + + {text} + + ), + }, + { + title: '发起单位', + dataIndex: 'deptName', + key: 'deptName', + width: 150, + }, + { + title: '评价开始时间', + dataIndex: 'startTime', + key: 'startTime', + width: 140, + }, + { + title: '评价结束时间', + dataIndex: 'endTime', + key: 'endTime', + width: 140, + }, + { + title: '评价状态', + dataIndex: 'status', + key: 'status', + width: 100, + render: (status: string, record: supplierAnnualTaskManage.TaskRecord) => getStatusTag(status, record.statusName), + }, + { + title: '操作', + key: 'action', + width: 180, + fixed: 'right' as const, + render: (_: any, record: supplierAnnualTaskManage.TaskRecord) => ( + + + {record.status === AnnualTaskStatus.PENDING && ( + + )} + + ), + }, + ]; + return ( - -
供应商年度任务管理模块
-
+
+
+
+ + + + + + + + + + + + + + +
+ +
+
+ +
+
+ + ); }; diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManageAdd.less b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManageAdd.less new file mode 100644 index 0000000..124c070 --- /dev/null +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManageAdd.less @@ -0,0 +1,85 @@ +// 步骤表单相关样式 - 特定于任务管理的样式 +.stepsContainer { + margin: 16px 0 24px; + padding: 24px 0; + background-color: #fff; + border-radius: 2px; +} + +.stepsLayout { + margin: 16px 0; +} + +.stepsLeft { + padding: 24px 0; + background-color: #fff; + border-radius: 2px; +} + +.stepsRight { + display: flex; + flex-direction: column; +} + +.verticalSteps { + height: 100%; + + :global { + .ant-steps-item-description { + max-width: 160px; + color: rgba(0, 0, 0, 0.45); + font-size: 12px; + } + } +} + +.stepsContent { + margin-top: 0; + padding: 24px; + background-color: #fff; + border-radius: 2px; + min-height: 300px; + flex: 1; +} +.toolbar { + margin-bottom: 16px; +} +.stepsAction { + margin-top: 24px; + text-align: center; +} + +// 表单相关特定样式 +.requiredLabel::before { + content: '*'; + color: #ff4d4f; + margin-right: 4px; +} + +.selectWithClear { + position: relative; + + .clearIcon { + position: absolute; + top: 50%; + right: 24px; + transform: translateY(-50%); + color: rgba(0, 0, 0, 0.25); + cursor: pointer; + z-index: 1; + + &:hover { + color: rgba(0, 0, 0, 0.45); + } + } +} + +.basicInfoStep { + .ant-form-item-label > label { + font-weight: 500; + } +} + +// 供应商选择步骤样式 +.supplierSelectStep { +} diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManageAdd.tsx b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManageAdd.tsx index e69de29..eb5a3b0 100644 --- a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManageAdd.tsx +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManageAdd.tsx @@ -0,0 +1,343 @@ +import React, { useRef, useEffect } from 'react'; +import { Card, Steps, Button, message, Space, Row, Col, Modal } from 'antd'; +import { connect, ConnectProps, history, Dispatch } from 'umi'; +import { ArrowLeftOutlined, SaveOutlined } from '@ant-design/icons'; +import BasicInfoStep from './components/BasicInfoStep'; +import SupplierSelectStep from './components/SupplierSelectStep'; +import EvaluatorSelectStep from './components/EvaluatorSelectStep'; +import styles from './supplierAnnualTaskManageAdd.less'; +import { TaskNotifyLowerUnits } from '@/dicts/supplierTaskDict'; +import { SupplierTaskModelState } from '@/models/supplierAnnualTaskManage'; + +const { Step } = Steps; + +/** + * 获取URL参数 + * 解析URL中的id和mode参数,用于判断是否为编辑模式 + * @returns {object} 包含id和mode的对象 + */ +const getUrlParams = () => { + const location = window.location.href; + const url = new URL(location); + return { + id: url.searchParams.get('id'), + mode: url.searchParams.get('mode'), + }; +}; + +/** + * 页面组件Props接口定义 + * 继承了ConnectProps,添加了supplierTaskManage和dispatch + */ +interface PageProps extends ConnectProps { + supplierAnnualTaskManage: SupplierTaskModelState; // dva model状态 + dispatch: Dispatch; // dva dispatch方法 +} + +/** + * 供应商任务管理添加/编辑组件 + * 使用步骤式表单引导用户完成添加或编辑任务的流程 + */ +const SupplierTaskManageAdd: React.FC = ({ supplierAnnualTaskManage, dispatch }) => { + // 获取dva model中的状态 + const { currentStep, loading, detailLoading } = supplierAnnualTaskManage; + + // 判断是否为编辑模式 + const urlParams = getUrlParams(); + const isEditMode = urlParams.mode === 'edit' && urlParams.id; + const isDivisionMode = urlParams.mode === 'division' && urlParams.id; + const taskId = urlParams.id || ''; + + // 创建表单引用,用于访问子组件的表单方法(主要用于验证) + const basicFormRef = useRef(null); // 基本信息表单引用 + const supplierFormRef = useRef(null); // 供应商选择表单引用 + const evaluatorFormRef = useRef(null); // 评价人员表单引用 + const divisionFormRef = useRef(null); // 评价分工表单引用 + + // 确认对话框可见性状态 + const [confirmModalVisible, setConfirmModalVisible] = React.useState(false); + + /** + * 编辑模式下获取任务详情,或新建模式下重置状态 + * 利用useEffect在组件挂载或依赖项变化时触发 + */ + useEffect(() => { + if ((isEditMode || isDivisionMode) && taskId && dispatch) { + dispatch({ + type: 'supplierAnnualTaskManage/saveMode', + payload: 'edit', + }); + // 编辑模式,获取任务详情 + dispatch({ + type: 'supplierAnnualTaskManage/fetchTaskDetail', + payload: { taskId }, + }); + if (isDivisionMode) { + dispatch({ + type: 'supplierAnnualTaskManage/saveMode', + payload: 'division', + }); + dispatch({ + type: 'supplierAnnualTaskManage/setCurrentStep', + payload: 2, + }); + } + } else if (dispatch) { + dispatch({ + type: 'supplierAnnualTaskManage/setMode', + payload: 'add', + }); + // 新建模式,重置状态 + dispatch({ + type: 'supplierAnnualTaskManage/resetState', + }); + } + }, [isEditMode, isDivisionMode, taskId, dispatch]); + + /** + * 组件卸载时重置状态 + */ + useEffect(() => { + return () => { + if (dispatch) { + dispatch({ + type: 'supplierAnnualTaskManage/resetState', + }); + } + }; + }, [dispatch]); + + // 步骤配置,定义每个步骤的标题、描述和内容组件 + const steps = [ + { + title: '基本信息', // 步骤标题 + description: '请填写基本信息', // 步骤描述 + content: ( + + ), // 步骤内容组件 + }, + { + title: '选择供应商', + description: '请选择参加评价的供应商', + content: ( + + ), + }, + { + title: '选择评价人员', + description: '请选择供应商评价人员', + content: ( + + ), + }, + ]; + + /** + * 处理返回按钮点击事件 + * 返回上一页 + */ + const handleBack = () => { + history.goBack(); + }; + + /** + * 处理下一步按钮点击事件 + * 验证当前步骤的表单数据,通过后进入下一步 + */ + 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 (dispatch) { + dispatch({ type: 'supplierAnnualTaskManage/nextStep' }); + } + } catch (errorInfo: any) { + // 表单验证失败 + console.log('表单验证失败:', errorInfo); + if (typeof errorInfo === 'string') { + message.error(errorInfo); + } else if (errorInfo && errorInfo.errorFields) { + message.error('请完成必填项'); + } + } + }; + + /** + * 处理上一步按钮点击事件 + * 返回上一个步骤 + */ + const handlePrev = () => { + if (dispatch) { + dispatch({ type: 'supplierAnnualTaskManage/prevStep' }); + } + }; + + /** + * 提交任务数据 + * @param taskStatus 任务状态,决定是否需要下级单位完善 + */ + const submitTaskData = (taskStatus: TaskNotifyLowerUnits | null) => { + if (dispatch) { + dispatch({ + type: 'supplierAnnualTaskManage/submitTaskData', + payload: { + taskStatus, + isEditMode, + taskId, + onSuccess: () => { + history.goBack(); + } + }, + }); + } + }; + + /** + * 处理提交按钮点击事件 + * 验证评价分工表单,通过后根据是否为编辑模式决定提交方式 + */ + const handleSubmit = async () => { + // 验证评价分工 + if (divisionFormRef.current) { + const result = divisionFormRef.current.validate(); + if (!result.valid) { + message.error(result.message); + return; + } + } + // 如果是编辑模式,则直接提交 提示是否确认后直接提交 + if (isEditMode) { + Modal.confirm({ + title: '提示', + content: '是否确认提交', + onOk: () => { + submitTaskData(null); + }, + }); + return; + } + + // 新增模式下,显示确认对话框询问是否需要下级单位完善 + setConfirmModalVisible(true); + }; + + /** + * 处理确认对话框的取消按钮点击事件 + */ + const handleConfirmCancel = () => { + setConfirmModalVisible(false); + }; + + /** + * 处理确认对话框的"是"选项点击事件 + * 提交任务,并设置状态为需要下级单位完善 + */ + const handleConfirmYes = () => { + submitTaskData(TaskNotifyLowerUnits.YES); // 未开始状态,需要下级单位完善 + setConfirmModalVisible(false); + }; + + /** + * 处理确认对话框的"否"选项点击事件 + * 提交任务,并设置状态为不需要下级单位完善 + */ + const handleConfirmNo = () => { + submitTaskData(TaskNotifyLowerUnits.NO); // 进行中状态,不需要下级单位完善 + setConfirmModalVisible(false); + }; + + return ( +
+ {/* 卡片容器,显示加载状态 */} + + {/* 页面头部,包含标题和返回按钮 */} +
+

{isEditMode ? '修改评价任务' : '新增评价任务'}

+ +
+ + {/* 步骤式表单布局 */} + + {/* 左侧步骤导航 */} +
+ + {steps.map((item) => ( + + ))} + + + {/* 右侧表单内容 */} + +
{steps[currentStep].content}
+ + {/* 步骤操作按钮 */} +
+ + {/* 如果当前是评价分工步骤,则不显示上一步按钮 */} + {currentStep > (isDivisionMode ? 2 : 0) && } + {currentStep < steps.length - 1 && ( + + )} + {currentStep === steps.length - 1 && ( + + )} + +
+ + + + + {/* 确认对话框 - 询问是否需要下级单位完善 */} + +

是否提示给下级单位继续完善评价人及评价分工?

+
+ + + + + +
+
+ + ); +}; + +// 将dva model中的状态映射到组件props +export default connect(({ supplierAnnualTaskManage }: { supplierAnnualTaskManage: SupplierTaskModelState }) => ({ + supplierAnnualTaskManage, +}))(SupplierTaskManageAdd); diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManageDetail.less b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManageDetail.less new file mode 100644 index 0000000..1c4dd92 --- /dev/null +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManageDetail.less @@ -0,0 +1,100 @@ +.loadingContainer { + display: flex; + justify-content: center; + align-items: center; + height: 300px; +} + +.detailCard { + margin-bottom: 24px; +} + +.supplierList { + display: flex; + flex-direction: column; + gap: 16px; +} + +.supplierCard { + margin-bottom: 16px; +} + +.indicatorList { + .indicatorItem { + padding: 8px 0; + border-bottom: 1px solid #f0f0f0; + display: flex; + align-items: center; + + &:last-child { + border-bottom: none; + } + } + + .indicatorIndex { + width: 30px; + font-weight: bold; + } + + .indicatorId { + flex: 1; + } +} + +.infoItem { + margin-bottom: 8px; + display: flex; + align-items: flex-start; +} + +.label { + font-weight: 500; + margin-right: 8px; + min-width: 100px; +} + +.evaluatorList { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.evaluatorTag { + margin-bottom: 8px; +} + +.idList { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.emptyData { + display: flex; + justify-content: center; + align-items: center; + height: 200px; + color: rgba(0, 0, 0, 0.45); + font-size: 14px; +} + +.tableContainer { + margin-top: 16px; +} + +.weightStatusInfo { + margin-bottom: 16px; + font-size: 14px; + + .label { + font-weight: bold; + margin-right: 8px; + } +} + +.evaluatorModalContent, +.indicatorModalContent { + max-height: 400px; + overflow-y: auto; + padding: 8px; +} diff --git a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManageDetail.tsx b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManageDetail.tsx index e69de29..f932f9e 100644 --- a/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManageDetail.tsx +++ b/src/pages/supplierAnnualManage/supplierAnnualTaskManage/supplierAnnualTaskManageDetail.tsx @@ -0,0 +1,153 @@ +import React, { useState, useEffect } from 'react'; +import { Card, Button, message, Spin, Tabs, Typography } from 'antd'; +import { history, useLocation } from 'umi'; +import { ArrowLeftOutlined } from '@ant-design/icons'; +import { getTaskDetail } from '@/servers/api/supplierEvaluate'; +import { + BasicInfo, + SupplierInfo, + EvaluatorInfo, + WeightInfo, + EvaluatorModal, + IndicatorModal +} from './components/Detail'; +import styles from './supplierAnnualTaskManageDetail.less'; + +const { TabPane } = Tabs; +const { Title } = Typography; + +// 解析查询参数 +const useQuery = () => { + const { search } = useLocation(); + return React.useMemo(() => new URLSearchParams(search), [search]); +}; + +const SupplierTaskManageDetail: React.FC = () => { + const query = useQuery(); + const taskId = query.get('id'); + + // 数据加载状态 + const [loading, setLoading] = useState(true); + + // 任务数据 + const [taskData, setTaskData] = useState(null); + + // 当前活动标签页 + const [activeTab, setActiveTab] = useState('1'); + + // 模态框状态 + const [evaluatorModalVisible, setEvaluatorModalVisible] = useState(false); + const [indicatorModalVisible, setIndicatorModalVisible] = useState(false); + const [currentSupplier, setCurrentSupplier] = useState(null); + const [currentIndicators, setCurrentIndicators] = useState([]); + const [currentEvaluator, setCurrentEvaluator] = useState(''); + + // 获取任务详情 + const fetchTaskDetail = async (id: string) => { + if (!id) { + message.error('任务ID不能为空'); + return; + } + + setLoading(true); + try { + const response = await getTaskDetail(id); + if (response.success) { + setTaskData(response.data); + } else { + message.error(response.message || '获取任务详情失败'); + } + } catch (error) { + console.error('获取任务详情失败:', error); + message.error('获取任务详情失败'); + } finally { + setLoading(false); + } + }; + + // 当组件挂载时,获取任务详情 + useEffect(() => { + if (taskId) { + fetchTaskDetail(taskId); + } else { + message.error('未提供任务ID'); + history.push('/supplier/supplierTaskManage'); + } + }, [taskId]); + + // 处理返回 + const handleBack = () => { + history.goBack(); + }; + + // 处理查看供应商评价人员 + const handleViewSupplierEvaluators = (supplier: any) => { + setCurrentSupplier(supplier); + setEvaluatorModalVisible(true); + }; + + // 处理查看评价人员分工 + const handleViewIndicators = (record: any) => { + setCurrentIndicators(record.indicatorIds || []); + setCurrentEvaluator(record.userId); + setIndicatorModalVisible(true); + }; + + return ( +
+ +
+ 评价任务详情 + +
+ + {loading ? ( +
+ +
+ ) : ( + + + {taskData && } + + + {taskData && } + + + {taskData && } + + + {taskData && } + + + )} +
+ + {/* 供应商评价人员模态框 */} + setEvaluatorModalVisible(false)} + /> + + {/* 评价人员分工模态框 */} + setIndicatorModalVisible(false)} + /> +
+ ); +}; + +export default SupplierTaskManageDetail; diff --git a/src/pages/supplierEvaluateManage/supplierTaskManage/supplierTaskManageDetail.tsx b/src/pages/supplierEvaluateManage/supplierTaskManage/supplierTaskManageDetail.tsx index 9752dc7..9ff682d 100644 --- a/src/pages/supplierEvaluateManage/supplierTaskManage/supplierTaskManageDetail.tsx +++ b/src/pages/supplierEvaluateManage/supplierTaskManage/supplierTaskManageDetail.tsx @@ -12,7 +12,6 @@ import { IndicatorModal } from './components/Detail'; import styles from './supplierTaskManageDetail.less'; -import type { TaskDetailData } from '@/servers/types/supplierEvaluateTask'; const { TabPane } = Tabs; const { Title } = Typography; @@ -31,7 +30,7 @@ const SupplierTaskManageDetail: React.FC = () => { const [loading, setLoading] = useState(true); // 任务数据 - const [taskData, setTaskData] = useState(null); + const [taskData, setTaskData] = useState(null); // 当前活动标签页 const [activeTab, setActiveTab] = useState('1'); diff --git a/src/servers/api/supplierAnnual.ts b/src/servers/api/supplierAnnual.ts index a20b7c6..2fba314 100644 --- a/src/servers/api/supplierAnnual.ts +++ b/src/servers/api/supplierAnnual.ts @@ -83,6 +83,55 @@ export async function disableAnnualTemplate(id: string) { }); } +// =================== 供应商年度任务管理 =================== + +/** + * 获取年度任务列表 + * @param params 查询参数 + * @returns Promise + */ +export async function getAnnualTaskList(params: supplierAnnualTaskManage.TaskListRequest) { + return request('/annualreview/task/getPage', { + method: 'POST', + data: params, + }); +} + +/** + * 获取年度任务详情 + * @param id 任务ID + * @returns Promise + */ +export async function getAnnualTaskDetail(id: string) { + return request(`/annualreview/task/${id}`, { + method: 'GET', + }); +} + +/** + * 添加年度任务 + * @param params 任务数据 + * @returns Promise + */ +export async function addAnnualTask(params: supplierAnnualTaskManage.AddTaskRequest) { + return request('/annualreview/task', { + method: 'POST', + data: params, + }); +} + +/** + * 更新年度任务 + * @param params 任务数据 + * @returns Promise + */ +export async function updateAnnualTask(params: supplierAnnualTaskManage.UpdateTaskRequest) { + return request('/annualreview/task', { + method: 'PUT', + data: params, + }); +} + // =================== 供应商年度审查模块 =================== /** diff --git a/src/servers/dao/supplierAnnualManage/supplierAnnualTaskManage.d.ts b/src/servers/dao/supplierAnnualManage/supplierAnnualTaskManage.d.ts new file mode 100644 index 0000000..ef9753b --- /dev/null +++ b/src/servers/dao/supplierAnnualManage/supplierAnnualTaskManage.d.ts @@ -0,0 +1,172 @@ +// 供应商年度任务管理dao + +declare namespace supplierAnnualTaskManage { + // 基础分页请求参数 + interface BasePageRequest { + pageNo: number; + pageSize: number; + [property: string]: any; + } + + // 列表请求参数 + interface TaskListRequest { + basePageRequest: BasePageRequest; + [property: string]: any; + } + + // 列表响应 + interface TaskListResponse { + code: number; + data: TaskListData; + message: string; + success: boolean; + [property: string]: any; + } + + // 列表数据 + interface TaskListData { + countId: null; + current: number; + hitCount: boolean; + maxLimit: null; + optimizeCountSql: boolean; + orders: any[]; + pages: number; + records: TaskRecord[]; + searchCount: boolean; + size: number; + total: number; + [property: string]: any; + } + + // 任务记录 + interface TaskRecord { + annualreviewTheme: string; + basePageRequest: null; + deptName: string; + endTime: string; + id: string; + startTime: string; + status: string; + statusName: string; + [property: string]: any; + } + + // 详情响应 + interface TaskDetailResponse { + code: number; + data: TaskDetailData; + message: string; + success: boolean; + [property: string]: any; + } + + // 详情数据 + interface TaskDetailData { + annualreviewTheme: string; + annualreviewYear: null; + blackSupplierVos: BlackSupplierVo[]; + categoryId: string; + categoryLimitation: string; + createBy: null; + createDate: null; + createTime: null; + deleteFlag: null; + delFlag: string; + deptId: null; + endTime: string; + id: string; + lastUpdateTime: null; + remark: null; + startTime: string; + status: string; + supplierPeopleVo: SupplierPeopleVo[]; + templateId: string; + tenantId: null; + tenantName: null; + updateBy: null; + updateDate: null; + updateTime: null; + userList: TaskUserList[]; + [property: string]: any; + } + + // 黑名单供应商数据 + interface BlackSupplierVo { + basePageRequest: null; + categoryName: null; + createTime: null; + deptName: string; + evaluateTime: null; + socialCreditCode: null | string; + supplierId: string; + supplierName: string; + themeName: null; + userList: BlackSupplierVoUserList[]; + [property: string]: any; + } + + // 黑名单供应商用户列表 + interface BlackSupplierVoUserList { + id: string; + name: string; + position: null; + selected: null; + userDept: string; + userDeptId: string; + [property: string]: any; + } + + // 供应商人员 + interface SupplierPeopleVo { + suppliedId: string; + userIds: string[]; + [property: string]: any; + } + + // 详情用户列表 + interface TaskUserList { + isSelected: boolean; + userDept: string; + userDeptId: string; + userId: string; + userName: string; + [property: string]: any; + } + + // 添加任务请求 + interface AddTaskRequest { + annualreviewTheme: string; + annualreviewYear: string; + categoryId: string; + categoryLimitation: string; + endTime: string; + startTime: string; + supplierPeopleVo: SupplierPeopleVo[]; + templateId: string; + [property: string]: any; + } + + // 更新任务请求 + interface UpdateTaskRequest { + annualreviewTheme: string; + annualreviewYear: string; + categoryId: string; + categoryLimitation: string; + endTime: string; + id: string; + startTime: string; + supplierPeopleVo: SupplierPeopleVo[]; + templateId: string; + [property: string]: any; + } + + // 通用API响应 + interface ApiResponse { + code: number; + data: T; + message: string; + success: boolean; + [property: string]: any; + } +} diff --git a/src/servers/dao/supplierAnnualTaskManag.ts b/src/servers/dao/supplierAnnualTaskManag.ts new file mode 100644 index 0000000..282d97f --- /dev/null +++ b/src/servers/dao/supplierAnnualTaskManag.ts @@ -0,0 +1,567 @@ + +import type { TaskNotifyLowerUnits } from '@/dicts/supplierTaskDict'; +/** + * 供应商评价相关接口类型定义 + */ + +// 模板相关类型 +/** + * 模板查询请求参数 + */ +export interface TemplateRequest { + current?: number; + pageSize?: number; + templateName?: string; + status?: string; +} + +/** + * 模板查询响应 + */ +export interface TemplateResponse { + code: number; + success: boolean; + data: { + records: TemplateItem[]; + total: number; + size: number; + current: number; + pages: number; + }; + message: string; +} + +/** + * 模板项 + */ +export interface TemplateItem { + id: string; + templateName: string; + status: string; + createTime: string; + updateTime: string; + createBy: string; + updateBy: string; + [key: string]: any; +} + +/** + * 模板详情响应 + */ +export interface TemplateDetailResponse { + code: number; + success: boolean; + data: { + id: string; + templateName: string; + status: string; + dimensions: TemplateDimension[]; + [key: string]: any; + }; + message: string; +} + +/** + * 模板维度 + */ +export interface TemplateDimension { + id: string; + dimensionName: string; + dimensionWeight: number; + indicators: TemplateIndicator[]; + [key: string]: any; +} + +/** + * 模板指标 + */ +export interface TemplateIndicator { + id: string; + indicatorName: string; + indicatorWeight: number; + [key: string]: any; +} + +/** + * 新增模板请求 + */ +export interface TemplateAddRequest { + templateName: string; + dimensions: { + dimensionName: string; + dimensionWeight: number; + indicators: { + indicatorName: string; + indicatorWeight: number; + }[]; + }[]; +} + +/** + * 更新模板请求 + */ +export interface TemplateUpdateRequest { + id: string; + templateName: string; + dimensions: { + id?: string; + dimensionName: string; + dimensionWeight: number; + indicators: { + id?: string; + indicatorName: string; + indicatorWeight: number; + }[]; + }[]; +} + +// 品类相关类型 +/** + * 品类树响应 + */ +export interface CategoryTreeResponse { + code: number; + success: boolean; + data: CategoryTreeItem[]; + message: string; +} + +/** + * 品类树节点 + */ +export interface CategoryTreeItem { + id: string; + name: string; + children?: CategoryTreeItem[]; + [key: string]: any; +} + +// 任务相关类型 +/** + * 任务查询请求 + */ + +// 供应商评价任务记录类型 +export interface TaskRecord { + id: string; + taskName: string; + taskCode: string; + taskType: string; + templateName: string; + status: string; + startTime: string; + endTime: string; + createBy: string; + createTime: string; + updateBy?: string; + updateTime?: string; + key?: string; +} +export interface TaskRequest { + current?: number; + pageSize?: number; + evaluateTheme?: string; + evaluateYear?: string; + startTime?: string; + endTime?: string; + categoryId?: string; + status?: string; +} +// 供应商评价任务查询参数 +export interface TaskSearchParams { + basePageRequest: API.BasePageRequest; + evaluateTheme?: string; + status?: string; + dateRange?: string[]; + startTime?: string; + endTime?: string; +} + +/** + * 任务查询响应 + */ +export interface TaskResponse { + code: number; + success: boolean; + data: { + records: TaskItem[]; + total: number; + size: number; + current: number; + pages: number; + }; + message: string; +} + +/** + * 任务项 + */ +export interface TaskItem { + id: string; + evaluateTheme: string; + evaluateYear: string; + startTime: string; + endTime: string; + templateId: string; + templateName: string; + status: string; + categoryId?: string; + categoryName?: string; + createTime: string; + createBy: string; + [key: string]: any; +} + + +// 评价结果相关类型 +/** + * 评价任务请求 + */ +export interface EvaluateTaskRequest { + current?: number; + pageSize?: number; + evaluateTheme?: string; + evaluateYear?: string; + startTime?: string; + endTime?: string; + status?: string; +} + +/** + * 评价任务数据 + */ +export interface EvaluateTaskData { + records: EvaluateTaskItem[]; + total: number; + size: number; + current: number; + pages: number; +} + +/** + * 评价任务项 + */ +export interface EvaluateTaskItem { + id: string; + evaluateTheme: string; + evaluateYear: string; + startTime: string; + endTime: string; + templateId: string; + templateName: string; + status: string; + createTime: string; + createBy: string; + supplierCount: number; + [key: string]: any; +} + +/** + * 评价供应商请求 + */ +export interface EvaluateSupplierRequest { + current?: number; + pageSize?: number; + taskId: string; + supplierName?: string; + status?: string; +} + +/** + * 评价供应商数据 + */ +export interface EvaluateSupplierData { + records: EvaluateSupplierItem[]; + total: number; + size: number; + current: number; + pages: number; +} + +/** + * 评价供应商项 + */ +export interface EvaluateSupplierItem { + id: string; + supplierId: string; + supplierName: string; + socialCreditCode: string; + status: string; + score: number; + level: string; + [key: string]: any; +} + +/** + * 评价分数请求 + */ +export interface EvaluateScoreRequest { + current?: number; + pageSize?: number; + taskId: string; + supplierId: string; +} + +/** + * 评价分数详情数据 + */ +export interface EvaluateScoreDetailData { + id: string; + supplierId: string; + supplierName: string; + taskId: string; + evaluateTheme: string; + evaluateYear: string; + dimensions: EvaluateDimension[]; + userScore: number; + totalScore: number; + level: string; + [key: string]: any; +} + +/** + * 评价维度 + */ +export interface EvaluateDimension { + id: string; + dimensionName: string; + dimensionWeight: number; + dimensionScore: number; + indicators: EvaluateIndicator[]; + [key: string]: any; +} + +/** + * 评价指标 + */ +export interface EvaluateIndicator { + id: string; + indicatorName: string; + indicatorWeight: number; + score: number; + remarks: string; + [key: string]: any; +} + +// 评价规则相关类型 +/** + * 评价规则响应 + */ +export interface EvaluateRuleResponse { + code: number; + success: boolean; + data: EvaluateRuleItem[]; + message: string; +} + +/** + * 评价规则项 + */ +export interface EvaluateRuleItem { + id: string; + levelName: string; + minScore: number; + maxScore: number; + status: string; + createTime: string; + updateTime: string; + createBy: string; + updateBy: string; + [key: string]: any; +} + +/** + * 评价规则详情响应 + */ +export interface EvaluateRuleDetailResponse { + code: number; + success: boolean; + data: EvaluateRuleItem; + message: string; +} + +/** + * 评价规则新增请求 + */ +export interface EvaluateRuleAddRequest { + levelName: string; + minScore: number; + maxScore: number; +} + +/** + * 评价规则更新请求 + */ +export interface EvaluateRuleUpdateRequest { + id: string; + levelName: string; + minScore: number; + maxScore: number; +} + +/** + * 供应商评价任务详情接口类型定义 + */ +export interface IndicatorItem { + indicatorIds: string[]; + type: number; + userId: string; +} + +/* +人员信息 +*/ +export interface PersonnelItem { + id: string; + name: string; + userId: string; + userName: string; + position: string; + userDept: string; + userDeptId: string; + indicatorIds?: string[]; +} +/** + * 任务详情数据 + */ +export interface TaskDetailData { + categoryId: string | null; + categoryLimitation: string | null; + createBy: string | null; + createDate: string | null; + createTime: string | null; + deleteFlag: string | null; + delFlag: string; + deptId: string | null; + endTime: string | null; + evaluateTheme: string | null; + evaluateYear: string | null; + id: string | null; + indicatorList: IndicatorItem[] | null; + lastUpdateTime: string | null; + startTime: string | null; + status: string; + supplierIds: { + id: string; + userIds: string[]; + }[] | null; + userList: PersonnelItem[]; + suppliers: { + id: string; + supplierName: string; + socialCreditCode?: string; + category?: string; + department?: string; + evaluators: { + id: string; + name: string; + department: string; + position?: string; + }[]; + [key: string]: any; + }[] | null; + templateId: string | null; + tenantId: string | null; + tenantName: string | null; + updateBy: string | null; + updateDate: string | null; + updateTime: string | null; + weightDept: string | null; + weightStatus: number | null; + weightValue: string | null; + taskDeptWeightList: DeptWeightItem[] | null; + blackSupplierVos: { + deptName: string; + supplierId: string; + supplierName: string; + userList: PersonnelItem[]; + }[], + [property: string]: any; +} +// 部门配置权重 +export interface DeptWeightItem { + //部门id + weightDept: string; + // 部门权重值 + weightValue: string; + // 部门名称 + weightDeptName: string; +} +/** + * 任务详情响应 + */ +export interface TaskDetailResponse { + code: number; + data: TaskDetailData; + message: string; + success: boolean; + [property: string]: any; +} + +// 新增评价任务和修改 请求参数定义 +export type TaskAddRequest = { + id?: string; + /** + * 品类限制类型(0.通用不限品类、1.限制品类) + */ + categoryLimitation?: string; + /** + * 评价结束时间 + */ + endTime: string; + /** + * 评价主题 + */ + evaluateTheme: string; + /** + * 评价年度 + */ + evaluateYear: string; + indicatorList: IndicatorItem[]; + /** + * 评价开始时间 + */ + startTime: string; + supplierIds: SupplierId[]; + /** + * 评价表模板id(cosco_evaluate_template表主键) + */ + templateId: string; + // 品类id + categoryId?: string; + /** + * 是否下级部门可以添加评价信息 0否1是 + */ + taskStatus?: TaskNotifyLowerUnits | null; + /* + 供应商列表 + */ + selectedSuppliers?: SupplierItem[]; + userList?: PersonnelItem[]; +} +/** + * 供应商项 + */ +export interface SupplierItem { + id: string; // 供应商ID + supplierName: string; // 供应商名称 + supplierId?: string; // 供应商ID + socialCreditCode?: string; // 统一社会信用代码 + category?: string; // 品类 + department?: string; // 准入部门 + evaluatorCount?: number; // 评价人员数量 + evaluators: PersonnelItem[]; // 评价人员列表 + deptName?: string; // 部门名称 + // 其他可能的字段 + [key: string]: any; +} +export type SupplierId = { + /** + * 供应商id + */ + id: string; + /** + * 用户集合 + */ + userIds?: string[]; +} diff --git a/src/utils/componentStyle.less b/src/utils/componentStyle.less index 822bc6d..a8088db 100644 --- a/src/utils/componentStyle.less +++ b/src/utils/componentStyle.less @@ -52,6 +52,9 @@ flex-wrap: wrap; gap: 10px; max-width: 100%; + .ant-form-item{ + margin-bottom: 0; + } .filter-btns { button {