扫码登录

197 阅读2分钟

扫码登录页面

a1.jpeg

export default ({ dispatch, logined }: { dispatch: (action: Action) => void; logined: () => void }) => {
    const [expired, setExpired] = useState(false);
    const [qrinfo, setQrinfo] = useState('');
    const [failed, setFailed] = useState(false);
    


    return (
        <div className={css.qrLogin}>
            <div className={css.qrcode} onClick={reflush}>
                {qrinfo && qrinfo !== '' && <Qrcode url={qrinfo} />}
                {expired && (
                    <div className={css.expiredMask}>
                        {failed ? '二维码获取失败' : '二维码已失效'}
                        <div className={css.reflush} onClick={reflush}>
                            点击刷新
                        </div>
                    </div>
                )}
            </div>
        </div>
    );
};

账号密码登录页面

a2.jpeg

// view
<div className={css.loginBtn} onClick={submit}>
     登录
</div>

// view
const userNameRef = createRef<HTMLInputElement>();
const userPWDRef = createRef<HTMLInputElement>();

   const submit = useCallback(() => {
        if (isEmpty(userNameRef) || isEmpty(userPWDRef)) {
            Message.error(loginType === LoginType.SMS ? '手机号和验证码不能为空' : '账号密码不能为空');
            return;
        }

        const vaildInfo = vaildInput(loginType, userNameRef, userPWDRef);
        if (!vaildInfo.passed) {
            Message.error(vaildInfo.result);
            return;
        }

        const accountOrNumber = userNameRef.current?.value.trim();
        const passwordOrSMSCode = userPWDRef.current?.value.trim();

        dispatch(
            loginType === LoginType.SMS
                ? {
                      type: LoginActions.QUICK_LOGIN,
                      payload: { phone: accountOrNumber, code: passwordOrSMSCode, logined }
                  }
                : { type: LoginActions.LOGIN, payload: { nickName: accountOrNumber, password: passwordOrSMSCode, logined } }
        );

        //提交登录
}, [loginType, smsDelay]);

// saga
export function* smsLogin() {
    while (true) {
        //sms: { phone, code }
        const {
            payload: { logined, ...payload }
        } = yield take(LoginActionTypes.QUICK_LOGIN);
        const result = yield call(request, { url: apiUrl.SMS_LOGIN, data: payload, method: 'POST' });
        if (yield call(vaildCode, result)) {
            //登录成功
            yield call(saveUserToken, result);
            yield call(logined);
        } else {
            yield call(showError, result.message ?? '验证码登录失败');
        }
    }
}

jest

it('手机号验证码登录', () => {
        expect(smsLoginSaga.next().value).toEqual(take(LoginActionTypes.QUICK_LOGIN));
        expect(smsLoginSaga.next({ payload: { logined: console.log } }).value).toEqual(
            call(request, { url: apiUrl.SMS_LOGIN, data: {}, method: 'POST' })
        );
        expect(smsLoginSaga.next({ code: 0 }).value).toEqual(call(vaildCode, { code: 0 }));
        expect(smsLoginSaga.clone().next(false).value).toEqual(call(showError, expect.any(String)));
        expect(smsLoginSaga.next(true).value).toEqual(call(saveUserToken, expect.any(Object)));
        expect(smsLoginSaga.next().value).toEqual(call(console.log));
        // expect(smsLoginSaga.next().value).toEqual(take(LoginActionTypes.USER_NEED_LOGIN));
    });

点击刷新

// saga
export function* quickLogin() {
    while (true) {
        const {
            payload: { qrcode, logined, failed }
        } = yield take(LoginActionTypes.QRCODE_CREATE);
        const { result, dispose } = yield race({
            result: call(request, { url: apiUrl.LOGIN_QRCODE_CREATE }),
            dispose: take(LoginActionTypes.QRCODE_DISPOSE)
        });

        if (dispose) {
            //提前销毁
            continue;
        }

        if (yield call(vaildCode, result)) {
            if (result.url) {
                yield call(qrcode, result.url);
                const checkTask = yield fork(checkQrcode, result.qr_code, logined, failed);
                yield put({
                    type: LoginActionTypes.SET_ERCODE,
                    payload: {
                        code: result.qr_code
                    }
                });

                yield race({ expired: take(LoginActionTypes.QRCODE_EXPIRED), dispose: take(LoginActionTypes.QRCODE_DISPOSE) });

                yield call(cancelAll, [checkTask]);
            } else {
                //yield call(showError, result.message ?? '二维码地址错误');
                yield call(failed);
            }
        } else {
            //yield call(showError, result.message ?? '获取二维码失败');
            yield call(failed);
        }
    }
}
// view
const reflush = useCallback(() => {
        setExpired(false);
        dispatch({ type: LoginActions.QRCODE_CREATE, payload: createOption });
    }, [expired, failed]);
    
    
export function* quickLogin() {
    while (true) {
        const {
            payload: { qrcode, logined, failed }
        } = yield take(LoginActionTypes.QRCODE_CREATE);
        const { result, dispose } = yield race({
            result: call(request, { url: apiUrl.LOGIN_QRCODE_CREATE }),
            dispose: take(LoginActionTypes.QRCODE_DISPOSE)
        });

        if (dispose) {
            //提前销毁
            continue;
        }

        if (yield call(vaildCode, result)) {
            if (result.url) {
                yield call(qrcode, result.url);
                const checkTask = yield fork(checkQrcode, result.qr_code, logined, failed);
                yield put({
                    type: LoginActionTypes.SET_ERCODE,
                    payload: {
                        code: result.qr_code
                    }
                });

                yield race({ expired: take(LoginActionTypes.QRCODE_EXPIRED), dispose: take(LoginActionTypes.QRCODE_DISPOSE) });

                yield call(cancelAll, [checkTask]);
            } else {
                //yield call(showError, result.message ?? '二维码地址错误');
                yield call(failed);
            }
        } else {
            //yield call(showError, result.message ?? '获取二维码失败');
            yield call(failed);
        }
    }
}

获取二维码

const expiredTask = useRef<any>();

const getQrcodeHandler = useCallback(
        (url: string) => {
            setExpired(false);
            setFailed(false);
            setQrinfo(url);

            clearTimeout(expiredTask.current);
            expiredTask.current = setTimeout(() => {
                setExpired(true);
                dispatch({ type: LoginActions.QRCODE_EXPIRED, payload: {} });
            }, 600 * 1000);
        },
        [expired, dispatch]
    );

const getQrcodeFailure = useCallback(() => {
        setExpired(true);
        setFailed(true);
        dispatch({ type: LoginActions.QRCODE_EXPIRED, payload: {} });
}, [failed]);
    
    
const createOption = useMemo(() => {
        return { qrcode: getQrcodeHandler, logined, failed: getQrcodeFailure };
    }, [getQrcodeHandler, getQrcodeFailure, logined]);
    
useEffect(() => {
        if (qrinfo === '') {
            dispatch({ type: LoginActions.QRCODE_CREATE, payload: createOption });
        }

        return () => {
            clearTimeout(expiredTask.current);
            dispatch({ type: LoginActions.QRCODE_DISPOSE, payload: {} });
        };
    }, []);