二、从登陆开始

基本登陆

使用路由导航守卫结合 token 处理视图访问拦截

参考链接:

title: 基于 Token 的验证流程 participant 客户端 as client participant 服务器 as server client -> server: 用户名+密码 server --> client: Token 令牌 note over client: 将 Token 存储到本地

src/components/login/script.js 中登陆成功,将服务器下发的 token 保存到本地存储:

  1. // 其它代码...
  2. handleLogin () {
  3. axios.post('http://localhost:8888/api/private/v1/login', this.loginForm)
  4. .then(res => {
  5. const {data, meta} = res.data
  6. const {msg, status} = meta
  7. if (status === 200) {
  8. // 将凭证放到到本地存储(会在路由守卫那里使用)
  9. window.localStorage.setItem('token', data.token)
  10. // 跳转到首页
  11. this.$router.push('/')
  12. } else if (status === 400) {
  13. window.alert(msg)
  14. }
  15. })
  16. }
  17. // 其它代码...

src/router/index.js 中,添加全局路由导航守卫对非登陆请求进行登陆权限判定:

  1. // 其它代码...
  2. const router = new Router({
  3. // ...
  4. })
  5. router.beforeEach((to, from, next) => {
  6. const {path} = to
  7. if (path !== '/login') { // 如果请求的不是 /login 则校验登陆状态
  8. const token = window.localStorage.getItem('token')
  9. if (!token) { // 如果没有 token 则让其跳转到 /login
  10. next('/login')
  11. } else { // 有 token,让其通过
  12. next()
  13. }
  14. } else {
  15. // 如果用户请求的就是 /login 则直接调用 next() 放行
  16. next()
  17. }
  18. })
  19. export default router

导入 ElementUI

参考链接:

安装依赖:

  1. # 或者 npm install element-ui
  2. yarn add element-ui

src/main.js 中加载并配置:

  1. import Vue from 'vue'
  2. import App from './App'
  3. import router from './router'
  4. import ElementUI from 'element-ui'
  5. import 'element-ui/lib/theme-chalk/index.css'
  6. Vue.use(ElementUI)
  7. Vue.config.productionTip = false
  8. /* eslint-disable no-new */
  9. new Vue({
  10. el: '#app',
  11. router,
  12. components: { App },
  13. template: '<App/>'
  14. })

布局登陆组件

参考链接:

参考 Element 的 Form表单组件文档,我们先来个最简单的登陆表单。

src/components/login/template.html 文件内容替换为:

  1. <div>
  2. <el-form :model="loginForm">
  3. <el-form-item>
  4. <el-input v-model="loginForm.username" placeholder="用户名"></el-input>
  5. </el-form-item>
  6. <el-form-item>
  7. <el-input type="password" v-model="loginForm.password" placeholder="密码"></el-input>
  8. </el-form-item>
  9. <el-form-item>
  10. <el-button type="primary" @click="handleLogin">登陆</el-button>
  11. </el-form-item>
  12. </el-form>
  13. </div>

接下来我们开始调整登陆页面的样式。

首先把公共样式写到 src/assets/css/index.css 文件中。

  1. html, body, #app {
  2. width: 100%;
  3. height: 100%;
  4. }
  5. body {
  6. margin: 0;
  7. padding: 0;
  8. }

然后在 src/main.js 加载:

  1. // 代码略...
  2. // 引入我们的公共样式
  3. import './assets/css/index.css'
  4. // 代码略...

最后,我们分别调整登陆组件的HTML结构、及CSS样式:

src/components/login/template.html:

  1. <div class="login-wrap">
  2. <div class="login-form">
  3. <el-form :model="loginForm">
  4. <el-form-item>
  5. <el-input v-model="loginForm.username" placeholder="用户名"></el-input>
  6. </el-form-item>
  7. <el-form-item>
  8. <el-input type="password" v-model="loginForm.password" placeholder="密码"></el-input>
  9. </el-form-item>
  10. <el-form-item>
  11. <el-button class="login-submit" type="primary" @click="handleLogin">登陆</el-button>
  12. </el-form-item>
  13. </el-form>
  14. </div>
  15. </div>

src/components/login/style.css:

  1. .login-wrap {
  2. width: 100%;
  3. height: 100%;
  4. background-color: #2d434c;
  5. display: flex;
  6. justify-content: center;
  7. align-items: center;
  8. }
  9. .login-wrap .login-form {
  10. background-color: #fff;
  11. padding: 50px 50px 20px 50px;
  12. width: 25%;
  13. }
  14. .login-wrap .login-form .login-submit {
  15. width: 100%;
  16. }

为登陆组件加入表单验证

  1. 为表单中需要验证的表单项 el-form-item 声明 prop 属性,属性值给一个有意义的名称
  1. <div class="login-wrap">
  2. <div class="login-form">
  3. <el-form :model="loginForm">
  4. <el-form-item prop="username">
  5. <el-input v-model="loginForm.username" placeholder="用户名"></el-input>
  6. </el-form-item>
  7. <el-form-item prop="password">
  8. <el-input type="password" v-model="loginForm.password" placeholder="密码"></el-input>
  9. </el-form-item>
  10. <el-form-item>
  11. <el-button class="login-submit" type="primary" @click="handleLogin">登陆</el-button>
  12. </el-form-item>
  13. </el-form>
  14. </div>
  15. </div>
  1. 在组件的 data 中增加一个属性对象 loginFormRule 配置 prop 字段属性的验证规则
  1. import axios from 'axios'
  2. export default {
  3. // ... 代码略
  4. data () {
  5. return {
  6. loginForm: {
  7. username: '',
  8. password: ''
  9. },
  10. loginFormRule: {
  11. username: [
  12. { required: true, message: '请输入用户名', trigger: 'blur' }
  13. ],
  14. password: [
  15. { required: true, message: '请输入密码', trigger: 'blur' }
  16. ]
  17. }
  18. }
  19. },
  20. // ... 代码略
  21. }
  1. 在登陆组件的模板中为 el-form 表单组件绑定 rules 属性到 data 中定义的 loginFormRule
  1. <div class="login-wrap">
  2. <div class="login-form">
  3. <el-form :model="loginForm" :rules="loginFormRule">
  4. ... 代码略
  1. 测试验证是否成功

我们在前面做的 1 - 4 步已经完成了基本的表单验证功能。接下来我们要在表单提交登陆发起请求的时候使用 JavaScript 校验是否通过表单验证,表单验证通过再去提交表单。

首先为登陆组件模板的 el-form 组件声明 ref 属性,属性值给一个有意义的名字。

  1. <div class="login-wrap">
  2. <div class="login-form">
  3. <el-form ref="form" :model="loginForm" :rules="loginFormRule">
  4. ... 代码略

然后在表单提交的时候调用 JavaScript 判断表单验证是否通过,通过再发起登陆请求。

  1. import axios from 'axios'
  2. export default {
  3. // ... 代码略
  4. methods: {
  5. handleLogin () {
  6. // ['form'] 中的 form 就是 el-form 标签 ref 属性值
  7. this.$refs['form'].validate((valid) => {
  8. if (!valid) {
  9. return
  10. }
  11. axios.post('http://localhost:8888/api/private/v1/login', this.loginForm)
  12. .then(res => {
  13. const {data, meta} = res.data
  14. const {msg, status} = meta
  15. if (status === 200) {
  16. // 将凭证放到到本地存储(会在路由守卫那里使用)
  17. window.localStorage.setItem('token', data.token)
  18. // 跳转到首页
  19. this.$router.push('/')
  20. } else if (status === 400) {
  21. window.alert(msg)
  22. }
  23. })
  24. })
  25. }
  26. }
  27. // ... 代码略
  28. }

使用 Message 消息提示给出操作反馈

无论登陆成功还是登陆失败,我们都应该给出用户一个友好的提示。这里我们可以使用 Element 提供的 Message 消息提示 组件来很方便的实现。

  1. import axios from 'axios'
  2. export default {
  3. // ... 代码略
  4. methods: {
  5. handleLogin () {
  6. // ['form'] 中的 form 就是 el-form 标签 ref 属性值
  7. this.$refs['form'].validate((valid) => {
  8. if (!valid) {
  9. return
  10. }
  11. axios.post('http://localhost:8888/api/private/v1/login', this.loginForm)
  12. .then(res => {
  13. const {data, meta} = res.data
  14. const {msg, status} = meta
  15. if (status === 200) {
  16. // 将凭证放到到本地存储(会在路由守卫那里使用)
  17. window.localStorage.setItem('token', data.token)
  18. // 跳转到首页
  19. this.$router.push('/')
  20. this.$message({
  21. message: '登陆成功',
  22. type: 'success'
  23. })
  24. } else if (status === 400) {
  25. this.$message.error(msg)
  26. }
  27. })
  28. })
  29. }
  30. }
  31. // ... 代码略
  32. }