vue自定义封装select下拉菜单

<template>
  <div class="sm-dropdown-bg"> 
    <div class="sm-dropdown-select" tabindex="-1" ref="smSelectRef" @click="smSelect" @blur="smSelectBlur">
        <span>{{ valueType }}</span>
        <span class="sm-dropdown-title" :class="{'sm-dropdown-title1': showType}"></span>
    </div>
    <ul class="sm-dropdown-ul" :class="{'sm-dropdown-ul-show': showType}">
      <li v-for="(item, index) in options1" 
      :key="index" 
      :value="item.value"
      :class="{'sm-dropdown-li': item.value == useValue}"
      @click.stop="liclick(item)">{{ item.text }}</li>
    </ul>
  </div>
</template>


<style scoped lang="less">
.sm-dropdown-bg{
  display: inline-block;
  position: relative;
  .sm-dropdown-select{
    min-width: 2.2rem;
    display: inline-block;
    text-align: center;
    background-color: white;
    padding: 6px 15px;
    // box-shadow: 0 0.001rem 0.03rem #eee;
    // border: 1px solid #efefef;
    font-size: .37rem;
    display: flex;
    justify-content: center;
    align-items: center;
  }
  .sm-dropdown-ul{
    width: 100%;
    max-height: 5rem;
    border: 1px solid #eee;
    line-height: 30px;
    font-size: .35rem;
    padding: 8px;
    box-sizing: border-box;
    text-align: center;
    background-color: #fff;
    position: absolute;
    z-index: 2002;
    overflow-y: auto;
    display: none;
  }
  .sm-dropdown-ul::-webkit-scrollbar{
    width: 0;
    height: 0;
  }
  .sm-dropdown-ul-show{
    display: block;
  }
  .sm-dropdown-li{
    color: red;
  }
  .sm-dropdown-title {
      margin-top: -0.133rem;
      margin-left: .1rem;
      border: 0.08rem solid;
      border-color: transparent transparent #dcdee0 #dcdee0;
      -webkit-transform: rotate(-45deg);
      transform: rotate(-45deg);
      opacity: .8;
      content: "";
  }
  .sm-dropdown-title1{
      margin-top: -0.027rem;
      -webkit-transform: rotate(135deg);
      transform: rotate(135deg);
  }
  .sm-dropdown-input{
    display: inline;
    padding: 0;
    border: 0px;
  }
}
</style>
<script>
// 调取接口 封装方法 (不需要可以拿掉)
import { getListUrl } from '../../api/demo'
export default {
  name: 'MpDropdownMenu',
  model: {
    prop: 'value',
    event: 'change'
  },
  props: {
    value: { // 绑定值
      type: String,
      default: () => {
        return '0'
      }
    },
    options: { // 数据源
      type: Array,
      default: () => {
        return [
          { text: '全部商品', value: '0' },
          { text: '新款商品', value: '1' },
          { text: '活动商品', value: '2' }
        ]
      }
    },
    urlOptions: { // 动态数据源
      type: Object,
      default: () => {
        return {
          url: '',
          labelKey: '',
          valueKey: ''
        }
      }
    }
  },
  watch: {
    options: { // 数据源
      handler (val) {
        this.options1 = val
        if (!val || val.length == 0) {
          this.options1 = [{ text: '默认数据', value: '0' }]
        }
        if (this.options1.find(v => v.value == this.useValue)) {
          this.valueType = this.options1.find(v => v.value == this.useValue).text
        }
      },
      immediate: true,
      deep: true
    },
    value: { // 绑定的值
      handler(val){
        this.useValue = val
      },
      immediate: true,
      deep: true
    },
    useValue: { // 数据改变时使用的值
      handler(val){
        if (this.options1.find(v => v.value == val)) {
          this.valueType = this.options1.find(v => v.value == val).text
        }
      },
      immediate: true,
      deep: true
    },
  },
  data(){
    return {
      showType: false, // 下拉显示隐藏
      valueType: '', // title 显示文字
      useValue: '', // 改变时使用的值
      options1: [] // 数据源
    }
  },
  mounted(){
    this.getList()
  },
  methods: {
    // 动态数据源时请求接口
     async getList(){
      if (!this.urlOptions.url) return
      const data = await getListUrl({}, this.urlOptions.url)
      const arr = data.data.data
      arr.map(v => {
        if (this.urlOptions.labelKey) {
          v.text = v[this.urlOptions.labelKey]
        }
        if (this.urlOptions.valueKey) {
          v.value = v[this.urlOptions.valueKey]
        }
      })
      this.options1 = arr
    },
    // 点击事件 控制显示隐藏
    smSelect(){
      this.showType = !this.showType
      /**
       * 给div增加focus
       * 必须添加属性 tabindex="-1"
       * 通过@blur 事件 让下拉显示框隐藏
       * */
      this.$refs.smSelectRef.focus()
    },
    // div失去焦点事件
    smSelectBlur(){
      setTimeout(()=>{
        this.showType = false
      },100)
    },
    // 下拉列表点击事件
    liclick(item){
      this.useValue = item.value
      this.showType = false
      this.$emit('change', item.value)
    }
  }
}
</script>

这里边 特别重要的就是通过ref给div添加focus

/**
       * 给div增加focus
       * 必须添加属性 tabindex="-1"
       * 通过@blur 事件 让下拉显示框隐藏
       * */
      this.$refs.smSelectRef.focus()

这样就可以通过@blur事件实现失去焦点的时候 让下拉框也可以隐藏掉

// div失去焦点事件
    smSelectBlur(){
      setTimeout(()=>{
        this.showType = false
      },100)
    },

值得注意的是,上边延时器是为了可以实现 下拉列表每一项可以选择 否则 点击每一项的时候事件不执行

 

 

*这个下拉菜单 是移动端 仿vant UI组件的,并没有封装多选功能


版权声明:本文为qq_41169990原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。