1.开发需求
在各大UI框架的select选择器中,在搜索时都是输入连续的搜索内容,比如“app-store”选项,你要输入“app-xxx”,才能匹配这个选择,要是想输入“a-s”这种不连续的匹配方式,就实现不了,用户体验较差,所以就开发了一个不连续搜索的select选择器,并带有输入内容的高亮提示。
2.实现演示
下面是我完成后的演示,请看
3.主要难点
1.如何不连续匹配选项
这里我们借用了一下第三方的api库——sdm2
2.匹配的代码逻辑
我这里做成了通用组件的模式:
点个赞呗~
4.代码
子组件代码
<template><!-- 非连续搜索下拉组件 --><el-select v-model="value" :clearable="clearable" filterable placeholder="请选择" :filter-method="(q) => (query = q)"@change="selectChange"><el-option v-for="item in filteredOptions" :key="item.value" :label="item.label" :value="item.value"><div v-if="props.isHigh" v-html="item.highlight"></div></el-option></el-select>
</template><script setup lang="ts">
import { ref, computed } from "vue";
import { match, filterMap } from "sdm2";const props = defineProps({selectValue: { type: String, default: '' }, //选中值clearable: { type: Boolean, default: true }, //是否可清除options: { type: Array<any>, default: [] },//选项isHigh: { type: Boolean, default: false }, //是否高亮highlightColor: { type: String, default: 'red' },//高亮颜色
});
const query = ref("");
const value = ref(props.selectValue);
const emits = defineEmits(["update:selectValue"]);// 格式化选项
const filteredOptions = computed(() => {if (!props.isHigh) {let optionsList = props.options.filter(({ label }) =>// 使用sdm2的match函数筛选match(label, query.value, {// 忽略大小写匹配ignoreCase: true,}))return optionsList;} else {let optionsList = filterMap(props.options, query.value, {// 忽略大小写匹配ignoreCase: true,// 把matchStr返回的字符串作为被匹配项matchStr: ({ label }) => label,// 匹配到后转换为html高亮字符串onMatched: (matchedStr) =>`<span style="color:${props.highlightColor}" class="highlight">${matchedStr}</span>`,// 将匹配到的项转换为所需要的格式,str为onMatched转换后的字符串,origin为原始项onMap: ({ str, origin }) => {return {highlight: str,...origin,};},})return optionsList;}
}
);
// 选中值
function selectChange() {emits("update:selectValue", value.value);
}
</script>
<style lang="less" scoped></style>
父组件代码:
<template><!-- 非连续下拉搜索框 --><div class="discontinuous-select"><span>高亮颜色 </span><el-color-picker v-model="highlightColor" /><p></p><span>是否高亮 </span><el-radio-group v-model="isHigh"><el-radio :label="true">是</el-radio><el-radio :label="false">否</el-radio></el-radio-group><p></p><span>组件模式 </span><DisSelect v-model:selectValue="selectValue" :options="options" :isHigh="isHigh" :highlightColor="highlightColor"></DisSelect></div>
</template><script setup lang="ts">
import { ref, computed, watch } from "vue";
import DisSelect from './components/DisSelect.vue'
const options = ref([{label: "Apple",value: "apple",},{label: "Banana",value: "banana",},{label: "Fig",value: "fig",},{label: "Grape",value: "grape",},{label: "Lemon",value: "lemon",},{label: "Mango",value: "mango",},{label: "Orange",value: "orange",},{label: "Pineapple",value: "pineapple",},
]);// 组件模式
const selectValue = ref("");
// 高亮颜色
const highlightColor = ref('#FF0000')
// 是否高亮
const isHigh = ref(true);
watch((selectValue), (newVal, oldVal) =>console.log("selectValue", newVal)
);
</script>
<style lang="less" scoped>
.discontinuous-select {height: 100%;width: 100%;text-align: center;
}
</style>
点个赞呗~