1.问题使用el-tree渲染的树形结构,当数据超过一万条以上的时候页面卡死
2.解决方法:
使用vue-easy-tree来实现树形虚拟列表,注意:vue-easy-tree需要设置高度
3.代码如下
javascript
<template>
<div class="ve-tree" style="height:calc(100vh - 20px)">
<!-- 不使用虚拟滚动时只需去掉height参数即可 -->
<vue-easy-tree
ref="veTree"
node-key="id"
show-checkbox
height="calc(100vh - 20px)"
:data="treeData"
:props="props"
></vue-easy-tree>
</div>
</template>
<script>
import VueEasyTree from "@wchbrad/vue-easy-tree";
// 样式文件,可以根据需要自定义样式或主题(使用这个样式需要装sass-loader以及node-sass)
import "@wchbrad/vue-easy-tree/src/assets/index.scss"
export default {
components: {
VueEasyTree
},
data() {
return {
props: {
label: "name",
children: "children"
},
treeData: []
};
},
created() {
const data = [],
root = 8,
children = 3,
base = 1000;
for (let i = 0; i < root; i++) {
data.push({
id: `${i}`,
name: `test-${i}`,
children: []
});
for (let j = 0; j < children; j++) {
data[i].children.push({
id: `${i}-${j}`,
name: `test-${i}-${j}`,
children: []
});
for (let k = 0; k < base; k++) {
data[i].children[j].children.push({
id: `${i}-${j}-${k}`,
name: `test-${i}-${j}-${k}`
});
}
}
}
this.treeData = data;
}
};
</script>
4. 使用方法,首先安装依赖
javascript
yarn add @wchbrad/vue-easy-tree
如果不引入样式文件可以不安装(sass-loader以及node-sass)
node-sass:4.14.1
sass-loader:8.0.2
(自己安装的时候失败了,所以选择不引入样式文件)
5.组件引入
javascript
import VueEasyTree from "@wchbrad/vue-easy-tree";
// 样式文件,可以根据需要自定义样式或主题
import "@wchbrad/vue-easy-tree/src/assets/index.scss"
export default {
components: {
VueEasyTree
}
}
6.功能列表
1.大数据量支持虚拟滚动
2.基本树形数据的展示
3.支持checkbox选择
4.支持懒加载
5.默认展开和默认选中
6.禁用节点
7.通过多种方式选中节点和获取选中的节点信息
8.支持自定义节点内容
9.支持节点过滤
10.非虚拟滚动下,支持手风琴模式
11.非懒加载时,支持节点拖拽
支持与element-ui完全相同的主题样式更换,提供与element-ui相同的图标供选用
如果使用element-ui的默认属性代码为
javascript
<template>
<div class="tree-comp">
<div class="input-box">
<el-input size="mini" suffix-icon="el-icon-search" clearable v-model="filterInputValue"
@change="onFilter" placeholder="请输入检索内容">
</el-input>
</div>
<div class="ve-tree" style="height:520px">
<!-- 不使用虚拟滚动时只需去掉height参数即可 -->
<vue-easy-tree
v-for="(treeItem, index) in treeOption" :key="index"
ref="treeComp"
node-key="id"
show-checkbox
height="520px"
:data="treeItem.treeData"
:props="props"
:filter-node-method="filterNode"
:highlight-current="true"
:default-checked-keys="allNodeIds"
:default-expanded-keys="defaultExpandedKeys[index]"
:check-on-click-node="true"
v-bind="treeItem.defaultProps"
v-on="treeItem.defaultActions"
@check-change="handleCheckChange"
></vue-easy-tree>
</div>
</div>
</template>
<script>
import VueEasyTree from "@wchbrad/vue-easy-tree";
const debounce = function debounce(fn, delay) {
let timer = null;
return function () {
clearTimeout(timer);
let args = arguments;
let that = this;
timer = setTimeout(function () {
fn.apply(that, args);
}, delay);
};
};
export default {
name: 'TreeComp',
props: {
treeOption: { type: Array },
selection: {
type: String,
default: 'multiple'
}
},
components: {
VueEasyTree
},
data() {
return {
filterInputValue: '', // 过滤搜素值
curChoosedData: {} , // 当前节点数据
filterParentNodeId: [],
selectedCount: 0,
defaultExpandedKeys: [],
allNodeIds: [],
props: {
label: "name",
children: "children"
},
treeData: []
}
},
watch: {
curChoosedData(val){
this.$emit('curChoosedDataChange', val);
},
},
computed: {
isSingle() {
return this.selection === 'single'
}
},
created(){
const data = [],
root = 8,
children = 3,
base = 9000;
for (let i = 0; i < root; i++) {
data.push({
id: `${i}`,
name: `test-${i}`,
children: []
});
for (let j = 0; j < children; j++) {
data[i].children.push({
id: `${i}-${j}`,
name: `test-${i}-${j}`,
children: []
});
for (let k = 0; k < base; k++) {
data[i].children[j].children.push({
id: `${i}-${j}-${k}`,
name: `test-${i}-${j}-${k}`
});
}
}
}
this.treeData = data;
},
mounted() {
this.getSelectedCount()
},
methods: {
expandedLevel(num = 1){
const treeCompRef = this.$refs.treeComp;
this.treeOption.forEach((item)=>{
item.treeData = this.$lodash.cloneDeep(item.treeData)
})
if(treeCompRef && treeCompRef.length > 0) {
for (const [index,item] of treeCompRef.entries()) {
let checkedKeys = item.getCheckedKeys()
let treeData = item.data
this.defaultExpandedKeys[index] = this.expandedReduce(treeData, num)
item.setCheckedKeys(checkedKeys)
}
}
},
//递归获取展开层级的id
expandedReduce(list,deep = 1){
return deep > 0 ? list.reduce((val ,next)=>{
return next.children? val.concat(next.id).concat(this.expandedReduce(next.children,deep-1)) : val.concat(next.id)
},[]) : []
},
// 过滤值改变触发filterNode
onFilter(filterVal) {
const treeCompRef = this.$refs.treeComp;
if(treeCompRef && treeCompRef.length >0){
for (let item of treeCompRef) {
this.filterParentNodeId = [];
item.filter(filterVal);
}
}
},
// 筛选树节点
filterNode(value, data) {
if (!value) return true;
let filterValue = value.split(',');
let flag = false;
filterValue.forEach((item) => {
if (data.name.indexOf(item) !== -1 || this.filterParentNodeId.includes(data.parentId)) {
this.filterParentNodeId.push(data.id);
flag = true;
}
});
return flag;
},
handleCheckChange:function (data, checked) {
if (this.isSingle) {
this.singleCheck(data,checked)
}
this.getSelectedCount()
},
singleCheck:debounce(function (data,checked){
this.$nextTick(()=>{
if (checked) {
this.$refs.treeComp[0].setCheckedKeys([data.id]);
}
})
},100),
getSelectedCount: debounce(function () {
this.selectedCount = 0
const treeCompRef = this.$refs.treeComp;
if(treeCompRef && treeCompRef.length >0){
for (const item of treeCompRef) {
let selectedNodes = item.getCheckedNodes()
let selectedChildrenNodes = selectedNodes.filter((node) => {
// !Object.prototype.hasOwnProperty.call(node, 'children')
// return node.children.length === 0
return !node.children || node.children.length === 0
})
this.selectedCount += selectedChildrenNodes.length
}
}
this.$emit('getSelectedCount', this.selectedCount)
},300)
}
}
</script>
<style scoped>
.tree-comp {
display: flex;
flex-direction: column;
overflow: hidden;
width: 100%;
height: 100%;
}
.tree-comp .input-box >>> .el-input__inner {
border: none;
border-radius: 0;
border-bottom: 1px solid #A8AED3;
height: 32px;
line-height: 32px;
}
.tree-comp .input-box >>> .el-input__suffix-inner {
line-height: 32px;
font-size: 16px;
}
.tree-comp .el-tree {
/* flex: 1; */
max-height: 100%;
overflow-y: auto;
}
.tree-node {
flex: 1;
height: 30px;
line-height: 30px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
/*background: #fff;*/
z-index: 2;
}
.tree-self {
width: 100%;
overflow: scroll;
}
.tree-self >>> .el-tree-node {
min-width: 100%;
display: table;
}
</style>