vue element select-tree组件 超简单实现 直接使用满足多选|单选,数据双向绑定,代码可二次开发

导读:el-select-tree 利用el-select 及el-tree特性重新封装,直接继承el-select 及el-tree的属性,单选多选支持顺畅,满足常规的表单下拉树支持,代码需要优化的地方还有很多,水平有限如有更好的方法请留言告知谢谢,感谢支持感谢开源。

近期因为工作需要故而有把此组件重新整理和封装了下,有如下几点调整:
1:使用$attars属性暴露子组件的属性,方法(自己写)和事件(v-bind="$listeners")请自行去暴露优化
2、应各位朋友期望,添加了组件搜素功能
3、其他一些更改

旧版代码:

<template>
  <el-select
    :collapseTags="collapseTags"
    :multiple="multiple"
    v-model='selectId'
    :clearable="clearable"
    :props='defaultProps'
    :placeholder='placeholder'
    @clear='handleClear'
    ref='selectUpResId'
    @remove-tag="handleRemoveTag">
    <!-- 设置一个空option 不然selectData为空下拉不显示 -->
    <el-option hidden value="xx" label="xx" key="xxxxx"></el-option>
    <el-option hidden v-for="(item, _index) in selectData" :value="item[defaultProps['id']]" :label="item[defaultProps['label']]" :key="_index"></el-option>
    <!-- 设置树形组件-->
  <el-tree
    :data='data'
    :show-checkbox="multiple"
    :node-key="defaultProps['id']"
    :props='defaultProps'
    :expand-on-click-node='false'
    :check-on-click-node='true'
    @node-click='handleNodeClick'
    @check="handCheck"
    ref="tree">
  </el-tree>
  </el-select>
</template>

<script>
  export default {
    name: 'ElSelectTree',
    componentName: 'ElSelectTree',
    props: {
      // ********* 属性 如有需要自行添加 ****
      multiple: Boolean, // 默认:false
      clearable: Boolean, // 默认:false
      collapseTags: Boolean, // 默认:false
      data: {  // tree 数据
        type: Array,
        default: function () {
          return []
        }
      },
      placeholder: {
        type: String,
        default: '请选择'
      },
      // ********* 属性 ******************
      defaultProps: {
        type: Object,
        default: {
          children: 'children',
          label: 'label',
          id: 'id'
        }
      },
      value: [String, Number, Object, Array]
    },
    data () {
      return {
        selectId: '',
        selectData: []
      }
    },
    mounted () {
      this.$nextTick(_ => {
        this.setDefaultValue(this.value)
      })
    },
    watch: {
      value: function (v) {
        console.log('watch value', v)
        this.setDefaultValue(v)
      }
    },
    methods: {
      // 回填值
      setDefaultValue (v) {
        if (this.multiple) { // 多选
          this.$refs.tree.setCheckedKeys(v)
          this.handCheckSetValue()
        } else { // 其他
          this.$refs.tree.setCurrentKey(v)
          this.$nextTick(_ => {
            this.handleNodeSetValue()
          })
        }
      },
      // 单选点击事件
      handleNodeClick (data) {
        if (this.multiple) return
        // 隐藏下拉框的效果
        this.$refs.selectUpResId.blur()
        this.$emit('input', this.handleNodeSetValue())
        console.log('handleNodeClick', this.value)
      },
      handleNodeSetValue () {
        var cNode = this.$refs.tree.getCurrentNode()
        this.selectData = cNode ? [cNode] : []
        // 设置值
        var _value = cNode ? cNode[this.defaultProps['id']] : null
        this.selectId = _value
        return _value
      },
      // 多选选择勾选
      handCheck (currentNode, checkedNodes) {
        if (!this.multiple) return
        this.$emit('input', this.handCheckSetValue())
        console.log('multiple handleNodeClick', this.value)
      },
      handCheckSetValue () {
        this.selectData = this.cloneDeep(this.$refs.tree.getCheckedNodes(true, false))
        // 设置值
        var _value = this.selectData.map(item => item[this.defaultProps['id']])
        this.selectId = _value
        return _value
      },
      // 清空触发
      handleClear () {
        this.$emit('input', this.multiple ? [] : null)
      },
      // 多选模式移除tag
      handleRemoveTag (v) {
        console.log('handlRemoveTag formData.id', this.selectId)
        this.$emit('input', this.selectId)
      }
    }

  }
</script>

调用方式:

<el-select-tree :defaultProps= "{
                  children: 'children',
                  label: 'name',
                  id: 'id'
    }" v-model="test" multiple collapse-tags clearable :data="treeList" placeholder="xxxxxxx"></el-select-tree>

新版代码

个别属性需要特殊处理,见props中的属性
<template>
  <el-select
    v-bind="$attrs"
    :style="styles"
    v-model='selectId'
    :multiple="multiple"
    :props='dynaDefProps'
    @clear='handleClear'
    ref='selectUpResId'
    @remove-tag="handleRemoveTag"
    :filter-method="handleFilterMethod"
    @visible-change="selectVisibleChange">
    <!-- 设置一个空option 不然selectData为空下拉不显示,这样处理无需用js去处理显示和隐藏了 -->
    <el-option hidden value="x" label="x" key="x"></el-option>
    <el-option hidden v-for="(item, _index) in selectData" :value="item[dynaDefProps['id']]" :label="getLable(item)" :key="_index"></el-option>
    <!-- 设置树形组件-->
    <el-tree
      v-bind="$attrs"
      :show-checkbox="multiple"
      :node-key="dynaDefProps['id']"
      :props='dynaDefProps'
      @node-click='handleNodeClick'
      @check="handCheck"
      :filter-node-method="handleFilterNode"
      ref="tree">
    </el-tree>
  </el-select>
</template>

<script>
  export default {
    inheritAttrs: false,
    name: 'ElSelectTree1',
    componentName: 'ElSelectTree1',
    props: {
      // ********* 自定义的属性 如有需要自行添加 ****
      multiple: Boolean, //是否多选 默认:false
      onlyLeaf: { // 是否只包含叶子节点
        type: Boolean,
        default: false
      },
      styles: {// 给一个样式属性
        type: String,
        default: 'width:100%'
      },
      defaultProps: {
        type: Object,
        required: false,
        default: () => ({
          parent: 'parentId',   // 父级唯一标识
          id: 'id',          // 唯一标识
          label: 'code',       // 标签显示
          children: 'children' // 子级
        })
      },
      value: [String, Number, Object, Array]
    },
    data () {
      return {
        selectId: '',
        selectData: []
      }
    },
    computed: {
      // 树节点配置选项
      dynaDefProps () {
        return Object.assign({}, {
          parentId: 'parentId',
          id: 'id',
          label: 'name',
          children: 'children',
          filter: 'filter'
        }, this.props)
      }
    },
    mounted () {
    },
    watch: {
      value: {
        handler (v, ov) {
          this.$nextTick(() => {
          // 针对第一次请求,如果data延迟加载而出现赋值显示不是label的属性问题
            if (this.$refs.tree.data.length) {
              this.setDefaultValue(v)
            } else {
              this.$refs.tree.$watch('data', () => {
                this.setDefaultValue(v)
              })
            }
          })
        },
        immediate:true, // watch立即执行
        deep: true
      }
    },
    methods: {
      getLable (item) {
        return (typeof this.dynaDefProps['label'] === 'string') ? item[this.dynaDefProps['label']] : this.dynaDefProps['label'](item)
      },
      // 回填值
      setDefaultValue (v) {
        if (this.multiple) { // 多选
          // this.$nextTick(() => {
          this.$refs.tree.setCheckedKeys(v, this.onlyLeaf)
          this.handCheckSetValue(v, null)
        
        } else { // 其他
          this.$refs.tree.setCurrentKey(v)
          this.handleNodeSetValue()
        }
      },
      // 单选点击事件
      handleNodeClick (data) {
        if (this.multiple) return
        // 隐藏下拉框的效果
        this.$refs.selectUpResId.blur()
        this.$emit('input', this.handleNodeSetValue())
        // console.log('handleNodeClick', this.value)
      },
      handleNodeSetValue () {
        var cNode = this.$refs.tree.getCurrentNode()
        this.selectData = cNode ? [cNode] : []
        // 设置值
        var _value = cNode ? cNode[this.dynaDefProps['id']] : null
        this.selectId = _value
        return this.selectId
      },

      
      // 多选选择勾选
      handCheck (currentNode, checkedNodesState) {
        // console.log('handCheck', checkedNodesState)
        if (!this.multiple) return
        this.$emit('input', this.handCheckSetValue())
        // console.log('multiple handleNodeClick', this.value)
      },
      /**
       * 特殊处理下多选值处理,解决父子节点强关联的时候用
       * checkedKeys选中的keys
       * checkedNodes 选中的nodes
       *  
       */
      handCheckSetValue (checkedKeys, checkedNodes) {
        // console.log('handCheckSetValue')
        this.selectData = this.$refs.tree.getCheckedNodes(this.onlyLeaf, false)
        this.selectId = checkedKeys || this.$refs.tree.getCheckedKeys(this.onlyLeaf)

        return this.selectId
      },


      // 清空触发
      handleClear () {
        this.$emit('input', this.multiple ? [] : null)
      },
      // 多选模式移除tag
      handleRemoveTag (v) {
        // console.log('handlRemoveTag formData.id', this.selectId, v)
        this.$emit('input', this.selectId)
      },
      // select搜索调用tree过滤
      handleFilterMethod (query) {
        this.$refs.tree.filter(query)
      },
      // tree 过滤
      handleFilterNode (value, data) {
        if (!value) return true
        var _label =  this.getLable(data);
        return _label.toLowerCase().indexOf(value.toLowerCase()) !== -1
      },
      // select 失去焦点并重置过滤器
      selectVisibleChange (v) {
        if (v || !this.filterable ) return
        this.handleFilterMethod(null)
        // console.log('selectVisibleChange', v, this.filterable)
      }
    }

  }
</script>

tips:
> selectVisibleChange方法中 this.selectVisibleChange 改为
> this.$refs.selectUpResId.selectVisibleChange

新版调用方式:

**注意:支持使用el-select 和el-tree的属性,外加自定的props属性**
<el-select-tree :defaultProps= "{
                  children: 'children',
                  label: 'name',
                  id: 'id'
    }" v-model="test" multiple collapse-tags clearable :data="treeList" placeholder="xxxxxxx"></el-select-tree>
  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值