vue+elementui搭建后台管理界面(7 vuex和mockjs的使用)
将权限管理应用到系统,首先做好登录, 点击登录按钮后,触发以下动作
- vuex 中的 login 动作,设置 cookie
- vuex 中的 getuserinfo , 获取权限、用户名、头像等
由于目前未使用连接后端服务器,所以使用 mockjs 拦截请求并返回。
在github中查看
1 全局请求拦截
使用axios 封装好请求和响应
src/utils/request.js
import axios from 'axios'
const clearRequest = {
source: {
token: null,
cancel: null
}
}
const cancelToken = axios.CancelToken
const source = cancelToken.source()
// 创建 axios 实例
const service = axios.create({
cancelToken: source.token,
timeout: 6000, // 请求超时时间
})
// request 拦截器
service.interceptors.request.use(
config => {
config.cancelToken = clearRequest.source.token
return config
},
error => {
return Promise.reject(error)
}
)
// response 拦截器
service.interceptors.response.use(
resp => resp,
error => {
return Promise.reject(error)
}
)
export { clearRequest }
export default service
2 封装一些常用的函数
src/utils/index.js
export function param2Obj(url){
const search = url.split('?')[1]
if(!search){
return {}
}
return JSON.parse(
'{"' +
decodeURIComponent(search)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"') +
'"}'
)
}
src/utils/auth.js
import Cookies from 'js-cookie'
const tokenKey = 'X-Token'
export function getToken(){
return Cookies.get(tokenKey)
}
export function setToken(token){
return Cookies.set(tokenKey, token)
}
export function removeToken(){
return Cookies.remove(tokenKey)
}
3 编写登录api
src/api/login.js 文件
import request from '@/utils/request'
export function loginByUsernameApi(username, password){
return request({
url: '/api/auth/api-token-auth',
method: 'post',
data: {
username,
password
}
})
}
export function getUserInfoApi(token){
return request({
url: '/api/userinfo',
method: 'get',
params: {token}
})
}
export function logoutApi(){
return request({
url: '/api/auth/logout',
method: 'post'
})
}
4 mock 拦截
src/mock/index.js
import Mock from 'mockjs'
import login from './login'
// 登录相关
Mock.mock(/\/api\/auth\/api-token-auth/, 'post', login.loginByUsername)
Mock.mock(/\/api\/auth\/logout/, 'post', login.logout)
Mock.mock(/\/api\/userinfo/, 'get', login.getUserInfo)
export default Mock
src/mock/login.js
import { param2Obj } from '@/utils'
const usermap = {
admin: {
token: 'admin',
introduction: '我是超级管理员',
name: 'Super Admin',
pass: 'e10adc3949ba59abbe56e057f20f883e',
roles: ['admin']
},
developer: {
token: 'developer',
introduction: '我是开发',
name: '工程师小王',
pass: 'e10adc3949ba59abbe56e057f20f883e',
roles: ['/system', '/system/permit', '/system/permit/account']
}
}
export default {
loginByUsername: config => {
const { username, password } = JSON.parse(config.body)
console.log('loginByUsername username, password: ', username, password)
if(username === 'admin'){
if(usermap[username].pass === password){
return usermap['admin']
}else{
return []
}
}
return usermap[username]
},
getUserInfo: config => {
console.log('getUserInfo config: ', config)
const { token } = param2Obj(config.url)
let tok = false
for(let key in usermap){
if(token.indexOf(usermap[key].token) !== -1){
tok = usermap[key]
break;
}
}
return tok
},
logout: () => 'success'
}
5 vuex 的使用
src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
import permission from './modules/permissions'
import getters from './getters'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
permission,
user,
},
getters
})
export default store
src/store/modules/user.js
import { loginByUsernameApi, logoutApi, getUserInfoApi } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
const user = {
state: {
token: getToken(),
name: '',
avatar: '',
roles: []
},
mutations: {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_NAME: ( state, name) => {
state.name = name
},
SET_AVATAR: (state, avatar) => {
state.avatar = avatar
},
SET_ROLES: (state, roles) => {
state.roles = roles
}
},
actions: {
// 登入
LoginByUsername({commit}, userinfo){
const username = userinfo.username.trim()
return new Promise((resolve, reject) => {
loginByUsernameApi(username, userinfo.password)
.then(resp => {
const data = resp.data
setToken(data.token)
console.log('in LoginByUsername, setToken: ', data.token)
commit('SET_TOKEN', data.token)
resolve()
})
.catch(err => {
reject(err)
})
})
},
// 获取用户权限等
GetUserInfo({commit, state}) {
console.log('in GetUserInfo')
return new Promise((resolve, reject) => {
getUserInfoApi(state.token)
.then(resp => {
if(!resp.data){
reject('error')
}
const data = resp.data
if(data.roles && data.roles.length){
commit('SET_ROLES', data.roles)
}else {
reject('getUserInfoApi: roles must be a non-null array!')
}
if(data.name){
commit('SET_NAME', data.name)
}
if(data.avatar){
commit('SET_AVATAR', data.avatar)
}
resolve(resp)
})
.catch(err => {
reject(err)
})
})
},
// 登出
LogOut({commit, state}){
return new Promise((resolve, reject) => {
logoutApi(state.token)
.then(() => {
commit('SET_TOKEN', '')
commit('SET_ROLES', [])
removeToken()
resolve()
})
.catch(err => {
reject(err)
})
})
},
// 前端登出
FedLogOut({commit}){
return new Promise(resolve => {
commit('SET_TOKEN', '')
removeToken()
resolve()
})
}
}
}
export default user
src/store/modules/permissions.js
import { asyncRouterMap, constantRouterMap } from '@/router'
/**
* 通过 meta.role 判断是否与当前用户权限匹配
*/
function hasRoles (roles, route){
if(route.meta && route.meta.roles){
return roles.some(role => route.meta.roles.includes(role))
}else{
return true
}
}
/**
* 递归过滤异步路由表,返回符合用户角色权限的路由表
*/
function filterAsyncRouter(asyncRouterMap, roles){
const accessedRouters = asyncRouterMap.filter(route => {
// 404
if(route.path === '*'){
return true
}else if(hasRoles(roles, route)){
if(route.children && route.children.length){
route.children = filterAsyncRouter(route.children, roles)
}
return true
}
return false
})
return accessedRouters
}
// 在有权限的路由表里,查找是否有到目标path的路由
// 为了保持路由唯一性,拼接父子路由
function hasDestRoute (froute, permitRouterMap, to) {
let r = froute === '/' ? '' : froute
return permitRouterMap.some(route => {
let path = r + '/' + route.path
if (to.path.indexOf(path) !== -1) {
return true;
}
if (route.children && route.children.length) { //如果有孩子就遍历孩子
return hasDestRoute(path, route.children, to)
}
})
}
const permission = {
state: {
routers: constantRouterMap,
addRouters: [],
sidebar_routers: {},
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers,
state.routers = constantRouterMap.concat(routers)
},
SET_NOW_ROUTERS: (state, to) => {
console.log('in SET_NOW_ROUTERS')
// 由于首页重定向到 /dashboard,并且不参与权限控制,特殊处理
if(to.path === '/dashboard'){
let dashboard = state.routers.filter(v => v.path === '/' )
state.sidebar_routers = dashboard[0]
}else{
// 递归访问 accessedRouters,找到包含to 的那个路由对象,设置给 sidebar_routers
state.addRouters.forEach(e => {
if (e.children && e.children.length) {
if ( hasDestRoute(e.path, e.children, to)){
if(state.sidebar_routers.path){
// 存在 sidebar_routers 且与目标路由不同
if(state.sidebar_routers.path !== e.path){
state.sidebar_routers = e;
}
}else{
state.sidebar_routers = e;
}
}
}
})
}
}
},
actions: {
GenerateRoutes({commit}, data) {
console.log('in GenerateRoutes')
return new Promise(resolve => {
const {roles} = data
let accessedRouters
if(roles.includes('admin')){
accessedRouters = asyncRouterMap
}else{
accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
}
commit('SET_ROUTERS', accessedRouters)
resolve()
})
},
GenSidebarRoutes({commit}, data) {
console.log('in GenSidebarRoutes')
return new Promise(resolve => {
commit('SET_NOW_ROUTERS', data)
resolve()
})
}
}
}
export default permission
src/store/getters.js
const getters = {
token: state => state.user.token,
roles: state => state.user.roles,
avatar: state => state.user.avatar,
name: state => state.user.name,
addRouters: state => state.permission.addRouters,
permission_routers: state => state.permission.routers,
sidebar_routers: state => state.permission.sidebar_routers,
}
export default getters
6 修改 login 页面
src/views/TheLogin.vue
handleSubmit(event) {
this.$refs.ruleForm2.validate((valid) => {
if (valid) {
this.logining = true;
// 触发 vuex 中 LoginByUsername 事件
const username = this.ruleForm2.username
let password = md5(this.ruleForm2.password)
if(this.hidePassword !== '' && this.ruleForm2.password === '********'){
password = this.hidePassword
}
this.$store.dispatch('LoginByUsername', {'username': username, 'password': password})
.then(() => {
this.logining = false
if(this.rememberme){
this.setCookie(this.ruleForm2.username, password, 7)
}else{
this.clearCookie()
}
// 重定向到首页
this.$router.push({ path: this.redirect || '/' })
})
.catch(err => {
this.logining = false
this.$alert(err, {
type: 'warning',
confirmButtonText: 'ok'
})
})
} else {
console.log('error submit!');
return false;
}
})
}
7 修改main 主入口
src/main.js
/** ...*/
import store from './store'
import '@/login'
import '@/mock'
Vue.use(ElementUI)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
render: h => h(App)
})
8 关键的权限控制
src/login.js
import router from './router'
import NProgress from 'nprogress'
import {clearRequest} from '@/utils/request'
import axios from 'axios'
import store from './store'
function hasPermission(roles, permissionRoles){
if(roles.indexOf('admin') >= 0){
return true // admin 权限 直接通过
}
// 没有配置权限的菜单直接进入
if(! permissionRoles){
return true
}
return roles.some(role => permissionRoles.indexOf(role) >= 0)
}
const whiteList = ['/login', ] // 不重定向白名单
router.beforeEach((to, from, next) => {
console.log('to.path: ' + to.path)
console.log('store.getters.token: ' + store.getters.token)
// 切换路由时清空上个路由未完成的所有请求
const cancelToken = axios.CancelToken
clearRequest.source.cancel && clearRequest.source.cancel()
clearRequest.source = cancelToken.source()
if(whiteList.indexOf(to.path) !== -1){
next()
}else{
if(store.getters.token){
if(to.path === '/login'){
next({path: '/'})
NProgress.done()
}else{
// 拉取用户信息
if(store.getters.roles.length === 0){
store.dispatch('GetUserInfo')
.then(resp => {
const roles = resp.data.roles
console.log('roles: ', roles)
// 根据权限生成可访问的路由表
store.dispatch('GenerateRoutes', {roles})
.then(() => {
// 动态添加路由表
router.addRoutes(store.getters.addRouters)
next({...to, replace: true}) // 确保 addRouters 已完成
})
})
.catch(err => {
store.dispatch('FedLogOut')
.then(() => {
console.log('认证失败,请重新登陆')
next({path: '/login'})
})
})
}else{
console.log('call GenSidebarRoutes')
store.dispatch('GenSidebarRoutes', to)
.then(() => {
if(hasPermission(store.getters.roles, to.meta.role)){
next()
}else{
next({path: '/', query: {noGoBack: true}})
}
})
}
}
}else{
// 重定向到 /login
next({path: '/login', query: {redirect: to.fullpath}})
NProgress.done()
}
}
})
router.afterEach(() => {
NProgress.done()
})
目录结构
│ App.vue
│ login.js
│ main.js
├─api
│ login.js
│ system.js
├─assets
│ logo.png
├─components
│ HelloWorld.vue
│ Navbar.vue
│ NavbarItem.vue
│ Sidebar.vue
│ SidebarItem.vue
├─containers
│ Container.vue
├─mock
│ article.js
│ index.js
│ login.js
│ system.js
├─router
│ │ index.js
│ │
│ └─modules
│ system.js
├─store
│ │ getters.js
│ │ index.js
│ │
│ └─modules
│ permissions.js
│ user.js
├─styles
│ animate.scss
│ browser-prefix.scss
│ index.scss
├─utils
│ auth.js
│ index.js
│ request.js
└─views
│ 404.vue
│ Home.vue
│ TheLogin.vue
├─article
│ index.vue
├─dashboard
│ index.vue
├─SidebarMenu
│ index.vue
└─system
└─permit
│ account.vue
│ accountgroup.vue
│ authorize.vue
│ index.vue
│ role.vue
└─components
DialogRoleMenu.vue
admin 用户权限:
developer 权限:
vue+elementui搭建后台管理界面(7 vuex和mockjs的使用)的更多相关文章
- vue+elementui搭建后台管理界面(6登录和菜单权限控制)
不同的权限对应不同的路由(菜单),同时侧边栏也根据权限异步生成,实现登录和鉴权思路如下: 登录:点击登录,服务器验证通过后返回一个 token ,然后存到 cookie,再根据 token 拉取用户权 ...
- vue+elementui搭建后台管理界面(2首页)
1 会话存储 使用html5的 sessionStorage 对象临时保存会话 // 保存会话 sessionStorage.setItem('user', username) // 删除会话 ses ...
- vue+elementui搭建后台管理界面
1 会话存储 使用html5的 sessionStorage 对象临时保存会话 // 保存会话 sessionStorage.setItem('user', username) // 删除会话 ses ...
- vue+elementui搭建后台管理界面(1登录)
1 node环境安装 从 node官网下载安装包 2 vue-cli npm install vue-cli -g 3 新建项目 vue init webpack vue-project 可保持默认, ...
- vue+elementui搭建后台管理界面(8 同步/异步获取数据渲染table)
elementui已经封装好了 el-table 组件,只需要指定 data 数据源即可,因此通常在 vue 实例生命周期的 created 阶段,从数据库获取数据,再将返回的数据绑定到 data 如 ...
- vue+elementui搭建后台管理界面(5递归生成侧栏路由)
有一个菜单树,顶层菜单下面有多个子菜单,子菜单下还有子菜单... 这时候就要用递归处理 1 定义多级菜单 修改 src/router/index.js 的 / 路由 { path: '/', redi ...
- vue+elementui搭建后台管理界面(3侧边栏菜单)
上一节搭好了主框架,但是标签页和侧边栏只是分别展示了各自的菜单,如何将二者联动起来? 定义路由规则:当有 children 属性时,从 children 里取出 path 填充到侧边栏,如: { pa ...
- vue+elementui搭建后台管理界面(6登录和菜单权限控制[二])
根据权限计算路由的代码 /** * 通过meta.role判断是否与当前用户权限匹配 * @param roles * @param route */ function hasRoles (roles ...
- vue+elementui搭建后台管理界面(4使用font-awesome)
使用font-awesome npm install --save font-awesome 修改 src/main.js 增加 import 'font-awesome/scss/font-awes ...
随机推荐
- maven设定项目编码
今天在DOS下执行mvn compile命令时报错说缺少必要符号,事实上根本就没有缺少,但何以如此呢,为啥eclipse在编译时就没有这问题呢? 原因是编码的问题造成的! eclipse在编译的使用使 ...
- day 08 预科
目录 可变和不可变 不可变类型 可变类型 可变: 列表/字典 ---->值变id不变 不可变: 数字/字符串 ---->值变id也变 列表的内置方法 字典的内置方法 可变和不可变 可变和不 ...
- 部署GitLab时, 问题
1. 开启防火墙可能会对 nginx 造成影响. 2. 安装 gitlab 会自带一个 nginx, 启动后会对 现有的nginx 造成影响, 解决方案 参考 连接 1
- django admin-过滤器
django框架的admin模块,通过list_filter提供给用户自定义分类查询的接口,并且我们可以在原有类的基础上扩展出符合自身应用场景的过滤器. 定义模型 以 Student 模型为准,管理类 ...
- docker 安装 oracle
docker 安装 oracle 部署链接:https://www.linuxidc.com/Linux/2017-03/141760.htm 本次使用docker安装oracle 1.搜索可使用的镜 ...
- Docker CMD ENTRYPOING 和Kubernetes command args对比
Docker CMD ENTRYPOING 和Kubernetes command args对比 exec 模式 使用 exec 模式时,容器中的任务进程就是容器内的 1 号进程 shell 模式 使 ...
- Python入门篇-高阶函数
Python入门篇-高阶函数 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.高级函数 1>.First Class Object 函数在Python中是一等公民 函数也 ...
- 08-Flutter移动电商实战-dio基础_伪造请求头获取数据
在很多时候,后端为了安全都会有一些请求头的限制,只有请求头对了,才能正确返回数据.这虽然限制了一些人恶意请求数据,但是对于我们聪明的程序员来说,就是形同虚设.这篇文章就以极客时间 为例,讲一下通过伪造 ...
- LeetCode 1130. Minimum Cost Tree From Leaf Values
原题链接在这里:https://leetcode.com/problems/minimum-cost-tree-from-leaf-values/ 题目: Given an array arr of ...
- elasticsearch安装和部署
1.可以在官网上下载不同版本的es,官网地址为:https://www.elastic.co/cn/downloads/past-releases#elasticsearch 2.解压elastics ...