前些天实现了一个树结构的下拉组件,可最近又让我支持下拉搜索功能,查了一圈没有找到合适的,只好今天自己写了一下,凑合能用吧,支持搜索功能了最起码,可有些参数还是要配置的
```
<template>
<div>
<el-popover
v-model:visible="popVisible"
placement="bottom"
:width="popoverWidth"
trigger="click"
>
<template #reference>
<el-input
v-model="filterText"
v-bind="$attrs"
:placeholder="placeholder"
@blur="handleBlur"
@focus="handleFocus"
@input="handleFilter"
>
<template #suffix>
<div class="suffix" @click="handleIcon">
<i :class="`el-icon-arrow-${popVisible ? 'up' : 'down'}`" />
</div>
</template>
</el-input>
</template>
<el-tree
ref="tree"
class="filter-tree"
:data="options"
:props="defaultProps"
default-expand-all
:filter-node-method="filterNode"
@node-click="handleNodeClick"
/>
</el-popover>
</div>
</template>
<script>
import { defineComponent, watch, onMounted, ref, computed } from "vue";
export default defineComponent({
props: {
popoverWidth: {
type: Number,
default: 400,
},
modelValue: {
type: String,
default: "",
},
options: {
type: Array,
default: () => [],
},
},
emits: ["update:modelValue", "selected"],
setup(props, context) {
const defaultProps = ref({
children: "children",
label: "label",
});
const popVisible = ref(false);
function handleIcon() {
popVisible.value = !popVisible.value;
}
const preText = ref("");
const placeholder = computed(() =>
preText.value ? preText.value : "select"
);
function filterNode(value, data) {
if (!value) return true;
return data.label.indexOf(value) !== -1;
}
function findLabel(arr, target) {
let res = target;
function find(arr) {
for (let i = 0; i < arr.length; i += 1) {
if (arr[i].value === target) {
res = arr[i].label;
return;
}
if (arr[i].children && arr[i].children.length) {
find(arr[i].children, target);
}
}
}
find(arr);
return res;
}
const filterText = ref("");
function handleNodeClick(node) {
popVisible.value = false;
filterText.value = node.label;
preText.value = node.label;
context.emit("selected", node);
context.emit("update:modelValue", node.value);
}
function handleBlur() {
setTimeout(() => {
filterText.value = preText.value;
}, 100);
}
function handleFocus() {
preText.value = filterText.value;
filterText.value = "";
}
watch(
() => props.modelValue,
() => {
filterText.value = findLabel(props.options, props.modelValue);
}
);
const tree = ref();
function handleFilter(val){
tree.value.filter(val);
}
onMounted(() => {
filterText.value = findLabel(props.options, props.modelValue);
});
return {
tree,
handleFilter,
placeholder,
defaultProps,
filterText,
popVisible,
preText,
handleNodeClick,
handleBlur,
handleFocus,
filterNode,
handleIcon,
};
},
});
</script>
<style scoped>
.suffix {
cursor: pointer;
display: flex;
width: 100%;
height: 100%;
align-items: center;
}
</style>
```
使用起来比之前那一篇麻烦了一丢丢,相对还是好用的
且看,
```
<treeSelect
style="width: 226px"
:popoverWidth="200"
v-model="selectData"
:options="options"
@selected="handleSelect"
></treeSelect>
```
在父组件里我们组要把对应要修改的value传进去,options对应树结构的array模样,不过一定要有value的,或者读者可以自己再稍微改改,emit出来的参数。
关于el-popover的宽度和高度都没有做设置,不适合过多的数据,需要的话就自己加个div限制一下,或者需要的人多的话,我再把它优化下。。
```
const selectData = ref("");
const options = ref([
{
label: "选项1",
value: "1",
children: [{ label: "选项1-1", value: "1-1" }],
},
{ label: "选项2", value: "2" },
]);
function handleSelect(node) {
console.log(node);
}
```
有哪里需要优化的提一下,持续改进哈。