vue-treeselect 问题合集、好用的树形下拉组件(vue-treeselect的使用、相关问题解决方案)

遇到问题,持续更新

最新更改:@input 事件里,想要拿到node参数,而不是value参数

上次更改:优化【问题五】的第二种解决方案(考虑页面上存在多个treeselect)

安装使用

vue-treeselect 安装使用

文章只记录部分问题,完整的 组件属性 及 组件的事件 等 可到文档中去查阅
官方文档:Vue-Treeselect
中文的这里有: Vue-Treeselect | Vue-Treeselect 中文网

  1. npm install --save @riophae/vue-treeselect
  2. 在main.js中
import Treeselect from '@riophae/vue-treeselect' // 导入vue-treeselect
import '@riophae/vue-treeselect/dist/vue-treeselect.css' // 导入样式

Vue.component('Treeselect', Treeselect); // 注册组件
  1. 在vue组件中进行使用
<treeselect
  :multiple="true"
  :options="options"
  placeholder="Select your favourite(s)..."
  v-model="value"
></treeselect>

相关问题解决方案

问题一:单选 绑定的值为""时,组件上会显示unkown

绑定的值设置为 null 或 undefined
提供另外一个思路,使用计算属性 computed
组件绑定的值 是 通过计算属性,处理后的值(对值为"" 时,进行处理,返回null或undefined)

问题二:数据提示设置为中文

组件默认的是英文
在这里插入图片描述
设置属性

在这里插入图片描述

<treeselect
  :multiple="true"
  :options="options"
  placeholder="Select your favourite(s)..."
  v-model="value"
  noChildrenText="没有数据了"
  noOptionsText="没有数据"
  noResultsText="没有搜索结果"
></treeselect>

问题三:节点下没有数据时(children为空时),不显示展开图标

对chlidren源数据进行控制

normalizer: (node) => {
    
    
   return{
    
    
     children: node.children && node.children.length > 0 ? node.children: 0,
   }
 },

问题四:vue-treeselect样式设置

因为当时使用时,项目elementui的控件风格用发mini,所以这里样式示例匹配的也是mini大小

// treeselect 样式设置
// search 为父元素的calss
.search {
    
    
  ::v-deep .vue-treeselect {
    
    
    width: 198px;
    height: 28px;
    line-height: 28px;
    margin-top: 7px;
    font-size: 12px;
  }
  ::v-deep .vue-treeselect__control {
    
    
    height: 28px;
  }
  ::v-deep .vue-treeselect__placeholder,
  ::v-deep .vue-treeselect__single-value {
    
    
    line-height: 28px;
  }
}

问题五:因为宽度原因导致的节点(子节点)文本显示不全

解决方案1:设置样式

使用插槽slot, 并对其设置样式(达到悬浮显示文本的效果)

<treeselect
	:multiple="true"
	:options="options"
	placeholder="Select your favourite(s)..."
	v-model="value"
	noChildrenText="没有数据了"
	noOptionsText="没有数据"
	noResultsText="没有搜索结果"
>
	<p
		 style="overflow: hidden;white-space: nowrap;text-overflow: ellipsis;width: 90%;"
		 slot="option-label"
		 slot-scope="{node}"
		 :title="node.label">
		 <template> {
   
   { node.label }}</template>
	</p>
</treeselect>

解决方案2:拖动改变宽度

新更改:对原来的解决方案进行优化。【考虑同时存在多个treeselect 情况】

直接使用下方代码(js、css),实测可用的哈,直接粘贴应该就行了
此方案有点繁杂,想了解的朋友可以看看,若有缺陷或更好的方案,欢迎大家补充
实现treeselect的右边拖动,直接改变组件宽度,使文字显示完全。动图如下:

treeselect拉伸改变宽度

【优化:找到谁处于展开状态】:现在将页面中存在的treeselect 都考虑进来,通过treeselect子级是否存在,来找到当前处于打开状态的treeselect,对其添加拉伸条,通过拉伸 拉伸条条,进而改变组件宽度

使用了MutationObserver接口对treeselect组件内的高度变化进行监听,这样能够做到:当子节点被展开时,组件内部高度增大。拖动条的高度能够跟随其变化。【保证拖动条的高度与组件内部高度一致
MutationObserver 接口提供了监视对 DOM 树所做更改的能力。

代码如下

// 本次优化只修改了openTreeSelect方法,并增加了addDragNode方法
/**
 * 首先绑定treeselect组件的open事件(菜单打开事件)
*/
openTreeSelect() {
    
    
  // 先对之前保存的旧数据进行清除
  this.parentNode = null
  this.offsetNode = null
  // 测试发现在一个treeselect已经打开的情况下,直接点击打开另一个treeselect,会出现同时存在两个 .vue-treeselect__menu 的DOM元素,
  // 即(i.childNodes[0] instanceof HTMLElement)存在两个true,则会导致拖动条永远是加在最后一个.vue-treeselect__menu DOM元素上
  // 解决一: 考虑使用setTimeout,异步执行(但经过对此测试,发现偶尔还是会出现上述情况,不能完全解决)
  // 解决二: 使用了setInterval 进行循环,对 .vue-treeselect__menu 的DOM元素数量进行判断,只有在数量为1的情况下,才进行后面的操作(调用addDragNode方法)
  let timer = setInterval(() => {
    
    
    this.$nextTick(() => {
    
    
      let offsetNodeArr = document.getElementsByClassName('vue-treeselect__menu-container');
      let num = 0;
      for(let i of offsetNodeArr) {
    
    
        if(i.childNodes.length > 0) {
    
    
          // 判断其类型是否是DOM元素
          if(!(i.childNodes[0] instanceof HTMLElement)) continue
          // console.log(i.childNodes[0] instanceof HTMLElement)
          num ++
          if(num >= 2) break
          this.offsetNode = i;
          this.parentNode = i.childNodes[0];
        }
      }
      if(num >= 2) {
    
    
        return // 存在多个 .vue-treeselect__menu DOM元素,终止函数执行
      } else {
    
    
        clearInterval(timer); // 清除timer
        this.addDragNode(); // 满足条件,调用增加拖动条的方法
      }
    })
  }, 200);
},
// 增加拖动条的方法
addDragNode() {
    
    
  // 如果当前存在创建过的拖动条,将其移除
  const dragNodes = document.getElementsByClassName('appendNode');
  if(dragNodes && dragNodes.length > 0) {
    
    
    for(let dragNode of dragNodes) {
    
    
      dragNode.remove();
    }
  }

  const dragNode = document.createElement("div"); // 创建拖动条
  dragNode.className= 'appendNode'; // 给拖动条设置样式

  const treeSelectContentNode = document.getElementsByClassName('vue-treeselect__list')[0]; // 获取并保存 .vue-treeselect__list dom元素;根据childNode的高度变化,更新 拖动条(node) 的高度
  // 如果元素都不存在
  if(!this.parentNode) return
  dragNode.style = `height: ${
      
      treeSelectContentNode.clientHeight}px`; // 获取treeSelectContentNode 的高度,并赋值给拖动条
  this.treeSelectLeft = this.offsetNode.getBoundingClientRect().left; // 保存tree-select左边到视口左边的距离  (getBoundingClientRect().left:dom的左边到视口左边的距离) 
  
  // 将拖动条添加至parentNode(.vue-treeselect__menu dom元素)下
  this.parentNode.appendChild(dragNode)


  /**
   * 设置点击、拖动事件
   */
  // 监听拖动条被左键点击按下
  dragNode.addEventListener('mousedown', (event) => {
    
    
    // 监听鼠标移动
    document.addEventListener('mousemove', this.resizeMove)
  })

  // 监听鼠标左键抬起
  document.addEventListener('mouseup',  () => {
    
    
    // 移除鼠标移动监听
    document.removeEventListener('mousemove', this.resizeMove)
  })
  
  /**
   * MutationObserver用来监视 DOM 变动 
   * 特点: 它与事件有一个本质不同。
   *       事件是同步触发,也就是说,DOM 的变动立刻会触发相应的事件
   *       Mutation Observer 是异步触发,DOM 的变动并不会马上触发,而是要等到当前所有 DOM 操作都结束才触发。
   */
  const MutationObserver = window.MutationObserver || window.webkitMutationObserver || window.MozMutationObserver;
  const observer = new MutationObserver((list) => {
    
    
    // 当被监听的元素发生变化,会执行该方法
    // 异步触发,得到最新的高度后,再对node的高度进行赋值 (保证node)
    dragNode.style = `height: ${
      
      treeSelectContentNode.clientHeight}px`
  })

  // 监听 treeSelectContentNode 的变化(treeselect内部变化)
  observer.observe(treeSelectContentNode, {
    
     attributes: true, subtree: true }) // attributes: 监听属性变化  subtree:监听子元素变化

  /**
   * disconnect() 方法告诉观察者停止观察变动
   * observer.disconnect() 无参数
   * 说明:如果被观察的元素被从 DOM 中移除,然后被浏览器的垃圾回收机制释放,此 MutationObserver 将同样被删除。
   */
},

// 拖动方法
resizeMove(event) {
    
    
  if(event.clientX - this.treeSelectLeft > this.offsetNode.clientWidth) {
    
    
    this.parentNode.style = `width: ${
      
      event.clientX - this.treeSelectLeft}px; max-height: 300px`
  }
},
 
/**
 1. 绑定treeselect组件的close事件(菜单关闭事件)
*/
closeTreeSelect() {
    
    
  // treeselect 关闭时, 将创建的拖动条进行移除
  const dragNodes = document.getElementsByClassName('appendNode');
  if(dragNodes && dragNodes.length > 0) {
    
    
    for(let dragNode of dragNodes) {
    
    
      dragNode.remove();
    }
  }
  this.parentNode = null;
  this.offsetNode = null;
}
::v-deep .appendNode {
    
    
  position: absolute;
  top: 0;
  right: 0px;
  height: 300px;
  width: 2px;
  transition: all linear 200ms;
}
::v-deep .appendNode:hover {
    
    
  background-color: #999;
  cursor: w-resize;
}

问题六:@input 事件里设置参数为node,而不是value

在treeselect事件中,我们来看看默认的input事件和select事件:
在这里插入图片描述在这里插入图片描述

可以看到,这里input事件传递的参数为value,在大多时候,我们都会将treeselect的value绑定为每一项的id。但在某些特定的时候,我们既需要使用input事件,又想事件传递的参数是整个node,就像select事件传递的node 参数一样那么这个时候就需要我们进行一些额外的设置
先看两个图:
在这里插入图片描述
在这里插入图片描述
value:当multiple="false"时,value对应的是id或node对象,当multiple="true"时,value对应的是id或nodeobject的数组。其格式取决于valueFormat属性。
valueFormat:能够决定value属性的格式。当设置为"id"时,value属性的格式就是 id 或 id数组。当设置为"object"时,value属性的格式就是 node 或 node数组。
所以,如果我们想在input事件中,拿到整个node对象,可以使用

<treeselect
	v-model="name"
	:options="nameList"
	valueFormat="object"
	:multiple="true"
    :flat="true"
	:normalizer="normalizer"
	@input="valueChange"
/>
valueChange(nodeArr) {
    
    
	console.log(nodeArr); // [node,node,node]
}

如有其他问题或其他解决方案,欢迎评论、欢迎补充

猜你喜欢

转载自blog.csdn.net/IT_dabai/article/details/127086572
今日推荐