2020-11-04记一次组件递归调用

最近做了个权限管理的功能,主要就是为不同的角色或者用户设置不同的权限。布局使用的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进行数据整理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值