next.js搭建博客系统-03-登录组件(登录弹窗)

751 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情

验证码弹窗

首先在components创建Login文件夹,在Login文件夹创建index.tsx文件和index.modules.scss

cd components
mkdir Login
cd Login
touch index.tsx
touch index.module.scss

在Navbar组件中的 登录按钮 添加点击事件

<Button type="primary" onClick={handleLogin}>
            登录
</Button>

定义一个state来控制 登录弹窗 是否显示

将isShowLogin 当做 props 传入 登录组件

const [isShowLogin, setIsShowLogin] = useState(false);

当点击登录按钮时,显示 登录弹窗

const handleLogin = () => {
    setIsShowLogin(true);
};

接下来 开始编写 登录弹窗

最终效果如下图:

代码如下:

return isShow ? (
    <div className={styles.loginArea}>
      <div className={styles.loginBox}>
        <div className={styles.loginTitle}>
          <div>手机号登录</div>
          <div className={styles.close} onClick={handleClose}>
            x
          </div>
        </div>
        <input
          name="phone"
          type="text"
          placeholder="请输入手机号"
          value={form.phone}
          onChange={handleFormChange}
        />
        <div className={styles.verifyCodeArea}>
          <input
            name="verify"
            type="text"
            placeholder="请输入验证码"
            value={form.verify}
            onChange={handleFormChange}
          />
          <span className={styles.verifyCode} onClick={handleGetVerifyCode}>
            {isShowVerifyCode ? (
              <CountDown time={10} onEnd={handleCountDownEnd} />
            ) : (
              '获取验证码'
            )}
          </span>
        </div>
        <div className={styles.loginBtn} onClick={handleLogin}>
          登录
        </div>
        <div className={styles.otherLogin} onClick={handleOAuthGithub}>
          使用 Github 登录
        </div>
        <div className={styles.loginPrivacy}>
          注册登录即表示同意
          <a
            href="https://moco.imooc.com/privacy.html"
            target="_blank"
            rel="noreferrer"
          >
            隐私政策
          </a>
        </div>
      </div>
    </div>
  ) : null;

css代码如下:

.loginArea {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 1000;
    width: 100vw;
    height: 100vh;
    background-color: rgb(0 0 0 / 30%);
  
    .loginBox {
      width: 320px;
      height: 320px;
      background-color: #fff;
      position: relative;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      padding: 20px;
  
      input {
        width: 100%;
        height: 37px;
        margin-bottom: 10px;
        padding: 10px;
        border-radius: 5px;
        border: 1px solid #888;
        outline: none;
      }
  
      input:focus {
        border: 1px solid #1e80ff;
      }
  
      .verifyCodeArea {
        position: relative;
        cursor: pointer;
  
        .verifyCode {
          color: #1e80ff;
          position: absolute;
          right: 20px;
          top: 8px;
          font-size: 14px;
        }
      }
    }
  
    .loginTitle {
      font-size: 20px;
      font-weight: bold;
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 20px;
  
      .close {
        color: #888;
        cursor: pointer;
      }
    }
  
    .loginBtn {
      height: 40px;
      line-height: 40px;
      border-radius: 5px;
      margin-top: 15px;
      background-color: #007fff;
      color: #fff;
      text-align: center;
      cursor: pointer;
    }
  
    .otherLogin {
      margin-top: 15px;
      font-size: 14px;
      color: #1e80ff;
      cursor: pointer;
    }
  
    .loginPrivacy {
      margin-top: 10px;
      color: #333;
      font-size: 14px;
  
      a {
        color: #1e80ff;
      }
    }
  }
  

接下来 编写 点击逻辑

首先 当点击关闭的时候,把弹窗关闭

使用 props 中的 onClose 方法,onClose方法在父组件 Navbar 通过isShowLogin控制隐藏

// Login/index.tsx
const { onClose } = props;
const handleClose = () => {
    onClose && onClose();
};
<Login isShow={isShowLogin} onClose={handleClose} />


  const handleClose = () => {
    setIsShowLogin(false);
  };

接下来开始编写 获取验证码的 逻辑

获取验证码 需要提前编写一个倒计时的组件

接下来开始编写 倒计时组件

cd components
mkdir CountDown
cd CountDown
touch index.tsx
touch index.module.scss

在index.tsx中编写如下代码:

思路是 提供一个 time,表示倒计时的时间。提供一个onEnd回调函数,表示当倒计时结束的时候,进行一些回调处理。

这里需要注意下, 当 time时间为0的时候,需要主动 调 一些 onEnd,表示结束。

import { useState, useEffect } from 'react';
import styles from './index.module.scss';

interface IProps {
  time: number;
  onEnd: Function;
}

const CountDown = (props: IProps) => {
  const { time, onEnd } = props;
  const [count, setCount] = useState(time || 60);

  useEffect(() => {
    const id = setInterval(() => {
      setCount((count) => {
        if (count === 0) {
          clearInterval(id);
          onEnd && onEnd();
          return count;
        }
        return count - 1;
      });
    }, 1000);
    return () => {
      clearInterval(id);
    };
  }, [time, onEnd]);

  return <div className={styles.countDown}>{count}</div>;
};

export default CountDown;

首先 通过 isShowVerifyCode 控制 显示 验证码文字 还是倒计时

<span className={styles.verifyCode} onClick={handleGetVerifyCode}>
            {isShowVerifyCode ? (
              <CountDown time={10} onEnd={handleCountDownEnd} />
            ) : (
              '获取验证码'
            )}
          </span>

接着当点击 获取验证码的时候,校验一下 手机号是否输入, 如果手机号没有输入,提示用户输入手机号

<span className={styles.verifyCode} onClick={handleGetVerifyCode}>获取验证码</span>



const handleGetVerifyCode = () => {
  if (!form?.phone) {
      message.warning('请输入手机号');
      return;
  }
}

如果 手机号输入,则开始 调 获取验证码的接口

const handleGetVerifyCode = () => {
    if (!form?.phone) {
      message.warning('请输入手机号');
      return;
    }

    request
      .post('/api/user/sendVerifyCode', {
        to: form?.phone,
        templateId: 1,
      })
      .then((res: any) => {
        if (res?.code === 0) {
          setIsShowVerifyCode(true);
        } else {
          message.error(res?.msg || '未知错误');
        }
      });
  };