前言
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)
}
},