From scratch, build a simple shopping platform (18) Front-end mall part

From scratch, build a simple shopping platform (17) Front-end mall part:
https://blog.csdn.net/time_____/article/details/108893925
Project source code (continuous update): https://gitee.com/ DieHunter/myCode/tree/master/shopping

The last article gave a brief introduction to the shopping cart. All the basic functions of the mall have been implemented. This article will introduce the implementation of user information and order-related functions. User information login has been implemented in the back-end management. We need Implement a registration function and email verification function. For specific implementation, please refer to a previous blog or this article

The implementation effects are as follows: login with username and password, login with email verification, and registration

Login section

The account password login is the same as the login of the background management system. If the user enters the user name and password to log in successfully, part of the user information will be encrypted into a token and sent to the front end and saved locally. Each time the token value is sent to the back end to continue the operation

E-mail verification

Create a new EmailTransporter static variable in the server config file to configure the nodemailer module used to send the mailbox

EmailTransporter: {
    // service: "qq", // 运营商  qq邮箱 网易  若使用QQ邮箱,则只需配置service:qq
    host: "smtp.163.com",// 若使用网易邮箱,则只需配置host:smtp.163.com
    port: 465, //端口
    auth: {
      user: "132*****[email protected]", //发送方的邮箱
      pass: "WAQM******WQEFKB", // pop3 授权码
    },
  },

Then create a new email verification code to configure EmailConfig

  EmailConfig: {
    time: 5,//验证码有效期,单位分钟
    codeLength: 4,//验证码长度
    sendTime: 1 * 60 * 1000,//后端验证码允许再次发送时间间隔,单位毫秒
    targetTime: 5 * 60 * 1000,//验证码有效期,单位毫秒
    title: "零食商贩",//验证码标题
  },

Then write a function to generate a random verification code in Utils

  /* 生成随机
   * @method    codeLength   函数名
   * @for    Utils     附属于哪个类
   * @param {number/string} len  随机数长度
   * @return {string}  _count   生成len个随机数
   */
  static codeLength(len) {
    let _count = "";
    for (let i = 0; i < len; i++) {
      _count += Math.floor(Math.random() * 10); //生成len个随机数
    }
    return _count;
  }

Generate a time stamp function in Utils, record the verification code and the verification code sending time and effective time

 /* 生成时间戳
   * @method    randomCode
   * @for    Utils
   * @param
   * @return {object}  _count   生成len个随机数
   */
  static randomCode() {
    return {
      code: this.codeLength(EmailConfig.codeLength), //生成的随机数
      sendTime: new Date().getTime() + EmailConfig.sendTime, //发送时间
      targetTime: new Date().getTime() + EmailConfig.targetTime, //截止时间
    };
  }

Create a new SendMail.js under the Utils folder and create a new send mail module, call it in utils

const nodemailer = require("nodemailer");
const Config = require("../config/config");
module.exports = class SendMail {
  static transporter = nodemailer.createTransport(Config.EmailTransporter); //邮箱配置项
  static mailOptions = null; //邮箱配置
  /* 发送邮件模块
   * @method    sendEmail
   * @for       SendMail
   * @param   {String} mail  用户邮箱号
   * @param   {String} title  邮件标题
   * @param   {String} content  邮件内容
   * @return {Boolean}   是否发送成功
   */
  static async sendEmail(mail, title, content) {
    this.mailOptions = {
      from: '"邮箱验证" <' + Config.EmailTransporter.auth.user + ">",
      to: mail,
      subject: title,
      text: content,
    };
    try {
      let result = await this.transporter.sendMail(this.mailOptions);
      console.log("发送成功");
      return true;
    } catch (error) {
      console.log(error);
      console.log("发送失败");
      return false;
    }
  }
};

Create a new function to generate mail content in utils

/* 生成邮件内容
   * @method    sendEmailCode
   * @for    Utils
   * @param   {String} code  验证码内容
   * @param   {String} email   用户邮箱
   */
  static async sendEmailCode(code, email) {
    return await SendMail.sendEmail(
      email,
      EmailConfig.title,
      `您的验证码为:${code},${EmailConfig.time}分钟内有效`
    );
  }

Finally, write a function to send mail asynchronously in utils

/* 异步发送邮箱验证
   * @method    createEmailCode
   * @for    Utils
   * @param   {Object} codeList  邮箱验证码列表
   * @param   {String} email   用户邮箱
   * @param   {Object} findRes  数据库搜寻到的用户信息
   * @return {Boolean}  isSuccess   是否发送成功
   */
  static async createEmailCode(codeList, email, findRes) {
    if (!codeList[email] || new Date().getTime() > codeList[email].sendTime) {
      //已过1分钟,防止多次请求邮箱
      codeList[email] = this.randomCode();
      codeList[email].info = findRes;
      return await this.sendEmailCode(codeList[email].code, email);
    } else {
      //未过1分钟
      return false;
    }
  }

A complete module for sending emails is completed. The next step is to verify the verification code.

 /* 核对验证码
   * @method    checkEmailCode
   * @for    Utils
   * @param   {Object} codeList  用户验证码列表
   * @param   {String} key   用户邮箱
   * @param   {Object} _data   用户提交的表单信息
   * @return   {Object} res   请求响应返回值
   */
  static checkEmailCode(codeList, key, _data) {
    if (!codeList[key]) {
      //未发送验证码
      return {
        result: 0,
        msg: "验证码错误",
      };
    } else if (
      new Date().getTime() < codeList[key].targetTime &&
      _data.mailcode == codeList[key].code
    ) {
      //验证码校对成功
      let _obj = {
        result: 1,
        token: Utils.createToken(
          codeList[key].info.userType || "",
          codeList[key].info.username || "",
          _data.remember || ""
        ),
        msg: "操作成功",
      };
      codeList[key] = null;
      return _obj;
    } else if (new Date().getTime() > codeList[key].targetTime) {
      //验证码超时
      return {
        result: 0,
        msg: "验证码超时",
      };
    } else {
      return {
        result: 0,
        msg: "验证失败",
      };
    }
  }

At this step, all the preparations for the verification code have been implemented. The next step will be to implement the registration and login function. There are two ways to log in. The email address is required when registering, so you can log in by email verification.

The server side obtains the verification code interface, through a codeType to distinguish the user login to obtain the verification code and registration to obtain the verification code

router.get(Config.ServerApi.getMailCode, async (_req, res) => {
  //用户邮箱验证
  let _data = Util.getCrypto(Util.parseUrl(_req, res).crypto);//解密参数
  //查询用户是否存在,若未找到用户,则返回错误响应值,否则异步发送邮件验证码
  let findRes = await findData(Mod, {
    mailaddress: _data.username.split('@')[0],
    mailurl: '@' + _data.username.split('@')[1],
  });
  if ((!findRes.length || !findRes) && _data.codeType !== 'reg') {//过滤区分用户注册登录
    res.send({
      result: 0,
      msg: "用户未注册"
    });
    return;
  }
  await Util.createEmailCode(userCodeList, _data.username, findRes[0] || {}) ? res.send({
    result: 1,
    msg: "发送成功",
  }) : res.send({
    result: 0,
    msg: "发送失败"
  });
});

Before implementing the registration part, we have to write a tool method for the verification code countdown, during which the user cannot click to send the request again

import Vue from "vue";
import Config from "../config/config";
const { GetCodeTime, CodeText } = Config;
class TimeTick {
  static timer = GetCodeTime / 1000;//倒计时时间
  static _timeTick = null;//定时器
  static timeTick(fn) {
    if (!TimeTick._timeTick) {
      TimeTick._timeTick = setInterval(() => {
        if (TimeTick.timer-- <= 1) {
          // 重置倒计时和发送邮箱验证开关
          TimeTick.clearTick();
          fn({ content: CodeText, res: 1 });//倒计时归零
        } else {
          fn({ content: TimeTick.timer + "S", res: 0 });//倒计时中,阻止用户重复点击
        }
      }, 1000);
    }
  }
  static clearTick() {
    //清除定时器
    clearInterval(TimeTick._timeTick);
    TimeTick._timeTick = null;
  }
}
Vue.prototype.$timeTick = TimeTick;

 User registration interface

<template>
  <div class="login">
    <div>
      <mt-field
        placeholder="请输入用户名"
        :state="userInfo.username.length ? 'success' : 'error'"
        v-model="userInfo.username"
      ></mt-field>
      <mt-field
        placeholder="请输入密码"
        :state="userInfo.password.length ? 'success' : 'error'"
        v-model="userInfo.password"
        type="password"
      ></mt-field>
      <mt-field
        placeholder="请重复输入密码"
        :state="
          userInfo.repassword.length && userInfo.password == userInfo.repassword
            ? 'success'
            : 'error'
        "
        v-model="userInfo.repassword"
        type="password"
      ></mt-field>
      <mt-field
        placeholder="请输入邮箱"
        v-model="userInfo.mailaddress"
        :state="userInfo.mailaddress.length ? 'success' : 'error'"
      >
        <mt-button class="btn" @click="selectMail">{
   
   {
          userInfo.mailurl
        }}</mt-button>
      </mt-field>
      <mt-field
        placeholder="请输入验证码"
        :state="userInfo.mailcode.length == 4 ? 'success' : 'error'"
        v-model="userInfo.mailcode"
        type="number"
      >
        <mt-button class="btn" :disabled="canGetCode" @click="getCode">{
   
   {
          codeTime
        }}</mt-button>
      </mt-field>
      <mt-button class="btn" type="primary" @click="submit">注册</mt-button>
      <div class="shopPicker"></div>
      <ShopPicker :ShopMaxCount="address" pickerTitle="邮箱类型"></ShopPicker>
    </div>
  </div>
</template>

<script>
import Config from "../../config/config";
import Mail from "../../config/mail";
import ShopPicker from "../shopPicker/shopPicker";
import { Field, Button, Picker, Popup } from "mint-ui";
import RegBussiness from "./bussiness";
const { GetCodeTime, EventName, CodeText } = Config;
const { address } = Mail;
export default {
  components: {
    ShopPicker,
  },
  data() {
    return {
      codeTime: CodeText, //获取验证码按钮显示值
      address, //邮箱默认地址
      canGetCode: false, //防止重复点击开关
      userInfo: {
        //注册表单默认数据
        username: "",
        password: "",
        repassword: "",
        mailurl: address[0],
        mailaddress: "",
        mailcode: "",
      },
    };
  },
  created() {
    this.regBussiness = new RegBussiness(this);
    this.$events.onEvent(EventName.ChangeCount, (_count) => {
      this.userInfo.mailurl = _count; //切换邮箱地址
    });
  },
  destroyed() {
    this.$events.offEvent(EventName.ChangeCount);
  },
  methods: {
    selectMail() {
      this.$events.emitEvent(EventName.ShowPicker);
    },
    getCode() {
      if (this.canGetCode) {
        //是否允许发送邮箱验证
        return;
      }
      this.regBussiness.sendCode().then((res) => {
        this.canGetCode = true;//关闭点击开关
        this.$timeTick.timeTick((state) => {
          this.codeTime = state.content;
          switch (state.res) {
            case 0:
              this.canGetCode = false;//允许用户点击
              break;
          }
        });
      });
    },
    submit() {
      this.regBussiness.submitData();
    },
  },
};
</script>

<style lang="less" scoped>
@import "../../style/init.less";

.login {
  .btn {
    .f_s(34);
    width: 100%;
    .h(100);
  }
}
</style>

Register the business logic part, bussiness.js

import Vue from 'vue'
import {
  Toast
} from "mint-ui";
import config from "../../config/config"
const {
  ServerApi,
  StorageName,
  EventName
} = config
export default class LoginBussiness extends Vue {
  constructor(_vueComponent) {
    super()
    this.vueComponent = _vueComponent
  }
  sendCode() {
    return new Promise((resolve, reject) => {
      if (!this.vueComponent.userInfo.mailaddress.length) {//过滤邮箱长度为0
        Toast('请填写正确的邮箱');
        return
      }
      this.$axios
        .get(ServerApi.user.getMailCode, {
          params: {
            crypto: this.$crypto.setCrypto({
              codeType: "reg",//区分注册登录类型
              username: this.vueComponent.userInfo.mailaddress + this.vueComponent.userInfo.mailurl
            })
          },
        }).then(res => {
          switch (res.result) {
            case 1:
              Toast(res.msg);
              resolve(res)
              break;
            default:
              reject(res)
              break;
          }
        }).catch(err => {
          reject(err)
        })
    })

  }
  submitData() {
    for (const key in this.vueComponent.userInfo) {
      if (this.vueComponent.userInfo.hasOwnProperty(key) && !this.vueComponent.userInfo[key].length) {//过滤表单项长度为0
        Toast('请填写完整的信息');
        return
      }
    }
    this.$axios
      .post(ServerApi.user.userReg, {
        crypto: this.$crypto.setCrypto({
          ...this.vueComponent.userInfo
        })
      }).then(res => {
        //成功后重置用户信息
        this.vueComponent.userInfo.password = "";
        this.vueComponent.userInfo.repassword = "";
        this.vueComponent.userInfo.mailcode = "";
        switch (res.result) {
          case 1:
            Toast(res.msg);
            history.go(-1)//返回登录页面
            break;
          default:
            break;
        }
      })
  }
}

The registration part is completed, the login function is similar to the registration function, here is only the generation of the server-side token

Modify the interface in the server user.js, like the management system login, add email authentication login, distinguish administrator and user login

router.post(Config.ServerApi.userLogin, async (req, res) => {
  let _data = Util.getCrypto(Util.parseUrl(req, res).crypto); //解密前端入参
  switch (_data.loginType) {
    case "code"://验证码登录,验证邮箱验证码
      res.send(Util.checkEmailCode(userCodeList, _data.username, _data));
      break;
    case "psd"://密码登录
    default:
      let findRes = await findData(Mod, {
        $or: [
          {
            mailaddress: _data.username.split("@")[0],
            mailurl: "@" + _data.username.split("@")[1],
          },
          {
            username: _data.username,
          },
          {
            phoneNum: _data.username,
          },
        ],
      });
      if (findRes && findRes.length > 0) {
        Util.checkBcrypt(_data.password, findRes[0].password)
          ? res.send({
              result: 1,
              token: Util.createToken(//生成前端token
                findRes[0].userType,
                findRes[0].username,
                _data.remember
              ),
              msg: "登录成功",
            })
          : res.send({
              result: 0,
              msg: "密码错误",
            });
        return;
      }
      res.send({
        result: 0,
        msg: "用户不存在",
      });
      break;
  }
});

Summary
This article basically implements the user’s registration and login email verification function. The main functions refer to the previous email verification and login to the registered blog . The focus of the article is the email verification function module, which is used in conjunction with registration and login. When registering, new users will be added and login. Then update the token value. The next article will introduce the user information modification, and the subsequent order generation and viewing functions will be realized

Guess you like

Origin blog.csdn.net/time_____/article/details/108918489