Files
fe_service_ebtp_frontend/src/pages/Tender/UploadResponse/index.tsx
2025-07-31 16:07:35 +08:00

654 lines
24 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState, useEffect, useRef } from 'react';
import { Button, Table, Modal, Collapse, message, Row, Col, Tag } from 'antd';
import ReactResumableJs from '@/components/Upload/react-resumable'
import { getUpload, withdrawTfile, getReceiptList, getDecrypt, getCodeInfo, saveUploadLog, getSupplierBlack, getCurrentTime, getTendererFileStatus } from './service';
import '@/assets/ld_style.less';
import { getProId, getProMethod, getSessionProjectData, getSessionUserData } from '@/utils/session';
import FileDown from '@/utils/Download';
import { getURLInformation } from '@/utils/CommonUtils';
const { Panel } = Collapse;
import { btnAuthority } from '@/utils/authority';
import { verificationSupplier } from '@/utils/IpassVerification'
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { getStatusByProId } from '@/services/downLoad';
import SupplierCommitment from './components/SupplierCommitment';
const { confirm } = Modal;
function guid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
let switchBtn: boolean = false; // 控制调取刘昊接口状态
let currentDate: any = {};
const Index: React.FC<{}> = () => {
let timeInteval: any;
let task: any;
const projectId = getProId()
const savedCallback = useRef<any>();
const [dateList, setDateList] = useState([]);
const [receiptList, setReceiptList] = useState([]);
const [uploadVisible, setUploadVisible] = useState<boolean>(false);
const [withdrawVisible, setWithdrawVisible] = useState<boolean>(false);
const [receiptVisible, setReceiptVisible] = useState<boolean>(false);
const [timestamp, setTimestamp] = useState<any>(0); // 时间戳
const [subsectionId, setSubsectionId] = useState<any>(); // 分段id
const [Tfile, setTfile] = useState<any>({ id: '1' }); // 上传文件参数
const [filePath, setFilePath] = useState<any>(); // 上传文件路径
const [fileT, setFileT] = useState<any>();
const [useIpass, setUseIpass] = useState<any>();
const [iPASSCode, setIpassCode] = useState<any>();
const bidMethodDict = getProMethod(); // 取采购方式
const projectType = getURLInformation('roomType');
const modalHeight = (innerHeight * 96) / 100;
const [endTime, setEndTime] = useState<number>(0); // 应答截止时间
const [uuid, setUuid] = useState<any>(); // 生成uuid给上传组件和验证接口
const [supplierStatus, setSupplierStatus] = useState<boolean>(false); // 供应商资质库显示状态
const [isModalVisible, setIsModalVisible] = useState<boolean>(false);
const [timeDateList, setTimeDateList] = useState([]);
const [currentDateList, setCurrentDateList] = useState<any>();
const [isInit, setIsInit] = useState<boolean>(false);//是否初始化列表-供应商承诺书用
const columns: any[] = [ // 列表数据
{
title: '序号',
width: '10%',
render: (text: any, record: any, index: any) => `${index + 1}`
},
{
title: '分段名称',
width: '25%',
dataIndex: 'fileCategory',
render: (_: any, record: any) => {
if (record.fileCategory === 0) {
return (<></>)
} else if (record.fileCategory === 1) {
return (<></>)
} else if (record.fileCategory === 2) {
return (<></>)
} else if (record.fileCategory === 3) {
return (<></>)
} else if (record.fileCategory === 5) {
return (<></>)
} else {
return (<></>)
}
}
},
{
title: '应答状态',
width: '15%',
dataIndex: 'status',
render: (_: any, record: any) => {
if (record.status === 1) {
return (<></>)
} else {
return (<></>)
}
}
},
{
title: '应答时间',
width: '20%',
render: (text: any, record: any) => {
if (record.status == 1) {
return (
<>{record.operateTime}</>
)
} else {
return (
<></>
)
}
}
},
{
title: '操作',
width: '30%',
render: (text: any, record: any) => {
let end: any = new Date(record.endDate.replaceAll("-", "/")).getTime()
let start: any = new Date(record.startDate.replaceAll("-", "/")).getTime()
if (end > timestamp && record.status != 1 && timestamp > start && record.openState == '1') {
return (
<>
<Button hidden={btnAuthority(['ebtp-supplier'])} type="link" danger onClick={() => { batchUpload(record, 'single') }}></Button>
</>
)
} else if (end > timestamp && record.status == null && timestamp > start && record.openState == '1') {
return (
<>
<Button hidden={btnAuthority(['ebtp-supplier'])} type="link" danger onClick={() => { batchUpload(record, 'single') }}></Button>
</>
)
}
else if (end > timestamp && record.status == 1 && record.openState == '1') {
return (
<>
<Button hidden={btnAuthority(['ebtp-supplier'])} type="link" danger onClick={() => submitWithdraw(record.id)} ></Button>
<Button hidden={btnAuthority(['ebtp-supplier'])} type="link" danger onClick={() => { lookReceipt(record.id) }}></Button>
</>
)
} else if (end < timestamp && record.status == 1) {
return (
<>
<Button hidden={btnAuthority(['ebtp-supplier'])} type="link" danger onClick={() => { lookReceipt(record.id) }}></Button>
</>
)
} else if (end > timestamp && record.status == 1 && record.openState == '0') {
return (
<>
<Button hidden={btnAuthority(['ebtp-supplier'])} type="link" danger onClick={() => { lookReceipt(record.id) }}></Button>
</>
)
}
},
},
];
const receiptColumns: any[] = [ // 回执列表数据
{
title: '序号',
render: (text: any, record: any, index: any) => `${index + 1}`
},
{
title: '回执类别',
dataIndex: 'status',
render: (_: any, record: any) => {
if (record.status === 0) {
return (<></>)
} else if (record.status === 1) {
return (<></>)
}
}
},
{
title: '回执时间',
dataIndex: 'createDate',
},
{
title: '操作',
render: (text: any, record: any, index: any) => (
<FileDown fileId={record.fileId} fileName={'回执文件' + (index + 1)} type='pdf' method='GET' btnName='回执导出' />
),
},
];
const submitWithdraw = (id: any) => { // 撤回
setWithdrawVisible(true)
setSubsectionId(id)
}
const getWithdraw = () => { // 确定撤回
withdrawTfile(subsectionId).then((res) => {
if (res.code == 200) {
setWithdrawVisible(false)
getList()
}
})
}
const lookReceipt = (id: any) => { // 查看回执
setReceiptVisible(true)
const data = {
relId: id
}
getReceiptList(data).then((res) => {
if (res.code == 200) {
setReceiptList(res.data)
}
})
}
/**
* 根据项目id获取供应商资质库信息引用状态
* @param id 项目id
* @returns true-有供应商资质库信息 false-无供应商资质库信息
*/
const getSupplierStatus = async (id: string) => {
await getStatusByProId({ id: id }).then(res => {
if (res?.code == 200) {
setSupplierStatus(res?.data)
}
})
}
const showConfirm = () => {
confirm({
title: <>
<p>使megp文件--</p>
<p style={(getSessionProjectData().isIPassFile == '1') && !supplierStatus ? { marginBottom: 0 } : {}}>便</p>
<p style={(getSessionProjectData().isIPassFile == '1') ? { color: "#b30000", marginBottom: 0 } : { color: "#b30000" }} hidden={!supplierStatus}>---</p>
<p style={{ color: "#b30000" }} hidden={getSessionProjectData().isIPassFile == '1' ? true : false}>iPASS</p>
<p style={{ color: "#b30000", marginBottom: 0 }} hidden={getSessionProjectData().isIPassFile == '1' ? true : false}>iPASS证书中维护的组织机构代码证号一致,4000560010-1iPASS更新 </p>
</>,
icon: <ExclamationCircleOutlined />,
width: supplierStatus || (getSessionProjectData().isIPassFile != '1') ? 600 : 416,
okText: '知道了',
centered: true,
cancelButtonProps: {
className: 'display-cancel',
},
onOk() {
if (START_ENV == 'UAT' || START_ENV == 'DEV' || START_ENV == 'sim') {
setUploadVisible(true)
} else {
if (useIpass == '0') {
if (verificationSupplier(iPASSCode)) {
setUploadVisible(true)
}
} else {
setUploadVisible(true)
}
}
},
});
}
const setOk = () => {
setIsModalVisible(false)
showConfirm()
}
const batchUpload = (val: any, index: any) => { // 上传
let deptIdList: any = []
deptIdList.push(getSessionUserData().deptId)
let date = {
"tpId": getProId(),
"ids": deptIdList
}
getSupplierBlack(date).then((res) => {
if (res.code == 200) {
if (res?.data[getSessionUserData().deptId] == true) {
setIsModalVisible(true)
} else {
showConfirm()
}
}
})
setUuid(guid())
let path, object, filetype
if (index == 'single') {
currentDate = val
setEndTime(new Date(val.endDate).getTime())
filetype = '.file'
// filetype = '.rar,.zip,.doc,.docx,.xls,.xlsx,.pdf'
object = JSON.stringify(val)
path = '/' + val.createYear + '/' + val.tpId + '/' + val.sectionId + '/' + val.tdocId + '/uploadFile/' + val.tendererId
} else {
currentDate = dateList[index].tfileList[0]
currentDate.tdocCatalogId = ""
setEndTime(new Date(dateList[index].endDate).getTime())
filetype = '.zip'
// filetype = '.rar,.zip'
object = JSON.stringify(dateList[index].tfileList[0])
path = '/' + dateList[index].createYear + '/' + dateList[index].tpId + '/' + dateList[index].sectionId + '/' + dateList[index].id + '/uploadFile/' + dateList[index].tfileList[0].tendererId
}
setTfile(object)
setFilePath(path)
setFileT(filetype)
}
const getCode = (id: any) => { // 获取iPASS码
let idArr = []
idArr.push(id)
getCodeInfo(idArr).then((res) => {
if (res.code == 200) {
if (res?.data[0]) {
setIpassCode(res.data[0].organizationCode)
}
}
})
}
const closeModal = () => { // 关闭上传
if (!switchBtn) {
setUploadVisible(false)
getList()
} else {
message.warn('请上传完附件再关闭弹窗!')
}
}
/**
* 大文件上传完成后的组件返回
*
* @param file
* @param resumable
* @param callback
*/
const onUploadSuccess = (file: any, cache: any, callback: any) => {
cache.push({
key: cache.length + 1,
filename: "文件验证中",
uploading: false,// 这不是文件上传作业
prograss: 0,
});
// TODO 下面演示一下接口如何调用
let test = 0;
switchBtn = true;
let num = 0;
task = setInterval(() => {
num += 1;
if (test < 80) {
test += 20;
} else if (test == 80) {
test = 80
} else if (test == 100) {
clearInterval(task);
}
if (num > 30) {
callback(cache.length - 1, "文件验证失败", 2);
message.error('缺少文件验证信息,请刷新页面后重新上传!')
switchBtn = false
clearInterval(task);
} else {
if (switchBtn) {
setTimeout(() => {
message.destroy()
}, 3000)
test = 100;
callback(cache.length - 1, "文件验证成功", test);
switchBtn = false;
clearInterval(task);
return;
// getDecrypt(uuid).then((res) => {
// if (res.code == 200) {
// if (res.data.consumptionState == '2' || res.data.consumptionState == '4') {
// if (res.data.stateMessage) {
// message.error(res.data.stateMessage)
// callback(cache.length - 1, res.data.stateMessage, 2);
// } else {
// message.error('验证错误,请刷新页面后重新上传!')
// callback(cache.length - 1, "文件验证失败", 2);
// }
// switchBtn = false;
// clearInterval(task);
// return;
// } else if (res.data.consumptionState == '3') {
// message.success(res.data.stateMessage)
// setTimeout(() => {
// message.destroy()
// }, 3000)
// test = 100;
// callback(cache.length - 1, "文件验证成功", test);
// switchBtn = false;
// clearInterval(task);
// return;
// } else {
// callback(cache.length - 1, "文件验证中", test);
// }
// }
// })
}
}
}, 3000);
}
const onFileAdded = (file: any, resumable: any) => { // 上传附件
switchBtn = true
let date = {
"message": Tfile + file.name
}
saveUploadLog(date).then((res) => {
if (res.code == 200) {
}
})
}
const onPauseUpload = (file: any, resumable: any) => { // 上传附件暂停
switchBtn = false
}
const onResumeUpload = (file: any, resumable: any) => { // 上传附件恢复
switchBtn = true
}
const onCancelUpload = (file: any, resumable: any) => { // 上传附件
// switchBtn = true
}
/**
* 大文件上传失败后的组件返回
*/
const onUploadError = (resumable: any) => {
if (resumable == '超过截止时间') {
message.error('已超过截止时间')
} else if (resumable == '撤销后再上传') {
message.error('您已上传应答文件,需撤销后再上传!')
setUploadVisible(false)
getList()
} else {
let strIndex = resumable.fileName.lastIndexOf(".")
let fileSuffix = resumable.fileName.substring(strIndex + 1, resumable.fileName.length)
// 判断文件类型
const fileTypes = fileT.split(',').map((type: string) => type.trim().toLowerCase());
// if (('.' + fileSuffix) != fileT) {
if (!fileTypes.includes('.' + fileSuffix)) {
message.error('请上传' + fileT + '类型的文件')
} else if (fileSuffix == 'file' && resumable.file.size > 200 * 1024 * 1024) {
message.error('文件最大为200MB')
} else if (fileSuffix == 'zip' && resumable.file.size > 500 * 1024 * 1024) {
message.error('文件最大为500MB')
} else {
message.error('上传失败!')
}
}
switchBtn = false
}
const getCurrent = () => {
getCurrentTime().then((res) => {
if (res.code == 200) {
let currentTiem = new Date(res.data.replace(/-/g, '/')).getTime()
setTimestamp(currentTiem)
}
})
}
const getList = () => {
let data = {
roomType: projectType
}
getUpload(projectId, data).then((res) => {
if (res.code == 200) {
getSupplierStatus(projectId)
setDateList(res?.data)
setUseIpass(res?.data[0]?.useIpass)
getCode(res?.data[0]?.companyId)
}
})
}
const handleTimeInterval = () => { // 倒计时
if (dateList.length > 0 && timestamp > 0) {
setTimestamp(timestamp + 1000);
let newTimeDateList: any = [];
dateList.map((item: any, index: any) => {
if (item?.endDate && item?.endDate != "" && item?.endDate != null) {
newTimeDateList.push(showTimeDetail(item?.endDate));
}
})
setCurrentDateList(new Date(timestamp).toLocaleString())
setTimeDateList(newTimeDateList);
}
}
useEffect(() => {
savedCallback.current = handleTimeInterval;
});
useEffect(() => {
if (isInit) {
getList();
getCurrent();
timeInteval = setInterval(() => { // 倒计时
savedCallback.current();
}, 1000);
}
return () => {
clearInterval(timeInteval);
if (task) {
clearInterval(task)
}
}
}, [isInit]);
const showTimeDetail = (endDate: any) => {
let endtime = new Date(endDate.replace(/-/g, '/')); //定义结束时间
let lefttime = endtime.getTime() - timestamp; //距离结束时间的毫秒数
let returnResult = "";
if (lefttime > 0) {
let leftd = Math.floor(lefttime / (1000 * 60 * 60 * 24)); //计算天数
let lefth = Math.floor(lefttime / (1000 * 60 * 60) % 24); //计算小时数
let leftm = Math.floor(lefttime / (1000 * 60) % 60); //计算分钟数
let lefts = Math.floor(lefttime / 1000 % 60); //计算秒数
returnResult = "距离递交结束还有 " + leftd + "天" + lefth + "时" + leftm + "分" + lefts + "秒";
} else {
returnResult = "已超过截止时间";
}
return returnResult;
}
return (
<>
<div className="uploadResponse">
<div>
<div>
<Row>
<Col span="24">
<div className="first-title"></div>
</Col>
<Col span="24">
<Tag hidden={!supplierStatus} style={{ marginBottom: "10px", marginLeft: "20px" }} icon={<ExclamationCircleOutlined />} color="warning"> --- </Tag>
<Tag hidden={getSessionProjectData().isIPassFile == '1' ? true : false} style={{ marginBottom: "10px", marginLeft: "20px" }} icon={<ExclamationCircleOutlined />} color="warning"> iPASS</Tag>
<Tag hidden={bidMethodDict != 'procurement_mode_7'} style={{ marginBottom: "10px", marginLeft: "20px" }} icon={<ExclamationCircleOutlined />} color="warning"> </Tag>
</Col>
</Row>
</div>
</div>
{
dateList.map((item: any, index: any) => {
return (
<>
<Collapse defaultActiveKey={['0']}>
<Panel header={item.sectionName} key={index}>
<div className="table-mess">
<span className="f12 mess">{item.endDate}</span>
<span className="f12 mess">{currentDateList}</span>
<span className="tr f12 mess">
<span className="red">{timeDateList.length > 0 ? timeDateList[index] : null}</span>
{
item.state == '0' && timestamp > 0 && new Date(item.startDate.replaceAll("-", "/")).getTime() < timestamp && new Date(item.endDate.replaceAll("-", "/")).getTime() > timestamp ?
<Button
hidden={btnAuthority(['ebtp-supplier'])}
id={'uploadBtn' + index}
onClick={() => batchUpload('1', index)}
className="b-red" type="primary" size="small" danger></Button> : null
}
</span>
</div>
<div>
{
item.tfileList.map((val: any, index: any) => {
val.startDate = item.startDate
val.endDate = item.endDate
val.openTime = item.openTime
val.signDateTime = item.signDateTime
val.tpId = item.tpId
val.sectionId = item.sectionId
val.pathId = item.id
val.companyId = item.companyId
val.createYear = item.createYear
val.openState = item.openState
val.tendererName = item.tendererName
})
}
<Table size='small' pagination={false} columns={columns} dataSource={item.tfileList} />
{
item.state != '0' ? null : <p className="red">--</p>
}
</div>
</Panel>
</Collapse>
</>
)
})
}
</div>
{
uploadVisible ?
<Modal // 批量上传
title="文件上传"
visible={uploadVisible}
onCancel={closeModal}
width={800}
centered
style={{ maxHeight: modalHeight }}
bodyStyle={{ maxHeight: modalHeight - 107, overflowY: 'auto' }}
footer={[<Button onClick={() => closeModal()}></Button>]}
>
<ReactResumableJs
query={{ 'relativePath': filePath, 'Tfile': Tfile, 'Tuid': uuid }}
maxFiles={1}
fileAccept={fileT}
endTime={endTime}
currentDate={currentDate}
onUploadSuccess={onUploadSuccess.bind(this)}
onUploadError={onUploadError.bind(this)}
onFileAdded={(file: any, resumable: any) => { onFileAdded(file, resumable) }}
onPauseUpload={(file: any, resumable: any) => { onPauseUpload(file, resumable) }}
onResumeUpload={(file: any, resumable: any) => { onResumeUpload(file, resumable) }}
onCancelUpload={(file: any, resumable: any) => { onCancelUpload(file, resumable) }}
service="/api/core-service-ebtp-updownload/v1/hulk/upload"
/>
</Modal> : null
}
<Modal // 撤回
width={300}
centered
visible={withdrawVisible}
onCancel={() => setWithdrawVisible(false)}
onOk={() => getWithdraw()}
>
</Modal>
<Modal // 查看回执
title="回执文件列表"
width={800}
visible={receiptVisible}
onCancel={() => setReceiptVisible(false)}
onOk={() => setReceiptVisible(false)}
centered
style={{ maxHeight: modalHeight }}
bodyStyle={{ maxHeight: modalHeight - 107, overflowY: 'auto' }}
>
<Table
size='small'
pagination={false}
columns={receiptColumns}
dataSource={receiptList}
/>
</Modal>
<Modal
title={false}
visible={isModalVisible}
closable={false}
centered
footer={[<Button type="primary" onClick={setOk}>,</Button>]}
>
<p></p>
</Modal>
<SupplierCommitment projectId={projectId} callback={() => { setIsInit(true) }} />
</>
)
}
export default Index