# 登录模块

# 登录页面基本布局

**目标**完成登录页面的基础布局

  • 基本布局
<!-- 放置标题图片 @是设置的src目录别名-->
<div class="title-container">
  <h3 class="title">
    <img src="@/assets/common/login-logo.png" alt="">
  </h3>
</div>
  • 样式处理
/* reset element-ui css */
.login-container {
  // 设置背景图片
  background-image: url('~@/assets/common/login.jpg'); 
  // 将图片位置设置为充满整个屏幕
  background-position: center; 
}

注意: 如需要在样式表中使用**@别名的时候,需要在@前面加上一个~**符号,否则不识别

  • 设置手机号和密码的字体颜色
// 修改输入框字体颜色
$light_gray: #407ffe; 
  • 设置输入表单整体背景色
.el-form-item {
  border: 1px solid rgba(255, 255, 255, 0.1);
  background: rgba(255, 255, 255, 0.9); // 输入登录表单的背景色
  border-radius: 5px;
  color: #454545;
}
  • 设置错误信息的颜色
.el-form-item__error {
  color: #fff;
  font-size: 14px;
}
  • 设置登录按钮的样式
.login-btn {
  background: #407ffe;
  height: 64px;
  line-height: 32px;
  font-size: 24px;
}

需要给el-button 增加一个login-btn的class样式

  • 修改显示的提示文本和登录文本
<div class="tips">
  <span style="margin-right:20px;">账号: 13800000002</span>
  <span> 密码: 123456</span>
</div>

# 登录表单的校验

**目标**对登录表单进行规则校验

基础模板已经有了基础校验的代码,我们只需在此基础上修改即可

  • el-form表单校验的必要条件

image-20210216125719740

  • 登录表单验证
    • 手机号必填,并且进行格式校验
    • 密码必填,长度6-16位之间
// @/utils/validate.js
// 校验手机号
export function validMobile(str) {
  return /^1[3-9]\d{9}$/.test(str) 
}
// -------------------------------------
import { validMobile } from '@/utils/validate'
data() {
  // 自定义校验函数
  const validateMobile = (rule, value, callback) => {
    if (!validMobile(value)) {
      callback(new Error('手机格式有误'))
    } else {
      callback()
    }
  }

  return {
    loginForm: {
      mobile: '13800000002',
      password: '123456'
    },
    loginRules: {
      mobile: [
        { required: true, message: '请输入手机号', trigger: ['blur', 'change'] },
        { validator: validateMobile, trigger: ['blur', 'change'] }
      ],
      password: [
        { required: true, message: '请输入密码', trigger: ['blur', 'change'] },
        { min: 6, max: 16, message: '密码必须是6-16位', trigger: ['blur', 'change'] }
      ]
    },
    loading: false,
    passwordType: 'password',
    redirect: undefined
  }
},

# 登录基本功能

目标 实现基本的登录功能

基地址: http://ihrm-java.itheima.net/api

handleLogin() {
  this.$refs.loginForm.validate(valid => {
    if (valid) {
      this.loading = true
			// 校验通过发送登录请求
      const loginUrl = 'http://ihrm-java.itheima.net/api/sys/login'
      this.$request.post(loginUrl, this.loginForm).then(res => {
        if (res.data.code === 10000) {
            this.$router.push('/')
        }
      })
    } else {
      console.log('error submit!!')
      return false
    }
  })
}

# 封装单独的登录接口

目标 在单独请求模块中,单独封装登录接口

完成登录模块之后,我们需要对登录接口进行封装, 登录请求应该单独封装到一个模块中, 便于维护和管理

  • 封装基本的接口模块 api/user.js
import request from '@/utils/request'

export function reqLogin(data) {
  return request({
    method: 'post',
    // 这里的 /api 由于开发环境所有接口都要加, 通过baseUrl加
    url: '/sys/login', 
    data
  })
}
  • 配置基准路径 utils/request.js
import axios from 'axios'

// 创建了axios实例, 使用的是自己的配置项
const instance = axios.create({
  // 开发环境, 找 env.development, 找 VUE_APP_BASE_API 变量
  // 生产环境, 找 env.production,  找 VUE_APP_BASE_API 变量
  baseURL: process.env.VUE_APP_BASE_API, // 环境变量
  timeout: 5000 // request timeout
})

// 请求拦截器

// 响应拦截器

export default instance

注意:.env.development.env.production 文件用于配置开发环境和生产环境的基准url地址

  • 重构登录功能
import { reqLogin } from '@/api/user'

handleLogin() {
  this.$refs.loginForm.validate(valid => {
    if (valid) {
      this.loading = true
      // 发送登录请求
      reqLogin(this.loginForm).then(res => {
        if (res.data.code === 10000) {
          this.$router.push('/')
        }
      })
    } else {
      console.log('error submit!!')
      return false
    }
  })
}

# 基于vuex管理token

目标 封装获取token的登录action, 在vuex中存储token

  • 准备状态store/modules/user.js
const state = {
  token: null // token字符串
}
const mutations = {}
const actions = {}
const getters = {}

export default {
  namespaced: true,
  state,
  mutations,
  actions,
  getters
}
  • 封装 action 和 对应的 mutation store/modules/user.js
const mutations = {
  setToken(state, newToken) {
    state.token = newToken
  }
}
const actions = {
  async login(context, data) {
    const res = await reqLogin(data)
    const newToken = res.data.data
    context.commit('setToken', newToken)
    return true
  }
}
  • 页面导入并调用
import { mapActions } from 'vuex'

methods: {
    ...mapActions('user', ['login']),
    handleLogin() {
      this.$refs.loginForm.validate(valid => {
        if (valid) {
          this.loading = true
          // 校验通过发送登录请求
          this.login(this.loginForm).then(res => {
            if (res) {
              this.$router.push('/')
            }
          })
        } else {
          console.log('error submit!!')
          return false
        }
      })
    }
}

# 添加token的getters

目标 通过getters获取token信息

为了更好的让其他模块和组件更好的获取token数据,我们可以在**store/getters.js**中将token值, 添加成公共的 getters, 便于将来访问

const getters = {
  sidebar: state => state.app.sidebar,
  device: state => state.app.device,
  token: state => state.user.token
}
export default getters

# token缓存封装

目标 封装通用的token操作模块

  • 模块封装**utils/auth.js**
import Cookies from 'js-cookie'

const TokenKey = 'hrsaas_hm_75_token'

export function getToken() {
  return Cookies.get(TokenKey) || null
}

export function setToken(token) {
  return Cookies.set(TokenKey, token)
}

export function removeToken() {
  return Cookies.remove(TokenKey)
}
  • 初始化获取缓存信息
const state = {
  // 一进来优先从缓存中取
  token: getToken() // token字符串
}
  • vuex中存token时, 也同步存到cookie中
const mutations = {
  // 设置token
  setToken(state, newToken) {
    state.token = newToken
    // 设置了 token 的同时, 同步到本地cookies中
    setToken(newToken)
  }
}

# axios响应拦截器

目标: 处理响应拦截器

axios返回的数据中默认增加了一层**data的包裹**, 每次我们都要 res.data.data, 太麻烦, 所以我们需要在这里处理下。

// 响应拦截器
instance.interceptors.response.use(function(response) {
  // 对响应数据做点什么
  const res = response.data
  return res
}, function(error) {
  // 对响应错误做点什么
  return Promise.reject(error)
})

# 导航守卫处理

目标:根据token处理主页的访问权限问题 (没有token的用户, 不让你进系统的)

  • 权限拦截的流程图

image-20210216132052672

  • 功能代码实现**src/permission.js**
// 路由前置守卫
// 进行登录拦截的控制 (后面自己写)
import router from '@/router'
import store from '@/store'

const whiteList = ['/login', '/404'] // 定义白名单  所有不受权限控制的页面

// 导航前置守卫
router.beforeEach((to, from, next) => {
  const token = store.getters.token
  // 判断有无token
  if (token) {
    // 判断是不是去登录页
    if (to.path === '/login') {
      next('/') // 跳到首页
    } else {
      next() // 放行
    }
  } else {
    // 看在不在白名单中(免登陆即可访问的页面)
    if (whiteList.includes(to.path)) {
      next()
    } else {
      next('/login')
    }
  }
})