最近项目中遇到一个需求,查询条件中是树形结构,因为数据较多,需要实现多选并且可以过滤的功能
<template>
<el-select ref="select" :value="value" placeholder="请选择" clearable :disabled="disabled" :filterable="filterable"
:filter-method="filterMethod" style="width: 100%;" @clear="clear" @visible-change="visibleChange">
<el-option ref="option" class="tree-select__option" :value="optionData.id" :label="optionData.name">
<el-tree ref="tree" class="tree-select__tree" :node-key="nodeKey" :data="data" :props="props"
:show-checkbox="multiple" :filter-node-method="filterNode" @node-click="handleNodeClick"
@check-change="handleCheckChange"></el-tree>
</el-option>
</el-select>
</template>
<script>
export default {
name: 'TreeSelect',
props: {
// v-model绑定
value: {
type: [String, Number],
default: ''
},
multiple: {
type: Boolean,
default: false
},// 树形的数据
data: {
type: Array,
default: function () {
return []
}
},
// 每个树节点用来作为唯一标识的属性
nodeKey: {
type: [String, Number],
default: 'id'
},
filterable: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
// tree的props配置
props: {
type: Object,
default: function () {
return {
label: 'value',
children: 'children'
}
}
}
},
data() {
return {
optionData: {
id: '',
name: ''
},
filterFlag: false
}
},
watch: {
value: {
handler(val) {
if (!this.isEmpty(this.data)) {
this.init(val)
}
},
immediate: true
},
data: function (val) {
if (!this.isEmpty(val)) {
this.init(this.value)
}
}
},
methods: {
// 是否为空
isEmpty(val) {
for (let key in val) {
return false
}
return true
},
handleNodeClick(data) {
if (this.multiple) {
return
}
this.$emit('input', data[this.nodeKey])
this.$refs.select.visible = false
},
handleCheckChange() {
const nodes = this.$refs.tree.getCheckedNodes()
const value = nodes.map((item) => item[this.nodeKey]).join(',')
this.$emit('input', value)
},
init(val) {
// 多选
if (this.multiple) {
const arr = val.toString().split(',')
this.$nextTick(() => {
this.$refs.tree.setCheckedKeys(arr)
const nodes = this.$refs.tree.getCheckedNodes()
this.optionData.id = val
this.optionData.name = nodes
.map((item) => item[this.props.label])
.join(',')
console.log(this.optionData, '==optionData==')
})
} else {
val = val === '' ? null : val
this.$nextTick(() => {
this.$refs.tree.setCurrentKey(val)
if (val === null) {
return
}
const label = this.props.label || 'name'
const node = this.$refs.tree.getNode(val)
this.optionData.id = val
this.optionData[label] = node.label
})
}
},
visibleChange(e) {
if (e) {
const tree = this.$refs.tree
this.filterFlag && tree.filter('')
this.filterFlag = false
let selectDom = null
if (this.multiple) {
selectDom = tree.$el.querySelector('.el-tree-node.is-checked')
} else {
selectDom = tree.$el.querySelector('.is-current')
}
setTimeout(() => {
this.$refs.select.scrollToOption({ $el: selectDom })
}, 0)
}
},
clear() {
this.$emit('input', '')
},
filterMethod(val) {
this.filterFlag = true
this.$refs.tree.filter(val)
},
filterNode(value, data) {
if (!value) return true
const label = this.props.label || 'name'
return data[label].indexOf(value) !== -1
}
}
}
</script>
<style lang="scss" scoped>
.tree-select__option {
&.el-select-dropdown__item {
height: auto;
line-height: 1;
padding: 0;
background-color: #fff;
}
}
.tree-select__tree {
padding: 4px 20px;
font-weight: 400;
}
</style>
组件使用:
<select-tree v-model="corpTagCode" :data="corpOptions" :filterable="true" :multiple="true"> </select-tree>