SOURCE

function getRows (data) {
    const result = []
    const values = Object.values(data)
    if (values.some(v => typeof v !== 'object' || !v || !Object.keys(v).length)) {
        result.push([{
            text: Object.keys(data).join(', '),
            rowspan: 1
        }])
        return result
    }
    for (let k in data) {
        const pathList = getRows(data[k])
        if (pathList.length) {
            const first = pathList[0]
            first.unshift({
                text: k,
                rowspan: pathList.length
            })
        }
        result.push(...pathList)
    }
    return result
}

function generateHtml (rows, head) {
    return `
    <table border="1">
        <tr>
          ${ head.map(h => `<th>${h}</th>`).join('') }
        </tr>
        ${rows.map(r => {
            return `
            <tr>
                ${r.map(({ text, rowspan }) => `<td rowspan="${rowspan}">${text}</td>`).join('')}
            </tr>
            `
        }).join('\n')}
    </table>
    `
}

function render (data) {
    const rows = getRows(data)
    const head = (rows[0] || []).map(r => `Column-${r.text[0]}`)
    const html = generateHtml(rows, head)
    const div = document.createElement('div')
    div.innerHTML = html
    document.body.appendChild(div)
}


const data = {
    A1: {
        B1: {
            C1: {
                D1: 1233,
                D2: 11
            },
            C2: {
                D1: 10,
                D2: 10
            }
        },
        B2: {
            C1: {
                D1: 10,
                D2: 11
            },
            C2: {
                D1: 10,
                D2: 10
            },
            C3: {
                D1: 10,
                D2: 10
            }
        }
    }
}

setTimeout(() => {
    render(data)
}, 1000)
console 命令行工具 X clear

                    
>
console