话不多说,先上效果图:
具体功能点:
- 可输入搜索,多选。
- 回车变成tag标签。
- 点X可删除,按键盘删除键也可以删除。
- 获取焦点时候显示输入的列表,点击页面其他地方隐藏下拉列表。
输入之后点回车就变成tag并调用接口(此文还没有对接口),后续对接接口后会再发一篇文章哦! 页面用了daisyUI + tailwindCss的样式,js逻辑全部手写的。
组件全部代码如下,这里没有封装,可以自行修改一下封装哦。
<template>
<div>
<div class="relative mt-3">
<div @click.stop="" :class="isFocusing ? 'border-black' : 'border-2ec1cc'"
class="w-[300px] min-h-13 rounded-2.5 border border-solid flex items-center flex-wrap p-1.5">
<div v-for="item in selectedItem"
class="px-2.5 py-1 leading-7 bg-F7F9FA rounded-1 mb-1 flex gap-2 items-center shrink-0 bg-gray-100 mr-1.5">
<span class="text-202020 text-sm">
{{ item.title }}
</span>
<button @click.stop="unSelectItem(item)"
class="cursor-pointer text-lg border-none flex items-center justify-center">
<img src="@/assets/images/no.svg" alt="" class="w-3 h-4">
</button>
</div>
<input type="text" v-model.trim="searchVal" :placeholder="!selectedItem.length ? placeholder : ''"
class="flex-1 bg-none border-none inline-block" @focus="focusEvent" @keyup.enter="enterFn"
@keydown.delete="deleteOne">
</div>
<div v-show="isFocusing"
class="w-[300px] absolute overflow-y-auto bg-white z-999 rounded-2.5 border-[1px] flex flex-col p-2" :style="{
boxShadow: '0px 0px 10px rgba(0,0,0,0.1)',
}">
<div v-for="item in selectedItem" @click.stop="clickItem(item)"
class="w-full leading-9 cursor-pointer px-5 shrink-0 flex justify-between items-center hover:bg-gray-100 py-2 rounded-1 mb-1"
:class="item.isSelected ? 'bg-gray-100' : ''">
<span class="text-sm">
{{ item.title }}
</span>
<img src="@/assets/images/yes.svg" alt="" class="w-4 h-4" v-if="item.isSelected">
</div>
<div v-if="selectedItem.length == 0" class="text-center leading-10 text-opacity-50 text-black h-10">暂无数据
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
interface MockType {
id: number;
title: string;
isSelected: boolean;
}
const isFocusing = ref<boolean>(false);
const searchVal = ref()
const placeholder = '请输入关键字'
const selectedItem = ref<MockType[]>([]);
//点击元素
const clickItem = (label: MockType) => {
isFocusing.value = true;
label.isSelected = !label.isSelected;
const index = selectedItem.value.findIndex((item: any) => label.id === item.id);
if (index === -1) {
selectedItem.value.push(label);
} else {
selectedItem.value.splice(index, 1)
}
}
// 回车
const enterFn = (val: any) => {
if (searchVal.value) {
selectedItem.value.push({
title: searchVal.value,
id: Math.random() * 10,
isSelected: true,
})
searchVal.value = ''
// 调用接口
}
}
// 聚焦
const focusEvent = (e: any) => {
isFocusing.value = true;
}
// 按删除键
const deleteOne = () => {
if (!searchVal.value) {
selectedItem.value.pop()
}
}
// 点击x按钮删除
const unSelectItem = (label: MockType) => {
const index = selectedItem.value.findIndex((item: any) => label.id === item.id);
if (index !== -1) selectedItem.value.splice(index, 1);
}
onMounted(() => {
document.addEventListener("click", () => {//点击其他地方就隐藏列表
isFocusing.value = false;
});
});
</script>
<style scoped>
/* 隐藏输入框边框 */
input {
outline: medium;
}
input::placeholder {
font-size: 14px;
}
</style>