This commit is contained in:
Pan 2019-02-28 15:02:23 +08:00
parent b65962d7e5
commit 85f3cc59d0
14 changed files with 338 additions and 816 deletions

View File

@ -1,6 +1,6 @@
import request from '@/utils/request' import request from '@/utils/request'
export function fetchRoles() { export function getRoles() {
return request({ return request({
url: '/roles', url: '/roles',
method: 'get' method: 'get'
@ -14,7 +14,7 @@ export function deleteRole(id) {
}) })
} }
export function newRole(data) { export function addRole(data) {
return request({ return request({
url: '/roles', url: '/roles',
method: 'post', method: 'post',

View File

@ -34,7 +34,7 @@ export default {
}, },
computed: { computed: {
routers() { routers() {
return this.$store.getters.permission_routers return this.$store.getters.permission_routes
}, },
lang() { lang() {
return this.$store.getters.language return this.$store.getters.language

View File

@ -87,7 +87,7 @@ export default {
github: 'Github Repository' github: 'Github Repository'
}, },
permission: { permission: {
newRole: 'New Role', addRole: 'New Role',
editPermission: 'Edit Permission', editPermission: 'Edit Permission',
roles: 'Your roles', roles: 'Your roles',
switchRoles: 'Switch roles', switchRoles: 'Switch roles',

View File

@ -87,9 +87,14 @@ export default {
github: 'Repositorio Github' github: 'Repositorio Github'
}, },
permission: { permission: {
addRole: 'Nuevo rol',
editPermission: 'Permiso de edición',
roles: 'Tus permisos', roles: 'Tus permisos',
switchRoles: 'Cambiar permisos', switchRoles: 'Cambiar permisos',
tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.' tips: 'In some cases it is not suitable to use v-permission, such as element Tab component or el-table-column and other asynchronous rendering dom cases which can only be achieved by manually setting the v-if.',
delete: 'Borrar',
confirm: 'Confirmar',
cancel: 'Cancelar'
}, },
guide: { guide: {
description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ', description: 'The guide page is useful for some people who entered the project for the first time. You can briefly introduce the features of the project. Demo is based on ',

View File

@ -87,7 +87,7 @@ export default {
github: 'Github 地址' github: 'Github 地址'
}, },
permission: { permission: {
newRole: '新增角色', addRole: '新增角色',
editPermission: '编辑权限', editPermission: '编辑权限',
roles: '你的权限', roles: '你的权限',
switchRoles: '切换权限', switchRoles: '切换权限',

View File

@ -3,7 +3,6 @@ import loginAPI from './login'
import articleAPI from './article' import articleAPI from './article'
import remoteSearchAPI from './remoteSearch' import remoteSearchAPI from './remoteSearch'
import transactionAPI from './transaction' import transactionAPI from './transaction'
import routesAPI from './routes'
import roleAPI from './role' import roleAPI from './role'
// 修复在使用 MockJS 情况下,设置 withCredentials = true且未被拦截的跨域请求丢失 Cookies 的问题 // 修复在使用 MockJS 情况下,设置 withCredentials = true且未被拦截的跨域请求丢失 Cookies 的问题
@ -20,9 +19,6 @@ Mock.XHR.prototype.send = function() {
// timeout: '350-600' // timeout: '350-600'
// }) // })
// 路由相关
Mock.mock(/\/routes/, 'get', routesAPI.getAsyncRoutesMap)
// 登录相关 // 登录相关
Mock.mock(/\/login\/login/, 'post', loginAPI.loginByUsername) Mock.mock(/\/login\/login/, 'post', loginAPI.loginByUsername)
Mock.mock(/\/login\/logout/, 'post', loginAPI.logout) Mock.mock(/\/login\/logout/, 'post', loginAPI.logout)
@ -30,7 +26,7 @@ Mock.mock(/\/user\/info\.*/, 'get', loginAPI.getUserInfo)
// 角色相关 // 角色相关
Mock.mock(/\/roles/, 'get', roleAPI.getRoles) Mock.mock(/\/roles/, 'get', roleAPI.getRoles)
Mock.mock(/\/roles$/, 'post', roleAPI.newRole) Mock.mock(/\/roles$/, 'post', roleAPI.addRole)
Mock.mock(/\/roles\/[A-Za-z0-9]+/, 'put', roleAPI.updateRole) Mock.mock(/\/roles\/[A-Za-z0-9]+/, 'put', roleAPI.updateRole)
Mock.mock(/\/roles\/[A-Za-z0-9]+/, 'delete', roleAPI.deleteRole) Mock.mock(/\/roles\/[A-Za-z0-9]+/, 'delete', roleAPI.deleteRole)

View File

@ -1,159 +1,13 @@
import Mock from 'mockjs' import Mock from 'mockjs'
// admin 角色可以访问所有菜单 // admin 角色可以访问所有菜单
const roles = [ const roles = []
{
id: 'editor', // 角色id
name: 'editor',
description: '编辑',
accessibleRoutes: [
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10',
'11',
'12',
'13',
'14',
'15',
'16',
'17',
'18',
'19',
'20',
'21',
'22',
'23',
'24',
'25',
'26',
'27',
'28',
'29',
'30',
'31',
'32',
'33',
'34',
'35',
'36',
'37',
'38',
'39',
'40',
'41',
'42',
'43',
'44',
'45',
'46',
'47',
'48',
'49',
'50',
'51',
'52',
'53',
'54',
'55',
'56',
'57',
'58',
'59',
'60',
'61',
'62',
'63',
'64',
'65',
'66',
'67'
] // 可访问的菜单id列表
},
{
id: 'operator', // 角色id
name: 'operator',
description: '运营',
accessibleRoutes: [
'1',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10',
'11',
'12',
'13',
'14',
'15',
'16',
'17',
'18',
'19',
'20',
'21',
'22',
'23',
'24',
'25',
'26',
'27',
'28',
'29',
'30',
'31',
'32',
'33',
'34',
'35',
'36',
'37',
'38',
'39',
'40',
'41',
'42',
'43',
'44',
'45',
'46',
'47',
'48',
'49',
'50',
'51',
'52',
'53',
'54',
'55',
'56',
'57',
'58',
'59',
'60',
'61',
'62',
'63',
'64',
'65'
] // 可访问的菜单id列表
}
]
export default { export default {
getRoles() { getRoles() {
return roles return roles
}, },
newRole() { addRole() {
const res = { const res = {
data: Mock.mock('id') data: Mock.mock('id')
} }

View File

@ -1,581 +0,0 @@
const asyncRoutesMap = [
{
id: '1',
path: '/permission',
component: 'layout/Layout',
redirect: '/permission/index',
alwaysShow: true, // will always show the root menu
meta: {
title: 'permission',
icon: 'lock',
roles: ['admin', 'editor'] // you can set roles in root nav
},
children: [
{
id: '2',
path: 'page',
component: 'permission/page',
name: 'PagePermission',
meta: {
title: 'pagePermission',
roles: ['admin']
}
},
{
id: '3',
path: 'directive',
component: 'permission/directive',
name: 'DirectivePermission',
meta: {
title: 'directivePermission',
roles: ['admin', 'editor']
}
},
{
id: '66',
path: 'role',
component: 'permission/role',
name: 'role',
meta: {
title: 'rolePermission',
roles: ['admin']
}
}
]
},
{
id: '4',
path: '/icon',
component: 'layout/Layout',
children: [
{
id: '5',
path: 'index',
component: 'svg-icons/index',
name: 'Icons',
meta: { title: 'icons', icon: 'icon', noCache: true, roles: ['admin', 'editor'] }
}
]
},
{
id: '6',
path: '/components',
component: 'layout/Layout',
redirect: 'noredirect',
name: 'ComponentDemo',
meta: {
title: 'components',
icon: 'component',
roles: ['admin', 'editor']
},
children: [
{
id: '7',
path: 'tinymce',
component: 'components-demo/tinymce',
name: 'TinymceDemo',
meta: { title: 'tinymce', roles: ['admin', 'editor'] }
},
{
id: '8',
path: 'markdown',
component: 'components-demo/markdown',
name: 'MarkdownDemo',
meta: { title: 'markdown', roles: ['admin', 'editor'] }
},
{
id: '9',
path: 'json-editor',
component: 'components-demo/jsonEditor',
name: 'JsonEditorDemo',
meta: { title: 'jsonEditor', roles: ['admin', 'editor'] }
},
{
id: '10',
path: 'splitpane',
component: 'components-demo/splitpane',
name: 'SplitpaneDemo',
meta: { title: 'splitPane', roles: ['admin', 'editor'] }
},
{
id: '11',
path: 'avatar-upload',
component: 'components-demo/avatarUpload',
name: 'AvatarUploadDemo',
meta: { title: 'avatarUpload', roles: ['admin', 'editor'] }
},
{
id: '12',
path: 'dropzone',
component: 'components-demo/dropzone',
name: 'DropzoneDemo',
meta: { title: 'dropzone', roles: ['admin', 'editor'] }
},
{
id: '13',
path: 'sticky',
component: 'components-demo/sticky',
name: 'StickyDemo',
meta: { title: 'sticky', roles: ['admin', 'editor'] }
},
{
id: '14',
path: 'count-to',
component: 'components-demo/countTo',
name: 'CountToDemo',
meta: { title: 'countTo', roles: ['admin', 'editor'] }
},
{
id: '15',
path: 'mixin',
component: 'components-demo/mixin',
name: 'ComponentMixinDemo',
meta: { title: 'componentMixin', roles: ['admin', 'editor'] }
},
{
id: '16',
path: 'back-to-top',
component: 'components-demo/backToTop',
name: 'BackToTopDemo',
meta: { title: 'backToTop', roles: ['admin', 'editor'] }
},
{
id: '17',
path: 'drag-dialog',
component: 'components-demo/dragDialog',
name: 'DragDialogDemo',
meta: { title: 'dragDialog', roles: ['admin', 'editor'] }
},
{
id: '18',
path: 'drag-select',
component: 'components-demo/dragSelect',
name: 'DragSelectDemo',
meta: { title: 'dragSelect', roles: ['admin', 'editor'] }
},
{
id: '19',
path: 'dnd-list',
component: 'components-demo/dndList',
name: 'DndListDemo',
meta: { title: 'dndList', roles: ['admin', 'editor'] }
},
{
id: '20',
path: 'drag-kanban',
component: 'components-demo/dragKanban',
name: 'DragKanbanDemo',
meta: { title: 'dragKanban', roles: ['admin', 'editor'] }
}
]
},
{
id: '21',
path: '/charts',
component: 'layout/Layout',
redirect: 'noredirect',
name: 'Charts',
meta: {
title: 'charts',
icon: 'chart',
roles: ['admin', 'editor']
},
children: [
{
id: '22',
path: 'keyboard',
component: 'charts/keyboard',
name: 'KeyboardChart',
meta: { title: 'keyboardChart', noCache: true, roles: ['admin', 'editor'] }
},
{
id: '23',
path: 'line',
component: 'charts/line',
name: 'LineChart',
meta: { title: 'lineChart', noCache: true, roles: ['admin', 'editor'] }
},
{
id: '24',
path: 'mixchart',
component: 'charts/mixChart',
name: 'MixChart',
meta: { title: 'mixChart', noCache: true, roles: ['admin', 'editor'] }
}
]
},
{
id: '25',
path: '/nested',
component: 'layout/Layout',
redirect: '/nested/menu1/menu1-1',
name: 'Nested',
meta: {
title: 'nested',
icon: 'nested',
roles: ['admin', 'editor']
},
children: [
{
id: '26',
path: 'menu1',
component: 'nested/menu1/index', // Parent router-view
name: 'Menu1',
meta: { title: 'menu1', roles: ['admin', 'editor'] },
redirect: '/nested/menu1/menu1-1',
children: [
{
id: '27',
path: 'menu1-1',
component: 'nested/menu1/menu1-1',
name: 'Menu1-1',
meta: { title: 'menu1-1', roles: ['admin', 'editor'] }
},
{
id: '28',
path: 'menu1-2',
component: 'nested/menu1/menu1-2',
name: 'Menu1-2',
redirect: '/nested/menu1/menu1-2/menu1-2-1',
meta: { title: 'menu1-2', roles: ['admin', 'editor'] },
children: [
{
id: '29',
path: 'menu1-2-1',
component: 'nested/menu1/menu1-2/menu1-2-1',
name: 'Menu1-2-1',
meta: { title: 'menu1-2-1', roles: ['admin', 'editor'] }
},
{
id: '30',
path: 'menu1-2-2',
component: 'nested/menu1/menu1-2/menu1-2-2',
name: 'Menu1-2-2',
meta: { title: 'menu1-2-2', roles: ['admin', 'editor'] }
}
]
},
{
id: '31',
path: 'menu1-3',
component: 'nested/menu1/menu1-3',
name: 'Menu1-3',
meta: { title: 'menu1-3', roles: ['admin', 'editor'] }
}
]
},
{
id: '32',
path: 'menu2',
name: 'Menu2',
component: 'nested/menu2/index',
meta: { title: 'menu2', roles: ['admin', 'editor'] }
}
]
},
{
id: '33',
path: '/table',
component: 'layout/Layout',
redirect: '/table/complex-table',
name: 'Table',
meta: {
title: 'Table',
icon: 'table',
roles: ['admin', 'editor']
},
children: [
{
id: '34',
path: 'dynamic-table',
component: 'table/dynamicTable/index',
name: 'DynamicTable',
meta: { title: 'dynamicTable', roles: ['admin', 'editor'] }
},
{
id: '35',
path: 'drag-table',
component: 'table/dragTable',
name: 'DragTable',
meta: { title: 'dragTable', roles: ['admin', 'editor'] }
},
{
id: '36',
path: 'inline-edit-table',
component: 'table/inlineEditTable',
name: 'InlineEditTable',
meta: { title: 'inlineEditTable', roles: ['admin', 'editor'] }
},
{
id: '37',
path: 'tree-table',
component: 'table/treeTable/treeTable',
name: 'TreeTableDemo',
meta: { title: 'treeTable', roles: ['admin', 'editor'] }
},
{
id: '38',
path: 'custom-tree-table',
component: 'table/treeTable/customTreeTable',
name: 'CustomTreeTableDemo',
meta: { title: 'customTreeTable', roles: ['admin', 'editor'] }
},
{
id: '39',
path: 'complex-table',
component: 'table/complexTable',
name: 'ComplexTable',
meta: { title: 'complexTable', roles: ['admin', 'editor'] }
}
]
},
{
id: '40',
path: '/example',
component: 'layout/Layout',
redirect: '/example/list',
name: 'Example',
meta: {
title: 'example',
icon: 'example',
roles: ['admin', 'editor']
},
children: [
{
id: '41',
path: 'create',
component: 'example/create',
name: 'CreateArticle',
meta: { title: 'createArticle', icon: 'edit', roles: ['admin', 'editor'] }
},
{
id: '42',
path: 'edit/:id(\\d+)',
component: 'example/edit',
name: 'EditArticle',
meta: { title: 'editArticle', noCache: true, roles: ['admin', 'editor'] },
hidden: true
},
{
id: '43',
path: 'list',
component: 'example/list',
name: 'ArticleList',
meta: { title: 'articleList', icon: 'list', roles: ['admin', 'editor'] }
}
]
},
{
path: '/tab',
component: 'layout/Layout',
children: [
{
id: '44',
path: 'index',
component: 'tab/index',
name: 'Tab',
meta: { title: 'tab', icon: 'tab', roles: ['admin', 'editor'] }
}
]
},
{
id: '45',
path: '/error',
component: 'layout/Layout',
redirect: 'noredirect',
name: 'ErrorPages',
meta: {
title: 'errorPages',
icon: '404',
roles: ['admin', 'editor']
},
children: [
{
id: '46',
path: '401',
component: 'errorPage/401',
name: 'Page401',
meta: { title: 'page401', noCache: true, roles: ['admin', 'editor'] }
},
{
id: '47',
path: '404',
component: 'errorPage/404',
name: 'Page404',
meta: { title: 'page404', noCache: true, roles: ['admin', 'editor'] }
}
]
},
{
id: '48',
path: '/error-log',
component: 'layout/Layout',
redirect: 'noredirect',
children: [
{
id: '67',
path: 'log',
component: 'errorLog/index',
name: 'ErrorLog',
meta: { title: 'errorLog', icon: 'bug', roles: ['admin', 'editor'] }
}
]
},
{
id: '49',
path: '/excel',
component: 'layout/Layout',
redirect: '/excel/export-excel',
name: 'Excel',
meta: {
title: 'excel',
icon: 'excel',
roles: ['admin', 'editor']
},
children: [
{
id: '50',
path: 'export-excel',
component: 'excel/exportExcel',
name: 'ExportExcel',
meta: { title: 'exportExcel', roles: ['admin', 'editor'] }
},
{
id: '51',
path: 'export-selected-excel',
component: 'excel/selectExcel',
name: 'SelectExcel',
meta: { title: 'selectExcel', roles: ['admin', 'editor'] }
},
{
id: '52',
path: 'upload-excel',
component: 'excel/uploadExcel',
name: 'UploadExcel',
meta: { title: 'uploadExcel', roles: ['admin', 'editor'] }
}
]
},
{
id: '53',
path: '/zip',
component: 'layout/Layout',
redirect: '/zip/download',
alwaysShow: true,
meta: { title: 'zip', icon: 'zip', roles: ['admin', 'editor'] },
children: [
{
id: '54',
path: 'download',
component: 'zip/index',
name: 'ExportZip',
meta: { title: 'exportZip', roles: ['admin', 'editor'] }
}
]
},
{
id: '55',
path: '/pdf',
component: 'layout/Layout',
redirect: '/pdf/index',
children: [
{
id: '56',
path: 'index',
component: 'pdf/index',
name: 'PDF',
meta: { title: 'pdf', icon: 'pdf', roles: ['admin', 'editor'] }
}
]
},
{
id: '57',
path: '/pdf/download',
component: 'pdf/download',
hidden: true
},
{
id: '58',
path: '/theme',
component: 'layout/Layout',
redirect: 'noredirect',
meta: {
roles: ['admin', 'editor']
},
children: [
{
id: '59',
path: 'index',
component: 'theme/index',
name: 'Theme',
meta: { title: 'theme', icon: 'theme', roles: ['admin', 'editor'] }
}
]
},
{
id: '60',
path: '/clipboard',
component: 'layout/Layout',
redirect: 'noredirect',
meta: {
roles: ['admin', 'editor']
},
children: [
{
id: '61',
path: 'index',
component: 'clipboard/index',
name: 'ClipboardDemo',
meta: { title: 'clipboardDemo', icon: 'clipboard', roles: ['admin', 'editor'] }
}
]
},
{
id: '62',
path: '/i18n',
component: 'layout/Layout',
meta: {
roles: ['admin', 'editor']
},
children: [
{
id: '63',
path: 'index',
component: 'i18n-demo/index',
name: 'I18n',
meta: { title: 'i18n', icon: 'international', roles: ['admin', 'editor'] }
}
]
},
{
id: '64',
path: 'external-link',
component: 'layout/Layout',
meta: {
roles: ['admin', 'editor']
},
children: [
{
id: '65',
path: 'https://github.com/PanJiaChen/vue-element-admin',
meta: { title: 'externalLink', icon: 'link', roles: ['admin', 'editor'] }
}
]
}
]
export default {
getAsyncRoutesMap() {
return asyncRoutesMap
}
}

View File

@ -3,13 +3,13 @@ import store from './store'
import { Message } from 'element-ui' import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // getToken from cookie import { getToken } from '@/utils/auth' // get token from cookie
NProgress.configure({ showSpinner: false }) // NProgress Configuration NProgress.configure({ showSpinner: false }) // NProgress Configuration
// permission judge function // permission judge function
function hasPermission(roles, permissionRoles) { function hasPermission(roles, permissionRoles) {
if (roles.findIndex(role => role === 'admin') >= 0) return true // admin permission passed directly if (roles.includes('admin')) return true // admin permission passed directly
if (!permissionRoles) return true if (!permissionRoles) return true
return roles.some(role => permissionRoles.indexOf(role) >= 0) return roles.some(role => permissionRoles.indexOf(role) >= 0)
} }
@ -20,6 +20,7 @@ router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar NProgress.start() // start progress bar
if (getToken()) { if (getToken()) {
// determine if there has token // determine if there has token
/* has token*/ /* has token*/
if (to.path === '/login') { if (to.path === '/login') {
next({ path: '/' }) next({ path: '/' })
@ -32,9 +33,9 @@ router.beforeEach((to, from, next) => {
.then(res => { .then(res => {
// 拉取user_info // 拉取user_info
const roles = res.data.roles // note: roles must be a object array! such as: [{id: '1', name: 'editor'}, {id: '2', name: 'developer'}] const roles = res.data.roles // note: roles must be a object array! such as: [{id: '1', name: 'editor'}, {id: '2', name: 'developer'}]
store.dispatch('GenerateRoutes', { roles }).then(excessRoutes => { store.dispatch('GenerateRoutes', { roles }).then(accessRoutes => {
// 根据roles权限生成可访问的路由表 // 根据roles权限生成可访问的路由表
router.addRoutes(excessRoutes) // 动态添加可访问路由表 router.addRoutes(accessRoutes) // 动态添加可访问路由表
next({ ...to, replace: true }) // hack方法 确保addRouters已完成 ,set the replace: true so the navigation will not leave a history record next({ ...to, replace: true }) // hack方法 确保addRouters已完成 ,set the replace: true so the navigation will not leave a history record
}) })
}) })

View File

@ -6,7 +6,13 @@ Vue.use(Router)
/* Layout */ /* Layout */
import Layout from '@/views/layout/Layout' import Layout from '@/views/layout/Layout'
/** note: Submenu only appear when children.length>=1 /* Router Modules */
import componentsRouter from './modules/components'
import chartsRouter from './modules/charts'
import tableRouter from './modules/table'
import nestedRouter from './modules/nested'
/** note: sub-menu only appear when children.length>=1
* detail see https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html * detail see https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
**/ **/
@ -19,10 +25,11 @@ import Layout from '@/views/layout/Layout'
* name:'router-name' the name is used by <keep-alive> (must set!!!) * name:'router-name' the name is used by <keep-alive> (must set!!!)
* meta : { * meta : {
roles: ['admin','editor'] will control the page roles (you can set multiple roles) roles: ['admin','editor'] will control the page roles (you can set multiple roles)
title: 'title' the name show in submenu and breadcrumb (recommend set) title: 'title' the name show in sub-menu and breadcrumb (recommend set)
icon: 'svg-name' the icon show in the sidebar icon: 'svg-name' the icon show in the sidebar
noCache: true if true, the page will no be cached(default is false) noCache: true if true, the page will no be cached(default is false)
breadcrumb: false if false, the item will hidden in breadcrumb(default is true) breadcrumb: false if false, the item will hidden in breadcrumb(default is true)
affix: true if true, the tag will affix in the tags-view
} }
**/ **/
export const constantRoutes = [ export const constantRoutes = [
@ -66,7 +73,7 @@ export const constantRoutes = [
path: 'dashboard', path: 'dashboard',
component: () => import('@/views/dashboard/index'), component: () => import('@/views/dashboard/index'),
name: 'Dashboard', name: 'Dashboard',
meta: { title: 'dashboard', icon: 'dashboard', noCache: true } meta: { title: 'dashboard', icon: 'dashboard', noCache: true, affix: true }
} }
] ]
}, },
@ -79,7 +86,7 @@ export const constantRoutes = [
path: 'index', path: 'index',
component: () => import('@/views/documentation/index'), component: () => import('@/views/documentation/index'),
name: 'Documentation', name: 'Documentation',
meta: { title: 'documentation', icon: 'documentation', noCache: true } meta: { title: 'documentation', icon: 'documentation', affix: true }
} }
] ]
}, },
@ -98,10 +105,265 @@ export const constantRoutes = [
} }
] ]
export const generalRoutes = [{ path: '*', redirect: '/404', hidden: true }]
export default new Router({ export default new Router({
// mode: 'history', // require service support // mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }), scrollBehavior: () => ({ y: 0 }),
routes: constantRoutes routes: constantRoutes
}) })
export const asyncRoutes = [
{
path: '/permission',
component: Layout,
redirect: '/permission/index',
alwaysShow: true, // will always show the root menu
meta: {
title: 'permission',
icon: 'lock',
roles: ['admin', 'editor'] // you can set roles in root nav
},
children: [
{
path: 'page',
component: () => import('@/views/permission/page'),
name: 'PagePermission',
meta: {
title: 'pagePermission',
roles: ['admin'] // or you can only set roles in sub nav
}
},
{
path: 'directive',
component: () => import('@/views/permission/directive'),
name: 'DirectivePermission',
meta: {
title: 'directivePermission'
// if do not set roles, means: this page does not require permission
}
}
]
},
{
path: '/icon',
component: Layout,
children: [
{
path: 'index',
component: () => import('@/views/svg-icons/index'),
name: 'Icons',
meta: { title: 'icons', icon: 'icon', noCache: true }
}
]
},
/** When your routing table is too long, you can split it into small modules**/
componentsRouter,
chartsRouter,
nestedRouter,
tableRouter,
{
path: '/example',
component: Layout,
redirect: '/example/list',
name: 'Example',
meta: {
title: 'example',
icon: 'example'
},
children: [
{
path: 'create',
component: () => import('@/views/example/create'),
name: 'CreateArticle',
meta: { title: 'createArticle', icon: 'edit' }
},
{
path: 'edit/:id(\\d+)',
component: () => import('@/views/example/edit'),
name: 'EditArticle',
meta: { title: 'editArticle', noCache: true },
hidden: true
},
{
path: 'list',
component: () => import('@/views/example/list'),
name: 'ArticleList',
meta: { title: 'articleList', icon: 'list' }
}
]
},
{
path: '/tab',
component: Layout,
children: [
{
path: 'index',
component: () => import('@/views/tab/index'),
name: 'Tab',
meta: { title: 'tab', icon: 'tab' }
}
]
},
{
path: '/error',
component: Layout,
redirect: 'noredirect',
name: 'ErrorPages',
meta: {
title: 'errorPages',
icon: '404'
},
children: [
{
path: '401',
component: () => import('@/views/errorPage/401'),
name: 'Page401',
meta: { title: 'page401', noCache: true }
},
{
path: '404',
component: () => import('@/views/errorPage/404'),
name: 'Page404',
meta: { title: 'page404', noCache: true }
}
]
},
{
path: '/error-log',
component: Layout,
redirect: 'noredirect',
children: [
{
path: 'log',
component: () => import('@/views/errorLog/index'),
name: 'ErrorLog',
meta: { title: 'errorLog', icon: 'bug' }
}
]
},
{
path: '/excel',
component: Layout,
redirect: '/excel/export-excel',
name: 'Excel',
meta: {
title: 'excel',
icon: 'excel'
},
children: [
{
path: 'export-excel',
component: () => import('@/views/excel/exportExcel'),
name: 'ExportExcel',
meta: { title: 'exportExcel' }
},
{
path: 'export-selected-excel',
component: () => import('@/views/excel/selectExcel'),
name: 'SelectExcel',
meta: { title: 'selectExcel' }
},
{
path: 'upload-excel',
component: () => import('@/views/excel/uploadExcel'),
name: 'UploadExcel',
meta: { title: 'uploadExcel' }
}
]
},
{
path: '/zip',
component: Layout,
redirect: '/zip/download',
alwaysShow: true,
meta: { title: 'zip', icon: 'zip' },
children: [
{
path: 'download',
component: () => import('@/views/zip/index'),
name: 'ExportZip',
meta: { title: 'exportZip' }
}
]
},
{
path: '/pdf',
component: Layout,
redirect: '/pdf/index',
children: [
{
path: 'index',
component: () => import('@/views/pdf/index'),
name: 'PDF',
meta: { title: 'pdf', icon: 'pdf' }
}
]
},
{
path: '/pdf/download',
component: () => import('@/views/pdf/download'),
hidden: true
},
{
path: '/theme',
component: Layout,
redirect: 'noredirect',
children: [
{
path: 'index',
component: () => import('@/views/theme/index'),
name: 'Theme',
meta: { title: 'theme', icon: 'theme' }
}
]
},
{
path: '/clipboard',
component: Layout,
redirect: 'noredirect',
children: [
{
path: 'index',
component: () => import('@/views/clipboard/index'),
name: 'ClipboardDemo',
meta: { title: 'clipboardDemo', icon: 'clipboard' }
}
]
},
{
path: '/i18n',
component: Layout,
children: [
{
path: 'index',
component: () => import('@/views/i18n-demo/index'),
name: 'I18n',
meta: { title: 'i18n', icon: 'international' }
}
]
},
{
path: 'external-link',
component: Layout,
children: [
{
path: 'https://github.com/PanJiaChen/vue-element-admin',
meta: { title: 'externalLink', icon: 'link' }
}
]
},
{ path: '*', redirect: '/404', hidden: true }
]

View File

@ -12,7 +12,7 @@ const getters = {
status: state => state.user.status, status: state => state.user.status,
roles: state => state.user.roles, roles: state => state.user.roles,
setting: state => state.user.setting, setting: state => state.user.setting,
permission_routers: state => state.permission.routers, permission_routes: state => state.permission.routes,
addRouters: state => state.permission.addRouters, addRouters: state => state.permission.addRouters,
errorLogs: state => state.errorLog.logs errorLogs: state => state.errorLog.logs
} }

View File

@ -1,15 +1,11 @@
import { constantRoutes, generalRoutes } from '@/router' import { asyncRoutes, constantRoutes } from '@/router'
import { fetchAsyncRoutes } from '@/api/routes'
const _import = path => () => import(`@/views/${path}`)
/** /**
* 通过meta.role判断是否与当前用户权限匹配 * 通过meta.role判断是否与当前用户权限匹配
* @param roles * @param roles
* @param route * @param route
*/ */
function hasPermission(roles, route) { function hasPermission(roles, route) {
// 如果是隐藏的菜单, 都是可访问的, 因为隐藏的菜单不会出现在左侧菜单栏, 不可编辑权限
if (route.hidden) return true
if (route.meta && route.meta.roles) { if (route.meta && route.meta.roles) {
return roles.some(role => route.meta.roles.includes(role)) return roles.some(role => route.meta.roles.includes(role))
} else { } else {
@ -17,44 +13,36 @@ function hasPermission(roles, route) {
} }
} }
// 将从服务器获得的路由表转换为vue-router的路由表
function mapAsyncRoutes(asyncRoutes) {
return asyncRoutes.map(route => {
route.component && (route.component = _import(route.component))
if (route.children) {
route.children = mapAsyncRoutes(route.children)
}
return route
})
}
/** /**
* 递归过滤异步路由表返回符合用户角色权限的路由表 * 递归过滤异步路由表返回符合用户角色权限的路由表
* @param routes asyncRoutes * @param routes asyncRoutes
* @param roles * @param roles
*/ */
function filterAsyncRoutes(routes, roles) { function filterAsyncRoutes(routes, roles) {
return routes.filter(route => { const res = []
if (!hasPermission(roles, route)) {
return false routes.forEach(route => {
const tmp = { ...route }
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles)
} }
if (route.children) { res.push(tmp)
route.children = filterAsyncRoutes(route.children, roles)
} }
return true
}) })
return res
} }
const permission = { const permission = {
state: { state: {
routers: [], routes: [],
addRouters: [], addRoutes: []
asyncRoutes: []
}, },
mutations: { mutations: {
SET_ROUTERS: (state, routes) => { SET_ROUTES: (state, routes) => {
state.addRouters = routes state.addRoutes = routes
state.routers = constantRoutes.concat(routes) state.routes = constantRoutes.concat(routes)
} }
}, },
actions: { actions: {
@ -62,17 +50,14 @@ const permission = {
return new Promise(resolve => { return new Promise(resolve => {
const { roles } = data const { roles } = data
let accessedRoutes let accessedRoutes
fetchAsyncRoutes().then(res => {
const asyncRoutes = res.data
if (roles.includes('admin')) { if (roles.includes('admin')) {
accessedRoutes = mapAsyncRoutes(asyncRoutes).concat(generalRoutes) accessedRoutes = asyncRoutes
} else { } else {
accessedRoutes = mapAsyncRoutes(filterAsyncRoutes(asyncRoutes, roles)).concat(generalRoutes) accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
} }
commit('SET_ROUTERS', accessedRoutes) commit('SET_ROUTES', accessedRoutes)
resolve(accessedRoutes) resolve(accessedRoutes)
}) })
})
} }
} }
} }

View File

@ -9,7 +9,7 @@
:active-text-color="variables.menuActiveText" :active-text-color="variables.menuActiveText"
mode="vertical" mode="vertical"
> >
<sidebar-item v-for="route in permission_routers" :key="route.path" :item="route" :base-path="route.path"/> <sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path"/>
</el-menu> </el-menu>
</el-scrollbar> </el-scrollbar>
</template> </template>
@ -23,7 +23,7 @@ export default {
components: { SidebarItem }, components: { SidebarItem },
computed: { computed: {
...mapGetters([ ...mapGetters([
'permission_routers', 'permission_routes',
'sidebar' 'sidebar'
]), ]),
variables() { variables() {

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="app-container wrapper"> <div class="app-container wrapper">
<el-button type="primary" @click="handleNewRole">{{ $t('permission.newRole') }}</el-button> <el-button type="primary" @click="handleaddRole">{{ $t('permission.addRole') }}</el-button>
<el-table :data="rolesData" style="width: 100%" class="roles-table"> <el-table :data="rolesData" style="width: 100%" class="roles-table">
<el-table-column label="Role Id" width="220"> <el-table-column label="Role Id" width="220">
@ -20,18 +20,18 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
<el-dialog :visible.sync="newRoleDialogVisible" title="New Role"> <el-dialog :visible.sync="addRoleDialogVisible" title="New Role">
<el-form :model="newRole" class="new-role-form"> <el-form :model="addRole" class="new-role-form">
<el-form-item label="Role Name"> <el-form-item label="Role Name">
<el-input v-model="newRole.name" placeholder="role name"/> <el-input v-model="addRole.name" placeholder="role name"/>
</el-form-item> </el-form-item>
<el-form-item label="Role Description"> <el-form-item label="Role Description">
<el-input v-model="newRole.describe" placeholder="role Description"/> <el-input v-model="addRole.describe" placeholder="role Description"/>
</el-form-item> </el-form-item>
</el-form> </el-form>
<el-button type="primary" @click="confirmNewRole">{{ $t('permission.confirm') }}</el-button> <el-button type="primary" @click="confirmaddRole">{{ $t('permission.confirm') }}</el-button>
<el-button type="danger" @click="cancleNewRole">{{ $t('permission.cancel') }}</el-button> <el-button type="danger" @click="cancleaddRole">{{ $t('permission.cancel') }}</el-button>
</el-dialog> </el-dialog>
<el-dialog :visible.sync="permissionDialogVisible" :title="$t('permission.editPermission')"> <el-dialog :visible.sync="permissionDialogVisible" :title="$t('permission.editPermission')">
@ -55,7 +55,7 @@
<script> <script>
import { deepClone } from '@/utils' import { deepClone } from '@/utils'
import { generateTitle } from '@/utils/i18n' import { generateTitle } from '@/utils/i18n'
import { newRole, deleteRole, updateRole, fetchRoles } from '@/api/role' import { addRole, deleteRole, updateRole, getRoles } from '@/api/role'
import { fetchAsyncRoutes } from '@/api/routes' import { fetchAsyncRoutes } from '@/api/routes'
export default { export default {
@ -63,13 +63,13 @@ export default {
return { return {
checkStrictly: false, checkStrictly: false,
routesData: [], routesData: [],
newRoleDialogVisible: false, addRoleDialogVisible: false,
permissionDialogVisible: false, permissionDialogVisible: false,
defaultProps: { defaultProps: {
children: 'children', children: 'children',
label: 'name' label: 'name'
}, },
newRole: { addRole: {
id: '', id: '',
name: '', name: '',
describe: '', describe: '',
@ -106,7 +106,7 @@ export default {
fetchAsyncRoutes().then(res => { fetchAsyncRoutes().then(res => {
this.routesData = res.data this.routesData = res.data
}) })
fetchRoles().then(res => { getRoles().then(res => {
console.log(res) console.log(res)
this.rolesData = res.data this.rolesData = res.data
}) })
@ -142,8 +142,8 @@ export default {
return false return false
}, },
handleNewRole() { handleaddRole() {
this.newRoleDialogVisible = true this.addRoleDialogVisible = true
}, },
handleEdit(scope) { handleEdit(scope) {
this.checkedRole.index = scope.$index this.checkedRole.index = scope.$index
@ -207,25 +207,25 @@ export default {
canclePermission() { canclePermission() {
this.permissionDialogVisible = false this.permissionDialogVisible = false
}, },
resetNewRole() { resetaddRole() {
this.newRole = { this.addRole = {
id: '', id: '',
name: '', name: '',
describe: '', describe: '',
accessibleRoutes: [] accessibleRoutes: []
} }
}, },
confirmNewRole() { confirmaddRole() {
newRole(this.newRole).then(res => { addRole(this.addRole).then(res => {
this.newRole.id = res.data.id this.addRole.id = res.data.id
this.rolesData.push(deepClone(this.newRole)) this.rolesData.push(deepClone(this.addRole))
this.resetNewRole() this.resetaddRole()
this.newRoleDialogVisible = false this.addRoleDialogVisible = false
}) })
}, },
cancleNewRole() { cancleaddRole() {
this.resetNewRole() this.resetaddRole()
this.newRoleDialogVisible = false this.addRoleDialogVisible = false
} }
} }
} }