Skip to content

Commit

Permalink
[fix]login captcha
Browse files Browse the repository at this point in the history
  • Loading branch information
piupuer committed Oct 16, 2024
1 parent 23c3476 commit e8fc035
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 27 deletions.
2 changes: 1 addition & 1 deletion src/layouts/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
18 changes: 12 additions & 6 deletions src/modules/user/index.ts
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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;
});
Expand Down Expand Up @@ -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);
});
Expand Down
100 changes: 90 additions & 10 deletions src/pages/Login/components/Login/index.tsx
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -17,6 +26,11 @@ export type ELoginType = 'password' | 'phone' | 'qrcode';
export default function Login() {
const [loginType, changeLoginType] = useState<ELoginType>('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<FormInstanceFunctions>();
const navigate = useNavigate();
Expand All @@ -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);
}
Expand All @@ -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);
Expand All @@ -60,7 +123,7 @@ export default function Login() {
initialData='super'
rules={[{ required: true, message: '账号必填', type: 'error' }]}
>
<Input size='large' placeholder='请输入用户名' prefixIcon={<UserIcon />} />
<Input size='large' placeholder='请输入用户名' onBlur={getUserStatus} prefixIcon={<UserIcon />} />
</FormItem>
<FormItem
name='password'
Expand All @@ -82,10 +145,20 @@ export default function Login() {
}
/>
</FormItem>
<div className={classnames(Style.checkContainer, Style.rememberPwd)}>
<Checkbox>记住账号</Checkbox>
<span className={Style.checkContainerTip}>忘记账号?</span>
</div>
{showCaptcha && !disableLogin && (
<FormItem name='captchaAnswer'>
<Input prefixIcon={<SecuredIcon />} placeholder='请输入验证码' clearable />
<span onClick={doRefreshCaptcha}>
<Image
key={refreshCaptchaCount}
src={captchaImg}
style={{ width: '64px', height: '32px', background: '#eee' }}
loading={<ImageErrorIcon style={{ width: '100%', height: '100%', background: '#999' }} />}
error={<ImageErrorIcon style={{ width: '100%', height: '100%', background: '#999' }} />}
/>
</span>
</FormItem>
)}
</>
)}

Expand Down Expand Up @@ -120,13 +193,20 @@ export default function Login() {
</FormItem>
</>
)}
{loginType !== 'qrcode' && (
{loginType !== 'qrcode' && !disableLogin && (
<FormItem className={Style.btnContainer}>
<Button block size='large' type='submit'>
登录
</Button>
</FormItem>
)}
{loginType !== 'qrcode' && disableLogin && (
<FormItem className={Style.btnContainer}>
<Button block size='large' type='submit' theme='default' disabled>
账户已锁定, 请过会儿再试
</Button>
</FormItem>
)}
{/* <div className={Style.switchContainer}> */}
{/* {loginType !== 'password' && ( */}
{/* <span className='tip' onClick={() => switchType('password')}> */}
Expand Down
10 changes: 1 addition & 9 deletions src/pages/Login/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,10 @@ export default memo(() => {
<div className={Style.titleContainer}>
<h1 className={Style.title}>登录到</h1>
<h1 className={Style.title}>TDesign Starter</h1>
<div className={Style.subTitle}>
<p className={classNames(Style.tip, Style.registerTip)}>
{type === 'register' ? '已有账号?' : '没有账号吗?'}
</p>
<p className={classNames(Style.tip, Style.loginTip)} onClick={handleSwitchLoginType}>
{type === 'register' ? '登录' : '注册新账号'}
</p>
</div>
</div>
{type === 'login' ? <Login /> : <Register />}
</div>
<footer className={Style.copyright}>Copyright @ 2021-2022 Tencent. All Rights Reserved</footer>
<footer className={Style.copyright}>Copyright @ 2024 Go Cinch. All Rights Reserved</footer>
</div>
);
});
2 changes: 1 addition & 1 deletion src/pages/System/User/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit e8fc035

Please sign in to comment.