如何基于Material实现表格的全选与非全选

如何基于Material实现表格的全选与非全选

发现问题

近日,我接到一个产品需求,一个基于Material开发的后台系统,前期还好,但是越使用越发现坑多,很多功能都不支持,比如今天我遇到的这样一个需求:
在这里插入图片描述
一个可以打开的二级表格,同时一级表格的复选框可以控制当前二级表格的全选与非全选,当然,当下面的所有勾选框全部选中之后,上面的以及表格复选框也要保持选中状态。这个功能在Material内是不提供的,没办法,手写吧。
在这里插入图片描述

实现

首先咱们实现二级表格的打开功能,实现原理很简单,随意一级还是二级表格,其实都是表格罢了,那么我们控制表格的数组不就行了嘛,比如一级表格有2个,打开当前表格之后,当前表格下面有四个子表格,咱们把表格数组相加就成了,同时控制一级二级表格的样式,话不多说,直接上代码(复选框的全选与费全选我也一并贴上来了,请注意看注释,代码中使用了Font Awesome, i标签为字体图标):

<template>
  <div>
    <md-table v-model="people" md-card>
      <md-table-row slot="md-table-row" slot-scope="{ item }">
        <md-table-cell md-label="" md-sort-by="select" style="width: 10px">
          <md-checkbox
            v-model="fatherArray"
            :disabled="remberFatherArr.indexOf(item.index) == -1 && item.index.indexOf('-') == -1"
            :class="{'change_checkbox_color':item.index.indexOf('-') > -1}"
            @change="showArray(item)"
            :value="item.index"></md-checkbox>
        </md-table-cell>
        <md-table-cell md-label="角色 / 成员" md-sort-by="name">
          <i class="fa fa-caret-down fa-lg filter_icon lmr cur"
             :class="{'fa-caret-down':remberFatherArr.indexOf(item.index) == -1,
               'fa-caret-up':remberFatherArr.indexOf(item.index) > -1}"
             @click="addArray(item)"
             v-show="item.index.indexOf('-') == -1"></i>
          <i class="fa fa-user fa-lg filter_icon ml lmr cur icon_pos"
             v-show="item.index.indexOf('-') > -1"></i>
          {{ item.name }}
        </md-table-cell>
        <md-table-cell md-label="名称" md-sort-by="email">{{ item.email }}</md-table-cell>
        <md-table-cell md-label="" md-sort-by="">
          <i class="fa fa-pencil fa-lg filter_icon lmr cur"
             v-show="item.index.indexOf('-') > -1"></i>
          <i class="fa fa-trash fa-lg filter_icon lmr cur"
             v-show="item.index.indexOf('-') > -1"></i>
        </md-table-cell>
      </md-table-row>
    </md-table>
  </div>
</template>

<script>

  export default {
    data() {
      return {
        fatherArray: [],
        remberFatherArr: [],
        people: [
          {
            index: "0",
            name: 'App Engine 查看者(4)',
          },
          {
            index: "1",
            name: 'App Engine 查看者(4)',
          },
        ],
        people_son: [
          {
            name: 'Odette Demageard',
            email: '[email protected]',
          },
          {
            name: 'Lonnie Izkovitz',
            email: '[email protected]',
          },
          {
            name: 'Thatcher Stave',
            email: '[email protected]',
          },
          {
            name: 'Clarinda Marieton',
            email: '[email protected]',
          }
        ],
        people_son2: [
          {
            name: 'Odette Demageard',
            email: '[email protected]',
          },
          {
            name: 'Lonnie Izkovitz',
            email: '[email protected]',
          },
          {
            name: 'Thatcher Stave',
            email: '[email protected]',
          },
          {
            name: 'Clarinda Marieton',
            email: '[email protected]',
          }
        ],
      }
    },
    created() {

    },
    methods: {
      showArray(item) {
// 所有复选框的全选非全选的逻辑,因当前业务需求,插件也不提供,故逻辑手写
// 暂时无需考虑性能问题,遍历次数不多,百次以下,前端响应几乎无影响
        let that = this;
        if (item.index.indexOf("-") == -1) {
          if (that.fatherArray.indexOf(item.index) > -1) {
// 全选
            that.people.forEach(function (val, ind) {
              (val.index.indexOf(item.index + "-") > -1 && that.fatherArray.indexOf(val.index) == -1) ?
                that.fatherArray.push(val.index) : "";
            })
          } else {
// 取消全选
            for (var i = that.fatherArray.length - 1; i >= 0; i--) {
              that.fatherArray[i].indexOf(item.index + "-") > -1 ? that.fatherArray.splice(i, 1) : ""
            }
          }
        } else {
// 其余非全选框的点击筛选逻辑 逻辑较为复杂 阅读请仔细
// 实现原理:遍历原数组和选中数组,判断选中值的类型个数是否相等,相等则全选框选中,否则全选框选中状态消失
// 注意在全选框选中状态取消的时候加了一层节流判断(that.fatherArray.indexOf(item.index.substring(0, item.index.indexOf('-'))) > -1)
          let fatherIndex = 0;
          let allInIndex = 0;
          that.fatherArray.forEach(function (value, index) {
            value.indexOf(item.index.substring(0, item.index.indexOf('-') + 1)) > -1 ? fatherIndex++ : "";
          })
          that.people.forEach(function (value, index) {
            value.index.indexOf(item.index.substring(0, item.index.indexOf('-') + 1)) > -1 ? allInIndex++ : "";
          });
          fatherIndex == allInIndex ?
            that.fatherArray.push(item.index.substring(0, item.index.indexOf('-'))) :
            ((that.fatherArray.indexOf(item.index.substring(0, item.index.indexOf('-'))) > -1) ?
              that.fatherArray.splice(that.fatherArray.indexOf(item.index.substring(0, item.index.indexOf('-'))), 1) : "")
        }
      },
      addArray(item) {
// 注意index的排序问题"0-1","1-1","2-1"
// 框架不提供该功能,此方法为手写逻辑
        let that = this;
        let arrIndex = -1;
        for (var i = 0; i < that.people.length; i++) {
          if (item.index == that.people[i].index) {
            arrIndex = i;
            break
          }
        }
// 判断是否是二次点击,如果是,删除,如果不是,添加
        if (that.remberFatherArr.indexOf(item.index) > -1) {
          for (var i = that.people.length - 1; i >= 0; i--) {
            that.people[i].index.indexOf(item.index + "-") > -1 ? that.people.splice(i, 1) : ""
          }
          that.remberFatherArr.splice(that.remberFatherArr.indexOf(item.index), 1)
          return
        }
        that.remberFatherArr.push(item.index);
        //注意这里以下的代码为你从后台取的二级表格数据数组,现在是我写的假数据people_son和people_son2
        //这里有一个问题需要注意,假数据数组不可重复使用,当然,当你从后台取值的时候就不用考虑了,
        //因为后端返回给你的数据肯定每次是新的(相同也无所谓)
        if (item.index == "0") {
          that.people_son.forEach(function (value, index) {
            arrIndex++
            that.people.splice(arrIndex, 0, value)
            that.people[arrIndex]['index'] = item.index + "-" + index
          })
        } else {
          that.people_son2.forEach(function (value, index) {
            arrIndex++
            that.people.splice(arrIndex, 0, value)
            that.people[arrIndex]['index'] = item.index + "-" + index
          })
        }
        console.log(that.people);
      },
    }
  }
</script>

<style lang="scss" scoped>
  .md-checkbox {
    margin: 0;
  }
</style>

实现效果如下(颜色不同是因为我有重置Material的默认样式,你也可以自定义一下):
在这里插入图片描述
嗯,就酱,大功告成,如果有什么问题欢迎给我留言,一定要看代码中的注释哦!
在这里插入图片描述

写在最后

Material的坑还有很多很多很多。。。补坑之路尚很漫长,共勉。希望这篇文档能给您带来帮助,感谢阅读。