登录模块
总的思路就是 登录输入账户密码。用户去登录 登录时拿回token 以及用户的信息 前端把这些信息存到本地 为接下来的请求做准备。
请求封装
这一块首先我们需要去封装一个请求 为我们后面的登录请求以及后面的各种请求做好准备。我们可以在utils文件夹里写一个js 当成我们的request.js 这样我们可以在我们的API接口文件引入它 实现接口的功能
import router from '@/router'
import Axios from 'axios'
import { Message } from 'element-ui'
import { getTimeStamp } from './auth'
const timeout = 3600 // 定义时间戳 的 超过时间
const service = Axios.create({
baseURL: process.env.VUE\_APP\_BASE\_API, // url = base url + request url
timeout: 50000
})
service.interceptors.request.use(config => {
// config 是请求的配置信息
if (store.getters.token) {
// 只有在有token的情况下 才有必要去检查时间戳 是否超时
config.headers\['Authorization'] = `Bearer ${store.getters.token}`
if (checkTimeOut()) {
// 超时了 token 过期了
// 把token删除了
store.dispatch('user/logout') // 登出操作
router.push('/login')
return Promise.reject(new Error('token 超时了'))
}
}
// 必须返回config
return config
})
// 响应拦截器
service.interceptors.response.use(response => {
const { success, message, data } = response.data
// 根据success 的值来执行
if (success) {
return data
} else {
Message.error(message) // 提示错误 进入catch
return Promise.reject(new Error(message))
}
}, error => {
// error 信息里面 的 response
// 处理token 过期
if (error.response?.data?.code === 10002) {
// 当满足条件的时候 表示后端告诉我 token超时了
// token 过期了
store.dispatch('user/logout') // 需要先删除token 才能 跳到登陆页
router.push('/login')
} else {
Message.error(error.message)
}
return Promise.reject(error) // 返回执行错误 让当前的执行链跳出成功 直接进入catch
})
// 定义是否超时
// 超时逻辑 当前时间 - 缓存中的时间 是否大于时间差
function checkTimeOut() {
var currentTime = Date.now() // 当前时间戳
var timeStamp = getTimeStamp() // 缓存的时间戳
return (currentTime - timeStamp) / 1000 > timeout
}
export default service
然后我们可以在API文件的登录接口去引入这个请求js 文件 然后这个去处理我们的请求 比如 我们可以写一个user.js 里面放着关于user的接口
import request from '@/utils/request'
export function login(data) {
// 返回一个promise 对象
return request({
url: '/sys/login',
method: 'post',
data
})
}
export function getUserInfo(token) {
console.log('fdsdfsdfs')
return request({
url: '/sys/profile',
method: 'post'
})
}
export function logout() {
return request({
url: '/vue-admin-template/user/logout',
method: 'post'
})
}
// 获取用户信息 现在写它 完全是为了显示头像
export function getUserDetailBy(id) {
return request({
url: `/sys/user/${id}`
})
}
到这里 我们的登录模块的登录接口已经实现了。接下来就是请求回来的数据的处理了。一般登录接口我们后端会给我们返回一个token 以及当前用户的信息 我们需要把这些信息保存到我们的本地 比如存到vuex. 或者用 localStorage sessionStorage cookie,都是可以的。 我这边封装了一个js去设置token 删除token等等。
import Cookies from 'js-cookie'
const TokenKey = 'qin_admin_template_token' // 设置一个独一无二的key
const timeKey = 'qin-time-key' // 设置一个独一无二的key
export function getToken() {
return Cookies.get(TokenKey)
}
export function setToken(token) {
console.log('settoken', token)
return Cookies.set(TokenKey, token)
}
export function removeToken() {
return Cookies.remove(TokenKey)
}
// 获取时间戳
export function getTimeStamp() {
return Cookies.get(timeKey)
}
// 读取时间戳
export function setTimeStamp() {
return Cookies.set(timeKey, Date.now())
}
然后为在vuex里面的登录的时候调用这些方法。注意 我的登录接口是在vuex model下面的user.js 去 实现的 所以为在登录页面的时候 需要调用这个action 去触发登录接口。然后才去操作数据的。而不是在登录页面去操作的。
import { getToken, setToken, removeToken, setTimeStamp } from '@/utils/auth'
import { login, getUserInfo, getUserDetailBy } from '@/api/user'
import { resetRouter } from '@/router'
const state = {
token: getToken(), // 设置token为共享状态 初始化vuex的时候 就先从缓存中读取
userInfo: {} // 这里定一个空对象
}
const mutations = {
setToken(state, token) {
state.token = token // 设置token
// 同步给缓存 实现双向同步 持久化
setToken(token)
},
removeToken(state) {
state.token = null // 将vuex的数据置空
removeToken()
},
setUserInfo(state, result) {
state.userInfo = result // 这样是响应式
// state.userInfo = { ...result } // 这样是响应式 属于浅拷贝
// state.userInfo['username'] = result // 这样不是响应式
},
removeUserInfo(state) {
state.userInfo = {}
}
}
const actions = {
async login(context, data) {
const result = await login(data) // 拿到token
// axios 默认加了一层data 在响应请求那里做了处理
if (result) {
// 如果为true 表示登陆成功
context.commit('setToken', result)
// 设置时间戳
setTimeStamp()
}
},
async getUserInfo(context) {
const result = await getUserInfo()
const baseInfo = await getUserDetailBy(result.userId) // 获取头像
const obj = { ...result, ...baseInfo } // 将两个接口的结构合并
context.commit('setUserInfo', obj) // 提交数据
return result // 为什么return 是因为这里是给我们后期做权限的时候 留下的伏笔
},
// 退出登陆
logout(context) {
context.commit('removeToken') // 删除token
context.commit('removeUserInfo') // 删除用户资料
// 重置路由
resetRouter()
// 设置权限模块下的路由为初始路由
// vuex 子模块子怎么调用子模块下的 action 都没有加锁的情况下 可以随意调用
// 不加命名空间的情况下 所有的mutations 和action 都是挂在全局上的 所有可以直接调用
// 但是加了命名空间的子模块 怎么调用另一个加了命名空间的子模块的mutations
// 加了命名空间的context 指的不是全局的context
// 三个参数 mutations名称 载荷payload 第三个参数是 { root:true } 调用根级的mutation或者 action
// 注意怎么看怎么写
context.commit('permission/setRoutes', [], { root: true })
}
}
export default {
namespaced: true,
state,
mutations,
actions
}
登录页的代码
methods: {
...mapActions(['user/login']),
// 登录事件
handleLogin()
this.$refs.loginForm.validate(async isOk => {
if (isOk) {
try {
this.loading = true
await this['user/login'](this.loginForm)
// 登陆成功
// async标记的函数实际上是一个promise 对象
// await 下面的代码 都是成功执行的代码
this.$router.push('/')
} catch (error) {
console.log(error)
} finally {
// 不论执行try 还是 catch 都会执行这里的代码
this.loading = false
}
}
})
}
}