vue中el-select 和el-tree二次封装实现

 更新时间:2024年11月25日 08:35:57   作者:程序猴老王  
本文介绍了vue中el-select 和el-tree二次封装实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

前言

本文章是本人在开发过程中,遇到使用树形数据,动态单选或多选的需求,element中没有这种组件,故自己封装一个,欢迎多多指教

开发环境:element-UI、vue2

组件效果

单选

多选

组件引用

<treeselect v-model="form.parentId"
                      :options="deptOptions"
                      :props="{value:'id',label:'name',children: 'children'}"
                      :placeholder="'选择上级部门'"
          />

组件代码

<template>
  <div>
    <el-select
      ref="treeSelect"
      popper-class="custom-select-popper"
      style="width: 100%"
      v-model="valueLabel"
      :clearable="clearable"
      :placeholder="placeholder"
      :multiple="multiple"
      @clear="handleClear"
      @remove-tag="handleRemoveTag"
    >
      <el-input v-if="filter"
                v-model="filterText"
                :placeholder="filterPlaceholder" style="margin-top: -6px;"
      />
      <el-option :value="value" :label="option.name" class="select-options">
        <el-tree
          id="tree-option"
          ref="treeSelectTree"
          :accordion="accordion"
          :data="options"
          :props="props"
          :node-key="props.value"
          :highlight-current="!multiple"
          :show-checkbox="multiple"
          :check-strictly="checkStrictly"
          :default-expand-all="expandAll"
          :expand-on-click-node="multiple"
          :filter-node-method="filterNode"
          @node-click="handleNodeClick"
          @check="handleNodeCheckbox"
        >
          <span slot-scope="{ node, data }" class="tree_label">
                {{ node.label }}
              </span>
        </el-tree>
      </el-option>
    </el-select>
  </div>
</template>
<script>
export default {
  name: 'TreeSelect',
  model: {
    prop: 'value',
    event: 'change'
  },
  props: {
    value: {
      type: [String, Number, Object, Array],
      default: () => {
        return ''
      }
    },
    clearable: {
      type: Boolean,
      default: true
    },
    placeholder: {
      type: String,
      default: '请选择'
    },
    multipleLimit: {
      type: Number,
      default: 2
    },
    //--------- filter props -----
    filter: {
      type: Boolean,
      default: true
    },
    filterPlaceholder: {
      type: String,
      default: '请输入关键字'
    },
    //----- tree props -----
    accordion: {
      type: Boolean,
      default: false
    },
    options: {
      type: Array,
      default: () => {
        return []
      }
    },
    props: {
      type: Object,
      default: () => {
        return {
          value: 'id',
          label: 'label',
          children: 'children'
        }
      }
    },
    expandAll: {
      type: Boolean,
      default: false
    },
    checkStrictly: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      tp: {
        value: 'id',
        label: 'label',
        children: 'children',
        prentId: 'parentId'
      },
      multiple: false,
      valueLabel: [],
      option: {
        id: '',
        name: ''
      },
      filterText: undefined,
      valueId: [],
      treeIds: []
    }
  },
  watch: {
    valueId() {
      if (this.multiple) {
        let valueStr = ''
        if (this.value instanceof Array) {
          valueStr = this.value.join()
        } else {
          valueStr = '' + this.value
        }
        if (valueStr !== this.valueId.join()) {
          this.$emit('change', this.valueId)
        }
      } else {
        let id = this.valueId.length > 0 ? this.valueId[0] : undefined
        if (id !== this.value) {
          this.$emit('change', id)
        }
      }
    },
    value: {
      handler(newVal, oldVal) {
        if (newVal !== oldVal) {
          this.init()
        }
      }
    },
    filterText: {
      handler(newVal, oldVal) {
        if (newVal !== oldVal) {
          this.$refs.treeSelectTree.filter(newVal)
        }
      }
    }
  },
  mounted() {
    for (let key in this.tp) {
      if (this.props[key] !== undefined) {
        this.tp[key] = this.props[key]
      }
    }
    this.multiple = this.multipleLimit > 1
    this.init()
    this.$nextTick(() => {
      if (this.multiple) {
        document.getElementsByClassName('el-select__tags')[0].style.maxHeight = document.getElementsByClassName('el-select')[0].offsetHeight * 2 - 4 + 'px'
      }
    })

  },
  methods: {
    init() {
      if (this.value instanceof Array) {
        this.valueId = this.value
      } else if (this.value === undefined) {
        this.valueId = []
      } else {
        this.valueId = [this.value]
      }
      if (this.multiple) {
        for (let id of this.valueId) {
          this.$refs.treeSelectTree.setChecked(id, true, false)
        }
      } else {
        this.$refs.treeSelectTree.setCurrentKey(this.valueId.length > 0 ? this.valueId[0] : undefined)
      }
      this.initValueLabel()
      this.initTreeIds()
      this.initScroll()
    },
    // 初始化滚动条
    initScroll() {
      this.$nextTick(() => {
        let scrollWrap = document.querySelectorAll('.el-scrollbar .el-select-dropdown__wrap')[0]
        scrollWrap.style.cssText = 'margin: 0px; max-height: none; overflow: hidden;'
        let scrollBar = document.querySelectorAll('.el-scrollbar .el-scrollbar__bar')
        scrollBar.forEach((ele) => (ele.style.width = 0))
      })
    },
    initTreeIds() {
      let treeIds = []
      let _this = this

      function traverse(nodes) {
        for (let node of nodes) {
          treeIds.push(node[_this.tp.value])
          if (node[_this.tp.children]) {
            traverse(node[_this.tp.children])
          }
        }
      }

      traverse(this.options)
      this.treeIds = treeIds
    },
    initValueLabel() {
      let labels = []
      let _this = this
      for (let id of this.valueId) {
        let node = this.traverse(this.options, node => node[_this.tp.value] === id)
        if (node) {
          labels.push(node[_this.tp.label])
        }
      }
      if (this.multiple) {
        this.valueLabel = labels
        this.option.name = labels.join()
      } else {
        this.valueLabel = labels.length > 0 ? labels[0] : undefined
        this.option.name = this.valueLabel
      }
    },
    traverse(tree, func) {
      for (let node of tree) {
        if (func(node)) {
          return node
        }
        if (node[this.tp.children]) {
          let result = this.traverse(node[this.tp.children], func)
          if (result !== undefined) {
            return result
          }
        }
      }
      return undefined
    },
    handleClear() {
      this.valueLabel = []
      this.valueId = []
      if (this.multiple) {
        for (let id of this.treeIds) {
          this.$refs.treeSelectTree.setChecked(id, false, false)
        }
      } else {
        this.$refs.treeSelectTree.setCurrentKey(null)
      }
    },
    /* 树filter方法 */
    filterNode(value, data) {
      if (!value) return true
      return data[this.props.label].indexOf(value) !== -1
    },
    /* 树节点点击事件 */
    handleNodeClick(data, node) {
      if (!this.multiple) {
        this.filterText = ''
        this.valueId = [data[this.tp.value]]
      }
      if(node.childNodes){
        node.expanded = true
      }
    },
    handleNodeCheckbox(data, node) {
      if (this.multiple) {
        if (this.multipleLimit >= node.checkedKeys.length) {
          this.valueId = node.checkedKeys
        } else {
          this.$refs.treeSelectTree.setChecked(data, false, !this.checkStrictly)
          this.$message.error('最多选择' + this.multipleLimit + '项')
        }
      }
    },
    handleRemoveTag(tag) {
      let n = this.traverse(this.options, node => node[this.tp.label] === tag)
      if (n) {
        this.$refs.treeSelectTree.setChecked(n[this.tp.value], false, !this.checkStrictly)
      }
      this.valueId = this.$refs.treeSelectTree.getCheckedKeys()
    }
  }
}

</script>

<style scoped lang="scss">
::v-deep .el-select__tags {
  overflow: auto;
}
.custom-select-popper{

}

.el-scrollbar {
  .el-scrollbar__view {
    .el-select-dropdown__item {
      height: auto;
      max-height: 300px;
      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;
    }
    .is-current > .el-tree-node__label{
      color: #409eff;
      font-weight: 700;
    }
  }
}


.tree_label {
  line-height: 23px;

  .label_index {
    background-color: rgb(0, 175, 255);
    width: 22px;
    height: 22px;
    display: inline-flex;
    border-radius: 4px;

    .label_index_font {
      color: #ffffff;
      width: 100%;
      text-align: center;
    }
  }
}
</style>

到此这篇关于vue中el-select 和el-tree二次封装实现的文章就介绍到这了,更多相关vue el-select 和el-tree封装内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

相关文章

  • Element通过v-for循环渲染的form表单校验的实现

    Element通过v-for循环渲染的form表单校验的实现

    日常业务开发中,form表单校验是一个很常见的问题,本文主要介绍了Element通过v-for循环渲染的form表单校验的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • vue中的 $slot 获取插槽的节点实例

    vue中的 $slot 获取插槽的节点实例

    今天小编就为大家分享一篇vue中的 $slot 获取插槽的节点实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2019-11-11
  • Vue登录功能的实现流程详解

    Vue登录功能的实现流程详解

    本文主要介绍了Vue实现登录功能全套详解(含封装axios),文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • Vue前端登录token信息验证功能实现

    Vue前端登录token信息验证功能实现

    最近公司新启动了个项目,用的是vue框架在做,下面这篇文章主要给大家介绍了关于vue实现token登录验证的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-12-12
  • vue3+vite兼容低版本的白屏问题详解(安卓7/ios11)

    vue3+vite兼容低版本的白屏问题详解(安卓7/ios11)

    这篇文章主要给大家介绍了关于vue3+vite兼容低版本的白屏问题的相关资料,还给大家介绍了vue打包项目以后白屏和图片加载不出来问题的解决方法,需要的朋友可以参考下
    2022-12-12
  • vue-quill-editor如何设置字体大小

    vue-quill-editor如何设置字体大小

    这篇文章主要介绍了vue-quill-editor如何设置字体大小,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-04-04
  • vue3如何实现锚点定位点击滚动高亮

    vue3如何实现锚点定位点击滚动高亮

    这篇文章主要介绍了vue3如何实现锚点定位点击滚动高亮问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-04-04
  • vant之van-list的使用及踩坑记录

    vant之van-list的使用及踩坑记录

    这篇文章主要介绍了vant之van-list的使用及踩坑记录,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-04-04
  • vue-router重写push方法,解决相同路径跳转报错问题

    vue-router重写push方法,解决相同路径跳转报错问题

    这篇文章主要介绍了vue-router重写push方法,解决相同路径跳转报错问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08
  • Vue学习-VueRouter路由基础

    Vue学习-VueRouter路由基础

    这篇文章主要介绍了Vue学习-VueRouter路由基础,路由本质上就是超链接,xiamian 文章围绕VueRouter路由的相关资料展开详细内容,需要的小伙伴可以参考一下,希望对你的学习有所帮助
    2021-12-12

最新评论