基于vue-element的树形表格(菜单功能页)实现方案

vue-logo.jpg

前言

中后台项目中菜单功能(权限管理)页总是必不可少的, 而仅使用element中el-table组件的树形表格并不能很好的满足对应的业务需求,所以,就想到了使用el-checkbox来替代列表第一列的选框,之后由函数控制对应的选中事件。写的或许不是很完善,还请各位大佬多指教啦!

实现结果

微信图片2.png

微信图片3.png

微信图片4.png

话不多说,来看代码吧!

页面代码

      <el-table
        border
        :data="tableData"
        row-key="id"
        default-expand-all
        @select-all="chooseAll"
      >
        <!-- 这里用了el-table的select-all事件控制全选和反选 -->
        <el-table-column
          type="selection"
          width="55"
          align="center"
          fixed="left"
         >
          <template slot-scope="{ row }">
            <!-- 用el-checkbox替代原本的选框 -->
            <!-- indeterminate属性控制选框的部分选中状态 -->
            <el-checkbox
              v-model="row.check"
              :indeterminate="row.inType"
              @change="chooseBlock(row)"
            ></el-checkbox>
          </template>
        </el-table-column>
        <el-table-column
          show-overflow-tooltip
          prop="title"
          label="菜单名称"
          width="180"
          fixed="left"
        ></el-table-column>
        <el-table-column label="功能操作">
          <template slot-scope="{ row }">
            <!-- 这里是根据数据渲染的功能项 -->
            <el-checkbox-group v-model="row.actives">
              <el-checkbox
                v-for="item in row.actionList"
                :key="item.id"
                :label="item.actionName"
                @change="chooseActive(row)"
                >{{ item.title }}</el-checkbox
              >
            </el-checkbox-group>
          </template>
        </el-table-column>
      </el-table>
    </el-card>
复制代码

数据格式

data() {
    return {
      tableData: [
        {
          title: "菜单1",
          id: 1,
          // pid 父级id 一级菜单为0
          // 方便之后处理数据时遍历数组
          pid: 0,
          // 控制每行第一列选框的选中状态
          check: false,
          inType: false,
          // 菜单对应的功能列表
          actionList: [
            {
              id: 0,
              actionName: "show",
              title: "展示",
            },
          ],
          // 已选中(以保存)的菜单功能
          actives: [],
          // 下级菜单
          children: [],
        },
      ],
    };
  },
复制代码

对应的事件函数

methods: {
    // 表格全选
    chooseAll(val) {
      // 需要递归处理数据
      if (val.length > 0) {
        // 大于0说明是选中状态
        this.handleAll(true, this.tableData);
      } else {
        // 取消全选
        this.handleAll(false, this.tableData);
      }
    },
    // 选中/取消所有选项
    handleAll(bol, list) {
      list.forEach((ele) => {
        // checkbox赋值
        ele.check = bol;
        // 重置部分选中状态
        ele.inType = false;
        if (ele.children && ele.children.length > 0) {
          // 判断children存在且不为空, 则当前对象没有功能选项, 遍历children
          this.handleAll(bol, ele.children);
        } else {
          // 当前对象存在选项
          // 清空已选
          ele.actives = [];
          // bol = true则全选 为false什么都不做
          if (bol) {
            ele.actionList.forEach((item) => {
              ele.actives.push(item.actionName);
            });
          }
        }
      });
    },
    // 左侧选框选中
    chooseBlock(obj) {
      // 选中时就不存在部分选中了,清除部分选中状态
      obj.inType = false;
      if (obj.actionList.length > 0) {
        // 数组长度大于0, 存在对应的选项, 处理数据
        // 清空选中数据
        obj.actives = [];
        // 判断选中状态 true:全选 false:取消
        if (obj.check) {
          // 全选 遍历选项列表
          obj.actionList.forEach((ele) => {
            obj.actives.push(ele.actionName);
          });
        }
        // 更新菜单选中状态
        this.inspectRow(obj.pid, this.tableData);
      } else {
        // 判断children是否为空或不存在
        if (obj.children && obj.children.length > 0) {
          if (obj.check) {
            this.handleAll(true, obj.children);
          } else {
            this.handleAll(false, obj.children);
          }
        }
      }
    },
    // 选中功能选项
    chooseActive(obj){
      // actives.length不为0说明有选中的项
      if(obj.actives.length != 0){
        if(obj.actives.length < obj.actionList.length){
          // 已选数量小于选项数量, 未全选, 菜单选框部分选中
          obj.inType = true;
          obj.check = false;
        } else {
          // 已选数量等于选项数量, 全选, 菜单选框选中
          obj.inType = false;
          obj.check = true;
        };
      } else {
        // actives.length == 0 没有选中, 取消选框状态
        obj.check = false;
        obj.inType = false;
      };
      // 更新菜单选中状态
      this.inspectRow(obj.pid, this.tableData);
    },
    // 选中功能选项, 更新菜单行选中状态
    inspectRow(pid, list){
      // 根据pid遍历数组
      if(pid != 0){
        // pid不为0 不为一级菜单
        // 遍历数组 寻找对应项
        let mobj = {};
        list.forEach(ele => {
          if(ele.id == pid){
            // 符合条件, 暂存
            mobj = ele;
          }
        });
        if(mobj.id){
          // 已找到对应pid的对象
          let listNum = mobj.children.length;
          let checkNum = 0;
          let inTypeNum = 0;
          // 遍历数组, 获取已全选和部分选中的对象数量
          mobj.children.forEach(ele => {
            ele.check == true ? checkNum++ : "";
            ele.inType == true ? inTypeNum++ : "";
          });
          if(checkNum == listNum){
            // 已全选数量和数组数量一致, 全选状态
            mobj.check = true;
            mobj.inType = false;
          } else if(checkNum < listNum && checkNum > 0){
            // 存在部分子对象全选, 父对象显示部分选中状态
            mobj.check = false;
            mobj.inType = true;
          } else if(inTypeNum > 0){
            // 没有全选状态的子对象, 但存在部分选中的子对象
            // 父对象显示部分选中状态
            mobj.check = false;
            mobj.inType = true;
          } else {
            // 子对象无选中
            mobj.check = false;
            mobj.inType = false;
          };
          // pid不为0 代表还有父级对象, 递归改变父级选框状态
          if(mobj.pid != 0){
            this.inspectRow(mobj.pid, this.tableData);
          }
        } else {
          // 当前数据层未找到符合条件的对象, 寻找下一层
          list.forEach(ele => {
            if(ele.children && ele.children.length > 0){
              this.inspectRow(pid, ele.children);
            }
          });
        }
      }
    }
  },
复制代码

遗留问题

微信图片5.png
如图所示,表头的选框只有控制全选/反选的功能,在数据展示上无法显示对应的部分选中状态,没有像下边的数据部分一样很好的联动展示,算是一点瑕疵吧。如果有更好的方法或者思路,还请多多指教!(在此拜各位大佬)

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享