1.问题使用el-tree渲染的树形结构,当数据超过一万条以上的时候页面卡死
2.解决方法:
使用vue-easy-tree来实现树形虚拟列表,注意:vue-easy-tree需要设置高度
3.代码如下
<template><div class="ve-tree" style="height:calc(100vh - 20px)"><!-- 不使用虚拟滚动时只需去掉height参数即可 --><vue-easy-treeref="veTree"node-key="id"show-checkboxheight="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. 使用方法,首先安装依赖
yarn add @wchbrad/vue-easy-tree
如果不引入样式文件可以不安装(sass-loader以及node-sass)
node-sass:4.14.1
sass-loader:8.0.2
(自己安装的时候失败了,所以选择不引入样式文件)
5.组件引入
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的默认属性代码为
<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-treev-for="(treeItem, index) in treeOption" :key="index"ref="treeComp"node-key="id"show-checkboxheight="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.datathis.defaultExpandedKeys[index] = this.expandedReduce(treeData, num)item.setCheckedKeys(checkedKeys)}}},//递归获取展开层级的idexpandedReduce(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)},[]) : []},// 过滤值改变触发filterNodeonFilter(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 = 0const 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 === 0return !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>