Ant Design Vue 表格合并和折叠展开的一种方法

文章讲述了如何在AntDesign框架下实现表格行的合并和展开折叠功能,尤其是在两者结合时遇到的挑战以及提供的解决方案,包括数据构造和对应处理的复杂性。
摘要由CSDN通过智能技术生成

功能需求来源:近期,客户提出了一项刁钻的需求,表格的行实现合并,并能展开折叠。antd虽然有行合并和展开折叠的实现,但并未有两者结合起来的写法。

结果截图:

表格初始是合并状态:点击“结构名称”列的“+”展开状态:点击“结构名称”列的“-”折叠收起状态实现代码如下:

<template>
  <div style="width: 1300px; height: 800px">
    <a-table
        :columns="tableColumn"
        :data-source="tableData"
        :bordered="true"
        :scroll="{ x: 900, y: 600 }"
        :pagination="false"
        :expandable="{ rows: true, childrenColumnName: 'children' }"
        :expandIconColumnIndex="1"
        @expand="expandRows"
    >
      <template slot="index" slot-scope="text, record">
        <span :title="text"> {{ record.key }} </span>
      </template>
    </a-table>
  </div>
</template>
<script>
export default {
  name: 'technology',
  data() {
    return {
      tableColumn: [
        {
          title: "序号",
          dataIndex: 'index',
          key: 'index',
          width: 100,
          scopedSlots: { customRender: 'index'}
        },
        {
          title: '结构名称',
          dataIndex: 'structName',
          key: 'structName',
          width: 200,
          customRender: (value, row) => {
            const obj = {
              children: value,
              attrs: {},
            };
            if (row.structShowFlag) {
              obj.attrs.rowSpan = this.structIdCountMap.get(row.structId);
            } else if (row.structShowFlag === false) {
              obj.attrs.rowSpan = 0;
            }
            return obj;
          }
        },
        {
          title: '功能名称',
          dataIndex: 'functionName',
          key: 'functionName',
          width: 200,
          customRender: (value, row) => {
            const obj = {
              children: value,
              attrs: {},
            };
            if (row.functionShowFlag) {
              obj.attrs.rowSpan = this.functionIdCountMap.get(row.functionId);
            } else if (row.functionShowFlag === false) {
              obj.attrs.rowSpan = 0;
            }
            return obj;
          }
        },
        {
          title: '要求名称',
          dataIndex: 'requireName',
          key: 'requireName',
          ellipsis: true,
          width: 150
        },
        {
          title: '规格值',
          dataIndex: 'standerValue',
          key: 'standerValue',
          ellipsis: true
        },
        {
          title: '功能种类',
          dataIndex: 'functionType',
          key: 'functionType',
          ellipsis: true
        }
      ],
      tableData: [],
      structIdCountMap: {},
      functionIdCountMap: {},
      standbyTableData: []
    };
  },

  mounted() {
    // 初始化默认折叠
    this.tableData = this.buildTableData(this.tableData, false);
    this.expandRows(false,null);
  },

  methods: {

    /**
     * 整理表格数据
     * 构造合并单元格数据和展开折叠数据
     * @param val
     * @param expand
     * @returns {({structId: number, functionId: number, functionName: string, structName: string, standerValue: string, requireName: string, functionType: string}|{structId: number, functionId: number, functionName: string, structName: string, standerValue: string, requireName: string, functionType: string}|{structId: number, functionId: number, functionName: string, structName: string, standerValue: string, requireName: string, functionType: string}|{structId: number, functionId: number, functionName: string, structName: string, standerValue: string, requireName: string, functionType: string}|{structId: number, functionId: number, functionName: string, structName: string, standerValue: string, requireName: string, functionType: string})[]}
     */
    buildTableData(val,expand) {
      val =  [
        {
          structId: 1,
          functionId: 1,
          structName: '结构 1',
          functionName: '功能 1',
          requireName: '要求 1',
          standerValue: '规格值 1',
          functionType: '功能类型 1'
        },
        {
          structId: 1,
          functionId: 1,
          structName: '结构 1',
          functionName: '功能 1',
          requireName: '要求 2',
          standerValue: '规格值 1',
          functionType: '功能类型 1'
        },
        {
          structId: 1,
          functionId: 1,
          structName: '结构 1',
          functionName: '功能 1',
          requireName: '要求 3',
          standerValue: '规格值 1',
          functionType: '功能类型 1'
        },
        {
          structId: 1,
          functionId: 2,
          structName: '结构 1',
          functionName: '功能 2',
          requireName: '要求 4',
          standerValue: '规格值 2',
          functionType: '功能类型 2'
        },
        {
          structId: 1,
          functionId: 2,
          structName: '结构 1',
          functionName: '功能 2',
          requireName: '要求 5',
          standerValue: '规格值 2',
          functionType: '功能类型 2'
        },
        {
          structId: 1,
          functionId: 3,
          structName: '结构 1',
          functionName: '功能 3',
          requireName: '要求 6',
          standerValue: '规格值 3',
          functionType: '功能类型 3'
        },
        {
          structId: 1,
          functionId: 3,
          structName: '结构 1',
          functionName: '功能 3',
          requireName: '要求 7',
          standerValue: '规格值 3',
          functionType: '功能类型 3'
        },
        {
          structId: 1,
          functionId: 4,
          structName: '结构 1',
          functionName: '功能 4',
          requireName: '要求 8',
          standerValue: '规格值 4',
          functionType: '功能类型 4'
        },
        {
          structId: 2,
          functionId: 5,
          structName: '结构 2',
          functionName: '功能 5',
          requireName: '要求 9',
          standerValue: '规格值 5',
          functionType: '功能类型 5'
        },
        {
          structId: 2,
          functionId: 6,
          structName: '结构 2',
          functionName: '功能 6',
          requireName: '要求 10',
          standerValue: '规格值 6',
          functionType: '功能类型 6'
        },
        {
          structId: 2,
          functionId: 7,
          structName: '结构 2',
          functionName: '功能 7',
          requireName: '要求 11',
          standerValue: '规格值 7',
          functionType: '功能类型 7'
        },
        {
          structId: 2,
          functionId: 8,
          structName: '结构 2',
          functionName: '功能 8',
          requireName: '要求 12',
          standerValue: '规格值 8',
          functionType: '功能类型 8'
        },
        {
          structId: 2,
          functionId: 9,
          structName: '结构 2',
          functionName: '功能 9',
          requireName: '要求 13',
          standerValue: '规格值 9',
          functionType: '功能类型 9'
        },
        {
          structId: 3,
          functionId: 10,
          structName: '结构 3',
          functionName: '功能 10',
          requireName: '要求 14',
          standerValue: '规格值 10',
          functionType: '功能类型 10'
        },
        {
          structId: 3,
          functionId: 11,
          structName: '结构 3',
          functionName: '功能 11',
          requireName: '要求 16',
          standerValue: '规格值 11',
          functionType: '功能类型 11'
        },
        {
          structId: 3,
          functionId: 12,
          structName: '结构 3',
          functionName: '功能 12',
          requireName: '要求 17',
          standerValue: '规格值 12',
          functionType: '功能类型 12'
        },
        {
          structId: 3,
          functionId: 13,
          structName: '结构 3',
          functionName: '功能 13',
          requireName: '要求 18',
          standerValue: '规格值 13',
          functionType: '功能类型 13'
        }
      ];
      console.log("expand",expand);
      if (val.length > 0) {
        let index = 0;
        val.forEach(item => {
          index += 1;
          item.key = index;
        });
      }
      // 备用的tableData
      this.standbyTableData = val;

      let structIdCountMap = new Map();
      let functionIdCountMap = new Map();

      val.forEach( item => {

         let structId = item.structId;
         let functionId = item.functionId ? item.functionId : null;

         if (structIdCountMap.has(structId)) {
           structIdCountMap.set(structId, structIdCountMap.get(structId) + 1);
           // 当点击展开时,才合并,否则赋值为null
           item.structShowFlag = expand === false ? null : false;
           item.isExpand = false;
         } else {
           structIdCountMap.set(structId, 1);
           item.structShowFlag = expand === false ? null : true;
           item.isExpand = true;
           item.children = [];
         }

         if (functionId) {
           if (functionIdCountMap.has(functionId)) {
             functionIdCountMap.set(functionId, functionIdCountMap.get(functionId) + 1);
             item.functionShowFlag = expand === false ? null : false;
           } else {
             functionIdCountMap.set(functionId, 1);
             item.functionShowFlag = expand === false ? null : true;
           }
         } else {
           item.functionShowFlag = null
         }
      });
      this.structIdCountMap = structIdCountMap;
      this.functionIdCountMap = functionIdCountMap;
      return val;
    },

    /**
     * 表格行折叠展开事件
     * @param isExpand
     * @param record
     */
    expandRows(isExpand, record) {

      // 初始化时
      if (isExpand === false && record === null) {
        let buildData = this.buildTableData(this.standbyTableData, false);
        let children = [];

        buildData.forEach(item => {
          if (!item.isExpand) {
            children.push(item);
          }
        });

        buildData = buildData.filter(item => item.isExpand === true);

        buildData.forEach(item => {
          if (item.isExpand) {
            item.children = children.filter(child => child.structId === item.structId);
          }
        });
        this.tableData = buildData;
      }

      // 点击折叠时,构造children列
      if (isExpand === false && record !== null) {
        // 找到与点击行相同structId但不包括点击行的数据
        let foldData = (this.tableData.filter(item => item.structId === record.structId)).slice(1);
        foldData.forEach(item => {
          item.structShowFlag = null;
          item.functionShowFlag = null;
        });
        // 设置折叠数据
        record.children = foldData;
        record.structShowFlag = null;
        record.functionShowFlag = null;
        // let newArray = array.map(function(item) {
        //   return item === 2 ? 10 : item;
        // });
        this.tableData = this.tableData.map(item => {
          return item.key === record.key ? record : item;
        });
        // this.tableData.splice(record.key - 1, 1, record);
        // 删除被折叠数据
        let afterDeleteData = this.tableData;
        foldData.forEach(item => {
          afterDeleteData = afterDeleteData.filter(afterDelete => afterDelete.key !== item.key);
        });
        this.tableData = afterDeleteData;
        console.log(this.tableData);
      }

      // 点击展开时,合并单元格
      if (isExpand && record !== null) {
        // 先取出该行的children,再把该行的children置空
        let children = record.children;
        record.children = [];
        // 设置结构合并数据
        record.structShowFlag = true;
        // 用于判断该结构的功能是否合并
        let functionShowList = [record, ...children];

        let functionIdCountMap = new Map();
        // 设置功能是否合并
        functionShowList.forEach(item => {

          let functionId = item.functionId ? item.functionId : null;

          if (functionId) {
            if (functionIdCountMap.has(functionId)) {
              functionIdCountMap.set(functionId, functionIdCountMap.get(functionId) + 1);
              item.functionShowFlag = false;
            } else {
              functionIdCountMap.set(functionId, 1);
              item.functionShowFlag = true;
            }
          } else {
            item.functionShowFlag = null
          }
        });

        // 替换元素
        this.tableData = this.tableData.map(item => {
          return item.key === record.key ? functionShowList[0] : item;
        });
        // this.tableData.splice(record.key - 1, 1, functionShowList[0]);

        children = functionShowList.slice(1);

        // 插入元素的索引
        let insertIndex = -1;

        for (let i = 0; i < this.tableData.length; i++) {
          if (this.tableData[i].key === record.key) {
            insertIndex = i + 1; // 在找到的对象后面插入,所以索引加1
            break;
          }
        }

        // 再将该行的children,插入tableData中。
        if (insertIndex !== -1) {
          children.forEach(item => {
            item.structShowFlag = false;
            this.tableData.splice(insertIndex, 0, item);
            insertIndex += 1;
          });
        }
        console.log("tableData",this.tableData);

        // this.tableData = this.buildTableData(this.standbyTableData, true);

      }
    }
  }
};
</script>

 问题:antd要求合并表格行,只能合并同级的行(根据官方文档的样例和API,实践得出的结论,当然也有可能是我理解和实践错了)。

举个例子:合并行的实现是写在表格columns配置中,以插槽的形式配合官方文档提供的API实现,但一跟行的展开折叠结合起来实现就会有冲突。

因为行的展开折叠是需要columns配置中有children,这就跟合并表格行只能合并同级行,产生了矛盾。

解决方案:以上代码的方案就是,当点击折叠时候,需要构造children,点击展开的时候需要再把children中的数据拿出来,进行合并。

难点:代码之所以写的罗里吧嗦的,是因为在把构造children数据和拿出children数据这个过程中,要能对应上,哪些数据是哪一行的children。

待解决的问题:这一版本的解决方案中,我并未加上分页和表格列拖拽之后代码(公司项目中的表格都有分页和列拖拽)。因为加上之后,显得更加罗嗦了,不能很好的展示主要的代码。提示一下,分页不会有任何问题,但表格列拖拽,会跟展开折叠冲突,需要在table组件加上一个key,当表格数据更新,也就是表格重新渲染的时候,更改表格的key,让其重新渲染一次。

总结:不难,但很费时费力,比较繁琐的一个小功能,双商正常的客户都不会提这样的需求。

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值