vue2 ant-design select组件自定义下拉框, dropdownRender 使用,以及遇到的坑

业务需求:下拉框需要满足用户可输入筛选 和 点击右侧 字符按钮 #A-Z进行用户选择

1、基础页面代码

<div>
    <a-select
      style="width: 100%"
      placeholder="请选择客户"
      allow-clear
      show-search
      :filter-option="false"
      :not-found-content="fetching ? undefined : null"
      :defaultActiveFirstOption="false"
      :getPopupContainer="getPopupContainer"
      @search="searhcCust"
      @dropdownVisibleChange="dropdownVisibleChange"
    >
      //dropdownRender 插件使用,自定义右边
      <div
        slot="dropdownRender"
        slot-scope="menu"
      >
        <div class="index-bar">
          <div
            v-for="letterItem in letter"
            :key="letterItem"
            @click.prevent="scrollOn(letterItem)"
            class="index-letter"
            @mousedown="e => e.preventDefault()" // 阻止调用默认事件
          >
            {{ letterItem }}
          </div>
        </div>
        <v-nodes :vnodes="menu" /> // 注意要在components中定义
      </div>
      <a-select-opt-group
        v-for="(group, index) in peoArray"
        :key="index"
      >
        <span
          slot="label"
          class="option-letter"
          :id="'peo_' + componentId + group.key"
        >{{group.key}}</span>
        <a-select-option
          v-for="option in group.list"
          :key="option.custRowId"
          :value="option.custRowId"
          :title="option.custName"
        >
          <span>{{ option.custName }}</span>
        </a-select-option>
      </a-select-opt-group>
    </a-select>
  </div>

2、script中的代码

<script>
import { debounce } from 'lodash'
export default {
  props: {
    componentId: { // 设置不同的id,用于同页面有多个此组件时锚点不生效
      type: String,
      default: ''
    }
  },
  components: {
    VNodes: {
      functional: true,
      render: (h, ctx) => ctx.props.vnodes,
    }
  },
  data() {
    return {
      dropOpen: false,
      searchValue: '',
      navBarHeight: '50px', // 导航栏高度
      letter: [], // 字母检索列表
      peoArray: [], // 通讯录列表
      custList: null,
      custRowId: undefined,
      fetching: false,
      debounceGetCustInfoKeyList: null,
    }
  },
  created() {
    this.getCustInfoKeyList()
    this.debounceGetCustInfoKeyList = debounce(this.getCustInfoKeyList, 500)
  },
  methods: {
    dropdownVisibleChange(open) {
      this.dropOpen = open
    },
    getPopupContainer(triggerNode) {
      if (this.modalSelect) {
        return triggerNode.parentNode
      } else {
        return document.body
      }
    },
    changeCust(val) {
      this.$emit('change', val)
    },
    getList(peoArray) {
      let newList = []
      peoArray.forEach(element => {
        newList.push(...element.list)
      })
      return newList
    },
    searhcCust(val) {
      this.searchValue = val
      this.debounceGetCustInfoKeyList()
    },
    getCustInfoKeyList() {
      const params = {
        custName: this.searchValue,
      }
      this.$http.XXX(params).then(res => {
        if (res.code === 200) {
          this.custList = res.data
          if (this.custList) {
            this.setList()
          } else {
            this.peoArray = []
          }
          this.fetching = false
        } else {
          this.$message.warn(res.msg)
          this.fetching = false
        }
      })
    },
    setList() {
      let list = []
      this.letter = []
      for (const key in this.custList) {
        this.letter.push(key)
        list.push({
          key,
          list: this.custList[key]
        })
      }
      setTimeout(() => {
        this.peoArray = list
      })
    },
    // 字母检索
    scrollOn(item) {
      let target = document.getElementById('peo_' + this.componentId + item) // 获取每个字母通讯录对象
      if (target) {
        const scrollDom = document.getElementsByClassName('ant-select-dropdown-menu')[0]
        scrollDom.scrollTo({
          behavior: 'smooth',
          top: target.offsetTop - 10
        })
      } else {
        this.$message.warn(`当前${item}元素下暂无客户`)
      }
    }
  }
}
</script>

3、基础CSS代码


.index-bar {
  position: absolute;
  z-index: 99;
  right: 10px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
}

.index-letter {
  margin: 0;
  cursor: pointer;
  color: #1677ff;
  font-weight: 500;
  font-size: 12px;
  line-height: 20px;
}
.option-letter {
  font-weight: 600;
  color: rgba(0, 0, 0, 0.45);
}

4、遇到的坑是什么呢?
就是在同一个页面,渲染同一个组件时,在点击前一个组件后,后面的组件右侧按钮滚动失效。
造成这个问题的原因就是 dropdownRender 渲染不会销毁,导致scrollOn获取到的DOM是同一组数据,解决方法有两种:

	// 1、在 dropdownRender 插件的地方
	<div
      v-if="dropOpen"
       slot="dropdownRender"
       slot-scope="menu"
     >
     // 2、scrollOn 中修改查询Dom方法
     let target = document.getElementById('peo_' + this.componentId + item) // 获取每个字母通讯录对象
     if (target) {
        const parentDom = document.getElementById(offsetParent.id)
        const scrollDom = parentDom.children[0]
        scrollDom.scrollTo({
          behavior: 'smooth',
          top: target.offsetTop - 10
        })
      } else {
        this.$message.warning(`当前${item}元素下暂无客户`)
      }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值