前端人力后台管理笔记(一)

47 阅读3分钟

登录模块

总的思路就是 登录输入账户密码。用户去登录 登录时拿回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
          }
        }
      })
    }
  }