用Echarts實現前端表格引用從屬關係視覺化

葡萄城技術團隊發表於2022-12-23

在金融行業,我們經常會有審計審查的需求,對某個計算結果進行審查,但是這個計算結果可能依賴多個單元格,而且會有會有多級依賴的情況,如果讓我們的從業人員靠眼睛找,工作量巨大,而且準確性存疑,基本上死路一條,因此讓整個審查過程視覺化,迫在眉睫,現在我們利用純前端表格和Echarts將審計審查過程視覺化

一.首先我們先了解一下前端表格或Excel中引用和從屬關係:
1.在單元格B1中設定公式 =SUM(A1)。 單元格A1是單元格B1的引用單元格(引用關係
2.在單元格B1中設定公式 =SUM(A1)。 單元格B1是單元格A1的從屬單元格(從屬關係

二.接下來我們看一下最終實現效果:

1.引用關係

2.從屬關係

三.本次我們用的是Echarts的樹圖將引用和從屬關係視覺化,關於Echarts上手,大家去Echarts官網有完整上手教程,Echarts社群有很多開發者做的許多有趣又實用的demo,這裡我們用的是樹圖

四.接下來我們要用純前端表格控制元件的獲取引用和從屬關係的api將某個單元格的引用和從屬關係順藤摸瓜,刨根問題,刨到“祖墳”上,將這些關係,構造成Echarts樹圖的data結構,廢話不說,直接上核心程式碼

// 遞迴構建追蹤樹
    buildNodeTreeAndPaint = (spreadSource, trackCellInfo) => {
        let info = this.getCellInfo(trackCellInfo);
        let sheetSource = spreadSource.getSheetFromName(info.sheetName);
        // 建立跟節點
        let rootNode = this.creatNode(info.row, info.col, sheetSource, 0, "");

        let name = rootNode.sheetName + "*" + rootNode.row + "*" + rootNode.col + "*" + Math.random().toString();
        let precedentsRootNode = '';
        let dependentsRootNode = '';
        if (this.state.trackType === "Precedents" || this.state.trackType === "Both") {
            this.getNodeChild(rootNode, sheetSource, "Precedents")
            debugger;
            console.log(rootNode)
            if (this.state.trackType === "Both") {
                let rootNodeChildren = JSON.parse(JSON.stringify(rootNode.children));
                rootNode.children = [];
                precedentsRootNode = JSON.parse(JSON.stringify(rootNode));
                precedentsRootNode.children.push({
                    name: "Precedents",
                    value: "Precedents",
                    children: rootNodeChildren
                })
                this.setState({
                    precedentsRootNode: JSON.parse(JSON.stringify(precedentsRootNode)),
                })
            }
        }
        if (this.state.trackType === "Dependents" || this.state.trackType === "Both") {
            this.getNodeChild(rootNode, sheetSource, "Dependents")
            console.log(rootNode)
            if (this.state.trackType === "Both") {
                let deepInfo = [1];
                let rootNodeChildren = JSON.parse(JSON.stringify(rootNode.children));
                rootNode.children = [];
                dependentsRootNode = JSON.parse(JSON.stringify(rootNode));
                dependentsRootNode.children.push({
                    name: "Dependents",
                    value: "Dependents",
                    children: rootNodeChildren
                })
                this.setState({
                    dependentsRootNode: JSON.parse(JSON.stringify(dependentsRootNode)),
                })
            }



        }
        if (this.state.trackType === "Both") {
            precedentsRootNode.children = precedentsRootNode.children.concat(dependentsRootNode.children);
            // let bothRootNode = precedentsRootNode.children[0].children.concat(dependentsRootNode.children[0].children)
            this.setState({
                rootNode1: JSON.parse(JSON.stringify(precedentsRootNode)),
            })
        } else {
            this.setState({
                rootNode1: JSON.parse(JSON.stringify(rootNode)),
            })
        }
    }
    creatNode = (row, col, sheet, deep, trackType) => {
        let node = {
            value: sheet.getValue(row, col),
            position: sheet.name() + "!" + GC.Spread.Sheets.CalcEngine.rangeToFormula(new GC.Spread.Sheets.Range(row, col, 1, 1)),
            deep: deep,
            name: `${sheet.name()}!${GC.Spread.Sheets.CalcEngine.rangeToFormula(new GC.Spread.Sheets.Range(row, col, 1, 1))}\nvalue:${sheet.getValue(row, col)}`,
            sheetName: sheet.name(),
            row: row,
            col: col,
            trackType: trackType
        };
        return node;
    }
    getNodeChild = (rootNode, sheet, trackType) => {
        let childNodeArray = [];
        let children = [];
        let row = rootNode.row, col = rootNode.col, deep = rootNode.deep;
        if (trackType == "Precedents") {
            children = sheet.getPrecedents(row, col);
        }
        else {
            children = sheet.getDependents(row, col);
        }
        // let self = this;
        if (children.length >= 1) {
            children.forEach((node) => {
                let row = node.row,
                    col = node.col,
                    rowCount = node.rowCount,
                    colCount = node.colCount,
                    _sheet = sheet.parent.getSheetFromName(node.sheetName);
                if (rowCount > 1 || colCount > 1) {
                    for (let r = row; r < row + rowCount; r++) {
                        for (let c = col; c < col + colCount; c++) {
                            let newNode = this.creatNode(r, c, _sheet, deep + 1, trackType)
                            // if (deep < self.maxDeep) {
                            this.getNodeChild(newNode, _sheet, trackType);
                            // }
                            childNodeArray.push(newNode);
                        }
                    }
                } else {
                    let newNode = this.creatNode(row, col, _sheet, deep + 1, trackType)
                    // if (deep < self.maxDeep) {
                    this.getNodeChild(newNode, _sheet, trackType);
                    // }
                    childNodeArray.push(newNode);
                }
            });
        }
        rootNode.children = childNodeArray;
    }

五.將構造好的引用和從屬樹rootNode在Echarts中渲染

myChart.setOption(
            (option = {
                tooltip: {
                    trigger: 'item',
                    triggerOn: 'mousemove'
                },
                series: [
                    {
                        type: 'tree',
                        data: [this.state.rootNode1],
                        top: '1%',
                        left: '15%',
                        bottom: '1%',
                        right: '7%',
                        symbolSize: 10,
                        orient: this.state.trackType === 'review'?'LR':'RL',
                        label: {
                            position: this.state.trackType === 'review'?'left':'right',
                            verticalAlign: 'middle',
                            align: this.state.trackType === 'review'?'right':'left',
                        },
                        leaves: {
                            label: {
                                position: this.state.trackType === 'review'?'right':'left',
                                verticalAlign: 'middle',
                                align: this.state.trackType === 'review'?'left':'right'
                            }
                        },
                        emphasis: {
                            focus: 'descendant'
                        },
                        // layout: 'radial',
                        expandAndCollapse: true,
                        animationDuration: 550,
                        animationDurationUpdate: 750
                    }
                ]
            })
        );

        option && myChart.setOption(option);

以上就是實現報表中公式引用從屬關係Echarts視覺化的核心實現邏輯,由於工程較大,需要完整內容可以留言。。

擴充閱讀

React + Springboot + Quartz,從0實現Excel報表自動化

電子表格也能做購物車?簡單三步就能實現

使用純前端類Excel表格控制元件SpreadJS構建企業現金流量表

相關文章