Merge branch '20230705-专家评审组照片维护功能,当选择电子评标室的标段,维护照片时为必选项。' into 'release_20230714'

7.14 专家评审组照片维护功能,当选择电子评标室的标段,维护照片时为必选项。

See merge request eshop/fe_service_ebtp_frontend!303
This commit is contained in:
jl-zhoujl2
2023-07-14 03:14:30 +00:00
6 changed files with 136 additions and 73 deletions

View File

@ -148,7 +148,7 @@ const ExpertPhotoUpload: React.FC<ExpertPhotoUpload> = (props) => {
> >
{uploadProps?.disabled || fileList.length >= 1 ? null : uploadButton} {uploadProps?.disabled || fileList.length >= 1 ? null : uploadButton}
</Upload> </Upload>
<Modal visible={previewVisible} title="查看" footer={null} onCancel={handleCancel} centered> <Modal visible={previewVisible} title="查看" width={'461px'} footer={null} onCancel={handleCancel} centered>
<img alt="example" style={{ width: '100%' }} src={previewImage} /> <img alt="example" style={{ width: '100%' }} src={previewImage} />
</Modal> </Modal>
</> </>

View File

@ -7,6 +7,7 @@ import React, { useEffect, useState } from "react";
import './judgList.less'; import './judgList.less';
import '@/assets/xsy_style.less'; import '@/assets/xsy_style.less';
import { saveAssistPeople } from "./service"; import { saveAssistPeople } from "./service";
import { isEmpty } from "@/utils/CommonUtils";
interface OutsourcingManageProps { interface OutsourcingManageProps {
modalVisible: boolean, modalVisible: boolean,
@ -124,6 +125,17 @@ const OutsourcingManage: React.FC<OutsourcingManageProps> = (props) => {
// }} // }}
onOk={() => { onOk={() => {
if (Number(assistNumber) == dataSource.length) { if (Number(assistNumber) == dataSource.length) {
let count = 0
for (let i = 0, length = dataSource.length; i < length; i++) {
const item = dataSource[i];
if (isEmpty(item.faceId)) {
count += 1;
}
}
if (count > 0) {
message.error(`请上传专家照片`);
return;
}
const params = { const params = {
juryId, juryId,
assistList: [...dataSource], assistList: [...dataSource],
@ -252,18 +264,19 @@ const OutsourcingManage: React.FC<OutsourcingManageProps> = (props) => {
> >
<Input style={{ width: "60%" }} /> <Input style={{ width: "60%" }} />
</Form.Item></Col></Row> </Form.Item></Col></Row>
<Form.Item label="照片" style={{ marginBottom: 0 }}> <Form.Item label="照片" required style={{ marginBottom: 0 }} tooltip="若预约了电子评标室,请提前维护外协人员照片,避免无法进入电子评标室。">
<Form.Item <Form.Item
name="faceId" name="faceId"
style={{ display: 'inline-block', width: '60%' }} style={{ display: 'inline-block', width: '80%' }}
extra={<span style={{ color: '#b30000' }}></span>} rules={[{ required: true, message: "请上传照片" }]}
extra={<span style={{ color: '#b30000' }}>2psJPG格式400k以下</span>}
> >
<ExpertPhotoUpload maxSize={200} /> <ExpertPhotoUpload maxSize={400} uploadProps={{ accept: ".jpg,.jpeg" }} />
</Form.Item> </Form.Item>
<Form.Item {/* <Form.Item
style={{ display: 'inline-block', width: '40%', position: "relative", right: '24%' }} style={{ display: 'inline-block', width: '40%', position: "relative", right: '24%' }}
> >
</Form.Item> </Form.Item> */}
</Form.Item> </Form.Item>
<Row><Col span={24}><Form.Item {...tailLayoutDrawer}> <Row><Col span={24}><Form.Item {...tailLayoutDrawer}>
<Button type="primary" loading={loading} hidden={btnAuthority(['ebtp-agency-project-manager', 'ebtp-purchase'])} onClick={async () => { <Button type="primary" loading={loading} hidden={btnAuthority(['ebtp-agency-project-manager', 'ebtp-purchase'])} onClick={async () => {

View File

@ -4,7 +4,7 @@ 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 { getList, getSecs, saveGroup, delOne, saveMember, changeEx, queryVoList, changeMember, applyFor, roomStatus, juryTem, rePassWord, getUserPhoto, unlockAccount } from './service';
import moment from 'moment'; import moment from 'moment';
import { getDefId, getProId, getProMethod, getSessionProjectData, getSessionUserData } from '@/utils/session'; import { getDefId, getProId, getProMethod, getSessionProjectData, getSessionUserData } from '@/utils/session';
import { getURLInformation } from '@/utils/CommonUtils'; import { getURLInformation, isEmpty } from '@/utils/CommonUtils';
import './judgList.less'; import './judgList.less';
import '@/assets/xsy_style.less'; import '@/assets/xsy_style.less';
import FileDown from '@/utils/Download'; import FileDown from '@/utils/Download';
@ -59,6 +59,7 @@ const JudgingPanel: React.FC<{}> = () => {
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 userData = getSessionUserData();//当前登录人用户信息
const [assistVisible, setAssistVisible] = useState<boolean>(false);//协办管理visible 2022.10.10 zhoujianlong const [assistVisible, setAssistVisible] = useState<boolean>(false);//协办管理visible 2022.10.10 zhoujianlong
const [reserveStatus, setReserveStatus] = useState<boolean>(false);//成员管理-当前行是否预约了评标室 true-预约了 false-没预约
function getShouName() { function getShouName() {
const method = getProMethod(); const method = getProMethod();
@ -167,6 +168,7 @@ const JudgingPanel: React.FC<{}> = () => {
await queryOpenStatus(record.id); await queryOpenStatus(record.id);
categorySet(record.juryCategoryVOList); categorySet(record.juryCategoryVOList);
juryIdSet(record.id); juryIdSet(record.id);
setReserveStatus(record.reserveStatus === 1);
memberCountSet(memberCount + 1); memberCountSet(memberCount + 1);
setMemberVis(true); setMemberVis(true);
}}></Button> }}></Button>
@ -1204,6 +1206,7 @@ const JudgingPanel: React.FC<{}> = () => {
checkBoxsSet([]); checkBoxsSet([]);
changeMemberIdSet(''); changeMemberIdSet('');
setCheckedList([]); setCheckedList([]);
setReserveStatus(false);
form.resetFields(); form.resetFields();
} }
const columnsMember: ProColumns<any>[] = [//成员管理页面表格 const columnsMember: ProColumns<any>[] = [//成员管理页面表格
@ -1616,19 +1619,20 @@ const JudgingPanel: React.FC<{}> = () => {
> >
<Input style={{ width: "60%" }} onChange={onCertificateChange} /> <Input style={{ width: "60%" }} onChange={onCertificateChange} />
</FormItem></Col></Row> </FormItem></Col></Row>
<Form.Item label="照片" style={{ marginBottom: 0 }}> <Form.Item label="照片" required={reserveStatus} style={{ marginBottom: 0 }} tooltip="评审专家照片为专家进入电子评标室报道及人脸识别认证使用,若预约了电子评标室,请维护专家照片。">
<Form.Item <Form.Item
name="faceId" name="faceId"
style={{ display: 'inline-block', width: '60%' }} style={{ display: 'inline-block', width: '80%' }}
extra={<span style={{ color: '#b30000' }}>使</span>} rules={[{ required: reserveStatus, message: "请上传照片" }]}
extra={<span style={{ color: '#b30000' }}>2psJPG格式400k以下</span>}
> >
<ExpertPhotoUpload maxSize={200} /> <ExpertPhotoUpload maxSize={400} uploadProps={{ accept: ".jpg,.jpeg" }} />
</Form.Item>
<Form.Item
style={{ display: 'inline-block', width: '40%', position: "relative", right: '24%' }}
>
{/* <Button type='primary' onClick={() => getExpertPhoto()}>获取照片</Button> */}
</Form.Item> </Form.Item>
{/* <Form.Item
style={{ display: 'inline-block', width: '20%', position: "relative", right: '24%' }}
> */}
{/* <Button type='primary' onClick={() => getExpertPhoto()}>获取照片</Button> */}
{/* </Form.Item> */}
</Form.Item> </Form.Item>
{/* <Row><Col span={24}><FormItem {/* <Row><Col span={24}><FormItem
name="type" name="type"
@ -1820,6 +1824,18 @@ const JudgingPanel: React.FC<{}> = () => {
// qita != undefined && qita.extractNumber==qita.juryMemberList.length ? params.push(...qita.juryMemberList) : null; // qita != undefined && qita.extractNumber==qita.juryMemberList.length ? params.push(...qita.juryMemberList) : null;
if (chackNum) { if (chackNum) {
let count = 0
for (let i = 0, length = params.length; i < length; i++) {
const item = params[i];
if (isEmpty(item.faceId)) {
count += 1;
}
}
if (count > 0) {
message.error(`请上传专家照片`);
loadingSet(false);
return;
}
const success = await saveMember({ juryMemberDTOList: params }).then((res) => { const success = await saveMember({ juryMemberDTOList: params }).then((res) => {
return res.success return res.success
}); });
@ -1889,7 +1905,7 @@ const JudgingPanel: React.FC<{}> = () => {
centered centered
destroyOnClose destroyOnClose
title="评审小组成员管理" title="评审小组成员管理"
bodyStyle={{ maxHeight: modalHeight - 140, overflow: 'auto', minHeight: '300px', padding: '16px 0px 0px 0px' }} bodyStyle={{ maxHeight: modalHeight - 140, overflow: 'auto', minHeight: '350px', padding: '16px 0px 0px 0px' }}
footer={returnFooterMem()} footer={returnFooterMem()}
onCancel={() => { onCancel={() => {
setMemberVis(false); setMemberVis(false);

View File

@ -17,6 +17,7 @@ import { getUserPhoto, unlockAccount } from '@/pages/Tender/ProjectManager/Judgi
import BidEvalAppointment from '@/components/ElecBidEvaluation/BidEvalAppointment'; import BidEvalAppointment from '@/components/ElecBidEvaluation/BidEvalAppointment';
import OutsourcingManage from '@/pages/Tender/ProjectManager/JudgingPanel/List/OutsourcingManage'; import OutsourcingManage from '@/pages/Tender/ProjectManager/JudgingPanel/List/OutsourcingManage';
import { dateTimeFormatter } from '@/utils/DateUtils'; import { dateTimeFormatter } from '@/utils/DateUtils';
import { isEmpty } from '@/utils/CommonUtils';
const JudgingPanel: React.FC<{}> = (props: any) => { const JudgingPanel: React.FC<{}> = (props: any) => {
const modalHeight = window.innerHeight * 96 / 100; const modalHeight = window.innerHeight * 96 / 100;
@ -56,6 +57,7 @@ const JudgingPanel: React.FC<{}> = (props: any) => {
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 userData = getSessionUserData();//当前登录人用户信息
const [assistVisible, setAssistVisible] = useState<boolean>(false);//协办管理visible 2022.10.10 zhoujianlong const [assistVisible, setAssistVisible] = useState<boolean>(false);//协办管理visible 2022.10.10 zhoujianlong
const [reserveStatus, setReserveStatus] = useState<boolean>(false);//成员管理-当前行是否预约了评标室 true-预约了 false-没预约
//外协管理 //外协管理
const [assistList, setAssistList] = useState<any>([]);//外协人员数据 assistList const [assistList, setAssistList] = useState<any>([]);//外协人员数据 assistList
const [assistNumber, setAssistNumber] = useState<string>("");//外协人员数量 assistNumber const [assistNumber, setAssistNumber] = useState<string>("");//外协人员数量 assistNumber
@ -155,6 +157,7 @@ const JudgingPanel: React.FC<{}> = (props: any) => {
await queryOpenStatus(record.id); await queryOpenStatus(record.id);
categorySet(record.juryCategoryVOList); categorySet(record.juryCategoryVOList);
juryIdSet(record.id); juryIdSet(record.id);
setReserveStatus(record.reserveStatus === 1);
memberCountSet(memberCount + 1); memberCountSet(memberCount + 1);
setMemberVis(true); setMemberVis(true);
}}></Button> }}></Button>
@ -1072,6 +1075,7 @@ const JudgingPanel: React.FC<{}> = (props: any) => {
updateKeyMemSet(-1); updateKeyMemSet(-1);
checkBoxsSet([]); checkBoxsSet([]);
changeMemberIdSet(''); changeMemberIdSet('');
setReserveStatus(false);
form.resetFields(); form.resetFields();
} }
const columnsMember: ProColumns<any>[] = [//成员管理页面表格 const columnsMember: ProColumns<any>[] = [//成员管理页面表格
@ -1484,19 +1488,20 @@ const JudgingPanel: React.FC<{}> = (props: any) => {
> >
<Input style={{ width: "60%" }} onChange={onCertificateChange} /> <Input style={{ width: "60%" }} onChange={onCertificateChange} />
</FormItem></Col></Row> </FormItem></Col></Row>
<Form.Item label="照片" style={{ marginBottom: 0 }}> <Form.Item label="照片" required={reserveStatus} style={{ marginBottom: 0 }} tooltip="评审专家照片为专家进入电子评标室报道及人脸识别认证使用,若预约了电子评标室,请维护专家照片。">
<Form.Item <Form.Item
name="faceId" name="faceId"
style={{ display: 'inline-block', width: '60%' }} style={{ display: 'inline-block', width: '80%' }}
extra={<span style={{ color: '#b30000' }}>使</span>} rules={[{ required: reserveStatus, message: "请上传照片" }]}
extra={<span style={{ color: '#b30000' }}>2psJPG格式400k以下</span>}
> >
<ExpertPhotoUpload maxSize={200} /> <ExpertPhotoUpload maxSize={400} uploadProps={{ accept: ".jpg,.jpeg" }} />
</Form.Item>
<Form.Item
style={{ display: 'inline-block', width: '40%', position: "relative", right: '24%' }}
>
{/* <Button type='primary' onClick={() => getExpertPhoto()}>获取照片</Button> */}
</Form.Item> </Form.Item>
{/* <Form.Item
style={{ display: 'inline-block', width: '20%', position: "relative", right: '24%' }}
> */}
{/* <Button type='primary' onClick={() => getExpertPhoto()}>获取照片</Button> */}
{/* </Form.Item> */}
</Form.Item> </Form.Item>
{/* <Row><Col span={24}><FormItem {/* <Row><Col span={24}><FormItem
name="type" name="type"
@ -1688,6 +1693,18 @@ const JudgingPanel: React.FC<{}> = (props: any) => {
// qita != undefined && qita.extractNumber==qita.juryMemberList.length ? params.push(...qita.juryMemberList) : null; // qita != undefined && qita.extractNumber==qita.juryMemberList.length ? params.push(...qita.juryMemberList) : null;
if (chackNum) { if (chackNum) {
let count = 0
for (let i = 0, length = params.length; i < length; i++) {
const item = params[i];
if (isEmpty(item.faceId)) {
count += 1;
}
}
if (count > 0) {
message.error(`请上传专家照片`);
loadingSet(false);
return;
}
const success = await saveMember({ juryMemberDTOList: params, roomId: roomId }).then((res) => { const success = await saveMember({ juryMemberDTOList: params, roomId: roomId }).then((res) => {
return res.success return res.success
}); });
@ -1757,7 +1774,7 @@ const JudgingPanel: React.FC<{}> = (props: any) => {
centered centered
destroyOnClose destroyOnClose
title="评审小组成员管理" title="评审小组成员管理"
bodyStyle={{ maxHeight: modalHeight - 140, overflow: 'auto', minHeight: '300px', padding: '16px 0px 0px 0px' }} bodyStyle={{ maxHeight: modalHeight - 140, overflow: 'auto', minHeight: '350px', padding: '16px 0px 0px 0px' }}
footer={returnFooterMem()} footer={returnFooterMem()}
onCancel={() => { onCancel={() => {
setMemberVis(false); setMemberVis(false);

View File

@ -2,7 +2,7 @@ import React, { useEffect, useState, useRef } from 'react';
import { Form, Button, Input, Row, Col, Modal, Spin, message, Tabs } from 'antd'; import { Form, Button, Input, Row, Col, Modal, Spin, message, Tabs } from 'antd';
import { UserOutlined, LockOutlined, SafetyCertificateOutlined } from '@ant-design/icons'; import { UserOutlined, LockOutlined, SafetyCertificateOutlined } from '@ant-design/icons';
import './style.less'; import './style.less';
import { changePass } from './service'; import { changePass, showFaceTab } from './service';
import logo from '@/images/login/logoPic.png'; import logo from '@/images/login/logoPic.png';
import { refreshTokenApi, ZjfakeAccountLogin, ZjfakeFaceLogin } from '@/services/login'; import { refreshTokenApi, ZjfakeAccountLogin, ZjfakeFaceLogin } from '@/services/login';
import { history } from 'umi'; import { history } from 'umi';
@ -40,6 +40,7 @@ const Index: React.FC<{}> = () => {
const { TabPane } = Tabs; const { TabPane } = Tabs;
const [submitLoading, setSubmitLoading] = useState<boolean>(false); const [submitLoading, setSubmitLoading] = useState<boolean>(false);
const [faceLoginDisable, setFaceLoginDisable] = useState<boolean>(false); const [faceLoginDisable, setFaceLoginDisable] = useState<boolean>(false);
const [faceLoginShow, setFaceLoginShow] = useState<boolean>(false);
const lv = useRef<LivingNotIE>(); const lv = useRef<LivingNotIE>();
const [timerShow, setTimeShow] = useState<boolean>(false); const [timerShow, setTimeShow] = useState<boolean>(false);
const [itemShow, setItemShow] = useState<boolean>(false); const [itemShow, setItemShow] = useState<boolean>(false);
@ -496,6 +497,11 @@ const Index: React.FC<{}> = () => {
} }
return false; return false;
} }
//是否显示人脸登录
const showFaceLogin = async () => {
const res = await showFaceTab();
setFaceLoginShow(res?.data === 1);
}
useEffect(() => { useEffect(() => {
@ -505,6 +511,7 @@ const Index: React.FC<{}> = () => {
if (!allowedToFaceLogin()) { if (!allowedToFaceLogin()) {
setFaceLoginDisable(true); setFaceLoginDisable(true);
} }
showFaceLogin();
}, []); }, []);
useEffect(() => { useEffect(() => {
@ -592,49 +599,51 @@ const Index: React.FC<{}> = () => {
</Form.Item> </Form.Item>
</Form> </Form>
</TabPane> </TabPane>
{/* <TabPane tab="人脸识别登录" key="2" disabled={faceLoginDisable}> {faceLoginShow && <>
<Form <TabPane tab="人脸识别登录" key="2" disabled={faceLoginDisable}>
name="basic2" <Form
className="form-box" name="basic2"
initialValues={{ remember: true }} className="form-box"
form={form2} initialValues={{ remember: true }}
// onFinish={hanleFaceSubmit.bind(this, null)} form={form2}
onFinish={!whetherIE.current?liveDetectStart:hanleFaceSubmit.bind(this, null)} // onFinish={hanleFaceSubmit.bind(this, null)}
// onFinishFailed={onFinishFailed} onFinish={!whetherIE.current ? liveDetectStart : hanleFaceSubmit.bind(this, null)}
> // onFinishFailed={onFinishFailed}
<Form.Item
label=""
name="userName"
rules={[{ required: true, message: '请输入用户名!' }]}
> >
<Input <Form.Item
prefix={ label=""
<UserOutlined style={{ fontSize: '18px' }} className="site-form-item-icon" /> name="userName"
} rules={[{ required: true, message: '请输入用户名!' }]}
placeholder="请输入用户名" >
/> <Input
</Form.Item> prefix={
<UserOutlined style={{ fontSize: '18px' }} className="site-form-item-icon" />
}
placeholder="请输入用户名"
/>
</Form.Item>
<Form.Item> <Form.Item>
{!whetherIE.current ? (<video ref={video} width="382" height="200"></video>) : (<FrameFaceLogin faceCompareEvent2={IELiveDetectFinish2} faceCompareEvent={IELiveDetectFinish} faceDetectStatusEvent = {UpdateDetectStatus}/>)} {!whetherIE.current ? (<video ref={video} width="382" height="200"></video>) : (<FrameFaceLogin faceCompareEvent2={IELiveDetectFinish2} faceCompareEvent={IELiveDetectFinish} faceDetectStatusEvent={UpdateDetectStatus} />)}
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Button type="primary" className="w100" loading={submitLoading} htmlType="submit"> <Button type="primary" className="w100" loading={submitLoading} htmlType="submit">
{submitLoading ? actionText(action, Math.round(timer/1000)) : '登 录'} {submitLoading ? actionText(action, Math.round(timer / 1000)) : '登 录'}
</Button> </Button>
</Form.Item> </Form.Item>
</Form> </Form>
</TabPane> */} </TabPane>
{/* 加载摄像头 */} {/* 加载摄像头 */}
{/* <Form.Item hidden={!itemShow}> <Form.Item hidden={!itemShow}>
<div> <div>
<span style={{color:classColor(action)}}>{actionText(action)}</span> <span style={{ color: classColor(action) }}>{actionText(action)}</span>
<span hidden={!timerShow}>{Math.round(timer/1000)}</span> <span hidden={!timerShow}>{Math.round(timer / 1000)}</span>
</div> </div>
</Form.Item> */} </Form.Item>
{/* <video ref={video} width="382" height="200"></video> */} <video ref={video} width="382" height="200"></video>
{/* onClick={() => {hanleFaceSubmit(null, null);}} */} {/* onClick={() => {hanleFaceSubmit(null, null);}} */}
</>}
</Tabs> </Tabs>
</div> </div>
</div> </div>

View File

@ -22,6 +22,14 @@ export async function rgbToBase64(params: any) {
}); });
return request('/api/core-service-ebtp-userinfo/outer/v1/ebtp/face/rgbArray2Base64', { return request('/api/core-service-ebtp-userinfo/outer/v1/ebtp/face/rgbArray2Base64', {
method: 'post', method: 'post',
body:_body, body: _body,
}); });
} }
/**
* 是否显示人脸识别tab
* @param params
*/
export async function showFaceTab() {
return request('/api/biz-service-ebtp-extend//v1/BizFuncSwitchConfig/bizfuncswitchconfig/getFaceRecognitionFlag');
}