Vue3电商项目实战-登录模块2【05-登录-表单校验、06-登录-消息提示组件封装、07-登录-账户登录、08-登录-手机号登录、09-退出登录】


05-登录-表单校验

文档:https://vee-validate.logaretm.com/v4/ 支持vue3.0

第一步:安装

  • 执行命令 npm i vee-validate@4.0.3

第二步:导入

  • 修改文件 src/views/login/index.vue
import { Form, Field } from 'vee-validate'

第三步:定义校验规则

  • 新建文件 src/utils/vee-validate-schema.js
// 定义校验规则提供给vee-validate组件使用
export default {
  // 校验account
  account (value) {
    // value是将来使用该规则的表单元素的值
    // 1. 必填
    // 2. 6-20个字符,需要以字母开头
    // 如何反馈校验成功还是失败,返回true才是成功,其他情况失败,返回失败原因。
    if (!value) return '请输入用户名'
    if (!/^[a-zA-Z]\w{5,19}$/.test(value)) return '字母开头且6-20个字符'
    return true
  },
  password (value) {
    if (!value) return '请输入密码'
    if (!/^\w{6,24}$/.test(value)) return '密码是6-24个字符'
    return true
  },
  mobile (value) {
    if (!value) return '请输入手机号'
    if (!/^1[3-9]\d{9}$/.test(value)) return '手机号格式错误'
    return true
  },
  code (value) {
    if (!value) return '请输入验证码'
    if (!/^\d{6}$/.test(value)) return '验证码是6个数字'
    return true
  },
  isAgree (value) {
    if (!value) return '请勾选同意用户协议'
    return true
  }
}
  • 提取目的 这些校验规则将来在其他表单验证时候可复用

第三步:使用 Form 组件,使用 vee-validate-schema 校验规则

  • 修改文件 src/views/login/index.vue
-<div class="form">...</div>
+<Form class="form" :validation-schema="schema" autocomplete="off">...</Form>
import veeSchema from '@/utils/vee-validate-schema'
  setup () {
    // 控制短信登录切换的
    const isMsgLogin = ref(false)
    // 表单对象数据
    const form = reactive({
      isAgree: true,
      account: null,
      password: null,
      mobile: null,
      code: null
    })
    // 校验规则对象
    const mySchema = {
      account: schema.account,
      password: schema.password,
      mobile: schema.mobile,
      code: schema.code,
      isAgree: schema.isAgree
    }
    return { isMsgLogin, form, scheam:mySchema, submit }
 } 

第四步:使用 Field 组件,添加表单项目校验

  • 修改文件 src/views/login/index.vue
          <div class="form-item">
            <div class="input">
              <i class="iconfont icon-user"></i>
+              <Field :class="{error:errors.mobile}" v-model="form.mobile" name="mobile" type="text" placeholder="请输入手机号" />
            </div>
+            <div class="error" v-if="errors.mobile"><i class="iconfont icon-warning" />{{errors.mobile}}</div>
          </div>
          <div class="form-item" v-if="!isMsgLogin">
            <div class="input">
              <i class="iconfont icon-lock"></i>
+              <Field :class="{error:errors.password}" v-model="form.password" name="password" type="password" placeholder="请输入密码" />
            </div>
+            <div class="error" v-if="errors.password"><i class="iconfont icon-warning" />{{errors.password}}</div>
          </div>
          <div class="form-item" v-else>
            <div class="input">
              <i class="iconfont icon-code"></i>
+              <Field :class="{error:errors.code}" v-model="form.code" name="code" type="password" placeholder="请输入验证码" />
              <span class="code">发送验证码</span>
            </div>
+            <div class="error" v-if="errors.code"><i class="iconfont icon-warning" />{{errors.code}}</div>
          </div>
<Form class="form" :validation-schema="schema" v-slot="{errors}" 
  • 其实就是把input改成 Field 组件,默认解析成input
  • Field 添加name属性,作用是指定使用schema中哪个校验规则
  • Form 添加 v-slot="{errors}" 使用作用域插槽暴露 errors 错误对象
    • 通过 errors['校验规则名称'] 取出错误信息,有则显示,无即隐藏

第五步:如何校验 自定义组件 XtxCheckbox

  • 修改文件 src/views/login/index.vue
-<XtxCheckbox v-model="form.isAgree" />
+<Field as="XtxCheckbox" name="isAgree" v-model="form.isAgree"/>
  • Fieldas 属性可以指定为其他标签,也可指定为组件。
    • 但是组件需要支持 v-model 否则校验不会触发。

第六步:如何在切换 短信 与 账户 登录时候清空表单和校验结果

  • 修改文件 src/views/login/index.vue
<Form ref="formCom" class="form" :validation-schema="schema" v-slot="{errors}" 
    // 切换表单元素,还原数据和清除校验效果
    const formCom = ref(null)
    watch(isMsgLogin, () => {
      // 还原数据
      form.isAgree = true
      form.account = null
      form.password = null
      form.mobile = null
      form.code = null
      // 补充校验效果清除,Form组件提供resetForm()
      formCom.value.resetForm()
    })
  • 首先需要自己手动清除数据,然后使用Form 组件提供 resetForm 方法对表单进行清除校验结果

第七步:如何整体表单校验

  • 修改文件 src/views/login/index.vue
<a @click="login()" href="javascript:;" class="btn">登 录</a>
    // 需要在点击登录的时候对整体表单进行校验
    const login = async () => {
      // Form组件提供了一个 validate 函数作为整体表单校验,当是返回的是一个promise
      const valid = await formCom.value.validate()
      console.log(valid)
    }

    return { isMsgLogin, form, schema: mySchema, formCom, login }
  • Form 组件提供 validate 方法对表单进行整体校验

06-登录-消息提示组件封装

目的:在接口请求报错的时候给用户进行提示

组件功能分析:

  • 固定顶部显示,有三种类型:成功,错误,警告。
  • 显示消息提示时需要动画从上滑入且淡出。
  • 组件使用的方式不够便利,封装成工具函数方式。

大致实现步骤:

  • 先把布局,和三种情况的显示,完成。

    • 定义组件:src/components/library/xtx-message.vue
<template>
  <div class="xtx-message" :style="style[type]">
    <!-- 上面绑定的是样式 -->
    <!-- 不同提示图标会变 -->
    <i class="iconfont" :class="[style[type].icon]"></i>
    <span class="text">{{text}}</span>
  </div>
</template>
<script>
export default {
  name: 'XtxMessage',
  props: {
    text: {
      type: String,
      default: ''
    },
    type: {
      type: String,
      // warn 警告  error 错误  success 成功
      default: 'warn'
    }
  },
  setup () {
    // 定义一个对象,包含三种情况的样式,对象key就是类型字符串
    const style = {
      warn: {
        icon: 'icon-warning',
        color: '#E6A23C',
        backgroundColor: 'rgb(253, 246, 236)',
        borderColor: 'rgb(250, 236, 216)'
      },
      error: {
        icon: 'icon-shanchu',
        color: '#F56C6C',
        backgroundColor: 'rgb(254, 240, 240)',
        borderColor: 'rgb(253, 226, 226)'
      },
      success: {
        icon: 'icon-queren2',
        color: '#67C23A',
        backgroundColor: 'rgb(240, 249, 235)',
        borderColor: 'rgb(225, 243, 216)'
      }
    }
    return { style }
  }
}
</script>
<style scoped lang="less">
.xtx-message {
  width: 300px;
  height: 50px;
  position: fixed;
  z-index: 9999;
  left: 50%;
  margin-left: -150px;
  top: 25px;
  line-height: 50px;
  padding: 0 25px;
  border: 1px solid #e4e4e4;
  background: #f5f5f5;
  color: #999;
  border-radius: 4px;
  i {
    margin-right: 4px;
    vertical-align: middle;
  }
  .text {
    vertical-align: middle;
  }
}
</style>
  • 使用组件
<XtxMessage text="手机号或密码错误" type="error" />
  • 实现显示的时候动画效果
<template>
+  <Transition name="down">
+    <div class='xtx-message' :style="style" v-show="show">
      <!-- 上面绑定的是样式 -->
      <!-- 不同提示图标会变 -->
      <i class="iconfont" :class="[style[type].icon]"></i>
      <span class="text">{{text}}</span>
    </div>
+  </Transition>
</template>

<script>
+import { onMounted, ref } from 'vue'
export default {
  name: 'XtxMessage',
  props: {
    text: {
      type: String,
      default: ''
    },
    type: {
      type: String,
      // warn 警告  error 错误  success 成功
      default: 'warn'
    }
  },
  setup () {
    // 定义一个对象,包含三种情况的样式,对象key就是类型字符串
    const style = {
      warn: {
        icon: 'icon-warning',
        color: '#E6A23C',
        backgroundColor: 'rgb(253, 246, 236)',
        borderColor: 'rgb(250, 236, 216)'
      },
      error: {
        icon: 'icon-shanchu',
        color: '#F56C6C',
        backgroundColor: 'rgb(254, 240, 240)',
        borderColor: 'rgb(253, 226, 226)'
      },
      success: {
        icon: 'icon-queren2',
        color: '#67C23A',
        backgroundColor: 'rgb(240, 249, 235)',
        borderColor: 'rgb(225, 243, 216)'
      }
    }
+    // 定义一个数据控制显示隐藏,默认是隐藏,组件挂载完毕显示
+    const visible = ref(false)
+    onMounted(() => {
+      visible.value = true
+    })
+    return { style, visible }
  }
}
</script>

<style scoped lang='less'>
+.down {
+  &-enter {
+    &-from {
+      transform: translate3d(0,-75px,0);
+      opacity: 0;
+    }
+    &-active {
+      transition: all 0.5s;
+    }
+    &-to {
+      transform: none;
+      opacity: 1;
+    }
+  }
+}
// 。。。 省略
  • 封装成vue实例函数式调用
    • vue3.0使用app.config.globalProperties挂载原型方法
    • 也支持直接导入函数使用

src/components/library/Message.js

// 实现使用函数调用xtx-message组件的逻辑
import { createVNode, render } from 'vue'
import XtxMessage from './xtx-message.vue'

// 准备dom容器
const div = document.createElement('div')
div.setAttribute('class', 'xtx-message-container')
document.body.appendChild(div)
// 定时器标识
let timer = null

export default ({ type, text }) => {
  // 实现:根据xtx-message.vue渲染消息提示
  // 1. 导入组件
  // 2. 根据组件创建虚拟节点
  const vnode = createVNode(XtxMessage, { type, text })
  // 3. 准备一个DOM容器
  // 4. 把虚拟节点渲染DOM容器中
  render(vnode, div)
  // 5. 开启定时,移出DOM容器内容
  clearTimeout(timer)
  timer = setTimeout(() => {
    render(null, div)
  }, 3000)
}

src/components/library/index.js

import Message from './Message'
    // 定义指令
    defineDirective(app)

+    // 如果你想挂载全局的属性,能够通过组件实例调用的属性   this.$message
+    app.config.globalProperties.$message = Message// 原型函数
}
  • 在登录逻辑中使用 src/views/login/index.vue
import Message from '@/components/library/Message'
          // 帐号密码登录
          userAccountLogin(form).then(data => {
            // 成功
          }).catch(e => {
            // 失败
+            Message({ type: 'error', text: '登录失败' })
          })

07-登录-账户登录

目的:完成以账户进行登录

定义API src/api/user.js

import request from '@/utils/request'

/**
 * 帐号登录
 * @param {String} account - 用户名
 * @param {String} password - 密码
 * @returns Promise
 */
export const userAccountLogin = ({ account, password }) => {
  return request('/login', 'post', { account, password })
}

定义修改用户信息的 mutations src/store/module/user.js

// 用户状态
export default {
  namespaced: true,
  state: () => ({
    id: '',
+    account:'',
    nickname: '',
    avatar: '',
    token: '',
    mobile: ''
  })
}

进行登录 src/views/login/index.vue

import { userAccountLogin } from '@/api/user'
import Message from '@/components/library/Message'
import { useStore } from 'vuex'
import { useRoute, useRouter } from 'vue-router'
    // 使用store
    const store = useStore()
    // 使用router
    const router = useRouter()
    // 使用route
    const route = useRoute()
    // 登录提交
    const submit = async () => {
      // 整体校验
      const valid = await target.value.validate()
      console.log(valid)
      if (valid) {
        // 发送请求
        if (!isMsgLogin.value) {
          // 帐号密码登录
          userAccountLogin(form).then(data => {
            // 成功
            // 1. 存储信息
            const { id, account, nickname, avatar, token, mobile } = data.result
            store.commit('user/setUser', { id, account, nickname, avatar, token, mobile })
            // 2. 提示
            Message({ type: 'success', text: '登录成功' })
            // 3. 跳转
            router.push(route.query.redirectUrl || '/')
          }).catch(e => {
            // 失败
            Message({ type: 'error', text: e.response.data.message || '登录失败' })
          })
        } else {
          // 短信登录
        }
      }
    }

08-登录-手机号登录

src/api/user.js实现代码

封装好发短信和进行短信登录的接口API

/**
 * 短信登录
 * @param {String} mobile - 手机号
 * @param {String} code - 验证码
 * @returns Promise
 */
export const userMobileLogin = ({ mobile, code }) => {
  return request('/login/code', 'post', { mobile, code })
}

/**
 * 获取短信登录验证码
 * @param {String} mobile - 手机号
 * @returns Promise
 */
export const userMobileLoginMsg= (mobile) => {
  return request('/login/code', 'get', { mobile })
}

src/views/login/index.vue 实现代码发送短信

根据数据渲染按钮文字,绑定点击事件发送验证码:

            <span @click="send()" class="code">
              {{time===0?'发送验证码':`${time}秒后发送`}}
            </span>

使用 schma 函数来校验mobile,如果成功继续执行,不成功使用Form组件错误 setFieldError

发送验证码,需要校验手机号,和判断是否60秒内,方可发送。组件销毁时候清除定时器。

import { useIntervalFn } from '@vueuse/core'
    // pause 暂停 resume 开始
    // useIntervalFn(回调函数,执行间隔,是否立即开启)
    const time = ref(0)
    const { pause, resume } = useIntervalFn(() => {
      time.value--
      if (time.value <= 0) {
        pause()
      }
    }, 1000, false)
    onUnmounted(() => {
      pause()
    })

    // 发送短信
    const send = async () => {
      const valid = mySchema.mobile(form.mobile)
      if (valid === true) {
        // 通过
        if (time.value === 0) {
        // 没有倒计时才可以发送
          await userMobileLoginMsg(form.mobile)
          Message({ type: 'success', text: '发送成功' })
          time.value = 60
          resume()
        }
      } else {
        // 失败,使用vee的错误函数显示错误信息 setFieldError(字段,错误信息)
        formCom.value.setFieldError('mobile', valid)
      }
    }
        return { isMsgLogin, form, schema: mySchema, formCom, login, send, time }

src/views/login/index.vue 实现代码手机号登录

    // 使用store
    const store = useStore()
    // 使用router
    const router = useRouter()
    // 使用route
    const route = useRoute()
    // 登录提交
    const submit = async () => {
      // 整体校验
      const valid = await target.value.validate()
      if (valid) {
        // 发送请求
        let data = null
        try {
          if (!isMsgLogin.value) {
            // 帐号登录
            data = await userAccountLogin(form)
          } else {
            // 短信登录
            // 1. 定义两个API  短信登录,获取短信验证码
            // 2. 实现发送短信验证码发送功能
            // 3. 完成手机号短信验证码登录逻辑
            data = await userMobileLogin(form)
          }
        } catch (e) {
          Message({ type: 'error', text: e.response.data.message || '登录失败' })
        }
        // 成功
        // 1. 存储信息
        const { id, account, nickname, avatar, token, mobile } = data.result
        store.commit('user/setUser', { id, account, nickname, avatar, token, mobile })
        // 2. 提示
        Message({ type: 'success', text: '登录成功' })
        // 3. 跳转
        router.push(route.query.redirectUrl || '/')
      }
    }

09-退出登录

目的:完成退出

src/components/app-topnav.vue

        <template v-if="profile.token">
          <li>
            <a href="javascript:;"><i class="iconfont icon-user"></i>
              {{profile.account}}
            </a>
          </li>
          <li><a @click="logout()" href="javascript:;">退出登录</a></li>
        </template>
import { computed } from 'vue'
import { useStore } from 'vuex'
import { useRouter } from 'vue-router'
export default {
  name: 'AppTopnav',
  setup () {
    // 获取用户的登录信息才能控制切换导航菜单
    const store = useStore()
    // 使用vuex中的state需要设置计算属性,否则不是响应式
    const profile = computed(() => {
      return store.state.user.profile
    })
+    const router = userRouter()
+    const logout = () => {
+      store.commit('user/setUser',{})
+      router.push('/login')
+    }
+    return { profile, logout}
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值