This commit is contained in:
Pan 2019-03-01 18:38:24 +08:00
parent 15b00fa0d0
commit 66a08f5203
7 changed files with 224 additions and 179 deletions

View File

@ -1,5 +1,12 @@
import request from '@/utils/request'
export function getRoutes() {
return request({
url: '/routes',
method: 'get'
})
}
export function getRoles() {
return request({
url: '/roles',

View File

@ -1,8 +0,0 @@
import request from '@/utils/request'
export function fetchAsyncRoutes() {
return request({
url: '/routes',
method: 'get'
})
}

View File

@ -25,6 +25,7 @@ Mock.mock(/\/login\/logout/, 'post', loginAPI.logout)
Mock.mock(/\/user\/info\.*/, 'get', loginAPI.getUserInfo)
// 角色相关
Mock.mock(/\/routes/, 'get', roleAPI.getRoutes)
Mock.mock(/\/roles/, 'get', roleAPI.getRoles)
Mock.mock(/\/roles$/, 'post', roleAPI.addRole)
Mock.mock(/\/roles\/[A-Za-z0-9]+/, 'put', roleAPI.updateRole)

View File

@ -1,9 +1,45 @@
import Mock from 'mockjs'
import { deepClone } from '@/utils'
import { filterAsyncRoutes } from '@/store/modules/permission'
import { asyncRoutes, constantRoutes } from '@/router'
// admin 角色可以访问所有菜单
const roles = []
const routes = deepClone([...constantRoutes, ...asyncRoutes])
const roles = [
{
key: 'admin',
name: 'admin',
description: 'Super Administrator. Have access to view all pages.',
routes: routes
},
{
key: 'editor',
name: 'editor',
description: 'Normal Editor. Can see all pages except permission page',
routes: filterAsyncRoutes(routes, ['editor'])
},
{
key: 'visitor',
name: 'visitor',
description: 'Just a visitor. Can only see the home page and the document page',
routes: [{
path: '',
redirect: 'dashboard',
children: [
{
path: 'dashboard',
name: 'Dashboard',
meta: { title: 'dashboard', icon: 'dashboard' }
}
]
}]
}
]
export default {
getRoutes() {
return routes
},
getRoles() {
return roles
},

View File

@ -80,7 +80,6 @@ export const constantRoutes = [
{
path: '/documentation',
component: Layout,
redirect: '/documentation/index',
children: [
{
path: 'index',
@ -140,6 +139,15 @@ export const asyncRoutes = [
title: 'directivePermission'
// if do not set roles, means: this page does not require permission
}
},
{
path: 'role',
component: () => import('@/views/permission/role'),
name: 'RolePermission',
meta: {
title: 'rolePermission',
roles: ['admin']
}
}
]
},

View File

@ -18,7 +18,7 @@ function hasPermission(roles, route) {
* @param routes asyncRoutes
* @param roles
*/
function filterAsyncRoutes(routes, roles) {
export function filterAsyncRoutes(routes, roles) {
const res = []
routes.forEach(route => {

View File

@ -1,18 +1,18 @@
<template>
<div class="app-container wrapper">
<el-button type="primary" @click="handleaddRole">{{ $t('permission.addRole') }}</el-button>
<div class="app-container">
<el-button type="primary" @click="handleAddRole">{{ $t('permission.addRole') }}</el-button>
<el-table :data="rolesData" style="width: 100%" class="roles-table">
<el-table-column label="Role Id" width="220">
<template slot-scope="scope">{{ scope.row.id }}</template>
<el-table :data="rolesList" style="width: 100%;margin-top:30px;" border>
<el-table-column align="center" label="Role Key" width="220">
<template slot-scope="scope">{{ scope.row.key }}</template>
</el-table-column>
<el-table-column label="Role Name" width="220">
<el-table-column align="center" label="Role Name" width="220">
<template slot-scope="scope">{{ scope.row.name }}</template>
</el-table-column>
<el-table-column label="Description">
<template slot-scope="scope">{{ scope.row.describe }}</template>
<el-table-column align="header-center" label="Description">
<template slot-scope="scope">{{ scope.row.description }}</template>
</el-table-column>
<el-table-column label="Operations">
<el-table-column align="center" label="Operations">
<template slot-scope="scope">
<el-button type="primary" size="small" @click="handleEdit(scope)">{{ $t('permission.editPermission') }}</el-button>
<el-button type="danger" size="small" @click="handleDelete(scope)">{{ $t('permission.delete') }}</el-button>
@ -20,105 +20,190 @@
</el-table-column>
</el-table>
<el-dialog :visible.sync="addRoleDialogVisible" title="New Role">
<el-form :model="addRole" class="new-role-form">
<el-form-item label="Role Name">
<el-input v-model="addRole.name" placeholder="role name"/>
<el-dialog :visible.sync="dialogVisible" :title="dialogType==='edit'?'Edit Role':'New Role'">
<el-form :model="role" label-width="80px" label-position="left" >
<el-form-item label="Key">
<el-input v-model="role.key" placeholder="Role Key"/>
</el-form-item>
<el-form-item label="Role Description">
<el-input v-model="addRole.describe" placeholder="role Description"/>
<el-form-item label="Name">
<el-input v-model="role.name" placeholder="Role Name"/>
</el-form-item>
</el-form>
<el-button type="primary" @click="confirmaddRole">{{ $t('permission.confirm') }}</el-button>
<el-button type="danger" @click="cancleaddRole">{{ $t('permission.cancel') }}</el-button>
</el-dialog>
<el-dialog :visible.sync="permissionDialogVisible" :title="$t('permission.editPermission')">
<el-form label-width="100px">
<el-form-item label="Role Name">
<span>{{ checkedRole.data.name }}</span>
<el-form-item label="Desc">
<el-input
v-model="role.description"
:autosize="{ minRows: 2, maxRows: 4}"
type="textarea"
placeholder="Role Description"/>
</el-form-item>
<el-form-item label="Menus">
<el-tree ref="tree" :check-strictly="checkStrictly" :data="MapRoutesData" :props="defaultProps" :filter-node-method="filterNode" show-checkbox node-key="id" class="permission-tree"/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="confirmPermission">{{ $t('permission.confirm') }}</el-button>
<el-button type="danger" @click="canclePermission">{{ $t('permission.cancel') }}</el-button>
<el-tree ref="tree" :check-strictly="checkStrictly" :data="routesData" :props="defaultProps" show-checkbox node-key="path" class="permission-tree"/>
</el-form-item>
</el-form>
<el-button type="primary" @click="confirmRole">{{ $t('permission.confirm') }}</el-button>
<el-button type="danger" @click="dialogVisible=false">{{ $t('permission.cancel') }}</el-button>
</el-dialog>
</div>
</template>
<script>
import path from 'path'
import { deepClone } from '@/utils'
import { generateTitle } from '@/utils/i18n'
import { addRole, deleteRole, updateRole, getRoles } from '@/api/role'
import { fetchAsyncRoutes } from '@/api/routes'
import { getRoutes, getRoles, addRole, deleteRole, updateRole } from '@/api/role'
import i18n from '@/lang'
const defaultRole = {
key: '',
name: '',
description: '',
routes: []
}
export default {
data() {
return {
role: Object.assign({}, defaultRole),
routes: [],
rolesList: [],
dialogVisible: false,
dialogType: 'new',
checkStrictly: false,
routesData: [],
addRoleDialogVisible: false,
permissionDialogVisible: false,
defaultProps: {
children: 'children',
label: 'name'
},
addRole: {
id: '',
name: '',
describe: '',
accessibleRoutes: []
},
checkedRole: {
index: null,
data: {
id: '',
name: '',
describe: '',
accessibleRoutes: []
}
},
rolesData: []
label: 'title'
}
}
},
computed: {
MapRoutesData() {
const traverseRoutes = routes => {
return routes.map(route => {
if (route.children && this.onlyOneShowingChild(route.children, route) && !route.alwaysShow) {
route = this.onlyOneShowingChild(route.children, route)
}
route.children && (route.children = traverseRoutes(route.children))
route.name = this.generateTitle((route.meta && route.meta.title) || route.name)
return route
})
}
return traverseRoutes(this.routesData)
routesData() {
return this.routes
}
},
created() {
fetchAsyncRoutes().then(res => {
this.routesData = res.data
})
getRoles().then(res => {
console.log(res)
this.rolesData = res.data
})
// Mock: get all routes and roles list from server
this.getRoutes()
this.getRoles()
},
methods: {
generateTitle,
// filter out the hidden route menu
filterNode(value, data) {
return !data.hidden
async getRoutes() {
const res = await getRoutes()
const routes = this.generateRoutes(res.data)
this.routes = this.i18n(routes)
console.log(this.routes)
},
async getRoles() {
const res = await getRoles()
this.rolesList = res.data
},
i18n(routes) {
const app = routes.map(route => {
route.title = i18n.t(`route.${route.title}`)
if (route.children) {
route.children = this.i18n(route.children)
}
return route
})
return app
},
// Reshape the routes structure so that it looks the same as the sidebar
generateRoutes(routes, basePath = '/') {
const res = []
for (let route of routes) {
const title = route.meta && route.meta.title
// skip some route
if (route.hidden) { continue }
const onlyOneShowingChild = this.onlyOneShowingChild(route.children, route)
if (route.children && onlyOneShowingChild && !route.alwaysShow) {
route = onlyOneShowingChild
}
const data = {
path: path.resolve(basePath, route.path),
title: title
}
// recursive child routes
if (route.children) {
data.children = this.generateRoutes(route.children, data.path)
}
res.push(data)
}
return res
},
generateArr(routes) {
let data = []
routes.forEach(route => {
data.push(route)
if (route.children) {
const temp = this.generateArr(route.children)
if (temp.length > 0) {
data = [...data, ...temp]
}
}
})
return data
},
handleAddRole() {
this.role = Object.assign({}, defaultRole)
this.dialogType = 'new'
this.dialogVisible = true
},
handleEdit(scope) {
this.dialogType = 'edit'
this.dialogVisible = true
this.checkStrictly = true
this.role = deepClone(scope.row)
this.$nextTick(() => {
const routes = this.generateRoutes(this.role.routes)
this.$refs.tree.setCheckedNodes(this.generateArr(routes))
// set checked state of a node not affects its father and child nodes
this.checkStrictly = false
})
},
handleDelete({ $index, row }) {
this.$confirm('Confirm to remove the role?', 'Warning', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'warning'
})
.then(async() => {
await deleteRole(row.id)
this.rolesList.splice($index, 1)
this.$message({
type: 'success',
message: 'Delete succed!'
})
})
.catch(err => { console.error(err) })
},
async confirmRole() {
const isEdit = this.dialogType === 'edit'
if (isEdit) {
await updateRole()
} else {
await addRole()
}
// const $tree = this.$refs.tree
// let routeIds = $tree.getCheckedNodes(false, true).reduce((result, node) => {
// // (onlyOneShowingChild),id
// return result.concat(node.id, node.parentId)
// }, [])
// routeIds = Array.from(new Set(routeIds))
// await updateRole(this.role)
// this.$message({
// message: 'Edit succed',
// type: 'success'
// })
// this.dialogVisible = false
},
// reference: src/view/layout/components/Sidebar/SidebarItem.vue
onlyOneShowingChild(children, parent) {
onlyOneShowingChild(children = [], parent) {
let onlyOneChild = null
const showingChildren = children.filter(item => {
if (item.hidden) {
@ -128,104 +213,20 @@ export default {
}
})
// When there is only one child router, the child router is displayed by default
// When there is only one child route, the child route is displayed by default
if (showingChildren.length === 1) {
onlyOneChild = showingChildren[0]
onlyOneChild.path = path.resolve(parent.path, onlyOneChild.path)
return onlyOneChild
}
// Show parent if there are no child router to display
// Show parent if there are no child route to display
if (showingChildren.length === 0) {
onlyOneChild = { ... parent, path: '', noShowingChildren: true }
return onlyOneChild
}
return false
},
handleaddRole() {
this.addRoleDialogVisible = true
},
handleEdit(scope) {
this.checkedRole.index = scope.$index
// setCheckedKeys leaf only, none-leaf nodecheckbug
this.checkStrictly = true
this.checkedRole.data = deepClone(scope.row)
this.permissionDialogVisible = true
this.$nextTick(() => {
this.$refs.tree.filter()
this.$refs.tree.setCheckedKeys(
this.checkedRole.data.accessibleRoutes,
true
)
this.checkStrictly = false
})
// this.checkStrictly = false
},
handleDelete(scope) {
this.$confirm('Confirm to remove the role?', 'Warning', {
confirmButtonText: 'Confirm',
cancelButtonText: 'Cancel',
type: 'warning'
})
.then(() => {
return deleteRole(scope.row.id)
})
.then(res => {
this.rolesData.splice(scope.$index, 1)
this.$message({
type: 'success',
message: 'Delete succed!'
})
})
.catch(err => {
console.error(err)
this.$message({
type: 'info',
message: 'Delete canceled'
})
})
},
confirmPermission() {
let routeIds = this.$refs.tree
.getCheckedNodes(false, true).reduce((result, node) => {
// (onlyOneShowingChild),id
return result.concat(node.id, node.parentId)
}, [])
routeIds = Array.from(new Set(routeIds))
updateRole(this.checkedRole.data.id, {
...this.rolesData[this.checkedRole.index],
accessibleRoutes: routeIds
}).then(res => {
this.rolesData[this.checkedRole.index].accessibleRoutes = routeIds
this.$message({
message: 'Edit succed',
type: 'success'
})
this.permissionDialogVisible = false
})
},
canclePermission() {
this.permissionDialogVisible = false
},
resetaddRole() {
this.addRole = {
id: '',
name: '',
describe: '',
accessibleRoutes: []
}
},
confirmaddRole() {
addRole(this.addRole).then(res => {
this.addRole.id = res.data.id
this.rolesData.push(deepClone(this.addRole))
this.resetaddRole()
this.addRoleDialogVisible = false
})
},
cancleaddRole() {
this.resetaddRole()
this.addRoleDialogVisible = false
}
}
}
@ -234,10 +235,10 @@ export default {
<style lang="scss" scoped>
.app-container {
.roles-table {
margin-top: 40px;
margin-top: 30px;
}
.permission-tree {
margin-bottom: 40px;
margin-bottom: 30px;
}
}
</style>