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

基于vue2+ElementUI的el-tree封装一个带搜索的树形组件

需求

在这里插入图片描述
实现一个如图带搜索框的下拉树形组件。

解决方案

利用el-input+el-tree实现自定义带搜索的下拉树形组件。

具体实现步骤

1、创建TreeSelect组件

<template><div class="tree-select-wrapper" v-clickoutside="handleClose"><el-inputclass="common-simple-input tree-select-input"v-model="selectedText":placeholder="placeholder":clearable="clearable":disabled="disabled":style="styleAttr"readonly@click.native="handleClick"@clear="handleClear"><iclass="el-input__icon":class="['el-icon-arrow-down', visible ? 'is-reverse' : '']"slot="suffix"></i></el-input><div v-show="visible" class="tree-select-dropdown" :style="styleAttr"><el-inputv-if="filterable"v-model="filterText"class="tree-select-filter common-simple-input"placeholder="请输入关键字进行过滤"clearable@click.native.stop></el-input><el-treeref="tree":data="options":props="defaultProps":node-key="nodeKey":default-expand-all="defaultExpandAll":expand-on-click-node="expandOnClickNode":filter-node-method="handleFilterNode"@node-click="handleNodeClick"></el-tree></div></div>
</template><script>
export default {name: 'TreeSelect',props: {value: {type: [String, Number],default: ''},textValue: {type: String,default: ''},placeholder: {type: String,default: '请选择'},options: {type: Array,default: () => []},defaultProps: {type: Object,default: () => ({children: 'children',label: 'label',value: 'value'})},nodeKey: {type: String,default: 'value'},defaultExpandAll: {type: Boolean,default: false},expandOnClickNode: {type: Boolean,default: true},filterable: {type: Boolean,default: true},clearable: {type: Boolean,default: true},disabled: {type: Boolean,default: false},styleAttr: {type: Object,default: () => ({})}},data() {return {visible: false,filterText: '',selectedText: this.textValue}},watch: {filterText(val) {this.$refs.tree.filter(val)},textValue(val) {this.selectedText = val},visible: {immediate: true,deep: true,handler(val) {if (val && this.$refs.tree) {this.$refs.tree.setCurrentKey(this.value)}}}},mounted() {if (this.value && this.$refs.tree) {this.$refs.tree.setCurrentKey(this.value)}},methods: {handleClick() {if (this.disabled) returnthis.visible = !this.visible},handleClose() {this.visible = false},handleClear() {this.$emit('input', '')this.$emit('update:textValue', '')this.$emit('change', '')this.selectedText = ''},handleNodeClick(node) {if (!node[this.nodeKey]) returnthis.$emit('input', node[this.nodeKey])this.$emit('update:textValue', node[this.defaultProps.label])this.$emit('change', node[this.nodeKey])this.selectedText = node[this.defaultProps.label]this.visible = false},handleFilterNode(value, data) {if (!value) return trueconst label = data[this.defaultProps.label] || ''return label.indexOf(value) !== -1}},directives: {clickoutside: {bind(el, binding) {function documentHandler(e) {if (el.contains(e.target)) {return false}if (binding.value) {binding.value()}}el.__vueClickOutside__ = documentHandlerdocument.addEventListener('click', documentHandler)},unbind(el) {document.removeEventListener('click', el.__vueClickOutside__)delete el.__vueClickOutside__}}}
}
</script><style lang="scss" scoped>
.tree-select-wrapper {position: relative;width: 100%;.tree-select-input {cursor: pointer;:deep(.el-input__suffix) {margin-right: vw(5);margin-top: vh(2);font-size: vw(14);}:deep(.el-input__icon) {transition: transform 0.3s;&.is-reverse {transform: rotateZ(180deg);}}}.tree-select-dropdown {width: 100%;position: absolute;top: 100%;left: 0;z-index: 1000;margin-top: 5px;padding: 5px 0;background-color: #152e58;border: 1px solid #0d59b4;border-radius: 4px;box-shadow: 0 2px 12px 0 rgba(112, 177, 218, 0.5);max-height: 400px;overflow-y: auto;cursor: pointer;.tree-select-filter {margin: 0 5px 5px;width: 90%;}:deep(.el-tree) {border: none;background: none !important;.el-tree__empty-text {color: #fff;font-size: vw(14);}.el-tree-node__label {cursor: pointer;color: #fff;font-size: vw(14) !important;}.el-tree-node__content {background: none !important;&:hover {color: #6cd9ff;background-color: rgba(27, 40, 61, 0.3) !important;}}.el-icon-caret-right:before {content: "\E791" !important;}.el-tree-node__expand-icon.expanded {transform: rotate(90deg) !important;-webkit-transform: rotate(90deg) !important;}.el-tree-node.is-current.is-focusable {background-color: rgba(27, 40, 61, 0.5) !important;.el-tree-node__label {color: #6cd9ff !important;}}}}
}
</style> 

2、使用TreeSelect组件

// 引入TreeSelect组件
import TreeSelect from "@/components/TreeSelect/index.vue";// 使用示例<TreeSelectv-model="searchForm.orgCode":text-value.sync="searchForm.orgName":options="orgTreeData"placeholder="请选择":default-props="{children: 'children',label: 'orgName',value: 'orgCode',}"node-key="orgCode":filterable="true":clearable="true":styleAttr="{ width: '200px' }"/>data() {orgTreeData: [],
},
mounted() {// 获取树形数据this.getOrgTreeData();},
methods: {// 获取树形数据方法async getOrgTreeData() {try {const res = await this.getOrgTreeListApi();this.orgTreeData = res.data || [];} catch (error) {console.error("获取事业部树形数据失败:", error);}},getOrgTreeListApi async(data) {return axios.post(`/api/org/tree`, data).then((resp) => resp.data)}
}              

写在最后

TreeSelect组件可以直接Copy进行使用,其中common-simple-input为图中el-input输入框的蓝底亮框样式,可根据自身需求进行样式自定义开发。

相关文章:

  • 【差分隐私相关概念】瑞丽差分隐私(RDP)-瑞丽散度约束了贝叶斯因子后验变化
  • 前端与Java后端交互出现跨域问题的14种解决方案
  • 使用 Node.js、Express 和 React 构建强大的 API
  • 4.15【A】pc homework3~
  • python实战案例:玩具销售数据分析
  • Java基础-泛型(泛型擦除)
  • 【redis】初识redis
  • 实验二.单按键控制LED
  • 自定义Jackson序列化和反序列化
  • 家用发电机的原理是什么?
  • 【STM32】在FreeRTOS下使用硬件SPI收发数据出现的时序耦合问题(WK2124芯片为例)
  • 使用 Sass 打造动态星空背景效果
  • 远方游子的归家记:模仿美食网页的制作与实现
  • React JSX?
  • C++红黑树
  • 时间的重构:科技如何重塑人类的时间感知与存在方式
  • 【大模型系列篇】深度研究智能体技术演进:从DeepResearch到DeepResearcher,如何重构AI研究范式
  • 深度访谈:数据中台的本质不是技术堆砌,而是业务引擎的重构
  • c++进阶-继承01
  • 加固笔记本:无人机领域智能作业的可靠算力中枢
  • 售卖自制外挂交易额超百万元,一男子因提供入侵计算机系统程序被抓
  • 国新办将于5月8日10时就《民营经济促进法》有关情况举行新闻发布会
  • 经济日报头版刊文:为什么贸易战没有出路
  • 强沙尘暴压城近万名游客被困,敦煌如何用3小时跑赢12级狂风?
  • 恒瑞医药通过港交所上市聆讯,最快或5月挂牌上市
  • 让党的理论“飞入寻常百姓家”,他如何做到有新意?