02 注册 登录

0.项目-注册和登录-页面准备


目标

  • 准备注册和登录页面组件及路由

讲解

  1. 新建注册页面组件, 在src/views/register/index.vue, 直接复制标签

<template>
  <div>
    Reg.vue
  </div>
</template>
​
<script>
export default {
  name: 'my-register'
}
</script>
​
<style lang="less"scoped></style>

  1. 新建登录页面组件, 在src/views/login/index.vue, 直接复制标签

<template>
  <div>
    Login.vue
  </div>
</template>
​
<script>
export default {
  name: 'my-login'
}
</script>
​
<style lang="less"scoped></style>

  1. 在src/router/index.js配置路由表

import Vue from'vue'
import VueRouter from'vue-router'
​
Vue.use(VueRouter)
​
constroutes= [
  {
    path: '/reg',
    component: () =>import('@/views/register')
  },
  {
    path: '/login',
    component: () =>import('@/views/login')
  }
]
​
const router=new VueRouter({
  routes
})
​
export default router

  1. 在 App.vue 组件中,定义 <router-view> 如下

可以把之前测试的代码直接覆盖掉
<template>
  <router-view></router-view>
</template>
​
<script>
export default {
  name: 'App'
}
</script>
​
<style lang="less"scoped></style>
  1. 启动webpack开发服务器, 然后在页面的地址栏, 手动切换路由地址看是否配置成功

1注册-标签布局和表单校验

目标

  • 完成注册页面, 标签和样式布局

讲解

  1. 在src/views/register/index.vue, 初始化注册页面的基础布局,并美化样式,

  1. ==先看需求图, 分析后直接复制后阅读==

1 编写基本样式

一 : 如何让一个盒子水平垂直居中:

1行内元素: text-align:center

2 div : margin: 0 auto 只能水平居中,不能垂直居中,不起作用

3 div : 定位 + 偏移

div: 弹性布局

div :子绝父相之后,给子元素div添加上下左右为0,外边距为auto。

参考:

https://blog.csdn.net/LYY_record/article/details/123215386

二: 编写习惯

从外到内,从上到下,从左到右

<template>
  <!-- 注册页面的整体盒子 -->
  <div class="reg-container">
    <!-- 注册的盒子 -->
    <div class="reg-box">
      <!-- 标题的盒子 -->
      <div class="title-box"></div>
      <!-- 注册的表单区域 -->
    </div>
  </div>
</template>
<script>
export default {
  name: 'my-register'
}
</script>
<style lang="less" scoped>
.reg-container {
  background: url('../../assets/images/login_bg.jpg') center;
  background-size: cover;
  height: 100vh;
  .reg-box {
    width: 400px;
    height: 335px;
    background-color: #fff;
    border-radius: 3px;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    padding: 0 30px;
    box-sizing: border-box;
    .title-box {
      height: 60px;
      background: url('../../assets/images/login_title.png') center no-repeat;
    }
    .btn-reg {
      width: 100%;
    }
  }
}
</style>

因为父级没有高度。导致 背景图 不显示

height:100% 往上找,他的父元素么有设置高度

默认body 是没有高度的,在全局样式中设置了

2 编写input

2.2 占位文字,绑定数据

2.3 添加点击事件

2.4 查看页面

label-width 是控制左侧的宽度的。 label都没了,这个应该去掉

修改注册按钮样式:

3表单校验

  1. 查找elementUI组件库, 要完成表单组件布局, 并带上基础校验, ==自己分析铺设, 变量可以看, 不可以复制==

  • 规则1: 用户名必须是1-10的大小写字母数字

  • 规则2: 密码必须是6-15的非空字符

  • 规则3: 确认密码必须和密码值一致

<!-- 注册的表单区域 -->
<el-form :model="regForm" :rules="regRules" ref="regRef">
  <!-- 用户名 -->
  <el-form-item prop="username">
    <el-input v-model="regForm.username" placeholder="请输入用户名"></el-input>
  </el-form-item>
  <!-- 密码 -->
  <el-form-item prop="password">
    <el-input v-model="regForm.password" type="password" placeholder="请输入密码"></el-input>
  </el-form-item>
  <!-- 确认密码 -->
  <el-form-item prop="repassword">
    <el-input v-model="regForm.repassword" type="password" placeholder="请再次确认密码"></el-input>
  </el-form-item>
  <el-form-item>
    <el-button type="primary" class="btn-reg">注册</el-button>
    <el-link type="info">去登录</el-link>
  </el-form-item>
</el-form>


<script>
export default {
  name: 'my-register',
  data () {
// 需要拿到data里边的密码进行校验,所以验证方法写在data里边
    const samePwd = (rule, value, callback) => {
      if (value !== this.regForm.password) {
        // 如果验证失败,则调用 回调函数时,指定一个 Error 对象。
        callback(new Error('两次输入的密码不一致!'))
      } else {
        // 如果验证成功,则直接调用 callback 回调函数即可。
        callback()
      }
    }
    return {
      // 注册表单的数据对象
      regForm: {
        username: '',
        password: '',
        repassword: ''
      },
      // 注册表单的验证规则对象
      regRules: {
        username: [
          { required: true, message: '请输入用户名', trigger: 'blur' },
          {
            pattern: /^[a-zA-Z0-9]{1,10}$/,
            message: '用户名必须是1-10的大小写字母数字',
            trigger: 'blur'
          }
        ],
        password: [
          { required: true, message: '请输入密码', trigger: 'blur' },
          {
            pattern: /^\S{6,15}$/,
            message: '密码必须是6-15的非空字符',
            trigger: 'blur'
          }
        ],
        repassword: [
          { required: true, message: '请再次输入密码', trigger: 'blur' },
          { pattern: /^\S{6,15}$/, message: '密码必须是6-15的非空字符', trigger: 'blur' },
          { validator: samePwd, trigger: 'blur' }
        ]
      }
    }
  }
}
</script>

看数据是否收集到:

注册按钮变大: 加width

  .btn-reg {
      width: 100%;
    }

2 注册-功能实现

目标

  • 完成点击注册按钮校验和注册功能

讲解

核心思想: 注册就是把用户输入的账号和密码做好校验以后, 收集到变量中, 再调用接口发给后台, 后台代码把他们存储到数据库中, 再给前端返回提示

  1. 注册按钮, 绑定点击事件

<el-button type="primary" class="btn-reg" @click="regNewUserFn">注册</el-button>
  1. 在事件处理函数中, 先执行表单校验

methods: {
    // 注册新用户
    regNewUserFn () {
      // 进行表单预验证
      this.$refs.regRef.validate(valid => {
        if (!valid) return false
        // 尝试拿到用户输入的内容
        console.log(this.regForm)
      })
    }
  }
  1. 前端准备好了, 准备调用后台接口了, 所以准备接口方法, 在src/api/index.js定义


/**
 * 注册接口
 * @param {*} param0 { username: 用户名, password: 密码 }
 * @returns Promise对象
 */
export const registerAPI = ({ username, password, repassword }) => {
  return request({
    url: '/api/reg',
    method: 'post',
    data: {
      username,
      password,
      repassword
    }
  })
}

  1. 在逻辑页面引用接口, 并在注册逻辑中调用, 并使用element绑定在Vue全局属性上的$message弹窗方法

// 注册新用户
regNewUserFn () {
    // 进行表单预验证
    this.$refs.regRef.validate(async valid => {
        if (!valid) return false
        // 尝试拿到用户输入的内容
        // console.log(this.regForm)
        // 1. 调用注册接口
        //解构赋值,把axios返回的数据对象里边的data字段对应的值保存到res上
        const { data: res } = await registerAPI(this.regForm)
        console.log(res)
        // 2. 注册失败,提示用户
        if (res.code !== 0) return this.$message.error(res.message)
        // 3. 注册成功,提示用户
        this.$message.success(res.message)
        // 4. 跳转到登录页面
        this.$router.push('/login')
    })
}

3 登录-标签布局和表单校验

目标

在login页面根据需求准备页面标签和样式

讲解

  1. 和注册页面差不多, 标签和校验一样, 在src/views/login/index.vue中, 复制如下标签并阅读核对

<template>
  <!-- 登录页面的整体盒子 -->
  <div class="login-container">
    <!-- 登录的盒子 -->
    <div class="login-box">
      <!-- 标题的盒子 -->
      <div class="title-box"></div>
      <!-- 登录的表单区域 -->
      <el-form :model="loginForm" :rules="loginRules" ref="loginRef">
        <!-- 用户名 -->
        <el-form-item prop="username">
          <el-input v-model="loginForm.username" placeholder="请输入用户名" maxlength="10" minlength="1"></el-input>
        </el-form-item>
        <!-- 密码 -->
        <el-form-item prop="password">
          <el-input
            v-model="loginForm.password"
            type="password"
            placeholder="请输入密码"
            maxlength="15"
            minlength="6"
          ></el-input>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" class="btn-login">登录</el-button>
          <el-link type="info">去注册</el-link>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>
<script>
export default {
  name: 'my-login',
  data () {
    return {
      // 登录表单的数据对象
      loginForm: {
        username: '',
        password: ''
      },
      // 登录表单的验证规则对象
      loginRules: {
        username: [
          { required: true, message: '请输入用户名', trigger: 'blur' },
          { pattern: /^[a-zA-Z0-9]{1,10}$/, message: '用户名必须是1-10的字母数字', trigger: 'blur' }
        ],
        password: [
          { required: true, message: '请输入密码', trigger: 'blur' },
          { pattern: /^\S{6,15}$/, message: '密码必须是6-15的非空字符', trigger: 'blur' }
        ]
      }
    }
  }
}
</script>
<style lang="less" scoped>
.login-container {
  background: url('../../assets/images/login_bg.jpg') center;
  background-size: cover;
  height: 100%;
  .login-box {
    width: 400px;
    height: 270px;
    background-color: #fff;
    border-radius: 3px;
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    padding: 0 30px;
    box-sizing: border-box;
    .title-box {
      height: 60px;
      background: url('../../assets/images/login_title.png') center no-repeat;
    }
    .btn-login {
      width: 100%;
    }
  }
}
</style>
  1. 实现注册页面, 点击去登录跳转效果, 在src/views/register/index.vue中, 找到对应标签绑定点击事件跳转路由页面

<el-link type="info" @click="$router.push('/login')">去登录</el-link>
  1. 实现登录页面, 点击去注册跳转效果, 在src/views/login/index.vue中, 找到对应标签点击中跳转路由

<el-link type="info" @click="$router.push('/reg')">去注册</el-link>

默认登录页

4登录-功能实现

目标

点击登录按钮, 实现登录逻辑

讲解

核心思想: 通过表单校验, 收集用户输入内容, 调用接口带给后台验证, 返回响应结果, 前端给用户提示结果

  1. 为登录按钮绑定点击事件处理函数如下

<el-button type="primary" class="btn-login" @click="loginFn">登录</el-button>
  1. 先封装要调用的登录接口, 在src/api/index.js中

/**
 * 登录接口
 * @param {*} param0 { username: 用户名, password: 密码 }
 * @returns Promise对象
 */
export const loginAPI = ({ username, password }) => {
  return request({
    url: '/api/login',
    method: 'post',
    data: {
      username,
      password
    }
  })
}
  1. 在src/view/login/index.vue登录页面, 引入接口方法并, 实现对应事件处理函数逻辑, 校验和调用接口

methods: {
    // 登录按钮->点击事件
    async loginFn () {
      this.$refs.loginRef.validate(async valid => {
        if (!valid) return
        // 1. 发起登录的请求
        const { data: res } = await loginAPI(this.loginForm)
        // 2. 登录失败
        if (res.code !== 0) return this.$message.error(res.message)
        // 3. 登录成功
        this.$message.success(res.message)
      })
    }
  }

5 登录-结果存入vuex中

多个页面使用的数据,保存到vuex当中

目标

把登录成功, 后台返回的token字符串存到vuex中

讲解

  1. 在src/store/index.js中定义, state里的token变量, 以及更新token的 updateToken

mutation 函数: 想要给state赋值,必须得用mutation

export default new Vuex.Store({
  state: {
    // 1. 用来存储登录成功之后,得到的 token
    token: ''
  },
  mutations: {
    // 2. 更新 token 的 mutation 函数
    updateToken(state, newToken) {
      state.token = newToken
    }
  }
})

  1. 在src/views/login/index.vue中, 在成功后, 调用vuex里的mutations方法

可以直接调用 / 映射调用

使用mutations 有几种方式?

两种:可以直接调用 / 映射调用

import { mapMutations } from 'vuex'
export default {
  // ...其他
  methods: {
    // 把updateToken映射到当前方法中,映射到组件中
    ...mapMutations(['updateToken']),
    // 登录按钮->点击事件
    async loginFn () {
      this.$refs.loginRef.validate(async valid => {
        if (!valid) return
        // 1. 发起登录的请求
        const { data: res } = await loginAPI(this.loginForm)
        // 2. 登录失败
        if (res.code !== 0) return this.$message.error(res.message)
        // 3. 登录成功
        this.$message.success(res.message)
        // 4. 保存到vuex中
        this.updateToken(res.token)
      })
    }
  }
}

查看是否保存成功

1登录后获取token, 保存vuex的代码执行思路是?

在登录接口返回响应成功后, 提取后台返回的token字符串, 调用mutations方法, 把值暂存到vuex的state内变量上, 但是仅仅在内存中, 刷新后state里token变量会变成空字符串(又相当于没登录一样)

6持久化存储vuex

vuex 只是内存里边的变量,刷新就没有了

因为一刷新,所有的代码都要重新执行,token又回归了

目标

刷新vuex的值会回归初始化, 如果在保存到vuex时, 它能自动保存到浏览器本地, 默认从浏览器本地取呢?

讲解

自己写localStorage.setItem等 需要一个个写, 很麻烦

这里介绍一个vuex的插件包叫做

vuex-persistedstate@3.2.1

版本(配合vue2使用, 默认最新版是配合vue3使用)

  1. 下载此包到当前工程中

yarn add vuex-persistedstate@3.2.1
  1. 在src/store/index.js中, 导入并配置

import Vue from 'vue'
import Vuex from 'vuex'

+ import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex)
export default new Vuex.Store({
  state: {
    // 1. 用来存储登录成功之后,得到的 token
    token: ''
  },
  mutations: {
    // 2. 更新 token 的 mutation 函数
    updateToken (state, newToken) {
      state.token = newToken
    }
  },
  // 配置为 vuex 的插件
+  plugins: [createPersistedState()]
})
  1. 这次再来重新登录, 查看浏览器调试工具vuex和浏览器本地存储位置, 是否自动同步进入

  1. 刷新网页看调试工具里vuex的默认值确实从本地取出了默认值, 保证了vuex的持久化

小结

  1. vuex为何要做持久化?

答案:

  • vuex运行时的值保存在内存里, 如果刷新vuex的变量会变成初始化的,所以让他的默认初始化的值从本地取, 当有人赋值给vuex也同步覆盖式保存到本地一份

7登录-跳转布局页

目标

完成主页的标签和样式以及路由, 然后登录成功跳转

一般都有个框(layout ), 来进行布局。 中间是正常内容显示

讲解

  1. 新建页面组件src/views/layout/index.vue, layout布局页面(它右下角包含的是主页), 直接根据需求画面,

==直接复制标签和样式==

<el-container>
  <el-header>Header</el-header>
  <el-container>
    <el-aside width="200px">Aside</el-aside>
    <el-main>Main</el-main>
  </el-container>
</el-container>
头上: 两个导航栏项

代码:

<template>
  <el-container class="main-container">
    <!-- 头部区域 -->
    <el-header>
      <!-- 左侧的 logo -->
      <img src="../../assets/images/logo.png" alt="" />
      <!-- 右侧的菜单 -->
      <el-menu
        class="el-menu-top"
        mode="horizontal"
        background-color="#23262E"
        text-color="#fff"
        active-text-color="#409EFF"
      >
        <el-submenu index="1">
          <template slot="title">
            <!-- 头像 -->
            <img src="../../assets/images/logo.png" alt="" class="avatar" />
            <span>个人中心</span>
          </template>
          <el-menu-item index="1-1"><i class="el-icon-s-operation"></i>基本资料</el-menu-item>
          <el-menu-item index="1-2"><i class="el-icon-camera"></i>更换头像</el-menu-item>
          <el-menu-item index="1-3"><i class="el-icon-key"></i>重置密码</el-menu-item>
        </el-submenu>
        <el-menu-item index="2"><i class="el-icon-switch-button"></i>退出</el-menu-item>
      </el-menu>
    </el-header>
    <el-container>
      <!-- 侧边栏区域 -->
      <el-aside width="200px">Aside</el-aside>
      <el-container>
        <!-- 页面主体区域 -->
        <el-main>
          Main.vue后台主页
        </el-main>
        <!-- 底部 footer 区域 -->
        <el-footer>© www.itheima.com - 黑马程序员</el-footer>
      </el-container>
    </el-container>
  </el-container>
</template>
<script>
export default {
  name: 'my-layout'
}
</script>
<style lang="less" scoped>
.main-container {
  height: 100%;
  .el-header,
  .el-aside {
    background-color: #23262e;
  }
  .el-header {
    padding: 0;
    display: flex;
    justify-content: space-between;
  }
  .el-main {
    overflow-y: scroll;
    height: 0;
    background-color: #F2F2F2;
  }
  .el-footer {
    background-color: #eee;
    font-size: 12px;
    display: flex;
    justify-content: center;
    align-items: center;
  }
}
.avatar {
  border-radius: 50%;
  width: 35px;
  height: 35px;
  background-color: #fff;
  margin-right: 10px;
  object-fit: cover;
}
</style>
  1. 在src/router/index.js路由中配置规则和组件

{
    path: '/',
    component: () => import('@/views/layout')
}

  1. 在src/views/login/index.vue登录页面, 登录成功后跳转到主页

// 登录成功之后,跳转到后台主页
this.$router.push('/')

8退出登录

目标

完成退出登录的功能, 和业务学习

讲解

核心思想: 退出登录就是清除vuex和本地所有缓存的值, 然后页面强制切换到登录页面

  1. 退出登录按钮, 点击事件绑定

<el-menu-item index="2" @click="logoutFn"><i class="el-icon-switch-button"></i>退出</el-menu-item>
  1. 实现对应事件处理函数和提示

methods: {
    logoutFn () {
      // 询问用户是否退出登录
      this.$confirm('您确认退出登录吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      })
        .then(() => {
          // TODO:执行退出登录的操作
        })
        .catch((err) => err)
    }
  }
  1. 执行退出逻辑代码

.then(() => {
    // TODO:执行退出登录的操作
    // 1. 清空 token
    this.$store.commit('updateToken', '')
    // 2. 跳转到登录页面
    this.$router.push('/login')
})

  • 退出登录的业务逻辑是什么?

答案

  • 就是把vuex和本地的值清空, 然后页面强制跳转到登录页面

  • 为何退出登录不用调用后台接口?

答案

  • 因为我们采用的是前端存储token来表名用户登录的身份, 没有做单点登录(就是多个设备只能保证1个设备登录状态), 所以后端无需记录登录状态所以无接口调用

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值