diff --git a/package.json b/package.json index e398a5b..22758d6 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "react-sortable-hoc": "^2.0.0", "react-to-print": "^3.0.5", "umi": "^4.4.11", + "vconsole": "^3.15.1", "weixin-js-sdk": "1.6.0", "xlsx": "^0.18.5" }, diff --git a/src/mobile/common/CommobileListEvent.js b/src/mobile/common/CommobileListEvent.js index f84e743..8bf4387 100644 --- a/src/mobile/common/CommobileListEvent.js +++ b/src/mobile/common/CommobileListEvent.js @@ -389,6 +389,9 @@ export default (ChildComponent) => { this.handleBatchCancelExamine(); } else if (name === 'BtnAdd') { this.handleAdd(); + } else if (name === 'BtnScanFace') { + console.log(this.props, 'BtnScanFace'); + } }; diff --git a/src/mobile/login/LoginMobile.js b/src/mobile/login/LoginMobile.js index 99c8a21..90d87fc 100644 --- a/src/mobile/login/LoginMobile.js +++ b/src/mobile/login/LoginMobile.js @@ -30,7 +30,13 @@ class LoginMobile extends React.Component { username: localStorage.getItem(`${commonConfig.prefix}username`) || '', userpwd: localStorage.getItem(`${commonConfig.prefix}userpwd`) || '', loginCompany: JSON.parse(localStorage.getItem(`${commonConfig.prefix}loginCompany`)) || [], + cameraVisible: false, + videoStream: null, + cameraError: null, + autoCaptureTimer: null, + faceRecognizing: false, }; + this.videoRef = React.createRef(); } async componentWillMount() { const configUrl = `${commonConfig.server_host}sysbrands/getSysbrands`; @@ -42,7 +48,7 @@ class LoginMobile extends React.Component { // const sId = commonUtils.isNotEmptyObject(this.state.sId) ? this.state.sId : companys[0].sId; if (commonUtils.isNotEmptyArr(companys)) { // eslint-disable-next-line array-callback-return - companys.map((item) => { + companys.forEach((item) => { const map = { ...item }; map.label = item.sName; map.value = item.sId; @@ -92,7 +98,7 @@ class LoginMobile extends React.Component { }, ] ); - }else { + } else { alert( @@ -106,32 +112,128 @@ class LoginMobile extends React.Component { 3.其他以颜色或加粗进行标识的重要条款。
如您对以上协议有任何疑问,可通过人工客服或发邮件至yanghl@xlyerp.com与我们联系。您点击“同意并继续”的行为即表示您已阅读完毕并同意以上协议的全部内容。

, [ - { - text: '不同意', - onPress: () => { - const { plus } = window; - if (plus) { - plus.runtime.quit(); - } - }, + { + text: '不同意', + onPress: () => { + const { plus } = window; + if (plus) { + plus.runtime.quit(); + } }, - { - text: '同意并继续', - onPress: () => { - localStorage.setItem(`${commonConfig.prefix}privacyPolicy`, 'agree'); - }, + }, + { + text: '同意并继续', + onPress: () => { + localStorage.setItem(`${commonConfig.prefix}privacyPolicy`, 'agree'); }, - ], + }, + ], ); } } // vConsole = new VConsole(); } - // componentWillUnmount() { - // vConsole.destroy(); - // } + componentWillUnmount() { + // vConsole.destroy(); + this.closeCamera(); + if (this.state.autoCaptureTimer) { + clearTimeout(this.state.autoCaptureTimer); + } + } + closeCamera = () => { + if (this.state.videoStream) { + this.state.videoStream.getTracks().forEach(track => track.stop()); + this.setState({ videoStream: null }); + } + if (this.videoRef.current) { + this.videoRef.current.srcObject = null; + } + if (this.state.autoCaptureTimer) { + clearTimeout(this.state.autoCaptureTimer); + } + }; + + startCamera = () => { + navigator.mediaDevices.getUserMedia({ + video: { + facingMode: 'user', + width: { ideal: 640 }, + height: { ideal: 480 }, + }, + }) + .then(stream => { + this.setState({ + cameraVisible: true, + videoStream: stream, + cameraError: null, + autoCaptureTimer: setTimeout(() => { + this.capturePhoto(); + }, 2000), + }, () => { + if (this.videoRef.current) { + this.videoRef.current.srcObject = stream; + this.videoRef.current.play(); + } + }); + }) + .catch(error => { + console.error('无法访问摄像头:', error); + this.setState({ + cameraError: '无法访问摄像头,请检查权限设置', + cameraVisible: false, + }); + Toast.fail('无法访问摄像头,请检查权限'); + }); + }; + capturePhoto = () => { + if (!this.videoRef.current || !this.state.cameraVisible) return; + + const video = this.videoRef.current; + const canvas = document.createElement('canvas'); + canvas.width = video.videoWidth; + canvas.height = video.videoHeight; + const ctx = canvas.getContext('2d'); + ctx.drawImage(video, 0, 0, canvas.width, canvas.height); + + const base64 = canvas.toDataURL('image/jpeg', 0.85).split(',')[1]; + // 不再立即 closeCamera(),而是进入识别状态 + this.setState({ faceRecognizing: true }); + this.processFaceRecognition(base64); + }; + processFaceRecognition = (base64) => { + const url = `${commonConfig.face_host}/face/faceSearch`; + commonServices.postValueService(null, [{ + image: base64, + image_type: "BASE64", + face_field: "age", + option: "COMMON" + }], url) + .then(({ data: dataReturn }) => { + if (dataReturn?.code === 1 && dataReturn.dataset.rows.length > 0) { + const value = { + sEmployeeNo: dataReturn.dataset.rows[0].sEmployeeNo, + sParentId: dataReturn.dataset.rows[0].sBrandsId, + sId: dataReturn.dataset.rows[0].sSubsidiaryId + }; + this.handleLogin(value); + } else { + Toast.fail(dataReturn?.msg || '人脸识别失败,请重试'); + } + }) + .catch(err => { + console.error('人脸识别失败:', err); + Toast.fail('人脸识别失败'); + }) + .finally(() => { + this.closeCamera(); + this.setState({ + cameraVisible: false, + faceRecognizing: false + }); + }); + }; onChange = (name, newValue) => { if (name === 'username') { this.setState({ username: newValue }); @@ -145,7 +247,7 @@ class LoginMobile extends React.Component { }); } - takeTenPhotos = () => { + takeTenPhotos = () => { if (!window.plus) { return Promise.reject(new Error('请在 HBuilder 打包的 App 中运行')); } @@ -279,18 +381,32 @@ class LoginMobile extends React.Component { } render() { - const { loginCompany, companys } = this.state; + const { loginCompany, companys, cameraVisible, cameraError } = this.state; console.log('2loginCompany22', loginCompany, companys); const { getFieldProps } = this.props.form; let sLanguage = commonUtils.isNotEmptyArr(companys) ? companys[0].sLanguage : 'sChinese'; - const settingTitle = sLanguage === 'sEnglish' ? 'server settings' :'服务器设置'; - const loginTitle = sLanguage === 'sEnglish' ? 'Login' :'登录'; - const loginFaceTitle = sLanguage === 'sEnglish' ? 'Face Login' :'人脸登录'; - const pleaseInputUser = sLanguage === 'sEnglish' ? 'please Input User' :'请输入用户'; - const pleaseInputPassword = sLanguage === 'sEnglish' ? 'please Input Password' :'请输入密码'; + const settingTitle = sLanguage === 'sEnglish' ? 'server settings' : '服务器设置'; + const loginTitle = sLanguage === 'sEnglish' ? 'Login' : '登录'; + const loginFaceTitle = sLanguage === 'sEnglish' ? 'Face Login' : '人脸登录'; + const pleaseInputUser = sLanguage === 'sEnglish' ? 'please Input User' : '请输入用户'; + const pleaseInputPassword = sLanguage === 'sEnglish' ? 'please Input Password' : '请输入密码'; return (
+ {/* 摄像头预览层 - 仅在cameraVisible为true时显示 */} + {cameraVisible && ( +
+
+ )}
login
@@ -349,7 +465,7 @@ class LoginMobile extends React.Component { -