当前位置: 首页 > news >正文

el-select+el-tree、el-select+vl-tree实现下拉树形选择

主要实现el-select下使用树结构,支持回显和筛选功能

el-select+el-tree   实现回显+搜索

数据量大时推荐使用 el-select+vl-tree 封装的组件避免页面卡顿

vl-tree 简介 | Virtual Tree

封装的组件 el-select+el-tree   实现回显+搜索

composeTree.vue

<template><el-select :popper-class="popperClass"v-model="selectedList"placeholder="请选择"filterable:filter-method="handleFilter" multiple:collapse-tags="collapseTags"size="mini"@visible-change="handleSelectVisibleChange"@remove-tag="removeTag"><el-tree :filter-node-method="filterNode" show-checkbox ref="tree"@check-change="handleCheckChange":data="treeList" :node-key="nodeKey":props="props"><template slot-scope="{ node, data }"><slot :node="node" :data="data"><span class="custom-tree-node">{{data[props.label]}}</span></slot></template></el-tree><el-option value="" style="display: none;"></el-option></el-select>
</template>
<script>
export default {props: {props: {type: Object,default: () => {return {children: 'children',label: 'label',value: 'value',}}},nodeKey: {type: String,default: 'value'},collapseTags: {type: Boolean,default: false},popperClass: {type: String,default: ''},selectedIdList: {type: Array,default: () => []},treeList: {type: Array,default: () => []},feekback: {type: Boolean,default: false}},model: {prop: 'selectedIdList',//选中的数组event: 'updateSelectedIdList'},watch: {feekback: {handler(val) {if (val) {this.$nextTick(() => {this.$refs['tree'].setCheckedKeys(this.selectedIdList, true);})}},immediate: true},},data() {return {list: [],searchVal: '',noFilterTreeNode: false,//是否过滤树节点selectedList: [],// checkedNodeList:[],}},created() {},methods: {//筛选handleFilter(val) {if (this.noFilterTreeNode) return;this.searchVal = val;this.$refs['tree']?.filter(val);},filterNode(value, data, node) {if (!value) return truereturn data[this.props.label].toLowerCase().indexOf(value.toLowerCase()) !== -1},removeTag(val) {this.watchCheckChangeRun = false;let obj = this.checkedNodeList.find(item => item[this.props.label] === val);this.$refs.tree.setChecked(obj[this.props.value], false);setTimeout(() => {//由于首次回显不对tree有任何操作的时候,直接移除标签,不能触发tree的change事件,所以添加个手动调用if (!this.watchCheckChangeRun) {console.log('手动调用handleCheckChange======');this.handleCheckChange();}},1)},handleSelectVisibleChange(val) {this.noFilterTreeNode = false;if (!val) {//select框隐藏时,重置树结构if (this.searchVal) {this.handleFilter('')}}},//树选择变化handleCheckChange() {this.watchCheckChangeRun = true;let checkList=this.$refs['tree'].getCheckedNodes(true);this.selectedList = checkList.map(item => item[this.props.label]);this.$emit('updateSelectedIdList', checkList.map(item => item[this.props.value]));this.checkedNodeList = checkList;this.noFilterTreeNode = true;//避免vl-tree筛选问题},}
}
</script>

largeAmountTree.vue

使用el-select+vl-tree 实现大数据量时的 回显+搜索

vl-tree地址  简介 | Virtual Tree

<template><!-- 插件demo地址 https://sangtian152.github.io/virtual-tree/zh/demo/ --> <el-select :popper-class="'treeScrollSep '+popperClass"v-model="selectedList"placeholder="请选择"filterable:filter-method="handleFilter" multiple:collapse-tags="collapseTags"size="mini"@visible-change="handleSelectVisibleChange"@remove-tag="removeTag"><vl-tree :filter-method="filterNode"  show-checkbox ref="tree"@check-change="handleCheckChange":data="treeList" :node-key="nodeKey":props="props"></vl-tree><el-option value="" style="display:none;"></el-option></el-select>
</template>
<script>
export default {data() {return {list: [],searchVal: '',noFilterTreeNode: false,//是否过滤树节点selectedList: [],firstFeedback:true,} },props: {props: {type: Object,default: () => {return {children: 'children',label: 'label',value: 'value',}}},nodeKey: {type: String,default: 'value'},collapseTags: {type: Boolean,default: false},popperClass: {type: String,default: ''},selectedIdList: {type: Array,default: () => []},treeList: {type: Array,default: () => []},feekback: {type: Boolean,default: false}},model: {prop: 'selectedIdList',//选中的数组event: 'updateSelectedIdList'},watch: {feekback: {handler(val) {if (val) {this.$nextTick(() => {this.$refs['tree'].setCheckedKeys(this.selectedIdList, true);this.handleCheckChange();})  }},immediate: true},},methods: {handleFilter(val) {if (this.noFilterTreeNode) return;this.searchVal = val;this.$refs['tree'].filter(val)},filterNode(value, data, node) {if (!value) return truereturn data[this.props.label].toLowerCase().indexOf(value.toLowerCase()) !== -1},removeTag(val) {this.watchCheckChangeRun = false;let obj = this.checkedNodeList.find(item => item[this.props.label] === val);this.$refs.tree.setChecked(obj[this.props.value], false);setTimeout(() => {//直接移除标签,不能触发tree的change事件,所以添加个手动调用if (!this.watchCheckChangeRun) {this.handleCheckChange();}},1)},handleSelectVisibleChange(val) {this.$nextTick(() => {this.$refs['tree'].$el.children[0].children[0].scrollTop = 0;})this.noFilterTreeNode = false;if (!val) {//select框隐藏时,重置树结构if (this.searchVal) {this.handleFilter('')}}},//树选择变化handleCheckChange() {this.watchCheckChangeRun = true;let checkList=this.$refs['tree'].getCheckedNodes(true);this.selectedList = checkList.map(item => item[this.props.label]);this.$emit('updateSelectedIdList', checkList.map(item => item[this.props.value]));this.checkedNodeList = checkList;this.noFilterTreeNode = true;//避免vl-tree筛选问题},getSelectedInfo(data, list , arr) {data.forEach((item) => {let d = false;d = list.some(itemId => {return item[this.props.value] === itemId[this.props.value]})if (d) {arr.push(item);}if (item[this.props.children]) {this.getSelectedInfo(item[this.props.children], list);// 检查子节点是否有 disabled 为 true 的情况const childHasDisabled = item[this.props.children].some(child => child.disabled);if (childHasDisabled) {arr.push(item);}}});},}}
</script>
<style lang="less">
.treeScrollSep{min-width:260px !important;.vl-scrollbar.vl-virtual-list__wrapper{padding-left:8px;}.el-scrollbar__bar.is-vertical{display:none;}.vl-tree__content,.vl-tree-node__label{white-space: nowrap;text-overflow: ellipsis;overflow: hidden;}
}
</style>

页面中引用组件

页面内使用promise模拟接口返回列表数据和回显数据

<template><div><h2>下拉框中树结构及搜索功能</h2><div v-for="(v,i) in list" :key="i" class="box"><composeTree  :props="props" :nodeKey="'id'"v-model="v.selectedIdList" :treeList="treeList" :collapseTags="false" :feekback="feekback"><!--  <template #default="{node,data}"><div>{{data.name}}-{{ data.id }}</div></template> --></composeTree><div>selectedIdList:{{ v.selectedIdList }}</div></div><largeAmountTree :treeList="treeList2" :collapseTags="true" nodeKey="nodeId" :props="largeProps" v-model="largeSelectedIdList" :feekback="feekback2"></largeAmountTree><div>largeSelectedIdList:{{ largeSelectedIdList }}</div></div>
</template>
<script>
import composeTree from './composeTree.vue';
import { resTest } from './test';
import largeAmountTree from './largeAmountTree.vue';
export default {data() {return {props:{children: 'children',label: 'name',value: 'id',},list: [],treeList: [],treeList2: [],largeProps: {children: 'children',label: 'itemCategoryName',value: 'nodeId',disabled:'disabled'},largeSelectedIdList: [],feekback: false,feekback2: false}},components: {composeTree,largeAmountTree},created() {// 异步获取列表数据const promise1 = new Promise(function(resolve, reject) {if (true) {setTimeout(() => {const initials = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']const options = Array.from({ length: 50 }).map((_, idx) => ({id: `Option${idx + 1}`,name: `${initials[idx % 10]}${idx}`,children: [{id: `Option${'0' + idx + 1}`,name: `${initials[idx % 10]}${'0'+idx}`,},{id: `Option${'1' + idx + 1}`,name: `${initials[idx % 10]}${'1'+idx}`,}]}));// this.treeList = options;resolve(options);},1000)} else {reject(error);}});// 异步获取列表数据const promise2 = new Promise(function(resolve, reject) {if (true) {setTimeout(() => {// this.treeList = options;resolve(resTest.data);},1000)} else {reject(error);}});// 异步获取回显数据const promise3 = new Promise(function(resolve, reject) {if (true) {setTimeout(() => {// this.treeList = options;resolve([{id: 1,selectedIdList:['Option001']},{id: 2,selectedIdList:['Option111']}]);},2000)} else {reject(error);}});// 异步获取回显数据const promise4 = new Promise(function(resolve, reject) {if (true) {setTimeout(() => {resolve(['4100000001107797_4100000001107901']);},2500)} else {reject(error);}});this.getData1 = promise1.then(res => {this.treeList = res;})this.getData2 = promise2.then(res => {this.treeList2 = res;})this.getData3 = promise3.then(res => {this.list = res;})this.getData4 = promise4.then(res => {this.largeSelectedIdList = res;})//列表数据和回显数据都返回时Promise.all([this.getData1, this.getData3]).then(() => {console.log('all done');this.feekback = true;})//列表数据和回显数据都返回时Promise.all([this.getData2, this.getData4]).then(() => {console.log('all done---2');this.feekback2 = true;})},mounted() {},
}
</script>
<style lang="less" scoped>
.box {margin-bottom: 20px;
}
</style>


http://www.mrgr.cn/news/96924.html

相关文章:

  • VS+Qt配置QtXlsx库实现execl文件导入导出(全教程)
  • 论文阅读9——更严格的汽车排放标准对气候、健康、农业和经济的影响
  • 酶动力学预测工具CataPro安装教程
  • LeetCode Hot100 刷题笔记(4)—— 二叉树、图论
  • 详解相机的内参和外参,以及内外参的标定方法
  • 论文阅读10——解开碳排放与碳足迹之间的关系:文献回顾和可持续交通框架
  • 国产系统服务器识别不到SATA盘
  • 洛谷题单3-P5720 【深基4.例4】一尺之棰-python-流程图重构
  • SQL语句(一)—— DDL
  • 【大模型系列篇】大模型基建工程:使用 FastAPI 构建 SSE MCP 服务器
  • WPF学习路线
  • 02_使用Docker在服务器上部署Jekins实现项目的自动化部署
  • 亚马逊云科技携手 DeepSeek:开启企业级生成式 AI 新征程
  • react中hooks使用
  • 04-深入解析 Spring 事务管理原理及源码
  • Transformer【学习记录】
  • LeetCode Hot100 刷题笔记(9)—— 二分查找、技巧
  • 【1】搭建k8s集群系列(二进制部署)之系统初始化
  • Python设计模式:代理模式
  • 2024年信息素养大赛 C++小学组初赛 算法创意实践挑战赛 真题答案解析