前言
某个需求要展示大量树节点,原本使用普通el-tree,默认不要展开太多层次(注意树节点默认全部展开页面渲染很卡),勉强能应付,但是检索功能会展开所有节点,还是很卡顿,只能硬着头皮改版,就想到了用element-plus的虚拟滚动树。
查了网上没有多少el-tree-v2的使用文章,下面是自己的踩坑过程,给有需要的人提供些帮助
关键代码
基本代码
需要注意v2不使用node-key=“id”,使用value:“id”
<template>
<!-- 计算最长文字宽度,显示横向滚动条用到 -->
<span style="visibility: hidden; position: fixed; z-index: -1" ref="maxlengthLabelRef">{{ state.maxlengthLabel }}</span>
<el-input class="mb10 ipt" v-model="filterText" clearable @input="onQueryChanged" placeholder="输入名称" />
<el-tree-v2
class="point-tree"
v-loading="state.treeLoading"
ref="treeRef"
:data="state.treeData"
@check-change="handleCheckChange"
:filter-method="filterNode"
:props="defaultProps"
:height="treeHeight"
show-checkbox
>
</el-tree-v2>
</template>
<script setup lang="ts">
interface Tree {
id: string;
label: string;
leaf: boolean;
disabled: boolean;
children: Tree[];
}
//重点,value对应id
const defaultProps = {
value: "id",
label: "label",
children: "children",
disabled: "disabled",
};
const state = reactive({
treeLoading: false,
treeData: [] as Tree[],
maxlengthLabel: "", //最长label
maxlengthLabelWidth: "",
});
const treeRef = ref();
//前端检索功能
const filterText = ref("");
const filterNode = (value: string, data: Tree) => {
if (!value) return true;
return data.label.includes(value);
};
const onQueryChanged = (query: string) => {
treeRef.value!.filter(query);
};
//勾选复选框事件
const handleCheckChange = (data: Tree) => {
//业务
……
};
</script>
使用代码勾选/取消复选框(回显功能)
nodeId指节点id,叶子节点和非叶子节点都可以
//勾选
treeRef.value.setChecked(nodeId, true, false);
//取消勾选
treeRef.value.setChecked(nodeId, false, false);
动态禁用/解除禁用复选框勾选(改变disabled属性)
//1、对treeData进行遍历处理,改变disabled属性状态为true/false
……递归或者遍历函数
func()
//2、重新赋值回去
treeRef.value.setData(state.treeData);
文字长度超出,显示横向滚动条
项目需求是文字过多,需要整体滚动,但是v2虚拟tree只展示视野内节点,动态生成,且节点是绝对定位absolute来实现,不能靠节点自身宽度撑开
解决思路,有点麻烦
1、获取treeData,遍历计算哪个label文字最多(计算规则,汉字占2个字符,数字、字母、符号占1个字符),返回maxLengthLabel本身
(这一步是借助大模型写出的代码)
// 计算字符最多label,规则为汉字两个字符,其他一个字符
const findLongestLabelWithCustomLength = (nodeList: Tree[], longestLabel = "", maxLength = 0): [string, number] => {
for (const node of nodeList) {
const currentLabel = node.label;
const currentLength = calculateLabelLength(currentLabel);
if (currentLength > maxLength) {
maxLength = currentLength;
longestLabel = currentLabel;
}
if (node.children && node.children.length > 0) {
const [childLongestLabel, childMaxLength] = findLongestLabelWithCustomLength(node.children, longestLabel, maxLength);
if (childMaxLength > maxLength) {
maxLength = childMaxLength;
longestLabel = childLongestLabel;
}
}
}
return [longestLabel, maxLength];
};
const calculateLabelLength = (label: string) => {
let length = 0;
for (const char of label) {
// 判断字符是否为汉字
if (isChinese(char)) {
length += 2;
} else {
length += 1;
}
}
return length;
};
const isChinese = (char: string) => {
// 判断字符是否属于Unicode中的汉字范围
const code = char.charCodeAt(0);
return (code >= 0x4e00 && code <= 0x9fff) || (code >= 0x3400 && code <= 0x4dbf);
};
2、在页面放一个隐藏的span标签,把maxLengthLabel的文本放进去,获取span标签宽度,这样做主要是考虑字体大小、字体因素影响。
<template>
<!-- 计算最长文字宽度,显示横向滚动条用到 -->
<span style="visibility: hidden; position: fixed; z-index: -1" ref="maxlengthLabelRef">{{ state.maxlengthLabel }}</span>
</template>
3、拿span宽度spanWidth赋值给tree,过程如下
const maxlengthLabelRef = ref();
const getTreeData = () => {
const params = {
};
state.treeLoading = true;
getTree(params).then((res: any) => {
state.treeLoading = false;
resHandler(res, () => {
state.treeData = res.data || [];
if (res.data.length) {
// 调用第一步的函数
const maxLabelLength = findLongestLabelWithCustomLength(res.data);
state.maxlengthLabel = maxLabelLength[0];
nextTick(() => {
//这里+150,是各层级前边的缩进,因为这个项目的tree层级不确定,我就懒省事多写了点,固定的
state.maxlengthLabelWidth = parseInt(getComputedStyle(maxlengthLabelRef.value, null).width) + 150 + "px";
});
}
});
});
};
4、给tree绑定宽度样式
<style lang="scss" scoped>
:deep() {
// 设置el-tree宽度,产生水平滚动
.el-vl__wrapper {
width: v-bind("state.maxlengthLabelWidth");
position: static;
}
//给虚拟滚动条重新定位,否则水平滚动滚动条会跟着滚走
.el-virtual-scrollbar {
position: fixed !important;
top: 317px !important;//根据页面来
left: 416px !important;//根据页面来
right: unset !important;
bottom: unset !important;
}
}
</style>
需要注意,以上显示水平滚动条有两个不足:
1、折叠时候,滚动条还会存在
2、树层级不定时候,宽度不够精准
有更好的办法,欢迎评论反馈