[TOC]

image.png

01-登录-路由与组件

目的:完成登录页的路由与组件配置
组件:src/views/login/index.vue

<template>
  <div class="page-login">
    Login
  </div>
</template>
<script>
export default {
  name: 'PageLogin'
}
</script>
<style scoped lang="less"></style>

路由:src/router/index.js 一级路由规则

+const Login = () => import('@/views/login/index')
...
const routes = [
    ...
+  { path: '/login', component: Login }
]

链接:src/components/app-topnav.vue

+<li><RouterLink to="/login">请先登录</RouterLink></li>

02-登录-基础布局

目的:完成登录页基础结构布局。
结构分析:

  • 登录头部 LoginHeader 需要留出副标题插槽
  • 登录主体 Login
  • 登录底部 LoginFooter

具体代码:

  • 头部组件 src/views/login/components/login-header.vue ```vue

- 底部组件 src/views/login/components/login-footer.vue
```vue
<template>
  <footer class="login-footer">
    <div class='container'>
      <p>
        <a href="javascript:;">关于我们</a>
        <a href="javascript:;">帮助中心</a>
        <a href="javascript:;">售后服务</a>
        <a href="javascript:;">配送与验收</a>
        <a href="javascript:;">商务合作</a>
        <a href="javascript:;">搜索推荐</a>
        <a href="javascript:;">友情链接</a>
      </p>
      <p>CopyRight &copy; 小兔鲜儿</p>
    </div>
  </footer>
</template>

<script>
export default {
  name: 'LoginFooter'
}
</script>

<style scoped lang='less'>
.login-footer {
  padding: 30px 0 50px;
  background: #fff;
  p {
    text-align: center;
    color: #999;
    padding-top: 20px;
    a {
      line-height: 1;
      padding:0 10px;
      color: #999;
      display: inline-block;
      ~ a {
        border-left: 1px solid #ccc;
      }
    }
  }
}
</style>
  • 主体组件 src/views/login/index.vue ```vue

<a name="qO7ub"></a>
## 03-登录-切换效果
目的:完成点击nav按钮切换,帐号登录和扫码登录。<br />**大致步骤:**

- 声明activeName数据提供模版使用
- 点击按钮设置activeName和绑定class属性
- 控制帐号登录盒子和扫码登录盒子显示隐藏

**落的代码:**<br />src/views/login/index.vue
```javascript
+import { ref } from 'vue'
export default {
  name: 'PageLogin',
  components: { LoginHeader, LoginFooter },
  setup () {
+    const activeName = ref('account')
+    return { activeName }
  }
}
    <div class="wrapper">
      <nav>
        <a @click="activeName='account'" :class="{active:activeName==='account'}" href="javascript:;">账户登录</a>
        <a @click="activeName='qrcode'" :class="{active:activeName==='qrcode'}" href="javascript:;">扫码登录</a>
      </nav>
      <!-- 表单 -->
      <div v-if="activeName==='account'" class="account-box">表单</div>
      <!-- 二维码 -->
      <div v-if="activeName==='qrcode'" class="qrcode-box">
        <img src="@/assets/images/qrcode.jpg" alt="">
        <p>打开 <a href="javascript:;">小兔鲜App</a> 扫码登录</p>
      </div>
    </div>
// 二维码容器
.qrcode-box {
  text-align: center;
  padding-top: 40px;
  p {
    margin-top: 20px;
    a {
      color: @xtxColor;
      font-size: 16px;
    }
  }
}

04-登录-表单组件

目的:完成表单布局和帐号登录,短信登录切换。
大致步骤:

  • 提取组件
    • 通过isMsgLogin切换短信登录
    • 通过form.isAgree绑定同意协议
  • 使用组件

落的代码:

  1. 定义组件 src/views/login/component/login-form.vue

基础结构:

<template>
  <div class="account-box">
    <div class="toggle">
      <a @click="isMsgLogin=false" href="javascript:;" v-if="isMsgLogin">
        <i class="iconfont icon-user"></i> 使用账号登录
      </a>
      <a @click="isMsgLogin=true" href="javascript:;" v-else>
        <i class="iconfont icon-msg"></i> 使用短信登录
      </a>
    </div>
    <div class="form">
      <template v-if="!isMsgLogin">
        <div class="form-item">
          <div class="input">
            <i class="iconfont icon-user"></i>
            <input type="text" placeholder="请输入用户名或手机号" />
          </div>
          <!-- <div class="error"><i class="iconfont icon-warning" />请输入手机号</div> -->
        </div>
        <div class="form-item">
          <div class="input">
            <i class="iconfont icon-lock"></i>
            <input type="password" placeholder="请输入密码">
          </div>
        </div>
      </template>
      <template v-else>
        <div class="form-item">
          <div class="input">
            <i class="iconfont icon-user"></i>
            <input type="text" placeholder="请输入手机号" />
          </div>
        </div>
        <div class="form-item">
          <div class="input">
            <i class="iconfont icon-code"></i>
            <input type="password" placeholder="请输入验证码">
            <span class="code">发送验证码</span>
          </div>
        </div>
      </template>
      <div class="form-item">
        <div class="agree">
          <XtxCheckbox v-model="form.isAgree" />
          <span>我已同意</span>
          <a href="javascript:;">《隐私条款》</a>
          <span>和</span>
          <a href="javascript:;">《服务条款》</a>
        </div>
      </div>
      <a href="javascript:;" class="btn">登录</a>
    </div>
    <div class="action">
      <img src="https://qzonestyle.gtimg.cn/qzone/vas/opensns/res/img/Connect_logo_7.png" alt="">
      <div class="url">
        <a href="javascript:;">忘记密码</a>
        <a href="javascript:;">免费注册</a>
      </div>
    </div>
  </div>
</template>

基础样式:

// 账号容器
.account-box {
  .toggle {
    padding: 15px 40px;
    text-align: right;
    a {
      color: @xtxColor;
      i {
        font-size: 14px;
      }
    }
  }
  .form {
    padding: 0 40px;
    &-item {
      margin-bottom: 28px;
      .input {
        position: relative;
        height: 36px;
        > i {
          width: 34px;
          height: 34px;
          background: #cfcdcd;
          color: #fff;
          position: absolute;
          left: 1px;
          top: 1px;
          text-align: center;
          line-height: 34px;
          font-size: 18px;
        }
        input {
          padding-left: 44px;
          border: 1px solid #cfcdcd;
          height: 36px;
          line-height: 36px;
          width: 100%;
          &.error {
            border-color: @priceColor;
          }
          &.active,&:focus {
            border-color: @xtxColor;
          }
        }
        .code {
          position: absolute;
          right: 1px;
          top: 1px;
          text-align: center;
          line-height: 34px;
          font-size: 14px;
          background: #f5f5f5;
          color: #666;
          width: 90px;
          height: 34px;
          cursor: pointer;
        }
      }
      > .error {
        position: absolute;
        font-size: 12px;
        line-height: 28px;
        color: @priceColor;
        i {
          font-size: 14px;
          margin-right: 2px;
        }
      }
    }
    .agree {
      a {
        color: #069;
      }
    }
    .btn {
      display: block;
      width: 100%;
      height: 40px;
      color: #fff;
      text-align: center;
      line-height: 40px;
      background: @xtxColor;
      &.disabled {
        background: #cfcdcd;
      }
    }
  }
  .action {
    padding: 20px 40px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    .url {
      a {
        color: #999;
        margin-left: 10px;
      }
    }
  }
}

依赖数据:

import { reactive, ref } from 'vue'
export default {
  name: 'LoginForm',
  setup () {
    // 是否短信登录
    const isMsgLogin = ref(false)
    // 表单信息对象
    const form = reactive({
      isAgree: true
    })
    return { isMsgLogin, form }
  }
}
  1. 使用组件 src/views/login/index.vue ```vue
  • javascript +import LoginForm from ‘./components/login-form’ import { ref } from ‘vue’ export default { name: ‘PageLogin’, components: { LoginHeader, LoginFooter,
  • LoginForm }, ```

    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 ```vue

        <div class="form-item">
          <div class="input">
            <i class="iconfont icon-user"></i>
    
  • {{errors.mobile}}
  • {{errors.password}}
  • 发送验证码
  • {{errors.code}}
    ```vue
    <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"/>
    
  • Field 的 as 属性可以指定为其他标签,也可指定为组件。

    • 但是组件需要支持 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" />
    
  • 实现显示的时候动画效果 ```vue