<template>
|
<div class="page">
|
<div class="page-title">
|
<div><h2>菜单权限管理</h2><p>按角色设置菜单和按钮权限,保存后切换角色会自动反显。</p></div>
|
</div>
|
<div class="toolbar">
|
<span>角色</span>
|
<el-select v-model="roleId" style="width:260px" @change="loadPermissions"><el-option v-for="item in roles" :key="item.id" :label="item.name" :value="item.id"></el-option></el-select>
|
<div class="spacer"></div>
|
<el-tag type="success">已选权限:{{ selectedRoleIds.length }} 个</el-tag>
|
<el-button type="primary" :loading="saving" @click="save">保存角色权限</el-button>
|
</div>
|
<section class="permission-panel">
|
<div class="panel-head"><h3>角色菜单及按钮权限</h3><el-checkbox v-model="roleCheckAll" @change="checkAll">全选</el-checkbox></div>
|
<el-tree ref="roleTree" :data="menus" node-key="id" show-checkbox default-expand-all :render-content="renderContent" :props="{ label: 'name', children: 'children' }" @check="syncCheckAllState"></el-tree>
|
</section>
|
</div>
|
</template>
|
<script>
|
import api from '@/api'
|
export default {
|
name: 'MenuPermission',
|
data() {
|
return {
|
menus: [],
|
flatMenus: [],
|
roles: [],
|
roleId: null,
|
saving: false,
|
roleCheckAll: false,
|
selectedRoleIds: []
|
}
|
},
|
computed: {
|
allMenuIds() { return this.flatMenus.map(item => String(item.id)) }
|
},
|
created() { this.loadBase() },
|
methods: {
|
loadBase() {
|
Promise.all([api.get('/system/menu/tree'), api.get('/system/menu/list'), api.get('/system/roles')]).then(([tree, flat, roles]) => {
|
this.menus = tree.data || []
|
this.flatMenus = flat.data || []
|
this.roles = roles.data || []
|
if (!this.roleId && this.roles.length) this.roleId = this.roles[0].id
|
this.$nextTick(this.loadPermissions)
|
})
|
},
|
loadPermissions() {
|
if (!this.roleId) {
|
this.selectedRoleIds = []
|
this.roleCheckAll = false
|
if (this.$refs.roleTree) this.$refs.roleTree.setCheckedKeys([])
|
return
|
}
|
api.get('/system/menu-permissions', { params: { roleId: this.roleId } }).then(res => {
|
const data = res.data || {}
|
this.selectedRoleIds = (data.roleMenuIds || []).map(String)
|
this.$nextTick(() => {
|
this.$refs.roleTree.setCheckedKeys(this.displayCheckedKeys(this.selectedRoleIds))
|
this.syncCheckAllState()
|
})
|
})
|
},
|
renderContent(h, { data }) {
|
const label = data.menuType === 2 ? data.name + ' [按钮 ' + (data.permissions || '') + ']' : data.name
|
return h('span', { class: { 'perm-button-node': data.menuType === 2 } }, label)
|
},
|
displayCheckedKeys(ids) {
|
const selected = new Set((ids || []).map(String))
|
const children = {}
|
const menuIds = new Set(this.flatMenus.map(item => String(item.id)))
|
this.flatMenus.forEach(item => {
|
const id = String(item.id)
|
const pid = item.pid == null ? '0' : String(item.pid)
|
if (!children[pid]) children[pid] = []
|
children[pid].push(id)
|
})
|
const result = []
|
const collectFullySelected = id => {
|
if (!selected.has(id)) return false
|
const childIds = children[id] || []
|
if (!childIds.length) {
|
result.push(id)
|
return true
|
}
|
const allChildrenSelected = childIds.map(collectFullySelected).every(Boolean)
|
if (allChildrenSelected) result.push(id)
|
return allChildrenSelected
|
}
|
this.flatMenus.filter(item => item.pid == null || String(item.pid) === '0' || !menuIds.has(String(item.pid))).forEach(item => collectFullySelected(String(item.id)))
|
return result
|
},
|
checkedMenuIds(ref) { return [...new Set([...(ref.getCheckedKeys(false) || []), ...(ref.getHalfCheckedKeys() || [])].map(String))] },
|
syncCheckAllState() {
|
const ref = this.$refs.roleTree
|
if (!ref) return
|
this.roleCheckAll = this.checkedMenuIds(ref).length === this.allMenuIds.length
|
},
|
checkAll() {
|
this.$refs.roleTree.setCheckedKeys(this.roleCheckAll ? this.allMenuIds : [])
|
this.$nextTick(this.syncCheckAllState)
|
},
|
save() {
|
if (!this.roleId) {
|
this.$message.warning('请先选择角色')
|
return
|
}
|
const roleMenuIds = this.checkedMenuIds(this.$refs.roleTree)
|
this.saving = true
|
api.post('/system/menu-permissions', { roleId: this.roleId, roleMenuIds }).then(() => {
|
this.selectedRoleIds = roleMenuIds
|
this.$message.success('角色菜单权限已保存')
|
}).finally(() => { this.saving = false })
|
}
|
}
|
}
|
</script>
|
<style scoped>
|
::v-deep .perm-button-node { color: #b8791f; }
|
|
.permission-panel {
|
min-height: calc(100vh - 220px);
|
padding: 14px;
|
border: 1px solid #dce4ec;
|
border-radius: 4px;
|
background: #fff;
|
overflow: auto;
|
}
|
.panel-head {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
margin-bottom: 12px;
|
}
|
.panel-head h3 {
|
margin: 0;
|
color: #31465d;
|
font-size: 15px;
|
}
|
</style>
|