vue+elementUI+下拉树(select+tree),不用vue的treeselect组件,解决下拉树组件无法校验,支持下拉搜索

经过本人使用vue的treeselect组件,功能都不错,但是有俩个问题,导致我抛弃使用这组件:

1,样式问题,用了elementui的card标签后,区域限制了高度,而treeselect组件的z-index不管怎么调大,都无法浮动在card上,导致过长的list列表大部分因此被遮挡,随可滚动选择,但是很难受。

2.第二点是校验问题,也是最大原因。组件由于是install下来的,改了源码,别人拉代码后用还是要去改下源码,不符合实际。

于是,整合百度的一些代码,自己封装了SelectTree组件。

使用方法:

第一步:建组件文件,文件位置,components下建文件夹SelectTree,SelectTree下建文件index.vue,代码如下直接复制:

<template>
  <el-select v-model="valueId" :value="valueId" :clearable="clearable" @clear="clearHandle" filterable :filter-method="filterMethod" class="selectTree" ref="elSelect">  
    <el-option :value="valueId" :label="valueTitle" >
      <el-tree  id="tree-option"
        ref="selectTree"
        :filter-node-method="filterNode"
        :accordion="accordion"
        :data="options"
        :props="props"
        :node-key="props.value"    
        :default-expanded-keys="defaultExpandedKey"
        @node-click="handleNodeClick">
      </el-tree>
    </el-option>
  </el-select>
</template>
<script>
export default {
  name: "SelectTree",
  props:{
    /* 配置项 */
    props:{
      type: Object,
      default:()=>{
        return {
          value:'id',             // ID字段名
          label: 'label',         // 显示名称
          children: 'children'    // 子级字段名
        }
      }
    },
    /* 选项列表数据(树形结构的对象数组) */
    options:{
      type: Array,       
      default: ()=>{ return [] }
    },
    /* 初始值 */
    value:{
      type: String,
      default: ()=>{ return null }
    },
    /* 可清空选项 */
    clearable:{
      type:Boolean,
      default:()=>{ return true }
    },
    /* 自动收起 */
    accordion:{
      type:Boolean,
      default:()=>{ return false }
    },
  },
  data() {
    return {
      valueId:this.value,    // 初始值
      valueTitle:'',
      defaultExpandedKey:[]    
    }
  },
  mounted(){
    this.initHandle()
  },
  methods: {
    // 选择器检索过滤方法
    filterMethod(query) {
      // 调用树形控件的过滤
      this.$refs.selectTree.filter(query);
      // 忽略选择器本身的过滤
      return true;
    },
    // 树节点过滤方法
    filterNode(value, data) {
      if (!value) return true;
      return data.label.indexOf(value) !== -1;
    },
    // 初始化值
    initHandle(){
      if(this.valueId){
        this.valueTitle = this.$refs.selectTree.getNode(this.valueId).data[this.props.label]     // 初始化显示
        this.$refs.selectTree.setCurrentKey(this.valueId)       // 设置默认选中
        this.defaultExpandedKey = [this.valueId]      // 设置默认展开
      } 
      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)
      })
 
    },
    // 切换选项
    handleNodeClick(node){
      this.valueTitle = node[this.props.label]
      this.valueId = node[this.props.value]
      this.$emit('input',this.valueId)
      this.$emit('change',this.valueId)
      
      this.defaultExpandedKey = []
      
      let scrollWrap = document.querySelectorAll('.el-select-dropdown.el-popper')
      scrollWrap.forEach(ele => {
        ele.style.display = 'none'
        ele.style.position = ''
        ele.style.top = ''
        ele.style.left = ''
      })
      this.clearSelected()
       this.$refs.elSelect.blur()
    },
    // 清除选中
    clearHandle(){
      this.valueTitle = ''
      this.valueId = null
      this.defaultExpandedKey = []
      this.clearSelected()
      this.$emit('input',null)
      this.$emit('change',null)
    },
    /* 清空选中样式 */
    clearSelected(){
      let allNode = document.querySelectorAll('#tree-option .el-tree-node')
      allNode.forEach((element)=>element.classList.remove('is-current'))
    }
  },
  watch: {
    valueId(newValue, oldValue) {
      this.valueId = newValue
      this.$emit('input',newValue)
      this.$emit('change', newValue)
      
      this.initHandle()
    }
  },
};
</script>
<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>

第二步:在需要用的页面,引入组件

import SelectTree from '@/components/SelectTree'

第三步:使用组件:

<template>
    <el-form ref="dataForm" :rules="rules" :model="temp">
        <el-form-item label="所属机构" prop="orgId">
          <SelectTree
          :options="optionData"
          placeholder="请选择机构"
          v-model="temp.orgId"
          id="orgSelect"
          @input="validateField('dataForm','orgId')"
          />
        </el-form-item>
    </el-form>
</template>
<script >
export default {
    components: {SelectTree},
    data() {
        const validateOrgId = (rule, value, callback) => {
            value = this.temp.orgId;
            if (!value) {
              document.querySelector(".selectTree").style.borderColor ='red'
              callback(new Error('请选择所属机构'))
            } else {
              document.querySelector(".selectTree").style.borderColor ='white'
              callback()
            }
        }
        return {
            temp:{
                orgId:null
            },
            rules: {
                orgId: [{  required: true, trigger:             ['input','change'],validator:validateOrgId}],
            }
        }
    },
    methods: {
      validateField(form,val){
        this.$nextTick(()=>{
          this.$refs[form].validateField(val);
        }) 
      }
    }
}
</script >

注意点:

1.validateField俩个参数:第一个是form表单ref的值,第二个是prop校验的变量

2.validateField下必须用this.$nextTick,因为,数据延迟,父子数据双向绑定,等select事件后数据才是最新的

3.组件里,this.$emit特别注意,必须是input,change,页面也是trigger: ['input','change']。不知为啥,经过测试,只有这样用,数据双向绑定,页面的input事件获取的temp.orgId才是选择后的最新值,我这里没用,需要获取值的可以自行加参数看看。

4.validator:validateOrgId,巨坑。不管是自定义组件,还是install的treeselect。const validateOrgId = (rule, value, callback),value值永远是初始值,初次进入赋值是啥,后面不管怎么select选择,value不变,导致校验根本就是错误校验。所以,在函数内直接获取最新值,并替换value, value = this.temp.orgId;这里就是为啥在上面一点强调必须input,change不能少,不能换位置,数据双向绑定,this.temp.orgId才能获取最新值。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值