From e8fc035f32c89f04614e91ba09fab26d095e23b9 Mon Sep 17 00:00:00 2001 From: piupuer Date: Wed, 16 Oct 2024 14:09:10 +0800 Subject: [PATCH] [fix]login captcha --- src/layouts/index.tsx | 2 +- src/modules/user/index.ts | 18 ++-- src/pages/Login/components/Login/index.tsx | 100 ++++++++++++++++++--- src/pages/Login/index.tsx | 10 +-- src/pages/System/User/index.tsx | 2 +- 5 files changed, 105 insertions(+), 27 deletions(-) diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx index 64582d5..8c51881 100644 --- a/src/layouts/index.tsx +++ b/src/layouts/index.tsx @@ -7,7 +7,7 @@ import { selectGlobal, toggleSetting, toggleMenu, ELayout, switchTheme } from 'm import Setting from './components/Setting'; import AppLayout from './components/AppLayout'; import Style from './index.module.less'; -import { getUserInfo, permissionMenuPaths, TOKEN_NAME, userInfo, userInfoError } from '../modules/user'; +import { info as getUserInfo, permissionMenuPaths, TOKEN_NAME, userInfo, userInfoError } from '../modules/user'; export default memo(() => { const globalState = useAppSelector(selectGlobal); diff --git a/src/modules/user/index.ts b/src/modules/user/index.ts index 5709ac2..91e5e24 100644 --- a/src/modules/user/index.ts +++ b/src/modules/user/index.ts @@ -1,6 +1,6 @@ import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; import { RootState } from '../store'; -import { login as remoteLogin, userInfo as remoteUserInfo } from 'services/user'; +import { login as remoteLogin, userInfo as remoteUserInfo, userStatus as remoteStatus } from 'services/user'; import allRoutes, { IRouter } from 'router'; import { UserInfoReply } from 'services/model/userModel'; @@ -30,8 +30,14 @@ export const login = createAsyncThunk(`${namespace}/login`, async (userInfo: Rec return res.token; }); -// getUserInfo -export const getUserInfo = createAsyncThunk(`${namespace}/getUserInfo`, async () => { +// login +export const status = createAsyncThunk(`${namespace}/status`, async (data: any) => { + const res = await remoteStatus(data); + return res; +}); + +// info +export const info = createAsyncThunk(`${namespace}/getInfo`, async () => { const res = await remoteUserInfo(); return res; }); @@ -107,15 +113,15 @@ const userSlice = createSlice({ state.token = action.payload; }) - .addCase(getUserInfo.pending, (state) => { + .addCase(info.pending, (state) => { state.error = false; }) - .addCase(getUserInfo.fulfilled, (state, action) => { + .addCase(info.fulfilled, (state, action) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore state.userInfo = { ...action.payload }; }) - .addCase(getUserInfo.rejected, (state) => { + .addCase(info.rejected, (state) => { state.error = true; localStorage.removeItem(TOKEN_NAME); }); diff --git a/src/pages/Login/components/Login/index.tsx b/src/pages/Login/components/Login/index.tsx index 51a127e..3ed31ae 100644 --- a/src/pages/Login/components/Login/index.tsx +++ b/src/pages/Login/components/Login/index.tsx @@ -1,14 +1,23 @@ import React, { useState, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; -import { Form, MessagePlugin, Input, Checkbox, Button, FormInstanceFunctions, SubmitContext } from 'tdesign-react'; -import { LockOnIcon, UserIcon, BrowseOffIcon, BrowseIcon, RefreshIcon } from 'tdesign-icons-react'; +import { Form, MessagePlugin, Input, Image, Button, FormInstanceFunctions, SubmitContext } from 'tdesign-react'; +import { + LockOnIcon, + UserIcon, + BrowseOffIcon, + BrowseIcon, + RefreshIcon, + SecuredIcon, + ImageErrorIcon, +} from 'tdesign-icons-react'; import classnames from 'classnames'; import QRCode from 'qrcode.react'; import { useAppDispatch } from 'modules/store'; -import { login } from 'modules/user'; +import { login, status } from 'modules/user'; import useCountdown from '../../hooks/useCountDown'; import Style from './index.module.less'; +import { refreshCaptcha } from '../../../../modules/system/user'; const { FormItem } = Form; @@ -17,6 +26,11 @@ export type ELoginType = 'password' | 'phone' | 'qrcode'; export default function Login() { const [loginType, changeLoginType] = useState('password'); const [showPsw, toggleShowPsw] = useState(false); + const [disableLogin, toggleDisableLogin] = useState(false); + const [showCaptcha, toggleShowCaptcha] = useState(false); + const [captchaId, setCaptchaId] = useState(''); + const [captchaImg, setCaptchaImg] = useState(''); + const [refreshCaptchaCount, setRefreshCaptchaCount] = useState(0); const { countdown, setupCountdown } = useCountdown(60); const formRef = useRef(); const navigate = useNavigate(); @@ -26,7 +40,12 @@ export default function Login() { if (e.validateResult === true) { try { const formValue = formRef.current?.getFieldsValue?.(true) || {}; - const res: any = await dispatch(login(formValue)); + const res: any = await dispatch( + login({ + ...formValue, + captchaId, + }), + ); if (res.error?.message) { throw new Error(res.error?.message); } @@ -36,10 +55,54 @@ export default function Login() { navigate('/'); } catch (e: any) { MessagePlugin.error(e.message); + await getUserStatus(); + } finally { } } }; + const getUserStatus = async () => { + try { + const formValue = formRef.current?.getFieldsValue?.(true) || {}; + const res: any = await dispatch(status({ username: formValue.username })); + if (res.error?.message) { + return; + } + const data = res.payload; + if (data.captcha) { + if (data.captcha.id !== '') { + toggleShowCaptcha(true); + } else { + toggleShowCaptcha(false); + } + setCaptchaId(data.captcha.id); + setCaptchaImg(data.captcha.img); + } else { + toggleShowCaptcha(false); + } + if (data.locked) { + toggleDisableLogin(true); + } else { + toggleDisableLogin(false); + } + } finally { + } + }; + + async function doRefreshCaptcha() { + try { + const res: any = await dispatch(refreshCaptcha()); + if (res.error?.message) { + throw new Error(res.error?.message); + } + setCaptchaId(res.payload?.captcha?.id); + setCaptchaImg(res.payload?.captcha?.img); + setRefreshCaptchaCount(refreshCaptchaCount + 1); + } catch (e: any) { + MessagePlugin.error(e.message); + } + } + // const switchType = (val: ELoginType) => { // formRef.current?.reset?.(); // changeLoginType(val); @@ -60,7 +123,7 @@ export default function Login() { initialData='super' rules={[{ required: true, message: '账号必填', type: 'error' }]} > - } /> + } /> -
- 记住账号 - 忘记账号? -
+ {showCaptcha && !disableLogin && ( + + } placeholder='请输入验证码' clearable /> + + } + error={} + /> + + + )} )} @@ -120,13 +193,20 @@ export default function Login() { )} - {loginType !== 'qrcode' && ( + {loginType !== 'qrcode' && !disableLogin && ( )} + {loginType !== 'qrcode' && disableLogin && ( + + + + )} {/*
*/} {/* {loginType !== 'password' && ( */} {/* switchType('password')}> */} diff --git a/src/pages/Login/index.tsx b/src/pages/Login/index.tsx index 8710812..f5a0dbf 100644 --- a/src/pages/Login/index.tsx +++ b/src/pages/Login/index.tsx @@ -25,18 +25,10 @@ export default memo(() => {

登录到

TDesign Starter

-
-

- {type === 'register' ? '已有账号?' : '没有账号吗?'} -

-

- {type === 'register' ? '登录' : '注册新账号'} -

-
{type === 'login' ? : }
-
Copyright @ 2021-2022 Tencent. All Rights Reserved
+ ); }); diff --git a/src/pages/System/User/index.tsx b/src/pages/System/User/index.tsx index 13660c4..8e60c5e 100644 --- a/src/pages/System/User/index.tsx +++ b/src/pages/System/User/index.tsx @@ -26,7 +26,7 @@ import CommonStyle from '../../../styles/common.module.less'; import { create, deleteByIds, find, refreshCaptcha, reset, selectSystemUser, update } from 'modules/system/user'; import { find as findRole } from 'modules/system/role'; import { find as findAction } from 'modules/system/action'; -import { BOOL, PAGE } from '../../../constants'; +import { PAGE } from '../../../constants'; import Permission from 'components/Permission'; const { FormItem } = Form;