AdaptiveSelect.vue 4.03 KB
<template>
  <div class="adaptive-select-container">
    <el-select
        v-model="selectedValue"
        :placeholder="placeholder"
        :popper-class="popperClass"
        :style="selectStyle"
        clearable
        filterable
        @change="handleChange"
        @visible-change="handleVisibleChange"
    >
      <el-option
          v-for="item in options"
          :key="getOptionKey(item)"
          :label="getOptionLabel(item)"
          :value="getOptionValue(item)"
      />
    </el-select>
  </div>
</template>

<script>
import {ref, computed, onMounted, onUnmounted} from 'vue';

export default {
  name: 'AdaptiveSelect',

  props: {
    // 选项列表
    options: {
      type: Array,
      required: true,
      default: () => []
    },
    // 默认选中的值
    modelValue: {
      type: [String, Number, Array],
      default: ''
    },
    // 占位文本
    placeholder: {
      type: String,
      default: '请选择'
    },
    // 小屏幕下的宽度 (默认100%)
    mobileWidth: {
      type: String,
      default: '100%'
    },
    // 中等屏幕下的宽度 (默认80%)
    tabletWidth: {
      type: String,
      default: '80%'
    },
    // 大屏幕下的宽度 (默认600px)
    desktopWidth: {
      type: String,
      default: '600px'
    },
    // 选项的value字段名
    valueKey: {
      type: String,
      default: 'value'
    },
    // 选项的label字段名
    labelKey: {
      type: String,
      default: 'label'
    },
    // 是否可多选
    multiple: {
      type: Boolean,
      default: false
    },
    // 是否可搜索
    filterable: {
      type: Boolean,
      default: true
    },
    // 是否可清空
    clearable: {
      type: Boolean,
      default: true
    }
  },

  emits: ['update:modelValue', 'change'],

  setup(props, {emit}) {
    const selectedValue = ref(props.modelValue);
    const windowWidth = ref(window.innerWidth);
    const isDropdownVisible = ref(false);

    // 监听窗口大小变化
    const handleResize = () => {
      windowWidth.value = window.innerWidth;
    };

    onMounted(() => {
      window.addEventListener('resize', handleResize);
    });

    onUnmounted(() => {
      window.removeEventListener('resize', handleResize);
    });

    // 根据窗口宽度调整选择框样式
    const selectStyle = computed(() => {
      if (windowWidth.value < 768) {
        return {width: props.mobileWidth};
      } else if (windowWidth.value < 1024) {
        return {width: props.tabletWidth};
      } else {
        return {width: props.desktopWidth};
      }
    });

    // 根据窗口宽度调整下拉框类名
    const popperClass = computed(() => {
      return windowWidth.value < 768 ? 'mobile-dropdown' : 'desktop-dropdown';
    });

    // 下拉框显示/隐藏时的处理
    const handleVisibleChange = (visible) => {
      isDropdownVisible.value = visible;
    };

    // 选项变化时的处理
    const handleChange = (value) => {
      emit('update:modelValue', value);
      emit('change', value);
    };

    // 获取选项的key
    const getOptionKey = (item) => {
      return item[props.valueKey] || item.value;
    };

    // 获取选项的label
    const getOptionLabel = (item) => {
      return item[props.labelKey] || item.label || item[props.valueKey] || item.value;
    };

    // 获取选项的value
    const getOptionValue = (item) => {
      return item[props.valueKey] || item.value;
    };

    return {
      selectedValue,
      selectStyle,
      popperClass,
      handleVisibleChange,
      handleChange,
      getOptionKey,
      getOptionLabel,
      getOptionValue
    };
  }
};
</script>


<style scoped>
.adaptive-select-container {
  padding: 0;
  max-width: 100%;
  box-sizing: border-box;
}
</style>

<style>
/* 全局样式,用于下拉框 */
.mobile-dropdown {
  width: 90vw !important;
  max-width: 100% !important;
}

.desktop-dropdown {
  min-width: 200px !important;
  max-width: 600px !important;
}

/* 响应式调整 */
@media (max-width: 768px) {
  .el-select-dropdown {
    max-width: calc(100vw - 40px) !important;
  }
}
</style>