<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.id1)">
{{ showText(row.id1) ? '收起' : '展开' }}
</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.id1)"
@click="toggleCategoryExpand(row.id2)"
>
{{ showText(row.id2) ? '收起' : '展开' }}
</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'
import { cloneDeep } from 'lodash-es'
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 = cloneDeep(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 }
}
}
}
}
const findNodeById = (data, id) => {
for (let i = 0; i < data.length; i++) {
const node = data[i]
if (node.id === id) {
return node
} else if (node.children) {
const foundNode = findNodeById(node.children, id)
if (foundNode) {
return foundNode
}
}
}
return null
}
renderTreeData.value.forEach((item) => {
if (item.children.length) {
item.children = []
}
})
list.value = toColTreeData(renderTreeData.value)
const toggleCategoryExpand = (id) => {
const originItem = findNodeById(treeData, id)
const originItemClone = cloneDeep(originItem)
if (originItemClone) {
const renderItem = findNodeById(renderTreeData.value, id)
if (renderItem && renderItem.children.length) {
renderItem.children = []
} else {
const result = []
originItemClone.children.forEach((item) => {
const { children, ...rest } = item
result.push({
...rest,
children: []
})
})
renderItem.children = result
}
list.value = toColTreeData(renderTreeData.value)
}
}
const showText = (id) => {
const originItem = findNodeById(renderTreeData.value, id)
return originItem.children.length
}
return {
rowspanMethod,
toggleCategoryExpand,
list,
showText
}
}
})
</script>