【vue3】黑马程序员前端Vue3小兔鲜电商项目【八】

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

image-20230624165512735

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

账号密码

accountpassword
cdshi0080123456
cdshi0081123456
cdshi0082123456
cdshi0083123456
cdshi0084123456
cdshi0085123456
cdshi0086123456
cdshi0087123456
cdshi0088123456

路由配置

模版代码

在 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: 所有表单都通过校验  才为trueconsole.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: 所有表单都通过校验  才为trueconsole.log(valid)// 以valid做为判断条件 如果通过校验才执行登录逻辑if (valid) {// TODO LOGINawait 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. 定义管理用户数据的stateconst userInfo = ref({})// 2. 定义获取接口数据的action函数const getUserInfo = async ({ account, password }) => {const res = await loginAPI({ account, password })userInfo.value = res.result}// 3. 以对象的格式把state和action returnreturn {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中,格式按
照后端要求的格式进行拼接处理。格式如图:

image-20230624164604499

修改 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.tokenif (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 returnreturn {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.清除用户信息 触发actionuserStore.clearUserInfo()// 2.跳转到登录页router.push('/login')
    }
    </script>

Token 失效拦截处理

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

在 src\utils\http.js 中进行处理:

// 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)
})

本文转自 https://blog.csdn.net/qq_20185737/article/details/131363742?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522267ecdbd4b79cab0f88ca091b1719024%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=267ecdbd4b79cab0f88ca091b1719024&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_ecpm_v1~rank_v31_ecpm-2-131363742-null-null.nonecase&utm_term=%E5%B0%8F%E5%85%94&spm=1018.2226.3001.4450,如有侵权,请联系删除。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/79471.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++学习:六个月从基础到就业——C++11/14:右值引用与移动语义

C学习&#xff1a;六个月从基础到就业——C11/14&#xff1a;右值引用与移动语义 本文是我C学习之旅系列的第三十九篇技术文章&#xff0c;也是第三阶段"现代C特性"的第一篇&#xff0c;主要介绍C11/14中引入的右值引用和移动语义。查看完整系列目录了解更多内容。 引…

基于Qlearning强化学习的电梯群控系统高效调度策略matlab仿真

目录 1.算法仿真效果 2.算法涉及理论知识概要 2.1 Q-learning强化学习原理 2.2 基于Q-learning的电梯群控系统建模 3.MATLAB核心程序 4.完整算法代码文件获得 1.算法仿真效果 matlab2022a仿真结果如下&#xff08;完整代码运行后无水印&#xff09;&#xff1a; 仿真操作…

31.软件时序控制方式抗干扰

软件时序控制方式扛干扰 1. 软件时序控制抗干扰的时间逻辑2. 应用案例 1. 软件时序控制抗干扰的时间逻辑 &#xff08;1&#xff09;将受软件控制的功能或软件检测到的状态一一罗列&#xff1b; &#xff08;2&#xff09;将其中的潜在干扰和敏感信号分开&#xff1b; &#x…

Ubuntu环境下使用uWSGI服务器【以flask应用部署为例】

0、前置内容说明 首先要知道WSGI是什么&#xff0c;关于WSGI服务器的介绍看这篇&#xff1a;WSGI&#xff08;Web Server Gateway Interface&#xff09;服务器 由于从Python 3.11开始限制了在系统级 Python 环境中使用 pip 安装第三方包&#xff0c;以避免与系统包管理器&am…

d3_v7绘制折线图

<!DOCTYPE html> <html><head><meta charsetutf-8><title>需求</title><script src"https://d3js.org/d3.v7.min.js"></script><style>* {margin: 0;padding: 0;}html, body {width: 100%;height: 100%;displ…

Hotspot分析(1):单细胞转录组识别信息基因(和基因模块)

这一期我们介绍一个常见的&#xff0c;高分文章引用很高的一个单细胞转录组分析工具Hotspot&#xff0c;它可针对单细胞转录组数据识别有意义基因或者基因module&#xff0c;类似于聚类模块。所谓的”informative "的基因是那些在给定度量中相邻的细胞之间以相似的方式表达…

爬虫准备前工作

1.Pycham的下载 网址&#xff1a;PyCharm: The only Python IDE you need 2.Python的下载 网址&#xff1a;python.org&#xff08;python3.9版本之后都可以&#xff09; 3.node.js的下载 网址&#xff1a;Node.js — 在任何地方运行 JavaScript&#xff08;版本使用18就可…

基于Springboot旅游网站系统【附源码】

基于Springboot旅游网站系统 效果如下&#xff1a; 系统登陆页面 系统主页面 景点信息推荐页面 路线详情页面 景点详情页面 确认下单页面 景点信息管理页面 旅游路线管理页面 研究背景 随着互联网技术普及与在线旅游消费习惯的深化&#xff0c;传统旅游服务模式面临效率低、…

利用KMP找出模式串在目标串中所有匹配位置的起始下标

问题关键&#xff1a;完成首次匹配之后需要继续进行模式匹配。 到这一步后&#xff0c;我们不能直接将j 0然后开始下一轮匹配&#xff0c;因为已经匹配过的部分&#xff08;蓝色部分&#xff09;中仍然可能存在与模式串重叠的子串&#xff1a; 解决办法&#xff1a; 找到蓝…

RR(Repeatable Read)级别如何防止幻读

在 MySQL 数据库事务隔离级别中&#xff0c;RR&#xff08;可重复读&#xff09; 通过 MVCC&#xff08;多版本并发控制&#xff09; 和 锁机制 的组合策略来避免幻读问题。 一、MVCC机制&#xff1a;快照读与版本控制 快照读&#xff08;Snapshot Read&#xff09; 每个事务启…

Android运行时ART加载类和方法的过程分析

目录 一,概述 二,ART运行时的入口 一,概述 既然ART运行时执行的都是翻译DEX字节码后得到的本地机器指令了&#xff0c;为什么还需要在OAT文件中包含DEX文件&#xff0c;并且将它加载到内存去呢&#xff1f;这是因为ART运行时提供了Java虚拟机接口&#xff0c;而要实现Java虚…

Javase 基础加强 —— 02 泛型

本系列为笔者学习Javase的课堂笔记&#xff0c;视频资源为B站黑马程序员出品的《黑马程序员JavaAI智能辅助编程全套视频教程&#xff0c;java零基础入门到大牛一套通关》&#xff0c;章节分布参考视频教程&#xff0c;为同样学习Javase系列课程的同学们提供参考。 01 认识泛型…

Oracle VirtualBox 在 macOS 上的详细安装步骤

Oracle VirtualBox 在 macOS 上的详细安装步骤 一、准备工作1. 系统要求2. 下载安装包二、安装 VirtualBox1. 挂载安装镜像2. 运行安装程序3. 处理安全限制(仅限首次安装)三、安装扩展包(增强功能)四、配置第一个虚拟机1. 创建新虚拟机2. 分配内存3. 创建虚拟硬盘4. 加载系…

RAGFlow 接入企业微信应用实现原理剖析与最佳实践

背景 近期有医美行业客户咨询我们智能客服产品&#xff0c;期望将自己企业的产品、服务以及报价信息以企微应用的方式给到客户进行体验互动&#xff0c;提升企业运营效率。关于企业微信对接&#xff0c;我们分享下最佳实践&#xff0c;抛砖引玉。效果图如下&#xff1a; 这里也…

【心海资源】子比主题新增注册与会员用户展示功能模块及实现方法

内容改写&#xff1a; 本次分享的是子比主题顶部展示注册用户与会员信息的功能模块及其实现方式。 你可以通过两种方式启用该功能&#xff1a; 直接在后台进入“外观 → 小工具”启用该展示模块&#xff0c;操作简便&#xff1b;也可将提供的代码覆盖至子比主题目录中&#…

CSDN积分详解(介绍、获取、用途)

&#x1f91f;致敬读者 &#x1f7e9;感谢阅读&#x1f7e6;笑口常开&#x1f7ea;生日快乐⬛早点睡觉 &#x1f4d8;博主相关 &#x1f7e7;博主信息&#x1f7e8;博客首页&#x1f7eb;专栏推荐&#x1f7e5;活动信息 文章目录 积分**一、积分类型及用途****二、积分获取途…

【iview】es6变量结构赋值(对象赋值)

变量的解构赋值 以iview的src/index.js中Vue.prototype.$IVIEW改造为例练习下怎么使用变量的解构赋值 原来的写法&#xff1a; const install function(Vue, opts {}) {if (install.installed) return;locale.use(opts.locale);locale.i18n(opts.i18n);Object.keys(iview).fo…

【c++深入系列】:万字详解vector(附模拟实现的vector源码)

&#x1f525; 本文专栏&#xff1a;c &#x1f338;作者主页&#xff1a;努力努力再努力wz &#x1f4aa; 今日博客励志语录&#xff1a; 种子破土时从不问‘会不会有光’&#xff0c;它只管生长 ★★★ 本文前置知识&#xff1a; 模版 1.什么是vector 那么想必大家都学过顺…

MySQL基础关键_007_DQL 练习

目 录 一、题目 二、答案&#xff08;不唯一&#xff09; 1.查询每个部门薪资最高的员工信息 2.查询每个部门高于平均薪水的员工信息 3. 查询每个部门平均薪资等级 4.查询部门中所有员工薪资等级的平均等级 5.不用分组函数 max 查询最高薪资 6.查询平均薪资最高的部门编…

Jenkis安装、配置及账号权限分配保姆级教程

Jenkis安装、配置及账号权限分配保姆级教程 安装Jenkins下载Jenkins启动Jenkins配置Jenkins入门Jenkins配置配置中文配置前端自动化任务流新建任务拉取代码打包上传云服务并运行配置后端自动化任务流新建任务拉取代码打包上传云服务并运行账号权限分配创建用户分配视图权限安装…