vue3原生仿element库select多选搜索输入框

404 阅读2分钟

话不多说,先上效果图:

image.png

具体功能点:

  • 可输入搜索,多选。
  • 回车变成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>