CustomSelect.vue 3.4 KB
<script lang="ts">
import { defineComponent, ref, toRef, watch, PropType, onMounted } from "vue";
import { Icon } from "vant";

interface Option {
  label: string;
  value: string | number;
}

export default defineComponent({
  name: "CustomSelect",
  props: {
    value: {
      type: [String, Number] as PropType<string | number>,
      default: "",
    },
    options: {
      type: Array as PropType<Option[]>,
      required: true,
    },
    activeColor: {
      type: String,
      default: "#FF7676",
    },
  },
  components: {
    Icon,
  },
  setup(props, { emit }) {
    const dropdownOpen = ref(false);
    const selectedLabel = ref("");
    const selectedValue = ref<string | number>(props.value);
    const options = toRef(props, "options");
    const activeColor = toRef(props, "activeColor");

    // watch 监听 value 的变化,保持组件的响应式
    watch(
      () => props.value,
      (newValue) => {
        selectedValue.value = newValue;
      }
    );

    const toggleDropdown = () => {
      dropdownOpen.value = !dropdownOpen.value;
    };

    const selectOption = (option: Option) => {
      selectedLabel.value = option.label;
      selectedValue.value = option.value;
      dropdownOpen.value = false;
      emit("update:value", selectedValue.value); // 双向绑定
      emit("change", selectedValue.value); // 提供额外的 change 事件
    };

    const initData = () => {
      if (props.value) {
        const defaultOption = options.value.find(
          (option) => option.value === props.value
        );
        if (defaultOption) {
          selectedLabel.value = defaultOption.label;
          selectedValue.value = defaultOption.value;
        }
      }
    };

    onMounted(() => {
      initData();
    });

    return {
      options,
      activeColor,
      dropdownOpen,
      selectedLabel,
      toggleDropdown,
      selectOption,
      selectedValue,
    };
  },
});
</script>

<template>
  <div class="custom-select">
    <div class="select-box" @click="toggleDropdown">
      <span :style="{ color: activeColor }">{{ selectedLabel }}</span>
      <Icon class="arrow" name="arrow-down" />
    </div>
    <ul v-if="dropdownOpen" class="options">
      <li
        v-for="option in options"
        :key="option.value"
        :style="{ color: selectedValue === option.value ? activeColor : '' }"
        @click="selectOption(option)"
      >
        {{ option.label }}
      </li>
    </ul>
  </div>
</template>

<style scoped>
.custom-select {
  position: relative;
  display: inline-block;
  width: 112px; /* 设置自定义下拉框的宽度 */
}

.select-box {
  height: 24px;
  border-radius: 4px;
  cursor: pointer;
  display: flex;
  padding-left: 8px;
  justify-content: space-between;
  align-items: center;
  background-color: #f5f5f5;
}

.select-box .arrow {
  padding-right: 10px;
}

.options {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  border: 1px solid #ccc;
  border-radius: 4px;
  background: white;
  list-style: none;
  padding: 0;
  margin: 0;
  max-height: 200px;
  overflow-y: auto;
  z-index: 99;
}

.options li {
  padding-left: 8px;
  height: 24px;
  cursor: pointer;
  width: 100%; /* 确保选项宽度一致 */
}

.options li:hover {
  background-color: #f2f2f2;
}
</style>