当前位置: 首页>后端>正文

vue2+Element UI 登录篇

实现思路:
登录:当用户填写完账号和密码后向服务端验证是否正确,验证通过之后,服务端会返回一个token,拿到token之后(我会将这个token存贮到cookie中,保证刷新页面后能记住用户登录状态),后续所有请求都需要携带token,前端会根据token再去拉取一个 user_info 的接口来获取用户的详细信息(如用户权限,用户名等等信息)。

权限验证:通过token获取用户对应的 role,动态根据用户的 role 算出其对应有权限的路由,通过 router.addRoutes 动态挂载这些路由。


vue2+Element UI 登录篇,第1张
1680969058572.jpg

引入项目后通过git checkout -b login,创建登录子分支,通过git branch查看项目所有分支


vue2+Element UI 登录篇,第2张

实现步骤:
一.创建登录组件,并且通过路由的形式将登录组件渲染到App跟组件中,并通过路由重定向让用户在访问根路径时,自动跳转到登录组件。

在项目中src文件下新建views文件夹-login文件夹,新建index.vue文件,编写登录页面。输入vbase生成页面模板(需要提前安装 VSCode快捷插件 Vue VSCode Snippets)

<template>
  <div>登录组件</div>
</template>

<script>
export default {
  name: 'Login'
}
</script>
<style lang="less" scoped>

</style>

新建router文件夹,新建index.js文件,导入登录组件,新建路由规则和页面重定向,之后在App根组件中放一个路由占位符。

路由index.js代码:

import Vue from 'vue'
import Router from 'vue-router'
// 导入登录组件
import Login from '../views/login/Login.vue'
Vue.use(Router)
const router = new Router({
  routes: [
    // 当路径是/时,重定向路径/login
    { path: '/', redirect: '/login' },
    // 当路径是/login时,展示组件Login
    { path: '/login', component: Login }
  ]
})
export default router

App.vue代码:

<template>
  <div id="app">
    <!-- 路由占位符 -->
    <router-view></router-view>
  </div>
</template>

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

<style>
</style>

二.登录组件布局
登录容器中编写表单样式,可以去Element UI官网搜索表单样式,或者elf快速生成表单样式(需要先安装 Element UI快捷插件 Element UI Snippets)。然后运行npm i element-ui -S命令

页面样式lang="less",要安装less和less-laoder的开发依赖


vue2+Element UI 登录篇,第3张
1681030395199.jpg

在assets-css文件夹中,新建global.css,写下全局样式,在入口文件中导入全局样式。

/* 全局样式 */
html,
body,
#app{
    height: 100%;
    padding: 0;
    margin: 0;
}
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import './plugins/element.js'
// 导入全局样式
import './assets/css/global.css'

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

在plugins文件夹中,新建element.js,按需引入Element UI样式全局注册 element 组件库。

import Vue from 'vue'
import { Button } from 'element-ui'
import { Form,FormItem } from 'element-ui'
import { Input } from 'element-ui'
//全局注册 element 组件库
Vue.use(Form)
Vue.use(FormItem)
Vue.use(Input)
Vue.use(Button)

在全局入口main.js中导入element-ui

import Vue from 'vue'
import App from './App.vue'
import router from './router'
// 导入element-ui
import './plugins/element.js'
// 导入全局样式
import './assets/css/global.css'

Vue.config.productionTip = false

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

Login.vue样式代码如下:

<template>
  <!-- 登录容器 -->
  <div class="login_container">
    <!-- 登录盒子 -->
    <div class="login_box">
      <!-- 头像盒子 -->
      <div class="avatar_box">
        <img src="../../assets/logo.png" alt="" />
      </div>
      <!-- 表单 -->
      <el-form label-width="0px" class="login_form">
        <!-- 用户名 -->
        <el-form-item>
          <el-input></el-input>
        </el-form-item>
        <!-- 密码 -->
        <el-form-item>
          <el-input></el-input>
        </el-form-item>
        <!-- 按钮 -->
        <el-form-item class="btns">
          <el-button type="primary">登录</el-button>
          <el-button type="info">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

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

<style lang="less" scoped>
  .login_container {
    // 登录组件背景色
    background-color: #2b4b6b;
    height: 100%;
    // 登录盒子设置
    .login_box {
      width: 450px;
      height: 300px;
      background-color: #fff;
      border-radius: 3px;
      // 盒子水平垂直居中设置
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      // 头像盒子
      .avatar_box {
        width: 130px;
        height: 130px;
        border: 1px solid #eee;
        border-radius: 50%;
        padding: 10px;
        box-shadow: 0 0 10px #ddd;
        position: absolute;
        left: 50%;
        transform: translate(-50%, -50%);
        background-color: #fff;
        img {
          width: 100%;
          height: 100%;
          border-radius: 50%;
          background-color: #eee;
        }
      }
      .login_form {
        position: absolute;
        bottom: 0;
        width: 100%;
        padding: 0 20px;
        box-sizing: border-box;
        .btns {
          display: flex;
          justify-content: flex-end;
        }
      }
    }
  }
</style>

三.表单组件的数据绑定
给登录表单绑定数据给el-form 通过:model进行属性绑定,绑定的值是一个数据对象,把数据对象在data中做定义,在为每一个具体表单项通过v-model双向绑定到每个数据对象的属性中。
Login.vue 代码

<template>
  <!-- 登录容器 -->
  <div class="login_container">
    <!-- 登录盒子 -->
    <div class="login_box">
      <!-- 头像盒子 -->
      <div class="avatar_box">
        <img src="../../assets/logo.png" alt="" />
      </div>
      <!-- 表单 -->
      <el-form label-width="0px" class="login_form" :model="loginForm">
        <!-- 用户名 -->
        <el-form-item>
          <el-input v-model="loginForm.username" placeholder="请输入用户名"></el-input>
        </el-form-item>
        <!-- 密码 -->
        <el-form-item>
          <el-input v-model="loginForm.password" type="password" placeholder="请输入密码"></el-input>
        </el-form-item>
        <!-- 按钮 -->
        <el-form-item class="btns">
          <el-button type="primary">登录</el-button>
          <el-button type="info">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Login',
  data() {
    return {
      // 通过v-model双向绑定登录表单数据
      loginForm: {
        username: '',
        password: ''
      }
    }
  }
}
</script>

<style lang="less" scoped>
  .login_container {
    // 登录组件背景色
    background-color: #2b4b6b;
    height: 100%;
    // 登录盒子设置
    .login_box {
      width: 450px;
      height: 300px;
      background-color: #fff;
      border-radius: 3px;
      // 盒子水平垂直居中设置
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      // 头像盒子
      .avatar_box {
        width: 130px;
        height: 130px;
        border: 1px solid #eee;
        border-radius: 50%;
        padding: 10px;
        box-shadow: 0 0 10px #ddd;
        position: absolute;
        left: 50%;
        transform: translate(-50%, -50%);
        background-color: #fff;
        img {
          width: 100%;
          height: 100%;
          border-radius: 50%;
          background-color: #eee;
        }
      }
      .login_form {
        position: absolute;
        bottom: 0;
        width: 100%;
        padding: 0 20px;
        box-sizing: border-box;
        .btns {
          display: flex;
          justify-content: flex-end;
        }
      }
    }
  }
</style>

四.登录表单验证
实现思路:输入用户名密码的时候,鼠标离开文本框时。对填写的数据进行验证
步骤:(Form 组件提供了表单验证的功能,详情参见组件 | Element)
1.在el-form上面绑定一个 rules 属性,值是约定的rules验证对象。
2.在data中定义rules对象,这里包含验证规则,一般是数组形式。
3.将 Form-Item 的 prop 属性设置为需校验的验证规则字段名。
代码:

<template>
  <!-- 登录容器 -->
  <div class="login_container">
    <!-- 登录盒子 -->
    <div class="login_box">
      <!-- 头像盒子 -->
      <div class="avatar_box">
        <img src="../../assets/logo.png" alt="" />
      </div>
      <!-- 表单 -->
      <el-form label-width="0px" class="login_form" :model="loginForm" :rules="loginFormRules">
        <!-- 用户名 -->
        <el-form-item prop="username">
          <el-input v-model="loginForm.username" placeholder="请输入用户名"></el-input>
        </el-form-item>
        <!-- 密码 -->
        <el-form-item prop="password">
          <el-input v-model="loginForm.password" type="password" placeholder="请输入密码"></el-input>
        </el-form-item>
        <!-- 按钮 -->
        <el-form-item class="btns">
          <el-button type="primary">登录</el-button>
          <el-button type="info">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Login',
  data() {
    return {
      // 通过v-model双向绑定登录表单数据
      loginForm: {
        username: '',
        password: ''
      },
     //表单验证规则
     loginFormRules:{
        //验证用户名
        username:[
        { required: true, message: '请输入用户名', trigger: 'blur' },
        { min: 3, max: 10, message: '长度在 3 到 10 个字符', trigger: 'blur' }
        ],
        //验证密码
        password:[
        { required: true, message: '请输入密码', trigger: 'blur' },
        { min: 6, max: 15, message: '长度在 6 到 15 个字符', trigger: 'blur' }
        ]
      }
  
    }
  }
}
</script>

<style lang="less" scoped>
  .login_container {
    // 登录组件背景色
    background-color: #2b4b6b;
    height: 100%;
    // 登录盒子设置
    .login_box {
      width: 450px;
      height: 300px;
      background-color: #fff;
      border-radius: 3px;
      // 盒子水平垂直居中设置
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      // 头像盒子
      .avatar_box {
        width: 130px;
        height: 130px;
        border: 1px solid #eee;
        border-radius: 50%;
        padding: 10px;
        box-shadow: 0 0 10px #ddd;
        position: absolute;
        left: 50%;
        transform: translate(-50%, -50%);
        background-color: #fff;
        img {
          width: 100%;
          height: 100%;
          border-radius: 50%;
          background-color: #eee;
        }
      }
      .login_form {
        position: absolute;
        bottom: 0;
        width: 100%;
        padding: 0 20px;
        box-sizing: border-box;
        .btns {
          display: flex;
          justify-content: flex-end;
        }
      }
    }
  }
</style>

五.表单内容重置
实现思路:通过表单的实例对象。其中resetFields方法重置表单为初始值并移除校验结果。
步骤:
1.获取表单对象,给el-from 添加一个ref引用,它的值就是表单的实例对象
2.给重置按钮添加一个点击方法
3.在方法中通过this.$refs.引用对象.resetFields()重置
代码:

<template>
  <!-- 登录容器 -->
  <div class="login_container">
    <!-- 登录盒子 -->
    <div class="login_box">
      <!-- 头像盒子 -->
      <div class="avatar_box">
        <img src="../../assets/logo.png" alt="" />
      </div>
      <!-- 表单 -->
      <el-form label-width="0px" class="login_form" :model="loginForm" :rules="loginFormRules" ref="loginFormRef">
        <!-- 用户名 -->
        <el-form-item prop="username">
          <el-input v-model="loginForm.username" placeholder="请输入用户名"></el-input>
        </el-form-item>
        <!-- 密码 -->
        <el-form-item prop="password">
          <el-input
            v-model="loginForm.password"
            type="password"
            placeholder="请输入密码"
          ></el-input>
        </el-form-item>
        <!-- 按钮 -->
        <el-form-item class="btns">
          <el-button type="primary">登录</el-button>
          <el-button type="info" @click="resetLoginForm">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Login',
  data() {
    return {
      // 通过v-model双向绑定登录表单数据
      loginForm: {
        username: '',
        password: ''
      },
      //表单验证规则
      loginFormRules: {
        //验证用户名
        username: [
          { required: true, message: '请输入用户名', trigger: 'blur' },
          { min: 3, max: 10, message: '长度在 3 到 10 个字符', trigger: 'blur' }
        ],
        //验证密码
        password: [
          { required: true, message: '请输入密码', trigger: 'blur' },
          { min: 6, max: 15, message: '长度在 6 到 15 个字符', trigger: 'blur' }
        ]
      }
    }
  },
  methods:{
    // 点击重置清空表单方法
    resetLoginForm(){
      this.$refs.loginFormRef.resetFields()
    }
  }
}
</script>

<style lang="less" scoped>
  .login_container {
    // 登录组件背景色
    background-color: #2b4b6b;
    height: 100%;
    // 登录盒子设置
    .login_box {
      width: 450px;
      height: 300px;
      background-color: #fff;
      border-radius: 3px;
      // 盒子水平垂直居中设置
      position: absolute;
      left: 50%;
      top: 50%;
      transform: translate(-50%, -50%);
      // 头像盒子
      .avatar_box {
        width: 130px;
        height: 130px;
        border: 1px solid #eee;
        border-radius: 50%;
        padding: 10px;
        box-shadow: 0 0 10px #ddd;
        position: absolute;
        left: 50%;
        transform: translate(-50%, -50%);
        background-color: #fff;
        img {
          width: 100%;
          height: 100%;
          border-radius: 50%;
          background-color: #eee;
        }
      }
      .login_form {
        position: absolute;
        bottom: 0;
        width: 100%;
        padding: 0 20px;
        box-sizing: border-box;
        .btns {
          display: flex;
          justify-content: flex-end;
        }
      }
    }
  }
</style>

六.登录预验证
实现思路:先获取表单的引用对象,之后调用表单的validate函数进行表单预校验,接收一个回调函数。里面的第一个形参是一个布尔值,通过布尔值可以判断是否通过校验
步骤:
1.获取表单对象,给el-from 添加一个ref引用,它的值就是表单的实例对象
2.给登录按钮添加一个点击方法
3.在方法中通过this.$refs.引用对象.validate()校验
代码:

  <!-- 按钮 -->
        <el-form-item class="btns">
          <el-button type="primary" @click="login">登录</el-button>
          <el-button type="info" @click="resetLoginForm">重置</el-button>
        </el-form-item>

 login(){
      // 登录预验证 返回布尔值
      this.$refs.loginFormRef.validate(valid=>{
        console.log(valid);
      })
    }

七.发起登录请求,并实现页面跳转。
实现思路:发起登录请求,拿到返回数据,根据数据信息进行判断登录,登录成功后将返回的token保存到客户端的sessionStorage中,并跳转页面。
步骤:
1.发送请求需要先安装axios,新建util文件夹新建一个request.js文件。用来封装请求和相应拦截。

npm install axios

request.js

// 引入axios
import axios from 'axios'
// 创建一个axios实例
const http = axios.create({
// 基础路径,发请求的时候会有api
  baseURL: '/api',
  // 请求超时时间
  timeout: 2000
});

// request请求拦截器
http.interceptors.request.use(
  config => {
  // config,配置对象,对象里面有一个属性很重要,header请求头
    return config;
  },
  error => {
    return Promise.error(error);
  }
);

// response响应拦截
http.interceptors.response.use(
  response => {
    // do something
    return Promise.resolve(response);
  },
  error => {
    // do something
    return Promise.reject(error);
  }
)

// 暴漏axios
export default http

2.新建api文件夹,在api文件夹下建立login.js文件,定义一个接口,通过axios实例的get方法得到对象数据
login.js接口代码

// 引入封装的axios
import http from '../util/request'

// 定义一个请求登录数据的接口 loginForm是data中包含用户名密码的数据,为了获取输入的数据
export const getLogin = loginForm => {
// 调用axios实例的get方法(url,参数),返回一个promise对象,
// get方法{data: loginForm}形式,如果是post请求这里的参数只需要写loginForm
  return http.get('/login', {data: loginForm})
}

3.新建mock文件夹,新建mock.js文件,通过Mock.mock()方法用来拦截请求,新建mockServerData-login.js文件用来存放模拟的后台数据。最后还要在程序入口main.js中引入mock.
mock.js

// 引入mock
import Mock from 'mockjs'
// 引入模拟的login页面数据
import {responeSuccess, responeFail, userDataList } from './mockServerData/login'
// 定义mock请求拦截 路径 请求方式 函数
Mock.mock('/api/login', 'get', function(options) {
  // 请求参数 用户输入的用户名密码
  const respone = JSON.parse(options.body)
  const username = respone.username
  const password = respone.password
  // 模拟的后台用户信息数据
  const userInfos = userDataList.userinfo
  for (const user of userInfos) {
    console.log('userName:' + user.username);
    console.log('password:' + user.password);
    // 判断输入的信息和后台的信息一致
    if (username === user.username && password === user.password) {
      // 这里返回的成功代码信息也是在后台模拟的
      return responeSuccess
    }
  }
  console.log('fail')
  // 这里返回的失败代码信息也是在后台模拟的
  return responeFail
})

mockServerData-login.js 模拟的后台数据

// 登录成功状态码
export const responeSuccess = {
  data: {
    meta: {
      status: 200,
      message: 'success',
      token: '000111222333444555666'
    }
  }
};
// 登录用户信息
export const userDataList = {
  userinfo: [{
    username: 'admin',
    password: '1234567890',
    token: '000111222333444555666'
  }, {
    username: 'editor',
    password: '0000000000',
    token: '145145145123123123111'
  }]
};

// 登录失败状态码
export const responeFail = {
  data: {
    meta: {
      status: 500,
      message: 'fail'
    }
  }
};

入口文件main.js

// 引入mock文件
import './mock/mock'

4.引入登录api,通过接口拿到数据状态进行判断

<script>
// 引入登录api
import { getLogin } from '../../api/login.js'
export default {
  name: 'Login',
  data() {
    return {
      // 通过v-model双向绑定登录表单数据
      loginForm: {
        username: '',
        password: ''
      },
      // 表单验证规则
      loginFormRules: {
        // 验证用户名
        username: [
          { required: true, message: '请输入用户名', trigger: 'blur' },
          { min: 3, max: 10, message: '长度在 3 到 10 个字符', trigger: 'blur' }
        ],
        // 验证密码
        password: [
          { required: true, message: '请输入密码', trigger: 'blur' },
          { min: 6, max: 15, message: '长度在 6 到 15 个字符', trigger: 'blur' }
        ]
      }
    }
  },
  methods: {
    // 点击重置清空表单方法
    resetLoginForm() {
      this.$refs.loginFormRef.resetFields()
    },
   login() {
      // 登录预验证 返回布尔值
      this.$refs.loginFormRef.validate(async valid => { // async异步和await配套
        if (!valid) return false
        // 发起请求,拿到服务器返回的数据,其中data是真实数据,解构出来,axios实例返回一个promise对象用await转换
        const { data: res } = await getLogin(this.loginForm)
        // 用拿到的后台状态数据进行判断登录
        if (res.data.meta.status !== 200) return this.$message.error('登录失败')
        this.$message.success('登录成功')
        // 将返回的token保存到客户端的sessionStorage中。
        window.sessionStorage.setItem('token', res.data.meta.token)
        // 跳转到后台主页
        this.$router.push('/home')
      })
    }
  }
}
</script>

5.登录成功失败的提示.$message,需要先在element.js中添加如下代码,再将文件引入到main.js中才可以。

// 导入弹窗提示组件
import { Form, FormItem, Input, Button, Message} from 'element-ui'
// 将Message挂在原型对象上便于其他组件调用
Vue.prototype.$message = Message

八.登录导航守卫的来控制访问权限
思路:拿到路由对象,挂在beforeEach的导航守卫,传一个回调函数,接受三个形参。根据to来确认访问的地址和token的值来确定是否发生页面跳转。

router文件夹-index.js文件中添加挂载路由导航守卫-登录的代码

import Vue from 'vue'
import VueRouter from 'vue-router'
// 导入登录组件
import Login from '../views/login/Login.vue'
// 导入Home组件
import Home from '../components/Home'
Vue.use(VueRouter)

const routes = [
  // 当路径是/时,重定向路径/login
  { path: '/', redirect: '/login' },
  // 当路径是/login时,展示组件Login
  { path: '/login', component: Login },
  { path: '/home', component: Home }
]

const router = new VueRouter({
  routes
})
// 挂载路由导航守卫-登录
router.beforeEach((to, from, next) => {
// to将要访问的路径
// from从哪个路径跳转来
// next函数,表示下一步放行
// 如果路径是登录页,下一步
  if (to.path === '/login') return next()
  // 获取token
  const tokenStr = window.sessionStorage.getItem('token')
  // 如果没有获取到token,返回登录页
  if (!tokenStr) return next('/login')
  // 如果存在则直接放行
  next()
})

export default router

九.退出功能实现
思路:销毁本地token,跳转到登录页
步骤:在home页,给推出按钮添加一个点击事件,在事件中添加销毁token方法

<template>
  <div>
    <el-button type="info" @click="exit">退出</el-button>
  </div>
</template>
<script>
export default {
  name: 'Home',
  methods: {
    exit() {
      // 清空token
      window.sessionStorage.clear()
      // 跳转登录页
      this.$router.push('/login')
    }
  }
}
</script>

https://www.xamrdz.com/backend/3ez1940942.html

相关文章: