SOURCE

let totalField = 'bank' // 开启合计的字段
let totalFieldObj = {bank:''}
let subtotalFields = ['bank','company','quater'] // 开启小计的字段
let subtotalFieldsObj = Object.fromEntries(subtotalFields.map(item=>[item,'']))
let featureFields = ['bank','company','quater'] // 特征字段
featureFieldsObj = Object.fromEntries(featureFields.map(item=>[item,'']))
featureFieldsValue = {
    bank:['中国银行','广发银行','建设银行','平安银行'],
    company:['广东公司','武汉公司','湖南公司'],
    quater:['第一季度','第二季度','第三季度','第四季度']
}
// console.log(featureFieldsObj)
let quatoFields = ['money','fee'] // 指标字段
let tableData = [
  {id:1,bank:'中国银行',company:'广东公司',quater:'第一季度',money:100,fee:200},
  {id:2,bank:'中国银行',company:'广东公司',quater:'第二季度',money:200,fee:300},
  {id:15,bank:'中国银行',company:'广东公司',quater:'第二季度',money:200,fee:300},
  {id:3,bank:'中国银行',company:'湖南公司',quater:'第一季度',money:300,fee:400},
  {id:4,bank:'中国银行',company:'湖南公司',quater:'第二季度',money:400,fee:500},
  {id:5,bank:'广发银行',company:'武汉公司',quater:'第三季度',money:400,fee:500},
  {id:6,bank:'广发银行',company:'广东公司',quater:'第一季度',money:100,fee:200},
  {id:7,bank:'广发银行',company:'广东公司',quater:'第二季度',money:500,fee:600},
  {id:8,bank:'广发银行',company:'湖南公司',quater:'第一季度',money:600,fee:700},
  {id:9,bank:'广发银行',company:'湖南公司',quater:'第二季度',money:600,fee:700},
  {id:10,bank:'建设银行',company:'广东公司',quater:'第一季度',money:100,fee:200},
  {id:11,bank:'建设银行',company:'广东公司',quater:'第二季度',money:500,fee:600},
  {id:12,bank:'建设银行',company:'湖南公司',quater:'第一季度',money:600,fee:700},
  {id:13,bank:'建设银行',company:'湖南公司',quater:'第二季度',money:600,fee:700},
  {id:14,bank:'建设银行',company:'湖南公司',quater:'第三季度',money:600,fee:700},
]





const attr = (data,arr,index=0,valueMap = {}) =>{
    let map = {}
    // 索引值等于数组长度,结束递归
    if(index===arr.length){
        return 
    }else{
        // 生成当前级对应索引值字段的值映射
        for(let rec of data){
            map[rec[arr[index]]] ={}
        }
        for(let k in map){
            // 如果索引值到最后一级
            if(index===arr.length-1){
                valueMap[arr[index]] = k
                // console.log(valueMap)
                map[k].children = data.filter((item)=>{
                    return arr.every(key=>item[key]==valueMap[key])
                })
                if(map[k].children.length){
                    
                    let keyArr = arr.slice(0,index+1)
                    map[k].total = data.filter(item=>{
                        return keyArr.every(key=>item[key]==valueMap[key])
                    }).reduce((pre,next)=>{
                        quatoFields.forEach(field=>{
                            pre[field] = pre[field]||0
                            pre[field]+=Number(next[field])
                        })
                        return pre
                    },{...featureFieldsObj})
                    map[k].total[arr[index]] = `${k}小计`
                }  
            }else{ // 否则,继续递归子级
                valueMap[arr[index]] = k  // 当前级别的字段对应值映射
                // console.log(valueMap)       
                map[k].children = attr(data,arr,index+1,{...valueMap})
                let keyArr = arr.slice(0,index+1) // 筛选的小计字段依据
                // 筛选当前级别的数据
                map[k].total = data.filter(item=>{
                    return keyArr.every(key=>item[key]==valueMap[key])
                })
                // 计算总计
                if(map[k].total.length){
                    map[k].total =  map[k].total.reduce((pre,next)=>{
                        quatoFields.forEach(field=>{
                            pre[field] = pre[field]||0
                            pre[field]+=Number(next[field])
                        })
                        return pre
                    },{...featureFieldsObj})
                    map[k].total[arr[index]] = `${k}小计`
                }    
            }
        }
        return map
    }
}
let subtotalMap = attr(tableData,subtotalFields)
console.log(subtotalMap)
let subtotalFieldRowSpan = calcRowSpan(subtotalMap) // 小计字段值行合并长度

// 第一个小计字段的数据值
let totalFieldValue = {
    中国银行:0,
    广发银行:0,
    建设银行:0,
    // 平安银行:0,
}

// 生成数据
function handleMap(map){
    let arr = []
    for(let k in map){
        if(Array.isArray(map[k].children)){       
            if(map[k].children.length){
                map[k].children.forEach((item,index)=>{
                    const rawItem = {...item}
                    if(!totalFieldValue[item[totalField]]){
                        totalFieldValue[item[totalField]] = 1
                    }else{
                       item = Object.assign(item,totalFieldObj)
                    }
                    if(index!==0){
                        item = Object.assign(item,subtotalFieldsObj)
                    }
                    item.rawItem = rawItem
                })
                arr.push(...map[k].children)
                arr.push(map[k].total)
            }  
        }else{ 
            let cArr = handleMap(map[k].children) 
            cArr.forEach((item,index)=>{
                if(index!==0){
                    if(!['中国银行','广发银行','建设银行','平安银行'].includes(k)){
                    item = Object.assign(item,{company:''}) 
                    }
                }
            })
            arr.push(...cArr)
            if(!Array.isArray(map[k].total)){
                arr.push(map[k].total)
            }
        }
    }
    return arr
}

let rowData = handleMap(subtotalMap)
// rowData.forEach(item=>{
//     if(item.id&&needToClearArr.indexOf(item.id)>-1){
//         item = Object.assign(item,subtotalFieldsObj)
//     }
// })
console.log(rowData)


function calcRowSpan(subtotalMap){
    let map = {}
    for(let k in subtotalMap){
         map[k] =  map[k]||{}
        map[k].rowSpan =  map[k].rowSpan||0
        if(Array.isArray(subtotalMap[k].children)){
             map[k].rowSpan = subtotalMap[k].children.length
        }else{
            map[k].children = map[k].children||{}
            map[k].children = calcRowSpan(subtotalMap[k].children)
            for(let k1 in map[k].children){
                 map[k].rowSpan += map[k].children[k1].rowSpan
                 if(map[k].children[k1].rowSpan!==0){
                     map[k].rowSpan += 1
                 }
            }
        }
    }
    return map
}

console.log(subtotalFieldRowSpan)


let obj = {
    "广东公司": {
        "children": {
            "中国银行": {
                "children": [
                     {id:1,bank:'中国银行',company:'广东公司',quater:'第一季度',money:100,fee:200},
                     {id:2,bank:'中国银行',company:'广东公司',quater:'第二季度',money:200,fee:300},
                ],
                "total":{bank:'中国银行合计',company:'',quater:'',money:300,fee:500}
            },
            "广发银行": {
                "children": [
                    {id:5,bank:'广发银行',company:'广东公司',quater:'第一季度',money:100,fee:200},
                    {id:6,bank:'广发银行',company:'广东公司',quater:'第二季度',money:500,fee:600},
                ],
                "total":{bank:'广发银行合计',company:'',quater:'',money:600,fee:800}
            }
        },
        "total":{bank:'',company:'广东公司合计',quater:'',money:900,fee:1300}
    },
    "湖南公司": {
        "children": {
            "中国银行": {
                "children": [
                     {id:3,bank:'中国银行',company:'湖南公司',quater:'第一季度',money:300,fee:400},
                     {id:4,bank:'中国银行',company:'湖南公司',quater:'第二季度',money:400,fee:500},
                ],
                "total":{bank:'中国银行合计',company:'',quater:'',money:700,fee:900}
            },
            "广发银行": {
                "children": [
                     {id:7,bank:'广发银行',company:'湖南公司',quater:'第一季度',money:600,fee:700},
                     {id:8,bank:'广发银行',company:'湖南公司',quater:'第二季度',money:600,fee:700},
                ],
                "total":{bank:'广发银行合计',company:'',quater:'',money:1200,fee:1400}
            }
        },
        "total":{bank:'',company:'湖南公司合计',quater:'',money:1900,fee:2300}
    }
}
// console.log(obj)
let finalData = [
    {id:1,company:'广东公司',    bank:'中国银行',   quater:'第一季度',money:100,fee:200},
    {id:2,company:'',           bank:'',           quater:'第二季度',money:200,fee:300},
    {     company:'',           bank:'中国银行合计',quater:'',        money:300,fee:500},

    {id:5,company:'',           bank:'广发银行',    quater:'第一季度',money:100,fee:200},
    {id:6,company:'',           bank:'',           quater:'第二季度',money:500,fee:600},
    {     company:'',           bank:'广发银行合计',quater:'',       money:600,fee:800},

    {     company:'广东公司合计',bank:'',           quater:'',       money:900,fee:1300},

    {id:3,company:'湖南公司',    bank:'中国银行',   quater:'第一季度',money:300,fee:400},
    {id:4,company:'',           bank:'',           quater:'第二季度',money:400,fee:500},
    {     company:'',           bank:'中国银行合计',quater:'',       money:700,fee:900},

    {id:7,company:'',           bank:'广发银行',   quater:'第一季度',money:600,fee:700},
    {id:8,company:'',           bank:'',           quater:'第二季度',money:600,fee:700},
    {     company:'',           bank:'广发银行合计',quater:'',       money:1200,fee:1400},

    {     company:'湖南公司合计',bank:'',           quater:'',       money:1900,fee:2300}
]
console 命令行工具 X clear

                    
>
console