供应商退出、准入、 工作台

This commit is contained in:
孙景学
2025-06-27 10:41:33 +08:00
parent 527637cce3
commit 4d54b36a16
40 changed files with 3746 additions and 1631 deletions

View File

@ -4,6 +4,7 @@ import { reviewInfo, update } from '../services';
interface ResultModalProps {
visible: boolean;
view: boolean;
record?: { id?: string; [key: string]: any } | null;
onCancel: () => void;
onSubmit: () => void;
@ -42,6 +43,7 @@ const RemarkViewModal: React.FC<{
const GroupLeaderModal: React.FC<ResultModalProps> = ({
visible,
record,
view,
onCancel,
onSubmit,
}) => {
@ -49,7 +51,7 @@ const GroupLeaderModal: React.FC<ResultModalProps> = ({
const [suppliers, setSuppliers] = useState<any[]>([]);
const [items, setItems] = useState<any[]>([]);
// groupSummaryResult: { [supplierId]: 0|1 }
const [groupSummaryResult, setGroupSummaryResult] = useState<{ [k: string]: 0 | 1 | undefined }>({});
const [groupSummaryResult, setGroupSummaryResult] = useState<{ [k: string]: '0' | '1' | undefined }>({});
// 查看备注弹窗
const [remarkModal, setRemarkModal] = useState({ open: false, remark: '', file: undefined as any });
@ -66,11 +68,12 @@ const GroupLeaderModal: React.FC<ResultModalProps> = ({
// 非summary项
setItems(allItems.filter((item: any) => item.itemType !== 'summary'));
// summary 行初始化
const summaryMap: { [k: string]: 0 | 1 | undefined } = {};
const summaryMap: { [k: string]: '0' | '1' | undefined } = {};
supplierList.forEach((sup: any) => {
// summary 行
const summaryItem = (sup.coscoAccessItemList || []).find((i: any) => i.itemType === 'summary');
summaryMap[sup.supplierId] = summaryItem?.reviewResult;
// summaryMap[sup.supplierId] = summaryItem?.reviewResult;
summaryMap[sup.supplierId] = summaryItem.coscoAccessUserItemList[0]?.reviewResult;
});
setGroupSummaryResult(summaryMap);
})
@ -83,7 +86,7 @@ const GroupLeaderModal: React.FC<ResultModalProps> = ({
}, [visible, record]);
// 处理summary下拉变更
const handleSummaryChange = (supplierId: string, value: 0 | 1) => {
const handleSummaryChange = (supplierId: string, value: '0' | '1') => {
setGroupSummaryResult(prev => ({
...prev,
[supplierId]: value,
@ -217,16 +220,21 @@ const GroupLeaderModal: React.FC<ResultModalProps> = ({
const colSpan = reviewerSet.size || 1;
return (
<Table.Summary.Cell index={index} key={sup.supplierId} colSpan={colSpan} align="center">
<Select
style={{ width: 120 }}
value={groupSummaryResult[sup.supplierId]}
placeholder="请选择"
onChange={val => handleSummaryChange(sup.supplierId, val)}
options={[
{ label: '合格', value: 0 },
{ label: '不合格', value: 1 }
]}
/>
{view && (
<span style={{color: groupSummaryResult[sup.supplierId] === '0'? '#52c41a': '#f5222d' }}> {groupSummaryResult[sup.supplierId] === '0'? '合格': '不合格'}</span>
)}
{ !view && (
<Select
style={{ width: 120 }}
value={groupSummaryResult[sup.supplierId]}
placeholder="请选择"
onChange={val => handleSummaryChange(sup.supplierId, val)}
options={[
{ label: '合格', value: '0' },
{ label: '不合格', value: '1' }
]}
/>
)}
</Table.Summary.Cell>
)
})}
@ -239,8 +247,14 @@ const GroupLeaderModal: React.FC<ResultModalProps> = ({
visible={visible}
onCancel={onCancel}
footer={[
<Button key="cancel" onClick={onCancel}></Button>,
<Button key="submit" type="primary" onClick={handleSubmit}></Button>
<Button key="cancel" onClick={onCancel}>
{view ? '关闭' : '取消'}
</Button>,
!view && (
<Button key="submit" type="primary" onClick={handleSubmit}>
</Button>
)
]}
width={1000}
bodyStyle={{ maxHeight: '60vh', overflowY: 'auto' }}

View File

@ -5,6 +5,7 @@ import type { ColumnsType } from 'antd/es/table';
//主体参数接口
interface ResultModalProps {
visible: boolean;
view: boolean;
record?: { id?: string; [key: string]: any } | null;
onCancel: () => void;
onSubmit: () => void;
@ -15,13 +16,46 @@ interface RowData {
// 其他如有
}
type CellValue = {
reviewResult?: 0 | 1;
reviewResult?: '0' | '1';
remark?: string;
file?: any;
};
// 只读备注弹窗
const RemarkViewModal: React.FC<{
visible: boolean;
onCancel: () => void;
remark: string;
file?: any;
}> = ({ visible, onCancel, remark, file }) => (
<Modal
visible={visible}
title="备注信息"
footer={null}
onCancel={onCancel}
destroyOnClose
>
<div>
<div style={{ marginBottom: 12 }}>
<b></b>{remark || '无'}
</div>
{file && file.fileUrl && (
<div>
<b></b>
<a href={file.fileUrl} target="_blank" rel="noopener noreferrer">
{file.fileName}
</a>
</div>
)}
</div>
</Modal>
);
//主体
const ResultModal: React.FC<ResultModalProps> = ({
visible,
view,
record,
onCancel,
onSubmit
@ -34,7 +68,8 @@ const ResultModal: React.FC<ResultModalProps> = ({
// userItemId为唯一key
const [cellData, setCellData] = useState<{ [userItemId: string]: CellValue }>({});
const [loading, setLoading] = useState(false);
// 查看备注弹窗
const [remarkModal, setRemarkModal] = useState({ open: false, remark: '', file: undefined as any });
// 备注弹窗
const [remarksModalVisible, setRemarksModalVisible] = useState(false);
const [remarks, setRemarks] = useState('');
@ -98,7 +133,7 @@ const ResultModal: React.FC<ResultModalProps> = ({
...prev,
[userItemId]: {
...prev[userItemId],
reviewResult: value === '合格' ? 0 : 1
reviewResult: value === '合格' ? '0' : '1'
}
}));
};
@ -185,7 +220,7 @@ const ResultModal: React.FC<ResultModalProps> = ({
accessWorkId = record?.id
}
// 提交审核
update( {coscoAccessUserItemList: result, accessWorkId }).then((res) => {
update( {coscoAccessUserItemList: result, accessWorkId }).then((res) => {
if(res.code == 200) {
message.success('提交成功');
onSubmit()
@ -212,32 +247,48 @@ const ResultModal: React.FC<ResultModalProps> = ({
if (!userItem) return null; // 无userItem不渲染
const v = cellData[userItem.id] || {};
return (
<div>
<Radio.Group
value={
v.reviewResult === 0
? '合格'
: v.reviewResult === 1
? '不合格'
: undefined
}
onChange={e => handleRadioChange(userItem.id, e.target.value)}
>
<Radio value="合格"></Radio>
<Radio value="不合格"></Radio>
</Radio.Group>
<Button type="link" onClick={() => openRemarksModal(userItem.id)}>
</Button>
{v.remark && (
<span style={{ color: '#aaa', fontSize: 12 }}></span>
<>
{view && (
<>
<span style={{color: v.reviewResult === '0'? '#52c41a': '#f5222d' }}> {v.reviewResult === '0'? '合格': '不合格'}</span>
<Button type="link" size="small" onClick={() =>
setRemarkModal({ open: true, remark: v.remark || '', file: v.file })
}></Button>
</>
)}
{v.file && (
<span style={{ color: '#52c41a', fontSize: 12, marginLeft: 8 }}>
</span>
{ !view && (
<div>
<Radio.Group
value={
v.reviewResult === '0'
? '合格'
: v.reviewResult === '1'
? '不合格'
: undefined
}
onChange={e => handleRadioChange(userItem.id, e.target.value)}
>
<Radio value="合格"></Radio>
<Radio value="不合格"></Radio>
</Radio.Group>
<Button type="link" onClick={() => openRemarksModal(userItem.id)}>
</Button>
{v.remark && (
<span style={{ color: '#aaa', fontSize: 12 }}></span>
)}
{v.file && (
<span style={{ color: '#52c41a', fontSize: 12, marginLeft: 8 }}>
</span>
)}
</div>
)}
</div>
</>
);
}
}))
@ -250,17 +301,20 @@ const ResultModal: React.FC<ResultModalProps> = ({
}));
return (
<>
<Modal
title="评审结果"
visible={visible}
onCancel={onCancel}
footer={[
<Button key="cancel" onClick={onCancel}>
{view ? '关闭' : '取消'}
</Button>,
<Button key="submit" type="primary" onClick={handleSubmit}>
</Button>
!view && (
<Button key="submit" type="primary" onClick={handleSubmit}>
</Button>
)
]}
width={1000}
bodyStyle={{ maxHeight: '60vh', overflowY: 'auto' }}
@ -301,6 +355,13 @@ const ResultModal: React.FC<ResultModalProps> = ({
</Upload>
</Modal>
</Modal>
<RemarkViewModal
visible={remarkModal.open}
onCancel={() => setRemarkModal({ open: false, remark: '', file: undefined })}
remark={remarkModal.remark}
file={remarkModal.file}
/>
</>
);
};

View File

@ -0,0 +1,86 @@
import React, { useState, useEffect } from 'react';
import { Modal, Descriptions } from 'antd';
import { coscoAccessWork } from '../services'
//数据接口
interface Data {
coscoAccessWork: coscoAccessWorks;
coscoAccessSupplierList: coscoAccessSupplierLists[];
coscoAccessCategoryList: coscoAccessCategoryLists[];
coscoAccessUserls: coscoAccessUserl[];
}
interface coscoAccessUserl {
deptId: string;
userId: string;
}
interface coscoAccessCategoryLists {
categoryName: string;
[property: string]: any;
}
interface coscoAccessSupplierLists {
supplierName: string;
[property: string]: any;
}
interface coscoAccessWorks {
deptId: string;
startTime: string;
endTime: string;
reviewStatusText: string;
}
const ViewModal: React.FC<{
visible: boolean;
record?: any;
onCancel: () => void;
}> = ({ visible, record = {}, onCancel }) => {
//渲染数据
const [data, setData] = useState<Data | null>(null);
//初始化
useEffect(() => {
if (record.id) {
coscoAccessWork(record.id).then((res) => {
const { code, data } = res;
if (code == 200) {
setData(data)
}
})
}
}, [record])
return (
<Modal title="查看详情" visible={visible} footer={null} onCancel={onCancel}>
{data && (
<Descriptions bordered column={1}>
<Descriptions.Item label="准入部门">{data.coscoAccessWork.deptId}</Descriptions.Item>
<Descriptions.Item label="准入供应商">
{data.coscoAccessSupplierList.map((item) => {
return (
<div style={{ margin: '5px' }}>{item.supplierName}</div>
)
})}
</Descriptions.Item>
<Descriptions.Item label="申请准入品类">
{data.coscoAccessCategoryList.map((item) => {
return (
<div style={{ margin: '5px' }}>{item.categoryName}</div>
)
})}
</Descriptions.Item>
<Descriptions.Item label="评审开始时间">{data.coscoAccessWork.startTime}</Descriptions.Item>
<Descriptions.Item label="评审结束时间">{data.coscoAccessWork.endTime}</Descriptions.Item>
<Descriptions.Item label="评审专家">
{data.coscoAccessUserls.map((item) => {
return (
<div style={{ margin: '5px' }}>{item.deptId} - {item.userId}</div>
)
})}
</Descriptions.Item>
<Descriptions.Item label="审批结果">{data.coscoAccessWork.reviewStatusText}</Descriptions.Item>
</Descriptions>
)}
</Modal>
);
};
export default ViewModal;

View File

@ -7,6 +7,7 @@ import { getPage } from './services';
//查看评审结果 弹窗
import ResultModal from './components/ResultModal';
import GroupLeaderModal from './components/GroupLeaderModal';
import ViewModal from './components/ViewModal';
interface Data {
deptName: string;
@ -17,26 +18,26 @@ interface Data {
}
interface ModalInfo {
type: 'teamMembers' | 'groupLeader' | null;
type: 'teamMembers' | 'groupLeader' | 'view' | null;
visible: boolean;
record: Data | null;
view: boolean;
}
const CooperateEnterprise: React.FC = () => {
const [searchForm] = Form.useForm();
const intl = useIntl();
const [data, setData] = useState<Data[]>([]);
const [loading, setLoading] = useState(false);
const [pagination, setPagination] = useState<TablePaginationConfig>({ current: 1, pageSize: 10, total: 0 });
const [modalInfo, setModalInfo] = useState<ModalInfo>({ type: null, visible: false, record: null });
const [modalInfo, setModalInfo] = useState<ModalInfo>({ type: null, visible: false, record: null, view:false });
const openModal = (type: 'teamMembers' | 'groupLeader', record: Data) => {
setModalInfo({ type, visible: true, record });
const openModal = (type: 'teamMembers' | 'groupLeader' | 'view', record: Data, view = false ) => {
setModalInfo({ type, visible: true, record, view });
};
//提交关闭审核
const closeModal = () => {
setModalInfo({ type: null, visible: false, record: null });
setModalInfo({ type: null, visible: false, record: null, view:false });
};
//提交审核
const submitModal = () => {
@ -121,19 +122,30 @@ const CooperateEnterprise: React.FC = () => {
title: '评审状态',
dataIndex: 'reviewStatusText',
key: 'reviewStatusText',
width: 120,
},
{
title: '操作',
width: 120,
render: (_: any, record: any) => {
// 已完成 3 、结果汇总中 2 、进行中1 、 未开始0
// 显示评审 按钮
const showAudit = (
(['未开始', '进行中'].includes(record.reviewStatusText) && record.isLeader === '0') ||
(['结果汇总中'].includes(record.reviewStatusText) && record.isLeader === '1')
(['0', '1'].includes(record.reviewStatus) && record.isLeader === '0') ||
(['2'].includes(record.reviewStatus) && record.isLeader === '1')
);
const type = ['未开始', '进行中'].includes(record.reviewStatusText)? 'teamMembers': 'groupLeader';
// 进行中1 、未开始0 弹出组员组件 否则弹出组长组件
const type = ['0', '1'].includes(record.reviewStatus)? 'teamMembers': 'groupLeader';
//评审结果 结果汇总中2 弹出组员组件, 已完成3弹出组长组件
const isLeader = (record.reviewStatus === '2' && record.isLeader === '0')? 'teamMembers':
(record.reviewStatus === '3' && record.isLeader === '1')? 'groupLeader':'';
return (
<Space>
{showAudit && <a onClick={() => openModal(type, record)}></a>}
</Space>
{showAudit && <a onClick={() => openModal(type, record)}></a>}
<a onClick={() => openModal('view', record)}></a>
{ isLeader != '' && <a onClick={() => openModal(isLeader, record, true)}></a>}
</Space>
)
},
@ -171,6 +183,7 @@ const CooperateEnterprise: React.FC = () => {
{ modalInfo.type && modalInfo.type === 'teamMembers' && (
<ResultModal
visible={modalInfo.visible}
view={modalInfo.view}
record={modalInfo.record}
onCancel={closeModal}
onSubmit={submitModal}
@ -179,11 +192,19 @@ const CooperateEnterprise: React.FC = () => {
{ modalInfo.type && modalInfo.type === 'groupLeader' && (
<GroupLeaderModal
visible={modalInfo.visible}
view={modalInfo.view}
record={modalInfo.record}
onCancel={closeModal}
onSubmit={submitModal}
/>
)}
{ modalInfo.type && modalInfo.type === 'view' && (
<ViewModal
visible={modalInfo.visible}
record={modalInfo.record}
onCancel={closeModal}
/>
)}
</>
);

View File

@ -33,7 +33,10 @@ export const uploadFile = async (file: File) => {
});
};
/**
* 供应商准入管理详情
*/
export const coscoAccessWork = (id: string) => request.get(`/coscoAccessWork/${id}`);
/**
* 组员评审
*/