794 lines
27 KiB
TypeScript
794 lines
27 KiB
TypeScript
import React, { useEffect, useState, useRef } from 'react';
|
||
import { Form, Button, Input, Row, Col, Modal, Spin, message, Tabs } from 'antd';
|
||
import { UserOutlined, LockOutlined, SafetyCertificateOutlined } from '@ant-design/icons';
|
||
import './style.less';
|
||
import { changePass, showFaceTab } from './service';
|
||
import logo from '@/images/login/logoPic.png';
|
||
import { refreshTokenApi, ZjfakeAccountLogin, ZjfakeFaceLogin } from '@/services/login';
|
||
import { history } from 'umi';
|
||
import cookie from 'react-cookies';
|
||
import moment from 'moment';
|
||
import FrameFaceLogin from '../faceLogin/FrameFaceLogin';
|
||
import LivingNotIE from './living.min.js';
|
||
import { _KJUR } from './jsrsasign-latest-all-min';
|
||
import { time } from 'echarts';
|
||
import request from '@/utils/request';
|
||
|
||
const layout = {
|
||
labelCol: { span: 7 },
|
||
wrapperCol: { span: 13 },
|
||
};
|
||
|
||
export interface RgbParams {
|
||
type: string;
|
||
image: string;
|
||
}
|
||
|
||
const Index: React.FC<{}> = () => {
|
||
const [form] = Form.useForm();
|
||
const [form2] = Form.useForm();
|
||
const [imgUrl, setImgUrl] = useState<any>('');
|
||
const [tmpToken, setTmpToken] = useState<any>('');
|
||
const [imgUrlEdit, setImgUrlEdit] = useState<any>('');
|
||
const remainingTime = 3 //刷新token的剩余时间,单位小时
|
||
const [changeForm] = Form.useForm();
|
||
const [isModalVisible, setIsModalVisible] = useState<boolean>(false)
|
||
const [spinning, setSping] = useState<boolean>(false);//加载遮罩
|
||
const video = useRef();
|
||
const whetherIE = useRef<boolean>(false);
|
||
const mediaStreamTrack = useRef<any>();
|
||
const { TabPane } = Tabs;
|
||
const [submitLoading, setSubmitLoading] = useState<boolean>(false);
|
||
const [faceLoginDisable, setFaceLoginDisable] = useState<boolean>(false);
|
||
const [faceLoginShow, setFaceLoginShow] = useState<boolean>(false);
|
||
const lv = useRef<LivingNotIE>();
|
||
const [timerShow, setTimeShow] = useState<boolean>(false);
|
||
const [itemShow, setItemShow] = useState<boolean>(false);
|
||
const [action, setAction] = useState<number>(1);
|
||
const [timer, setTimer] = useState<number>(10000);
|
||
const urlRef = useRef<any>("");
|
||
/**
|
||
* 设置活体检测token
|
||
*/
|
||
const setLiveDetectToken = () => {
|
||
let appKey = 'nkYy3g1yT0alE8pF6a1UTC4I'
|
||
let appSecrect = 'L30zHpTyAtTlY7tTCpbzdxxKCQgwWIQL'
|
||
// Header
|
||
var header = {
|
||
typ: "JWT", // 声明类型
|
||
alg: "HS256" // 声明加密的算法 通常直接使用 HMAC SHA256
|
||
};
|
||
|
||
var payload = {
|
||
iss: appKey,
|
||
iat: moment().unix(),
|
||
exp: moment()
|
||
.add(29, "minutes")
|
||
.unix()
|
||
};
|
||
var sHeader = JSON.stringify(header);
|
||
var sPayload = JSON.stringify(payload);
|
||
var sJWT = _KJUR.jws.JWS.sign("HS256", sHeader, sPayload, {
|
||
utf8: appSecrect,
|
||
});
|
||
console.log(sJWT);
|
||
return sJWT;
|
||
};
|
||
/**
|
||
* 开始人脸识别(活体和比对)
|
||
*/
|
||
const liveDetectStart = () => {
|
||
liveDetectStop();
|
||
let _lv = new LivingNotIE(null, {
|
||
timer: 10000,
|
||
action: [1, 3, 2],
|
||
token: setLiveDetectToken(),
|
||
proxy: '/living/api',
|
||
getFacePicture: () => {
|
||
return getLiveDetectFile()
|
||
},
|
||
//开始活体检测,此时token已鉴权成功
|
||
onDetectStart: () => {
|
||
setSubmitLoading(true);
|
||
},
|
||
//每轮检测开始
|
||
onDetectActionStart: (action: number) => {
|
||
setAction(action);
|
||
// this.drawCanvas()
|
||
},
|
||
//每轮检测计时
|
||
onDetectActionProgress: (action: number, timer: number) => {
|
||
setTimer(timer);
|
||
},
|
||
//每轮检测结束
|
||
onDetectActionFinish: (action: number, data: any) => {
|
||
// this.clearCanvas()
|
||
setTimer(10000);
|
||
console.log(data)
|
||
},
|
||
//活体检测完成
|
||
onDetectFinish: (data: any) => {
|
||
if (data.code == 1) {
|
||
setAction(4);
|
||
setTimeShow(false);
|
||
//人脸比对
|
||
hanleFaceSubmit(data.file.file, null);
|
||
} else {
|
||
message.error('活体检测失败');
|
||
setSubmitLoading(false);
|
||
}
|
||
// data.code === 1? message.success('活体检测通过'):message.error('活体检测失败')
|
||
}
|
||
});
|
||
//开始检测
|
||
_lv.start();
|
||
lv.current = _lv;
|
||
}
|
||
/**
|
||
* 停止活体检测
|
||
*/
|
||
const liveDetectStop = () => {
|
||
lv.current && lv.current.stop();
|
||
}
|
||
/**
|
||
* 给活体检测提供照片
|
||
* @returns 活体照片
|
||
*/
|
||
const getLiveDetectFile = () => {
|
||
const canvas = document.createElement('canvas');
|
||
canvas.setAttribute('width', '300');
|
||
canvas.setAttribute('height', '200');
|
||
const context = canvas.getContext('2d');
|
||
context.drawImage(video.current, 0, 0, 300, 200);
|
||
let _imgURI = canvas.toDataURL("image/jpeg",);
|
||
console.log(_imgURI);
|
||
let _file = base64ToBlob(_imgURI);
|
||
return {
|
||
file: _file//传给活体检测服务的文件
|
||
}
|
||
}
|
||
const classColor = (action: any) => {
|
||
let color = '';
|
||
switch (action) {
|
||
case 1:
|
||
color = '#409eff';
|
||
break;
|
||
case 2:
|
||
color = '#5daf34';
|
||
break;
|
||
case 3:
|
||
color = 'gold';
|
||
break;
|
||
case 4:
|
||
color = 'grey';
|
||
break;
|
||
}
|
||
return color;
|
||
}
|
||
const actionText = (action: number, timer: number) => {
|
||
let text = '';
|
||
switch (action) {
|
||
case 1:
|
||
text = '对准摄像头眨眨眼(' + timer + 's)';
|
||
break;
|
||
case 2:
|
||
text = '对准摄像头张张嘴(' + timer + 's)';
|
||
break;
|
||
case 3:
|
||
text = '对准摄像头摇摇头(' + timer + 's)';
|
||
break;
|
||
case 4:
|
||
text = '请稍候,正在验证中...';
|
||
}
|
||
return text;
|
||
}
|
||
|
||
const genRandomString = (len: number) => {
|
||
const text = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||
const rdmIndex = (text: string | any[]) => (Math.random() * text.length) | 0;
|
||
let rdmString = '';
|
||
for (; rdmString.length < len; rdmString += text.charAt(rdmIndex(text)));
|
||
return rdmString;
|
||
};
|
||
const changeCaptcha = () => {
|
||
let tmpToken = genRandomString(16);
|
||
let url = '/api/auth/captcha?token=' + tmpToken;
|
||
setTmpToken(tmpToken);
|
||
setImgUrl(url);
|
||
};
|
||
const changeCaptchaEdit = async () => {//修改密码-获取验证码
|
||
let tmpToken = genRandomString(16);
|
||
|
||
// 获取window的URL对像 并做好浏览器兼容性处理
|
||
const windowUrl = window.URL || window.webkitURL
|
||
windowUrl.revokeObjectURL(urlRef.current);
|
||
// 开始ajax请求
|
||
const xhr = new XMLHttpRequest()
|
||
// 验证码请求地址
|
||
const url = '/api/api/mall-expe/v1/expebaseinfo/getCaptcha?token=' + tmpToken
|
||
xhr.open('POST', url, true)
|
||
// 设置响应数据的类型 blod是将响应数据转换成二进制数据的Blob对象
|
||
xhr.responseType = 'blob'
|
||
xhr.onload = function () {
|
||
if (this.status === 200) {
|
||
const blob = this.response
|
||
// 将响应数据转换成url对象 赋值给src变量 传递给img
|
||
const blob_url = windowUrl.createObjectURL(blob);
|
||
urlRef.current = blob_url;
|
||
changeForm.setFieldsValue({ token: tmpToken });
|
||
setImgUrlEdit(blob_url);
|
||
}
|
||
};
|
||
|
||
xhr.send();
|
||
}
|
||
|
||
//登录刷新Token方法
|
||
const refreshToken = async (data: any) => {
|
||
const params = {
|
||
grant_type: "refresh_token",
|
||
refresh_token: data?.refreshToken?.value,
|
||
old_token: data?.value,
|
||
}
|
||
await refreshTokenApi(params).then(res => {
|
||
if (res?.success == true) {
|
||
sessionStorage.setItem('Authorization', res?.data?.value);
|
||
// sessionStorage.setItem('refreshToken', res?.data?.refreshToken.value);
|
||
sessionStorage.setItem('scope', res?.data?.scope);
|
||
history.push('/redirect');
|
||
}
|
||
})
|
||
}
|
||
|
||
const handleSubmit = async (values: any) => {
|
||
await ZjfakeAccountLogin({ ...values, tmpToken }).then((res) => {
|
||
if (res?.success) {
|
||
if (moment(res?.data?.expiration).diff(moment(), 'hours') < remainingTime) {
|
||
refreshToken(res?.data)
|
||
} else {
|
||
sessionStorage.setItem('Authorization', res?.data?.value);
|
||
// sessionStorage.setItem('refreshToken', res?.data?.refreshToken.value);
|
||
sessionStorage.setItem('scope', res?.data?.scope);
|
||
history.push('/redirect');
|
||
}
|
||
}
|
||
changeCaptcha();
|
||
});
|
||
};
|
||
|
||
const onChange = (key: string) => {
|
||
if (key == '2') {
|
||
BrowserType();
|
||
InitMedia();
|
||
} else {
|
||
if (allowedToFaceLogin()) {
|
||
if (whetherIE.current) {
|
||
releaseCamera('faceLoginFrame');
|
||
} else {
|
||
mediaStreamTrack.current?.stop();
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
const releaseCamera = (id: string) => {
|
||
const childFrameObj = document.getElementById(id);
|
||
childFrameObj?.contentWindow?.postMessage('releaseCamera', '*');
|
||
}
|
||
|
||
const hanleFaceSubmit = async (multipartFiles: any, values: any) => {
|
||
let userName = form2.getFieldValue('userName');
|
||
if (whetherIE.current) {
|
||
if (!multipartFiles) {
|
||
const childFrameObj = document.getElementById('faceLoginFrame');
|
||
childFrameObj.contentWindow.postMessage('capture', '*');
|
||
} else {
|
||
await ZjfakeFaceLogin({ userName, multipartFiles }).then((res) => {
|
||
if (res?.success) {
|
||
if (moment(res?.data?.expiration).diff(moment(), 'hours') < remainingTime) {
|
||
refreshToken(res?.data)
|
||
} else {
|
||
releaseCamera('faceLoginFrame');
|
||
sessionStorage.setItem('Authorization', res?.data?.value);
|
||
sessionStorage.setItem('refreshToken', res?.data?.refreshToken.value);
|
||
sessionStorage.setItem('scope', res?.data?.scope);
|
||
history.push('/redirect');
|
||
}
|
||
}
|
||
})
|
||
.finally(
|
||
() => { setSubmitLoading(false); }
|
||
);
|
||
}
|
||
}
|
||
else {
|
||
if (!multipartFiles) {
|
||
const canvas = document.createElement('canvas');
|
||
canvas.setAttribute('width', '300');
|
||
canvas.setAttribute('height', '200');
|
||
document.body.appendChild(canvas);
|
||
const context = canvas.getContext('2d');
|
||
context.drawImage(video.current, 0, 0, 300, 200);
|
||
canvas.toBlob(function (result: any) {
|
||
hanleFaceSubmit(result, null);
|
||
})
|
||
} else {
|
||
setSubmitLoading(true);
|
||
await ZjfakeFaceLogin({ userName, multipartFiles }).then((res) => {
|
||
if (res?.success) {
|
||
if (moment(res?.data?.expiration).diff(moment(), 'hours') < remainingTime) {
|
||
refreshToken(res?.data)
|
||
} else {
|
||
mediaStreamTrack.current?.stop();
|
||
sessionStorage.setItem('Authorization', res?.data?.value);
|
||
sessionStorage.setItem('refreshToken', res?.data?.refreshToken.value);
|
||
sessionStorage.setItem('scope', res?.data?.scope);
|
||
history.push('/redirect');
|
||
}
|
||
}
|
||
})
|
||
.finally(
|
||
() => { setSubmitLoading(false); }
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
const handleOk = () => { // 确定修改密码
|
||
changeForm.validateFields().then(res => {
|
||
if (changeForm.getFieldValue("newPassword") !== changeForm.getFieldValue("newPassword1")) {
|
||
message.warn('两次密码输入不一致,请重新输入')
|
||
} else {
|
||
setSping(true);
|
||
const date = changeForm.getFieldsValue();
|
||
changePass({ ...date }).then(res => {
|
||
if (res.success) {
|
||
setIsModalVisible(false);
|
||
message.success('修改密码成功');
|
||
}
|
||
}).finally(() => {
|
||
changeCaptchaEdit();
|
||
setSping(false);
|
||
});
|
||
}
|
||
})
|
||
}
|
||
//浏览器类型
|
||
const BrowserType = () => {
|
||
//取得浏览器的userAgent字符串
|
||
var userAgent = navigator.userAgent;
|
||
//判断是否IE<11
|
||
var isIE = userAgent.indexOf("compatible") > -1 && userAgent.indexOf("MSIE") > -1;
|
||
//判断是否IE的Edge浏览器
|
||
var isEdge = userAgent.indexOf("Edge") > -1 && !isIE;
|
||
//判断是否IE11
|
||
var isIE11 = userAgent.indexOf("Trident") > -1 && userAgent.indexOf("rv:11.0") > -1;
|
||
if (isIE) {
|
||
var reIE = new RegExp("MSIE(\\d+\\.\\d+);");
|
||
reIE.test(userAgent);
|
||
var fIEVersion = parseFloat(RegExp["$1"]);
|
||
if (fIEVersion == 7) {
|
||
return 7;
|
||
} else if (fIEVersion == 8) {
|
||
return 8;
|
||
} else if (fIEVersion == 9) {
|
||
return 9;
|
||
} else if (fIEVersion == 10) {
|
||
return 10;
|
||
} else {
|
||
//IE版本<=7
|
||
return 6;
|
||
}
|
||
} else if (isEdge) {
|
||
return 'edge';
|
||
} else if (isIE11) {
|
||
//IE11
|
||
whetherIE.current = true;
|
||
return 11;
|
||
} else {
|
||
//不是IE浏览器
|
||
return -1;
|
||
}
|
||
}
|
||
//初始化video
|
||
const InitMedia = () => {
|
||
if (!whetherIE.current) {
|
||
InitUserMedia({ video: { width: 480, height: 320 } }, success, error);
|
||
}
|
||
};
|
||
//访问用户媒体设备的兼容方法
|
||
const InitUserMedia = (constraints: any, success: any, error: any) => {
|
||
if (navigator.mediaDevices?.getUserMedia) {
|
||
//最新的标准API
|
||
navigator.mediaDevices?.getUserMedia(constraints).then(success).catch(error);
|
||
} else if (navigator.webkitGetUserMedia) {
|
||
//webkit核心浏览器
|
||
navigator.webkitGetUserMedia(constraints, success, error);
|
||
} else if (navigator.mozGetUserMedia) {
|
||
//firfox浏览器
|
||
navigator.mozGetUserMedia(constraints, success, error);
|
||
} else if (navigator.getUserMedia) {
|
||
//旧版API
|
||
navigator.getUserMedia(constraints, success, error);
|
||
}
|
||
}
|
||
//调用媒体设备成功回调方法
|
||
const success = (stream: any) => {
|
||
var result = stream.getVideoTracks().some(function (track: any) {
|
||
return track.enabled && track.readyState === 'live';
|
||
});
|
||
if (result) {
|
||
//兼容webkit核心浏览器
|
||
const CompatibleURL = window.URL || window.webkitURL;
|
||
//将视频流设置为video元素的源
|
||
console.log(stream);
|
||
//video.src = CompatibleURL.createObjectURL(stream);
|
||
video.current.srcObject = stream;
|
||
mediaStreamTrack.current = stream.getTracks()[0];
|
||
video.current.play();
|
||
}
|
||
}
|
||
//调用媒体设备失败回调方法
|
||
const error = (error: any) => {
|
||
message.warn('无法获取到摄像头权限,请确认是否存在摄像头及是否授权使用摄像头');
|
||
console.log(`访问用户媒体设备失败${error.name}, ${error.message}`);
|
||
}
|
||
|
||
//base64转blob
|
||
const base64ToBlob = (base64: string) => {
|
||
const parts = base64.split(";base64,");
|
||
const contentType = parts[0].split(":")[1];
|
||
const raw = window.atob(parts[1]);
|
||
const rawLength = raw.length;
|
||
const uInt8Array = new Uint8Array(rawLength);
|
||
for (let i = 0; i < rawLength; i += 1) {
|
||
uInt8Array[i] = raw.charCodeAt(i);
|
||
}
|
||
return new Blob([uInt8Array], { type: contentType });
|
||
};
|
||
|
||
const IELiveDetectFinish2 = async (data: any) => {
|
||
console.log(data);
|
||
setAction(4);
|
||
hanleFaceSubmit(data, null);
|
||
}
|
||
|
||
//IELiveDetectFinish
|
||
const IELiveDetectFinish = async (data: any) => {
|
||
if (data.code != 0) {
|
||
console.log(data);
|
||
setAction(4);
|
||
hanleFaceSubmit(data.file.file, null);
|
||
} else {
|
||
message.error('活体检测失败');
|
||
setSubmitLoading(false);
|
||
}
|
||
}
|
||
|
||
const UpdateDetectStatus = async (action: number, timer: number) => {
|
||
setSubmitLoading(true);
|
||
setAction(action);
|
||
setTimer(timer);
|
||
}
|
||
|
||
//是https或者是本地
|
||
const httpsOrLocal = () => {
|
||
let protocol = document.location.protocol;
|
||
if (protocol == 'https:') {
|
||
return true;
|
||
} else {
|
||
let host = window.location.hostname;
|
||
if (host == '127.0.0.1' || host == 'localhost') {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
//是否允许人脸登录
|
||
const allowedToFaceLogin = () => {
|
||
let browseType = BrowserType()
|
||
if ((browseType == 11 || browseType == -1) && httpsOrLocal()) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
//是否显示人脸登录
|
||
const showFaceLogin = async () => {
|
||
const res = await showFaceTab();
|
||
setFaceLoginShow(res?.data === 1);
|
||
}
|
||
|
||
|
||
useEffect(() => {
|
||
cookie.remove('mall3_token');
|
||
sessionStorage.clear();
|
||
changeCaptcha();
|
||
if (!allowedToFaceLogin()) {
|
||
setFaceLoginDisable(true);
|
||
}
|
||
showFaceLogin();
|
||
}, []);
|
||
|
||
useEffect(() => {
|
||
if (isModalVisible) {
|
||
changeForm.resetFields()
|
||
changeCaptchaEdit();
|
||
}
|
||
}, [isModalVisible]);
|
||
|
||
return (
|
||
<Spin spinning={spinning}>
|
||
<div className="login-box">
|
||
<div className="top">
|
||
<img src={logo} />
|
||
<h3>中国联通智慧供应链平台 | 招标采购中心</h3>
|
||
<Button className="change" danger style={{ borderColor: '#b30000', color: '#b30000' }} onClick={() => (setIsModalVisible(true))}>修改密码</Button>
|
||
</div>
|
||
<div className="main">
|
||
<div className="text">
|
||
<Tabs defaultActiveKey="1" onChange={onChange} size={'large'}>
|
||
<TabPane tab="账号密码登录" key="1">
|
||
<Form
|
||
name="basic"
|
||
className="form-box"
|
||
initialValues={{ remember: true }}
|
||
form={form}
|
||
onFinish={handleSubmit}
|
||
// onFinishFailed={onFinishFailed}
|
||
>
|
||
<Form.Item
|
||
label=""
|
||
name="userName"
|
||
rules={[{ required: true, message: '请输入用户名!' }]}
|
||
>
|
||
<Input
|
||
prefix={
|
||
<UserOutlined style={{ fontSize: '18px' }} className="site-form-item-icon" />
|
||
}
|
||
placeholder="请输入用户名"
|
||
/>
|
||
</Form.Item>
|
||
<Form.Item
|
||
label=""
|
||
name="password"
|
||
rules={[{ required: true, message: '请输入密码!' }]}
|
||
>
|
||
<Input.Password
|
||
prefix={
|
||
<LockOutlined style={{ fontSize: '18px' }} className="site-form-item-icon" />
|
||
}
|
||
placeholder="请输入密码"
|
||
/>
|
||
</Form.Item>
|
||
|
||
<Row>
|
||
<Col span={14}>
|
||
<Form.Item
|
||
label=""
|
||
name="captcha"
|
||
rules={[{ required: true, message: '请输入验证码!' }]}
|
||
>
|
||
<Input
|
||
prefix={
|
||
<SafetyCertificateOutlined
|
||
style={{ fontSize: '18px' }}
|
||
className="site-form-item-icon"
|
||
/>
|
||
}
|
||
placeholder="请输入验证码"
|
||
/>
|
||
</Form.Item>
|
||
</Col>
|
||
<Col span={10}>
|
||
<img className="verification" onClick={() => changeCaptcha()} src={imgUrl} />
|
||
</Col>
|
||
</Row>
|
||
|
||
{/* <Form.Item className="remember" name="remember" valuePropName="checked">
|
||
<Checkbox>记住密码</Checkbox>
|
||
</Form.Item> */}
|
||
<Form.Item>
|
||
<Button type="primary" className="w100" htmlType="submit">
|
||
登 录
|
||
</Button>
|
||
</Form.Item>
|
||
</Form>
|
||
</TabPane>
|
||
{faceLoginShow && <>
|
||
<TabPane tab="人脸识别登录" key="2" disabled={faceLoginDisable}>
|
||
<Form
|
||
name="basic2"
|
||
className="form-box"
|
||
initialValues={{ remember: true }}
|
||
form={form2}
|
||
// onFinish={hanleFaceSubmit.bind(this, null)}
|
||
onFinish={!whetherIE.current ? liveDetectStart : hanleFaceSubmit.bind(this, null)}
|
||
// onFinishFailed={onFinishFailed}
|
||
>
|
||
<Form.Item
|
||
label=""
|
||
name="userName"
|
||
rules={[{ required: true, message: '请输入用户名!' }]}
|
||
>
|
||
<Input
|
||
prefix={
|
||
<UserOutlined style={{ fontSize: '18px' }} className="site-form-item-icon" />
|
||
}
|
||
placeholder="请输入用户名"
|
||
/>
|
||
</Form.Item>
|
||
|
||
<Form.Item>
|
||
{!whetherIE.current ? (<video ref={video} width="382" height="200"></video>) : (<FrameFaceLogin faceCompareEvent2={IELiveDetectFinish2} faceCompareEvent={IELiveDetectFinish} faceDetectStatusEvent={UpdateDetectStatus} />)}
|
||
</Form.Item>
|
||
<Form.Item>
|
||
|
||
<Button type="primary" className="w100" loading={submitLoading} htmlType="submit">
|
||
{submitLoading ? actionText(action, Math.round(timer / 1000)) : '登 录'}
|
||
</Button>
|
||
</Form.Item>
|
||
</Form>
|
||
</TabPane>
|
||
{/* 加载摄像头 */}
|
||
<Form.Item hidden={!itemShow}>
|
||
<div>
|
||
<span style={{ color: classColor(action) }}>{actionText(action)}</span>
|
||
<span hidden={!timerShow}>{Math.round(timer / 1000)}</span>
|
||
</div>
|
||
</Form.Item>
|
||
<video ref={video} width="382" height="200"></video>
|
||
{/* onClick={() => {hanleFaceSubmit(null, null);}} */}
|
||
</>}
|
||
</Tabs>
|
||
</div>
|
||
</div>
|
||
<Modal
|
||
title="修改密码"
|
||
visible={isModalVisible}
|
||
onCancel={() => setIsModalVisible(false)}
|
||
footer={[
|
||
<Button type='primary' onClick={() => handleOk()} loading={spinning}>确 定</Button>,
|
||
<Button onClick={() => setIsModalVisible(false)}>取 消</Button>
|
||
]}
|
||
width={600}
|
||
>
|
||
<Spin spinning={spinning}>
|
||
<Form
|
||
{...layout}
|
||
name="basic"
|
||
form={changeForm}
|
||
>
|
||
<Form.Item
|
||
label="身份证(用户名)"
|
||
name="identityCard"
|
||
rules={[
|
||
{
|
||
required: true,
|
||
message: '请输入身份证(用户名)',
|
||
},
|
||
]}
|
||
>
|
||
<Input />
|
||
</Form.Item>
|
||
<Form.Item
|
||
label="原密码"
|
||
name="oldPassword"
|
||
rules={[
|
||
{
|
||
required: true,
|
||
message: '请输入原始密码',
|
||
},
|
||
]}
|
||
>
|
||
<Input.Password />
|
||
</Form.Item>
|
||
<Form.Item
|
||
label="新密码"
|
||
name="newPassword"
|
||
// rules={[
|
||
// {
|
||
// required: true,
|
||
// validator: (rule, value, callback) => {
|
||
// const oNumber = new RegExp(/\d/);
|
||
// const oLetter = new RegExp(/[a-zA-Z]/);
|
||
// const oSpecial = '.~!@#$%^&*()_+=-{}|:<>?,./[]-;\\"';
|
||
// if (!value) {
|
||
// callback('请输入新密码');
|
||
// return;
|
||
// }
|
||
// if (value.length < 6) {
|
||
// callback('密码不能小于六位,至少含字母、数字、特殊字符其中的2种!');
|
||
// return;
|
||
// }
|
||
// try {
|
||
// [...value].forEach(val => {
|
||
// if (
|
||
// !(
|
||
// oNumber.test(val) ||
|
||
// oLetter.test(val) ||
|
||
// oSpecial.indexOf(val) >= 0
|
||
// )
|
||
// ) {
|
||
// throw new Error();
|
||
// }
|
||
// });
|
||
// } catch (e) {
|
||
// callback('密码不能小于六位,至少含字母、数字、特殊字符其中的2种!');
|
||
// }
|
||
// const contain: boolean[] = [];
|
||
// [...value].forEach(val => {
|
||
// if (oNumber.test(val)) {
|
||
// contain[0] = true;
|
||
// }
|
||
// if (oLetter.test(val)) {
|
||
// contain[1] = true;
|
||
// }
|
||
// if (oSpecial.indexOf(val) >= 0) {
|
||
// contain[2] = true;
|
||
// }
|
||
// });
|
||
// if (contain.filter(item => item === true).length < 2) {
|
||
// callback('密码不能小于六位,至少含字母、数字、特殊字符其中的2种!');
|
||
// return;
|
||
// }
|
||
// callback();
|
||
// }
|
||
// }
|
||
// ]}
|
||
rules={[
|
||
{
|
||
required: true,
|
||
message: '请输入新密码',
|
||
}, {
|
||
pattern: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[`~!@#$%^&*()\-_=+\\|[{}\];:'",<.>/?])[a-zA-Z0-9`~!@#$%^&*()\-_=+\\|[{}\];:'",<.>/?]{8,20}$/,
|
||
message: "密码不符合规则,密码长度8-20位,必须包含大小写字母、数字和特殊字符",
|
||
}
|
||
]}
|
||
>
|
||
<Input.Password />
|
||
</Form.Item>
|
||
<Form.Item
|
||
label="新密码确认"
|
||
name="newPassword1"
|
||
rules={[
|
||
{
|
||
required: true,
|
||
message: '请再次输入新密码',
|
||
},
|
||
]}
|
||
>
|
||
<Input.Password />
|
||
</Form.Item>
|
||
<Form.Item
|
||
name="token"
|
||
hidden
|
||
>
|
||
<Input />
|
||
</Form.Item>
|
||
<Form.Item label="验证码" required>
|
||
<Form.Item
|
||
noStyle
|
||
name="code"
|
||
rules={[{ required: true, message: '请输入验证码!' }]}
|
||
>
|
||
<Input
|
||
style={{ width: "calc(60% - 8px)", marginRight: 8 }}
|
||
placeholder="请输入验证码"
|
||
/>
|
||
</Form.Item>
|
||
<Form.Item noStyle>
|
||
<img className="verification" onClick={() => changeCaptchaEdit()} src={imgUrlEdit} style={{ width: "40%" }} />
|
||
</Form.Item>
|
||
</Form.Item>
|
||
</Form>
|
||
</Spin>
|
||
</Modal>
|
||
</div>
|
||
</Spin>
|
||
);
|
||
};
|
||
|
||
export default Index;
|
||
|