封装Select组件

113 阅读1分钟

select组件

<script setup>
import { ref, watch } from 'vue'
const props = defineProps({
  options: {
    type: Array,
    required: true
  },
  placeholder: {
    type: String,
    default: '请选择',
  },
  modelValue: {
    type: [String, Number],
    default: '',
  },
  title: {
    type: String,
    default: '名称',
  },
  borderColor: {
    type: String,
    default: '#2561ef',
  }
})
const emit = defineEmits(['update:modelValue'])
const isDropdownVisible = ref(false)
const selectedOption = ref(null)
const selectedItem = ref(props.options);
console.log(selectedItem);
const toggleDropdown = () => {
  isDropdownVisible.value = !isDropdownVisible.value
};
​
const selectOption = (option) => {
  selectedOption.value = option
  isDropdownVisible.value = true
  emit('update:modelValue', option.value)
  console.log(selectedOption, option, 22222);
}
​
watch(() => props.modelValue, (newValue) => {
  const option = props.options.find((item) => item.value === newValue);
  selectedOption.value = option || null;
})
​
</script>
<template>
  <p class="s-title">{{ props.title }}</p>
  <div class="el-select" @click="toggleDropdown">
​
    <div class="selected-item" @click="handleSelect">
      <span v-if="selectedOption
        ">{{
    selectedOption.label
  }}</span>
      <span v-else class="placeholder">{{
        placeholder
      }}</span>
      <el-icon class="el-icon-arrow-down">
        <CaretBottom />
      </el-icon>
    </div>
    <transition name="el-zoom-in-top">
      <div v-show="isDropdownVisible
          " class="dropdown">
        <ul>
          <li :class="[option == selectedOption ? 'is-active' : '']" v-for="option in options" :key="option.value
            " @click="
    selectOption(
      option
    )
    ">
            {{
              option.label
            }}
          </li>
        </ul>
      </div>
    </transition>
  </div>
</template>
<style
  lang="scss"
  scoped
>
.s-title {
  width: 55px;
  padding: 0 20px 0 30px;
  color: var(--cp-neutral-3);
  text-align: right;
  line-height: 34px;
}
​
.el-select {
  position: relative;
  width: 200px;
  cursor: pointer;
​
  .selected-item {
    padding: 0 15px;
    border: 1px solid #ccc;
    border-radius: 4px;
    display: flex;
    align-items: center;
    justify-content: space-between;
​
    .placeholder {
      color: var(--cp-neutral-1);
    }
​
    .el-icon-arrow-down {
      color: var(--cp-neutral-3);
    }
​
    &:hover {
      border-color: v-bind(borderColor);
    }
  }
​
  .dropdown {
    position: absolute;
    top: 46px;
    left: 0;
    width: 100%;
    background-color: #fff;
    border-top: none;
    border-radius: 0 0 4px 4px;
    max-height: 200px;
    overflow-y: auto;
    z-index: 9999;
    box-shadow: 0px 5px 15px rgba(242,
        242,
        242,
        1);
  }
​
  .dropdown ul {
    padding: 0;
    margin: 0;
    list-style: none;
  }
​
  .dropdown li {
    padding: 2px 10px;
    cursor: pointer;
  }
​
  .dropdown li:hover {
    background-color: #2661ef14;
  }
​
  .is-active {
    color: #2362e9;
  }
}
</style>

成品

image.png