feat(login): 添加验证码功能并优化登录页面

- 在登录页面添加验证码输入和验证功能
- 实现验证码图片的获取和更新
- 优化登录逻辑,处理登录失败的情况
- 调整表单字段和布局以支持验证码
main
Tuzki 4 months ago
parent e01ba9d839
commit 7e65a15c86
  1. 4
      web/client/api/system/index.ts
  2. 7
      web/new-components/common/blurredCard/style.css
  3. 58
      web/pages/login.tsx

@ -137,3 +137,7 @@ export const getUserMenu = () => {
export const getMenuDetail = (id: number) => {
return GET<any, any>(`/api/menu/menu_detail/` + id);
};
//验证码
export const getVerifyCode = () => {
return GET<any, any>(`/api/captcha`);
};

@ -26,4 +26,9 @@
position: relative;
overflow: hidden;
}
.m-dropdown-menu {
z-index: 1050; /* 小于 Modal 的 z-index */
}
.ant-modal-wrap {
z-index: 1051;
}

@ -1,27 +1,33 @@
// login.tsx 最简测试版本
import { doLogin, getUserMenu } from '@/client/api';
import { doLogin, getUserMenu, getVerifyCode } from '@/client/api';
import { STORAGE_USERINFO_KEY, STORAGE_USERINFO_VALID_TIME_KEY } from '@/utils/constants/index';
import { useRequest } from 'ahooks';
import { Button, Form, Image, Input, Spin, notification } from 'antd';
import { useRouter } from 'next/router';
import { FC } from 'react';
import { FC, useState, useEffect } from 'react';
const imgSrc =
'data:image/png;base64,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';
'data:image/png;base64,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';
const LoginPage: FC = () => {
const router = useRouter();
const [form] = Form.useForm(); // ✅ 新增 form 实例
const [verifyCodeImage, setVerifyCodeImage] = useState<string>(imgSrc);
const [catcha_id, setCaptcha_id] = useState<string>('');
const { run: doLoginFn, loading } = useRequest(
async (formData: { username: string; password: string }) => {
return await doLogin(formData); // 直接传递表单对象
async (formData: { username: string; password: string; catcha_id: string; verificationCode: string }) => {
formData.catcha_id = catcha_id;
return await doLogin(formData);
},
{
manual: true,
onSuccess: async (res, params) => {
const [formData] = params;
if (!res.data.success) {
notification.error({
message: '登录失败,请检查账号密码',
message: res.data.err_msg ||'登录失败,请检查账号密码',
});
return;
}
@ -36,7 +42,6 @@ const LoginPage: FC = () => {
localStorage.setItem(STORAGE_USERINFO_KEY, JSON.stringify(user));
localStorage.setItem(STORAGE_USERINFO_VALID_TIME_KEY, Date.now().toString());
// 获取用户菜单
const menuRes = await getUserMenu();
if (!menuRes.data.success) {
notification.error({
@ -47,10 +52,42 @@ const LoginPage: FC = () => {
localStorage.setItem('menu', JSON.stringify(menuRes.data.data));
router.push('/playground/appGround');
},
onError: (error, params) => {
notification.error({
message: error.response.data.err_msg || '登录失败,请稍后再试',
});
},
debounceWait: 500,
}
);
const { run: fetchVerifyCode, loading: verifying } = useRequest(
async () => {
const res = await getVerifyCode();
if (res.data && res.data.success) {
setVerifyCodeImage(res.data.data.captcha_image);
const newCaptchaId = res.data.data.captcha_id;
setCaptcha_id(newCaptchaId); // ✅ 新增 captcha_id 变量
form.setFieldsValue({ captcha_id: newCaptchaId }); // ✅ 更新表单字段
console.log('captcha_id:', newCaptchaId); // ✅ 打印最新值
} else {
notification.error({
message: '获取验证码失败',
description: res.data.err_msg || '请稍后再试',
});
}
},
{
manual: true,
debounceWait: 500,
}
);
useEffect(() => {
fetchVerifyCode();
}, []);
return (
<div className='relative h-screen w-screen overflow-hidden'>
{/* 视频背景 */}
@ -69,14 +106,17 @@ const LoginPage: FC = () => {
<Input placeholder='用户名' />
</Form.Item>
<Form.Item name='phone' rules={[{ required: true, message: '请输入验证码' }]}>
<Form.Item name='verificationCode' rules={[{ required: true, message: '请输入验证码' }]}>
<div className='flex items-center justify-between'>
<Input placeholder="请输入验证码">
</Input>
<img src={imgSrc} className='h-[30px] mx-[40px]' />
<img onClick={fetchVerifyCode} src={verifyCodeImage} className='h-[30px] mx-[40px]' />
</div>
</Form.Item>
<Form.Item name="catcha_id" noStyle>
<Input type="hidden" />
</Form.Item>
<Form.Item name='password' rules={[{ required: true, message: '请输入密码' }]}>
<Input.Password placeholder='密码' />
</Form.Item>

Loading…
Cancel
Save