优化elementui框架Select组件远程搜索不支持上拉加载

2020-02-18  本文已影响0人  前端蜗牛老师

一,封装组件背景

elementui框架Select组件远程搜索不支持上拉加载功能, 本教程用vue 自定义指令(directive)结合element Select远程搜索mixin混合模式,封装select选择器上拉夹杂支持远程搜索功能组件

image.png

二,首先要封装一个自定义组件

image.png

loadMore.js 文件里的代码如下

import { addClass } from '../../utils'

export default {
  bind(el, binding) {
    // 获取element-ui定义好的scroll盒子
    const SELECTWRAP_DOM = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap')
    const loadingDom = document.createElement('div')
    loadingDom.style.width = '100%'
    loadingDom.style.height = '50px'
    loadingDom.style.lineHeight = '30px'
    loadingDom.style.backgroundColor = '#fff'
    loadingDom.style.color = '#606266'
    loadingDom.style.textAlign = 'center'
    loadingDom.innerHTML = '加载中...'

    SELECTWRAP_DOM.addEventListener('scroll', function() {
      /*
       * scrollHeight 获取元素内容高度(只读)
       * scrollTop 获取或者设置元素的偏移值,常用于, 计算滚动条的位置, 当一个元素的容器没有产生垂直方向的滚动条, 那它的scrollTop的值默认为0.
       * clientHeight 读取元素的可见高度(只读)
       * 如果元素滚动到底, 下面等式返回true, 没有则返回false:
       * ele.scrollHeight - ele.scrollTop === ele.clientHeight;
       */
      const CONDITION = this.scrollHeight - this.scrollTop <= this.clientHeight
      if (CONDITION) {
        SELECTWRAP_DOM.append(loadingDom)
        binding.value()
      }
    })
  }
}

(本案例不讲解自定义组件的知识,请自行学习或者加入我们的qq群或者关注公众号 程序员蜗牛,回复你的问题)
文件结构是
---src--
-------directive--
------------------index.js(directive入口文件,解释如下)
------------------loadMore--
------------------------------index.js和loadMore.js

index.js 里面代码

import loadMore from '@/directive/load-more/index'
Vue.directive('loadMore', loadMore)

main.js里面代码

import '@/directive/index'

自此自定义指令就可以引用了
在需要使用自定义指令的地方 v-load-more=loadMore(接收函数)


image.png image.png

结果:


屏幕录制-2020-02-18-下午1.42.15.gif

这样做不太完美,接下来就是高度封装代码
在 loadMore.js

import { addClass, removeClass } from '../../utils'

export default {
  bind(el, binding, vNode) {
    // 获取element-ui定义好的scroll盒子
    const SELECTWRAP_DOM = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap')
    const loadingDom = document.createElement('div')
    loadingDom.style.width = '100%'
    loadingDom.style.height = '50px'
    loadingDom.style.lineHeight = '30px'
    loadingDom.style.backgroundColor = '#fff'
    loadingDom.style.color = '#606266'
    loadingDom.style.textAlign = 'center'
    loadingDom.style.fontSize = '14px'
    loadingDom.className = 'loadingBox'
    const loadingIcon = document.createElement('i')
    loadingDom.append(loadingIcon)
    SELECTWRAP_DOM.append(loadingDom)
    const i = el.getElementsByClassName('loadingBox')[0].getElementsByTagName('i')[0]
    SELECTWRAP_DOM.addEventListener('scroll', function() {
      /*
       * scrollHeight 获取元素内容高度(只读)
       * scrollTop 获取或者设置元素的偏移值,常用于, 计算滚动条的位置, 当一个元素的容器没有产生垂直方向的滚动条, 那它的scrollTop的值默认为0.
       * clientHeight 读取元素的可见高度(只读)
       * 如果元素滚动到底, 下面等式返回true, 没有则返回false:
       * ele.scrollHeight - ele.scrollTop === ele.clientHeight;
       */
      const CONDITION = this.scrollHeight - this.scrollTop <= this.clientHeight
      if (CONDITION) {
        addClass(i, 'el-icon-loading')
        binding.value()
      } else {
        removeClass(i, 'el-icon-loading')
      }
    })
  }
}

然后在自定义组件里新建hospitalForm.vue

<template>
  <el-form-item label="医院名称:" prop="hospitalNo">
    <el-select
      v-model="ruleForm.hospitalNo"
      v-load-more="loadMore"
      filterable
      placeholder="请选择"
      remote
      :remote-method="remoteMethod"
      value=""
      clearable
      :loading="loading"
      popper-class="option-box"
      :style="styles"
      @change="handleChange"
    >
      <el-option
        v-for="(item, index) in hospitalNoOptions"
        :key="index"
        :label="item.hospitalName"
        :value="item.hospitalNo"
      />
    </el-select>
  </el-form-item>
</template>

<script>
import { searchHospital } from '../../api/user'
import { S_OK } from '../../utils/constant'

export default {
  name: 'HospitalForm',
  props: {
    hospitalNo: {
      type: String,
      default: ''
    },
    styles: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      ruleForm: {
        hospitalNo: ''
      },
      hospitalNoOptions: [],
      loading: false,
      pageNo: 0,
      searchKey: {
        'hospitalNo': '',
        'hospitalName': '',
        'pageNo': 1
      },
      hospitalTotalCount: 0
    }
  },
  mounted() {
    this.searchHospitalFun()
  },
  methods: {
    // 初始化数据,并上啦加载时
    searchHospitalFun() {
      searchHospital(this.searchKey).then(res => {
        if (res.resultCode === S_OK) {
          this.hospitalNoOptions = this.hospitalNoOptions.concat(res.resultObj.list)
          this.hospitalTotalCount = res.resultObj.totalCount
          if (res.resultObj.pages * 10 > res.resultObj.totalCount) {
            this.searchKey.pageNo = res.resultObj.pages
           this.$refs['hospitalForm'].popperElm.getElementsByClassName('loadingBox')[0].innerHTML = '~见底了~'
          }
        }
      })
    },
    // 上拉加载
    loadMore() {
      if (this.searchKey.pageNo * 10 <= this.hospitalTotalCount) {
        this.handleLoad()
        this.searchHospitalFun()
      }
      this.searchKey.pageNo += 1
    },
    // 远程搜索
    remoteMethod(query) {
      this.handleLoad()
      this.handleTop()
      if (query !== '') {
        this.loading = true
        this.searchKey.hospitalName = query
        this.searchKey.pageNo = 1
        searchHospital(this.searchKey).then(res => {
          if (res.resultCode === S_OK) {
            this.hospitalNoOptions = res.resultObj.list
            this.hospitalTotalCount = res.resultObj.totalCount
            this.loading = false
            if (res.resultObj.pages * 10 > res.resultObj.totalCount) {
              this.searchKey.pageNo = res.resultObj.pages
      this.$refs['hospitalForm'].popperElm.getElementsByClassName('loadingBox')[0].innerHTML = '~见底了~'
            }
          }
        })
      } else {
        this.hospitalNoOptions = []
        this.searchKey = {
          'hospitalNo': '',
          'hospitalName': '',
          'pageNo': 1
        }
        this.searchHospitalFun()
      }
    },
    // 点击搜索时,就将见底了变回加载icon
    handleLoad() {
      this.$refs['hospitalForm'].popperElm.getElementsByClassName('loadingBox')[0].innerHTML = `
           <i class='el-icon-loading'></i>
        `
    },
    handleChange() {
      this.$emit('update:hospitalNo', this.ruleForm.hospitalNo)
    },
    handleTop() {
      const dom = document.getElementsByClassName('option-box')[0].getElementsByClassName('el-scrollbar__wrap')[0]
      dom.scrollTop = 0 // 回到顶部,有点问题,滚动条没有回去,其实是回到顶部了,有看出问题的也欢迎留言
    }
  }
}
</script>

<style scoped>

</style>

回到顶部,有点问题,滚动条没有会去,其实是回到顶部了,有看出问题的也欢迎提留言

定义全局组件:在main.js中

Vue.component('hospitalForm', () => import('@/components/hospitalForm'))

接下来就是使用了

  <hospital-form styles="width: 225px" :hospital-no.sync="ruleForm.hospitalNo" />

其中子组件改变父组件传过来的值并没有保存是因为使用了.sync修饰符
https://www.jianshu.com/p/3dbbbc7a259c

最终实现结果为

屏幕录制-2020-02-18-下午6.17.04.gif

欢迎关注我的公众号:程序员蜗牛,会有惊喜哦


image.png
上一篇下一篇

猜你喜欢

热点阅读