用zTree替代Element UI的Tree,解決大數(shù)據(jù)卡頓問題

前言

Vue Element UI的Tree組件在加載大量節(jié)點(diǎn)時(shí)會(huì)出現(xiàn)明顯的卡頓,電腦配置差點(diǎn)更是難受。即使使用懶加載,即每展開父節(jié)點(diǎn)再去加載子節(jié)點(diǎn),若字節(jié)點(diǎn)數(shù)目同樣眾多,Tree組件在使用過程中依舊卡頓。這里提供一個(gè)jquery的方案,使用zTree替代Element UI的Tree組件,讓樹組件使用非常流暢。
因?yàn)闃涔?jié)點(diǎn)數(shù)目眾多,使用zTree依舊延續(xù)展開父節(jié)點(diǎn)再去加載子節(jié)點(diǎn)的方式。

顯示zTree

zTree:http://www.treejs.cn/v3/main.php#_zTreeInfo。首先是zTree的標(biāo)簽:

<div v-loading="treeLoading" style="height: 400px">
    // zTree
    <ul id="ztree" class="ztree" />
</div>

參照zTree官方文檔,配置zTree的setting:

data() {
    return {
        ztree: null,
        setting: {
            simpleData: {
                enable: true
            },
            check: {
                enable: true,
                chkboxType: { Y: 'ps', N: 'ps' }
            },
            view: {
                showIcon: false,
                showLine: false
            },
            callback: {
                beforeExpand: this.beforeExpand,
                onAsyncSuccess: this.onAsyncSuccess,
                onAsyncError: this.onAsyncError,
                onClick: this.zTreeOnClick
            }
        }
    }
},
async mounted() {
    // 在mountd加載根節(jié)點(diǎn)
    let nodes = await this.getLSKKTree(0, 'org')
    $.fn.zTree.init($('#ztree'), this.setting, nodes)
    this.ztree = $.fn.zTree.getZTreeObj('treeDemo')
    await this.beforeExpand(null, this.ztree.getNodes()[0])
    this.checkedLastNodes()
},

展開父節(jié)點(diǎn)加載對應(yīng)的子節(jié)點(diǎn):

async beforeExpand(treeId, treeNode) {
    // 根據(jù)業(yè)務(wù)需求判斷該節(jié)點(diǎn)是不是父節(jié)點(diǎn)
    if (treeNode.children.length === 0 && treeNode.type !== 'fec') {
        let result = await this.getLSKKTree(treeNode.id, treeNode.type)
        // 添加子節(jié)點(diǎn)到父節(jié)點(diǎn)
        this.ztree.addNodes(treeNode, result, true) 
        // 展開父節(jié)點(diǎn),顯示新加載的子節(jié)點(diǎn)
        this.ztree.expandNode(treeNode, true, false, false) 
        this.checkedChildNode(treeNode.getCheckStatus(), treeNode.children)
    }
    return true
},

接下來設(shè)置滾動(dòng)條。這里用到了vuescroll組件:https://vuescrolljs.yvescoding.org/zh/guide/

<vue-scroll ref="vs" :ops="treeScroll">
    // zTree
    <ul id="ztree" class="ztree" />
</vue-scroll>

data() {
    return  {
        treeScroll: {
            scrollPanel: {
                initialScrollY: true,
                initialScrollX: true,
                scrollingX: true,
                scrollingY: true
            },
            vuescroll: {
                mode: 'native',
                sizeStrategy: 'number',
                detectResize: true
            },
            rail: {
                size: '8px'
            },
            bar: {
                background: '#c9c9c9',
                minSize: false,
                size: '8px',
                disable: false,
                onlyShowBarOnScroll: false
            }
        },
    }
}

因?yàn)楸緛碚麄€(gè)項(xiàng)目使用的是Element UI的視覺方案,所以還要適配樣式:
zTreeStandard.png

完善功能

自動(dòng)勾選子節(jié)點(diǎn)

父節(jié)點(diǎn)的勾選狀態(tài)有三種:勾選、半選(即只有部分子節(jié)點(diǎn)選中)、不勾選。
父節(jié)點(diǎn)勾選,則新加載的子節(jié)點(diǎn)全部勾選。

// 展開父節(jié)點(diǎn),子節(jié)點(diǎn)選中狀態(tài)跟隨父節(jié)點(diǎn)
checkedChildNode(status, children) {
    if (status !== null && status.checked && status.half) {
        for (let child of children) {
            this.ztree.checkNode(child, true, true)
        }
    }
},

節(jié)點(diǎn)復(fù)選

  • 節(jié)點(diǎn)復(fù)選:有時(shí)候我們打開窗口勾選樹的節(jié)點(diǎn),然后點(diǎn)擊確定關(guān)閉窗口。再次打開窗口后,樹自動(dòng)勾選上一次勾選的節(jié)點(diǎn)。
  • 復(fù)選步驟:每次勾選節(jié)點(diǎn)時(shí)記錄勾選的節(jié)點(diǎn)checkArray、以及勾選節(jié)點(diǎn)到根節(jié)點(diǎn)的所有父節(jié)點(diǎn)pathArray。復(fù)選時(shí)根據(jù)pathArray展開該節(jié)點(diǎn)的所有父節(jié)點(diǎn),然后再勾選節(jié)點(diǎn)。

記錄checkArray、pathArray

getCheckedNodes() {
    // 獲取勾選和半選節(jié)點(diǎn)
    let nodes = this.ztree.getCheckedNodes()
    let pathArray = []
    for (let i = 0; i < nodes.length; i++) {
        let status = nodes[i].getCheckStatus()
        // 只保留勾選節(jié)點(diǎn)
        if (status.checked && status.half) {
            nodes.splice(i, 1)
            i--
        }
    }
    for (let node of nodes) {
        let path = []
        path.push(node)
        this.getPath(node, path)
        // 索引值越小,越靠近根節(jié)點(diǎn)
        path.sort((a, b) => {
            return a.tId.split('_')[1] - b.tId.split('_')[1]
        })
        pathArray.push(path)
    }
    return {
        checkArray: nodes,
        pathArray: pathArray
    }
},
// 從當(dāng)前節(jié)點(diǎn)到根節(jié)點(diǎn)
getPath(node, path) {
    let pNode = node.getParentNode()
    // 根節(jié)點(diǎn),返回 null
    if (pNode !== null) {
        if (path.length === 0 || path.findIndex(item => item.tId === pNode.tId) === -1) {
            let node = {
                name: pNode.name,
                tId: pNode.tId,
                type: pNode.type,
                id: pNode.id
            }
            path.push(node)
            this.getPath(pNode, path)
        }
    }
},
/*
    選中的節(jié)點(diǎn):
    checkArray: [
        {"name":"節(jié)點(diǎn)11","id":"1","type":"fec","tId":"treeDemo_619"}
    ]
    選中節(jié)點(diǎn)的所有父節(jié)點(diǎn)
    pathArray: 
    [
        [
            {"name":"節(jié)點(diǎn)0","tId":"treeDemo_1","type":"org","id":"1"},
            {"name":"節(jié)點(diǎn)1","tId":"treeDemo_2","type":"org","id":"4"}
        ]
    ]
*/
async checkedLastNodes() {
    if (this.checkArray.length > 0) {
        let pathArray = this.pathArray
        const ztree = this.ztree
        for (let path of pathArray) {
            for (let i in path) {
                var node = ztree.getNodeByTId(path[i].tId)
                // 還沒有加載該節(jié)點(diǎn)
                if (node === null && i > 0) {
                    // 通過上一級節(jié)點(diǎn)加載該節(jié)點(diǎn)
                    let result = await this.getLSKKTree(path[i - 1].id, path[i - 1].type)
                    node = ztree.getNodeByTId(path[i - 1].tId)
                    ztree.addNodes(node, result, true)
                    ztree.expandNode(node, true, false, false)
                }
            }
        }
        for (let check of this.checkArray) {
            let node = ztree.getNodeByTId(check.tId)
            // 勾選節(jié)點(diǎn)
            ztree.checkNode(node, true, false, false)
        }
        // vuescroll跳轉(zhuǎn)到第一個(gè)選中節(jié)點(diǎn)的位置
        this.$refs['vs'].scrollIntoView('#' + this.checkArray[0].tId, 200)
    }
},
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容