在网上看到几篇例子,但或多或少都有些问题,不能满足自己的需求,只能自己封装一个。
<template>
<div>
<el-select ref="select" style="width: 100%" clearable popper-class="select-tree-popper"
v-model="selectLabel"
@clear="clearHandle"
:placeholder="placeholder">
<el-option :value="selectLabel" :label="selectLabel">
<el-input @click.stop style="width:90%; margin-left: 5%" v-model="filterText" />
<el-tree :data="treeData"
:props="defaultProps"
:node-key="nodeKey"
ref="tree"
:default-expanded-keys="[defaultExpanded]"
:filter-node-method="filterNode"
@node-click="handleNodeClick"></el-tree>
</el-option>
</el-select>
</div>
</template>
<script>
import { onMounted, ref, reactive, toRefs, watch, nextTick } from 'vue';
export default {
props: {
defaultExpanded: [String, Number],
// el-tree的默认配置项
defaultProps: {
type: Object,
required: false,
default: () => ({
children: 'children',
label: 'label'
})
},
// 树结构数据
treeData: Array,
modelValue: [String, Number],
// 树结构的唯一标识
nodeKey: {
type: String,
default: 'id'
},
placeholder: String
},
emits: ['update:modelValue'],
setup(props, { emit }) {
const select = ref(null);
const tree = ref(null);
const status = reactive({
selectId: props.modelValue,
selectLabel: '',
filterText: ''
})
onMounted(() => {
initFn()
})
// 初始化回显
const initFn = async() => {
status.selectId = props.modelValue;
await nextTick();
if (tree.value && tree.value.getNode(status.selectId)) {
status.selectLabel = tree.value.getNode(status.selectId).data[props.defaultProps.label]
}
}
// 清除选中
const clearHandle = () => {
status.selectLabel = ''
status.selectId = undefined
emit('update:modelValue', undefined)
}
const filterNode = (value, data) => {
if (!value) return true
return data[props.defaultProps.label].indexOf(value) !== -1
}
const handleNodeClick = (node) => {
status.selectLabel = node[props.defaultProps.label]
status.selectId = node[props.nodeKey]
emit('update:modelValue', status.selectId)
select.value.blur()
}
watch(() => status.filterText, (val) => {
tree.value.filter(val)
})
watch(() => props.treeData, () => {
initFn()
})
return {
...toRefs(status),
select,
tree,
handleNodeClick,
clearHandle,
filterNode
}
}
}
</script>
使用时可通过 v-model 实现双向数据绑定,传入 el-tree 官方文档要求的符合树结构的数据即可。其他需要自定义的属性,都可以自行扩充。以下是一个最简单的使用案例
<template>
<ssSelectTree v-model="id" :treeData="treeData"></ssSelectTree>
</template>
<script>
import selectTree from 'select-tree';
import { ref } from 'vue';
export default {
components: { selectTree },
setup(){
const id = ref();
const treeData = ref([
{
label: 'Level one 1',
children: [
{
label: 'Level two 1-1',
children: [
{
label: 'Level three 1-1-1',
},
],
},
],
},
{
label: 'Level one 2',
children: [
{
label: 'Level two 2-1',
children: [
{
label: 'Level three 2-1-1',
},
],
},
{
label: 'Level two 2-2',
children: [
{
label: 'Level three 2-2-1',
},
],
},
],
},
{
label: 'Level one 3',
children: [
{
label: 'Level two 3-1',
children: [
{
label: 'Level three 3-1-1',
},
],
},
{
label: 'Level two 3-2',
children: [
{
label: 'Level three 3-2-1',
},
],
},
],
},
])
return {
id,
treeData
}
}
}
</script>