快速集成 antd pro-components 的登录能力

一、动机?

有时候我们想快速的拥有一个登录页面的需求,而不是自己重新写一个新的登录页面。

如果是一个管理系统,对 UI 的要求不高,需要快速的集成登录相关的内容。自己写一个登录页可能也花费不了多长时间。但是今天分享的是 pro-components 的登录页给出的方案,快速的制作登录的逻辑。

二、登录常见的需求

登录类型

  • 密码登录
  • 手机登录
  • 扫码登录
  • ...

@ant-design/icons 集成了两种方案,一种是登录表单 LoginForm 和登录页面 LoginFormPage 两种不同的组件。提供的登录 UI 有:

  • 账号密码登录
  • 手机号登录

其实简单的情况我们不需要手机号登录。这两种方式实现的方式是 Tabs/Tabs.TabPane 切换:

ts 复制代码
<Tabs
    centered
    activeKey={loginType}
    onChange={(activeKey) => setLoginType(activeKey as LoginType)}
>
    <Tabs.TabPane key={'account'} tab={'账号密码登录'} />
    <Tabs.TabPane key={'phone'} tab={'手机号登录'} />
</Tabs>

这里的 Tabs 分为两个,一个密码登录的,一个是手机登录的。当然你也可以根据自己的需求,自己自定义,实现二维码登录。

登录表单

登录表单中常用中的常用内容:

  • 用户名(或邮箱)
  • 密码(hash with salt 加密方法或者根据自己需求使用加密算法)
  • 验证码 (短信验证码,图片验证码等方式)
  • 二维码 (二维码登录)

关于加密的类库:

  • WebCryptoAPI 是 web api 使用时,更具兼容性考虑
  • CryptoJS 提供了各种加密算法
  • bcrypt.js

表单验证

antd 表单中对前端表单提供了验证的机制:

  • 必须项目
  • 错误提示
  • 校验规则:使用 rules 属性

当然 antd 中支持自定义校验规则。

自动登录与忘记密码

  • checkbox 标记记住密码
  • a 标签,跳转忘记密码页面

三、集成 pro-components

在 React 项目中集成 pro-components 非常简单,如果还需用到图标相关的内容,需要 @ant-design/iconsantd

sh 复制代码
@ant-design/pro-components @ant-design/icons antd

组件

ts 复制代码
import {
  LoginForm,
  ProConfigProvider,
  ProFormCaptcha,
  ProFormCheckbox,
  ProFormText,
  setAlpha,
} from '@ant-design/pro-components';

LoginForm

这里 LoginForm 就是登录组件的框架,登录的标题以及一些非表单的额外信息,可以在这里处理:

  • logo 地址位置
  • title 主标题
  • subTitle 副标题
  • actions 额外的登录信息
  • message 顶部配置信息

ProFormCaptcha

tsx 复制代码
<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');
    }}
    />

由验证码按钮和验证码输入框组成。同时 captchaTextRender 根据倒计时开情况进行或者, onGetCaptcha 可以发送请求到后端获取验证码。

四、LoginFormPage

在 Login 基础上实现了左右布局,增加一个广告位置,同时在组件也添加了一些额外的属性:

  • backgroundImageUrl 整个区域的背景图片
  • activityConfig 活动配置* *

五、Remix 中实践

创建应用并安装依赖

sh 复制代码
npx create-remix@latest app
cd app
pnpm add @ant-design/icons @ant-design/pro-components antd remix-utils crypto-js
pnpm add @types/crypto-js -D

添加 ClientOnly 到 _Admin.tsx 中:

tsx 复制代码
import { Outlet } from "@remix-run/react";
import { ClientOnly } from "remix-utils/client-only";

export default function Admin() {
  return <ClientOnly>{() => <Outlet />}</ClientOnly>;
}

ClientOnly 组件原因是组件有些是客户端渲染的内容,不需要服务端渲染,或者有组件,不支持服务端渲染。

工具函数处理 password (示例)

ts 复制代码
import * as cj from 'crypto-js'

export function hashedPassword(password: string) {
  const salted = /* your hash*/;
  return cj.SHA256(password + salted).toString()
} 

这里使用 crypto-js 提供的 SHA256 方法, 在密码与加盐的拼接之后,进行加密,不会直接将数据传递给后端。

登录实现

tsx 复制代码
import type { ActionFunction } from '@remix-run/node'

import { useEffect } from "react";
import { json } from "@remix-run/node";
import { useActionData, useNavigate, useSubmit } from "@remix-run/react";

import { message, theme } from "antd";
import * as _icons from "@ant-design/icons";
import { LoginForm, ProFormCheckbox, ProFormText } from "@ant-design/pro-components";


// utils
import { hashedPassword } from "~/utils/hash";


const { UserOutlined, LockOutlined } = _icons;

export const action: ActionFunction = async ({ request }) => {
  const formData = await request.formData()

  return json({
    name: formData.get('name'),
    password: formData.get("password")
  })
}

export default function AdminLogin() {
  const { token } = theme.useToken();
  const actionData = useActionData<typeof action>()
  const submit = useSubmit()
  const navigate = useNavigate()

  const onFinish = async (values: any) => {
    values.password  = hashedPassword(values.password)

    submit(values, { method: 'POST'})
  }

  useEffect(() => {
    if(actionData) {
      message.info('action sucess')
      navigate('/')
    }
  }, [actionData, navigate])
  return (
    <div>
      <LoginForm logo="/favicon.ico" title="快速开始" subTitle="副标题" onFinish={onFinish}>
        <ProFormText
          name="name"
          fieldProps={{
            size: "large",
            prefix: <UserOutlined className={"prefixIcon"} />,
          }}
          placeholder={"用户名: admin or user"}
          rules={[
            {
              required: true,
              message: "请输入用户名!",
            },
          ]}
        />
        <ProFormText.Password
          name="password"
          fieldProps={{
            size: "large",
            prefix: <LockOutlined className={"prefixIcon"} />,
            strengthText:
              "Password should contain numbers, letters and special characters, at least 8 characters long.",

            statusRender: (value) => {
              const getStatus = () => {
                if (value && value.length > 12) {
                  return "ok";
                }
                if (value && value.length > 6) {
                  return "pass";
                }
                return "poor";
              };
              const status = getStatus();
              if (status === "pass") {
                return (
                  <div style={{ color: token.colorWarning }}>强度:中</div>
                );
              }
              if (status === "ok") {
                return (
                  <div style={{ color: token.colorSuccess }}>强度:强</div>
                );
              }
              return <div style={{ color: token.colorError }}>强度:弱</div>;
            },
          }}
          placeholder={"密码: ant.design"}
          rules={[
            {
              required: true,
              message: "请输入密码!",
            },
          ]}
        />
        <div
            style={{
              marginBlockEnd: 24,
            }}
          >
            <ProFormCheckbox noStyle name="autoLogin">
              自动登录
            </ProFormCheckbox>
            <a
              style={{
                float: 'right',
              }}
              href=""
            >
              忘记密码
            </a>
          </div>
      </LoginForm>
    </div>
  );
}

在 onFinish 中使用 submit 函数进行提交 post 请求,在 action 函数中直接接受 post 方法,并且获取 formData 中的数据,直接返回给前端,这里并没有区分请求方法,直接获取数据。前端通过 useActionData 获取 action 中返回值,然后 message 弹出,并路由跳转到主页的整个流程。

六、小结

本文主要讲解登录中的基本需求,以及 antd pro-component 中的 LoginForm 和 LoginFormPage 方便快捷的接入 React 相关的框架中。其中包含登录前端基本组件的使用以及密码的安全管理。本文篇前端部分,为了方便在 Remix 中有一个简单实现,如果你要需要一个简单快速的实现一个登录页面,值得尝试。

相关推荐
黄尚圈圈17 分钟前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水1 小时前
简洁之道 - React Hook Form
前端
2401_857622663 小时前
SpringBoot框架下校园资料库的构建与优化
spring boot·后端·php
正小安3 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
2402_857589363 小时前
“衣依”服装销售平台:Spring Boot框架的设计与实现
java·spring boot·后端
哎呦没4 小时前
大学生就业招聘:Spring Boot系统的架构分析
java·spring boot·后端
_.Switch5 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光5 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   5 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   5 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d