用vue实现el-select+el-tree下拉树形选择

主要功能: 多选,移除标签,清空标签,模糊搜索,禁用,全选,清空,反选。
实现效果在这里插入图片描述

1.父组件

HTML

 <PropertyTreeSelect
     :options="options"   // 树形数据
     :defaultProps="defaultProps" // 树形数据格式
     :defaultValue="defaultValue"  // 回显的值
     @changeSelectDataList="changeSelectDataList">  // 父组件获取子组件选中的值
  </PropertyTreeSelect>

js

data () {
	return {
		options: [],  // 级联选择器数据
	     // 数据格式化
	     defaultProps:{
			children: 'childCategory',
			label: 'text',
        	value: 'categoryId'
		 },
		 defaultValue: [],
	}
},
methods: {
	changeSelectDataList (val) {
      this.list = val.map(v => v.categoryId)
    },
}
1.子组件

完整代码,直接复用即可,参数名称在最下方表格里面

<template>
  <div>
    <el-select v-model="selectValue" multiple :placeholder="placeholder" :filterable="filterable" :filter-method="dataFilter" :popper-append-to-body="appendToBody"
      @remove-tag="removeTag" @clear="clearAll" :clearable="isCanDelete" style="width:100%" :disabled="disabledSelect">
      <el-option :value="selectTree" v-loading="treeLoading" element-loading-background="rgba(255, 255, 255, 0.5)"
        element-loading-text="loading" class="option-style" disabled>
        <div class="check-box">
          <el-button type="text"  @click="handlecheckAll">全选</el-button>
          <el-button type="text"  @click="handleReset">清空</el-button>
          <el-button type="text"  @click="handleReverseCheck">反选</el-button>
        </div>
        <el-tree
          :data="options"
          :props="defaultProps"
          class="tree-style"
          ref="treeNode"
          show-checkbox
          :node-key="defaultProps.value"
          :filter-node-method="filterNode"
          :default-checked-keys="defaultValue"
          :check-strictly="checkStrictly"
          @check-change="handleNodeChange">
        </el-tree>
      </el-option>
  </el-select>
  </div>
</template>

<script>
export default {
  name: 'TreeSelect',
  props: {
    //编辑时回显的数组
    defaultValue: {
      type: Array,
      default: () => ([])
    },
    //可用选项的数组
    options: {
      type: Array,
      default: () => ([])
    },
    // 配置选项
    defaultProps: {
      type: Object,
      default: () => ({ // 属性值为后端返回的对应的字段名
        children: 'children',
		label: 'label',
        value: 'value'
      })
    },
    // 是否将组件添加到body上面(组件在弹窗或者表格里面时可设为true)
    appendToBody: {
      type: Boolean,
      default: false
    },
    // 是否可搜索
    filterable: {
      type: Boolean,
      default: true // 不可以搜索
    },
    // 是否禁用下拉框
    disabledSelect: {
      type: Boolean,
      default: false
    },
    // 父子不互相关联
    checkStrictly: {
      type: Boolean,
      default: false // 关联
    },
    // 父类id字段名 (如果父子联动则必传该字段,不联动则不用传)
    parentValue: {
      type: String,
      default: 'parentValue'
    },
    // 回显的值是否可被删除 true: 可以删除;false:不能删除
    isCanDelete: {
      type: Boolean,
      default: true
    },
    placeholder: {
      type: String,
      default: '请选择'
    },
    // 不可删除报错
    errMessage: {
      type: String,
      default: '该选项不可被删除'
    }
  },
  data () {
    return {
      selectTree: [], // 绑定el-option的值
      selectValue: [], // 文本框中的标签
      VALUE_NAME: this.defaultProps.value, // value转换后的字段
      VALUE_TEXT: this.defaultProps.label, // label转换后的字段
      treeLoading: false // 加载loading~
    }
  },
  watch: {
    // 监听回显的数据
    defaultValue (val) {
      if (val.length) {
        this.$nextTick(() => {
          let  datalist = this.$refs.treeNode.getCheckedNodes()
          if (!this.checkStrictly) {
          const parentList = datalist.filter(v => v[this.defaultProps.children]).map(v => v[this.VALUE_NAME])
          datalist = datalist.filter(v => parentList.indexOf(v[this.parentValue]) === -1)
        }
          this.selectTree = datalist
          this.selectValue = datalist.map(v => v[this.VALUE_TEXT])
        })
      }
    }
  },
  methods: {
    // 全选
    handlecheckAll () {
      this.treeLoading = true
      setTimeout(() => {
        this.$refs.treeNode.setCheckedNodes(this.options)
        this.treeLoading = false
      }, 200)
    },
    // 清空
    handleReset () {
      if (this.isCanDelete) {
        this.treeLoading = true
        setTimeout(() => {
          this.$refs.treeNode.setCheckedNodes([])
          this.treeLoading = false
        }, 200)
      } else {
        this.$message.error(this.errMessage)
      }
    },
    /**
     * @description: 反选处理方法
     * @param {*} nodes 整个tree的数据
     * @param {*} refs  this.$refs.treeNode
     * @param {*} flag  选中状态
     * @param {*} seleteds 当前选中的节点
     * @return {*}
     */
    batchSelect(nodes, refs, flag, seleteds) {
      if (Array.isArray(nodes)) {
        nodes.forEach(element => {
          refs.setChecked(element, flag, true)
        })
      }
      if (Array.isArray(seleteds)) {
        seleteds.forEach(node => {
          refs.setChecked(node, !flag, true)
        })
      }
    },
    // 反选
    handleReverseCheck() {
      if (this.isCanDelete) {
        this.treeLoading = true
        setTimeout(() => {
          let res = this.$refs.treeNode
          let nodes = res.getCheckedNodes(true, true)
          this.batchSelect(this.options, res, true, nodes)
          this.treeLoading = false
        }, 200)
      } else {
        this.$message.error(this.errMessage)
      }
    },
    // 输入框关键字
    dataFilter (val) {
      this.$refs.treeNode.filter(val)
    },
    /**
     * @description: tree搜索过滤
     * @param {*} value 搜索的关键字
     * @param {*} data  筛选到的节点
     * @return {*}
     */
    filterNode (value, data) {
      if (!value) return true
      return data[this.defaultProps.label].toLowerCase().indexOf(value.toLowerCase()) !== -1
    },
    /**
     * @description: 勾选树形选项
     * @param {*} data 该节点所对应的对象
     * @param {*} self 节点本身是否被选中
     * @param {*} child 节点的子树中是否有被选中的节点
     * @return {*}
     */
    handleNodeChange(data,self,child) {
        const flag = this.defaultValue.some(v => v === data[this.VALUE_NAME])
		let  datalist = this.$refs.treeNode.getCheckedNodes()
        if (!self && !this.isCanDelete && flag) {
          this.$message.error(this.errMessage)
          this.$refs.treeNode.setChecked(data,true, true)
        }
        if (!this.checkStrictly) { // 如果联动则需处理父子值关系
          const parentList = datalist.filter(v => v[this.defaultProps.children]).map(v => v[this.VALUE_NAME])
          datalist = datalist.filter(v => parentList.indexOf(v[this.parentValue]) === -1)
        }
		this.selectTree = datalist
		this.selectValue = datalist.map(v => v[this.VALUE_TEXT])
        this.$emit('changeSelectDataList', this.selectTree)
	},
    // 移除单个标签
    removeTag(tagName){
          const flagName= this.selectTree.filter(v => v[this.VALUE_NAME] === this.defaultValue.find(item => item === v[this.VALUE_NAME])).map(v => v[this.VALUE_TEXT])
          const flag = flagName.includes(tagName)
		  if (this.isCanDelete) { // 判断回显的值是否可删除
	          this.selectTree = this.selectTree.filter(v => v[this.VALUE_TEXT] !== tagName)
	          const selectTreeValue = this.selectTree.map(v => v[this.VALUE_NAME])
	          let setlist = this.$refs.treeNode.getCheckedNodes()
	          setlist = setlist.filter(v => v[this.VALUE_NAME] === selectTreeValue.find(item => item === v[this.VALUE_NAME]))
	          this.$nextTick(() => {
	            this.$refs.treeNode.setCheckedNodes(setlist)
	          })
	          this.$emit('changeSelectDataList', this.selectTree)
        } else {
          if (!flag) {  // 判断回显时新增的是否可删除
              this.selectTree = this.selectTree.filter(v => v[this.VALUE_TEXT] !== tagName)
              const selectTreeValue = this.selectTree.map(v => v[this.VALUE_NAME])
              let setlist = this.$refs.treeNode.getCheckedNodes()
              setlist = setlist.filter(v => v[this.VALUE_NAME] === selectTreeValue.find(item => item === v[this.VALUE_NAME]))
              this.$nextTick(() => {
                this.$refs.treeNode.setCheckedNodes(setlist)
              })
              this.$emit('changeSelectDataList', this.selectTree)
          } else {
              this.selectValue = this.selectTree.map(v => v[this.VALUE_TEXT])
              this.$message.error(this.errMessage)
          }
        }
	},
    // 文本框清空
	clearAll(){
		this.selectTree = []
		this.$refs.treeNode.setCheckedNodes([])
      	this.$emit('changeSelectDataList', this.selectTree)
	}
  }

}
</script>

<style lang="scss" scoped>
.check-box{
  padding: 0 20px;
}
/deep/.el-scrollbar{
  height: 280px;
  .el-select-dropdown__wrap{
    max-height: 280px;
    overflow: hidden;
    .el-select-dropdown__list{
      padding: 0;
    }
  }
}
.option-style {
  height: 280px;
  padding: 0 0 10px 0 !important;
  margin: 0;
  overflow-y: auto;
  cursor: default !important;
}
</style>

参数名说明默认值是否必传
optionsel-tree默认数据[]
defaultProps配置选项{ children: ‘children’, label: ‘label’, value: ‘value’ } 注:如后端返回options的字段名defaultProps默认的属性不一致,需重新给defaultProps传值并将属性值改为后端返回的字段名
defaultValue回显的数组[]
appendToBody是否将组件添加到body上面(组件在弹窗或者表格里面时可设为true)false
filterable是否可搜索false
disabledSelect是否禁用下拉框false
checkStrictly父子不互相关联false (关联)
parentValue父类id字段名 (绑定的父类id)‘parentValue’checkStrictly为false则必传该字段否则不用传
isCanDelete回显的值是否可被删除 true: 可以删除;false:不能删除false
errMessage不可删除时报错文案该选项不可被删除isCanDelete为true必传
placeholder输入框占位文本请选择

以上就是封装好的下拉树形组件

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐