zoukankan      html  css  js  c++  java
  • ElementUI的el-tree树形组件实现分配权限注意的点

     前言

    随着Vue的爆火(很大部分是我们国内开发者功劳),自开源以来,在GitHub上已经斩获176k票star,位居前三,所以饿了么推出的基于Vue的前端框架ElementUI以简洁的UI与实用的组件等普及度越来越高。

    下面是一个完整的el-tree实例,包含了我所遇到的坑。

    Template代码

            <el-tree style="height:100%;" :data="data" @check="handleCheckChange" show-checkbox node-key="menuid" ref="authTree"
              highlight-current :props="defaultProps">
            </el-tree>
    • data:树结构绑定的数据源,后台借口传过来的数据
    • check函数:当复选框被点击的时候触发
    • show-checkbox:显示选项的多选框
    • node-key="menuid":什么意思呢,官网很模糊只是说一些功能都必须要提供这个属性(比如选中等等),它其实就是我们数据源中的唯一标识,比如数据库中你的权限表主键为id,这里就是填id
    • ref=""authTree":创建引用到此组件的类似指针的功能
    • :props:"defaultProps":这个属性就是定义你要绑定的子节点和展示标题相关,官网有四个属性可选值,比较常用的就这两个,其他的可以移步官网
    •   label:定义你要绑定的标签属性
    •   children:定义子树的属性值

    script代码

    只用语言是描述不了的,我直接在代码中解释:

        data: function() {
          return {
            page: 1,//分页
            rows: 10,//行数
            total: 0,//总记录数
            data: [],//我们后台接口访问到的展示数据
            defaultProps: {//这里就是props属性绑定的值
              children: 'permissions', //填写你的数据格式里面的子集,我这里是permissions
              label: 'menuname' //填写你要展示的标题名称,我这是menuname
            },
            //权限Tree选中的节点
            moduleids: [],
            //获取table当前选中行row对象
            curRow: null
          };
        },

    我后台的数据结构:

    这个结构看懂了关系就一目了然,node-key属性就是我们选中节点的值,我们将这个值发送到后端进行操作,后台只需要根据逗号分割就可以得到所有选中的权限节点,也就能够理解为什么官方说这个属性是必须要指定的。

    然后是方法部分的代码,比较重要的参数传递和绑值的问题:

            let params = {
              roleId: this.curRow.roleId,
              sysPermission: this.moduleids.toString()//传递参数时直接toString也更省事,后台只需切割一下。
            }

    巨坑(查询角色的权限后父节点的子节点全部选中了)

    明明查询到的是很正常的数组,但是父节点的所有子节点都被选中了,这时候只能用一个方法 setChecked() ,另外两个方法( setCheckedKeys()  setCheckedNodes() )会造成子节点选中的情况。
    例如:A 是 父节点, B C D 分别是 子节点,勾选了 B节点,checkedId仅包含B,没有问题,如果checkedId包含了A和B,A节点下的节点都被选中。
    后台必须返回节点的id数组,利用循环绑定node-key。

    返回一个id数组:

    查询角色对应权限并且绑定选择框:

     queryRoleModuleId: function(roleId) {
            //先清空,再查权限
            this.$refs['authTree'].setCheckedKeys([]);
            var url = this.axios.urls.PERMISSION_QUERYPERMISSIONBYROLEID;
            this.axios.post(url, {
              roleId: roleId
            }).then(resp => {
              this.moduleids = resp.data.data;
              console.log(this.moduleids);
              if (this.moduleids) {
                this.$nextTick(() => {
                  this.moduleids.forEach(value=>{//真的大坑,我自己摸索好久!!!
                    this.$refs.authTree.setChecked(value, true,false) //给树节点赋值
                  });
                  this.checkStrictly = false //重点: 赋值完成后 设置为false
                })
              }
    
            }).catch();
          },

    获取选中节点的坑

    默认的选中是不包含父节点的,所以我们需要拼接半选中的节点:

          handleCheckChange: function(data, checked) {
            //checked.checkedKeys  选中的节点id数组z
            //checked.halfCheckedKeys 半选中节点id数组
            this.moduleids = checked.halfCheckedKeys.concat(checked.checkedKeys); //选中节点和半选中节点所有的id
          }

    整个组件源码

    <template>
      <el-container class="auth-container">
        <el-header style="100%;margin:5px 0px;padding:0px 15px;">
          <el-form :inline="true">
            <el-form-item>
              <el-input v-model="roleName" size="small" placeholder="角色名称"></el-input>
            </el-form-item>
            <el-form-item>
              <el-button type="primary" style="background-color: #42B983;" size="small" @click="query(1)"><i class="el-icon-search"></i>搜索</el-button>
            </el-form-item>
            <el-form-item>
              <el-button size="small" @click="refresh()"><i class="el-icon-refresh"></i>刷新</el-button>
            </el-form-item>
            <el-form-item>
              <el-button type="primary" size="small" icon="el-icon-edit-outline" @click="saveAuth">保存角色权限</el-button>
            </el-form-item>
          </el-form>
        </el-header>
        <el-container class="auth-container">
          <el-aside class="auth-aside">
            <el-tree style="height:100%;" :data="data" :check-strictly="checkStrictly" @check="handleCheckChange"
              show-checkbox node-key="menuid" ref="authTree" highlight-current :props="defaultProps">
            </el-tree>
          </el-aside>
          <el-main class="auth-main">
            <!--数据表格-->
            <el-table size="medium" :loading="true" :header-cell-style="{background:'#eef1f6',color:'#606266'}" ref="singleTable"
              :highlight-current-row="true" @row-click="rowClick" :data="result" style=" 100%">
              <el-table-column type="index" label="序号" width="50" align="center" :index="indexMethod"></el-table-column>
              </el-table-column>
              <el-table-column prop="roleName" label="角色名称" align="center">
                <template slot-scope="scope">
                  <el-button type="text" @click="queryRoleModuleId(scope.row.roleId)">{{scope.row.roleName}}</el-button>
                </template>
              </el-table-column>
              <el-table-column prop="roleTime" label="上次更新时间" :formatter="formatter" align="center">
              </el-table-column>
            </el-table>
    
            <!--分页组件-->
            <div class="paginationClass">
              <el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="page"
                :page-sizes="[10, 20, 30, 40]" :page-size="rows" layout="total, sizes, prev, pager, next, jumper" :total="total">
              </el-pagination>
            </div>
    
          </el-main>
        </el-container>
      </el-container>
    </template>
    <script>
      import date from '../../api/date.js'
      export default {
        name: 'BookList',
        data: function() {
          return {
            result: [],
            roleName: '',
            page: 1,
            rows: 10,
            total: 0,
            data: [],
            checkStrictly: false,
            defaultProps: {
              children: 'permissions', //填写你的数据格式里面的子集
              label: 'menuname' //填写你要展示的标题名称
            },
            //权限Tree选中的节点
            moduleids: [],
            //获取table当前选中行row对象
            curRow: null
          };
        },
        methods: {
          refresh() {
            this.roleName = '';
            this.page = 1;
            this.rows = 10;
            this.curRow = null;
            this.$refs.authTree.setCheckedKeys([]);
            //折叠
            let nodes = this.$refs.authTree.store._getAllNodes();
            for (let i = 0; i < nodes.length; i++) {
              nodes[i].expanded = false
            }
            this.$forceUpdate();
            this.query(1);
          },
          formatter(row, column) {
            return date(row.roleTime);
          },
          //自定义索引
          indexMethod: function(index) {
            return (this.page - 1) * this.rows + (index + 1);
          },
          //分页
          handleSizeChange: function(rows) {
            this.page = 1;
            this.rows = rows;
            this.query(1);
          },
          handleCurrentChange: function(page) {
            this.page = page;
            this.query(page);
          },
          //数据查询
          query: function(num) {
            var params = {
              roleName: this.roleName,
              page: num !== null ? num : this.page,
              rows: this.rows,
            }
            var url = this.axios.urls.ROLE_QUERYROLEPAGER;
            this.axios.post(url, params).then(resp => {
              this.result = resp.data.data;
              this.total = resp.data.total;
            }).catch();
          },
          //表格行的单击事件
          rowClick: function(row, column, event) {
            this.curRow = row;
          },
          //保存权限
          saveAuth: function() {
            if (this.moduleids.length <= 0) {
              this.$message({
                showClose: true,
                message: '请选择权限分配给指定角色!',
                type: 'error'
              });
              return false;
            }
            if (null == this.curRow) {
              this.$message({
                showClose: true,
                message: '请选中你要分配权限的角色!',
                type: 'error'
              });
              return false;
            }
            if (this.curRow.roleId == 1) {
              return this.$message({
                type: 'error',
                message: '管理员不允许操作哦!'
              });
            }
            let params = {
              roleId: this.curRow.roleId,
              sysPermission: this.moduleids.toString() //传递参数时直接toString也更省事,后台只需切割一下。
            }
            //保存角色权限
            var url = this.axios.urls.PERMISSION_SAVEATUTH;
            this.axios.post(url, params).then(resp => {
              this.$message({
                showClose: true,
                message: resp.status == 200 ? resp.data.msg : '授予权限失败!',
                type: resp.status == 200 ? 'success' : 'error'
              });
              //保存成功,刷新列表和清空数据信息
              this.curRow = null;
              this.moduleids = [];
              this.query(1);
              this.$refs['authTree'].setCheckedKeys([]);
            }).catch();
          },
          queryRoleModuleId: function(roleId) {
            //先清空,再查权限
            this.$refs['authTree'].setCheckedKeys([]);
            var url = this.axios.urls.PERMISSION_QUERYPERMISSIONBYROLEID;
            this.axios.post(url, {
              roleId: roleId
            }).then(resp => {
              this.moduleids = resp.data.data;
              console.log(this.moduleids);
              if (this.moduleids) {
                this.$nextTick(() => {
                  this.moduleids.forEach(value=>{//真的大坑,我自己摸索好久!!!
                    this.$refs.authTree.setChecked(value, true,false) //给树节点赋值
                  });
                  this.checkStrictly = false //重点: 赋值完成后 设置为false
                })
              }
    
            }).catch();
          },
          handleCheckChange: function(data, checked) {
            //checked.checkedKeys  选中的节点id数组z
            //checked.halfCheckedKeys 半选中节点id数组
            this.moduleids = checked.halfCheckedKeys.concat(checked.checkedKeys); //选中节点和半选中节点所有的id
          },
        },
        created: function() {
          //加载权限树
          if (this.$store.getters.getTreeList.length <= 0) {
            this.axios.post(this.axios.urls.PERMISSION_QUERYPERMISSIONPAGER, {
              pagination: false
            }).then(res => {
              this.$store.commit('setTreeList', res.data.data);
            }).catch(err => console.log(err));
          }
          //得到所有节点
          this.query(1);
          this.data = this.$store.getters.getTreeList;
        }
      }
    </script>
    <style scoped>
      .paginationClass {
        margin-top: 15px;
        bottom: 0;
        right: 0;
        float: right;
      }
    
      .auth-container {
        height: 100%;
         100%;
        display: flex;
        object-fit: fill;
    
      }
    
      .auth-aside {
         200px !important;
        padding: 0px 10px;
        height: 100%;
        object-fit: fill;
        margin-left: 5px;
      }
    
      .auth-main {
        padding: 0px;
        height: 100%;
        margin-right: 15px;
      }
    
      .el-row-bg {
        padding-top: 10px;
        padding-left: 10px;
        color: #000000;
        font-weight: bold;
        height: 60px;
        background: #f4f4f5;
      }
    </style>
  • 相关阅读:
    Windows下NodeJS环境搭建
    大前端是什么?
    TeamCity+Rancher+Docker实现.Net Core项目DevOps(目前成本最小的DevOps实践)
    2019春运抢票终极攻略,让你躺着也能抢到票回家!
    ASP.NET CORE 2.0 发布到IIS,IIS如何设置环境变量来区分生产环境和测试环境
    使用第三方容器服务,自动化部署.Net Core
    记React+.NetCore API实现动态列导出
    6.前端基于react,后端基于.net core2.0的开发之路(6) 服务端渲染(SSR)
    5.前端基于react,后端基于.net core2.0的开发之路(5) 配置node层,session设置、获取,请求拦截
    4.前端基于react,后端基于.net core2.0的开发之路(4) 前端打包,编译,路由,模型,服务
  • 原文地址:https://www.cnblogs.com/StarChen20/p/14083541.html
Copyright © 2011-2022 走看看