本文是《React管理平台》第九节
通过本文我们将学会登录页的制作过程
布局
根据设计图制作登录页面,我们采用了Ant Design组件库进行布局,并加以CSS样式定制,使得布局对不同屏幕尺寸友好。
UI设计图
HTML布局核心代码
下面是Login.tsx
文件的HTML部分代码,使用了Ant Design的Form
, Input
, 和Button
组件:
<div className={styles.loginBackground}>
<div className={styles.login}>
<div className={styles.title}>用户登录</div>
<div className={styles.subTitle}>User Login</div>
<Form name='basic' className={styles.loginForm} initialValues={{ remember: true }} onFinish={onFinish}>
<Form.Item name='username' rules={[{ required: true, message: '请输入用户名!' }]}>
<Input placeholder='用户名' size='large' />
</Form.Item>
<Form.Item name='password' rules={[{ required: true, message: '请输入密码!' }]}>
<Input.Password placeholder='密码' autoComplete='new-password' size='large' />
</Form.Item>
<Form.Item>
<Button
className={styles.loginSubmit}
size='large'
type='primary'
block
htmlType='submit'
loading={loading}
>
登录
</Button>
</Form.Item>
</Form>
</div>
</div>
这里需要注意,我们在Input.Password
组件上使用了autoComplete='new-password'
即可禁用浏览器的账号密码填充
如果只给密码加上autoComplete='new-password'
,编辑器会出现警告
所以<Input placeholder='用户名' autoComplete='new-password' size='large' />
也需要加上autoComplete
CSS
CSS样式适配不同屏幕尺寸的响应式设计,代码如下:
.loginBackground{
height: 100vh;
background: url("@/assets/images/login/loginBackground.png") center no-repeat;
display: flex;
justify-content: center;
align-items: center;
}
.login{
position: absolute;
margin-left: 140px;
left: 50%;
top: 0;
bottom:0;
width: 450px;
display: flex;
flex-direction: column;
justify-content: center;
}
.title{
font-size: 34px;
color: #4e4e4e;
letter-spacing: 4px;
}
.subTitle{
font-size: 18px;
color: #4e4e4e;
}
.loginForm{
margin-top: 50px;
}
.loginSubmit{
margin-top: 10px;
}
:global(.ant-input-outlined){
background: #f6f9fe;
border:1px solid #f6f9fe;
height: 52px;
border-radius: 26px;
padding: 0 30px;
}
:global(.ant-input::placeholder){
color:#1a76e1;
}
:global(.ant-btn-primary){
background:#1a76e1;
height: 52px;
font-size: 24px;
border-radius: 26px;
}
/* 媒体查询样式 */
@media (max-width: 1536px) and (max-height:900px) {
.loginBackground {
background-size: 1536px 900px;
}
.login {
margin-left: 80px;
width: 400px;
}
}
@media (max-width: 1024px) {
.loginBackground {
background: #e7eeff;
}
.login {
margin-left: 0;
position: static;
width: 80vw;
}
.title {
font-size: 30px;
text-align: center;
}
.subTitle {
font-size: 16px;
text-align: center;
}
:global(.ant-input-outlined) {
background: #f8f9fe;
height: 52px;
border-radius: 26px;
padding: 0 30px;
}
:global(.ant-input::placeholder) {
color: #a7aaaf;
}
:global(.ant-btn-primary) {
background: #4785e8;
height: 52px;
font-size: 20px;
border-radius: 26px;
}
}
在样式部分,我们使用了媒体查询以适配不同尺寸的设备,使用:global
覆盖Ant Design的样式
在浏览器中的效果:
最终的登录页面在浏览器中的效果能够随屏幕大小变化而自适应,保持美观和功能性。以下是一个示例动画,展现了页面的响应式设计:
逻辑
loading状态
在我们的Login.tsx
文件中,通过useState
来控制loading
状态。它在开始时被设置为false
。
const [loading, setLoading] = useState(false)
每当onFinish
函数被调用时,我们将loading
设置为true
。
const onFinish = async () => {
setLoading(true)
}
在浏览器中的效果:
登录接口
我们在src/types/api/system
目录中新增login.ts
文件,该文件是登录接口的类型声明文件,内容为:
export interface LoginParams {
username: string
password: string
}
export interface LoginResult {
token: string
}
启动我们之前章节中创建的json-server
服务来模拟mock数据
登录请求接口位于src/api/system
目录中的login.ts
文件中。
// 登录参数与返回结果定义
export interface LoginParams { username: string; password: string; }
export interface LoginResult { token: string; }
// 登录请求与服务定义
export function postLogin(params: LoginParams) {
return request.post<LoginResult>('/system/login', params, { isShowLoading: false, isShowError: true })
}
上述代码中,由于登录页增加了loading
效果,所以全局的loading
效果我们设置为false
,即{ isShowLoading: false }
在onFinish
函数中,我们首先将loading
状态设置为true
,然后通过postLogin
函数向后台发送请求。如果请求成功,我们将loading
状态设置为false
并显示成功的提示信息,最后我们将路由重定向到/welcome
。一旦请求失败,我们只需要简单地把loading
状态重置为false
即可:
const onFinish = async (values: LoginParams) => {
try {
setLoading(true)
await postLogin(values)
setLoading(false)
message.success('登录成功')
router.navigate('/welcome', { replace: true }).then(() => {})
} catch (error) {
setLoading(false)
}
}
我们尝试输入错误的用户名和密码,看浏览器效果:
现在我们尝试输入正确的用户名和密码,看浏览器效果:
Token存储及更新
一旦用户成功登录,我们需要保存返回的token
。我们将其存储在localStorage
中并在zustand
的token状态中进行更新。
先导入之前章节中封装的localStorage:import storage from '@/utils/localStorage.ts'
在接口请求下边新增存储token:
const response = await postLogin(values)
storage.set('token', response.data.token) // <-这里
之后我们需要更新zustand
的token
先导入
import { useUserStore } from '@/store'
调用useUserStore
:
const updateToken = useUserStore(state => state.updateToken)
之后在接口请求下边调用:
const response = await postLogin(values)
storage.set('token', response.data.token)
updateToken(response.data.token) // <-这里
让我在浏览器中测试:
完整代码
原文链接:https://juejin.cn/post/7351301965030604834 作者:辰火流光