zoukankan      html  css  js  c++  java
  • 手把手教学~基于element封装tree树状下拉框

    在日常项目开发中,树状下拉框的需求还是比较常见的,但是element并没有这种组件以供使用。在这里,小编就基于element如何封装一个树状下拉框做个详细的介绍。

    通过这篇文章,你可以了解学习到一个树状下拉框组件是如何一步一步封装成功的。

    话不多说,先看效果图:

    封装组件

    该组件主要基于element的select组件、tree组件及input组件进行二次封装的。

    组件布局

    首先我们需要基于这几个组件对我们的组件进行布局,话不多说直接上代码:

    <template>
      <el-select ref="select">
    
        <el-option class="options">
          <el-tree  id="tree-option"
            ref="selectTree"
          >
          </el-tree>
        </el-option>
      </el-select>
    </template>
    
    <style scoped>
      .el-scrollbar .el-scrollbar__view .el-select-dropdown__item{
        height: auto;
        max-height: 274px;
        padding: 0;
        overflow: hidden;
        overflow-y: auto;
      }
      .el-select-dropdown__item.selected{
        font-weight: normal;
      }
      ul li >>>.el-tree .el-tree-node__content{
        height:auto;
        padding: 0 20px;
      }
      .el-tree-node__label{
        font-weight: normal;
      }
      .el-tree >>>.is-current .el-tree-node__label{
        color: #409EFF;
        font-weight: 700;
      }
      .el-tree >>>.is-current .el-tree-node__children .el-tree-node__label{
        color:#606266;
        font-weight: normal;
      }
    </style>
    
    

    注:css添加scoped属性,是为了让css只在该组件生效,避免样式污染

    这个时候直接使用肯定是会报错的,因为我们组件该传的参数还未传递。

    组件数据完善

    上面我们已经完成了布局,接下来就是为其丰富数据了,因为我们这个组件肯定是复用的,因此我们设计数据的时候,需要把常用的数据属性提取出来通过props传递接收。我提取的主要有几下几个数据:

    props:{
        /* 配置项 */
        props:{
          type: Object,
          default:()=>{
            return {
              value:'id',             // ID字段名
              label: 'title',         // 显示名称
              children: 'children'    // 子级字段名
            }
          }
        },
        /* 选项列表数据(树形结构的对象数组) */
        options:{
          type: Array,       
          default: ()=>{ return [] }
        },
        /* 初始值 */
        value:{
          default: ()=>{ return null }
        },
        /* 可清空选项 */
        clearable:{
          type:Boolean,
          default:()=>{ return true }
        },
        /* 自动收起 */
        accordion:{
          type:Boolean,
          default:()=>{ return false }
        },
        placeholder:{
          type:String,
          default:()=>{return "请选择"}
        }
      },
    

    大家可能注意到,我所有prop字段都给了type属性,唯独value没有,这是因为在实际使用中下拉框的数据value值可能是字符串(String)也可能是数字(Number),为了项目开发中控制台不报太多无意义的错,此处就没有规定其type。

    接收到prop之后,我们就开始对组件进行数据的处理,直接上代码:

    <template>
      <el-select  :placeholder="placeholder" ref="select">
    
        <el-option class="options">
          <el-tree  id="tree-option"
            ref="selectTree"
            :accordion="accordion"
            :data="options"
            :props="props"
            :node-key="props.value"
            :default-expanded-keys="[]"
          >
          </el-tree>
        </el-option>
      </el-select>
    </template>
    
    

    当数据过多的时候,滚动条会出现两条,如下所示:

    处理方法如下:

    // 初始化滚动条
    initScroll(){
      this.$nextTick(()=>{
        let scrollWrap = document.querySelectorAll('.el-scrollbar .el-select-dropdown__wrap')[0]
        let scrollBar = document.querySelectorAll('.el-scrollbar .el-scrollbar__bar')
        scrollWrap.style.cssText = 'margin: 0px; max-height: none; overflow: hidden;'
        scrollBar.forEach(ele => ele.style.width = 0)
      })
    },
    

    mounted中调用该方法就可以了,效果如下:

    点击选中

    数据也渲染显示出来了,这个时候我们需要实现点击数据选中功能。

    思路很简单:

    • select组件绑定value值
    • tree组件绑定节点点击事件
    • 点击事件中获取value和label
    • 将获取的值赋给select组件以及返回给父组件

    代码如下:

    <template>
      <el-select :value="valueTitle" :placeholder="placeholder" ref="select">
    
        <el-option :value="valueTitle" :label="valueTitle" class="options">
          <el-tree  id="tree-option"
            ref="selectTree"
            :accordion="accordion"
            :data="options"
            :props="props"
            :node-key="props.value"
            :default-expanded-keys="defaultExpandedKey"
            @node-click="handleNodeClick"
            >
          </el-tree>
        </el-option>
      </el-select>
    </template>
    
    data() {
        return {
          valueId:this.value,// 初始值
          valueTitle:'',
          defaultExpandedKey:[]
        }
    },
    
    // 切换选项
    handleNodeClick(node){
      this.valueTitle = node[this.props.label]//获取label
      this.valueId = node[this.props.value]//获取value
      this.$emit('getValue',this.valueId)//传值给父组件
    },
    

    这样点击选中功能就实现了,但是有个问题,点击之后,下拉框选项没有隐藏,我们只需要再调用一下select组件的blur方法即可实现隐藏

    数据初始化

    细心的小伙伴肯定已经发现了,上面有一个初始值,并且在选择器中,初始数据也是必不可少的。实现思路如下:

    • watch监听prop中value数据变化
    • 将初始值做对应赋值
    • 获取初始值对应的label并做对应赋值
    • 设置tree组件的默认选中状态
    • 设置tree组件的默认展开节点

    代码如下:

    watch: {
        value(){
          this.valueId = this.value
          this.initHandle()
        }
    },
    // 初始化值
    initHandle(){
      if(this.valueId){
        // 初始化显示label
        this.valueTitle = this.$refs.selectTree.getNode(this.valueId).data[this.props.label]     
        this.$refs.selectTree.setCurrentKey(this.valueId)// 设置默认选中
        this.defaultExpandedKey = [this.valueId]// 设置默认展开
      } 
    },
    

    mounted中调用执行既可

    清除选中

    一般输入框或者选择器都有清除功能,我们的组件自然也少不了清除功能,实现思路如下:

    • 给select组件设置clearable属性
    • 给select组件添加清除监听事件
    • 在监听事件中清除tree组件选中,并清除父组件中的值

    代码如下:

    <el-select :value="valueTitle" :clearable="clearable" @clear="clearHandle" :placeholder="placeholder" ref="select">
    </el-select>
    
    // 清除选中
    clearHandle(){
      this.valueTitle = ''
      this.valueId = null
      this.defaultExpandedKey = []
      this.clearSelected()
      this.$emit('getValue',null)
    },
    /* 清空选中样式 */
    clearSelected(){
      let allNode = document.querySelectorAll('#tree-option .el-tree-node')
      allNode.forEach((element)=>element.classList.remove('is-current'))
    },
    

    筛选数据

    当tree中数据量过大时,我们需要筛选数据,实现思路如下:

    • 给tree组件添加filter-node-method方法
    • 添加一个输入框,输入筛选的内容
    • 监听输入内容变化,并调用tree组件的筛选方法

    代码如下:

    <template>
      <el-select :value="valueTitle" :clearable="clearable" @clear="clearHandle" :placeholder="placeholder" ref="select">
        <el-input
          class="selectInput"
          placeholder="检索关键字"
          v-model="filterText">
        </el-input>
    
        <el-option :value="valueTitle" :label="valueTitle" class="options">
          <el-tree  id="tree-option"
            ref="selectTree"
            :accordion="accordion"
            :data="options"
            :props="props"
            :node-key="props.value"    
            :default-expanded-keys="defaultExpandedKey"
            :filter-node-method="filterNode"
            @node-click="handleNodeClick">
          </el-tree>
        </el-option>
      </el-select>
    </template>
    
    .selectInput{
        padding: 0 5px;
        box-sizing: border-box;
    }
    
    filterNode(value, data) {
      if (!value) return true;
      return data.name.indexOf(value) !== -1;
    }
    watch: {
        filterText(val) {
          this.$refs.selectTree.filter(val);
        }
    },
    

    这样一个简单的树状下拉框组件就封装好了。

    使用组件

    下面给个简单的使用示例:

    <template>
      <basic-container>
        <treeSelect
          :props="defaultProps"
          :options="treeData"
          :value="value"
          :accordion="true"
          @getValue="getValue($event)"
          placeholder="请选择所属区域"
        />
        <span>选中的id:{{value}}</span>
      </basic-container>
    </template>
    <script>
    import treeSelect from "@/components/treeSelect/treeSelect";
    export default {
      components: {
        treeSelect,
      },
      data() {
        return {
          defaultProps: {
            label: "name",
            value: "id",
            children: "children",
          },
          value:'',//选中的数据
          treeData:[
              {id:1,name:'monkey',children:[{id:2,name:'monkey2'},{id:3,name:'monkey3'},{id:4,name:'monkey4'}]},
              {id:5,name:'小猴子的web成长之路'},
              {id:6,name:'小猴子的web成长之路'},
              {id:7,name:'小猴子的web成长之路'},
              {id:8,name:'小猴子的web成长之路'},
              {id:9,name:'小猴子的web成长之路'},
              {id:10,name:'小猴子的web成长之路'},
              {id:11,name:'小猴子的web成长之路'},
              {id:12,name:'小猴子的web成长之路'},
              {id:13,name:'小猴子的web成长之路'},
              {id:14,name:'小猴子的web成长之路'},
              {id:15,name:'小猴子的web成长之路'},
              {id:16,name:'小猴子的web成长之路'},
              {id:17,name:'小猴子的web成长之路'},
          ]
        };
      },
      methods:{
          // 取值
        getValue(value) {
          this.value = value
        },
      }
    };
    </script>
    

    源码收录在公众号【小猴子的web成长之路】,关注公众号回复关键字【treeSelect】即可获取

  • 相关阅读:
    easyui 单元格超出鼠标放上弹出全部
    EasyUI datagrid单元格文本超出显示省略号,鼠标移动到单元格显示文本
    easyui 回车搜索
    js控制easyui文本框例子及控制html例子
    Android错误---NoClassDefFoundError: org.ksoap2.transport.HttpTransportSE
    [Android错误]The literal 100000000000000 of type int is out of range
    Eclipse错误:Conversion to Dalvik format failed with error 1
    Android微信分享音乐加网络图片失败分析
    android.view.WindowManager$BadTokenException异常
    Android错误--Attempted to add application window with unknown token
  • 原文地址:https://www.cnblogs.com/monkeySoft/p/13470066.html
Copyright © 2011-2022 走看看