【Vue3+Element】实现选择器输入框SelectInput,可以过滤筛选项,可以直接输入搜索

问题需求

目前ElementPlus直接可以过滤的选择器,但是输入框内的文本只能是options匹配项目中的一个。
有时候用户想要自己直接输入一段文本去搜索,或者在选择一个匹配项后继续输入文本,这时element就不支持了。
这里我们实现一个既可以选择匹配项又可以在任何时候输入文本的输入框。

page-img1

page-img2

page-img3

Element的选择器(筛选选项): https://element-plus.org/zh-CN/component/select.html#%E7%AD%9B%E9%80%89%E9%80%89%E9%A1%B9

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<template>
<el-select v-model="value" filterable placeholder="Select">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>

<script lang="ts" setup>
import { ref } from 'vue'

const value = ref('')
const options = [
{
value: 'Option1',
label: 'Option1',
},
{
value: 'Option2',
label: 'Option2',
},
{
value: 'Option3',
label: 'Option3',
},
{
value: 'Option4',
label: 'Option4',
},
{
value: 'Option5',
label: 'Option5',
},
]
</script>

自定义组件SearchInput

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<template>
<el-dropdown
style="width: 100%"
@command="onChangeCommand"
:teleported="false"
placement="bottom-start"
trigger="click"
>
<el-input
:model-value="props.modelValue"
:placeholder="props.placeholder"
@input="onInput"
@change="onInputChange"
clearable
/>
<template #dropdown v-if="options.length > 0">
<el-dropdown-menu>
<el-dropdown-item
v-for="item in options"
:key="item.value"
:command="item.value"
>
{{ item.value }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>

<script setup lang="ts">
import { ref } from 'vue';
// 接收父组件的传参
const props = defineProps({
modelValue: {
type: String,
},
options: { type: Array },
placeholder: { type: String },
});

// 触发父组件的方法
const emit = defineEmits(['change', 'update:modelValue']);

const options = ref(props.options as any);

const filterOptions = (val: string) => {
console.log("过滤筛选项")
options.value = props.options?.filter((item: any) => {
return item.value?.indexOf(val) !== -1;
});
};
const onChangeCommand = (val: string) => {
console.log("点击菜单项触发的事件回调")

emit('update:modelValue', val);
emit('change', val);
filterOptions(val);
};
const onInput = (val: string) => {
console.log("在 Input 值改变时触发")

emit('update:modelValue', val);
filterOptions(val);
};

const onInputChange = (val: string) => {
console.log("仅当 modelValue 改变时,当输入框失去焦点或用户按Enter时触发")
emit('change', val);
};
</script>

<style lang="scss"></style>

页面中应用SearchInput

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<template>
<div>
<el-form
ref="searchFormRef"
:model="searchForm"
label-width="110px"
label-position="left"
>
<el-form-item :label="`水果`" prop="fruit">
<SearchInput
v-model="searchForm.fruit"
:placeholder="`请输入水果`"
:options="fruitOptions?.[project.version]"
v-if="drawerVisible"
/>
</el-form-item>

<div class="button-container">
<el-button @click="submitForm(searchFormRef)"> 确定 </el-button>
</div>
</el-form>
</div>
</template>

<script setup lang="ts">
import { ref, toRefs, reactive } from 'vue';
import type { FormInstance, FormRules } from 'element-plus';

const fruitOptions = [
{ value: 'apple' },
{ value: 'banana' },
{ value: 'orange' },
{ value: 'lemon' },
];

const searchFormRef = ref<FormInstance>();
const searchForm = reactive({
deviceId: '',
fruit: '',
});

// 点击确认按钮
const submitForm = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate((valid) => {
if (valid) {
console.log('按下确认键');
}
});
};

页面最终效果图
page-gif1