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>
点个赞呗~