文章只记录部分问题,完整的 组件属性 及 组件的事件 等 可到文档中去查阅
官方文档:Vue-Treeselect
中文的这里有: Vue-Treeselect | Vue-Treeselect 中文网
import Treeselect from '@riophae/vue-treeselect' // 导入vue-treeselect
import '@riophae/vue-treeselect/dist/vue-treeselect.css' // 导入样式
Vue.component('Treeselect', Treeselect); // 注册组件
<treeselect
:multiple="true"
:options="options"
placeholder="Select your favourite(s)..."
v-model="value"
>treeselect>
绑定的值设置为 null 或 undefined
提供另外一个思路,使用计算属性 computed
组件绑定的值 是 通过计算属性,处理后的值(对值为"" 时,进行处理,返回null或undefined)
<treeselect
:multiple="true"
:options="options"
placeholder="Select your favourite(s)..."
v-model="value"
noChildrenText="没有数据了"
noOptionsText="没有数据"
noResultsText="没有搜索结果"
>treeselect>
对chlidren源数据进行控制
normalizer: (node) => {
return{
children: node.children && node.children.length > 0 ? node.children: 0,
}
},
因为当时使用时,项目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;
}
}
使用插槽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>
直接使用下方代码(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;
}
在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]
}