新增权限管理功能

This commit is contained in:
macro 2020-02-16 14:27:58 +08:00
parent bdbffaedb6
commit 993dcd2dc7
28 changed files with 2188 additions and 23 deletions

View File

@ -24,3 +24,57 @@ export function logout() {
method: 'post' method: 'post'
}) })
} }
export function fetchList(params) {
return request({
url: '/admin/list',
method: 'get',
params: params
})
}
export function createAdmin(data) {
return request({
url: '/admin/register',
method: 'post',
data: data
})
}
export function updateAdmin(id, data) {
return request({
url: '/admin/update/' + id,
method: 'post',
data: data
})
}
export function updateStatus(id, params) {
return request({
url: '/admin/updateStatus/' + id,
method: 'post',
params: params
})
}
export function deleteAdmin(id) {
return request({
url: '/admin/delete/' + id,
method: 'post'
})
}
export function getRoleByAdmin(id) {
return request({
url: '/admin/role/' + id,
method: 'get'
})
}
export function allocRole(data) {
return request({
url: '/admin/role/update',
method: 'post',
data: data
})
}

55
src/api/menu.js Normal file
View File

@ -0,0 +1,55 @@
import request from '@/utils/request'
export function fetchList(parentId, params) {
return request({
url: '/menu/list/' + parentId,
method: 'get',
params: params
})
}
export function deleteMenu(id) {
return request({
url: '/menu/delete/' + id,
method: 'post'
})
}
export function createMenu(data) {
return request({
url: '/menu/create',
method: 'post',
data: data
})
}
export function updateMenu(id, data) {
return request({
url: '/menu/update/' + id,
method: 'post',
data: data
})
}
export function getMenu(id) {
return request({
url: '/menu/' + id,
method: 'get',
})
}
export function updateHidden(id, params) {
return request({
url: '/menu/updateHidden/' + id,
method: 'post',
params: params
})
}
export function fetchTreeList() {
return request({
url: '/menu/treeList',
method: 'get'
})
}

39
src/api/resource.js Normal file
View File

@ -0,0 +1,39 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url: '/resource/list',
method: 'get',
params: params
})
}
export function createResource(data) {
return request({
url: '/resource/create',
method: 'post',
data: data
})
}
export function updateResource(id, data) {
return request({
url: '/resource/update/' + id,
method: 'post',
data: data
})
}
export function deleteResource(id) {
return request({
url: '/resource/delete/' + id,
method: 'post'
})
}
export function fetchAllResourceList() {
return request({
url: '/resource/listAll',
method: 'get'
})
}

View File

@ -0,0 +1,31 @@
import request from '@/utils/request'
export function listAllCate() {
return request({
url: '/resourceCategory/listAll',
method: 'get'
})
}
export function createResourceCategory(data) {
return request({
url: '/resourceCategory/create',
method: 'post',
data: data
})
}
export function updateResourceCategory(id, data) {
return request({
url: '/resourceCategory/update/' + id,
method: 'post',
data: data
})
}
export function deleteResourceCategory(id) {
return request({
url: '/resourceCategory/delete/' + id,
method: 'post'
})
}

78
src/api/role.js Normal file
View File

@ -0,0 +1,78 @@
import request from '@/utils/request'
export function fetchList(params) {
return request({
url: '/role/list',
method: 'get',
params: params
})
}
export function createRole(data) {
return request({
url: '/role/create',
method: 'post',
data: data
})
}
export function updateRole(id, data) {
return request({
url: '/role/update/' + id,
method: 'post',
data: data
})
}
export function updateStatus(id, params) {
return request({
url: '/role/updateStatus/' + id,
method: 'post',
params: params
})
}
export function deleteRole(data) {
return request({
url:'/role/delete',
method:'post',
data:data
})
}
export function fetchAllRoleList() {
return request({
url: '/role/listAll',
method: 'get'
})
}
export function listMenuByRole(roleId) {
return request({
url: '/role/listMenu/'+roleId,
method: 'get'
})
}
export function listResourceByRole(roleId) {
return request({
url: '/role/listResource/'+roleId,
method: 'get'
})
}
export function allocMenu(data) {
return request({
url: '/role/allocMenu',
method: 'post',
data:data
})
}
export function allocResource(data) {
return request({
url: '/role/allocResource',
method: 'post',
data:data
})
}

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1580710958385" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4938" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M512 1024C229.248 1024 0 794.752 0 512S229.248 0 512 0s512 229.248 512 512-229.248 512-512 512z m0-938.666667C276.352 85.333333 85.333333 276.352 85.333333 512s191.018667 426.666667 426.666667 426.666667 426.666667-191.018667 426.666667-426.666667S747.648 85.333333 512 85.333333z m0 768a340.650667 340.650667 0 0 1-266.24-128A340.650667 340.650667 0 0 1 512 597.333333a340.48 340.48 0 0 1 266.197333 128A340.48 340.48 0 0 1 512 853.333333z m0-298.666666a170.666667 170.666667 0 1 1 0-341.333334 170.666667 170.666667 0 0 1 0 341.333334z" p-id="4939"></path></svg>

After

Width:  |  Height:  |  Size: 941 B

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1580711460345" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7290" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M374.272 440.832H127.488c-33.792 0-61.44-27.648-61.44-61.44V132.608c0-33.792 27.648-61.44 61.44-61.44h247.296c33.792 0 61.44 27.648 61.44 61.44v247.296c-0.512 33.792-27.648 60.928-61.952 60.928zM127.488 132.608v247.296h247.296V132.608H127.488zM762.88 492.032c-16.384 0-31.744-6.144-43.52-17.92l-174.592-174.592c-11.776-11.776-17.92-27.136-17.92-43.52s6.144-31.744 17.92-43.52l174.592-174.592c11.776-11.776 27.136-17.92 43.52-17.92s31.744 6.144 43.52 17.92l174.592 174.592c11.776 11.776 17.92 27.136 17.92 43.52s-6.144 31.744-17.92 43.52l-174.592 174.592c-11.776 11.776-27.136 17.92-43.52 17.92z m0-410.624L588.288 256 762.88 430.592 937.472 256 762.88 81.408zM374.272 952.832H127.488c-33.792 0-61.44-27.648-61.44-61.44v-247.296c0-33.792 27.648-61.44 61.44-61.44h247.296c33.792 0 61.44 27.648 61.44 61.44v247.296c-0.512 34.304-27.648 61.44-61.952 61.44z m-246.784-308.224v247.296h247.296v-247.296H127.488zM886.272 952.832h-247.296c-33.792 0-61.44-27.648-61.44-61.44v-247.296c0-33.792 27.648-61.44 61.44-61.44h247.296c33.792 0 61.44 27.648 61.44 61.44v247.296c0 34.304-27.136 61.44-61.44 61.44z m-246.784-308.224v247.296h247.296v-247.296h-247.296z" p-id="7291"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1580711541501" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10218" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M955.092096 763.262604l0-102.226243-69.523423-11.606339c-0.642636-1.61887-1.313925-3.234671-2.01182-4.845355l40.981367-57.363476-72.29249-72.29556-57.378825 40.951691c-1.604544-0.693802-3.218298-1.360997-4.835122-2.001587l-11.605316-69.505004-102.224196 0-11.606339 69.51933c-1.626034 0.644683-3.248997 1.316995-4.864798 2.016937l-57.343009-40.986483-15.316847 15.31787c-8.084119-3.351328-16.255219-6.407943-24.503067-9.169847 17.087167-10.036587 33.016974-22.307052 47.39852-36.68962 44.329625-44.329625 68.743664-103.270015 68.743664-165.964916 0-62.694901-24.413016-121.636315-68.743664-165.965939-44.328601-44.329625-103.270015-68.743664-165.964916-68.743664-62.694901 0-121.636315 24.413016-165.965939 68.743664-44.330648 44.329625-68.743664 103.271038-68.743664 165.965939 0 62.694901 24.413016 121.635291 68.743664 165.964916 14.387685 14.387685 30.321586 26.662243 47.415916 36.699853-9.124821 3.051499-18.155499 6.456039-27.076682 10.229992-44.551682 18.844184-84.556814 45.815465-118.907157 80.165808-34.349319 34.348296-61.320601 74.355475-80.163761 118.906134-19.515473 46.13883-29.40982 95.134731-29.40982 145.627728l51.223638 0c0-0.243547 0.008186-0.48607 0.00921-0.729617L568.286446 875.278817l34.093493 34.094516 57.376779-40.945551c1.613754 0.698918 3.227508 1.36816 4.838192 2.006703l11.607362 69.494771 102.224196 0 11.604292-69.518307c1.627057-0.645706 3.252067-1.319042 4.869914-2.02103l57.340963 40.961924 72.299653-72.29863-41.001833-57.354266c0.700965-1.613754 1.373277-3.230578 2.015913-4.850471L955.092096 763.262604zM260.51585 318.411957c0-101.174284 82.311681-183.485965 183.485965-183.485965 101.173261 0 183.484941 82.311681 183.484941 183.485965 0 101.173261-82.311681 183.483918-183.484941 183.483918C342.826508 501.895875 260.51585 419.585217 260.51585 318.411957zM539.391349 824.052109 125.316635 824.052109c24.923646-153.425322 158.330028-270.933619 318.685179-270.933619 35.588543 0 70.209038 5.66093 103.32425 16.864086l-17.237593 17.238616 40.98853 57.369615c-0.696872 1.611707-1.36509 3.224438-2.004657 4.839215l-69.537749 11.605316 0 102.224196 69.530586 11.610432c0.645706 1.629103 1.314948 3.243881 2.00568 4.842285L539.391349 824.052109zM855.620593 831.341119l-9.087982 9.081843-47.445592-33.836643-14.563694 8.170077c-7.928576 4.419659-16.367783 7.907087-25.115004 10.370185l-16.07614 4.564969-9.592472 57.466829-12.849656 0-9.575076-57.482179-16.138562-4.51585c-8.56098-2.400676-17.007349-5.90243-25.092491-10.402931l-14.59337-8.140401-47.397496 33.841759-9.086959-9.085936 33.890878-47.432289-8.192589-14.647605c-4.408403-7.848759-7.894807-16.273638-10.370185-25.073048l-4.562922-16.078187-57.467853-9.572006 0-12.849656 57.415664-9.586332 4.626367-16.118096c2.429329-8.650007 5.92085-17.084097 10.389628-25.091468l8.124028-14.612813-33.855062-47.38624 9.087982-9.087982 47.418986 33.863249 14.628162-8.157797c7.888667-4.426823 16.325827-7.92346 25.062815-10.387581l16.114003-4.524037 9.573029-57.493435 12.850679 0 9.596566 57.466829 16.095583 4.524037c8.613168 2.430352 17.060561 5.940292 25.146727 10.453073l14.57802 8.077979 47.376007-33.82334 9.087982 9.089006-33.898041 47.439452 8.190543 14.598486c4.41352 7.880481 7.91118 16.327874 10.404977 25.12626l4.569062 16.041348 57.434084 9.589402 0 12.844539-57.470923 9.549493-4.572132 16.159028c-2.397606 8.570189-5.889127 17.002232-10.379395 25.059745l-8.136308 14.595416L855.620593 831.341119zM727.311299 627.965152c-46.417169 0-84.181261 37.764092-84.181261 84.183308 0 46.420239 37.763069 84.186378 84.181261 84.186378 46.419216 0 84.184331-37.765115 84.184331-84.186378C811.49563 665.730267 773.730515 627.965152 727.311299 627.965152zM727.311299 743.063563c-17.045211 0-30.913057-13.868869-30.913057-30.915104s13.867846-30.915104 30.913057-30.915104c17.047258 0 30.916127 13.868869 30.916127 30.915104S744.358557 743.063563 727.311299 743.063563z" p-id="10219"></path></svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1580711048745" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6525" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M508.928 546.304c-120.32 0-218.112-97.792-218.112-218.112s97.792-218.112 218.112-218.112S727.04 207.872 727.04 328.192s-97.792 218.112-218.112 218.112z m0-385.024c-92.16 0-166.912 74.752-166.912 166.912s74.752 166.912 166.912 166.912S675.84 420.352 675.84 328.192s-74.752-166.912-166.912-166.912z" p-id="6526"></path><path d="M851.456 884.736c-14.336 0-25.6-11.264-25.6-25.6 0-167.936-136.704-304.64-304.64-304.64S216.576 691.2 216.576 859.136c0 14.336-11.264 25.6-25.6 25.6s-25.6-11.264-25.6-25.6c0-196.096 159.744-355.84 355.84-355.84s355.84 159.744 355.84 355.84c0 14.336-11.264 25.6-25.6 25.6zM293.888 451.072C194.048 451.072 112.64 370.176 112.64 270.336c0-99.84 80.896-180.736 180.736-180.736 42.496 0 83.456 14.848 115.2 41.472l-32.768 38.912c-22.016-18.432-52.224-29.184-82.432-29.184-71.68 0-129.536 57.856-129.536 129.536s58.368 129.536 130.048 129.536v51.2z" p-id="6527"></path><path d="M35.84 726.016c-14.336 0-25.6-11.264-25.6-25.6 0-161.792 131.584-293.376 293.376-293.376 14.336 0 25.6 11.264 25.6 25.6s-11.264 25.6-25.6 25.6c-133.632 0-242.176 108.544-242.176 242.176 0 14.336-11.264 25.6-25.6 25.6z" p-id="6528"></path><path d="M736.768 458.24v-51.2c71.68 0 129.536-57.856 129.536-129.536s-57.856-129.536-129.536-129.536c-30.72 0-59.392 10.24-82.432 29.184l-32.768-39.424c32.256-27.136 72.192-41.472 115.2-41.472 99.84 0 180.736 80.896 180.736 180.736 0 100.352-80.896 181.248-180.736 181.248z" p-id="6529"></path><path d="M994.304 726.016c-14.336 0-25.6-11.264-25.6-25.6 0-133.632-108.544-242.176-242.176-242.176-14.336 0-25.6-11.264-25.6-25.6s11.264-25.6 25.6-25.6c161.792 0 293.376 131.584 293.376 293.376 0 14.336-11.264 25.6-25.6 25.6z" p-id="6530"></path></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

1
src/icons/svg/ums.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1580698548494" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2117" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><defs><style type="text/css"></style></defs><path d="M512 38.4L85.344 183.456V550.4c0 149.344 85.344 281.6 226.144 345.6l192 89.6 204.8-93.856c140.8-59.744 230.4-196.256 230.4-349.856v-358.4L512.032 38.432z m341.344 503.456c0 119.456-68.256 226.144-179.2 273.056l-170.656 76.8-157.856-72.544c-106.656-46.944-174.944-153.6-174.944-268.8v-307.2l341.344-115.2 341.344 115.2v298.656z" p-id="2118"></path><path d="M341.344 384c0 81.056 55.456 145.056 128 166.4V768h85.344v-42.656h42.656V640h-42.656v-89.6c72.544-17.056 128-85.344 128-166.4 0-93.856-76.8-170.656-170.656-170.656s-170.656 76.8-170.656 170.656z m256 0c0 46.944-38.4 85.344-85.344 85.344s-85.344-38.4-85.344-85.344 38.4-85.344 85.344-85.344c46.944 0 85.344 38.4 85.344 85.344z" p-id="2119"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -15,7 +15,12 @@ router.beforeEach((to, from, next) => {
} else { } else {
if (store.getters.roles.length === 0) { if (store.getters.roles.length === 0) {
store.dispatch('GetInfo').then(res => { // 拉取用户信息 store.dispatch('GetInfo').then(res => { // 拉取用户信息
next() let menus=res.data.menus;
let username=res.data.username;
store.dispatch('GenerateRoutes', { menus,username }).then(() => { // 生成可访问的路由表
router.addRoutes(store.getters.addRouters); // 动态添加可访问路由表
next({ ...to, replace: true })
})
}).catch((err) => { }).catch((err) => {
store.dispatch('FedLogOut').then(() => { store.dispatch('FedLogOut').then(() => {
Message.error(err || 'Verification failed, please login again') Message.error(err || 'Verification failed, please login again')

View File

@ -31,7 +31,10 @@ export const constantRouterMap = [
component: () => import('@/views/home/index'), component: () => import('@/views/home/index'),
meta: {title: '首页', icon: 'home'} meta: {title: '首页', icon: 'home'}
}] }]
}, }
]
export const asyncRouterMap = [
{ {
path: '/pms', path: '/pms',
component: Layout, component: Layout,
@ -57,20 +60,6 @@ export const constantRouterMap = [
meta: {title: '修改商品', icon: 'product-add'}, meta: {title: '修改商品', icon: 'product-add'},
hidden: true hidden: true
}, },
{
path: 'productRecycle',
name: 'productRecycle',
component: () => import('@/views/pms/product/index'),
meta: {title: '商品回收站', icon: 'product-recycle'},
hidden: true
},
{
path: 'productComment',
name: 'productComment',
component: () => import('@/views/pms/product/index'),
meta: {title: '商品评价', icon: 'product-comment'},
hidden: true
},
{ {
path: 'productCate', path: 'productCate',
name: 'productCate', name: 'productCate',
@ -301,6 +290,74 @@ export const constantRouterMap = [
} }
] ]
}, },
{
path:'/ums',
component: Layout,
redirect: '/ums/admin',
name: 'ums',
meta: {title: '权限', icon: 'ums'},
children: [
{
path: 'admin',
name: 'admin',
component: () => import('@/views/ums/admin/index'),
meta: {title: '用户列表', icon: 'ums-admin'}
},
{
path: 'role',
name: 'role',
component: () => import('@/views/ums/role/index'),
meta: {title: '角色列表', icon: 'ums-role'}
},
{
path: 'allocMenu',
name: 'allocMenu',
component: () => import('@/views/ums/role/allocMenu'),
meta: {title: '分配菜单'},
hidden: true
},
{
path: 'allocResource',
name: 'allocResource',
component: () => import('@/views/ums/role/allocResource'),
meta: {title: '分配资源'},
hidden: true
},
{
path: 'menu',
name: 'menu',
component: () => import('@/views/ums/menu/index'),
meta: {title: '菜单列表', icon: 'ums-menu'}
},
{
path: 'addMenu',
name: 'addMenu',
component: () => import('@/views/ums/menu/add'),
meta: {title: '添加菜单'},
hidden: true
},
{
path: 'updateMenu',
name: 'updateMenu',
component: () => import('@/views/ums/menu/update'),
meta: {title: '修改菜单'},
hidden: true
},
{
path: 'resource',
name: 'resource',
component: () => import('@/views/ums/resource/index'),
meta: {title: '资源列表', icon: 'ums-resource'}
},
{
path: 'resourceCategory',
name: 'resourceCategory',
component: () => import('@/views/ums/resource/categoryList'),
meta: {title: '资源分类'},
hidden: true
}
]
},
{path: '*', redirect: '/404', hidden: true} {path: '*', redirect: '/404', hidden: true}
] ]

View File

@ -4,6 +4,8 @@ const getters = {
token: state => state.user.token, token: state => state.user.token,
avatar: state => state.user.avatar, avatar: state => state.user.avatar,
name: state => state.user.name, name: state => state.user.name,
roles: state => state.user.roles roles: state => state.user.roles,
addRouters: state => state.permission.addRouters,
routers: state => state.permission.routers
} }
export default getters export default getters

View File

@ -2,6 +2,7 @@ import Vue from 'vue'
import Vuex from 'vuex' import Vuex from 'vuex'
import app from './modules/app' import app from './modules/app'
import user from './modules/user' import user from './modules/user'
import permission from './modules/permission'
import getters from './getters' import getters from './getters'
Vue.use(Vuex) Vue.use(Vuex)
@ -9,7 +10,8 @@ Vue.use(Vuex)
const store = new Vuex.Store({ const store = new Vuex.Store({
modules: { modules: {
app, app,
user user,
permission
}, },
getters getters
}) })

View File

@ -0,0 +1,110 @@
import { asyncRouterMap, constantRouterMap } from '@/router/index';
//判断是否有权限访问该菜单
function hasPermission(menus, route) {
if (route.name) {
let currMenu = getMenu(route.name, menus);
if (currMenu!=null) {
//设置菜单的标题、图标和可见性
if (currMenu.title != null && currMenu.title !== '') {
route.meta.title = currMenu.title;
}
if (currMenu.icon != null && currMenu.title !== '') {
route.meta.icon = currMenu.icon;
}
if(currMenu.hidden!=null){
route.hidden = currMenu.hidden !== 0;
}
if (currMenu.sort != null && currMenu.sort !== '') {
route.sort = currMenu.sort;
}
return true;
} else {
route.sort = 0;
if (route.hidden !== undefined && route.hidden === true) {
return true;
} else {
return false;
}
}
} else {
return true
}
}
//根据路由名称获取菜单
function getMenu(name, menus) {
for (let i = 0; i < menus.length; i++) {
let menu = menus[i];
if (name===menu.name) {
return menu;
}
}
return null;
}
//对菜单进行排序
function sortRouters(accessedRouters) {
for (let i = 0; i < accessedRouters.length; i++) {
let router = accessedRouters[i];
if(router.children && router.children.length > 0){
router.children.sort(compare("sort"));
}
}
accessedRouters.sort(compare("sort"));
}
//降序比较函数
function compare(p){
return function(m,n){
let a = m[p];
let b = n[p];
return b - a;
}
}
const permission = {
state: {
routers: constantRouterMap,
addRouters: []
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers;
state.routers = constantRouterMap.concat(routers);
}
},
actions: {
GenerateRoutes({ commit }, data) {
return new Promise(resolve => {
const { menus } = data;
const { username } = data;
const accessedRouters = asyncRouterMap.filter(v => {
//admin帐号直接返回所有菜单
// if(username==='admin') return true;
if (hasPermission(menus, v)) {
if (v.children && v.children.length > 0) {
v.children = v.children.filter(child => {
if (hasPermission(menus, child)) {
return child
}
return false;
});
return v
} else {
return v
}
}
return false;
});
//对菜单进行排序
sortRouters(accessedRouters);
commit('SET_ROUTERS', accessedRouters);
resolve();
})
}
}
};
export default permission;

View File

@ -36,7 +36,7 @@ service.interceptors.response.use(
}) })
// 401:未登录; // 401:未登录;
if (res.code === 401||res.code === 403) { if (res.code === 401) {
MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', { MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
confirmButtonText: '重新登录', confirmButtonText: '重新登录',
cancelButtonText: '取消', cancelButtonText: '取消',

View File

@ -1,6 +1,7 @@
export function isvalidUsername(str) { export function isvalidUsername(str) {
const valid_map = ['admin', 'test'] // const valid_map = ['admin', 'test']
return valid_map.indexOf(str.trim()) >= 0 // return valid_map.indexOf(str.trim()) >= 0
return str.trim().length>=3
} }
/* 合法uri*/ /* 合法uri*/

View File

@ -23,10 +23,12 @@ export default {
components: { SidebarItem, ScrollBar }, components: { SidebarItem, ScrollBar },
computed: { computed: {
...mapGetters([ ...mapGetters([
'sidebar' 'sidebar',
'routers'
]), ]),
routes() { routes() {
return this.$router.options.routes // return this.$router.options.routes
return this.routers
}, },
isCollapse() { isCollapse() {
return !this.sidebar.opened return !this.sidebar.opened

View File

@ -0,0 +1,344 @@
<template> 
<div class="app-container">
<el-card class="filter-container" shadow="never">
<div>
<i class="el-icon-search"></i>
<span>筛选搜索</span>
<el-button
style="float:right"
type="primary"
@click="handleSearchList()"
size="small">
查询搜索
</el-button>
<el-button
style="float:right;margin-right: 15px"
@click="handleResetSearch()"
size="small">
重置
</el-button>
</div>
<div style="margin-top: 15px">
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<el-form-item label="输入搜索:">
<el-input v-model="listQuery.keyword" class="input-width" placeholder="帐号/姓名" clearable></el-input>
</el-form-item>
</el-form>
</div>
</el-card>
<el-card class="operate-container" shadow="never">
<i class="el-icon-tickets"></i>
<span>数据列表</span>
<el-button size="mini" class="btn-add" @click="handleAdd()" style="margin-left: 20px">添加</el-button>
</el-card>
<div class="table-container">
<el-table ref="adminTable"
:data="list"
style="width: 100%;"
v-loading="listLoading" border>
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="帐号" align="center">
<template slot-scope="scope">{{scope.row.username}}</template>
</el-table-column>
<el-table-column label="姓名" align="center">
<template slot-scope="scope">{{scope.row.nickName}}</template>
</el-table-column>
<el-table-column label="邮箱" align="center">
<template slot-scope="scope">{{scope.row.email}}</template>
</el-table-column>
<el-table-column label="添加时间" width="160" align="center">
<template slot-scope="scope">{{scope.row.createTime | formatDateTime}}</template>
</el-table-column>
<el-table-column label="最后登录" width="160" align="center">
<template slot-scope="scope">{{scope.row.loginTime | formatDateTime}}</template>
</el-table-column>
<el-table-column label="是否启用" width="140" align="center">
<template slot-scope="scope">
<el-switch
@change="handleStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.status">
</el-switch>
</template>
</el-table-column>
<el-table-column label="操作" width="180" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleSelectRole(scope.$index, scope.row)">分配角色
</el-button>
<el-button size="mini"
type="text"
@click="handleUpdate(scope.$index, scope.row)">
编辑
</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination-container">
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper"
:current-page.sync="listQuery.pageNum"
:page-size="listQuery.pageSize"
:page-sizes="[10,15,20]"
:total="total">
</el-pagination>
</div>
<el-dialog
:title="isEdit?'编辑用户':'添加用户'"
:visible.sync="dialogVisible"
width="40%">
<el-form :model="admin"
ref="adminForm"
label-width="150px" size="small">
<el-form-item label="帐号:">
<el-input v-model="admin.username" style="width: 250px"></el-input>
</el-form-item>
<el-form-item label="姓名:">
<el-input v-model="admin.nickName" style="width: 250px"></el-input>
</el-form-item>
<el-form-item label="邮箱:">
<el-input v-model="admin.email" style="width: 250px"></el-input>
</el-form-item>
<el-form-item label="密码:">
<el-input v-model="admin.password" type="password" style="width: 250px"></el-input>
</el-form-item>
<el-form-item label="备注:">
<el-input v-model="admin.note"
type="textarea"
:rows="5"
style="width: 250px"></el-input>
</el-form-item>
<el-form-item label="是否启用:">
<el-radio-group v-model="admin.status">
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleDialogConfirm()" size="small"> </el-button>
</span>
</el-dialog>
<el-dialog
title="分配角色"
:visible.sync="allocDialogVisible"
width="30%">
<el-select v-model="allocRoleIds" multiple placeholder="请选择" size="small" style="width: 80%">
<el-option
v-for="item in allRoleList"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
<span slot="footer" class="dialog-footer">
<el-button @click="allocDialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleAllocDialogConfirm()" size="small"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {fetchList,createAdmin,updateAdmin,updateStatus,deleteAdmin,getRoleByAdmin,allocRole} from '@/api/login';
import {fetchAllRoleList} from '@/api/role';
import {formatDate} from '@/utils/date';
const defaultListQuery = {
pageNum: 1,
pageSize: 10,
keyword: null
};
const defaultAdmin = {
id: null,
username: null,
password: null,
nickName: null,
email: null,
note: null,
status: 1
};
export default {
name: 'adminList',
data() {
return {
listQuery: Object.assign({}, defaultListQuery),
list: null,
total: null,
listLoading: false,
dialogVisible: false,
admin: Object.assign({}, defaultAdmin),
isEdit: false,
allocDialogVisible: false,
allocRoleIds:[],
allRoleList:[],
allocAdminId:null
}
},
created() {
this.getList();
this.getAllRoleList();
},
filters: {
formatDateTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
}
},
methods: {
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
},
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
handleAdd() {
this.dialogVisible = true;
this.isEdit = false;
this.admin = Object.assign({},defaultAdmin);
},
handleStatusChange(index, row) {
this.$confirm('是否要修改该状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
updateStatus(row.id, {status: row.status}).then(response => {
this.$message({
type: 'success',
message: '修改成功!'
});
});
}).catch(() => {
this.$message({
type: 'info',
message: '取消修改'
});
this.getList();
});
},
handleDelete(index, row) {
this.$confirm('是否要删除该用户?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteAdmin(row.id).then(response => {
this.$message({
type: 'success',
message: '删除成功!'
});
this.getList();
});
});
},
handleUpdate(index, row) {
this.dialogVisible = true;
this.isEdit = true;
this.admin = Object.assign({},row);
},
handleDialogConfirm() {
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) {
updateAdmin(this.admin.id,this.admin).then(response => {
this.$message({
message: '修改成功!',
type: 'success'
});
this.dialogVisible =false;
this.getList();
})
} else {
createAdmin(this.admin).then(response => {
this.$message({
message: '添加成功!',
type: 'success'
});
this.dialogVisible =false;
this.getList();
})
}
})
},
handleAllocDialogConfirm(){
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let params = new URLSearchParams();
params.append("adminId", this.allocAdminId);
params.append("roleIds", this.allocRoleIds);
allocRole(params).then(response => {
this.$message({
message: '分配成功!',
type: 'success'
});
this.allocDialogVisible = false;
})
})
},
handleSelectRole(index,row){
this.allocAdminId = row.id;
this.allocDialogVisible = true;
this.getRoleListByAdmin(row.id);
},
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
});
},
getAllRoleList() {
fetchAllRoleList().then(response => {
this.allRoleList = response.data;
});
},
getRoleListByAdmin(adminId) {
getRoleByAdmin(adminId).then(response => {
let allocRoleList = response.data;
this.allocRoleIds=[];
if(allocRoleList!=null&&allocRoleList.length>0){
for(let i=0;i<allocRoleList.length;i++){
this.allocRoleIds.push(allocRoleList[i].id);
}
}
});
}
}
}
</script>
<style></style>

View File

@ -0,0 +1,14 @@
<template> 
<menu-detail :is-edit='false'></menu-detail>
</template>
<script>
import MenuDetail from './components/MenuDetail'
export default {
name: 'addMenu',
components: { MenuDetail }
}
</script>
<style>
</style>

View File

@ -0,0 +1,153 @@
<template>
<el-card class="form-container" shadow="never">
<el-form :model="menu"
:rules="rules"
ref="menuFrom"
label-width="150px">
<el-form-item label="菜单名称:" prop="title">
<el-input v-model="menu.title"></el-input>
</el-form-item>
<el-form-item label="上级菜单:">
<el-select v-model="menu.parentId"
placeholder="请选择菜单">
<el-option
v-for="item in selectMenuList"
:key="item.id"
:label="item.title"
:value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="前端名称:" prop="name">
<el-input v-model="menu.name"></el-input>
</el-form-item>
<el-form-item label="前端图标:" prop="icon">
<el-input v-model="menu.icon" style="width: 80%"></el-input>
<svg-icon style="margin-left: 8px" :icon-class="menu.icon"></svg-icon>
</el-form-item>
<el-form-item label="是否显示:">
<el-radio-group v-model="menu.hidden">
<el-radio :label="0"></el-radio>
<el-radio :label="1"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="排序:">
<el-input v-model="menu.sort"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit('menuFrom')">提交</el-button>
<el-button v-if="!isEdit" @click="resetForm('menuFrom')">重置</el-button>
</el-form-item>
</el-form>
</el-card>
</template>
<script>
import {fetchList, createMenu, updateMenu, getMenu} from '@/api/menu';
const defaultMenu = {
title: '',
parentId: 0,
name: '',
icon: '',
hidden: 0,
sort: 0
};
export default {
name: "MenuDetail",
props: {
isEdit: {
type: Boolean,
default: false
}
},
data() {
return {
menu: Object.assign({}, defaultMenu),
selectMenuList: [],
rules: {
title: [
{required: true, message: '请输入菜单名称', trigger: 'blur'},
{min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'}
],
name: [
{required: true, message: '请输入前端名称', trigger: 'blur'},
{min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'}
],
icon: [
{required: true, message: '请输入前端图标', trigger: 'blur'},
{min: 2, max: 140, message: '长度在 2 到 140 个字符', trigger: 'blur'}
]
}
}
},
created() {
if (this.isEdit) {
getMenu(this.$route.query.id).then(response => {
this.menu = response.data;
});
} else {
this.menu = Object.assign({}, defaultMenu);
}
this.getSelectMenuList();
},
methods: {
getSelectMenuList() {
fetchList(0, {pageSize: 100, pageNum: 1}).then(response => {
this.selectMenuList = response.data.list;
this.selectMenuList.unshift({id: 0, title: '无上级菜单'});
});
},
onSubmit(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
this.$confirm('是否提交数据', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) {
updateMenu(this.$route.query.id, this.menu).then(response => {
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
this.$router.back();
});
} else {
createMenu(this.menu).then(response => {
this.$refs[formName].resetFields();
this.resetForm(formName);
this.$message({
message: '提交成功',
type: 'success',
duration: 1000
});
this.$router.back();
});
}
});
} else {
this.$message({
message: '验证失败',
type: 'error',
duration: 1000
});
return false;
}
});
},
resetForm(formName) {
this.$refs[formName].resetFields();
this.menu = Object.assign({}, defaultMenu);
this.getSelectMenuList();
},
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,196 @@
<template>
<div class="app-container">
<el-card class="operate-container" shadow="never">
<i class="el-icon-tickets" style="margin-top: 5px"></i>
<span style="margin-top: 5px">数据列表</span>
<el-button
class="btn-add"
@click="handleAddMenu()"
size="mini">
添加
</el-button>
</el-card>
<div class="table-container">
<el-table ref="menuTable"
style="width: 100%"
:data="list"
v-loading="listLoading" border>
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="菜单名称" align="center">
<template slot-scope="scope">{{scope.row.title}}</template>
</el-table-column>
<el-table-column label="菜单级数" width="100" align="center">
<template slot-scope="scope">{{scope.row.level | levelFilter}}</template>
</el-table-column>
<el-table-column label="前端名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<el-table-column label="前端图标" width="100" align="center">
<template slot-scope="scope"><svg-icon :icon-class="scope.row.icon"></svg-icon></template>
</el-table-column>
<el-table-column label="是否显示" width="100" align="center">
<template slot-scope="scope">
<el-switch
@change="handleHiddenChange(scope.$index, scope.row)"
:active-value="0"
:inactive-value="1"
v-model="scope.row.hidden">
</el-switch>
</template>
</el-table-column>
<el-table-column label="排序" width="100" align="center">
<template slot-scope="scope">{{scope.row.sort }}</template>
</el-table-column>
<el-table-column label="设置" width="120" align="center">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
:disabled="scope.row.level | disableNextLevel"
@click="handleShowNextLevel(scope.$index, scope.row)">查看下级
</el-button>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
@click="handleUpdate(scope.$index, scope.row)">编辑
</el-button>
<el-button
size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination-container">
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper"
:page-size="listQuery.pageSize"
:page-sizes="[10,15,20]"
:current-page.sync="listQuery.pageNum"
:total="total">
</el-pagination>
</div>
</div>
</template>
<script>
import {fetchList,deleteMenu,updateMenu,updateHidden} from '@/api/menu'
export default {
name: "menuList",
data() {
return {
list: null,
total: null,
listLoading: true,
listQuery: {
pageNum: 1,
pageSize: 5
},
parentId: 0
}
},
created() {
this.resetParentId();
this.getList();
},
watch: {
$route(route) {
this.resetParentId();
this.getList();
}
},
methods: {
resetParentId(){
this.listQuery.pageNum = 1;
if (this.$route.query.parentId != null) {
this.parentId = this.$route.query.parentId;
} else {
this.parentId = 0;
}
},
handleAddMenu() {
this.$router.push('/ums/addMenu');
},
getList() {
this.listLoading = true;
fetchList(this.parentId, this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
});
},
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
handleHiddenChange(index, row) {
updateHidden(row.id,{hidden:row.hidden}).then(response=>{
this.$message({
message: '修改成功',
type: 'success',
duration: 1000
});
});
},
handleShowNextLevel(index, row) {
this.$router.push({path: '/ums/menu', query: {parentId: row.id}})
},
handleUpdate(index, row) {
this.$router.push({path:'/ums/updateMenu',query:{id:row.id}});
},
handleDelete(index, row) {
this.$confirm('是否要删除该菜单', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteMenu(row.id).then(response => {
this.$message({
message: '删除成功',
type: 'success',
duration: 1000
});
this.getList();
});
});
}
},
filters: {
levelFilter(value) {
if (value === 0) {
return '一级';
} else if (value === 1) {
return '二级';
}
},
disableNextLevel(value) {
if (value === 0) {
return false;
} else {
return true;
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,14 @@
<template> 
<menu-detail :is-edit='true'></menu-detail>
</template>
<script>
import MenuDetail from './components/MenuDetail'
export default {
name: 'updateMenu',
components: { MenuDetail }
}
</script>
<style>
</style>

View File

@ -0,0 +1,156 @@
<template> 
<div class="app-container">
<el-card shadow="never" class="operate-container">
<i class="el-icon-tickets"></i>
<span>数据列表</span>
<el-button size="mini" class="btn-add" @click="handleAdd()">添加</el-button>
</el-card>
<div class="table-container">
<el-table ref="resourceCategoryTable"
:data="list"
style="width: 100%;"
v-loading="listLoading" border>
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<el-table-column label="创建时间" align="center">
<template slot-scope="scope">{{scope.row.createTime | formatDateTime}}</template>
</el-table-column>
<el-table-column label="排序" align="center">
<template slot-scope="scope">{{scope.row.sort}}</template>
</el-table-column>
<el-table-column label="操作" width="180" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleUpdate(scope.$index, scope.row)">编辑
</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<el-dialog
title="添加分类"
:visible.sync="dialogVisible"
width="40%">
<el-form :model="resourceCategory"
ref="resourceCategoryForm"
label-width="150px" size="small">
<el-form-item label="名称:">
<el-input v-model="resourceCategory.name" style="width: 250px"></el-input>
</el-form-item>
<el-form-item label="排序:">
<el-input v-model="resourceCategory.sort" style="width: 250px"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleDialogConfirm()" size="small"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {listAllCate,createResourceCategory,updateResourceCategory,deleteResourceCategory} from '@/api/resourceCategory';
import {formatDate} from '@/utils/date';
const defaultResourceCategory={
name:null,
sort:0
};
export default {
name: 'resourceCategoryList',
data() {
return {
list: null,
listLoading: false,
dialogVisible:false,
isEdit:false,
resourceCategory:Object.assign({},defaultResourceCategory)
}
},
created() {
this.getList();
},
filters:{
formatDateTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
}
},
methods: {
handleAdd() {
this.dialogVisible = true;
this.isEdit = false;
this.resourceCategory = Object.assign({},defaultResourceCategory);
},
handleUpdate(index,row){
this.dialogVisible = true;
this.isEdit = true;
this.resourceCategory = Object.assign({},row);
},
handleDelete(index,row){
this.$confirm('是否要删除该分类?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteResourceCategory(row.id).then(response => {
this.$message({
type: 'success',
message: '删除成功!'
});
this.getList();
});
});
},
handleDialogConfirm() {
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) {
updateResourceCategory(this.resourceCategory.id,this.resourceCategory).then(response => {
this.$message({
message: '修改成功!',
type: 'success'
});
this.dialogVisible =false;
this.getList();
})
} else {
createResourceCategory(this.resourceCategory).then(response => {
this.$message({
message: '添加成功!',
type: 'success'
});
this.dialogVisible =false;
this.getList();
})
}
})
},
getList() {
this.listLoading = true;
listAllCate({}).then(response => {
this.listLoading = false;
this.list = response.data;
});
}
}
}
</script>
<style>
</style>

View File

@ -0,0 +1,273 @@
<template> 
<div class="app-container">
<el-card class="filter-container" shadow="never">
<div>
<i class="el-icon-search"></i>
<span>筛选搜索</span>
<el-button
style="float:right"
type="primary"
@click="handleSearchList()"
size="small">
查询搜索
</el-button>
<el-button
style="float:right;margin-right: 15px"
@click="handleResetSearch()"
size="small">
重置
</el-button>
</div>
<div style="margin-top: 15px">
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<el-form-item label="资源名称:">
<el-input v-model="listQuery.nameKeyword" class="input-width" placeholder="资源名称" clearable></el-input>
</el-form-item>
<el-form-item label="资源路径:">
<el-input v-model="listQuery.urlKeyword" class="input-width" placeholder="资料路径" clearable></el-input>
</el-form-item>
<el-form-item label="资源分类:">
<el-select v-model="listQuery.categoryId" placeholder="全部" clearable class="input-width">
<el-option v-for="item in categoryOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
</el-form>
</div>
</el-card>
<el-card class="operate-container" shadow="never">
<i class="el-icon-tickets"></i>
<span>数据列表</span>
<el-button size="mini" class="btn-add" @click="handleAdd()" style="margin-left: 20px">添加</el-button>
<el-button size="mini" class="btn-add" @click="handleShowCategory()">资源分类</el-button>
</el-card>
<div class="table-container">
<el-table ref="resourceTable"
:data="list"
style="width: 100%;"
v-loading="listLoading" border>
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="资源名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<el-table-column label="资源路径" align="center">
<template slot-scope="scope">{{scope.row.url}}</template>
</el-table-column>
<el-table-column label="描述" align="center">
<template slot-scope="scope">{{scope.row.description}}</template>
</el-table-column>
<el-table-column label="添加时间" width="160" align="center">
<template slot-scope="scope">{{scope.row.createTime | formatDateTime}}</template>
</el-table-column>
<el-table-column label="操作" width="140" align="center">
<template slot-scope="scope">
<el-button size="mini"
type="text"
@click="handleUpdate(scope.$index, scope.row)">
编辑
</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination-container">
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper"
:current-page.sync="listQuery.pageNum"
:page-size="listQuery.pageSize"
:page-sizes="[10,15,20]"
:total="total">
</el-pagination>
</div>
<el-dialog
:title="isEdit?'编辑资源':'添加资源'"
:visible.sync="dialogVisible"
width="40%">
<el-form :model="resource"
ref="resourceForm"
label-width="150px" size="small">
<el-form-item label="资源名称:">
<el-input v-model="resource.name" style="width: 250px"></el-input>
</el-form-item>
<el-form-item label="资源路径:">
<el-input v-model="resource.url" style="width: 250px"></el-input>
</el-form-item>
<el-form-item label="资源分类:">
<el-select v-model="resource.categoryId" placeholder="全部" clearable style="width: 250px">
<el-option v-for="item in categoryOptions"
:key="item.value"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="描述:">
<el-input v-model="resource.description"
type="textarea"
:rows="5"
style="width: 250px"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleDialogConfirm()" size="small"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {fetchList,createResource,updateResource,deleteResource} from '@/api/resource';
import {listAllCate} from '@/api/resourceCategory';
import {formatDate} from '@/utils/date';
const defaultListQuery = {
pageNum: 1,
pageSize: 10,
nameKeyword: null,
urlKeyword: null,
categoryId:null
};
const defaultResource = {
id: null,
name: null,
url: null,
categoryId: null,
description:''
};
export default {
name: 'resourceList',
data() {
return {
listQuery: Object.assign({}, defaultListQuery),
list: null,
total: null,
listLoading: false,
dialogVisible: false,
resource: Object.assign({}, defaultResource),
isEdit: false,
categoryOptions:[],
defaultCategoryId:null
}
},
created() {
this.getList();
this.getCateList();
},
filters: {
formatDateTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
}
},
methods: {
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
},
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
handleAdd() {
this.dialogVisible = true;
this.isEdit = false;
this.resource = Object.assign({},defaultResource);
this.resource.categoryId = this.defaultCategoryId;
},
handleDelete(index, row) {
this.$confirm('是否要删除该资源?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteResource(row.id).then(response => {
this.$message({
type: 'success',
message: '删除成功!'
});
this.getList();
});
});
},
handleUpdate(index, row) {
this.dialogVisible = true;
this.isEdit = true;
this.resource = Object.assign({},row);
},
handleDialogConfirm() {
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) {
updateResource(this.resource.id,this.resource).then(response => {
this.$message({
message: '修改成功!',
type: 'success'
});
this.dialogVisible =false;
this.getList();
})
} else {
createResource(this.resource).then(response => {
this.$message({
message: '添加成功!',
type: 'success'
});
this.dialogVisible =false;
this.getList();
})
}
})
},
handleShowCategory(){
this.$router.push({path: '/ums/resourceCategory'})
},
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
});
},
getCateList(){
listAllCate().then(response=>{
let cateList = response.data;
for(let i=0;i<cateList.length;i++){
let cate = cateList[i];
this.categoryOptions.push({label:cate.name,value:cate.id});
}
this.defaultCategoryId=cateList[0].id;
})
}
}
}
</script>
<style></style>

View File

@ -0,0 +1,101 @@
<template>
<el-card class="form-container" shadow="never">
<el-tree
:data="menuTreeList"
show-checkbox
default-expand-all
node-key="id"
ref="tree"
highlight-current
:props="defaultProps">
</el-tree>
<div style="margin-top: 20px" align="center">
<el-button type="primary" @click="handleSave()">保存</el-button>
<el-button @click="handleClear()">清空</el-button>
</div>
</el-card>
</template>
<script>
import {fetchTreeList} from '@/api/menu';
import {listMenuByRole,allocMenu} from '@/api/role';
export default {
name: "allocMenu",
data() {
return {
menuTreeList: [],
defaultProps: {
children: 'children',
label: 'title'
},
roleId:null
};
},
created() {
this.roleId = this.$route.query.roleId;
this.treeList();
this.getRoleMenu(this.roleId);
},
methods: {
treeList() {
fetchTreeList().then(response => {
this.menuTreeList = response.data;
});
},
getRoleMenu(roleId){
listMenuByRole(roleId).then(response=>{
let menuList = response.data;
let checkedMenuIds=[];
if(menuList!=null&&menuList.length>0){
for(let i=0;i<menuList.length;i++){
let menu = menuList[i];
if(menu.parentId!==0){
checkedMenuIds.push(menu.id);
}
}
}
this.$refs.tree.setCheckedKeys(checkedMenuIds);
});
},
handleSave() {
let checkedNodes = this.$refs.tree.getCheckedNodes();
let checkedMenuIds=new Set();
if(checkedNodes!=null&&checkedNodes.length>0){
for(let i=0;i<checkedNodes.length;i++){
let checkedNode = checkedNodes[i];
checkedMenuIds.add(checkedNode.id);
if(checkedNode.parentId!==0){
checkedMenuIds.add(checkedNode.parentId);
}
}
}
this.$confirm('是否分配菜单?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(()=>{
let params = new URLSearchParams();
params.append("roleId",this.roleId);
params.append("menuIds",Array.from(checkedMenuIds));
allocMenu(params).then(response => {
this.$message({
message: '分配成功',
type: 'success',
duration: 1000
});
this.$router.back();
})
})
},
handleClear() {
this.$refs.tree.setCheckedKeys([]);
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,187 @@
<template>
<el-card class="form-container" shadow="never">
<div v-for="(cate,index) in allResourceCate" :class="index===0?'top-line':null" :key="'cate'+cate.id">
<el-row class="table-layout" style="background: #F2F6FC;">
<el-checkbox v-model="cate.checked"
:indeterminate="isIndeterminate(cate.id)"
@change="handleCheckAllChange(cate)">
{{cate.name}}
</el-checkbox>
</el-row>
<el-row class="table-layout">
<el-col :span="8" v-for="resource in getResourceByCate(cate.id)" :key="resource.id" style="padding: 4px 0">
<el-checkbox v-model="resource.checked" @change="handleCheckChange(resource)">
{{resource.name}}
</el-checkbox>
</el-col>
</el-row>
</div>
<div style="margin-top: 20px" align="center">
<el-button type="primary" @click="handleSave()">保存</el-button>
<el-button @click="handleClear()">清空</el-button>
</div>
</el-card>
</template>
<script>
import {fetchAllResourceList} from '@/api/resource';
import {listAllCate} from '@/api/resourceCategory';
import {allocResource,listResourceByRole} from '@/api/role';
export default {
name: "allocResource",
data() {
return {
roleId: null,
allResource: null,
allResourceCate: null
};
},
created() {
this.roleId = this.$route.query.roleId;
this.getAllResourceCateList();
},
methods: {
getAllResourceList() {
fetchAllResourceList().then(response => {
this.allResource = response.data;
for (let i = 0; i < this.allResource.length; i++) {
this.allResource[i].checked = false;
}
this.getResourceByRole(this.roleId);
});
},
getAllResourceCateList() {
listAllCate().then(response => {
this.allResourceCate = response.data;
for (let i = 0; i < this.allResourceCate.length; i++) {
this.allResourceCate[i].checked = false;
}
this.getAllResourceList();
});
},
getResourceByCate(categoryId) {
let cateResource = [];
if (this.allResource == null) return null;
for (let i = 0; i < this.allResource.length; i++) {
let resource = this.allResource[i];
if (resource.categoryId === categoryId) {
cateResource.push(resource);
}
}
return cateResource;
},
getResourceByRole(roleId){
listResourceByRole(roleId).then(response=>{
let allocResource = response.data;
this.allResource.forEach(item=>{
item.checked = this.getResourceChecked(item.id,allocResource);
});
this.allResourceCate.forEach(item=>{
item.checked = this.isAllChecked(item.id);
});
this.$forceUpdate();
});
},
getResourceChecked(resourceId,allocResource){
if(allocResource==null||allocResource.length===0) return false;
for(let i=0;i<allocResource.length;i++){
if(allocResource[i].id===resourceId){
return true;
}
}
return false;
},
isIndeterminate(categoryId) {
let cateResources = this.getResourceByCate(categoryId);
if (cateResources == null) return false;
let checkedCount = 0;
for (let i = 0; i < cateResources.length; i++) {
if (cateResources[i].checked === true) {
checkedCount++;
}
}
return !(checkedCount === 0 || checkedCount === cateResources.length);
},
isAllChecked(categoryId) {
let cateResources = this.getResourceByCate(categoryId);
if (cateResources == null) return false;
let checkedCount = 0;
for (let i = 0; i < cateResources.length; i++) {
if (cateResources[i].checked === true) {
checkedCount++;
}
}
if(checkedCount===0){
return false;
}
return checkedCount === cateResources.length;
},
handleSave() {
this.$confirm('是否分配资源?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let checkedResourceIds = new Set();
if (this.allResource != null && this.allResource.length > 0) {
this.allResource.forEach(item => {
if (item.checked) {
checkedResourceIds.add(item.id);
}
});
}
let params = new URLSearchParams();
params.append("roleId", this.roleId);
params.append("resourceIds", Array.from(checkedResourceIds));
allocResource(params).then(response => {
this.$message({
message: '分配成功',
type: 'success',
duration: 1000
});
this.$router.back();
})
})
},
handleClear() {
this.allResourceCate.forEach(item => {
item.checked = false;
});
this.allResource.forEach(item => {
item.checked = false;
});
this.$forceUpdate();
},
handleCheckAllChange(cate) {
let cateResources = this.getResourceByCate(cate.id);
for (let i = 0; i < cateResources.length; i++) {
cateResources[i].checked = cate.checked;
}
this.$forceUpdate();
},
handleCheckChange(resource) {
this.allResourceCate.forEach(item=>{
if(item.id===resource.categoryId){
item.checked = this.isAllChecked(resource.categoryId);
}
});
this.$forceUpdate();
}
}
}
</script>
<style scoped>
.table-layout {
padding: 20px;
border-left: 1px solid #DCDFE6;
border-right: 1px solid #DCDFE6;
border-bottom: 1px solid #DCDFE6;
}
.top-line {
border-top: 1px solid #DCDFE6;
}
</style>

View File

@ -0,0 +1,286 @@
<template> 
<div class="app-container">
<el-card class="filter-container" shadow="never">
<div>
<i class="el-icon-search"></i>
<span>筛选搜索</span>
<el-button
style="float:right"
type="primary"
@click="handleSearchList()"
size="small">
查询搜索
</el-button>
<el-button
style="float:right;margin-right: 15px"
@click="handleResetSearch()"
size="small">
重置
</el-button>
</div>
<div style="margin-top: 15px">
<el-form :inline="true" :model="listQuery" size="small" label-width="140px">
<el-form-item label="输入搜索:">
<el-input v-model="listQuery.keyword" class="input-width" placeholder="角色名称" clearable></el-input>
</el-form-item>
</el-form>
</div>
</el-card>
<el-card class="operate-container" shadow="never">
<i class="el-icon-tickets"></i>
<span>数据列表</span>
<el-button size="mini" class="btn-add" @click="handleAdd()" style="margin-left: 20px">添加</el-button>
</el-card>
<div class="table-container">
<el-table ref="roleTable"
:data="list"
style="width: 100%;"
v-loading="listLoading" border>
<el-table-column label="编号" width="100" align="center">
<template slot-scope="scope">{{scope.row.id}}</template>
</el-table-column>
<el-table-column label="角色名称" align="center">
<template slot-scope="scope">{{scope.row.name}}</template>
</el-table-column>
<el-table-column label="描述" align="center">
<template slot-scope="scope">{{scope.row.description}}</template>
</el-table-column>
<el-table-column label="用户数" width="100" align="center">
<template slot-scope="scope">{{scope.row.adminCount}}</template>
</el-table-column>
<el-table-column label="添加时间" width="160" align="center">
<template slot-scope="scope">{{scope.row.createTime | formatDateTime}}</template>
</el-table-column>
<el-table-column label="是否启用" width="140" align="center">
<template slot-scope="scope">
<el-switch
@change="handleStatusChange(scope.$index, scope.row)"
:active-value="1"
:inactive-value="0"
v-model="scope.row.status">
</el-switch>
</template>
</el-table-column>
<el-table-column label="操作" width="160" align="center">
<template slot-scope="scope">
<el-row>
<el-button size="mini"
type="text"
@click="handleSelectMenu(scope.$index, scope.row)">分配菜单
</el-button>
<el-button size="mini"
type="text"
@click="handleSelectResource(scope.$index, scope.row)">分配资源
</el-button>
</el-row>
<el-row>
<el-button size="mini"
type="text"
@click="handleUpdate(scope.$index, scope.row)">
编辑
</el-button>
<el-button size="mini"
type="text"
@click="handleDelete(scope.$index, scope.row)">删除
</el-button>
</el-row>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination-container">
<el-pagination
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
layout="total, sizes,prev, pager, next,jumper"
:current-page.sync="listQuery.pageNum"
:page-size="listQuery.pageSize"
:page-sizes="[5,10,15]"
:total="total">
</el-pagination>
</div>
<el-dialog
:title="isEdit?'编辑角色':'添加角色'"
:visible.sync="dialogVisible"
width="40%">
<el-form :model="role"
ref="roleForm"
label-width="150px" size="small">
<el-form-item label="角色名称:">
<el-input v-model="role.name" style="width: 250px"></el-input>
</el-form-item>
<el-form-item label="描述:">
<el-input v-model="role.description"
type="textarea"
:rows="5"
style="width: 250px"></el-input>
</el-form-item>
<el-form-item label="是否启用:">
<el-radio-group v-model="role.status">
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small"> </el-button>
<el-button type="primary" @click="handleDialogConfirm()" size="small"> </el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import {fetchList,createRole,updateRole,updateStatus,deleteRole} from '@/api/role';
import {formatDate} from '@/utils/date';
const defaultListQuery = {
pageNum: 1,
pageSize: 5,
keyword: null
};
const defaultRole = {
id: null,
name: null,
description: null,
adminCount: 0,
status: 1
};
export default {
name: 'roleList',
data() {
return {
listQuery: Object.assign({}, defaultListQuery),
list: null,
total: null,
listLoading: false,
dialogVisible: false,
role: Object.assign({}, defaultRole),
isEdit: false
}
},
created() {
this.getList();
},
filters: {
formatDateTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
}
},
methods: {
handleResetSearch() {
this.listQuery = Object.assign({}, defaultListQuery);
},
handleSearchList() {
this.listQuery.pageNum = 1;
this.getList();
},
handleSizeChange(val) {
this.listQuery.pageNum = 1;
this.listQuery.pageSize = val;
this.getList();
},
handleCurrentChange(val) {
this.listQuery.pageNum = val;
this.getList();
},
handleAdd() {
this.dialogVisible = true;
this.isEdit = false;
this.role = Object.assign({},defaultRole);
},
handleStatusChange(index, row) {
this.$confirm('是否要修改该状态?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
updateStatus(row.id, {status: row.status}).then(response => {
this.$message({
type: 'success',
message: '修改成功!'
});
});
}).catch(() => {
this.$message({
type: 'info',
message: '取消修改'
});
this.getList();
});
},
handleDelete(index, row) {
this.$confirm('是否要删除该角色?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
let ids = [];
ids.push(row.id);
let params=new URLSearchParams();
params.append("ids",ids);
deleteRole(params).then(response => {
this.$message({
type: 'success',
message: '删除成功!'
});
this.getList();
});
});
},
handleUpdate(index, row) {
this.dialogVisible = true;
this.isEdit = true;
this.role = Object.assign({},row);
},
handleDialogConfirm() {
this.$confirm('是否要确认?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
if (this.isEdit) {
updateRole(this.role.id,this.role).then(response => {
this.$message({
message: '修改成功!',
type: 'success'
});
this.dialogVisible =false;
this.getList();
})
} else {
createRole(this.role).then(response => {
this.$message({
message: '添加成功!',
type: 'success'
});
this.dialogVisible =false;
this.getList();
})
}
})
},
handleSelectMenu(index,row){
this.$router.push({path:'/ums/allocMenu',query:{roleId:row.id}})
},
handleSelectResource(index,row){
this.$router.push({path:'/ums/allocResource',query:{roleId:row.id}})
},
getList() {
this.listLoading = true;
fetchList(this.listQuery).then(response => {
this.listLoading = false;
this.list = response.data.list;
this.total = response.data.total;
});
}
}
}
</script>
<style></style>