文章目录
- 前言
- 一、新增登录页面
- 1.登录页面代码
- 2.登录处理
- 3.修改app.tsx
- 二、说一下逻辑
- 1. 流程图
- 2. 注意点
- 总结
前言
前面写了创建项目及修改一些配置。这一章写写登录页面。
一、新增登录页面
新增登录页面,会涉及Umi的目录结构。先看一下文档再动手。
1.登录页面代码
// src/pages/user/login.tsx
import {
LockOutlined,
MobileOutlined,
UserOutlined,
} from '@ant-design/icons';
import {
LoginForm,
ProFormCaptcha,
ProFormCheckbox,
ProFormText,
} from '@ant-design/pro-components';
import { Divider, Space, Tabs, message } from 'antd';
import { useState } from 'react';
import { history,useModel } from '@umijs/max';
import { login } from "@/services/demo/UserController"; //该处代码下面见
import { flushSync } from 'react-dom'; //强刷新一下
type LoginType = 'phone' | 'account'; //看字面意思就可以了
export default () => {
const items = [
{ label: '账户密码登录', key: 'account' },
{ label: '手机号登录', key: 'phone' },
];
const [loginType, setLoginType] = useState<LoginType>('account');
const { initialState, setInitialState } = useModel('@@initialState'); //注意一下,后面解释
//写一个获取用户信息的函数
const fetchUserInfo = async () => {
const userInfo = await initialState?.fetchUserInfo?.(); //出现initialState了,后面解释
if (userInfo) {
flushSync(() => {
setInitialState((s) => ({
...s,
currentUser: userInfo,
}));
});
}
};
const onSubmit = async (formData: API.LoginParams) => {
try {
const success = await login({ ...formData });
if (success) {
message.success("登录成功");
await fetchUserInfo(); //调用获取用户信息函数
history.push('/');
}else{
message.error("登录失败");
}
}catch (error:any) {
if(error.response){
message.error(error.response.data.message);
}else{
message.error("未知错误");
}
}
};
return (
<div
style={{
display: 'flex',
flexDirection: 'column',
height: '100vh',
overflow: 'auto',
backgroundImage:
"url('https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/V-_oS6r-i7wAAAAAAAAAAAAAFl94AQBr')",
backgroundSize: '100% 100%',
}}
>
<LoginForm
onFinish={onSubmit}
title="AntDesgin Pro"
subTitle="基于Umi从零搭建Ant Desgin Pro"
>
<Tabs
centered
items={items}
activeKey={loginType}
onChange={(activeKey) => setLoginType(activeKey as LoginType)}
></Tabs>
{loginType === 'account' && (
<>
<ProFormText
name="username"
fieldProps={{
size: 'large',
prefix: <UserOutlined className={'prefixIcon'} />,
}}
placeholder={'请输入账号/邮箱/电话号码'}
rules={[
{
required: true,
message: '请输入用户名!',
},
]}
/>
<ProFormText.Password
name="password"
fieldProps={{
size: 'large',
prefix: <LockOutlined className={'prefixIcon'} />,
}}
placeholder={'请输入密码'}
rules={[
{
required: true,
message: '请输入密码!',
},
]}
/>
</>
)}
{loginType === 'phone' && (
<>
<ProFormText
fieldProps={{
size: 'large',
prefix: <MobileOutlined className={'prefixIcon'} />,
}}
name="mobile"
placeholder={'手机号'}
rules={[
{
required: true,
message: '请输入手机号!',
},
{
pattern: /^1\d{10}$/,
message: '手机号格式错误!',
},
]}
/>
<ProFormCaptcha
fieldProps={{
size: 'large',
prefix: <LockOutlined className={'prefixIcon'} />,
}}
captchaProps={{
size: 'large',
}}
placeholder={'请输入验证码'}
captchaTextRender={(timing, count) => {
if (timing) {
return `${count} ${'获取验证码'}`;
}
return '获取验证码';
}}
name="captcha"
rules={[
{
required: true,
message: '请输入验证码!',
},
]}
onGetCaptcha={async () => {
message.success('获取验证码成功!验证码为:1234');
}}
/>
</>
)}
<div style={{ marginBlockEnd: 24 }}>
<ProFormCheckbox noStyle name="autoLogin">
自动登录
</ProFormCheckbox>
<a style={{ float: 'right' }}>忘记密码 </a>
</div>
</LoginForm>
</div>
);
};
2.登录处理
代码没有做优化,目前代码只是跑通。只为搞明白逻辑关系。
/* eslint-disable */
// src/services/demo/UserController.ts
import { request } from '@umijs/max';
/** 登录接口 POST /api/login/account */
export async function login(body: API.LoginParams, options?: { [key: string]: any }) {
return request<API.LoginResult>('/api/admin/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** 获取当前的用户 GET /api/currentUser */
export async function currentUser(options?: { [key: string]: any }) {
return request<{
data: API.CurrentUser;
}>('/api/admin/currentUser', {
method: 'GET',
...(options || {}),
});
}
3.修改app.tsx
import { currentUser as queryCurrentUser } from './services/demo/UserController';
import { AvatarName } from '@/components';
// 全局初始化数据配置,用于 Layout 用户信息和权限初始化
// 更多信息见文档:https://umijs.org/docs/api/runtime-config#getinitialstate
const loginPath = '/user/login';
export async function getInitialState(): Promise<{ name: string,currentUser?: API.CurrentUser; fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;}> {
const fetchUserInfo = async () => {
try {
const msg = await queryCurrentUser({
skipErrorHandler: true,
});
return msg.data;
} catch (error) {
history.push(loginPath);
}
return undefined;
};
const { location } = history;
if (![loginPath, '/user/register', '/user/register-result'].includes(location.pathname)) {
const currentUser = await fetchUserInfo();
return {
fetchUserInfo,
currentUser,
name: '@umijs/max',
};
}
return {
fetchUserInfo,
name: '@umijs/max',
};
}
二、说一下逻辑
1. 流程图
和AntDesign Pro的流程基本一样。
2. 注意点
- Login页面中的fetchUserInfo 调用了InitialState中的fetchUserInfo,并将获取的currentUser初始化,并flushSync了一下。
- 用户认证与用户信息获取是分开的。
- 在上一篇Umi从零搭建Ant Design Pro项目(1)中,我已经做了网络拦截,将获取的
token
进行了处理。就是说包括fetchUserInfo
在内的网络请求都会带上token
时,后端就会鉴权。
总结
今天就写这么多内容。
代码点击查看