# 登录模块
# 登录页面基本布局
**
目标**完成登录页面的基础布局
- 基本布局
<!-- 放置标题图片 @是设置的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表单校验的必要条件

- 登录表单验证
- 手机号必填,并且进行格式校验
- 密码必填,长度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的用户, 不让你进系统的)
- 权限拦截的流程图

- 功能代码实现**
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')
}
}
})