From 0a3fa905cae0bc4cc5398b8e35737989d3406fbb Mon Sep 17 00:00:00 2001 From: jlzhangyx5 Date: Fri, 11 Jul 2025 08:44:50 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=8B=E5=86=99=E7=AD=BE=E5=90=8D=EF=BC=8C?= =?UTF-8?q?=E7=BA=BF=E4=B8=8B=E8=AF=84=E5=AE=A1=EF=BC=8C=E9=A2=84=E5=AE=A1?= =?UTF-8?q?=E8=AF=84=E5=A7=94=E4=BC=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/JuryRoom/router_menuJury.config.ts | 5 + config/YuShen/router_yushen.ts | 2 +- src/assets/signPen/signPen.png | Bin 0 -> 18773 bytes src/assets/styles.module.less | 31 + src/components/BiddingRoom/index.js | 71 +- src/models/bidev.js | 19 +- .../GroupLeader/components/GroupLeader.tsx | 162 +- .../ReviewResults/Jury/components/Jury.tsx | 163 +- .../ReviewResults/ManagerEntry/index.tsx | 13 + .../projectManager/ReviewResults/service.ts | 7 + .../JudgingPanel/List/preIndex.tsx | 1730 +++++++++++++++++ src/services/bidev.js | 5 + 12 files changed, 2183 insertions(+), 25 deletions(-) create mode 100644 src/assets/signPen/signPen.png create mode 100644 src/assets/styles.module.less create mode 100644 src/pages/Evaluation/projectManager/ReviewResults/ManagerEntry/index.tsx create mode 100644 src/pages/Tender/ProjectManager/JudgingPanel/List/preIndex.tsx diff --git a/config/JuryRoom/router_menuJury.config.ts b/config/JuryRoom/router_menuJury.config.ts index ca1e891..9e6c9ae 100644 --- a/config/JuryRoom/router_menuJury.config.ts +++ b/config/JuryRoom/router_menuJury.config.ts @@ -38,6 +38,11 @@ export default [//评标 path: '/EvaRoom/Evaluation/projectManager/ReviewResults/Manager', component: './Evaluation/projectManager/ReviewResults/Manager', }, + //评审结果-线下评审-项目经理 + { + path: '/EvaRoom/Evaluation/projectManager/ReviewResults/ManagerEntry', + component: './Evaluation/projectManager/ReviewResults/ManagerEntry', + }, //评审结果-组长 { path: '/EvaRoom/Evaluation/expert/ReviewResults/GroupLeader', diff --git a/config/YuShen/router_yushen.ts b/config/YuShen/router_yushen.ts index 00f765c..21e0e43 100644 --- a/config/YuShen/router_yushen.ts +++ b/config/YuShen/router_yushen.ts @@ -39,7 +39,7 @@ export default //评委会设置 资审发售 项目经理 { path: '/ProjectLayout/ZYuShen/Tender/ProjectManager/JudgingPanel', - component: './Tender/ProjectManager/JudgingPanel/List' + component: './Tender/ProjectManager/JudgingPanel/List/preIndex' }, //澄清 资审发售 项目经理 { diff --git a/src/assets/signPen/signPen.png b/src/assets/signPen/signPen.png new file mode 100644 index 0000000000000000000000000000000000000000..02aa52c9873c8fcb218476ba2c8ea57f1ded3d6d GIT binary patch literal 18773 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 Jw9V>`{{s|`=%4@q literal 0 HcmV?d00001 diff --git a/src/assets/styles.module.less b/src/assets/styles.module.less new file mode 100644 index 0000000..2081384 --- /dev/null +++ b/src/assets/styles.module.less @@ -0,0 +1,31 @@ +/** +* 手写签名用 +*/ +.container { + top: 10%; + left: 10%; + width: 100%; + height: 50vh; +} + +.sigContainer { + width: 100%; + height: 100%; + margin: 0 auto; + background-color: #fff; + cursor: auto; +} + +.sigPointer { + width: 100%; + height: 100%; + margin: 0 auto; + background-color: #fff; + cursor: url("./signPen/signPen.png"),Crosshair; +} + +.sigPad { + width: 100%; + height: 100%; + background: #fff; +} diff --git a/src/components/BiddingRoom/index.js b/src/components/BiddingRoom/index.js index dc412ce..986f754 100644 --- a/src/components/BiddingRoom/index.js +++ b/src/components/BiddingRoom/index.js @@ -5,7 +5,7 @@ import styles from './index.less'; import { connect } from "dva"; import { routerRedux } from 'dva/router'; import React, { useState, useEffect, useReducer } from 'react'; -import { getSessionUserData, getRoomId, getProMethod, getSessionRoleData, getIPassDecode, getDefId, getProId } from '@/utils/session'; +import { getSessionUserData, getRoomId, getRoomStatus, getProMethod, getSessionRoleData, getIPassDecode, getDefId, getProId, getOfflineStatusById } from '@/utils/session'; import { getURLInformation } from '@/utils/CommonUtils'; import { getLeader, isShowResult, isShowCount, getErrorStatus, getRiskStatus, isShowRiskModal, saveConfirm, isLeaderConfirm } from './service'; import logo from '@/images/opening/logo.svg' @@ -34,8 +34,13 @@ const BiddingRoom = (props) => { let data = getSessionUserData(); //获取比选一阶段二次项目,自定义流程,当前供应商 const isBxOneSecondCustom = sessionStorage.getItem("isBxOneSecondCustom"); + //评审方式:状态:0-默认,1-已确认配置分工及组长 + const roomJuryConfigStatus = sessionStorage.getItem("roomJuryConfigStatus"); + //评审方式:0-线上、1-线下 + const roomReviewMethod = sessionStorage.getItem("roomReviewMethod"); //获取评审室id const roomId = getRoomId(); + const roomStatus = getRoomStatus(); const [list, setList] = useState(); //风险提示文字弹窗控制 const [riskVisible, setRiskVisible] = useState(false); @@ -96,6 +101,28 @@ const BiddingRoom = (props) => { path: "/EvaRoom/Evaluation/projectManager/ReviewResults/Manager", text: "评审结果" }] + //项目经理角色 + let managerOffList = [ + { + id: 1, + path: "/EvaRoom", + text: "基本信息" + }, + { + id: 2, + path: "/EvaRoom/BiddingDocumentsDecrypt", + text: `${responseType}文件查看` + }, + { + id: 3, + path: "/EvaRoom/Evaluation/BidControl/BidControlManager", + text: "风险点展示" + }, + { + id: 8, + path: "/EvaRoom/Evaluation/projectManager/ReviewResults/ManagerEntry", + text: "评审结果录入" + }] let JuryList = [ { id: 1, @@ -179,13 +206,25 @@ const BiddingRoom = (props) => { //评审结果页签点击事件 const onclick = async (path, id) => { + await getOfflineStatusById(roomId) if ((role == 'ebtp-agency-project-manager' || role == 'ebtp-purchase') && id == 8) { //代理&采购经理进入评审结果 - const success = await isClickResult(); - if (success) { - history.push({ pathname: path }); - setSelectedPath(path); + if (roomReviewMethod == 0){ + const success = await isClickResult(); + if (success) { + history.push({ pathname: path }); + setSelectedPath(path); + } else { + message.info("未到评审结果环节,无法进入评审结果") + } } else { - message.info("未到评审结果环节,无法进入评审结果") + console.log("roomJuryConfigStatus", roomJuryConfigStatus) + + if (roomJuryConfigStatus == 1) { + history.push({ pathname: path }); + setSelectedPath(path); + } else { + message.info("未到评审结果环节,无法进入评审结果") + } } } else if (role == 'ebtp-expert' && id == 8) {//专家进入评审结果 const success = await isClickResult(); @@ -246,14 +285,14 @@ const BiddingRoom = (props) => { if (res?.data == "Review") { return "/EvaRoom/Evaluation/expert/ReviewResults/Jury" } else { - const result = await isLeaderConfirm({ assessRoomId: roomId });//供应商股权关系-专家组长是否确认风险 - if (result?.success && result?.data) { + //TODO zyx暂时不需要const result = await isLeaderConfirm({ assessRoomId: roomId });//供应商股权关系-专家组长是否确认风险 + //TODO zyx暂时不需要if (result?.success && result?.data) { return "/EvaRoom/Evaluation/expert/ReviewResults/GroupLeader" - } else { - setIsResult(true) - setRiskVisible(true) - return false; - } + //TODO zyx暂时不需要} else { + //TODO zyx暂时不需要 setIsResult(true) + //TODO zyx暂时不需要 setRiskVisible(true) + //TODO zyx暂时不需要 return false; + //TODO zyx暂时不需要} } } else { return false @@ -347,7 +386,11 @@ const BiddingRoom = (props) => { } //代理&项目经理 if (role == "ebtp-agency-project-manager" || role == "ebtp-purchase") {//代理和采购经理 - setList(managerList); + if(true){ + setList(managerOffList); + }else { + setList(managerList); + } } else if (role == "ebtp-expert") {//专家 setList(JuryList) } else if (role == "ebtp-supplier") {//供应商 diff --git a/src/models/bidev.js b/src/models/bidev.js index d4d0e79..067150e 100644 --- a/src/models/bidev.js +++ b/src/models/bidev.js @@ -1,4 +1,4 @@ -import { fetchManagerList,openBizassessroom,resetVerificationCode,fetchJuryList,fetchSupplierList +import { fetchManagerList,openBizassessroom,openOffBizassessroom,resetVerificationCode,fetchJuryList,fetchSupplierList ,fetchJuryMemInfo,updateJuryMemInfo,validateVerificationCode,fetchbidslist ,fetchPreSupplierList,fetchPreList,fetchPreListt,bxmultifetchPreListt,pushRedirectRe,getCheckedByRoomId,checkOpenBidSupplier,zmmultiOpenBizassessroom } from '../services/bidev'; import { message } from 'antd'; @@ -43,6 +43,23 @@ export default { } callback(); }, + // 开启线下评审室 + *openOffBizassessroom({payload,callback}, { call, put }){ + const response =yield call(openOffBizassessroom,payload) + if(response?.code==200){ + // 查询招标代理列表 + const params={ + pageNo:1, + pageSize:10, + reviewMethod:1, + roomType: getURLInformation('roomType'), + tpId:getProId()//项目id + } + yield put({ type: 'fetchManagerList', payload: {...params} }); + message.success("开启成功!") + } + callback(); + }, // 开启评审室(多轮招募) *zmmultiOpenBizassessroom({payload,callback}, { call, put }){ const response =yield call(zmmultiOpenBizassessroom,payload) diff --git a/src/pages/Evaluation/expert/ReviewResults/GroupLeader/components/GroupLeader.tsx b/src/pages/Evaluation/expert/ReviewResults/GroupLeader/components/GroupLeader.tsx index 117f5fc..110048b 100644 --- a/src/pages/Evaluation/expert/ReviewResults/GroupLeader/components/GroupLeader.tsx +++ b/src/pages/Evaluation/expert/ReviewResults/GroupLeader/components/GroupLeader.tsx @@ -11,6 +11,8 @@ import { reviewReportExpertsConfirmed } from '../../Jury/service'; import { getDicData, getProMethod, getRoomId } from '@/utils/session'; import { btnAuthority } from '@/utils/authority'; import WebOffice0609, { WebOfficeRefProps } from '@/pages/webOffice/weboffice0609'; +import styles from '@/assets/styles.module.less' +import SignaturePad from 'react-signature-canvas' import SortEditableTable from './SortEditableTable'; import ReviewReportUpload from '@/utils/ReviewReportUpload'; import { ExclamationCircleOutlined } from '@ant-design/icons'; @@ -111,6 +113,10 @@ const GroupLeader: React.FC = () => { const [resultDisabledStatus, setResultDisabledStatus] = useState(false); //报价类型 true-百分比类型(折扣率,优惠率)false-数值类型(总价,单价) const [quotationMethod, setQuotationMethod] = useState(false); + //评审报告确认无误弹出窗 + const [visibleConfirm, setVisibleConfirm] = useState(false); + //鼠标指针ref + const pointRef = useRef() //评价方法 eval_method_1 最低价法 eval_method_2 综合评估法 const [evalMethodDict, setEvalMethodDict] = useState(''); //评审结果排序弹窗 @@ -1446,6 +1452,142 @@ const GroupLeader: React.FC = () => { setSpinning(false); }) } + + /** + * 评审报告确认无误(签字面板) + * zhoujianlong 2021.8.19 + * @returns + */ + const modalConfirm = () => { + //保存loading + const [loading, setLoading] = useState(false); + //关闭modal + const onCancel = () => { + init(); + setVisibleConfirm(false); + } + //canvas ref + let sigPad: any = {} + //手写签名存储 + const [saveTrimCanvas, setSaveTrimCanvas] = useState('') + const canvasBase64 = reviewReportData?.expertList?.[0]?.expertSign + useEffect(() => { + if(canvasBase64 != undefined){ + setSaveTrimCanvas(canvasBase64) + } + }, [canvasBase64]) + + /** + * 保存或发送调用接口 + * @params expertSign 签名产生的base64值 + * status true-保存 false-发送 + */ + const toSaveOrSend = (expertSign: string,status: boolean) => { + let params = { + expertOpinion: 1, + reportId: reviewReportData.id, + expertSign: expertSign + }; + if(status){ //判断是保存还是发送 + params["status"] = 0 + } + setLoading(true) + reviewReportExpertsConfirmed(params).then(res => { + if(res?.code == 200 && res?.success === true) { + commonMessage(res); + if(status) {} else { + onCancel(); + } + } + }).finally(() => { + setLoading(false) + }) + } + //提交 status true-保存 false-发送 + const submit = (status: boolean) => { + let emptyStatus = sigPad.isEmpty(); + if(emptyStatus) { + message.error("当前无签名内容,请先签名"); + return; + } + //得到画布 + let canvas = sigPad._canvas; + if (canvas.getContext) { + let ctx = canvas.getContext('2d'); + // 将canvas的透明背景设置成白色 + let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + for(var i = 0; i < imageData.data.length; i += 4) { + // 当该像素是透明的,则设置成白色 + if(imageData.data[i + 3] == 0) { + imageData.data[i] = 255; + imageData.data[i + 1] = 255; + imageData.data[i + 2] = 255; + imageData.data[i + 3] = 255; + } + } + ctx.putImageData(imageData, 0, 0); + } + let imgUrl = sigPad.toDataURL('image/png',1); + setSaveTrimCanvas(imgUrl) + toSaveOrSend(imgUrl,status) + } + //clear + const clear = () => { + sigPad.clear(); + } + //SignaturePad ref + const refRender = (ref: any) => { + sigPad = ref //存储ref + if(ref != null) { + ref?.clear() + ref?.fromDataURL(saveTrimCanvas) + } + } + return ( + <> + submit(false)}> + 发送 + , + , + , + , + 提示:按住鼠标左键进行签名 + ]} + > + +
+
pointRef.current.className = styles.sigPointer} onMouseUp={() => pointRef.current.className = styles.sigContainer}> + +
+
+
+
+ + ) + } /** * 评审报告存在问题 @@ -1529,7 +1671,7 @@ const GroupLeader: React.FC = () => { ) } /** - * 评审报告确认无误 + * 评审报告确认无误(无需手写签名) */ const beConfirmed = () => { setReviewReportLoading(true); @@ -1544,6 +1686,14 @@ const GroupLeader: React.FC = () => { }) } + //确认无误的手写签名判断方法 + const toReportConfirm = () => { + if(String(reviewReportData?.signStatus) == '1') {//判断是否启用手写签名 1-是 0-否 + setVisibleConfirm(true); + } else { + beConfirmed(); + } + } const saveTable = (data: any, config: any) => { let form = config?.form; form.validateFields().then((res: any) => { @@ -1606,11 +1756,12 @@ const GroupLeader: React.FC = () => { value={supplierTableData} onChange={setSupplierTableData} scroll={{ x: 1300 }} - onRow={record => { + onRow={(record, index) => { return { // 鼠标移入行 onMouseEnter: () => { setEditableRowKeys(resultDisabledStatus ? [] : [record.id]) + setEditableRowKeys((resultDisabledStatus || (assessId == "1558990153220034560" && index == 0)) ? [] : [record.id]) }, onMouseLeave: () => { setEditableRowKeys([]); @@ -1633,12 +1784,12 @@ const GroupLeader: React.FC = () => { {isNotEmpty(reviewReportData.id) && ( - + {reportFileList.length == 0 ? null : } - - + + @@ -1664,6 +1815,7 @@ const GroupLeader: React.FC = () => { )} {modelIssue()} + {modalConfirm()} {sortTableVisible && setSortTableVisible(false)} diff --git a/src/pages/Evaluation/expert/ReviewResults/Jury/components/Jury.tsx b/src/pages/Evaluation/expert/ReviewResults/Jury/components/Jury.tsx index 7f29f13..6337234 100644 --- a/src/pages/Evaluation/expert/ReviewResults/Jury/components/Jury.tsx +++ b/src/pages/Evaluation/expert/ReviewResults/Jury/components/Jury.tsx @@ -10,6 +10,8 @@ import Weboffice from "@/pages/webOffice/weboffice"; import { getDicData, getProMethod, getRoomId } from '@/utils/session'; import { btnAuthority } from '@/utils/authority'; import WebOffice0609, { WebOfficeRefProps } from '@/pages/webOffice/weboffice0609'; +import styles from '@/assets/styles.module.less' +import SignaturePad from 'react-signature-canvas' import ReviewReportUpload from '@/utils/ReviewReportUpload'; import { getFileListByBid } from '@/utils/DownloadUtils'; import MACAddressPrompt from '@/pages/Evaluation/BidControl/BidControlManager/components/MACAddressPrompt'; @@ -53,6 +55,10 @@ const Jury: React.FC = () => { const [ptcpMode, setPtcpMode] = useState(); //报价类型 %-百分比类型(折扣率,优惠率)元-数值类型(总价,单价) const [quotationMethod, setQuotationMethod] = useState('元'); + //评审报告确认无误弹出窗 + const [visibleConfirm, setVisibleConfirm] = useState(false); + //鼠标指针ref + const pointRef = useRef() //初始化显示字段 const candidateType = bidMethodDict == 'procurement_mode_1' || bidMethodDict == 'procurement_mode_2' ? '中标' : '中选';//初始化中标中选字段 //评审报告附件modal visible @@ -663,6 +669,143 @@ const Jury: React.FC = () => { ) } + + /** + * 评审报告确认无误(签字面板) + * zhoujianlong 2021.8.19 + * @returns + */ + const modalConfirm = () => { + //保存loading + const [loading, setLoading] = useState(false); + //关闭modal + const onCancel = () => { + init(); + setVisibleConfirm(false); + } + //canvas ref + let sigPad: any = {} + //手写签名存储 + const [saveTrimCanvas, setSaveTrimCanvas] = useState('') + const canvasBase64 = reviewReportData?.expertList?.[0]?.expertSign + useEffect(() => { + if(canvasBase64 != undefined){ + setSaveTrimCanvas(canvasBase64) + } + }, [canvasBase64]) + + /** + * 保存或发送调用接口 + * @params expertSign 签名产生的base64值 + * status true-保存 false-发送 + */ + const toSaveOrSend = (expertSign: string,status: boolean) => { + let params = { + expertOpinion: 1, + reportId: reviewReportData.id, + expertSign: expertSign + }; + if(status){ //判断是保存还是发送 + params["status"] = 0 + } + setLoading(true) + reviewReportExpertsConfirmed(params).then(res => { + if(res?.code == 200 && res?.success === true) { + commonMessage(res); + if(status) {} else { + onCancel(); + } + } + }).finally(() => { + setLoading(false) + }) + } + //提交 status true-保存 false-发送 + const submit = (status: boolean) => { + let emptyStatus = sigPad.isEmpty(); + if(emptyStatus) { + message.error("当前无签名内容,请先签名"); + return; + } + //得到画布 + let canvas = sigPad._canvas; + if (canvas.getContext) { + let ctx = canvas.getContext('2d'); + // 将canvas的透明背景设置成白色 + let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); + for(var i = 0; i < imageData.data.length; i += 4) { + // 当该像素是透明的,则设置成白色 + if(imageData.data[i + 3] == 0) { + imageData.data[i] = 255; + imageData.data[i + 1] = 255; + imageData.data[i + 2] = 255; + imageData.data[i + 3] = 255; + } + } + ctx.putImageData(imageData, 0, 0); + } + let imgUrl = sigPad.toDataURL('image/png',1); + setSaveTrimCanvas(imgUrl) + toSaveOrSend(imgUrl,status) + } + //clear + const clear = () => { + sigPad.clear(); + } + //SignaturePad ref + const refRender = (ref: any) => { + sigPad = ref //存储ref + if(ref != null) { + ref?.clear() + ref?.fromDataURL(saveTrimCanvas) + } + } + return ( + <> + submit(false)}> + 发送 + , + , + , + , + 提示:按住鼠标左键进行签名 + ]} + > + +
+
pointRef.current.className = styles.sigPointer} onMouseUp={() => pointRef.current.className = styles.sigContainer}> + +
+
+
+
+ + ) + } + /** * 评审报告 我有问题 */ @@ -783,7 +926,7 @@ const Jury: React.FC = () => { ) } /** - * 确认无误 + * 确认无误(无需手写签名) */ const submitReviewReport = () => { setReviewReportLoading(true); @@ -797,6 +940,15 @@ const Jury: React.FC = () => { init(); }) } + //确认无误的手写签名判断方法 + const toReportConfirm = () => { + console.log(String(reviewReportData?.signStatus)) + if(String(reviewReportData?.signStatus) == '1') {//判断是否启用手写签名 1-是 0-否 + setVisibleConfirm(true); + } else { + submitReviewReport(); + } + } /** * 获取表头 */ @@ -844,12 +996,14 @@ const Jury: React.FC = () => { { (isNotEmpty(reviewReportData) && isNotEmpty(reviewReportData.id)) && ( - + {reportFileList.length == 0 ? null : } - - + {/* + */} + + @@ -876,6 +1030,7 @@ const Jury: React.FC = () => { {modelIssue()} {modelProblem()} + {modalConfirm()} {reportUploadVisible && setReportUploadVisible(false)} diff --git a/src/pages/Evaluation/projectManager/ReviewResults/ManagerEntry/index.tsx b/src/pages/Evaluation/projectManager/ReviewResults/ManagerEntry/index.tsx new file mode 100644 index 0000000..6169442 --- /dev/null +++ b/src/pages/Evaluation/projectManager/ReviewResults/ManagerEntry/index.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import Manager from './components/Manager'; +/** + * 项目经理 + */ +const Index: React.FC = () => { + return ( + <> + + + ); +} +export default Index; \ No newline at end of file diff --git a/src/pages/Evaluation/projectManager/ReviewResults/service.ts b/src/pages/Evaluation/projectManager/ReviewResults/service.ts index acb2ea7..bbce483 100644 --- a/src/pages/Evaluation/projectManager/ReviewResults/service.ts +++ b/src/pages/Evaluation/projectManager/ReviewResults/service.ts @@ -51,3 +51,10 @@ export function finishFlow(id: any) { export function getAssessRoomStatus(id: any) { return request('/api/biz-service-ebtp-process/v1/bizassessroom/info/' + id); } +/** + * 生成签名报告 + * @param id + */ +export function saveSignPdfReport(id: any) { + return request('/api/biz-service-ebtp-evaluation/v1/review/report/signpdf/' + id); +} diff --git a/src/pages/Tender/ProjectManager/JudgingPanel/List/preIndex.tsx b/src/pages/Tender/ProjectManager/JudgingPanel/List/preIndex.tsx new file mode 100644 index 0000000..63c7ceb --- /dev/null +++ b/src/pages/Tender/ProjectManager/JudgingPanel/List/preIndex.tsx @@ -0,0 +1,1730 @@ +import React, { useEffect, useRef, useState } from 'react'; +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, getSecs } from './service'; +import moment from 'moment'; +import { getDefId, getProId, getProMethod, getProOpenTenderForm, getSessionUserData } from '@/utils/session'; +import { getURLInformation, isEmpty } from '@/utils/CommonUtils'; +import './judgList.less'; +import '@/assets/xsy_style.less'; +import FileDown from '@/utils/Download'; +import { UploadOutlined } from '@ant-design/icons'; +import { btnAuthority } from '@/utils/authority'; +import RiskPrevention from '@/utils/RiskPrevention'; +import { downloadPath } from '@/utils/DownloadUtils'; +import { sortBy } from 'lodash'; +import ExpertSchemeProfessionalInfo from './ExpertSchemeProfessionalInfo'; + +const JudgingPanel: React.FC<{}> = () => { + const modalHeight = window.innerHeight * 96 / 100; + const proId = getProId(); + const roomType = getURLInformation('roomType'); + // const tailLayout = { wrapperCol: { offset: 8, span: 20 }, }; + const formLayout = { labelCol: { span: 8 }, wrapperCol: { span: 16 }, }; + const form24Layout = { labelCol: { span: 4 }, wrapperCol: { span: 20 }, }; + const FormItem = Form.Item; + const [form] = Form.useForm(); + const { Option } = Select; + const { Panel } = Collapse; + const { TextArea } = Input; + const CheckboxGroup = Checkbox.Group; + // const { TabPane } = Tabs; + const actionRef = useRef(); + const [spin, spinSet] = useState(false); + const [loading, loadingSet] = useState(false); + const [sections, setSections] = useState([]); + const [sectionsVal, setSectionsVal] = useState([]); + const [modalVis, setModalVis] = useState(false);//新增评委会 + const [updateData, updateDataSet] = useState();//评委会modal数据 + const [memberVis, setMemberVis] = useState(false); + const [open, openSet] = useState(true);//评委会里是否有开启评标的评审室 true有开启的 false无 + const [allEnd, allEndSet] = 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 [userPhotoId, setUserPhotoId] = useState("");//电子评标室-录入外部专家-相片id 2022.8.29 zhoujianlong + const userData = getSessionUserData();//当前登录人用户信息 + + const [professionalMap, setProfessionalMap] = useState(); + //当前可编辑的行 + const [editableKeys, setEditableRowKeys] = useState([]); + const [tableData, setTableData] = useState([]); + function getShouName() { + const method = getProMethod(); + let showNameT: any = { zbr: '', bb: '', pb: '', }//相关标段 标书费 保证金 服务费 + // let num = 3; + if (method === 'procurement_mode_1' || method === 'procurement_mode_2') {//招标 + showNameT = { zbr: '招标人', bb: '标段', pb: '评标', }; + // num = 5; + } else if (method === 'procurement_mode_4') {//招募类 + showNameT = { zbr: '采购人', bb: '包件', pb: '评审', } + } else {//谈判类 + showNameT = { zbr: '采购人', bb: '采购包', pb: '评审', } + } + showNameSet(showNameT); + } + + //多选控制 + const [indeterminate, setIndeterminate] = useState(false); + const [checkAll, setCheckAll] = useState(false); + const [checkedList, setCheckedList] = useState(); + const [sectionCount, sectionCountSet] = useState(0);//控制获取标包 + + //创建评委会小组 + const saveG = async (fields: any) => { + const hide = message.loading('正在配置'); + try { + const success = await saveGroup({ ...fields }).then((res) => { + if (res?.code == 4004 && res?.success == false) { //2021.9.7 zhoujianlong 新增评委会保存增加风控 + const data = res?.data?.result == undefined ? [] : res?.data?.result + setRiskData(data) + setRiskVisible(true) + } + return res.success + }); + hide(); + if (success) { + message.success('配置成功'); + } + return true; + } catch (error) { + hide(); + message.error('配置失败请重试!'); + return false; + } + }; + + //主页表格 + const columns: ProColumns[] = [ + { title: '序号', valueType: 'index', width: 50, }, + { title: `${showName.bb}名称`, dataIndex: 'sectionName', }, + { title: '专家抽取数量', dataIndex: 'expertNumber', width: 120 }, + { title: `直接录入数量`, dataIndex: 'representativeNumber', width: 120 }, + { title: '预计开始时间', dataIndex: 'startTime', valueType: 'dateTime', width: '10%', }, + { title: '预计结束时间', dataIndex: 'endTime', valueType: 'dateTime', width: '10%', }, + { + title: '操作', dataIndex: 'option', width: '16%', + valueType: 'option', + render: (_, record) => { + if (record.status == 0 || record.status == 2) { + return ( + <> + {returnCheck(record, false)} + { + spinSet(true) + await del({ id: record.id }); + actionRef.current?.reload(); + spinSet(false) + }} + okText="确定" + cancelText="取消" + > + + + + + ) + } else if (record.status == 1) {//已提交申请 + return ( + <>{returnCheck(record, true)} + ) + } else if (record.status == 3) { + return ( + <> + {returnCheck(record, true)} + + + ); + } else { + return (<>) + } + } + }, + ]; + function returnCheck(record: any, check: any) {//返回操作列查看、修改按钮 + return ( + + ) + } + 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 handleMember = async (record: any) => { + form.resetFields(); + const res = await queryVoList({ id: record.id }); + await queryOpenStatus(record.id); + categorySet(res.data.juryCategoryVOList); + juryIdSet(record.id); + memberCountSet(memberCount + 1); + setMemberVis(true); + }; + + //删除 + const del = async (fields: any) => { + const hide = message.loading('正在删除'); + try { + const success = await delOne({ ...fields }).then((res) => { + return res.success + }); + hide(); + if (success) { + message.success('删除成功'); + return true; + } else { + message.error('删除失败'); + return false; + } + } catch (error) { + hide(); + message.error('删除失败请重试!'); + actionRef.current?.reload(); + return false; + } + }; + //提交申请 + const apply = async (fields: any) => { + const hide = message.loading('正在提交'); + try { + const success = await await applyFor({ ...fields }).then((res) => { + return res.success + }); + hide(); + if (success) { + message.success('提交成功'); + return true; + } else { + message.error('提交失败'); + return false; + } + } catch (error) { + hide(); + message.error('提交失败请重试!'); + actionRef.current?.reload(); + return false; + } + }; + + //创建修改 标包方法 + 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); + 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 : []); + setIndeterminate(false); + setCheckAll(e.target.checked); + }; + + + function disabledDate(current: any) {//日期选择 + return current && current < moment().startOf('day'); + } + + + const rule = (mes: any) => {//创建修改 规则 + return [ + { + required: true, + message: `请录入${mes}`, + }, + ] + } + //专家账号解锁 + const unlockExportAccount = async (record: any, juryId: any, proId: any) => { + try { + const res = await unlockAccount(record.id, juryId, proId); + if (res.success) { + message.success(res.data); + } + } catch (error) { + + } + } + + useEffect(() => {//获取标包信息 + + let params = { tpId: proId, roomType: roomType }; + let checked: any = []; + if (updateData !== undefined && JSON.stringify(updateData) !== "{}") { + params['juryId'] = updateData.id; + updateData.juryRoomList.map((item: any) => { + checked.push(item.sectionId); + }); + setCheckedList(checked); + } else { + 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);//初始化赋值数据 + }) : null; + + }, [sectionCount]); + + useEffect(() => {//给表单赋值 + form.setFieldsValue({ + juryType: updateData != undefined ? updateData.juryType : null, + representativeNumber: updateData != undefined ? updateData.representativeNumber : null, + expertNumber: updateData != undefined ? updateData.expertNumber : null, + startTime: updateData != undefined ? updateData.reserveStatus == 1 ? moment(updateData.elecEvalRoomReserve.reserveStartDate, 'yyyy-MM-DD HH:mm:ss') : moment(updateData.startTime, 'yyyy-MM-DD HH:mm:ss') : null, + 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, + }) + }, [updateData]); + + const group = () => {//评审小组modal + return ( + <> + { + setModalVis(false); + updateDataSet({}); + setTableData([]); + setCheckedList([]); + form.resetFields(); + }} + > + {tab1()} + + + ) + } + //人数校验 + function checkMan(count: number, expCon: any) { + let res = true; + const defId = getDefId();//16 + const method = getProMethod();//9 + const yushenType = getProOpenTenderForm(); + 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") + // } + } else if (method === 'procurement_mode_3') {//比选 + if (expCon < count * 2 / 5) { + res = false; + message.error("专家人数不少于评委会总人数2/5") + } + } else if (method === 'procurement_mode_4') {//招募 + manNumT = 1; + if (count < manNumT) { + res = false; + message.error(`评委会总人数需大于1`) + } else { + return true; + } + } else if (method === 'procurement_mode_5') {//竞谈 + if (manNumT = 5) {//最低价 + manNumT = 3; + } + if (expCon < count * 1 / 3) { + res = false; + message.error("专家人数不少于评委会总人数1/3") + } + } else if (method === 'procurement_mode_6') {//单一 + manNumT = 3; + } else if (method === 'procurement_mode_9') {//单一 + manNumT = 3; + } else if (defId == 'inquiry') {//询价 + manNumT = 3; + } + if (count < manNumT || count % 2 == 0) {//校验总人数 + res = false; + message.error(`评委会总人数需大于等于${manNumT}人且为单数`) + } + return res + } + //添加抽取drawer + const formLayoutDrawer = { labelCol: { span: 8 }, wrapperCol: { span: 16 }, }; + const tailLayoutDrawer = { wrapperCol: { offset: 8, span: 20 }, }; + const [cqData, cqDataSet] = useState([]);//抽取表格,提交时需要 + // 在组件内定义函数 + const calculateExpertNumber = () => { + // 没有选中标段时直接返回,不清空 expertNumber 是为了兼容已有值的情况 + if (!checkedList || checkedList.length === 0) return; + + 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; + } + + const juryNumber = parseInt(uniqueJuryNumbers[0]) || 0; + const repNumber = parseInt(form.getFieldValue('representativeNumber')) || 0; + + + 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(() => { + if (Array.isArray(sections) && sections.length > 0) { + calculateExpertNumber(); + } + }, [form.getFieldValue('representativeNumber'), checkedList, sections]); + const tab1 = () => {//cqtab1 + return ( + <> +

关联{showName.bb}

+
+ + + { return '1' } + }, + () => ({ + validator(rule, value,) { + let leg = 0; + checkedList != undefined ? leg = checkedList.length : leg = 0; + if (leg > 0) { + return Promise.resolve(); + } else { + message.error(`请选择${showName.bb}`) + return Promise.reject(`请选择${showName.bb}`); + } + }, + }), + ]} + > + { + disabled ? + {checkSectionName} + : <> + { + sections.length > 0 ? <> +
+ 全选 +
+
+ + + { + sections.map((item: any, index: any) => { + return ( + + {item.sectionName} + + + + ) + }) + } + + + + : 无可用{showName.bb} + } + + } +
+ +
+
+

评标时间和地点管理

+
+ + + + + + + + + + + + + +
+

专家申请基本信息

+
+ + + { + // 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); + } + } + 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 + return ( + <> + + + + ) + }; + + //成员管理 + const [add, setAdd] = useState(false);//录入外部专家Drawer显隐 + const [changeMan, changeManSet] = useState(false);//更换Drawer显隐 + const [juryId, juryIdSet] = useState('');//更换专家所需评委会id + const [reason, reasonSet] = useState('');//更换专家原因 + const [category, categorySet] = useState([]);//类别 juryCategoryVOList + const [daibiao, daibiaoSet] = useState({});//代表 + const [jishu, jishuSet] = useState({});//技术 + const [shangwu, shangwuSet] = useState({});//商务 + const [falv, falvSet] = useState({});//法律 + const [qita, qitaSet] = useState({});//其它 + const [luru, luruSet] = useState(0);//录入外部传类别 + const [updateKeyMem, updateKeyMemSet] = useState(-1);//触发修改存key + const [memberCount, memberCountSet] = useState(0);//刷新成员管理页面 + const [checkBoxs, checkBoxsSet] = useState([]);//更换专家按钮组 + const [checkBoxsStatus, checkBoxsStatusSet] = useState({});//更换专家按钮组 + const [changeMemberId, changeMemberIdSet] = useState('');//更换专家id + const [changeBtn, changeBtnSet] = useState(true);//更换按钮显隐 + const [formMem] = Form.useForm(); + function reset() {//重置 + categorySet([]); + luruSet(0); + updateKeyMemSet(-1); + checkBoxsSet([]); + changeMemberIdSet(''); + setCheckedList([]); + form.resetFields(); + } + const columnsMember: ProColumns[] = [//成员管理页面表格 + { title: '序号', valueType: 'index', width: 50, }, + { title: '专家姓名', dataIndex: 'name', }, + { title: '手机号码', dataIndex: 'mobile', }, + { title: '证件号码', dataIndex: 'certificate', }, + { title: '工作单位', dataIndex: 'workunit', }, + // { title: '通知状态', dataIndex: 'status', }, + // { title: '通知结果备注', dataIndex: 'remark', }, + // { + // title: '照片', + // dataIndex: 'faceId', + // render: (_, record) => { + // if (record.faceId) { + // return { + return ( + <> + + { confirmMem(record); }} + okText="确定" + cancelText="取消" + > + + + + + + + ); + } + }, + ]; + function returnType(inId: any) {//修改时根据record.categoryId判断类别 + category != undefined ? category.map((item: any,) => { + if (item.id == inId) { + if (item.category == 1) { + luruSet(1); + } else if (item.category == 2 && item.subCategory == 1) { + luruSet(2); + } else if (item.category == 2 && item.subCategory == 2) { + luruSet(3); + } else if (item.category == 2 && item.subCategory == 3) { + luruSet(4); + } else if (item.category == 2 && item.subCategory == 4) { + luruSet(5); + } + } + }) : null; + } + function returnCate(luru: any) {//取data + switch (luru) { + case 1: return daibiao; + case 2: return jishu; + case 3: return shangwu; + case 4: return falv; + case 5: return qita; + default: break; + } + } + function dataSet(data: any, luru: any) {//塞data + let dataT = []; + switch (luru) { + case 1: dataT = daibiao; dataT.juryMemberList = data; daibiaoSet(dataT); break; + case 2: dataT = jishu; dataT.juryMemberList = data; jishuSet(dataT); break; + case 3: dataT = shangwu; dataT.juryMemberList = data; shangwuSet(dataT); break; + case 4: dataT = falv; dataT.juryMemberList = data; falvSet(dataT); break; + case 5: dataT = qita; dataT.juryMemberList = data; qitaSet(dataT); break; + default: break; + } + } + function returnHeader(name: any) { + return ( +

{name}

+ ) + } + function returnCheckBox(type: any) {//更改选人 + return ( + <> + {type != undefined ? + type.map((item: any) => { + return ( + { + let data = [...checkBoxs]; + let status = { ...checkBoxsStatus }; + if (e.target.checked) { + data.push({ memberId: item.id }); + status[item.id] = true; + } else { + data.map((item: any, index: any) => { + item.memberId == e.target.value ? data.splice(index, 1) : null; + }) + status[item.id] = false; + } + checkBoxsStatusSet(status); + checkBoxsSet(data); + }}>{item.name} + ) + }) : null + } + + ) + } + useEffect(() => {//拆类别 + daibiaoSet({}); + jishuSet({}); + shangwuSet({}); + falvSet({}); + qitaSet({}); + category.map((item: any) => { + if (item.category == 1) { + daibiaoSet(item); + } else { + if (item.subCategory == 1) { + jishuSet(item); + } else if (item.subCategory == 2) { + shangwuSet(item); + } else if (item.subCategory == 3) { + falvSet(item); + } else if (item.subCategory == 4) { + qitaSet(item); + } + } + }); + }, [memberCount]); + async function queryOpenStatus(id: any) {//查是否开启评标 id:评委会id + await roomStatus(id).then((res) => { + if (res.data) { + openSet(res.data.anyOpenRoom); + allEndSet(res.data.allEndRoom); + } + }) + } + const returnPanel = () => {//返回手风琴 + return ( + <> + {daibiao.juryMemberList != undefined ? + + [ + + ]} + /> + + : null} + {jishu.juryMemberList != undefined ? + + [ + + ]} + /> + + : null} + {shangwu.juryMemberList != undefined ? + + [ + + ]} + /> + + : null} + {falv.juryMemberList != undefined ? + + [ + + ]} + /> + + : null} + {qita.juryMemberList != undefined ? + + [ + // + ]} + /> + + : null} + + ) + } + function checkRe(data: any) {//查重 + let pass = true; + !checkReRoot(daibiao, data, 1) ? pass = false : null; + !checkReRoot(jishu, data, 2) ? pass = false : null; + !checkReRoot(shangwu, data, 3) ? pass = false : null; + !checkReRoot(falv, data, 4) ? pass = false : null; + !checkReRoot(qita, data, 5) ? pass = false : null; + return pass; + } + function checkReRoot(type: any, data: any, updateType: any) {//查重 + const { certificate, mobile, name } = data; + let pass = true; + if (certificate == '' || mobile == '' || name == '' || certificate == undefined || mobile == undefined || name == undefined) { + pass = false; + } else { + if (type.juryMemberList != undefined && updateType != luru) { + for (const item of type.juryMemberList) { + if (item.certificate == certificate) {//身份证号重复 + pass = false; + message.error('身份证号重复'); + break; + } + if (item.mobile == mobile) {//手机号重复 + pass = false; + message.error('手机号重复'); + break; + } + } + } else if (type.juryMemberList != undefined && updateType == luru) { + for (const item of type.juryMemberList) { + if (item.certificate == certificate && item.key != updateKeyMem) {//身份证号重复 + pass = false; + message.error('身份证号重复'); + break; + } + if (item.mobile == mobile && item.key != updateKeyMem) {//手机号重复 + pass = false; + message.error('手机号重复'); + break; + } + } + } + } + + return pass; + } + const returnDrawerMem = () => {//成员管理抽屉(专家信息)添加、修改 + return ( + <> + { + setAdd(false); + changeBtnSet(true); + // formMem.resetFields(); + }} + visible={add} + getContainer={false} + style={{ position: 'absolute' }} + > +
+ + + + + + + + + + + + + + + + + + + + + + +
+
+ + ) + } + const returnDrawerChange = () => {//更换专家抽屉 + return ( + <> + changeManSet(false)} + visible={changeMan} + getContainer={false} + style={{ position: 'absolute' }} + footer={
+    + +
} + > + {daibiao.juryMemberList != undefined ? <> + {returnHeader('直接录入专家')} + {returnCheckBox(daibiao.juryMemberList)} + : null} + {jishu.juryMemberList != undefined ? <> + {returnHeader('技术类专家')} + {returnCheckBox(jishu.juryMemberList)} + : null} + {shangwu.juryMemberList != undefined ? <> + {returnHeader('商务类专家')} + {returnCheckBox(shangwu.juryMemberList)} + : null} + {falv.juryMemberList != undefined ? <> + {returnHeader('法律类专家')} + {returnCheckBox(falv.juryMemberList)} + : null} + {qita.juryMemberList != undefined ? <> + {returnHeader('抽取专家')} + {returnCheckBox(qita.juryMemberList)} + : null} +

取消原因: +