一、组件需求背景
产品要做一个树形组织选择组件,既可以选中或取消所有下级,也可以单独选中当前组织。 支持树的搜索等功能
二、功能特点
- 支持单选/多选模式
-
支持搜索过滤
-
支持展示选择路径
-
支持清空/删除已选项
-
支持禁用状态
-
支持权限控制
-
支持自定义占位符
-
支持可折叠标签
三、核心实现
1. 选择器外观实现
看起来就是一个普通的下拉选择器,点击之后又出现弹框。
<template>
<div class="el-select">
<!-- 多选标签区域 -->
<div class="el-select__tags" v-if="multiple">
<!-- 折叠标签模式 -->
<span v-if="collapseTags">
<el-tag>{{ tagsList[0] }}</el-tag>
<el-tag v-if="tagsList.length > 1">
+ {{ tagsList.length - 1 }}
</el-tag>
</span>
<!-- 完整标签模式 -->
<transition-group v-else>
<el-tag v-for="item in tagsList">
{{ item }}
</el-tag>
</transition-group>
</div>
<!-- 输入框区域 -->
<el-input
v-model="innerValue"
readonly
:placeholder="placeholder"
>
<i slot="suffix" class="el-select__caret"></i>
</el-input>
</div>
</template>
点击之后出现弹框
2. 弹窗选择实现
支持点击节点和勾选节点
<template>
<el-dialog title="选择组织" width="800px">
<div class="transfer__body">
<!-- 左侧树形选择 -->
<div class="transfer-pane">
<el-input
v-model="keyword"
placeholder="请输入关键词查询"
/>
<el-tree
:data="treeData"
:props="props"
:expand-on-click-node="false"
default-expand-all
@node-click="handleNodeClick"
class="JNPF-common-el-tree"
node-key="id"
v-loading="loading"
ref="tree"
show-checkbox
check-strictly
@check="handleCheck"
:filter-node-method="filterNode">
<span class="custom-tree-node" slot-scope="{ node, data }">
<i :class="data.icon"></i>
<span class="text">{{ node.label }}</span>
</span>
</el-tree>
</div>
<!-- 右侧已选列表 -->
<div class="transfer-pane">
<div v-for="item in selectedData">
<span>{{ item }}</span>
<i class="el-icon-delete" @click="removeData"></i>
</div>
</div>
</div>
</el-dialog>
</template>
3. 树节点选择与联动
支持双边操作
methods: {
// 处理节点选择
handleCheck(node, {checkedKeys}) {
const tree = this.$refs.tree;
// 获取所有子节点
const children = this.getChildren(node);
if (children.length > 0) {
if (checkedKeys.includes(node.id)) {
// 选中父节点时联动选中子节点
children.forEach(child => {
tree.setChecked(child.id, true);
this.handleNodeClick(child);
});
} else {
// 取消父节点时联动取消子节点
children.forEach(child => {
tree.setChecked(child.id, false);
this.removeSelectedNode(child);
});
}
}
},
// 获取所有子节点
getChildren(node) {
const children = [];
const traverse = (node) => {
if (node.children) {
node.children.forEach(child => {
children.push(child);
traverse(child);
});
}
};
traverse(node);
return children;
}
}
4. 搜索过滤功能
methods: {
// 搜索过滤
filterNode(value, data) {
if (!value) return true;
return data.fullName.indexOf(value) !== -1;
},
// 递归判断子节点是否包含关键字
includesChildren(node, keyword) {
let flag = false;
const traverse = (node) => {
if (node.children) {
node.children.forEach(child => {
if(child.fullName.includes(keyword)){
flag = true;
} else {
traverse(child);
}
});
}
};
traverse(node);
return flag;
}
}
5. 数据回显与同步
methods: {
// 设置默认值
setDefault() {
if (!this.value?.length) {
this.clearSelection();
return;
}
// 处理选中数据
let selectedIds = this.multiple ? this.value : [this.value];
this.selectedIds = JSON.parse(JSON.stringify(selectedIds));
// 构建显示文本
let textList = selectedIds.map(item => {
return item.map(id => {
const node = this.allList.find(n => n.id === id);
return node?.fullName;
}).join('/');
});
this.selectedData = textList;
this.updateInputValue();
},
// 更新输入框显示
updateInputValue() {
if (this.multiple) {
this.innerValue = '';
this.tagsList = this.selectedData.map(text =>
text.split('/').slice(-1)[0]
);
} else {
this.innerValue = this.selectedData.join(',');
}
}
}
四、性能优化
1. 虚拟滚动
对于大量数据的组织树,可以使用虚拟滚动优化性能:
import VirtualScroll from 'vue-virtual-scroll-list'
components: {
VirtualScroll
},
computed: {
virtualizedData() {
return this.treeData.map((node, index) => ({
index,
node
}))
}
}
2. 懒加载
对于深层次的组织树,采用懒加载方式:
async loadNode(node, resolve) {
if (node.level === 0) {
resolve(this.getRootNodes())
return
}
const children = await this.loadChildrenNodes(node.data.id)
resolve(children)
}
3. 搜索防抖
import { debounce } from 'lodash'
created() {
this.search = debounce(this.search, 300)
}
五、使用示例
<template>
<com-select-org
v-model="selectedOrgs"
:multiple="true"
:auth="true"
placeholder="请选择组织"
@change="handleOrgChange"
/>
</template>
<script>
export default {
data() {
return {
selectedOrgs: []
}
},
methods: {
handleOrgChange(ids, data) {
console.log('选中的组织ID:', ids)
console.log('选中的组织数据:', data)
}
}
}
</script>
写在最后
本文详细介绍了Element Tree组件在企业级应用中的高级使用方法,从基础的节点状态控制到复杂的父子联动选择,从简单的数据过滤到完整的节点管理机制。
希望这篇文章能给正在使用Element Tree组件的同学带来帮助!如果觉得文章对你有帮助,欢迎点赞转发,也欢迎在评论区分享你在使用Element Tree时的心得体会!
关注我,带你玩转前端开发,一起探讨更多技术话题!