经过本人使用vue的treeselect组件,功能都不错,但是有俩个问题,导致我抛弃使用这组件:
1,样式问题,用了elementui的card标签后,区域限制了高度,而treeselect组件的z-index不管怎么调大,都无法浮动在card上,导致过长的list列表大部分因此被遮挡,随可滚动选择,但是很难受。
2.第二点是校验问题,也是最大原因。组件由于是install下来的,改了源码,别人拉代码后用还是要去改下源码,不符合实际。
于是,整合百度的一些代码,自己封装了SelectTree组件。
第一步:建组件文件,文件位置,components下建文件夹SelectTree,SelectTree下建文件index.vue,代码如下直接复制:
<template> <el-select v-model="valueId" :value="valueId" :clearable="clearable" @clear="clearHandle" filterable :filter-method="filterMethod" class="selectTree" ref="elSelect"> <el-option :value="valueId" :label="valueTitle" > <el-tree id="tree-option" ref="selectTree" :filter-node-method="filterNode" :accordion="accordion" :data="options" :props="props" :node-key="props.value" :default-expanded-keys="defaultExpandedKey" @node-click="handleNodeClick"> </el-tree> </el-option> </el-select> </template> <script> export default { name: "SelectTree", props:{ /* 配置项 */ props:{ type: Object, default:()=>{ return { value:'id', // ID字段名 label: 'label', // 显示名称 children: 'children' // 子级字段名 } } }, /* 选项列表数据(树形结构的对象数组) */ options:{ type: Array, default: ()=>{ return [] } }, /* 初始值 */ value:{ type: String, default: ()=>{ return null } }, /* 可清空选项 */ clearable:{ type:Boolean, default:()=>{ return true } }, /* 自动收起 */ accordion:{ type:Boolean, default:()=>{ return false } }, }, data() { return { valueId:this.value, // 初始值 valueTitle:'', defaultExpandedKey:[] } }, mounted(){ this.initHandle() }, methods: { // 选择器检索过滤方法 filterMethod(query) { // 调用树形控件的过滤 this.$refs.selectTree.filter(query); // 忽略选择器本身的过滤 return true; }, // 树节点过滤方法 filterNode(value, data) { if (!value) return true; return data.label.indexOf(value) !== -1; }, // 初始化值 initHandle(){ if(this.valueId){ this.valueTitle = this.$refs.selectTree.getNode(this.valueId).data[this.props.label] // 初始化显示 this.$refs.selectTree.setCurrentKey(this.valueId) // 设置默认选中 this.defaultExpandedKey = [this.valueId] // 设置默认展开 } this.$nextTick(()=>{ let scrollWrap = document.querySelectorAll('.el-scrollbar .el-select-dropdown__wrap')[0] let scrollBar = document.querySelectorAll('.el-scrollbar .el-scrollbar__bar') scrollWrap.style.cssText = 'margin: 0px; max-height: none; overflow: hidden;' scrollBar.forEach(ele => ele.style.width = 0) }) }, // 切换选项 handleNodeClick(node){ this.valueTitle = node[this.props.label] this.valueId = node[this.props.value] this.$emit('input',this.valueId) this.$emit('change',this.valueId) this.defaultExpandedKey = [] let scrollWrap = document.querySelectorAll('.el-select-dropdown.el-popper') scrollWrap.forEach(ele => { ele.style.display = 'none' ele.style.position = '' ele.style.top = '' ele.style.left = '' }) this.clearSelected() this.$refs.elSelect.blur() }, // 清除选中 clearHandle(){ this.valueTitle = '' this.valueId = null this.defaultExpandedKey = [] this.clearSelected() this.$emit('input',null) this.$emit('change',null) }, /* 清空选中样式 */ clearSelected(){ let allNode = document.querySelectorAll('#tree-option .el-tree-node') allNode.forEach((element)=>element.classList.remove('is-current')) } }, watch: { valueId(newValue, oldValue) { this.valueId = newValue this.$emit('input',newValue) this.$emit('change', newValue) this.initHandle() } }, }; </script> <style scoped> .el-scrollbar .el-scrollbar__view .el-select-dropdown__item{ height: auto; max-height: 274px; 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; } .el-tree >>>.is-current .el-tree-node__label{ color: #409EFF; font-weight: 700; } .el-tree >>>.is-current .el-tree-node__children .el-tree-node__label{ color:#606266; font-weight: normal; } </style>第二步:在需要用的页面,引入组件
import SelectTree from '@/components/SelectTree'第三步:使用组件:
<template> <el-form ref="dataForm" :rules="rules" :model="temp"> <el-form-item label="所属机构" prop="orgId"> <SelectTree :options="optionData" placeholder="请选择机构" v-model="temp.orgId" id="orgSelect" @input="validateField('dataForm','orgId')" /> </el-form-item> </el-form> </template> <script > export default { components: {SelectTree}, data() { const validateOrgId = (rule, value, callback) => { value = this.temp.orgId; if (!value) { document.querySelector(".selectTree").style.borderColor ='red' callback(new Error('请选择所属机构')) } else { document.querySelector(".selectTree").style.borderColor ='white' callback() } } return { temp:{ orgId:null }, rules: { orgId: [{ required: true, trigger: ['input','change'],validator:validateOrgId}], } } }, methods: { validateField(form,val){ this.$nextTick(()=>{ this.$refs[form].validateField(val); }) } } } </script >1.validateField俩个参数:第一个是form表单ref的值,第二个是prop校验的变量
2.validateField下必须用this.$nextTick,因为,数据延迟,父子数据双向绑定,等select事件后数据才是最新的
3.组件里,this.$emit特别注意,必须是input,change,页面也是trigger: ['input','change']。不知为啥,经过测试,只有这样用,数据双向绑定,页面的input事件获取的temp.orgId才是选择后的最新值,我这里没用,需要获取值的可以自行加参数看看。
4.validator:validateOrgId,巨坑。不管是自定义组件,还是install的treeselect。const validateOrgId = (rule, value, callback),value值永远是初始值,初次进入赋值是啥,后面不管怎么select选择,value不变,导致校验根本就是错误校验。所以,在函数内直接获取最新值,并替换value, value = this.temp.orgId;这里就是为啥在上面一点强调必须input,change不能少,不能换位置,数据双向绑定,this.temp.orgId才能获取最新值。