vue3.2 + vxe-table4.x 实现多层级结构的 合并、 展开、收起 功能

<template>
  <div style="padding: 20px">
    <vxe-table border :data="list" :height="800" :span-method="rowspanMethod">
      <vxe-column title="一级类目" field="category1">
        <template #default="{ row }">
          <a-space>
            <span>{{ row.category1 }}</span>
            <a-button @click="toggleCategoryExpand(row, '1')">
              {{ showText(row, '1') ? '收起' : '展开' }}
            </a-button>
          </a-space>
        </template>
      </vxe-column>
      <vxe-column title="二级类目" field="category2">
        <template #default="{ row }">
          <a-space>
            <span>{{ row.category2 }}</span>
            <a-button
              v-if="showText(row, '1')"
              @click="toggleCategoryExpand(row, '2')"
            >
              {{ showText(row, '2') ? '收起' : '展开' }}
            </a-button>
          </a-space>
        </template>
      </vxe-column>
      <vxe-column title="三级类目" field="category3"></vxe-column>
      <vxe-column title="报告金额" field="amount"></vxe-column>
      <vxe-column title="合计" field="total"></vxe-column>
    </vxe-table>
  </div>
</template>

<script>
import { defineComponent, ref } from 'vue'
import XEUtils from 'xe-utils'

export default defineComponent({
  setup() {
    const flattenedData = [
      {
        id: '0',
        category: '人工成本',
        parentId: null
      },
      {
        id: '0-0',
        parentId: '0',
        category: '人工成本0-0'
      },
      {
        id: '0-0-0',
        parentId: '0-0',
        category: '人工成本0-0-0'
      },
      {
        id: '0-0-1',
        parentId: '0-0',
        category: '人工成本0-0-1'
      },
      {
        id: '0-1',
        parentId: '0',
        category: '人工成本0-1'
      },
      {
        id: '0-1-0',
        parentId: '0-1',
        category: '人工成本0-1-0'
      },
      {
        id: '0-1-1',
        parentId: '0-1',
        category: '人工成本0-1-1'
      }
    ]
    const treeData = XEUtils.toArrayTree(flattenedData)
    const treeDataTemp = JSON.parse(JSON.stringify(treeData))
    const renderTreeData = ref(treeDataTemp)
    const list = ref([])

    // 工具方法
    const toColTreeData = (treeData) => {
      const options = { children: 'children' }
      const list = []
      const keyMap = {}
      XEUtils.eachTree(
        treeData,
        (item, index, result, paths, parent) => {
          keyMap[item.id] = item
          item.keys = parent ? parent.keys.concat([item.id]) : [item.id]
          if (!item.children || !item.children.length) {
            const row = {}
            item.keys.forEach((key, index) => {
              const level = index + 1
              const obj = keyMap[key]
              row[`category${level}`] = obj.category
              row[`id${level}`] = obj.id
            })
            list.push(row)
          }
        },
        options
      )
      return list
    }

    const rowspanMethod = ({ row, _rowIndex, column, visibleData }) => {
      const fields = ['category1', 'category2']
      const cellValue = row[column.field]
      if (cellValue && fields.includes(column.field)) {
        const prevRow = visibleData[_rowIndex - 1]
        let nextRow = visibleData[_rowIndex + 1]
        if (prevRow && prevRow[column.field] === cellValue) {
          return { rowspan: 0, colspan: 0 }
        } else {
          let countRowspan = 1
          while (nextRow && nextRow[column.field] === cellValue) {
            nextRow = visibleData[++countRowspan + _rowIndex]
          }
          if (countRowspan > 1) {
            return { rowspan: countRowspan, colspan: 1 }
          }
        }
      }
    }

    renderTreeData.value.forEach((item) => {
      if (item.children.length) {
        item.children = []
      }
    })

    list.value = toColTreeData(renderTreeData.value)

    const toggleCategoryExpand = (row, level) => {
      if (level === '1') {
        toggleLevelOne(row)
      }
      if (level === '2') {
        toggleLevelTwo(row)
      }
    }
    const toggleLevelOne = (row) => {
      const { id1 } = row
      const item = renderTreeData.value.find((item) => item.id === id1)
      if (item.children.length) {
        item.children = []
      } else {
        const a = treeData.find((item) => item.id === id1)
        item.children = JSON.parse(JSON.stringify(a.children))
        item.children.forEach((child) => {
          child.children = []
        })
      }
      list.value = toColTreeData(renderTreeData.value)
    }

    const toggleLevelTwo = (row) => {
      const { id1, id2 } = row
      const item1 = renderTreeData.value.find((item) => item.id === id1)
      const item2 = item1.children.find((item) => item.id === id2)
      if (item2.children.length) {
        item2.children = []
      } else {
        const a1 = treeData.find((item) => item.id === id1)
        const a2 = a1.children.find((item) => item.id === id2)
        item2.children = a2.children
      }
      list.value = toColTreeData(renderTreeData.value)
    }

    const showText = (row, level) => {
      const { id1, id2 } = row
      if (level === '1') {
        const item = renderTreeData.value.find((item) => item.id === id1)
        if (item) {
          return item.children.length
        }
        return false
      }
      if (level === '2') {
        const item1 = renderTreeData.value.find((item) => item.id === id1)
        const item2 = item1.children.find((item) => item.id === id2)
        if (item2) {
          return item2.children.length
        }
        return false
      }
    }

    return {
      rowspanMethod,
      toggleCategoryExpand,
      list,
      showText
    }
  }
})
</script>

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

web_Hsir

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值