From b5b0ca8a882f89850f46e021e892784c1d6d279d Mon Sep 17 00:00:00 2001
From: chenxt <10125295+chen-xintao97@user.noreply.gitee.com>
Date: Sun, 4 Jan 2026 11:39:24 +0800
Subject: [PATCH] 扫脸登录
---
src/mobile/login/LoginMobile.js | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
1 file changed, 143 insertions(+), 27 deletions(-)
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 && (
+
+
+ {cameraError && (
+
{cameraError}
+ )}
+
+ )}
@@ -349,7 +465,7 @@ class LoginMobile extends React.Component {
-
--
libgit2 0.22.2