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 ...
随机推荐
- js删除对象里的某一个属性
var a={"id":1,"name":"danlis"}; //添加属性 a.age=18; console.log(a); //结果: ...
- nginx服务的基本配置
Nginx在运行时,至少必须加载几个核心模块和一个事件类模块.这些模块运行时所支持的配置项称为基本配置.由于配置项较多,所以把它们按照用户使用时的预期功能分为四类: 用于调试.定位问题的配置项 正常运 ...
- scrapy 下载器中间件 随机切换user-agent
下载器中间件如下列表 ['scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware', 'scrapy.downloadermiddlewa ...
- System.Runtime.Caching中MemoryCache帮助类
值得参考的几个内存缓存帮助类: 参考资料: https://github.com/Hendy/memory-cache-helper https://gist.github.com/jdalley/0 ...
- javascript原生图片懒加载
一,原生javascript图片懒加载 1. 使用方法,例如 // 要绑定的图片地址 <img data-src={url} alt=" "> 2. 在页面中引入下列原 ...
- 洛谷 P2722 总分题解
题目描述 我们可以从几个种类中选取竞赛的题目,这里的一个"种类"是指一个竞赛题目的集合,解决集合中的题目需要相同多的时间并且能得到相同的分数.你的任务是写一个程序来告诉USACO的 ...
- windows下面,PHP如何启动一些扩展功能
我今天在试这个时,发现php有些默认设置,是需要人为介入修改的. 比如,当我们在安装一个软件,而这个软件需要启用php一些扩展功能. 那么,按一般套路,将php.ini文件里的相关行的注释去掉即可. ...
- docker学习4-docker安装mysql环境
前言 docker安装mysql环境非常方便,简单的几步操作就可以了 拉取mysql镜像 先拉取mysql的镜像,可以在docker的镜像仓库找到不同TAG标签的版本https://hub.docke ...
- failed to recover intents
failed to recover intents 无法恢复意图
- 案例实战之如何写一个webpack loader
通过以下几个实例掌握webpack loader的写法 1.写一个多语言替换的loader 在index.js在页面上插入了一个{{title}}文本,我们需要在打包的时候将其替换成对应的多语言 fu ...