最近做了个权限管理的功能,主要就是为不同的角色或者用户设置不同的权限。布局使用的elementUI的抽屉组件和container容器布局。实现方式是组件递归调用。主要用到了父子组件之间的通信。
点击列表中的权限设置,弹出抽屉组件。
抽屉组件在主页面中使用
<el-drawer
:title="drawerTitle"
size="90%"
:visible.sync="drawer"
custom-class="permission-drawer"
:with-header="false">
<permission-list :id="roleId" :is-role="isRole" @close="closeDrawer"></permission-list>
</el-drawer>
permission-list组件包装了权限列表及确定和取消操作
<!-- permission list -->
<template>
<div class="biz-permission-list">
<div class="permission-wrap"><permission-item :data="plist[0]" :default-permission="defaultPermission" :check="checkDefault" @change="getPermission"></permission-item></div>
<div class="footer-btn flex-center-x">
<el-button size="small" type="primary" @click="confirmModify">确认</el-button>
<el-button size="small" type="primary" plain @click="cancel">取消</el-button>
</div>
</div>
</template>
<script>
import PermissionItem from './BizPermissionItem'
export default {
name: 'BizPermissionList',
props: {
id: {
type: [Number, String]
},
isRole: {
type: Boolean
}
},
mixins: {},
components: {
PermissionItem
},
data () {
return {
plist: [],
// 暂存子组件传过来的数据
permissionObj: {},
defaultPermission: [],
// 通知子组件开始检查已选中权限
checkDefault: false
}
},
created () {
this.requestPermissionList()
this.requestPermissionListByRole()
},
mounted () {},
filters: {},
computed: {},
watch: {
id (val) {
if (val) {
this.requestPermissionListByRole()
}
}
},
methods: {
async requestPermissionList () {
const [data] = await this.$api.permissionApi.getPermissionList()
if (data) {
this.plist.push({
name: '',
child: data
})
}
},
async requestAssignPermission (params) {
const [data] = await this.$api.permissionApi.assignPermission(params)
if (data) {
// 修改完毕关闭并清空数据
this.cancel()
}
},
confirmModify () {
var arr = []
for (let k in this.permissionObj) {
arr = arr.concat(this.permissionObj[k])
}
this.requestAssignPermission({
role_id: this.id,
pids: arr,
is_role: this.isRole ? 1 : 0
})
},
cancel () {
this.defaultPermission = []
this.checkDefault = false
this.$emit('close')
},
getPermission (data) {
this.permissionObj[data.name] = data.value
},
async requestPermissionListByRole () {
const [data] = await this.$api.permissionApi.getPermissionListByRole({
role_id: this.id,
is_role: this.isRole ? 1 : 0
})
if (data) {
this.defaultPermission = data.pids
this.checkDefault = true
}
}
}
}
</script>
<style lang="less" scoped>
.biz-permission-list{
.permission-wrap{
width: 95%;
height: 800px;
overflow: scroll;
border-top: 1px solid #000;
border-right: 1px solid #000;
}
& /deep/ .el-container{
align-items: stretch;
background: aliceblue;
}
& /deep/ .el-aside{
padding: 0 20px;
display: flex;
align-items: center;
border-bottom: 1px solid green;
border-right: 1px solid green;
}
& /deep/ .el-main{
padding: 0;
}
.footer-btn{
margin: 24px auto;
}
}
</style>
permission-item组件是真正需要复用的组件
<!-- permission item -->
<template>
<el-container>
<el-aside width="200px" v-show="data && data.name">
<div>{{data.name}}</div>
</el-aside>
<el-main v-if="data && data.child">
<template v-if="data.child[0].child">
<permission-item v-for="(obj, index) in data.child" :check="check" :key="`bpic-${index}-${obj.code}`" :data="obj" :default-permission="defaultPermission" @change="change"></permission-item>
</template>
<div v-else class="child-wrap">
<el-checkbox v-model="checkedAll" @change="selectAllToggle">全选</el-checkbox>
<el-checkbox-group v-model="checkedPermissions" @change="handleCheckedPermissionChange">
<el-checkbox v-for="(p, index) in data.child" :label="p.id" :key="`bpi-${index}-${p.id}`">{{p.name}}</el-checkbox>
</el-checkbox-group>
</div>
</el-main>
</el-container>
</template>
<script>
export default {
name: 'PermissionItem',
props: {
// 全部权限列表
data: {
type: Object,
default: () => {}
},
// 默认的已选中
defaultPermission: {
type: Array,
default: () => []
},
// 是否开始检查默认已选中
check: {
type: Boolean
}
},
mixins: {},
components: {},
data () {
return {
checkedAll: false,
checkedPermissions: [],
dataObj: {}
}
},
created () {
},
mounted () {},
filters: {},
computed: {},
watch: {
check (val) {
if (val) {
this.handleDefaultPermission()
}
},
checkedPermissions (val) {
if (val.length === this.data.child.length) {
this.checkedAll = true
} else {
this.checkedAll = false
}
}
},
methods: {
handleDefaultPermission () {
this.checkedPermissions = []
if (this.data && this.data.child && !this.data.child[0].child) {
this.data.child.forEach(item => {
if (this.defaultPermission.includes(item.id)) {
this.checkedPermissions.push(item.id)
}
})
this.$emit('change', { name: this.data.name, value: this.checkedPermissions })
}
},
dataTransform () {
if (this.data.child) {
const arr = []
this.data.child.forEach(item => {
arr.push(item.id)
})
return arr
} else {
return []
}
},
selectAllToggle (val) {
this.checkedPermissions = val ? this.dataTransform() : []
this.$emit('change', { name: this.data.name, value: this.checkedPermissions })
},
handleCheckedPermissionChange (val) {
this.checkedAll = val.length === this.data.child.length
this.$emit('change', { name: this.data.name, value: this.checkedPermissions })
},
change (data) {
this.$emit('change', data)
}
}
}
</script>
<style lang="less" scoped>
.child-wrap{
padding: 10px;
border-bottom: 1px solid #000;
& /deep/ .el-checkbox{
margin-left: 20px;
}
}
</style>
为了更像表格,使用了element布局
并重写了一部分样式,使用了穿刺方式
& /deep/ .el-container{
align-items: stretch;
background: aliceblue;
}
& /deep/ .el-aside{
padding: 0 20px;
display: flex;
align-items: center;
border-bottom: 1px solid green;
border-right: 1px solid green;
}
& /deep/ .el-main{
padding: 0;
}
说实话,当时只是因为这个布局都搞了蛮久的,开始的时候想用table布局,想到合并单元格有些费事就作罢。不过最终的样式还可以(对,你没看错,没有UI,全靠自我感觉)
现在是业务处理的部分。需要先获取所有的权限,然后选择某些权限给角色或用户,向后端发送数据。
一、权限列表的展示
先看看后端返回的数据:
特点就是没有特点,我询问了下,有层级限制吗?答:没有。哈哈哈。
言归正传,每一级数据都有name和child字段,最后一级除外,所以打算在aside部分显示name字段,main部分显示child字段,以此实现递归。鉴于给的数据直接为数组形式,决定包装一层:
this.plist.push({name: '',child: data})
至于为什么name字段内容为空,是为了去除一个多余的空白的aside,当然要配合下面的代码.
<el-aside width="200px" v-show="data && data.name">
<div>{{data.name}}</div>
</el-aside>
配合使用checkbox-group这个组件,实现了下面的效果。
二、选中权限,最终传给permission-list组件,获得发送给后端的权限列表。
当checkbox-group组件发生变化的时候,通过$emit('change', 数据),父组件接收change事件,获得数据并继续向父级,最终在permission-list组件中通过change事件拿到的数据就是这一次选中的值。
那么问题来了,每次拿到的数据都是最新一次的变化的值,之前的数据呢?肯定是不能丢掉的。
解决方法:在permission-list组件data中定义一个数组类型的变量,每次有新值,就push到数组中。
实际:每次新增加权限是OK的,但是删除某些权限就力所不能及了。
最终解决:$emit发送的数据做成obj形式,带着这次权限所在的组或者说name。父组件中定一个obj类型的数据,将传过来的数据的name作为key,value作为值。这样,当新增某些权限时,直接增加新的key/value,修改的时候,对于同一个key,取最后的值。新增修改一下全解决了。
// 子组件
handleCheckedPermissionChange (val) {
this.$emit('change', { name: this.data.name, value: this.checkedPermissions })
}
// 父组件
getPermission (data) {
this.permissionObj[data.name] = data.value
}
至此,新增权限所需要的数据就准备好了,按照后端的格式要求把拿到的权限放在一个数组中。
var arr = []
for (let k in this.permissionObj) {
arr = arr.concat(this.permissionObj[k])
}
三、已有权限回显至权限列表中
首先说明的是,所有的数据获取或者发送都在父组件中完成,子组件只负责显示,或者将将数据发送给父组件。
根据选中角色/用户的id,通过接口得到已有权限。当父组件created后,分别拿到权限列表和当前已有权限,并将其传给子组件,然后傻眼了,怎么都没有回显???
哈哈哈,亏这种东西,吃多了就有数了。原因在打印了子组件获取的已有权限列表后发现为空。原来是异步的问题。解决方式:可以在获取权限列表成功的回调里执行获取已有权限列表,这样有个问题,权限列表是相对固定的,不需要回回调用。我的解决方式是给子组件增加一个属性值check,并进行监听。当check为true时,才进行回显的操作。
// 父组件
async requestPermissionListByRole () {
const [data] = await this.$api.permissionApi.getPermissionListByRole({
role_id: this.id,
is_role: this.isRole ? 1 : 0
})
if (data) {
this.defaultPermission = data.pids
this.checkDefault = true
}
}
// 子组件
// 监听:
check (val) {
if (val) {
this.handleDefaultPermission()
}
}
// 处理回显
handleDefaultPermission () {
this.checkedPermissions = []
if (this.data && this.data.child && !this.data.child[0].child) {
this.data.child.forEach(item => {
if (this.defaultPermission.includes(item.id)) {
this.checkedPermissions.push(item.id)
}
})
this.$emit('change', { name: this.data.name, value: this.checkedPermissions })
}
}
好了,至此回显的问题也解决了。
组件的递归使用这是第二次实践,上一次是关于组织机构树。经常碰到一个问题就是调用组件本身时忘记传值,实在是不该。
本次使用或遇到的问题总结:
1、父子组件之间的传值;
2、请求接口的时机,监听某些值改变时才进行请求;
3、drawer组件隐藏时的数据清理,防止污染其他数据;
4、利用object进行数据整理。