vant封装类似element-ui的下拉选择器

1、组件封装

<template>
  <div class="select-container">
    <div class="input-wrapper">
      <span class="input-prefix">{{ prefix }}</span>
      <input
        v-model="selectedValue"
        type="text"
        class="input"
        :placeholder="placeholder"
        :readonly="readonly"
        @click="handleClick(selectedValue)"
      >
      <span class="input-suffix">
        <i v-if='showOptions' class="iconfont qywx-xiala1"/>
        <i v-else class="iconfont qywx-xiala"/>
      </span>
    </div>
    <div
      v-if="showOptions"
      class="options-container"
    >
      <ul class="options">
        <li
          v-for="(option, index) in options"
          :key="index"
          class="option"
          :class="{'active': option[valueProp] === selectedValue, 'disabled': option.isDisabled,'hidden': !option.visible}"
          @click="handleSelect(option)"
        >
          <span> {{ option[labelProp] }}</span>
          <span v-if="option[valueProp] === selectedValue">
            <i class="iconfont qywx-a-xuanzhonglansexuanzhong"/>
          </span>
        </li>
        <li
          v-if="filteredOptions.length === 0 && selectedValue !== ''"
          class="option disabled"
        >
          无搜索结果
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
export default {
  name: 'Select',
  props: {
    prefix: {
      type: String,
      default: ''
    },
    selectedValue: {
      type: [String, Number],
      default: ''
    },
    options: {
      type: Array,
      required: true
    },
    readonly: {
      type: Boolean,
      default: false
    },
    placeholder: {
      type: String,
      default: '请选择'
    },
    labelProp: {
      type: String,
      default: 'label'
    },
    valueProp: {
      type: String,
      default: 'value'
    }
  },
  model: {
    prop: 'selectedValue',
    event: 'change'
  },
  data() {
    return {
      showOptions: false,
    }
  },
  computed: {
    filteredOptions() {
      const searchValue = this.selectedValue.trim().toLowerCase();
      const labelProp = this.labelProp;
      return this.options.map(option => {
        option.visible = option[labelProp].toLowerCase().includes(searchValue)
        return option;
      })
    }
  },
  methods: {
    handleClick(selectedValue) {
      this.selectedValue = '';
      if(!selectedValue) {
        this.$emit('change', selectedValue);
      }
      this.showOptions = !this.showOptions;
      if (this.showOptions) {
        const selectedOption = this.options.find(option => option[this.labelProp] === selectedValue);
        if (selectedOption && !selectedOption.isDisabled) {
          // 更新dom
          this.$nextTick(() => {
            // 如果有选中的选项,则将其高亮
            const optionIndex = this.options.indexOf(selectedOption);
            const optionsList = this.$el.querySelector('.options');
            const selectedOptionEl = optionsList.children[optionIndex];
            selectedOptionEl.classList.add('active');
          })

        }
      }
    },
    handleSelect(option) {
      if(!option.isDisabled){
        this.showOptions = false;
        this.$emit('change', option);
      }

    }
  }
}

<style scoped>
.select-container {
    position: relative;
}

.input-wrapper {
    position: relative;
    display: flex;
    align-items: center;
}

.input-prefix {
    padding: 0 5px;
    color: #999;
    font-size: 14px;
}

.input {
    flex: 1;
    height: 40px;
    line-height: 40px;
    padding: 0 10px;
    //border: 1px solid #dcdfe6;
    border: none;
    border-radius: 4px;
    font-size: 14px;
}

.input-suffix {
    display: flex;
    align-items: center;
    padding: 0 10px;
    color: #999;
    font-size: 14px;
    cursor: pointer;
}

.options-container {
    position: absolute;
    top: 45px;
    //left: 0;
    //right: 0;
    background-color: #fff;
    box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.15);
    z-index: 99;
    //width: 100%;
    //margin: 4px 37px 4px 39px;
    flex: 1;
    width: -webkit-fill-available;
    height: 120px;
    overflow: auto;
}

.options {
    list-style: none;
    margin: 0;
    padding: 0;
}
.hidden {
  display: none;
}
.option {
    height: 40px;
    line-height: 40px;
    padding: 0 10px;
    cursor: pointer;
    color: #000000;
}

.option:hover {
    background-color: #f5f5f5;
}

.active {
    background-color: #eaf2ff;
    color: #0075f9;
}
.disabled {
    color: #c6c7c9;
}
.iconfont {
    font-size: 16px;
}

.icon-xiala:before {
    content: '\e645';
}

.icon-xuanzhong:before {
    content: '\e648';
}
</style>

2、使用

<template>
  <div>
    <van-form @submit="onSubmit">
      <van-field
        v-model="selectedCity"
        center
        clearable
        name='selectedCity'
        required
        label="短信验证码"
        placeholder="请输入短信验证码"
        :rules="[{ required: true, message: '请填写用户名' }]"
      >
        <template #input>
          <Select
            v-model="selectedCity"
            :options="cityOptions"
            @change="handlecityChange"
          />
        </template>
      </van-field>
      <div style="margin: 16px;">
        <van-button round block type="info" native-type="submit">提交</van-button>
      </div>
    </van-form>

  </div>
</template>

<script>
import Select from './SearchableDropdown.vue';

export default {
  name: 'App',
  components: { Select },
  data() {
    return {
      cityOptions: [
        { label: '北京', value: 'beijing',isDisabled: false,visible: false },
        { label: '上海', value: 'shanghai',isDisabled: true,visible: false },
        { label: '广州', value: 'guangzhou',isDisabled: false,visible: false},
        { label: '深圳', value: 'shenzhen',isDisabled: false,visible: false},
      ],
      selectedCity: '',
      username: null,
    };
  },
  methods: {
    handlecityChange(option) {
      console.log(option);
      this.selectedCity = option.label;
    },
    onSubmit(value) {
      console.log(value);
    }
  }
};
</script>
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值