Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
A
ant-design-pro-vue
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
tianzhuanghu
ant-design-pro-vue
Commits
7eb46668
Unverified
Commit
7eb46668
authored
Mar 27, 2019
by
Sendya
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: dynamic menu
parent
4e827600
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
169 additions
and
384 deletions
+169
-384
dynamic-menu.json
public/dynamic-menu.json
+60
-0
router.config.js
src/config/router.config.js
+1
-325
permission.js
src/permission.js
+3
-4
permission.js
src/store/modules/permission.js
+13
-55
routerUtil.js
src/utils/routerUtil.js
+92
-0
No files found.
public/dynamic-menu.json
0 → 100644
View file @
7eb46668
{
"message"
:
""
,
"result"
:
[
{
"title"
:
"首页"
,
"key"
:
""
,
"name"
:
"index"
,
"component"
:
"BasicLayout"
,
"redirect"
:
"/dashboard/workplace"
,
"children"
:
[
{
"title"
:
"仪表盘"
,
"key"
:
"dashboard"
,
"component"
:
"RouteView"
,
"icon"
:
"dashboard"
,
"children"
:
[
{
"title"
:
"分析页"
,
"key"
:
"analysis"
,
"icon"
:
""
},
{
"title"
:
"监控页"
,
"key"
:
"monitor"
,
"icon"
:
""
},
{
"title"
:
"工作台"
,
"key"
:
"workplace"
,
"icon"
:
""
}
]
},
{
"title"
:
"系统管理"
,
"key"
:
"system"
,
"component"
:
"PageView"
,
"icon"
:
"setting"
,
"children"
:
[
{
"title"
:
"用户管理"
,
"key"
:
"userList"
},
{
"title"
:
"角色管理"
,
"key"
:
"roleList"
},
{
"title"
:
"权限管理"
,
"key"
:
"tableList"
}
]
}
]
}
],
"status"
:
200
,
"timestamp"
:
1534844188679
}
\ No newline at end of file
src/config/router.config.js
View file @
7eb46668
// eslint-disable-next-line
// eslint-disable-next-line
import
{
UserLayout
,
BasicLayout
,
RouteView
,
BlankLayout
,
PageView
}
from
'@/layouts'
import
{
UserLayout
,
BlankLayout
}
from
'@/layouts'
import
{
bxAnaalyse
}
from
'@/core/icons'
export
const
asyncRouterMap
=
[
{
path
:
'/'
,
name
:
'index'
,
component
:
BasicLayout
,
meta
:
{
title
:
'首页'
},
redirect
:
'/dashboard/workplace'
,
children
:
[
// dashboard
{
path
:
'/dashboard'
,
name
:
'dashboard'
,
redirect
:
'/dashboard/workplace'
,
component
:
RouteView
,
meta
:
{
title
:
'仪表盘'
,
keepAlive
:
true
,
icon
:
bxAnaalyse
,
permission
:
[
'dashboard'
]
},
children
:
[
{
path
:
'/dashboard/analysis'
,
name
:
'Analysis'
,
component
:
()
=>
import
(
'@/views/dashboard/Analysis'
),
meta
:
{
title
:
'分析页'
,
keepAlive
:
false
,
permission
:
[
'dashboard'
]
}
},
// 外部链接
{
path
:
'https://www.baidu.com/'
,
name
:
'Monitor'
,
meta
:
{
title
:
'监控页(外部)'
,
target
:
'_blank'
}
},
{
path
:
'/dashboard/workplace'
,
name
:
'Workplace'
,
component
:
()
=>
import
(
'@/views/dashboard/Workplace'
),
meta
:
{
title
:
'工作台'
,
keepAlive
:
true
,
permission
:
[
'dashboard'
]
}
}
]
},
// forms
{
path
:
'/form'
,
redirect
:
'/form/base-form'
,
component
:
PageView
,
meta
:
{
title
:
'表单页'
,
icon
:
'form'
,
permission
:
[
'form'
]
},
children
:
[
{
path
:
'/form/base-form'
,
name
:
'BaseForm'
,
component
:
()
=>
import
(
'@/views/form/BasicForm'
),
meta
:
{
title
:
'基础表单'
,
keepAlive
:
true
,
permission
:
[
'form'
]
}
},
{
path
:
'/form/step-form'
,
name
:
'StepForm'
,
component
:
()
=>
import
(
'@/views/form/stepForm/StepForm'
),
meta
:
{
title
:
'分步表单'
,
keepAlive
:
true
,
permission
:
[
'form'
]
}
},
{
path
:
'/form/advanced-form'
,
name
:
'AdvanceForm'
,
component
:
()
=>
import
(
'@/views/form/advancedForm/AdvancedForm'
),
meta
:
{
title
:
'高级表单'
,
keepAlive
:
true
,
permission
:
[
'form'
]
}
}
]
},
// list
{
path
:
'/list'
,
name
:
'list'
,
component
:
PageView
,
redirect
:
'/list/table-list'
,
meta
:
{
title
:
'列表页'
,
icon
:
'table'
,
permission
:
[
'table'
]
},
children
:
[
{
path
:
'/list/table-list'
,
name
:
'TableListWrapper'
,
hideChildrenInMenu
:
true
,
// 强制显示 MenuItem 而不是 SubMenu
component
:
()
=>
import
(
'@/views/list/TableList'
),
meta
:
{
title
:
'查询表格'
,
keepAlive
:
true
,
permission
:
[
'table'
]
}
},
{
path
:
'/list/basic-list'
,
name
:
'BasicList'
,
component
:
()
=>
import
(
'@/views/list/StandardList'
),
meta
:
{
title
:
'标准列表'
,
keepAlive
:
true
,
permission
:
[
'table'
]
}
},
{
path
:
'/list/card'
,
name
:
'CardList'
,
component
:
()
=>
import
(
'@/views/list/CardList'
),
meta
:
{
title
:
'卡片列表'
,
keepAlive
:
true
,
permission
:
[
'table'
]
}
},
{
path
:
'/list/search'
,
name
:
'SearchList'
,
component
:
()
=>
import
(
'@/views/list/search/SearchLayout'
),
redirect
:
'/list/search/article'
,
meta
:
{
title
:
'搜索列表'
,
keepAlive
:
true
,
permission
:
[
'table'
]
},
children
:
[
{
path
:
'/list/search/article'
,
name
:
'SearchArticles'
,
component
:
()
=>
import
(
'../views/list/TableList'
),
meta
:
{
title
:
'搜索列表(文章)'
,
permission
:
[
'table'
]
}
},
{
path
:
'/list/search/project'
,
name
:
'SearchProjects'
,
component
:
()
=>
import
(
'../views/list/TableList'
),
meta
:
{
title
:
'搜索列表(项目)'
,
permission
:
[
'table'
]
}
},
{
path
:
'/list/search/application'
,
name
:
'SearchApplications'
,
component
:
()
=>
import
(
'../views/list/TableList'
),
meta
:
{
title
:
'搜索列表(应用)'
,
permission
:
[
'table'
]
}
}
]
}
]
},
// profile
{
path
:
'/profile'
,
name
:
'profile'
,
component
:
RouteView
,
redirect
:
'/profile/basic'
,
meta
:
{
title
:
'详情页'
,
icon
:
'profile'
,
permission
:
[
'profile'
]
},
children
:
[
{
path
:
'/profile/basic'
,
name
:
'ProfileBasic'
,
component
:
()
=>
import
(
'@/views/profile/basic/Index'
),
meta
:
{
title
:
'基础详情页'
,
permission
:
[
'profile'
]
}
},
{
path
:
'/profile/advanced'
,
name
:
'ProfileAdvanced'
,
component
:
()
=>
import
(
'@/views/profile/advanced/Advanced'
),
meta
:
{
title
:
'高级详情页'
,
permission
:
[
'profile'
]
}
}
]
},
// result
{
path
:
'/result'
,
name
:
'result'
,
component
:
PageView
,
redirect
:
'/result/success'
,
meta
:
{
title
:
'结果页'
,
icon
:
'check-circle-o'
,
permission
:
[
'result'
]
},
children
:
[
{
path
:
'/result/success'
,
name
:
'ResultSuccess'
,
component
:
()
=>
import
(
/* webpackChunkName: "result" */
'@/views/result/Success'
),
meta
:
{
title
:
'成功'
,
keepAlive
:
false
,
hiddenHeaderContent
:
true
,
permission
:
[
'result'
]
}
},
{
path
:
'/result/fail'
,
name
:
'ResultFail'
,
component
:
()
=>
import
(
/* webpackChunkName: "result" */
'@/views/result/Error'
),
meta
:
{
title
:
'失败'
,
keepAlive
:
false
,
hiddenHeaderContent
:
true
,
permission
:
[
'result'
]
}
}
]
},
// Exception
{
path
:
'/exception'
,
name
:
'exception'
,
component
:
RouteView
,
redirect
:
'/exception/403'
,
meta
:
{
title
:
'异常页'
,
icon
:
'warning'
,
permission
:
[
'exception'
]
},
children
:
[
{
path
:
'/exception/403'
,
name
:
'Exception403'
,
component
:
()
=>
import
(
/* webpackChunkName: "fail" */
'@/views/exception/403'
),
meta
:
{
title
:
'403'
,
permission
:
[
'exception'
]
}
},
{
path
:
'/exception/404'
,
name
:
'Exception404'
,
component
:
()
=>
import
(
/* webpackChunkName: "fail" */
'@/views/exception/404'
),
meta
:
{
title
:
'404'
,
permission
:
[
'exception'
]
}
},
{
path
:
'/exception/500'
,
name
:
'Exception500'
,
component
:
()
=>
import
(
/* webpackChunkName: "fail" */
'@/views/exception/500'
),
meta
:
{
title
:
'500'
,
permission
:
[
'exception'
]
}
}
]
},
// account
{
path
:
'/account'
,
component
:
RouteView
,
redirect
:
'/account/center'
,
name
:
'account'
,
meta
:
{
title
:
'个人页'
,
icon
:
'user'
,
keepAlive
:
true
,
permission
:
[
'user'
]
},
children
:
[
{
path
:
'/account/center'
,
name
:
'center'
,
component
:
()
=>
import
(
'@/views/account/center/Index'
),
meta
:
{
title
:
'个人中心'
,
keepAlive
:
true
,
permission
:
[
'user'
]
}
},
{
path
:
'/account/settings'
,
name
:
'settings'
,
component
:
()
=>
import
(
'@/views/account/settings/Index'
),
meta
:
{
title
:
'个人设置'
,
hideHeader
:
true
,
permission
:
[
'user'
]
},
redirect
:
'/account/settings/base'
,
hideChildrenInMenu
:
true
,
children
:
[
{
path
:
'/account/settings/base'
,
name
:
'BaseSettings'
,
component
:
()
=>
import
(
'@/views/account/settings/BaseSetting'
),
meta
:
{
title
:
'基本设置'
,
hidden
:
true
,
permission
:
[
'user'
]
}
},
{
path
:
'/account/settings/security'
,
name
:
'SecuritySettings'
,
component
:
()
=>
import
(
'@/views/account/settings/Security'
),
meta
:
{
title
:
'安全设置'
,
hidden
:
true
,
keepAlive
:
true
,
permission
:
[
'user'
]
}
},
{
path
:
'/account/settings/custom'
,
name
:
'CustomSettings'
,
component
:
()
=>
import
(
'@/views/account/settings/Custom'
),
meta
:
{
title
:
'个性化设置'
,
hidden
:
true
,
keepAlive
:
true
,
permission
:
[
'user'
]
}
},
{
path
:
'/account/settings/binding'
,
name
:
'BindingSettings'
,
component
:
()
=>
import
(
'@/views/account/settings/Binding'
),
meta
:
{
title
:
'账户绑定'
,
hidden
:
true
,
keepAlive
:
true
,
permission
:
[
'user'
]
}
},
{
path
:
'/account/settings/notification'
,
name
:
'NotificationSettings'
,
component
:
()
=>
import
(
'@/views/account/settings/Notification'
),
meta
:
{
title
:
'新消息通知'
,
hidden
:
true
,
keepAlive
:
true
,
permission
:
[
'user'
]
}
}
]
}
]
},
// other
{
path
:
'/other'
,
name
:
'otherPage'
,
component
:
PageView
,
meta
:
{
title
:
'其他组件'
,
icon
:
'slack'
,
permission
:
[
'dashboard'
]
},
redirect
:
'/other/icon-selector'
,
children
:
[
{
path
:
'/other/icon-selector'
,
name
:
'TestIconSelect'
,
component
:
()
=>
import
(
'@/views/other/IconSelectorView'
),
meta
:
{
title
:
'IconSelector'
,
icon
:
'tool'
,
keepAlive
:
true
,
permission
:
[
'dashboard'
]
}
},
{
path
:
'/other/list'
,
component
:
RouteView
,
meta
:
{
title
:
'业务布局'
,
icon
:
'layout'
,
permission
:
[
'support'
]
},
redirect
:
'/other/list/tree-list'
,
children
:
[
{
path
:
'/other/list/tree-list'
,
name
:
'TreeList'
,
component
:
()
=>
import
(
'@/views/other/TreeList'
),
meta
:
{
title
:
'树目录表格'
,
keepAlive
:
true
}
},
{
path
:
'/other/list/edit-table'
,
name
:
'EditList'
,
component
:
()
=>
import
(
'@/views/other/TableInnerEditList'
),
meta
:
{
title
:
'内联编辑表格'
,
keepAlive
:
true
}
},
{
path
:
'/other/list/user-list'
,
name
:
'UserList'
,
component
:
()
=>
import
(
'@/views/other/UserList'
),
meta
:
{
title
:
'用户列表'
,
keepAlive
:
true
}
},
{
path
:
'/other/list/role-list'
,
name
:
'RoleList'
,
component
:
()
=>
import
(
'@/views/other/RoleList'
),
meta
:
{
title
:
'角色列表'
,
keepAlive
:
true
}
},
{
path
:
'/other/list/system-role'
,
name
:
'SystemRole'
,
component
:
()
=>
import
(
'@/views/role/RoleList'
),
meta
:
{
title
:
'角色列表2'
,
keepAlive
:
true
}
},
{
path
:
'/other/list/permission-list'
,
name
:
'PermissionList'
,
component
:
()
=>
import
(
'@/views/other/PermissionList'
),
meta
:
{
title
:
'权限列表'
,
keepAlive
:
true
}
}
]
}
]
}
]
},
{
path
:
'*'
,
redirect
:
'/404'
,
hidden
:
true
}
]
/**
/**
* 基础路由
* 基础路由
...
@@ -370,5 +47,4 @@ export const constantRouterMap = [
...
@@ -370,5 +47,4 @@ export const constantRouterMap = [
path
:
'/404'
,
path
:
'/404'
,
component
:
()
=>
import
(
/* webpackChunkName: "fail" */
'@/views/exception/404'
)
component
:
()
=>
import
(
/* webpackChunkName: "fail" */
'@/views/exception/404'
)
}
}
]
]
src/permission.js
View file @
7eb46668
...
@@ -21,13 +21,12 @@ router.beforeEach((to, from, next) => {
...
@@ -21,13 +21,12 @@ router.beforeEach((to, from, next) => {
NProgress
.
done
()
NProgress
.
done
()
}
else
{
}
else
{
if
(
store
.
getters
.
roles
.
length
===
0
)
{
if
(
store
.
getters
.
roles
.
length
===
0
)
{
store
store
.
dispatch
(
'GetInfo'
)
.
dispatch
(
'GetInfo'
)
.
then
(
res
=>
{
.
then
(
res
=>
{
const
roles
=
res
.
result
&&
res
.
result
.
role
const
roles
=
res
.
result
&&
res
.
result
.
role
// 调用 vuex 的 从后端获取用户的路由菜单,动态添加可访问路由表
store
.
dispatch
(
'GenerateRoutes'
,
{
roles
}).
then
(()
=>
{
store
.
dispatch
(
'GenerateRoutes'
,
{
roles
}).
then
(()
=>
{
// 根据roles权限生成可访问的路由表
// 把已获取到的路由菜单加入到路由表中
// 动态添加可访问路由表
router
.
addRoutes
(
store
.
getters
.
addRouters
)
router
.
addRoutes
(
store
.
getters
.
addRouters
)
const
redirect
=
decodeURIComponent
(
from
.
query
.
redirect
||
to
.
path
)
const
redirect
=
decodeURIComponent
(
from
.
query
.
redirect
||
to
.
path
)
if
(
to
.
path
===
redirect
)
{
if
(
to
.
path
===
redirect
)
{
...
...
src/store/modules/permission.js
View file @
7eb46668
import
{
asyncRouterMap
,
constantRouterMap
}
from
'@/config/router.config'
import
{
constantRouterMap
}
from
'@/config/router.config'
import
{
generatorDynamicRouter
}
from
'@/utils/routerUtil'
/**
* 过滤账户是否拥有某一个权限,并将菜单从加载列表移除
*
* @param permission
* @param route
* @returns {boolean}
*/
function
hasPermission
(
permission
,
route
)
{
if
(
route
.
meta
&&
route
.
meta
.
permission
)
{
let
flag
=
false
for
(
let
i
=
0
,
len
=
permission
.
length
;
i
<
len
;
i
++
)
{
flag
=
route
.
meta
.
permission
.
includes
(
permission
[
i
])
if
(
flag
)
{
return
true
}
}
return
false
}
return
true
}
/**
* 单账户多角色时,使用该方法可过滤角色不存在的菜单
*
* @param roles
* @param route
* @returns {*}
*/
// eslint-disable-next-line
function
hasRole
(
roles
,
route
)
{
if
(
route
.
meta
&&
route
.
meta
.
roles
)
{
return
route
.
meta
.
roles
.
includes
(
roles
.
id
)
}
else
{
return
true
}
}
function
filterAsyncRouter
(
routerMap
,
roles
)
{
const
accessedRouters
=
routerMap
.
filter
(
route
=>
{
if
(
hasPermission
(
roles
.
permissionList
,
route
))
{
if
(
route
.
children
&&
route
.
children
.
length
)
{
route
.
children
=
filterAsyncRouter
(
route
.
children
,
roles
)
}
return
true
}
return
false
})
return
accessedRouters
}
const
permission
=
{
const
permission
=
{
state
:
{
state
:
{
...
@@ -62,12 +13,19 @@ const permission = {
...
@@ -62,12 +13,19 @@ const permission = {
}
}
},
},
actions
:
{
actions
:
{
/**
* 构建获取和构建路由和菜单信息
* @param commit
* @param data
* @returns {Promise<any>}
* @constructor
*/
GenerateRoutes
({
commit
},
data
)
{
GenerateRoutes
({
commit
},
data
)
{
return
new
Promise
(
resolve
=>
{
return
new
Promise
(
resolve
=>
{
const
{
roles
}
=
data
generatorDynamicRouter
(
data
).
then
(
routers
=>
{
const
accessedRouters
=
filterAsyncRouter
(
asyncRouterMap
,
role
s
)
commit
(
'SET_ROUTERS'
,
router
s
)
commit
(
'SET_ROUTERS'
,
accessedRouters
)
resolve
(
)
resolve
(
)
}
)
})
})
}
}
}
}
...
...
src/utils/routerUtil.js
0 → 100644
View file @
7eb46668
import
{
axios
}
from
'@/utils/request'
// eslint-disable-next-line
import
{
UserLayout
,
BasicLayout
,
RouteView
,
BlankLayout
,
PageView
}
from
'@/layouts'
// 前端路由表
const
constantRouterComponents
=
{
// 基础页面 layout 必须引入
BasicLayout
:
BasicLayout
,
BlankLayout
:
BlankLayout
,
RouteView
:
RouteView
,
PageView
:
PageView
,
// 你需要动态引入的页面组件
analysis
:
()
=>
import
(
'@/views/dashboard/Analysis'
),
workplace
:
()
=>
import
(
'@/views/dashboard/Workplace'
),
monitor
:
()
=>
import
(
'@/views/dashboard/Monitor'
)
// ...more
}
// 前端未找到页面路由(固定不用改)
const
notFoundRouter
=
{
path
:
'*'
,
redirect
:
'/404'
,
hidden
:
true
}
/**
* 获取后端路由信息的 axios API
* @returns {Promise}
*/
export
const
getRouterByUser
=
()
=>
{
return
axios
({
url
:
'/user/dynamic-menu'
,
method
:
'get'
/* headers: {
'Access-Token': 'xxx'
}
*/
})
}
/**
* 获取路由菜单信息
*
* 1. 调用 getRouterByUser() 访问后端接口获得路由结构数组
* @see https://github.com/sendya/ant-design-pro-vue/blob/feature/dynamic-menu/public/dynamic-menu.json
* 2. 调用
* @returns {Promise<any>}
*/
export
const
generatorDynamicRouter
=
()
=>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
// ajax
getRouterByUser
().
then
(
res
=>
{
const
result
=
res
.
result
const
routers
=
generator
(
result
)
routers
.
push
(
notFoundRouter
)
resolve
(
routers
)
}).
catch
(
err
=>
{
reject
(
err
)
})
})
}
/**
* 格式化 后端 结构信息并递归生成层级路由表
*
* @param routerMap
* @param parent
* @returns {*}
*/
export
const
generator
=
(
routerMap
,
parent
)
=>
{
return
routerMap
.
map
(
item
=>
{
const
currentRouter
=
{
// 路由地址 动态拼接生成如 /dashboard/workplace
path
:
`
${
parent
&&
parent
.
path
||
''
}
/
${
item
.
key
}
`
,
// 路由名称,建议唯一
name
:
item
.
name
||
item
.
key
||
''
,
// 该路由对应页面的 组件
component
:
constantRouterComponents
[
item
.
component
||
item
.
key
],
// meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉)
meta
:
{
title
:
item
.
title
,
icon
:
item
.
icon
||
undefined
,
permission
:
item
.
key
&&
[
item
.
key
]
||
null
}
}
// 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠
currentRouter
.
path
=
currentRouter
.
path
.
replace
(
'//'
,
'/'
)
// 重定向
item
.
redirect
&&
(
currentRouter
.
redirect
=
item
.
redirect
)
// 是否有子菜单,并递归处理
if
(
item
.
children
&&
item
.
children
.
length
>
0
)
{
// Recursion
currentRouter
.
children
=
generator
(
item
.
children
,
currentRouter
)
}
return
currentRouter
})
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment