diff --git a/src/pages/Tender/ProjectManager/JudgingPanel/List/index.tsx b/src/pages/Tender/ProjectManager/JudgingPanel/List/index.tsx index 7ce2e9a..231bb6a 100644 --- a/src/pages/Tender/ProjectManager/JudgingPanel/List/index.tsx +++ b/src/pages/Tender/ProjectManager/JudgingPanel/List/index.tsx @@ -1,26 +1,23 @@ import React, { useEffect, useRef, useState } from 'react'; -import { Button, Checkbox, Col, Collapse, DatePicker, Drawer, Form, Input, message, Modal, Popconfirm, Row, Select, Spin, Upload, Image, Radio, RadioChangeEvent, Tooltip, Popover, Typography } from 'antd' -import ProTable, { ActionType, ProColumns } from '@ant-design/pro-table'; -import { getList, getSecs, saveGroup, delOne, saveMember, changeEx, queryVoList, changeMember, applyFor, roomStatus, juryTem, rePassWord, getUserPhoto, unlockAccount } from './service'; +import { Button, Checkbox, Col, Collapse, DatePicker, Drawer, Form, Input, message, Modal, Popconfirm, Row, Select, Spin, Upload, Image, InputNumber } from 'antd' +import ProTable, { ActionType, EditableProTable, ProColumns } from '@ant-design/pro-table'; +import { getList, saveGroup, delOne, saveMember, changeEx, queryVoList, changeMember, applyFor, roomStatus, juryTem, rePassWord, getUserPhoto, unlockAccount, getCrotchListUsingGET } from './service'; import moment from 'moment'; -import { getDefId, getProId, getProMethod, getSessionProjectData, getSessionUserData } from '@/utils/session'; +import { getDefId, getProId, getProMethod, getSessionUserData } from '@/utils/session'; import { getURLInformation, isEmpty } from '@/utils/CommonUtils'; import './judgList.less'; import '@/assets/xsy_style.less'; import FileDown from '@/utils/Download'; -import { DownOutlined, EllipsisOutlined, QuestionCircleOutlined, UploadOutlined } from '@ant-design/icons'; +import { UploadOutlined } from '@ant-design/icons'; import { btnAuthority } from '@/utils/authority'; import RiskPrevention from '@/utils/RiskPrevention'; -import BidEvalAppointment from '@/components/ElecBidEvaluation/BidEvalAppointment'; import { downloadPath } from '@/utils/DownloadUtils'; -import ExpertPhotoUpload from '@/components/ElecBidEvaluation/ExpertPhotoUpload'; import { sortBy } from 'lodash'; -import OutsourcingManage from './OutsourcingManage'; -import { dateTimeFormatter } from '@/utils/DateUtils'; +import ExpertSchemeProfessionalInfo from './ExpertSchemeProfessionalInfo'; const JudgingPanel: React.FC<{}> = () => { const modalHeight = window.innerHeight * 96 / 100; - const proId = getProId(); + const proId = "1513327631627579392";//getProId();//TODO zyx-假数据 const roomType = getURLInformation('roomType'); // const tailLayout = { wrapperCol: { offset: 8, span: 20 }, }; const formLayout = { labelCol: { span: 8 }, wrapperCol: { span: 16 }, }; @@ -30,7 +27,6 @@ const JudgingPanel: React.FC<{}> = () => { const { Option } = Select; const { Panel } = Collapse; const { TextArea } = Input; - const { Paragraph, Text, Link, Title } = Typography; const CheckboxGroup = Checkbox.Group; // const { TabPane } = Tabs; const actionRef = useRef(); @@ -43,26 +39,21 @@ const JudgingPanel: React.FC<{}> = () => { const [memberVis, setMemberVis] = useState(false); const [open, openSet] = useState(true);//评委会里是否有开启评标的评审室 true有开启的 false无 const [allEnd, allEndSet] = useState(true);//评委会关联标段的评审室是否都关闭了 - // const [yushen, yushenSet] = useState(false);//是否预审 - // const [readOnly, readOnlySet] = useState(true);//只读 const [disabled, disabledSet] = useState(true);//禁用 const [checkSectionName, checkSectionNameSet] = useState('');//查看 关联标包显示 const [showName, showNameSet] = useState({ zbr: '', bb: '', pb: '', });//字段名 // const [manNum, manNumSet] = useState(5);//人数 const [riskVisible, setRiskVisible] = useState(false);//风控弹窗 2021.9.7 zhoujianlong const [riskData, setRiskData] = useState([]);//风控数据 2021.9.7 zhoujianlong - const [selectEvalVisible, setSelectEvalVisible] = useState(false);//电子评标室-评标室预约选择 2022.8.26 zhoujianlong - const [selectEvalData, setSelectEvalData] = useState();//电子评标室-评标室预约选择-数据 2022.8.26 zhoujianlong - const [initEvalTime, setInitEvalTime] = useState();//电子评标室-时间输入框默认时间(跟随标段) 2022.9.27 zhoujianlong // const [userPhotoId, setUserPhotoId] = useState("");//电子评标室-录入外部专家-相片id 2022.8.29 zhoujianlong - const [appoType, setAppoType] = useState("0");//电子评标室-预约框状态 2022.8.29 zhoujianlong - const [isReserve, setIsReserve] = useState("0");//电子评标室-是否预约电子评标室 2022.9.23 zhoujianlong 0-不预约 1-预约 const userData = getSessionUserData();//当前登录人用户信息 - const [assistVisible, setAssistVisible] = useState(false);//协办管理visible 2022.10.10 zhoujianlong - const [reserveStatus, setReserveStatus] = useState(false);//成员管理-当前行是否预约了评标室 true-预约了 false-没预约 + const [professionalMap, setProfessionalMap] = useState(); + //当前可编辑的行 + const [editableKeys, setEditableRowKeys] = useState([]); + const [tableData, setTableData] = useState([]); function getShouName() { - const method = getProMethod(); + const method = 'procurement_mode_1';//getProMethod();//TODO zyx-假数据 let showNameT: any = { zbr: '', bb: '', pb: '', }//相关标段 标书费 保证金 服务费 // let num = 3; if (method === 'procurement_mode_1' || method === 'procurement_mode_2') {//招标 @@ -73,7 +64,6 @@ const JudgingPanel: React.FC<{}> = () => { } else {//谈判类 showNameT = { zbr: '采购人', bb: '采购包', pb: '评审', } } - // manNumSet(num); showNameSet(showNameT); } @@ -82,8 +72,6 @@ const JudgingPanel: React.FC<{}> = () => { const [checkAll, setCheckAll] = useState(false); const [checkedList, setCheckedList] = useState(); const [sectionCount, sectionCountSet] = useState(0);//控制获取标包 - //创建控制 - // const [activeKey, activeKeySet] = useState('1'); //创建评委会小组 const saveG = async (fields: any) => { @@ -112,18 +100,9 @@ const JudgingPanel: React.FC<{}> = () => { //主页表格 const columns: ProColumns[] = [ { title: '序号', valueType: 'index', width: 50, }, - // { - // title: '评审小组分类', dataIndex: 'juryType', width: '10%', - // valueEnum: { - // '1': { text: `资格预审${showName.pb}`, }, - // '2': { text: `招标项目${showName.pb}`, }, - // } - // }, { title: `${showName.bb}名称`, dataIndex: 'sectionName', }, - { title: '专家数量', dataIndex: 'expertNumber', width: 80 }, - { title: `${showName.zbr}代表数量`, dataIndex: 'representativeNumber', width: 120 }, - { title: '是否预约电子评标室', dataIndex: 'reserveStatus', width: 150, valueEnum: { 1: "是", 0: "否" } }, - { title: `${showName.pb}地点`, dataIndex: 'evalLocation', width: '18%', ellipsis: true, render: (_: any, record: any) => sectionNameValue(record.elecEvalRoomReserve, record.evalLocation, record.reserveStatus) }, + { title: '专家抽取数量', dataIndex: 'expertNumber', width: 120 }, + { title: `直接录入数量`, dataIndex: 'representativeNumber', width: 120 }, { title: '预计开始时间', dataIndex: 'startTime', valueType: 'dateTime', width: '10%', }, { title: '预计结束时间', dataIndex: 'endTime', valueType: 'dateTime', width: '10%', }, { @@ -168,22 +147,9 @@ const JudgingPanel: React.FC<{}> = () => { await queryOpenStatus(record.id); categorySet(record.juryCategoryVOList); juryIdSet(record.id); - setReserveStatus(record.reserveStatus === 1); memberCountSet(memberCount + 1); setMemberVis(true); }}>成员管理 - {record.assistNumber != null && } - {record.elecEvalRoomReserve && (record.elecEvalRoomReserve.status == -1 || record.elecEvalRoomReserve.status == 0) && } ); } else { @@ -197,24 +163,63 @@ const JudgingPanel: React.FC<{}> = () => { ) } + const handleUpdate = async (record: any) => { + form.resetFields(); + const res = await queryVoList({ id: record.id }); + + + // 提取专业抽取列表 + const extractSpecialityList = res.data.extractSpecialityList || []; + // 设置表格数据 + let specList = extractSpecialityList.map((item: any, index: number) => ({ + ...item, + uid: item.id || String(index + Date.now()) // 确保每个条目有唯一 key + })) + res.data.extractSpecialityList = specList; + setTableData(specList); + // 设置 editableKeys,确保每一行都能编辑 + setEditableRowKeys(specList.map((item: any, index: number) => item.uid)); + // setEditableRowKeys(specList.map(item => item.uid)); + updateDataSet(res.data); + + //剔除代表 + let list = [...res.data.juryCategoryVOList]; + list.map((item: any, index: any) => { + if (item.category == 1) { + list.splice(index, 1); + } + }); + cqDataSet(list); + setModalVis(true); + sectionCountSet(sectionCount + 1); + // readOnlySet(check); + checkSectionNameSet(res.data.sectionName); + + }; //删除 const del = async (fields: any) => { @@ -263,21 +268,42 @@ const JudgingPanel: React.FC<{}> = () => { //创建修改 标包方法 const onChangeCheckBox = (checkedList: any[]) => { + // 获取当前选中的所有 section 对象 + const selectedSections = sections.filter((section: any) => + checkedList.includes(section.sectionId) + ); + + // 获取所有不同的 juryNumber 值 + const uniqueJuryNumbers = [...new Set(selectedSections.map((s: any) => s.juryNumber))]; + + if (uniqueJuryNumbers.length > 1) { + message.error('所选标段的评标委员会人数不一致,不能同时选择'); + return; // 阻止更新 + } + setCheckedList(checkedList); - getEarliestTime(sections, checkedList, isReserve); + getEarliestTime(sections, checkedList); setIndeterminate(!!checkedList.length && checkedList.length < sectionsVal.length); setCheckAll(checkedList.length === sectionsVal.length); }; const onCheckAllChange = (e: { target: { checked: any; }; }) => { + + const uniqueJuryNumbers = [ + ...new Set(sections.map((s: any) => s.juryNumber)), + ]; + + if (e.target.checked && uniqueJuryNumbers.length > 1) { + message.error("存在不同评标委员会人数,不能全选"); + return; + } setCheckedList(e.target.checked ? sectionsVal : []); - getEarliestTime(sections, e.target.checked ? sectionsVal : [], isReserve); + getEarliestTime(sections, e.target.checked ? sectionsVal : []); setIndeterminate(false); setCheckAll(e.target.checked); }; function disabledDate(current: any) {//日期选择 - // Can not select days before today and today return current && current < moment().startOf('day'); } @@ -290,48 +316,6 @@ const JudgingPanel: React.FC<{}> = () => { }, ] } - //评标地点 评标室更多信息 - const evalLocationTitle = (detail: any, updateData: any) => { - return ( -
-

详细地址:{updateData?.reserveStatus == 1 ? updateData.elecEvalRoomReserve.areaAddress : updateData?.evalLocation}

-

可容纳人数:{detail?.numberInMeeting}

-

联系人:{detail?.contactName}

-

联系电话:{detail?.contactTel}

-
- ) - } - //说明 - const sectionNameValue = (detail: any, evalLocation: any, reserveStatus: any) => { - const address = reserveStatus == 1 ? detail?.areaAddress : evalLocation; - const content = ( - -
-
-

详细地址:{address}

-

可容纳人数:{detail?.numberInMeeting}

-

联系人:{detail?.contactName}

-

联系电话:{detail?.contactTel}

-
-
-
- ) - let text = address.length > 30 ? address.slice(0, 30) + "..." : address; - return ( - detail != null ? ( - - - - {text} - - - - - ) : ( - {text} - ) - ) - } //专家账号解锁 const unlockExportAccount = async (record: any, juryId: any, proId: any) => { try { @@ -344,8 +328,6 @@ const JudgingPanel: React.FC<{}> = () => { } } - - useEffect(() => {//获取标包信息 let params = { tpId: proId, roomType: roomType }; @@ -360,25 +342,38 @@ const JudgingPanel: React.FC<{}> = () => { params['juryId'] = ''; } getShouName();//根据采购类型变名字 - modalVis == true ? getSecs({ ...params }).then((res) => { - let data = []; - let secVals = []; - if (res.success) { - secVals = res.data.map((item: any) => { return item.sectionId }) - data = res.data; - } - setSections(data); - setSectionsVal(secVals); - setIndeterminate(!!checked.length && checked.length < secVals.length); - setCheckAll(checked.length === secVals.length); - updateData && String(updateData?.reserveStatus) == "1" && getEarliestTime(data, checked, String(updateData?.reserveStatus));//初始化赋值数据 - }) : null; + // TODO zyx假数据 + // modalVis == true ? getSecs({ ...params }).then((res) => { + let data = []; + let secVals = []; + // TODO zyx假数据 + // if (res.success) { + let res = { + "code": 200, "success": true, "message": "success", + "data": [{ + "id": "1905086119331868672", "tpId": "1905086117686558720", "sectionId": "1905086117879496704", + "sectionNum": "ZX00852409000329", "sectionName": "0926", "roomType": 2, "status": 1, + "openTime": "2025-04-27 14:42:00", "juryNumber": "5" + }, + { + "id": "1905086119331868671", "tpId": "1905086117686558720", "sectionId": "1905086117879496701", + "sectionNum": "ZX00852409000321", "sectionName": "0926-2", "roomType": 2, + "status": 1, "openTime": "2025-04-25 12:42:00", "juryNumber": "7" + }] + }; + secVals = res.data.map((item: any) => { return item.sectionId }) + data = res.data; + // } + setSections(data); + setSectionsVal(secVals); + setIndeterminate(!!checked.length && checked.length < secVals.length); + setCheckAll(checked.length === secVals.length); + updateData && String(updateData?.reserveStatus) == "1" && getEarliestTime(data, checked);//初始化赋值数据 + // }) : null; - // roomType == 1 ? yushenSet(true) : null; }, [sectionCount]); useEffect(() => {//给表单赋值 - updateData && setIsReserve(String(updateData?.reserveStatus));//赋值给是否预约电子评标室 form.setFieldsValue({ juryType: updateData != undefined ? updateData.juryType : null, representativeNumber: updateData != undefined ? updateData.representativeNumber : null, @@ -387,9 +382,7 @@ const JudgingPanel: React.FC<{}> = () => { endTime: updateData != undefined ? updateData.reserveStatus == 1 ? moment(updateData.elecEvalRoomReserve.reserveEndDate, 'yyyy-MM-DD HH:mm:ss') : moment(updateData.endTime, 'yyyy-MM-DD HH:mm:ss') : null, evalLocation: updateData != undefined ? updateData.reserveStatus == 1 ? updateData.elecEvalRoomReserve.areaAddress : updateData.evalLocation : null, description: updateData != undefined ? updateData.description : null, - reserveBy: updateData?.reserveStatus == 1 ? updateData.elecEvalRoomReserve.reserveBy : null, - reserveContactNumber: updateData?.reserveStatus == 1 ? updateData.elecEvalRoomReserve.reserveContactNumber : null, - assistNumber: updateData?.reserveStatus == 1 ? updateData.assistNumber : null, + extractSpecialityList: updateData != undefined ? updateData.extractSpecialityList : null, }) }, [updateData]); @@ -408,48 +401,35 @@ const JudgingPanel: React.FC<{}> = () => { onCancel={() => { setModalVis(false); updateDataSet({}); - // cqDataSet([]); - // updateKeySet(-1); - // activeKeySet('1'); + setTableData([]); + setCheckedList([]); + form.resetFields(); }} > - {/* - - {tab1()} - - - {tab2()} - - - {cqDrawer()} */} {tab1()} ) } - // function changeTabs(key: any) { - // if (key == 1) { - // activeKeySet(key); - // } else { - // nextStep() - // } - // } //人数校验 function checkMan(count: number, expCon: any) { let res = true; const defId = getDefId();//16 const method = getProMethod();//9 - const yushenType = getSessionProjectData().openTenderForm; + //TODO zyx--假数据--start + const yushenType = "open_tender_form_1"; + // const yushenType = getSessionProjectData().openTenderForm; + //TODO zyx--假数据--end let manNumT = 5; if (method === 'procurement_mode_1' || method === 'procurement_mode_2') { if (roomType == '1' && yushenType == 'open_tender_form_2') {//资格预审 自愿招标为3 manNumT = 3; } - if (expCon < count * 2 / 3) { - res = false; - message.error("专家人数不少于评委会总人数2/3") - } + // if (expCon < count * 2 / 3) { + // res = false; + // message.error("专家人数不少于评委会总人数2/3") + // } } else if (method === 'procurement_mode_3') {//比选 if (expCon < count * 2 / 5) { res = false; @@ -484,257 +464,47 @@ const JudgingPanel: React.FC<{}> = () => { } return res } - // async function nextStep() {//下一步 - // const formVals = form.getFieldsValue(); - // let verify = false; - // await form.validateFields().then(() => { - // let juryRoomListTem: any = []; - // verify = true; - // checkedList != undefined ? checkedList.map((item: any) => { - // const sectionId = item; - // sections.map((item: any) => { - // if (item.sectionId == sectionId) { - // juryRoomListTem.push({ roomId: item.id, sectionId: sectionId, roomType: roomType }); - // } - // }); - // juryRoomListSet(juryRoomListTem); - // }) : verify = false; - // //招标人数需要大于5 需要取项目类型 - // const count = parseInt(formVals.representativeNumber) + parseInt(formVals.expertNumber); - // if (verify) { - // verify = checkMan(count, parseInt(formVals.expertNumber)); - // } - // const st = moment(formVals.startTime).format('yyyy-MM-DD HH:mm:ss'); - // const et = moment(formVals.endTime).format('yyyy-MM-DD HH:mm:ss'); - // if (st > et || st == et) {//校验时间 - // verify = false; - // message.error("预计开始时间应早于预计结束时间") - // } - // if (isReserve == "1") {//预约了评标室 - // const selectStartTime = moment(selectEvalData.reserveStartDate).format('yyyy-MM-DD HH:mm:ss'); - // const selectEndTime = moment(selectEvalData.reserveEndDate).format('yyyy-MM-DD HH:mm:ss'); - // if (selectStartTime != st || selectEndTime != et) { - // verify = false; - // message.error("预约时间需与选择评标室中的预约时间相等"); - // } - // } - // if (verify) { - // activeKeySet('2'); - // } - // }); - // } //添加抽取drawer const formLayoutDrawer = { labelCol: { span: 8 }, wrapperCol: { span: 16 }, }; const tailLayoutDrawer = { wrapperCol: { offset: 8, span: 20 }, }; - // const [juryRoomList, juryRoomListSet] = useState([]); const [cqData, cqDataSet] = useState([]);//抽取表格,提交时需要 - // const [addCq, addCqSet] = useState(false);//添加抽取抽屉显示控制 - // const [updateKey, updateKeySet] = useState(-1);//触发修改存key - // const [formCq] = Form.useForm(); - // const [display, displaySet] = useState(true); - // const columnsCq: ProColumns[] = [//抽取表格 - // { title: '序号', valueType: 'index', width: 50, }, - // { - // title: '专家分类', dataIndex: 'subCategory', - // valueEnum: { - // '1': { text: '技术', }, - // '2': { text: '商务', }, - // '3': { text: '法律', }, - // '4': { text: '其它', }, - // } - // }, - // { title: '需抽数量', dataIndex: 'extractNumber', }, - // { - // title: '抽取方式', dataIndex: 'extractMethod', - // valueEnum: { - // '1': { text: '外部录入', }, - // '2': { text: '随机抽取', }, - // } - // }, - // { title: '抽取规则要求', dataIndex: 'cqRude', }, - // { - // title: '操作', dataIndex: 'option', width: 100, - // valueType: 'option', - // render: (_, record) => { - // return ( - // <> - // { - // !disabled ? - // <> - // - // { - // let dataTem = [...cqData]; - // dataTem.map((item: any, index: any) => { - // if (item.key == record.key) { - // dataTem.splice(index, 1) - // } - // }); - // cqDataSet(dataTem); - // }} - // okText="确定" - // cancelText="取消" - // > - // - // - // - // : <> - // } + // 在组件内定义函数 + const calculateExpertNumber = () => { + // 没有选中标段时直接返回,不清空 expertNumber 是为了兼容已有值的情况 + if (!checkedList || checkedList.length === 0) return; - // - // ); - // } - // }, - // ]; - // const cqDrawer = () => {//添加抽取条件、修改 - // return ( - // <> - // { addCqSet(false); updateKeySet(-1); formCq.resetFields(); }} - // visible={addCq} - // getContainer={false} - // style={{ position: 'absolute' }} - // > - //
- // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - //
- // {/* - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // - // 中国联通 - // - // - // - // - // - // - // 中国联通 - // - // - // - // - // - // - // 中国联通 - // - // - // */} - //
- // - // - // - //
- //
- // - // ) - // } + const selectedSections = sections.filter((section: any) => + checkedList.includes(section.sectionId) + ); + + const uniqueJuryNumbers = [...new Set(selectedSections.map((s: any) => s.juryNumber))]; + + if (uniqueJuryNumbers.length > 1) { + form.setFieldsValue({ expertNumber: undefined }); + message.error("所选标段评标委员会人数不一致"); + return; + } + console.log("checkedList", checkedList); + + const juryNumber = parseInt(uniqueJuryNumbers[0]) || 0; + const repNumber = parseInt(form.getFieldValue('representativeNumber')) || 0; + + console.log(juryNumber, repNumber); + if (repNumber < 0 || repNumber > juryNumber) { + message.error(`直接录入数量应在 0 ~ ${juryNumber} 之间`); + form.setFieldsValue({ representativeNumber: undefined, expertNumber: undefined }); + return; + } + + const expertNumber = Math.max(0, juryNumber - repNumber); + (expertNumber === 0) && setTableData([]); + form.getFieldValue('representativeNumber') && form.setFieldsValue({ expertNumber }); + }; + + // 在 useEffect 中监听依赖项 + useEffect(() => { + calculateExpertNumber(); + }, [form.getFieldValue('representativeNumber'), checkedList]); const tab1 = () => {//cqtab1 return ( <> @@ -789,7 +559,11 @@ const JudgingPanel: React.FC<{}> = () => { sections.map((item: any, index: any) => { return ( - {item.sectionName} + {item.sectionName} + + 评标委员会人数: {item.juryNumber}人 + + ) }) @@ -805,59 +579,19 @@ const JudgingPanel: React.FC<{}> = () => { -

预约电子评标室

+

评标时间和地点管理

- 注:若选择预约电子评标室,请提前做好相关准备,并按预约时间进入评标场所,系统将下发相关人员名单并获取相关数据} - > - - - - - - {isReserve == "1" && !disabled ? (//预约电子评标室 - - - - - - - - - - - ) : ( - - - - - - )} - {isReserve == "1" && (//预约电子评标室 - + - - - )} + + + = () => { format="YYYY-MM-DD HH:mm:ss" disabledDate={disabledDate} showTime={{ defaultValue: moment('00:00:00', 'HH:mm:ss') }} - disabled={disabled || (isReserve == "1")} + disabled={disabled} showNow={false} style={{ width: "90%" }} /> @@ -881,227 +615,212 @@ const JudgingPanel: React.FC<{}> = () => { format="YYYY-MM-DD HH:mm:ss" disabledDate={disabledDate} showTime={{ defaultValue: moment('00:00:00', 'HH:mm:ss') }} - disabled={disabled || (isReserve == "1")} + disabled={disabled} showNow={false} style={{ width: "90%" }} /> - {isReserve == "1" && (//预约电子评标室 - <> - - - - - - - - )}

专家申请基本信息

-
- {/* { - display ? - - - - - - - - - : - - - - - - - - } */} + - - - - - + { + const value = e.target.value; + if (value && !checkedList?.length) { + form.resetFields(['representativeNumber']); + message.error("请先选择关联标段"); + return; + } - + + + + + + + + + {() => { + const expertNumber = form.getFieldValue('expertNumber'); + if (!expertNumber || expertNumber <= 0) return null; + + return ( + + + rowKey="uid" + toolBarRender={false} + columns={professColumns} + dataSource={tableData} + recordCreatorProps={{ + newRecordType: 'dataSource', + hidden: disabled, + creatorButtonText: '新增', + record: () => ({ + uid: String(Date.now()), + // id: "", + // extractNumber: "", + // specialityId: "", + }), + }} + editable={{ + type: 'multiple', + editableKeys, + onChange: (keys, rows) => { + setEditableRowKeys(keys); + }, + onValuesChange: (record, recordList) => { + setTableData(recordList); + }, + actionRender: (row, _, dom) => { + return [dom.delete]; + }, + }} + /> + + ); + }} + + + {/* - + */}
) } - // const tab2 = () => {//cqtab2 - // return ( - // <> - //
- //

抽取条件

- //
- // - //
- //
- // - //
- //

回避单位信息

- //
- // - //
- //
- //
- //

回避专家信息

- //
- // - //
- //
- // - // ) - // } + //抽取专业-表格类型定义 + type DataSourceType = { + id?: string; + extractNumber: number; + specialityId?: string; + }; + const professColumns: ProColumns[] = [ + { + title: '主键', + dataIndex: 'id', + hideInTable: true, + }, + { + title: '专业名称', + dataIndex: 'specialityId', + valueType: 'select', + valueEnum: professionalMap, + width: '30%', + }, + { + title: '人数', + dataIndex: 'extractNumber', + width: '30%', + renderFormItem: (_, { record }) => { + return ; + }, + }, + { + title: '操作', + valueType: 'option', + hideInTable: disabled, + }, + ]; + /** + * 校验函数 + * @param schemaData + * @returns + */ + const checkData = (schemaData: any) => { + let res = true; + let spec = 0; + let specNum = 0; + for (let i = 0; i < schemaData?.extractSpecialityList?.length; i++) { + const item = schemaData?.extractSpecialityList?.[i]; + if ( + item.hasOwnProperty("specialityId") && + item.hasOwnProperty("extractNumber") + ) { + spec += 1; + } + if (item?.extractNumber) { + specNum += Number(item?.extractNumber); + } + } + console.log("expertNumber", schemaData?.expertNumber, "specNum", specNum); + if (Number(schemaData?.expertNumber) !== specNum) { + res = false; + message.error("抽取专业人数与专家需求人数不符!"); + } + return res; + } + const checkSpecialityUnique = () => { + let res = true; + const specialityIds = tableData.map(item => item.specialityId).filter(Boolean); + const unique = [...new Set(specialityIds)]; + if (specialityIds.length !== unique.length) { + res = false; + message.error('抽取专业名称不可重复选择'); + } + return res; + }; + const idToNameMap = Object.entries(professionalMap || {}).reduce((acc, [id, option]) => { + acc[id] = option.text; + return acc; + }, {} as Record); + const idToCodeMap = Object.entries(professionalMap || {}).reduce((acc, [id, option]) => { + acc[id] = option.text; + return acc; + }, {} as Record); + // 遍历表格数据并附加专业名称 + const enhancedData = tableData.map(item => { + const name = idToNameMap[item.specialityId] || ''; // 获取对应名称 + const code = idToCodeMap[item.specialityId] || ''; // 获取对应名称 + return { + ...item, + specialityName: name, // 添加专业名称字段 + specialityNo: code, + }; + }); const renderFooter = () => {//评审小组footer - // if (activeKey === '1') { - // return ( - // <> - // - // - // - // ); - // } - // if (activeKey === '2') { - // return ( - // <> - // - // - // - // - // ); - // } else { - // return (<>) - // } return ( <> */} - {/* */} - - {/* - - */} + {record.assistNumber != null && } + {record.elecEvalRoomReserve && (record.elecEvalRoomReserve.status == -1 || record.elecEvalRoomReserve.status == 0) && } ); } else { @@ -163,63 +197,24 @@ const JudgingPanel: React.FC<{}> = () => { ) } - const handleUpdate = async (record: any) => { - form.resetFields(); - const res = await queryVoList({ id: record.id }); - - - // 提取专业抽取列表 - const extractSpecialityList = res.data.extractSpecialityList || []; - // 设置表格数据 - let specList = extractSpecialityList.map((item: any, index: number) => ({ - ...item, - uid: item.id || String(index + Date.now()) // 确保每个条目有唯一 key - })) - res.data.extractSpecialityList = specList; - setTableData(specList); - // 设置 editableKeys,确保每一行都能编辑 - setEditableRowKeys(specList.map((item: any, index: number) => item.uid)); - // setEditableRowKeys(specList.map(item => item.uid)); - updateDataSet(res.data); - - //剔除代表 - let list = [...res.data.juryCategoryVOList]; - list.map((item: any, index: any) => { - if (item.category == 1) { - list.splice(index, 1); - } - }); - cqDataSet(list); - setModalVis(true); - sectionCountSet(sectionCount + 1); - // readOnlySet(check); - checkSectionNameSet(res.data.sectionName); - - }; //删除 const del = async (fields: any) => { @@ -268,42 +263,21 @@ const JudgingPanel: React.FC<{}> = () => { //创建修改 标包方法 const onChangeCheckBox = (checkedList: any[]) => { - // 获取当前选中的所有 section 对象 - const selectedSections = sections.filter((section: any) => - checkedList.includes(section.sectionId) - ); - - // 获取所有不同的 juryNumber 值 - const uniqueJuryNumbers = [...new Set(selectedSections.map((s: any) => s.juryNumber))]; - - if (uniqueJuryNumbers.length > 1) { - message.error('所选标段的评标委员会人数不一致,不能同时选择'); - return; // 阻止更新 - } - setCheckedList(checkedList); - getEarliestTime(sections, checkedList); + getEarliestTime(sections, checkedList, isReserve); setIndeterminate(!!checkedList.length && checkedList.length < sectionsVal.length); setCheckAll(checkedList.length === sectionsVal.length); }; const onCheckAllChange = (e: { target: { checked: any; }; }) => { - - const uniqueJuryNumbers = [ - ...new Set(sections.map((s: any) => s.juryNumber)), - ]; - - if (e.target.checked && uniqueJuryNumbers.length > 1) { - message.error("存在不同评标委员会人数,不能全选"); - return; - } setCheckedList(e.target.checked ? sectionsVal : []); - getEarliestTime(sections, e.target.checked ? sectionsVal : []); + getEarliestTime(sections, e.target.checked ? sectionsVal : [], isReserve); setIndeterminate(false); setCheckAll(e.target.checked); }; function disabledDate(current: any) {//日期选择 + // Can not select days before today and today return current && current < moment().startOf('day'); } @@ -316,6 +290,48 @@ const JudgingPanel: React.FC<{}> = () => { }, ] } + //评标地点 评标室更多信息 + const evalLocationTitle = (detail: any, updateData: any) => { + return ( +
+

详细地址:{updateData?.reserveStatus == 1 ? updateData.elecEvalRoomReserve.areaAddress : updateData?.evalLocation}

+

可容纳人数:{detail?.numberInMeeting}

+

联系人:{detail?.contactName}

+

联系电话:{detail?.contactTel}

+
+ ) + } + //说明 + const sectionNameValue = (detail: any, evalLocation: any, reserveStatus: any) => { + const address = reserveStatus == 1 ? detail?.areaAddress : evalLocation; + const content = ( + +
+
+

详细地址:{address}

+

可容纳人数:{detail?.numberInMeeting}

+

联系人:{detail?.contactName}

+

联系电话:{detail?.contactTel}

+
+
+
+ ) + let text = address.length > 30 ? address.slice(0, 30) + "..." : address; + return ( + detail != null ? ( + + + + {text} + + + + + ) : ( + {text} + ) + ) + } //专家账号解锁 const unlockExportAccount = async (record: any, juryId: any, proId: any) => { try { @@ -328,6 +344,8 @@ const JudgingPanel: React.FC<{}> = () => { } } + + useEffect(() => {//获取标包信息 let params = { tpId: proId, roomType: roomType }; @@ -342,38 +360,25 @@ const JudgingPanel: React.FC<{}> = () => { params['juryId'] = ''; } getShouName();//根据采购类型变名字 - // TODO zyx假数据 - // modalVis == true ? getSecs({ ...params }).then((res) => { - let data = []; - let secVals = []; - // TODO zyx假数据 - // if (res.success) { - let res = { - "code": 200, "success": true, "message": "success", - "data": [{ - "id": "1905086119331868672", "tpId": "1905086117686558720", "sectionId": "1905086117879496704", - "sectionNum": "ZX00852409000329", "sectionName": "0926", "roomType": 2, "status": 1, - "openTime": "2025-04-27 14:42:00", "juryNumber": "5" - }, - { - "id": "1905086119331868671", "tpId": "1905086117686558720", "sectionId": "1905086117879496701", - "sectionNum": "ZX00852409000321", "sectionName": "0926-2", "roomType": 2, - "status": 1, "openTime": "2025-04-25 12:42:00", "juryNumber": "7" - }] - }; - secVals = res.data.map((item: any) => { return item.sectionId }) - data = res.data; - // } - setSections(data); - setSectionsVal(secVals); - setIndeterminate(!!checked.length && checked.length < secVals.length); - setCheckAll(checked.length === secVals.length); - updateData && String(updateData?.reserveStatus) == "1" && getEarliestTime(data, checked);//初始化赋值数据 - // }) : null; + modalVis == true ? getSecs({ ...params }).then((res) => { + let data = []; + let secVals = []; + if (res.success) { + secVals = res.data.map((item: any) => { return item.sectionId }) + data = res.data; + } + setSections(data); + setSectionsVal(secVals); + setIndeterminate(!!checked.length && checked.length < secVals.length); + setCheckAll(checked.length === secVals.length); + updateData && String(updateData?.reserveStatus) == "1" && getEarliestTime(data, checked, String(updateData?.reserveStatus));//初始化赋值数据 + }) : null; + // roomType == 1 ? yushenSet(true) : null; }, [sectionCount]); useEffect(() => {//给表单赋值 + updateData && setIsReserve(String(updateData?.reserveStatus));//赋值给是否预约电子评标室 form.setFieldsValue({ juryType: updateData != undefined ? updateData.juryType : null, representativeNumber: updateData != undefined ? updateData.representativeNumber : null, @@ -382,7 +387,9 @@ const JudgingPanel: React.FC<{}> = () => { endTime: updateData != undefined ? updateData.reserveStatus == 1 ? moment(updateData.elecEvalRoomReserve.reserveEndDate, 'yyyy-MM-DD HH:mm:ss') : moment(updateData.endTime, 'yyyy-MM-DD HH:mm:ss') : null, evalLocation: updateData != undefined ? updateData.reserveStatus == 1 ? updateData.elecEvalRoomReserve.areaAddress : updateData.evalLocation : null, description: updateData != undefined ? updateData.description : null, - extractSpecialityList: updateData != undefined ? updateData.extractSpecialityList : null, + reserveBy: updateData?.reserveStatus == 1 ? updateData.elecEvalRoomReserve.reserveBy : null, + reserveContactNumber: updateData?.reserveStatus == 1 ? updateData.elecEvalRoomReserve.reserveContactNumber : null, + assistNumber: updateData?.reserveStatus == 1 ? updateData.assistNumber : null, }) }, [updateData]); @@ -401,35 +408,48 @@ const JudgingPanel: React.FC<{}> = () => { onCancel={() => { setModalVis(false); updateDataSet({}); - setTableData([]); - setCheckedList([]); - form.resetFields(); + // cqDataSet([]); + // updateKeySet(-1); + // activeKeySet('1'); }} > + {/* + + {tab1()} + + + {tab2()} + + + {cqDrawer()} */} {tab1()} ) } + // function changeTabs(key: any) { + // if (key == 1) { + // activeKeySet(key); + // } else { + // nextStep() + // } + // } //人数校验 function checkMan(count: number, expCon: any) { let res = true; const defId = getDefId();//16 const method = getProMethod();//9 - //TODO zyx--假数据--start - const yushenType = "open_tender_form_1"; - // const yushenType = getSessionProjectData().openTenderForm; - //TODO zyx--假数据--end + const yushenType = getSessionProjectData().openTenderForm; let manNumT = 5; if (method === 'procurement_mode_1' || method === 'procurement_mode_2') { if (roomType == '1' && yushenType == 'open_tender_form_2') {//资格预审 自愿招标为3 manNumT = 3; } - // if (expCon < count * 2 / 3) { - // res = false; - // message.error("专家人数不少于评委会总人数2/3") - // } + if (expCon < count * 2 / 3) { + res = false; + message.error("专家人数不少于评委会总人数2/3") + } } else if (method === 'procurement_mode_3') {//比选 if (expCon < count * 2 / 5) { res = false; @@ -464,47 +484,257 @@ const JudgingPanel: React.FC<{}> = () => { } return res } + // async function nextStep() {//下一步 + // const formVals = form.getFieldsValue(); + // let verify = false; + // await form.validateFields().then(() => { + // let juryRoomListTem: any = []; + // verify = true; + // checkedList != undefined ? checkedList.map((item: any) => { + // const sectionId = item; + // sections.map((item: any) => { + // if (item.sectionId == sectionId) { + // juryRoomListTem.push({ roomId: item.id, sectionId: sectionId, roomType: roomType }); + // } + // }); + // juryRoomListSet(juryRoomListTem); + // }) : verify = false; + // //招标人数需要大于5 需要取项目类型 + // const count = parseInt(formVals.representativeNumber) + parseInt(formVals.expertNumber); + // if (verify) { + // verify = checkMan(count, parseInt(formVals.expertNumber)); + // } + // const st = moment(formVals.startTime).format('yyyy-MM-DD HH:mm:ss'); + // const et = moment(formVals.endTime).format('yyyy-MM-DD HH:mm:ss'); + // if (st > et || st == et) {//校验时间 + // verify = false; + // message.error("预计开始时间应早于预计结束时间") + // } + // if (isReserve == "1") {//预约了评标室 + // const selectStartTime = moment(selectEvalData.reserveStartDate).format('yyyy-MM-DD HH:mm:ss'); + // const selectEndTime = moment(selectEvalData.reserveEndDate).format('yyyy-MM-DD HH:mm:ss'); + // if (selectStartTime != st || selectEndTime != et) { + // verify = false; + // message.error("预约时间需与选择评标室中的预约时间相等"); + // } + // } + // if (verify) { + // activeKeySet('2'); + // } + // }); + // } //添加抽取drawer const formLayoutDrawer = { labelCol: { span: 8 }, wrapperCol: { span: 16 }, }; const tailLayoutDrawer = { wrapperCol: { offset: 8, span: 20 }, }; + // const [juryRoomList, juryRoomListSet] = useState([]); const [cqData, cqDataSet] = useState([]);//抽取表格,提交时需要 - // 在组件内定义函数 - const calculateExpertNumber = () => { - // 没有选中标段时直接返回,不清空 expertNumber 是为了兼容已有值的情况 - if (!checkedList || checkedList.length === 0) return; + // const [addCq, addCqSet] = useState(false);//添加抽取抽屉显示控制 + // const [updateKey, updateKeySet] = useState(-1);//触发修改存key + // const [formCq] = Form.useForm(); + // const [display, displaySet] = useState(true); + // const columnsCq: ProColumns[] = [//抽取表格 + // { title: '序号', valueType: 'index', width: 50, }, + // { + // title: '专家分类', dataIndex: 'subCategory', + // valueEnum: { + // '1': { text: '技术', }, + // '2': { text: '商务', }, + // '3': { text: '法律', }, + // '4': { text: '其它', }, + // } + // }, + // { title: '需抽数量', dataIndex: 'extractNumber', }, + // { + // title: '抽取方式', dataIndex: 'extractMethod', + // valueEnum: { + // '1': { text: '外部录入', }, + // '2': { text: '随机抽取', }, + // } + // }, + // { title: '抽取规则要求', dataIndex: 'cqRude', }, + // { + // title: '操作', dataIndex: 'option', width: 100, + // valueType: 'option', + // render: (_, record) => { + // return ( + // <> + // { + // !disabled ? + // <> + // + // { + // let dataTem = [...cqData]; + // dataTem.map((item: any, index: any) => { + // if (item.key == record.key) { + // dataTem.splice(index, 1) + // } + // }); + // cqDataSet(dataTem); + // }} + // okText="确定" + // cancelText="取消" + // > + // + // + // + // : <> + // } - const selectedSections = sections.filter((section: any) => - checkedList.includes(section.sectionId) - ); - - const uniqueJuryNumbers = [...new Set(selectedSections.map((s: any) => s.juryNumber))]; - - if (uniqueJuryNumbers.length > 1) { - form.setFieldsValue({ expertNumber: undefined }); - message.error("所选标段评标委员会人数不一致"); - return; - } - console.log("checkedList", checkedList); - - const juryNumber = parseInt(uniqueJuryNumbers[0]) || 0; - const repNumber = parseInt(form.getFieldValue('representativeNumber')) || 0; - - console.log(juryNumber, repNumber); - if (repNumber < 0 || repNumber > juryNumber) { - message.error(`直接录入数量应在 0 ~ ${juryNumber} 之间`); - form.setFieldsValue({ representativeNumber: undefined, expertNumber: undefined }); - return; - } - - const expertNumber = Math.max(0, juryNumber - repNumber); - (expertNumber === 0) && setTableData([]); - form.getFieldValue('representativeNumber') && form.setFieldsValue({ expertNumber }); - }; - - // 在 useEffect 中监听依赖项 - useEffect(() => { - calculateExpertNumber(); - }, [form.getFieldValue('representativeNumber'), checkedList]); + // + // ); + // } + // }, + // ]; + // const cqDrawer = () => {//添加抽取条件、修改 + // return ( + // <> + // { addCqSet(false); updateKeySet(-1); formCq.resetFields(); }} + // visible={addCq} + // getContainer={false} + // style={{ position: 'absolute' }} + // > + //
+ // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + //
+ // {/* + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // 中国联通 + // + // + // + // + // + // + // 中国联通 + // + // + // + // + // + // + // 中国联通 + // + // + // */} + //
+ // + // + // + //
+ //
+ // + // ) + // } const tab1 = () => {//cqtab1 return ( <> @@ -559,11 +789,7 @@ const JudgingPanel: React.FC<{}> = () => { sections.map((item: any, index: any) => { return ( - {item.sectionName} - - 评标委员会人数: {item.juryNumber}人 - - + {item.sectionName} ) }) @@ -579,19 +805,59 @@ const JudgingPanel: React.FC<{}> = () => {
-

评标时间和地点管理

+

预约电子评标室

- - 注:若选择预约电子评标室,请提前做好相关准备,并按预约时间进入评标场所,系统将下发相关人员名单并获取相关数据} + > + + + + + + {isReserve == "1" && !disabled ? (//预约电子评标室 + + + + + + + + + + + ) : ( + + + + + + )} + {isReserve == "1" && (//预约电子评标室 + - - - + + + )} = () => { format="YYYY-MM-DD HH:mm:ss" disabledDate={disabledDate} showTime={{ defaultValue: moment('00:00:00', 'HH:mm:ss') }} - disabled={disabled} + disabled={disabled || (isReserve == "1")} showNow={false} style={{ width: "90%" }} /> @@ -615,212 +881,227 @@ const JudgingPanel: React.FC<{}> = () => { format="YYYY-MM-DD HH:mm:ss" disabledDate={disabledDate} showTime={{ defaultValue: moment('00:00:00', 'HH:mm:ss') }} - disabled={disabled} + disabled={disabled || (isReserve == "1")} showNow={false} style={{ width: "90%" }} /> + {isReserve == "1" && (//预约电子评标室 + <> + + + + + + + + )}

专家申请基本信息

-
+ + {/* { + display ? + + + + + + + + + : + + + + + + + + } */} - { - const value = e.target.value; - if (value && !checkedList?.length) { - form.resetFields(['representativeNumber']); - message.error("请先选择关联标段"); - return; - } - - // 继续触发计算 - form.setFieldsValue({ representativeNumber: value }); - calculateExpertNumber(); - }} - /> + + + + - - - - - - - - {() => { - const expertNumber = form.getFieldValue('expertNumber'); - if (!expertNumber || expertNumber <= 0) return null; - return ( - - - rowKey="uid" - toolBarRender={false} - columns={professColumns} - dataSource={tableData} - recordCreatorProps={{ - newRecordType: 'dataSource', - hidden: disabled, - creatorButtonText: '新增', - record: () => ({ - uid: String(Date.now()), - // id: "", - // extractNumber: "", - // specialityId: "", - }), - }} - editable={{ - type: 'multiple', - editableKeys, - onChange: (keys, rows) => { - setEditableRowKeys(keys); - }, - onValuesChange: (record, recordList) => { - setTableData(recordList); - }, - actionRender: (row, _, dom) => { - return [dom.delete]; - }, - }} - /> - - ); - }} - - - {/* - */} +
) } - //抽取专业-表格类型定义 - type DataSourceType = { - id?: string; - extractNumber: number; - specialityId?: string; - }; - const professColumns: ProColumns[] = [ - { - title: '主键', - dataIndex: 'id', - hideInTable: true, - }, - { - title: '专业名称', - dataIndex: 'specialityId', - valueType: 'select', - valueEnum: professionalMap, - width: '30%', - }, - { - title: '人数', - dataIndex: 'extractNumber', - width: '30%', - renderFormItem: (_, { record }) => { - return ; - }, - }, - { - title: '操作', - valueType: 'option', - hideInTable: disabled, - }, - ]; - /** - * 校验函数 - * @param schemaData - * @returns - */ - const checkData = (schemaData: any) => { - let res = true; - let spec = 0; - let specNum = 0; - for (let i = 0; i < schemaData?.extractSpecialityList?.length; i++) { - const item = schemaData?.extractSpecialityList?.[i]; - if ( - item.hasOwnProperty("specialityId") && - item.hasOwnProperty("extractNumber") - ) { - spec += 1; - } - if (item?.extractNumber) { - specNum += Number(item?.extractNumber); - } - } - console.log("expertNumber", schemaData?.expertNumber, "specNum", specNum); - if (Number(schemaData?.expertNumber) !== specNum) { - res = false; - message.error("抽取专业人数与专家需求人数不符!"); - } - return res; - } - const checkSpecialityUnique = () => { - let res = true; - const specialityIds = tableData.map(item => item.specialityId).filter(Boolean); - const unique = [...new Set(specialityIds)]; - if (specialityIds.length !== unique.length) { - res = false; - message.error('抽取专业名称不可重复选择'); - } - return res; - }; - const idToNameMap = Object.entries(professionalMap || {}).reduce((acc, [id, option]) => { - acc[id] = option.text; - return acc; - }, {} as Record); - const idToCodeMap = Object.entries(professionalMap || {}).reduce((acc, [id, option]) => { - acc[id] = option.text; - return acc; - }, {} as Record); - // 遍历表格数据并附加专业名称 - const enhancedData = tableData.map(item => { - const name = idToNameMap[item.specialityId] || ''; // 获取对应名称 - const code = idToCodeMap[item.specialityId] || ''; // 获取对应名称 - return { - ...item, - specialityName: name, // 添加专业名称字段 - specialityNo: code, - }; - }); + // const tab2 = () => {//cqtab2 + // return ( + // <> + //
+ //

抽取条件

+ //
+ // + //
+ //
+ // + //
+ //

回避单位信息

+ //
+ // + //
+ //
+ //
+ //

回避专家信息

+ //
+ // + //
+ //
+ // + // ) + // } const renderFooter = () => {//评审小组footer + // if (activeKey === '1') { + // return ( + // <> + // + // + // + // ); + // } + // if (activeKey === '2') { + // return ( + // <> + // + // + // + // + // ); + // } else { + // return (<>) + // } return ( <> */} + {/* */} + + {/* + + */}