VUE3 个人制作选择框组件

效果如下

代码如下

<template>
  <div class="text-sm font-medium text-green-700">{{ placeholder }}</div>
  <div class="relative w-full max-w-md mx-auto">

    <button @click="toggleDropdown"
      class="w-full px-4 py-2 text-left text-green-800 bg-green-50 border-2 rounded-lg focus:outline-none transition-all duration-300 ease-in-out"
      :class="[isOpen ? 'border-green-500 ring-2 ring-green-400' : 'border-green-300', { 'text-green-400': !selectedOption }]">
      {{ selectedOption || placeholder }}
      <ChevronDownIcon
        class="absolute right-4 top-1/2 transform -translate-y-1/2 w-5 h-5 text-green-600 transition-transform duration-300"
        :class="{ 'rotate-180': isOpen }" />
    </button>
    <transition enter-active-class="transition duration-100 ease-out" enter-from-class="transform scale-95 opacity-0"
      enter-to-class="transform scale-100 opacity-100" leave-active-class="transition duration-75 ease-in"
      leave-from-class="transform scale-100 opacity-100" leave-to-class="transform scale-95 opacity-0">
      <ul v-if="isOpen"
        class="absolute z-10 w-full mt-1 bg-white border-2 border-green-300 rounded-lg shadow-lg max-h-60 overflow-auto">
        <!-- 搜索框 -->
        <li class="px-4 py-2">
          <input type="text" v-model="searchQuery" placeholder="搜索..."
            class="w-full px-2 py-1 border-2 border-green-300 rounded-lg focus:outline-none" />
        </li>

        <!-- 显示过滤后的选项 -->
        <li v-for="option in filteredOptions" :key="option.value" @click="selectOption(option)"
          class="px-4 py-2 cursor-pointer text-green-800 hover:bg-green-100 transition-colors duration-200"
          :class="{ 'bg-green-200': option.value === modelValue }">
          {{ option.label }}
        </li>
      </ul>
    </transition>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'
import { ChevronDownIcon } from 'lucide-vue-next'

const props = defineProps({
  modelValue: {
    type: [String, Number],
    default: ''
  },
  options: {
    type: Array,
    required: true
  },
  placeholder: {
    type: String,
    default: 'Select an option'
  },
  label: {
    type: String,
    default: ''
  }
})

const emit = defineEmits(['update:modelValue'])

const isOpen = ref(false)
const searchQuery = ref('')  // 搜索框的输入值

const selectedOption = computed(() => {
  const option = props.options.find(opt => opt.value === props.modelValue)
  return option ? option.label : ''
})

const toggleDropdown = () => {
  isOpen.value = !isOpen.value
}

const closeDropdown = () => {
  setTimeout(() => {
    isOpen.value = false
  }, 200)
}

const selectOption = (option) => {
  emit('update:modelValue', option.value)
  isOpen.value = false
}

// 计算过滤后的选项
const filteredOptions = computed(() => {
  if (!searchQuery.value) return props.options
  return props.options.filter(option =>
    option.label.toLowerCase().includes(searchQuery.value.toLowerCase())
  )
})
</script>

<style scoped>
/* 样式调整:使搜索框和下拉菜单美观 */
input {
  width: 100%;
  padding: 0.5rem;
  margin-bottom: 0.5rem;
  border-radius: 0.375rem;
  border: 1px solid #ddd;
  font-size: 1rem;
  color: #333;
}

input:focus {
  border-color: #4CAF50;
}
</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值