黑马程序员前端 Vue3 小兔鲜电商项目——(八)登录页面

这篇具有很好参考价值的文章主要介绍了黑马程序员前端 Vue3 小兔鲜电商项目——(八)登录页面。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

黑马程序员前端 Vue3 小兔鲜电商项目——(八)登录页面

登录页面的主要功能就是表单校验和登录登出业务。

账号密码

account password
cdshi0080 123456
cdshi0081 123456
cdshi0082 123456
cdshi0083 123456
cdshi0084 123456
cdshi0085 123456
cdshi0086 123456
cdshi0087 123456
cdshi0088 123456

路由配置

模版代码

在 src\views\Login\index.vue 中添加登录页代码:

<script setup>

</script>

<template>
  <div>
    <header class="login-header">
      <div class="container m-top-20">
        <h1 class="logo">
          <RouterLink to="/">小兔鲜</RouterLink>
        </h1>
        <RouterLink class="entry" to="/">
          进入网站首页
          <i class="iconfont icon-angle-right"></i>
          <i class="iconfont icon-angle-right"></i>
        </RouterLink>
      </div>
    </header>
    <section class="login-section">
      <div class="wrapper">
        <nav>
          <a href="javascript:;">账户登录</a>
        </nav>
        <div class="account-box">
          <div class="form">
            <el-form label-position="right" label-width="60px"
              status-icon>
              <el-form-item  label="账户">
                <el-input/>
              </el-form-item>
              <el-form-item label="密码">
                <el-input/>
              </el-form-item>
              <el-form-item label-width="22px">
                <el-checkbox  size="large">
                  我已同意隐私条款和服务条款
                </el-checkbox>
              </el-form-item>
              <el-button size="large" class="subBtn">点击登录</el-button>
            </el-form>
          </div>
        </div>
      </div>
    </section>

    <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>
  </div>
</template>

<style scoped lang='scss'>
.login-header {
  background: #fff;
  border-bottom: 1px solid #e4e4e4;

  .container {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
  }

  .logo {
    width: 200px;

    a {
      display: block;
      height: 132px;
      width: 100%;
      text-indent: -9999px;
      background: url("@/assets/images/logo.png") no-repeat center 18px / contain;
    }
  }

  .sub {
    flex: 1;
    font-size: 24px;
    font-weight: normal;
    margin-bottom: 38px;
    margin-left: 20px;
    color: #666;
  }

  .entry {
    width: 120px;
    margin-bottom: 38px;
    font-size: 16px;

    i {
      font-size: 14px;
      color: $xtxColor;
      letter-spacing: -5px;
    }
  }
}

.login-section {
  background: url('@/assets/images/login-bg.png') no-repeat center / cover;
  height: 488px;
  position: relative;

  .wrapper {
    width: 380px;
    background: #fff;
    position: absolute;
    left: 50%;
    top: 54px;
    transform: translate3d(100px, 0, 0);
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.15);

    nav {
      font-size: 14px;
      height: 55px;
      margin-bottom: 20px;
      border-bottom: 1px solid #f5f5f5;
      display: flex;
      padding: 0 40px;
      text-align: right;
      align-items: center;

      a {
        flex: 1;
        line-height: 1;
        display: inline-block;
        font-size: 18px;
        position: relative;
        text-align: center;
      }
    }
  }
}

.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;
      }
    }
  }
}

.account-box {
  .toggle {
    padding: 15px 40px;
    text-align: right;

    a {
      color: $xtxColor;

      i {
        font-size: 14px;
      }
    }
  }

  .form {
    padding: 0 20px 20px 20px;

    &-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;
      }
    }
  }
}

.subBtn {
  background: $xtxColor;
  width: 100%;
  color: #fff;
}
</style>

配置路由跳转

修改 src\views\Layout\components\LayoutNav.vue 文件中【请先登录】的 a 标签:

 <li><a href="javascript:;" @click="router.push('/login')">请先登录</a></li>

表单校验实现

作用:前端提前校验可以省去一些错误的请求提交,为后端节省接口压力。

ElementPlus 表单组件内置了表单校验功能,只需要按照组件要求配置必要参数即可(直接看文档)。ElementPlus表单组件内置了初始的校验配置,应付简单的校验只需要通过配置即可,如果想要定制一些特殊的校验需求,可以使用自定义校验规则。

校验要求

用户名:不能为空,字段名为 account
密码:不能为空且为6-14个字符,字段名为 password
同意协议:必选,字段名为 agree

代码实现

  1. 按照字段准备表单对象:

    // 1.准备表单对象
    const form = ref({
      account: '',
      password: '',
      agree: true
    })
    
  2. 按照产品要求准备规则对象:

    // 2. 校验规则对象
    const rules = {
      account: [
        { required: true, message: '用户名不能为空', trigger: 'blur' }
      ],
      password: [
        { required: true, message: '密码不能为空', trigger: 'blur' },
        { min: 6, max: 24, message: '密码长度要求6-14个字符', trigger: 'blur' }
      ],
      agree: [
        {
          validator: (rule, value, callBack) => {
            console.log(value)
            //自定义校验逻辑
            // 勾选协议通过,不勾选不通过
            if (value) {
              callBack()
            } else {
              callBack(new Error('请勾选协议'))
            }
          }
        }
      ]
    }
    
  3. 给表单绑定用户表单对象和校验规则:

    <el-form label-position="right" :model="form" :rules="rules" label-width="60px" status-icon>
      ...
    </el-form>
    
  4. 指定表单域的校验字段名:

    <el-form-item prop="account" label="账户">
      <el-input />
    </el-form-item>
    <el-form-item prop="password" label="密码">
      <el-input />
    </el-form-item>
    <el-form-item prop="agree" label-width="22px">
      <el-checkbox size="large">
        我已同意隐私条款和服务条款
      </el-checkbox>
    </el-form-item>
    
  5. 把表单对象进行双向绑定:

<el-form-item prop="account" label="账户">
  <el-input v-model="form.account"/>
</el-form-item>
<el-form-item prop="password" label="密码">
  <el-input v-model="form.password"/>
</el-form-item>
<el-form-item prop="agree" label-width="22px">
  <el-checkbox size="large" v-model="form.agree">
    我已同意隐私条款和服务条款
  </el-checkbox>
</el-form-item>

统一校验

在点击登录时需要对所有需要校验的表单进行统一校验。

  1. 获取表单实例:

    // 3.获取 form 实例做统一校验
    const formRef = ref(null)
    

    与表单进行绑定:

    <el-form ref="formRef" label-position="right" :model="form" :rules="rules" label-width="60px" status-icon>
      ...
    </el-form>
    
  2. 编写登录逻辑:

    const doLogin = () => {
      // 调用实例方法
      formRef.value.validate(async (valid) => {
        // valid: 所有表单都通过校验  才为true
        console.log(valid)
        // 以valid做为判断条件 如果通过校验才执行登录逻辑
        if (valid) {
          // TODO LOGIN
        }
      })
    }
    
  3. 与登录按钮进行绑定:

     <el-button size="large" class="subBtn" @click="doLogin">点击登录</el-button>
    

登录基础业务实现

基础思想

  1. 调用登录接口获取用户信息
  2. 提示用户当前是否成功
  3. 跳转到首页
  1. 新建 src\apis\user.js 文件,编写登录 api:

    //封装所有和用户相关的接口函数
    import http from '@/utils/http'
    
    export const loginApi = ({ account, password }) => {
        return http({
            url: '/login',
            method: 'POST',
            data: {
                account, password
            }
        })
    }
    
  2. src\views\Login\index.vue 中完善登录逻辑:

    // 3.获取 form 实例做统一校验
    const router = useRouter()
    const formRef = ref(null)
    const doLogin = () => {
      const { account, password } = form.value
      // 调用实例方法
      formRef.value.validate(async (valid) => {
        // valid: 所有表单都通过校验  才为true
        console.log(valid)
        // 以valid做为判断条件 如果通过校验才执行登录逻辑
        if (valid) {
          // TODO LOGIN
          await loginAPI({ account, password })
          // 1. 提示用户
          ElMessage({ type: 'success', message: '登录成功' })
          // 2. 跳转首页
          router.replace({ path: '/' })
        }
      })
    }
    

统一错误信息提示

在 src\utils\http.js 中的响应拦截器中进行处理:

// axios响应式拦截器
http.interceptors.response.use(res => res.data, e => {
  // 统一错误提示
  ElMessage({
    type: 'warning',
    message: e.response.data.message
  })
  return Promise.reject(e)
})

Pinia 管理用户数据

由于用户数据的特殊性,在很多组件中都有可能进行共享,共享的数据使用 Pinia 管理会更加方便。Pinia 负责用户数据相关的 state 和 action,组件中只负责触发 action 函数并传递参数。

  1. 添加 src\stores\user.js 文件,在其中添加保存用户数据的方法:

    // 管理用户数据相关
    import { defineStore } from 'pinia'
    import { ref } from 'vue'
    import { loginAPI } from '@/apis/user'
    
    export const useUserStore = defineStore('user', () => {
        // 1. 定义管理用户数据的state
        const userInfo = ref({})
        // 2. 定义获取接口数据的action函数
        const getUserInfo = async ({ account, password }) => {
            const res = await loginAPI({ account, password })
            userInfo.value = res.result
        }
        // 3. 以对象的格式把state和action return
        return {
            userInfo,
            getUserInfo
        }
    })
    
  2. 替换 src\views\Login\index.vue 中登录逻辑的原代码:

    import {useUserStore} from '@/stores/user'
    ...
    const userStore =  useUserStore()
    // TODO LOGIN
    await userStore.getUserInfo({ account, password })
    

Pinia 用户数据持久化

用户数据中有一个关键的数据叫做 Token(用来标识当前用户是否登录),而 Token 持续一段时间才会过期。Pinia 的存储是基于内存的,刷新就丢失,为了保持登录状态就要做到刷新不丢失,需要配合持久化进行存储。

最终效果:操作 state 时会自动把用户数据在本地的 localStorage 也存一份,刷新的时候会从 localStorage 中先取。

  1. 安装 pinia 持久化插件 persistedstate:

    npm i pinia-plugin-persistedstate  
    
  2. 在 main.js 中注册 pinia 持久化插件:

    import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
    
    const app = createApp(App)
    const pinia = createPinia()
    //注册持久化插件
    pinia.use(piniaPluginPersistedstate)
    app.use(pinia)
    
  3. 对 store 持久化配置:

    export const useUserStore = defineStore('user', () => {
      ...
    }, {
      persist: true
    })
    

用户登录状态

在首页根据用户登录状态区分显示的模块。

在 src\views\Layout\components\LayoutNav.vue 中获取 pinia 中存储的用户数据:

import { useUserStore } from '@/stores/user'
const userStore = useUserStore()

修改模板的 v-if 判断逻辑:

<!--多模版渲染区分登录状态和非登录状态-->
<!--适配思路:登录时显示第一块非登录时显示第二块是否有token-->
<template v-if="userStore.userInfo.token">
  <li><a href="javascript:;"><i class=" iconfont icon-user"></i>{{userStore.userInfo.account}}</a></li>
  ...
</template>
<template v-else>
  ...
</template>

请求拦截器携带 token

Token作为用户标识,在很多个接口中都需要携带Token才可以正确获取数据,所以需要在接口调用时携带Token。另外,为了统一控制采取请求拦截器携带的方案。

Axiosi请求拦截器可以在接口正式发起之前对请求参数做一些事情,通常Token数据会被注入到请求header中,格式按
照后端要求的格式进行拼接处理。格式如图:

黑马程序员前端 Vue3 小兔鲜电商项目——(八)登录页面

修改 src\utils\http.js 文件中的请求拦截器,从 pinia 获取token数据,将 token 存储到请求的请求头中:

// axios请求拦截器
htpp.interceptors.request.use(config => {
  // 1. 从pinia获取token数据
  const userStore = useUserStore()
  // 2. 按照后端的要求拼接token数据
  const token = userStore.userInfo.token
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
}, e => Promise.reject(e))

退出登录实现

基础思想:

  1. 清除用户信息
  2. 跳转到登录页
  1. src\stores\user.js 中新增清除用户信息方法:

    export const useUserStore = defineStore('user', () => {
      ...
      // 退出时清除用户信息
      const clearUserInfo = () => {
        userInfo.value = {}
      }
      // 3. 以对象的格式把state和action return
      return {
        userInfo,
        getUserInfo,
        clearUserInfo
      }
    }, {
      persist: true
    })
    
  2. 执行退出逻辑,清除用户信息:

    <script setup>
    import { useUserStore } from '@/stores/userStore'
    import { useRouter } from 'vue-router'
    const userStore = useUserStore()
    const router = useRouter()
    const confirm = () => {
      console.log('用户要退出登录了')
      // 退出登录业务逻辑实现
      // 1.清除用户信息 触发action
      userStore.clearUserInfo()
      // 2.跳转到登录页
      router.push('/login')
    }
    </script>
    

Token 失效拦截处理

Token的有效性可以保持一定时间,如果用户一段时间不做任何操作,Token.就会失效,使用失效的Token再去请求一
些接口,接口就会报401状态码错误,需要我们做额外处理。

在 src\utils\http.js 中进行处理:文章来源地址https://www.toymoban.com/news/detail-499754.html

// axios响应式拦截器
http.interceptors.response.use(res => res.data, e => {
  // 从pinia获取token数据
  const userStore = useUserStore()
  // 统一错误提示
  ElMessage({
    type: 'warning',
    message: e.response.data.message
  })
  // 401 token 失效处理
  // 1.清楚本地用户数据
  // 2.跳转登录页
  if(e.response.status===401){
    userStore.clearUserInfo()
    router.push('/login')
  }
  return Promise.reject(e)
})

到了这里,关于黑马程序员前端 Vue3 小兔鲜电商项目——(八)登录页面的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【前端Vue】Vue3+Pinia小兔鲜电商项目第1篇:认识Vue3,1. Vue3组合式API体验【附代码文档】

    通过 Counter 案例 体验Vue3新引入的组合式API 特点: 代码量变少 分散式维护变成集中式维护 ![image.png]( create-vue是Vue官方新的脚手架工具,底层切换到了 vite (下一代前端工具链),为开发提供极速响应 ![image.png]( 前置条件 - 已安装16.0或更高版本的Node.js 执行如下命令,这一指

    2024年03月17日
    浏览(46)
  • 【前端Vue】Vue3+Pinia小兔鲜电商项目第2篇:什么是pinia,1. 创建空Vue项目【附代码文档】

    Pinia 是 Vue 的专属状态管理库,可以实现跨组件或页面共享状态,是 vuex 状态管理工具的替代品,和 Vuex相比,具备以下优势 提供更加简单的API (去掉了 mutation ) 提供符合组合式API风格的API (和 Vue3 新语法统一) 去掉了modules的概念,每一个store都是一个独立的模块 搭配

    2024年03月21日
    浏览(37)
  • 【前端Vue】Vue3+Pinia小兔鲜电商项目第4篇:静态结构搭建和路由配置,1. 准备分类组件【附代码文档】

    Vue3+ElementPlus+Pinia开发小兔鲜电商项目完整教程(附代码资料)主要内容讲述:认识Vue3,使用create-vue搭建Vue3项目1. Vue3组合式API体验,2. Vue3更多的优势,1. 认识create-vue,2. 使用create-vue创建项目,1. setup选项的写法和执行时机,2. setup中写代码的特点。什么是pinia,创建空Vue项目并安装

    2024年04月11日
    浏览(34)
  • 购物车功能实现(小兔鲜儿)【Vue3】

    购物车业务逻辑梳理拆解 整个购物车的实现分为两个大分支, 本地购物车操作和接口购物车操作 由于购物车数据的特殊性, 采取Pinia管理购物车列表数据并添加持久化缓存 本地购物车 - 加入购物车实现 添加购物车 基础思想:如果已经添加过相同的商品,就在其数量count上加一

    2024年02月15日
    浏览(36)
  • 黑马程序员前端学习接口变更

    VUE 刘老师离职了,VUE的接口失效的,一律换为黑马官方接口 axios.defaults.baseURL = \\\'https://www.escook.cn\\\' 换成 axios.defaults.baseURL = \\\'https://applet-base-api-t.itheima.net\\\'    微信小程序 https://api-ugo-web.itheima.net https://www.uinav.com 刘老师博客简介为不再讲课的程序员,但他真的是一位不错的老师

    2024年02月09日
    浏览(38)
  • 登录页的具体实现 (小兔鲜儿)【Vue3】

    整体认识 登录页面的主要功能就是 表单校验和登录登出业务 准备模板 配置路由跳转 为什么需要校验 作用:前端提前校验可以 省去一些错误的请求提交 ,为后端节省接口压力 表单如何进行校验 ElementPlus表单组件内置了表单校验功能,只需要 按照组件要求配置必要参数 即

    2024年02月15日
    浏览(43)
  • CSS笔记(黑马程序员pink老师前端)定位

    定位可以让盒子自由的在某个盒子内移动位置或者固定在屏幕中某个位置,并且可以压住其他盒子。 定位 = 定位模式 + 边偏移 定位模式 说明 static 静态定位,按标准流特性摆放,没有边偏移,很少用 relative 相对定位,相对自身原有位置移动,原有位置继续占有(不脱标) absolute 绝

    2024年02月09日
    浏览(40)
  • 小程序入门笔记(一) 黑马程序员前端微信小程序开发教程

    微信小程序基本介绍 小程序和普通网页有以下几点区别: 运行环境:小程序可以在手机的操作系统上直接运行,如微信、支付宝等;而普通网页需要在浏览器中打开才能运行。 开发技术:小程序采用前端技术进行开发,如HTML、CSS、JavaScript等;而普通网页也是使用类似的前

    2024年02月08日
    浏览(41)
  • CSS笔记(黑马程序员pink老师前端)浮动,清除浮动

    浮动可以改变标签的默认排列方式。浮动元素常与标准流的父元素搭配使用. 网页布局第一准则 :多个块级元素纵向排列找标准流,多个块级元素横向排列找浮动。 float属性用于创建浮动框,将其移动到一边,直到左边缘或右边缘触及包含块或另一个浮动框的边缘。 1.浮动元素

    2024年02月09日
    浏览(38)
  • 《JavaScript前端开发案例教程》课后习题及答案(黑马程序员编著)

    一、填空题 1.window.document.body可以简写为______。 2.console.log(alert(‘Hello’))在控制台的输出结果是______。 3.编辑器中“以UTF-8无BOM格式编码”中的BOM指的是______。 4.alert(‘测试’.length)的输出结果是______。 二、判断题 1.JavaScript是Java语言的脚本形式。( ) 2.JavaScript中的方法名不

    2024年02月09日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包