快速集成 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 中有一个简单实现,如果你要需要一个简单快速的实现一个登录页面,值得尝试。

相关推荐
星栈几秒前
oh-my-pi工程级AI编码工使用体验
人工智能·后端·agent
AI大模型-小华34 分钟前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
立心者02 小时前
SpringBoot中使用TOTP实现MFA(多因素认证)
java·spring boot·后端
AskHarries2 小时前
SEO 页面怎么生成
后端
做前端的娜娜子3 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
爱吃牛肉的大老虎3 小时前
Rust对象之结构体,枚举,特性
开发语言·后端·rust
石榴3 小时前
NestJS 的请求到底经过了什么:装饰器、守卫、拦截器、管道与中间件如何配合
后端
小帅不太帅3 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
Gopher_HBo3 小时前
moby-client客户端
后端