11.21 11.15电子评标室预约优化

This commit is contained in:
jl-zhoujl2
2022-11-21 10:38:05 +08:00
parent c852fd29e0
commit f42e7dbacf
6 changed files with 403 additions and 238 deletions

View File

@ -13,15 +13,15 @@ interface OutsourcingManageProps {
onCancel: () => void,
onSubmit: () => void,//保存回调
open: boolean,//评委会里是否有开启评标的评审室 true有开启的 false无
assistData: any[],//协人数据
assistNumber: string,//协人数量
assistData: any[],//协人数据
assistNumber: string,//协人数量
juryId: string,//评委会id
}
const formLayoutDrawer = { labelCol: { span: 8 }, wrapperCol: { span: 16 }, };
const tailLayoutDrawer = { wrapperCol: { offset: 8, span: 20 }, };
/**
* 协管理
* 协管理
* @param props
* @returns
*/
@ -34,11 +34,11 @@ const OutsourcingManage: React.FC<OutsourcingManageProps> = (props) => {
//loading
const [loading, setLoading] = useState<boolean>(false);
//录入协人员 drawerVisible
//录入协人员 drawerVisible
const [add, setAdd] = useState<boolean>(false);
//协人员数据
//协人员数据
const [dataSource, setDataSource] = useState<any[]>([]);
//协人员-修改存key
//协人员-修改存key
const [updateKeyMem, updateKeyMemSet] = useState<number>(-1);
//删除回调
const confirmMem = async (record: any) => {
@ -113,7 +113,7 @@ const OutsourcingManage: React.FC<OutsourcingManageProps> = (props) => {
width={"60%"}
centered
destroyOnClose
title="协人员管理"
title="协人员管理"
bodyStyle={{ maxHeight: modalHeight - 140, minHeight: modalHeight * 0.6, overflow: 'auto', padding: '16px 0px 0px 0px' }}
okButtonProps={{ hidden: btnAuthority(['ebtp-agency-project-manager', 'ebtp-purchase']), disabled: open, loading: loading }}
cancelText="取消"
@ -138,18 +138,20 @@ const OutsourcingManage: React.FC<OutsourcingManageProps> = (props) => {
setLoading(false);
})
} else {
message.error('协人数不符合规定!');
message.error('协人数不符合规定!');
}
}}
onCancel={onCancel}
>
<Spin spinning={loading}>
<div className='headerDiv pl24 pr16'>
<h3 className="first-title floatLeft"></h3>
<h3 className="first-title"></h3>
</div>
<div style={{ marginTop: '11px', marginLeft: '35px' }}>
<span style={{ color: '#b30000' }}></span>
</div>
<Collapse className='xsy-collapse' style={{ marginTop: '16px' }} defaultActiveKey={['1']}>
<Panel header={`人员(最大人数:${assistNumber})`} key="1">
<Panel header={`协人员(最大人数:${assistNumber})`} key="1">
<ProTable
key='proTable1'
columns={columnsMember}
@ -168,14 +170,14 @@ const OutsourcingManage: React.FC<OutsourcingManageProps> = (props) => {
message.error('录入人数已满')
}
}}
> </Button>
> </Button>
]}
/>
</Panel>
</Collapse>
</Spin>
<Drawer
title="录入协人员"
title="录入协人员"
placement="right"
width={'50%'}
onClose={() => { setAdd(false) }}
@ -254,6 +256,7 @@ const OutsourcingManage: React.FC<OutsourcingManageProps> = (props) => {
<Form.Item
name="faceId"
style={{ display: 'inline-block', width: '60%' }}
extra={<span style={{ color: '#b30000' }}></span>}
>
<ExpertPhotoUpload maxSize={200} />
</Form.Item>

View File

@ -1,5 +1,5 @@
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 } from 'antd'
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 } from './service';
import moment from 'moment';
@ -8,7 +8,7 @@ import { getURLInformation } from '@/utils/CommonUtils';
import './judgList.less';
import '@/assets/xsy_style.less';
import FileDown from '@/utils/Download';
import { UploadOutlined } from '@ant-design/icons';
import { DownOutlined, EllipsisOutlined, UploadOutlined } from '@ant-design/icons';
import { btnAuthority } from '@/utils/authority';
import RiskPrevention from '@/utils/RiskPrevention';
import BidEvalAppointment from '@/components/ElecBidEvaluation/BidEvalAppointment';
@ -16,6 +16,7 @@ import { downloadPath } from '@/utils/DownloadUtils';
import ExpertPhotoUpload from '@/components/ElecBidEvaluation/ExpertPhotoUpload';
import { sortBy } from 'lodash';
import OutsourcingManage from './OutsourcingManage';
import { dateTimeFormatter } from '@/utils/DateUtils';
const JudgingPanel: React.FC<{}> = () => {
const modalHeight = window.innerHeight * 96 / 100;
@ -23,11 +24,13 @@ const JudgingPanel: React.FC<{}> = () => {
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 { Paragraph, Text, Link, Title } = Typography;
const CheckboxGroup = Checkbox.Group;
// const { TabPane } = Tabs;
const actionRef = useRef<ActionType>();
@ -53,7 +56,7 @@ const JudgingPanel: React.FC<{}> = () => {
const [initEvalTime, setInitEvalTime] = useState<any>();//电子评标室-时间输入框默认时间(跟随标段) 2022.9.27 zhoujianlong
// const [userPhotoId, setUserPhotoId] = useState<string>("");//电子评标室-录入外部专家-相片id 2022.8.29 zhoujianlong
const [appoType, setAppoType] = useState<string>("0");//电子评标室-预约框状态 2022.8.29 zhoujianlong
const [isReserve, setIsReserve] = useState<string>("0");//电子评标室-是否预约评标室 2022.9.23 zhoujianlong 0-不预约 1-预约
const [isReserve, setIsReserve] = useState<string>("0");//电子评标室-是否预约电子评标室 2022.9.23 zhoujianlong 0-不预约 1-预约
const userData = getSessionUserData();//当前登录人用户信息
const [assistVisible, setAssistVisible] = useState<boolean>(false);//协办管理visible 2022.10.10 zhoujianlong
@ -118,6 +121,8 @@ const JudgingPanel: React.FC<{}> = () => {
{ title: '专家数量', dataIndex: 'expertNumber', width: '8%', },
{ title: `${showName.zbr}代表数量`, dataIndex: 'representativeNumber', width: '12%', },
{ title: `${showName.bb}名称`, dataIndex: 'sectionName', },
{ title: '是否预约电子评标室', dataIndex: 'reserveStatus', width: '10%', valueEnum: { 1: "是", 0: "否" } },
{ title: `${showName.pb}地点`, dataIndex: 'evalLocation', width: '15%', ellipsis: true, render: (_: any, record: any) => sectionNameValue(record.elecEvalRoomReserve, record.evalLocation, record.reserveStatus) },
{ title: '预计开始时间', dataIndex: 'startTime', valueType: 'dateTime', width: '10%', },
{ title: '预计结束时间', dataIndex: 'endTime', valueType: 'dateTime', width: '10%', },
{
@ -173,10 +178,10 @@ const JudgingPanel: React.FC<{}> = () => {
setAssistVisible(true);
}}></Button>}
{record.elecEvalRoomReserve && (record.elecEvalRoomReserve.status == -1 || record.elecEvalRoomReserve.status == 0) && <Button type='text' onClick={() => {
setSelectEvalData(record.elecEvalRoomReserve)
setSelectEvalData({ ...record.elecEvalRoomReserve, areaAddress: record.evalLocation })
setAppoType("1");
setSelectEvalVisible(true);
}}></Button>}
}}></Button>}
</>
);
} else {
@ -203,7 +208,7 @@ const JudgingPanel: React.FC<{}> = () => {
sectionCountSet(sectionCount + 1);
disabledSet(check);
// readOnlySet(check);
setSelectEvalData(record.elecEvalRoomReserve)
setSelectEvalData({ ...record.elecEvalRoomReserve, areaAddress: record.evalLocation })
checkSectionNameSet(record.sectionName);
}}>{check ? '查看' : '修改'}</Button>
)
@ -283,6 +288,47 @@ const JudgingPanel: React.FC<{}> = () => {
},
]
}
//评标地点 评标室更多信息
const evalLocationTitle = (detail: any, updateData: any) => {
return (
<div className="eval-location-title">
<p>{updateData?.reserveStatus == 1 ? updateData.elecEvalRoomReserve.areaAddress : updateData?.evalLocation}</p>
<p>{detail?.numberInMeeting}</p>
<p>{detail?.contactName}</p>
<p>{detail?.contactTel}</p>
</div>
)
}
//说明
const sectionNameValue = (detail: any, evalLocation: any, reserveStatus: any) => {
const address = reserveStatus == 1 ? detail?.areaAddress : evalLocation;
const content = (
<Paragraph>
<blockquote style={{ width: '300px' }}>
<div className="eval-location-title">
<p>{address}</p>
<p>{detail?.numberInMeeting}</p>
<p>{detail?.contactName}</p>
<p>{detail?.contactTel}</p>
</div>
</blockquote>
</Paragraph>
)
let text = address.length > 30 ? address.slice(0, 30) + "..." : address;
return (
detail != null ? (
<span>
<Popover content={content} trigger={["hover"]} placement="bottom">
{text}
<DownOutlined style={{ cursor: 'pointer', marginLeft: '4px' }} />
</Popover>
</span>
) : (
<span>{text}</span>
)
)
}
useEffect(() => {//获取标包信息
@ -317,14 +363,14 @@ const JudgingPanel: React.FC<{}> = () => {
}, [sectionCount]);
useEffect(() => {//给表单赋值
updateData && setIsReserve(String(updateData?.reserveStatus));//赋值给是否预约评标室
updateData && setIsReserve(String(updateData?.reserveStatus));//赋值给是否预约电子评标室
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.evalLocation : 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,
@ -744,18 +790,109 @@ const JudgingPanel: React.FC<{}> = () => {
</Col>
</Row>
</Form>
<h3 className="first-title"></h3>
<Form {...formLayout}>
<h3 className="first-title"></h3>
<Form {...formLayout} form={form}>
<Row>
<Col span={12}><FormItem
label="是否预约评标室"
<Col span={24}><FormItem
label="是否预约电子评标室"
required
{...form24Layout}
extra={<span style={{ color: '#b30000' }}></span>}
>
<Radio.Group onChange={onRadioChange} value={isReserve} disabled={disabled}>
<Radio value="1"></Radio>
<Radio value="0"></Radio>
</Radio.Group>
</FormItem></Col>
{isReserve == "1" && !disabled ? (//预约电子评标室
<Col span={12}>
<Form.Item label={`${showName.pb}地点`} style={{ marginBottom: 0 }} required>
<FormItem
name="evalLocation"
rules={[...rule(`${showName.pb}地点`), { type: 'string', message: '请输入正确内容' }, { max: 100, message: '内容超长' }]}
style={{ display: 'inline-block', width: 'calc(60% - 8px)' }}
>
<Input style={{ width: "100%" }} disabled={true} placeholder={`${showName.pb}地点`} />
</FormItem>
<Form.Item
style={{ display: 'inline-block', width: 'calc(40% - 8px)', margin: '0 8px' }}
>
<Button type='primary' onClick={() => selectEvalClick()} disabled={disabled}></Button>
</Form.Item>
</Form.Item>
</Col>
) : (
<Col span={12}>
<FormItem
name="evalLocation"
label={`${showName.pb}地点`}
rules={[...rule(`${showName.pb}地点`), { type: 'string', message: '请输入正确内容' }, { max: 100, message: '内容超长' }]}
>
<Input style={{ width: "90%" }} disabled={disabled} placeholder={`${showName.pb}地点`}
addonAfter={
disabled && <Tooltip title={() => evalLocationTitle(selectEvalData, updateData)}>
<EllipsisOutlined />
</Tooltip>
} />
</FormItem>
</Col>
)}
{isReserve == "1" && (//预约电子评标室
<Col span={12}><FormItem
name="assistNumber"
label="外协人员数量"
rules={[{ type: 'string', message: '请输入正确内容' }, { pattern: /^([1-9]?\d|100)$/, message: '请输入正确数值' }]}
tooltip="在合规情况下临时可进入电子评标室人员,如视察领导等。"
>
<Input style={{ width: "90%" }} type="number" disabled={disabled} />
</FormItem></Col>
)}
<Col span={12}><FormItem
name="startTime"
label={`预计${showName.pb}开始时间`}
rules={rule('开始时间')}
>
<DatePicker
format="YYYY-MM-DD HH:mm:ss"
disabledDate={disabledDate}
showTime={{ defaultValue: moment('00:00:00', 'HH:mm:ss') }}
disabled={disabled || (isReserve == "1")}
showNow={false}
style={{ width: "90%" }}
/>
</FormItem></Col>
<Col span={12}><FormItem
name="endTime"
label={`预计${showName.pb}结束时间`}
rules={rule('结束时间')}
>
<DatePicker
format="YYYY-MM-DD HH:mm:ss"
disabledDate={disabledDate}
showTime={{ defaultValue: moment('00:00:00', 'HH:mm:ss') }}
disabled={disabled || (isReserve == "1")}
showNow={false}
style={{ width: "90%" }}
/>
</FormItem></Col>
{isReserve == "1" && (//预约电子评标室
<>
<Col span={12}><FormItem
name="reserveBy"
label="预约人"
rules={[{ required: true, message: `请录入预约人` }, { max: 100, message: '内容超长' }]}
>
<Input style={{ width: "90%" }} disabled={true} placeholder="预约人" />
</FormItem></Col>
<Col span={12}><FormItem
name="reserveContactNumber"
label="预约人联系方式"
rules={[{ required: true, message: `请录入预约人联系方式` }, { max: 100, message: '内容超长' }]}
>
<Input type="number" style={{ width: "90%" }} disabled={true} placeholder="预约人联系方式" />
</FormItem></Col>
</>
)}
</Row>
</Form>
<h3 className="first-title"></h3>
@ -813,34 +950,7 @@ const JudgingPanel: React.FC<{}> = () => {
>
<Input style={{ width: "90%" }} type="number" disabled={disabled} placeholder="专家数量" />
</FormItem></Col>
{isReserve == "1" ? (//预约评标室
<Col span={12}>
<Form.Item label={`${showName.pb}地点`} style={{ marginBottom: 0 }} required>
<FormItem
name="evalLocation"
rules={[...rule(`${showName.pb}地点`), { type: 'string', message: '请输入正确内容' }, { max: 100, message: '内容超长' }]}
style={{ display: 'inline-block', width: 'calc(60% - 8px)' }}
>
<Input style={{ width: "100%" }} disabled={true} placeholder={`${showName.pb}地点`} />
</FormItem>
<Form.Item
style={{ display: 'inline-block', width: 'calc(40% - 8px)', margin: '0 8px' }}
>
<Button type='primary' onClick={() => selectEvalClick()} disabled={disabled}></Button>
</Form.Item>
</Form.Item>
</Col>
) : (
<Col span={12}>
<FormItem
name="evalLocation"
label={`${showName.pb}地点`}
rules={[...rule(`${showName.pb}地点`), { type: 'string', message: '请输入正确内容' }, { max: 100, message: '内容超长' }]}
>
<Input style={{ width: "90%" }} disabled={disabled} placeholder={`${showName.pb}地点`} />
</FormItem>
</Col>
)}
<Col span={12}><FormItem
name="description"
label="申请要求"
@ -848,61 +958,6 @@ const JudgingPanel: React.FC<{}> = () => {
>
<Input style={{ width: "90%" }} disabled={disabled} />
</FormItem></Col>
{isReserve == "1" && (//预约评标室
<Col span={12}><FormItem
name="assistNumber"
label="外协人员数量"
rules={[{ type: 'string', message: '请输入正确内容' }, { pattern: /^([1-9]?\d|100)$/, message: '请输入正确数值' }]}
>
<Input style={{ width: "90%" }} type="number" disabled={disabled} />
</FormItem></Col>
)}
<Col span={12}><FormItem
name="startTime"
label={`预计${showName.pb}开始时间`}
rules={rule('开始时间')}
>
<DatePicker
format="YYYY-MM-DD HH:mm:ss"
disabledDate={disabledDate}
showTime={{ defaultValue: moment('00:00:00', 'HH:mm:ss') }}
disabled={disabled || (isReserve == "1")}
showNow={false}
style={{ width: "90%" }}
/>
</FormItem></Col>
<Col span={12}><FormItem
name="endTime"
label={`预计${showName.pb}结束时间`}
rules={rule('结束时间')}
>
<DatePicker
format="YYYY-MM-DD HH:mm:ss"
disabledDate={disabledDate}
showTime={{ defaultValue: moment('00:00:00', 'HH:mm:ss') }}
disabled={disabled || (isReserve == "1")}
showNow={false}
style={{ width: "90%" }}
/>
</FormItem></Col>
{isReserve == "1" && (//预约评标室
<>
<Col span={12}><FormItem
name="reserveBy"
label="预约人"
rules={[{ required: true, message: `请录入预约人` }, { max: 100, message: '内容超长' }]}
>
<Input style={{ width: "90%" }} disabled={true} placeholder="预约人" />
</FormItem></Col>
<Col span={12}><FormItem
name="reserveContactNumber"
label="预约人联系方式"
rules={[{ required: true, message: `请录入预约人联系方式` }, { max: 100, message: '内容超长' }]}
>
<Input type="number" style={{ width: "90%" }} disabled={true} placeholder="预约人联系方式" />
</FormItem></Col>
</>
)}
</Row>
</Form>
</>
@ -1553,6 +1608,7 @@ const JudgingPanel: React.FC<{}> = () => {
<Form.Item
name="faceId"
style={{ display: 'inline-block', width: '60%' }}
extra={<span style={{ color: '#b30000' }}>使</span>}
>
<ExpertPhotoUpload maxSize={200} />
</Form.Item>
@ -1829,9 +1885,9 @@ const JudgingPanel: React.FC<{}> = () => {
}}
>
<Spin spinning={spin}>
<div className='headerDiv pl24 pr16'>
<h3 className="first-title floatLeft"></h3>
<div className='rightDiv'>
<div className='headerDiv pl24 pr16' style={{ display: 'flex', justifyContent: 'space-between' }}>
<h3 className="first-title"></h3>
<div>
{/* <Button key="1" className='mR8' onClick={() => { }}>通知专家</Button> */}
<Button key="2" type='primary' className='mR8' hidden={!open || allEnd || btnAuthority(['ebtp-agency-project-manager', 'ebtp-purchase'])} onClick={() => {
checkBoxsSet([]);
@ -1844,6 +1900,9 @@ const JudgingPanel: React.FC<{}> = () => {
</Upload>
</div>
</div>
<div style={{ marginTop: '11px', marginLeft: '35px' }}>
<span style={{ color: '#b30000' }}>使</span>
</div>
<Collapse className='xsy-collapse' style={{ marginTop: '16px' }} defaultActiveKey={['1', '2', '3', '4', '5',]}>
{returnPanel()}
@ -1922,7 +1981,7 @@ const JudgingPanel: React.FC<{}> = () => {
getExpertPhoto();
}
}
//是否预约评标室
//是否预约电子评标室
const onRadioChange = (e: RadioChangeEvent) => {
setIsReserve(e.target.value);
setSelectEvalData(null);
@ -1933,8 +1992,17 @@ const JudgingPanel: React.FC<{}> = () => {
const getEarliestTime = (packageData: any[], selectData: any[], reserve: string) => {
let selectPackageData = packageData.filter(item => selectData.includes(item.sectionId));
let sortable = sortBy(selectPackageData, item => item.openTime);//按照开标时间排序
let startTime = sortable?.[0]?.openTime ? moment(sortable[0].openTime).startOf("hour").add(1, 'h') : null;
let endTime = sortable?.[0]?.openTime ? moment(sortable[0].openTime).startOf("hour").add(3, 'h') : null;
let startTime = null;//开始时间小于17点正常17点+则为null
let endTime = null;
if (sortable?.[0]?.openTime) {
if (moment(sortable[0].openTime).hour() <= 15) {//结束时间小于16点正常+2h16-17点只能给18点17点+则为null
startTime = moment(sortable[0].openTime).startOf("hour").add(1, 'h');
endTime = moment(sortable[0].openTime).startOf("hour").add(3, 'h');
} else if (moment(sortable[0].openTime).hour() == 16) {
startTime = moment().startOf("hour").hour(17);
endTime = moment().startOf("hour").hour(18);
}
}
if (reserve == "1") {//预约了评标室
setInitEvalTime({ startTime, endTime, reserveBy: userData?.fullName, reserveContactNumber: userData?.mobilePhone, });
} else {

View File

@ -1,40 +1,52 @@
@import '~antd/lib/style/themes/default.less';
.headerDiv{
.headerDiv {
// padding: 16px 24px 0px 0px;
margin-left: 0px;
width:100%;
width: 100%;
// border-bottom: solid;
// border-bottom-color: rgb(187,16,39);
// border-width: 1px;
height:32px;
height: 32px;
}
.leftDiv{
font-size: middle;
.leftDiv {
font-size: middle;
font-weight: normal;
background-color: rgb(187,16,39);
background-color: rgb(187, 16, 39);
padding: 3px 8px;
width: 130px;
float: left;
height:30px;
height: 30px;
text-align: center;
}
.rightDiv{
.rightDiv {
float: right;
height:30px;
height: 30px;
text-align: center;
}
.mt15{
.mt15 {
margin-top: 15px;
}
.pl24{
.pl24 {
padding-left: 24px;
}
.pr16{
.pr16 {
padding-right: 16px;
}
.xsy-collapse{
.ant-collapse-content-box{
padding:0px 0px 16px 0px
.xsy-collapse {
.ant-collapse-content-box {
padding: 0px 0px 16px 0px
}
}
.eval-location-title {
&>p {
margin-bottom: 0;
}
}