antDesign vue 实现表格单元格合并

相信大家在做业务需求的过程中都遇到过表格单元格合并的问题吧,今天我们来简单讲讲antDesign 配合 vue 来实现单元格合并

1. 首先我们来看一下官方给出的示例

antDesign 文档链接 (https://2x.antdv.com/docs/vue/introduce-cn)
我们来看下官方的这个例子
例子是在需要合并的列上增加了一个 customRender 函数,index 代表就是哪一行。rowSpan 是几就代表向下合并几列。显然这种固定的合并模式
是无法满足于我们的业务需求的。我们实际业务场景数据都是动态,可能需要根据某一个id 是一样的来进行动态的合并。比如 产品编码,订单id,
活动id. 我们就需要一个方法来算一下需要合并的列
	const columns = [
		{
        title: 'Home phone',
        colSpan: 2,
        dataIndex: 'tel',
        customRender: ({ text, index }) => {
          const obj = {
            children: text,
            props: {},
          };
          if (index === 2) {
            obj.props.rowSpan = 2;
          }
          // These two are merged into above cell
          if (index === 3) {
            obj.props.rowSpan = 0;
          }
          if (index === 4) {
            obj.props.colSpan = 0;
          }
          return obj;
        },
	]

定义一个计算合并列的方法

	这个函数总共接口两个参数,一个是需要渲染的数据,第二个参数是合并的唯一值。(比如,要根据订单号一样的进行合并,就需要把订单号orderId 传进来)
	/**
 * @description: 返回表格列合并的数组
 * @param {Object} tableData data 列表数据
 * @param {String} itemProperty 要合并的属性依据
 */
export function getSpanArr(tableData, itemProperty) {
  if (tableData.length === 0) {
    return false;
  }

  const spanArr = [];
  let pos;
  for (let i = 0; i < tableData.length; i++) {
    if (i === 0) {
      spanArr.push(1);
      pos = 0;
    } else {
      // 判断当前元素与上一个元素是否相同 相同则加 1
      if (tableData[i][itemProperty] && tableData[i][itemProperty] === tableData[i - 1][itemProperty]) {
        spanArr[pos] += 1;
        spanArr.push(0);
      } else {
        spanArr.push(1);
        pos = i;
      }
    }
  }

  return {
    spanArr: spanArr,
  };
}

在页面中调用

	 import {getSpanArr } from '/@/utils/tools'; // 引入文件
	 const dataList =ref([]) // 接口返回的数据
	 const dataObj = ref({}) 
	 const dataObj = getSpanArr(dataList, 'orderId'); // 在调用接口返回成功之后执行该方法 orderId, 需要合并的字段
     spanObj.value = dataObj;
       
       //  spanObj.value.spanArr 按照表格索引列出了每一个项需要合并的列。可以打印一下 
	  const renderContent = ({ text, index }: any) => {
	   const obj = {
	      children: text,
	      props: {} as any,
	    };
	   const spanArr = spanObj.value.spanArr;
	    const _row = spanArr[index];
	    obj.props.rowSpan = _row;
	    return obj;
	  };
	
	// 将 renderContent方法传入需要合并的列中
	  const columns = [
	    {
	      title: '商品信息',
	      dataIndex: 'productName',
	      key: 'productName',
	      customRender: renderContent,
	    },
	    {
	      title: '产品型号',
	      dataIndex: 'productModel',
	      key: 'productModel',
	    },
	    {
	      title: ' 收货信息',
	      dataIndex: 'address',
	      customRender: renderContent,
	    },
	  ];

这样的弊端

 如果表格前面有 checkbox, checkbox 不能合并。需要按如下方法进行合并。如此一来就大功告成了。
   <div id="tableContent">
     <a-table
         :scroll="{ y: 500 }"
         bordered
         :pagination="false"
         :rowKey="
           (record) => {
             return record.orderId;
           }
         "
         :row-selection="{ onSelect: onSelect, onSelectAll: onSelectAll, selectedRowKeys: selectedRowKeysList }"
         :columns="columns"
         :data-source="startLeaseDataList"
       >
       a-table>
     div>
 // 我们就需要再进行额外的处理
 // 在异步更新dom之后,找到表格复选框对应的dom节点进行合并
nextTick(() => {
// dataList 接口返回的数据
 if (dataList.length <= 0) {
    return false;
  }
  // tableContent 表格外层的id 
  var tableContent = document.getElementById('tableContent');
  const tableBody = tableContent?.getElementsByClassName('ant-table-tbody');
  const tr = tableBody[0].getElementsByTagName('tr');
  const cellMegetList = spanObj.value.spanArr; // 如上定义合并单元格方法返回的合并项
  const trList = Array.from(tr);
  const newTrList = trList.filter((item, index) => {
    return index > 0;
  })
  // 数值大于 0 隐藏,否则进行rowSpan 合并
  newTrList.forEach((item, index) => {
    if (cellMegetList[index] === 0) {
      item.childNodes[1].setAttribute('style', 'display: none');
    } else {
      item.childNodes[1].setAttribute('rowspan', cellMegetList[index]);
    }
  });
});

demo 如下,可以复制到项目中自己调试一下 (如有别的问题请留言或私信作者)

<template>
  <div>
    <div class="dialog-content">
      <a-spin :spinning="spinning">
        <div id="tableContent">
          <a-table
            :scroll="{ y: 500 }"
            bordered
            :pagination="false"
            :rowKey="
              (record) => {
                return record.subOrderDetailId;
              }
            "
            :row-selection="{ onSelect }"
            :columns="columns"
            :data-source="startLeaseDataList"
          />
        </div>
      </a-spin>
    </div>
  </div>
</template>

<script name="shopCardSource" lang="ts" setup>
  import { ref, nextTick } from 'vue';
  import { getSpanArr } from '/@/utils/tools';
  const spinning = ref(false);
  const spanObj = ref({}); // 合并的数据对象

  // 列合并渲染处理,需要合并哪个列,就在哪个列传入 。显然 checkbox 在这里无法在这里控制,我们只能通过操作dom 来实现
  const renderContent = ({ text, index }: any) => {
    const obj = {
      children: text,
      props: {} as any,
    };
    const spanArr = spanObj.value.spanArr;
    const _row = spanArr[index];
    obj.props.rowSpan = _row;
    return obj;
  };

  const columns = [
    {
      title: '第一列',
      dataIndex: 'productName',
      key: 'productName',
      width: '200px',
      customRender: renderContent,
    },
    {
      title: '第二列',
      dataIndex: 'productModel',
      key: 'productModel',
    },
    {
      title: '第三列',
      dataIndex: 'serialNumber',
      key: 'serialNumber',
    },
    {
      title: ' 第四列',
      dataIndex: 'assetCode',
      key: 'assetCode',
    },
    {
      title: '第五列',
      dataIndex: 'address',
      key: 'address',
      customRender: renderContent,
    },
  ];

  //  本数据是通过 subOrderDetailId 作为唯一的标识
  const startLeaseDataList = ref([
    {
      mainOrderId: '20230903073209292508',
      subOrderId: '16936975294936948',
      subOrderDetailId: '1',
      productName: '【单品】苹果显示器(1080*900/36寸)',
      address: '北京市 市辖区 西城区 十大科技上的可适当放宽经济法开福寺129号',
      productModel: 'zh20230730001',
      serialNumber: 'runaceshiB4',
      assetCode: 'MS-XS-857042',
    },
    {
      mainOrderId: '20230903073209292508',
      subOrderId: '16936975294936948',
      subOrderDetailId: '1',
      productName: '【单品】苹果显示器(1080*900/36寸)',
      address: '北京市 市辖区 西城区 十大科技上的可适当放宽经济法开福寺12',
      productModel: 'zh20230730001',
      serialNumber: 'runaceshiB4',
      assetCode: 'MS-XS-857042',
    },
    {
      mainOrderId: '20230903073209292508',
      subOrderId: '16936975294936948',
      subOrderDetailId: '1',
      productName: '【单品】苹果显示器(1080*900/36寸)',
      address: '北京市 市辖区 西城区 十大科技上的可适当放宽经济法开福寺',
      productModel: 'zh20230730001',
      serialNumber: 'runaceshiB4',
      assetCode: 'MS-XS-857042',
    },
    {
      mainOrderId: '20230903073209292508',
      subOrderId: '16936975294936942',
      subOrderDetailId: '3',
      productName: '【单品】苹果显示器(1080*900/36寸)',
      address: '北京市 市辖区 西城区 十大科技上的可适当放宽经济法开福寺',
      productModel: 'zh20230730001',
      serialNumber: 'runaceshiB4',
      assetCode: 'MS-XS-857042',
    },
    {
      mainOrderId: '20230903073209292508',
      subOrderId: '16936975294936942',
      subOrderDetailId: '4',
      productName: '【单品】苹果显示器(1080*900/36寸)',
      address: '北京市 市辖区 西城区 十大科技上的可适当放宽经济法开福寺',
      productModel: 'zh20230730001',
      serialNumber: 'runaceshiB4',
      assetCode: 'MS-XS-857042',
    },
    {
      mainOrderId: '20230903073209292508',
      subOrderId: '16936975294936941',
      subOrderDetailId: '4',
      productName: '【单品】苹果显示器(1080*900/36寸)',
      address: '北京市 市辖区 西城区 十大科技上的可适当放宽经济法开福寺',
      productModel: 'zh20230730001',
      serialNumber: 'runaceshiB4',
      assetCode: 'MS-XS-857042',
    },
  ]); //确认起租列表

  // startLeaseDataList) 接口返回的数据,此处为模拟。
  // 处理需要合并的列以及需要合并几个列
  const dataObj = getSpanArr(startLeaseDataList.value, 'subOrderDetailId');
  spanObj.value = dataObj;

  // 合并表格前面的 checkbox
  nextTick(() => {
    if (startLeaseDataList.value.length <= 0) {
      return false;
    }
    var tableContent = document.getElementById('tableContent');
    const tableBody = tableContent?.getElementsByClassName('ant-table-tbody');
    const tr = tableBody[0].getElementsByTagName('tr');
    const cellMegetList = dataObj.spanArr;
    const trList = Array.from(tr);
    const newTrList = trList.filter((item, index) => {
      return index > 0;
    });
    newTrList.forEach((item, index) => {
      if (cellMegetList[index] === 0) {
        item.childNodes[1].setAttribute('style', 'display: none');
      } else {
        item.childNodes[1].setAttribute('rowspan', cellMegetList[index]);
      }
    });
  });
</script>

<style scoped lang="less"></style>

你可能感兴趣的:(vue3,vue.js,javascript,anti-design-vue)