vue-优雅合并单元格

838 阅读2分钟

在日常开发过程中,难免会用到表格。虽然现在很多框架自带表格的合并功能,但是有时候如果指示想开发一个小功能而且引入一个ui库,有点杀鸡用牛刀的感觉。其实,用自定义指令和函数,也可以批量合并单元格。

场景

我们用vue生成表格时,一般是数据为数组,然后用v-for变量。

// 数据
  data(){
    return {
      tableData: [
        {name:'张三',school: '混沌小学',degree:'小学'},
        {name:'张三',school: '混沌高中',degree:'高中'},
        {name:'张三',school: '混沌大学',degree:'本科'},
        {name:'李四',school: '秩序小学',degree:'小学'},
        {name:'李四',school: '秩序小学',degree:'高中'},
        {name:'李四',school: '秩序小学',degree:'本科'}
      ]
    }
  },
  // template
  <template>
  <div id="app">
    <table class="table">
      <tr>
        <td class="bg-light">姓名</td>
        <td class="bg-light">学校</td>
        <td class="bg-light">学历</td>
    </tr>
    <tr v-for="(el,index) in tableData" :key="el+index">
        <td >{{el.name}}</td>
        <td>{{el.school}}</td>
        <td>{{el.degree}}</td>
    </tr>
    </table>
  </div>
</template>

效果如下:

企业微信截图_16623558755164.png 这时,测试就希望姓名这里能合并起来

企业微信截图_16624483033251.png 达成这种效果有两个点:

  1. name所有相同的第一项开始的位置,确定开始跨行,以及连续出现的次数,确定跨行的数值。
  2. tb标签是变量出来的,开始跨行后,将后续多余的删掉。

解决这两个就能实现跨行了。

先解决第一个问题,统计name有多少不同的值,不同的值起始位置,出现的次数。

     // 这么我们先分两部
    // 1.将tableData中我们所需要的name先拿出来,可以用es6中的map优雅的实现出来
    const getKeys = (arr,key)=>arr.map((item,index)=>({key:item[key],index:index}));
    // 2.统计出现的不同的值种类,每个不同种类的值、个数以及起始位置。这个可以用es6的reduce实现。
     const rowSpanCount =arr=> arr.reduce((cur,prev)=>{
        const {key,index} = prev;
        Reflect.has(cur,key)?cur[key].num++:Reflect.set(cur,key,{start:index,num:1});
        return cur;
      },{})
  // 结果
  rowSpanCount(getKeys(this.tableData,'name')) 
  /*{
    "张三": {
        "start": 0,
        "num": 3
    },
    "李四": {
        "start": 3,
        "num": 3
    }
}*/

然后在computed中调用

  computed: {
    rowCount() {
      const getKeys = (arr,key)=>arr.map((item,index)=>({key:item[key],index:index}));
      const rowSpanCount =arr=> arr.reduce((cur,prev)=>{
        const {key,index} = prev;
        Reflect.has(cur,key)?cur[key].num++:Reflect.set(cur,key,{start:index,num:1});
        return cur;
      },{})
      return   rowSpanCount(getKeys(this.tableData,'name'))
    }
  },

解决第一个后,后面就是为td设置属性,并且删除多余的td了。方式有很多,这里推荐用自定义指令的方式,能减少不必要的代码。

// 定义一个方法,用于判断是不是开始跨行的项,
methods: {
    setRowSpan(key, activeIndex) {
      const value = this.rowCount[key];
      const { num, start } = value;
      return activeIndex === start? num : 0;//如果第一次出现的项,为跨行相,返回需要跨行的数值,如果不是第一次出现,则为需要删除的多余相,返回值0;
    },
  }
// 自定义指令,用途设置rowSpan和删除多余的td
  directives:{
    rowSpan:{
      inserted(el, binding) {
        const { value } = binding;
        value ? el.setAttribute('rowSpan', value) : el.parentNode.removeChild(el);
      }
    }
  },