4
头图

在近日的开发中遇到了需要将antd表格的列合并与插槽合并使用的问题,现整理如下:

antd行列合并

关于antd行列合并的实现,antd官方文档中有详细介绍:

表头只支持列合并,使用 column 里的 colSpan 进行设置。
表格支持行/列合并,使用 render 里的单元格属性 colSpan 或者 rowSpan 设值为 0 时,设置的表格不会渲染。

本文以行合并(纵向合并)为例:
关于rowSpan属性值的获取,我写了一个方法,仅供参考:

/*
** data - 即table的dataSource
** index - 需要被合并的属性名
*/
function getRowSpanList (data, index) {
 var RowSpanList = new Array(data.length)
 var x = ''
 var count = 0
 var startindex = 0
 for (var i = 0; i < data.length;i++) {
    var val = data[i][index]
    if (i === 0 ) {
      x = val
      count = 1
      RowSpanList[0] = 1
    } else {
      if (val === x) {
        count ++
        RowSpanList[startindex] = count
        RowSpanList[i] = 0
      } else {
        count = 1
        x = val
        startindex = i
        RowSpanList[i] = 1
      }
    }
  }
  return RowSpanList
}

在table的column的相应属性中做配置,此处以class为例:

column: [{
  title: '类型',
  key: 'class',
  dataIndex: 'class'
  customRender: (text, record, index) => {
    return {
     children: text,
     attrs: {
       rowSpan: record.classRowSpanValue //由上文中的方法获取,仅供参考
     }
    }
  }
},...]

再将对应的rowSpan值赋给dataSource中相应对象即可:

var classRowSpanList = getRowSpanList(dataSource, 'class')
for (var i = 0;i < dataSource.length; i++) {
  dataSource[i].classRowSpanValue = classRowSpanList[i]
}

至此已可以实现简单的行合并,效果如下:
简单的行合并

如果需要实现下图所示的有主从关系的行合并(即不同的合并项之间有明显的先后关系,红色框的合并受蓝色框限定):
有主从关系的行合并

需要结合主要属性对次要属性设置rowSpan值,方法如下,仅供参考:

/*
** data - 即table的dataSource
** index - 需要被合并的属性名
** formatArr - 合并主要项的rowSpan数组,通过getRowSpanList方法获取
*/
function getRowSpanListByFormatter (data, index, formatArr) {
  var newArray = new Array(data.length)
  var startIndex, endIndex, val, counter
  for(var i = 0; i < data.length; i++) {
    if (formatArr[i] === 1) {
      newArray[i] = 1
    } else if (formatArr[i] === 0) {
      continue
    } else {
      // 内层遍历
      startIndex = i
      endIndex = i + formatArr[i] - 1
      val = data[startIndex][index]
      counter = 1
      for(var j = i + 1; j <= endIndex; j++) {
        var currentVal = data[j][index]
        if (val === currentVal) {
          // 当前位置的colspan值为0,计数器++
          newArray[j] = 0
          counter++
          newArray[startIndex] = counter
        } else {
          // 上次的col数量
          newArray[startIndex] = counter
          // 对比数据重置
          startIndex = j
          val = data[startIndex][index]
          counter = 1
          if (j === endIndex) {
            newArray[j] = counter
          }
        }
     }
    }
  }
  return newArray
}

再将对应的rowSpan值赋给dataSource中相应对象即可。
列合并同理。

行列合并与插槽同时使用

普通的table插槽是在column对象中配置scopedSlots: { customRender: 'xxx' },并在<a-table>标签中写相应的代码实现的,与此处关系不大,不做过多的介绍。

上文中提到,行列合并时要对customRender做配置,相应单元格的重写则需要写在customRender返回的children中,使用的是类似React的语法:

customRender: (text, record, index) => {
  return {
   children: text, // 这里
   attrs: {
     rowSpan: 2 
   }
  }
}

注意,在同时使用行列合并与插槽功能时column应放在组件的data()中,否则渲染会出错,具体原因我也不清楚,期待大佬解答

例子如下,我希望在实现行合并的同时对超出单元格的内容做省略处理,并使用a-tooltip组件实现鼠标悬浮显示:

column:[{
    title: '类型',
    key: 'class',
    dataIndex: 'class',
    customRender: (text,record) => {
      var childrenVal
      if (record.scZoneRowSpan === 0) {
        // 为被合并的项,不被显示,他的内容意义不大
        childrenVal = ''
      } else if (record.scZoneRowSpan === 1) {
        // 不合并、单独显示的项,超出单元格做省略处理
        // 插槽与HTML代码相同,只是Vue中的{{}} 要换成 {}
        // 在children中的HTML已不支持v-if等Vue指令所以需要判断一下
        if (text && text.length >= 9) {
          childrenVal = (
            <a-tooltip>
              <template slot="title">{text}</template>
              <div style="width: 133px;height:21px;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;">{text}</div>
            </a-tooltip>)
        } else {
          childrenVal = (<span> {text} </span>)
        }
      } else if (record.scZoneRowSpan >= 2) {
        // 合并项,因为有足够的高度,可以直接显示
        childrenVal = text
      } 
      return {
        children: childrenVal,
        attrs: {
          rowSpan: record.scZoneRowSpan
        }
      }
    }
}, ...]

效果如图:
行合并与插槽并用

如果需要对插槽中的元素添加事件:

 childrenVal = (<div onclick={()=>{this.clickHandler(record)}} >{text}</div>)// 此处clickHandler正常写在methods中即可

总结

Ant Desing Vue 真难用

第一次写文章,也不知道讲清楚了没有,欢迎交流指导,各位大佬多多包涵。


Shylock_zh
53 声望2 粉丝