vue3 + element plus 树组件 实现左侧勾选树节点 右侧渲染树结构 包括树节点限选、删除

Vue非懒加载树实现动态节点过滤
文章介绍了如何使用ElementUI的el-tree组件,通过非懒加载策略,在用户勾选左侧树节点时,动态过滤右侧树的节点显示。通过`filter-node-method`属性和自定义的过滤函数,实现实时筛选功能。

最终效果如下

在这里插入图片描述

解决思路

我们使用非懒加载树,使用同一数据,在勾选左侧树节点的时候,通过 element自带的filter 方法来过滤树节点,筛选出右侧的树形结构

代码片段如下

HTML

<p>已选择 {{ checkedList.keys.length }} / 10</p>
// 左侧树
<el-tree
  ref="treeLeftRef"
  :data="treeData" // 数据
  :props="treeProps" // 配置选项
  :default-checked-keys="checkedList.keys" // 设置默认选中
  show-checkbox // 显示勾选框
  node-key="name" // 每个树节点用来作为唯一标识
  @check="handleChecked" // 勾选树节点时触发
>
  <template #default="{ node, data }"> //自定义树节点
    <span>{{ data.name }} </span>
  </template>
</el-tree>
// 右侧树
<el-tree
  v-show="checkedList.keys.length > 0"
  ref="treeRightRef"
  :data="treeData"
  :props="treeProps"
  node-key="name"
  :filter-node-method="filterRightTree" // 右侧树的过滤方法
>
  <template #default="{ node, data }"> // 自定义树节点
    <span
      >{{ data.name }}
      // 删除小图标
      <img
        src="@/img/垃圾桶.png"
        alt="删除"
        @click="handleDelete(data)" 
      />
    </span>
  </template>
</el-tree>

JS

// 左侧树ref
const treeLeftRef = ref("");
// 右侧树ref
const treeRightRef = ref("");
// 数据
const treeData= ref([
	{
	    "children": [
	        {
	            "children": [
	                {
	                    "name": 1101
	                }
	            ],
	            "disabled": true,
	            "name": "1层"
	        }
	    ],
	    "disabled": true,
	    "name": "1栋"
	},
	{
	    "children": [
	        {
	            "children": [
	                {
	                    "name": 2101
	                 }
	             ],
	             "disabled": true,
	             "name": "1层"
	         }
	     ],
	     "disabled": true,
	     "name": "2栋"
	 }
]);
// 配置选项
const treeProps = {
  label: "name",
  children: "children",
  isLeaf: "isLeaf",
  disabled: "disabled", //前两层不可勾选
};
// 存储已选节点
const checkedList = ref([]);
// 右侧树过滤方法
function filterRightTree(value, data) {
  if (!value) return true;
  return value.includes(data.name);
}
// 左侧树勾选方法
function handleChecked(node, data) {
  if (checkedList.value.keys.length < 10) {
    checkedList.value.keys = treeLeftRef.value.getCheckedKeys(true);
   // 在此处调用过滤方法 筛选出右侧树节点
    treeRightRef.value.filter(checkedList.value.keys);
  } else {
    treeLeftRef.value.setChecked(node.name, false);
    ElMessage({
      message: "已选择10间",
      type: "warning",
    });
  }
}
// 右侧树删除按钮
function handleDelete(data) {
  // 删除左侧树勾选节点
  treeLeftRef.value.setChecked(data.name, false);
  // 重新获取左侧树勾选节点
  checkedList.value.keys = treeLeftRef.value.getCheckedKeys(true);
  // 调用过滤方法
  treeRightRef.value.filter(checkedList.value.keys);
}
<think>根据用户需求,用户需要在Vue 3实现一个功能:使用tab切换组件和tree组件,在搜索节点后点击tab切换,实现tab切换后过滤树节点的功能。具体来说,用户可能会在多个tab页中展示不同的树结构,并且在搜索节点后切换tab时,希望每个tab页的组件能够根据搜索条件过滤显示相应的节点。 解决方案思路: 1. 使用Vue 3的Composition API(setup语法)来实现。 2. 使用tab切换组件(如element-plus的el-tabs)和树形组件(如element-plus的el-tree)。 3. 每个tab页对应一个组件,每个组件有自己独立的过滤状态(即搜索关键词)。 4. 维护一个全局的搜索关键词,或者每个tab页单独维护自己的搜索关键词。这里考虑到每个tab的可能是独立的数据,且过滤条件可能不同,建议每个tab独立维护搜索关键词。 5. 当在某个tab页的搜索框中输入关键词进行搜索后,该tab页的会过滤显示。此时切换tab,另一个tab页的应该能够根据自己tab页的搜索关键词(如果有)重新过滤。 6. 注意:切换tab时,由于组件可能被重新渲染(如果使用v-if),或者保持状态(使用v-show),我们需要在切换tab时触发的过滤方法,或者利用响应式数据自动更新。 步骤: a. 使用el-tabs组件,每个tab对应一个el-tree和一个搜索输入框(也可以将搜索框放在tab外部,但这里假设每个tab有自己的搜索框)。 b. 为每个tab页的绑定一个独立的filterText(搜索关键词)和数据。 c. 使用watch监听每个tab的filterText,当关键词变化时,调用组件的filter方法。 d. 但注意,当切换tab时,我们希望当前激活的tab页的能够根据当前tab的搜索关键词进行过滤。然而,由于每个tab的组件的filterText是独立的,所以切换tab时,只需要确保当前tab的能够根据它自己的filterText进行过滤即可。但是,如果我们在切换tab时改变了全局的filterText,那么我们需要在切换tab时重置每个tab的过滤状态?不,应该是每个tab独立保存过滤状态。 具体实现: 1. 定义状态: - 当前激活的tab(activeTab) - 每个tab对应的数据(treeData)和过滤关键词(filterTexts)。我们可以用一个数组或对象来存储每个tab的filterText,这里为了简单,假设有两个tab,使用ref分别存储。 2. 组件的过滤方法: - 使用el-tree的filter-node-method属性,自定义过滤方法。 - 通过ref获取组件的实例,然后调用其filter方法。但是注意,每个tab的组件是独立的,所以我们要为每个组件分别设置ref,并分别调用filter方法。 3. 切换tab时,由于每个tab的搜索关键词是独立的,所以不需要额外的操作,因为每个tab的会根据自己的filterText进行过滤(通过watch监听filterText的变化,然后调用对应组件的filter方法)。但是,如果我们在切换tab时,希望保留每个tab的搜索状态(即每个tab之前搜索过的关键词仍然生效),那么我们就需要保存每个tab的过滤状态。 4. 可能遇到的问题:切换tab时,组件的重新渲染。如果使用v-if来控制tab页的显示,那么每次切换tab,组件都会重新创建。这时,我们需要在组件创建后,根据当前tab的filterText重新过滤。我们可以通过watch当前tab的filterText,并在组件挂载时执行一次过滤。 但是,更简单的方式是:每个tab页的组件都绑定一个filterText,然后通过watch这个filterText,执行过滤方法。这样,无论什么时候filterText变化(包括切换tab后,当前tab的filterText被修改),都会触发对应的过滤。 然而,注意:切换tab时,如果新的tab页还没有设置过filterText(比如初始为空),那么不会过滤。如果用户之前在这个tab页搜索过,那么filterText不为空,切换回来时,由于watch的存在,会自动过滤。 5. 如何实现多个组件?我们可以通过循环tab列表,每个tab页内放置一个组件和一个搜索框,搜索框使用v-model绑定到对应tab的filterText。 示例代码结构(使用element-plus组件): <template> <div> <el-tabs v-model="activeTab" @tab-click="handleTabClick"> <el-tab-pane v-for="(tab, index) in tabs" :key="index" :label="tab.label" :name="tab.name"> <!-- 每个tab页的搜索框 --> <el-input v-model="filterTexts[index]" placeholder="输入关键字进行过滤" /> <!-- 组件,使用v-show或v-if控制显示,这里用v-show保持状态 --> <el-tree ref="treeRefs" :data="tab.treeData" :props="defaultProps" :filter-node-method="filterNode" v-show="activeTab === tab.name" /> </el-tab-pane> </el-tabs> </div> </template> 但是注意:这里我们使用同一个filter-node-method方法,并且ref="treeRefs"会是一个数组,对应每个tab的组件实例。 然而,由于每个tab的组件可能同时存在(使用v-show),但是只有当前激活的tab才会显示,所以我们可以为每个组件分别设置ref?可以使用动态ref。 但是,更推荐的做法是:每个tab页的组件的ref使用动态ref,例如:ref="(el) => setTreeRef(el, index)",然后在methods里面将组件实例存储到数组中。或者我们也可以使用一个数组来存储每个组件的ref。 不过,在Vue3中,我们可以使用数组来存储ref,但是要注意初始化。 具体步骤: 1. 定义状态: - activeTab: 当前激活的tab,可以是tab的标识(如字符串或索引) - tabs: 一个数组,包含每个tab的配置(包括label, name, treeData, filterText) - treeRefs: 一个数组,用于存储每个组件的实例(用ref(null)初始化,数组长度与tabs相同) 2. 在模板中,使用v-for循环tabs,为每个组件设置ref,通过索引设置到treeRefs的对应位置。 3. 定义filterNode方法,这是el-tree的过滤方法,按照el-tree的要求,它接收两个参数:value(过滤值)和data(节点数据),然后返回true/false表示是否匹配。 4. 使用watch监听每个tab的filterText,当变化时,调用对应组件的filter方法。 但是,注意:每个tab的filterText是独立的,所以我们需要为每个tab单独监听。可以在循环tab的时候,使用computed为每个tab的filterText创建响应式,然后watch。或者,我们可以在每个tab的组件内部去监听?但这里我们使用一个数组来存储每个tab的filterText,然后在watch中监听整个数组的变化,并带索引。 另一种思路:在模板中,每个tab页的组件实际上都是独立的,并且每个都有自己的filterText绑定。我们可以把每个tab封装成一个单独的组件,这样每个组件内部管理自己的filterText和实例,这样逻辑更清晰。但是,如果用户希望在一个组件内完成,我们可以用以下方式: 示例代码: 注意:以下代码使用Vue3的Composition API和element-plus组件库。 </think>在 Vue 3实现 tab 切换组件和 tree 组件联动过滤功能,可通过以下步骤实现: --- ### 实现方案 #### 1. 组件结构设计 ```vue <template> <div> <!-- Tab切换组件 --> <el-tabs v-model="activeTab" @tab-click="handleTabChange"> <el-tab-pane v-for="tab in tabs" :key="tab.name" :label="tab.label" :name="tab.name"> <!-- 树形组件 --> <el-tree ref="treeRef" :data="treeData" :filter-node-method="filterNode" node-key="id" :default-expand-all="true" > <!-- 自定义节点内容 --> <template #default="{ node }"> <span>{{ node.label }}</span> </template> </el-tree> </el-tab-pane> </el-tabs> <!-- 搜索框 --> <el-input v-model="filterText" placeholder="输入关键字过滤节点" /> </div> </template> ``` #### 2. 核心逻辑实现 ```javascript <script setup> import { ref, watch, nextTick } from &#39;vue&#39;; // Tab相关状态 const activeTab = ref(&#39;tab1&#39;); const tabs = [ { name: &#39;tab1&#39;, label: &#39;分类1&#39; }, { name: &#39;tab2&#39;, label: &#39;分类2&#39; } ]; // 数据(示例) const treeData = ref([ { id: 1, label: &#39;父节点1&#39;, children: [...] }, { id: 2, label: &#39;父节点2&#39;, children: [...] } ]); // 过滤关键词 const filterText = ref(&#39;&#39;); // 组件引用 const treeRef = ref(null); // 监听搜索词变化 watch(filterText, (val) => { treeRef.value?.filter(val); // 调用的过滤方法 }); // Tab切换处理 const handleTabChange = () => { // 切换后保留过滤状态 nextTick(() => { if (filterText.value) { treeRef.value?.filter(filterText.value); } }); }; // 节点过滤方法 const filterNode = (value, data) => { if (!value) return true; return data.label.includes(value); }; </script> ``` #### 3. 关键点说明 1. **状态管理** - `filterText`:统一管理搜索关键词,避免每个Tab单独存储 - `activeTab`:当前激活的Tab标识 - `treeRef`:通过Vue的ref获取组件实例 2. **过滤联动** - `watch`监听`filterText`变化,自动触发的`filter()`方法 - Tab切换时通过`nextTick`确保DOM更新后重新应用过滤条件 3. **数据一致性** - 使用单数据源`treeData`,避免多Tab数据冗余 - 通过`filter-node-method`自定义过滤逻辑 --- ### 完整特性优化 1. **跨Tab状态保持** ```javascript // 存储每个Tab的独立过滤状态 const tabFilters = ref({}); watch(filterText, (val) => { tabFilters.value[activeTab.value] = val; }); watch(activeTab, (newTab) => { filterText.value = tabFilters.value[newTab] || &#39;&#39;; }); ``` 2. **异步数据加载** ```javascript // 按Tab加载不同数据 const loadTreeData = async (tabName) => { const res = await axios.get(`/api/tree-data/${tabName}`); treeData.value = res.data; }; watch(activeTab, loadTreeData, { immediate: true }); ``` 3. **性能优化** ```javascript // 防抖处理搜索 import { debounce } from &#39;lodash-es&#39;; watch(filterText, debounce((val) => { treeRef.value?.filter(val); }, 300)); ``` --- ### 常见问题解决 1. **切换Tab后过滤失效** - 原因:新Tab的实例未绑定过滤方法 - 方案:使用`nextTick`确保新渲染完成 2. **多组件冲突** - 方案A:使用`v-if`销毁重建(推荐) ```vue <el-tree v-if="activeTab === &#39;tab1&#39;" ...> ``` - 方案B:通过动态key强制刷新 ```vue <el-tree :key="activeTab" ...> ``` 3. **复杂过滤逻辑** ```javascript const filterNode = (value, data) => { // 匹配节点/子节点 const matchSelf = data.label.includes(value); const matchChild = data.children?.some(child => child.label.includes(value) ); return matchSelf || matchChild; }; ``` --- ### 示例效果 1. 用户输入搜索词 "财务" 过滤节点 2. 切换到其他Tab时: - 自动应用该Tab上次的过滤状态 - 若无历史状态则显示完整 3. 切回原Tab时恢复 "财务" 过滤状态 > 实现参考:[Element Plus Tree组件](https://element-plus.org/zh-CN/component/tree.html) 和 [Tabs组件](https://element-plus.org/zh-CN/component/tabs.html) 的联动控制[^2] --- ### 相关问题 1. 如何在组件实现勾选状态跨Tab同步? 2. Vue 3下如何优化大型组件渲染性能? 3. 如何实现树节点的动态懒加载与搜索过滤结合? [^1]: Liquor Tree 组件库文档 [^2]: Element Plus 树形表格联动示例 [^3]: 侧边栏组件搜索实现方案
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值