vue中el-autocomplete支持分页上拉加载功能

目录
  • el-autocomplete使用
  • template
  • 实现需求分析
    • 1. 输入框为空时聚焦或失焦后又重新聚焦不会触发请求数据接口
    • 2. 缓存上一次已查询的数据&搜索条件:blurArr、blurTxt
    • 3.滚动加载指令(监听容器的scroll事件并进行防抖处理)
    • 4. 分页加载
      • 4.0 获取数据,并进行格式化
      • 4.1 关闭加载圈
      • 4.2 分页加载事件
      • 4.3 清空输入框,重置上次记录的数据
      • 4.4 选中时记录相关数据
  • 数据展示不稳定问题
  • 完整的 scss 文件
  • 完整的 js 文件
  • 总结

el-autocomplete使用

效果图

template

<template>
  <el-autocomplete
    :clearable="true"                                  //支持清空
    :title="searchStr"                                 // 鼠标移上去提示文案
    :trigger-on-focus="true"                           // 聚焦时是否触发下拉列表展示
    :fetch-suggestions="querySearchAsync"              // 筛选符合条件的数据
    :placeholder="placeholder"                         // 占位符提示信息
    v-scrollLoad="load"                                // 自定义上拉加载指令
    v-model="searchStr"                                // 搜索关键字
    popper-class="diy-autocomplete"                    // 下拉框自定义class控制样式
    class="el-autocomplete-component"                  // 给当前组件定义专属类名
    size="small"                                       // 组件显示尺寸
    ref="autocomplete"                                 // 用于后期获取dom元素
    @select="handleSelect"                             // 选中时触发事件
    @blur="handleBlur"                                 // 失去焦点时触发
    @clear="handleClear"                               // 清空数据时触发
  ></el-autocomplete>
</template>

实现需求分析

1. 输入框为空时聚焦或失焦后又重新聚焦不会触发请求数据接口

// blurTxt: 记录上次失焦时 和 选中时的筛选字段
// blurArr: 记录上次失焦时 和 选中时已经查询到的数据
async querySearchAsync(queryString, cb) {
      if (this.blurTxt === queryString || !queryString) {
        cb(this.blurArr)
        return
      }
    },

2. 缓存上一次已查询的数据&搜索条件:blurArr、blurTxt

    // 失焦事件
    handleBlur() {
      this.blurTxt = this.searchStr || ''
      this.blurArr = this.$refs['autocomplete'].$data.suggestions
    },
    // 过滤数据时及时更新筛选字段
    async querySearchAsync(queryString, cb) {
      this.blurTxt = searchVal
    },

3.滚动加载指令(监听容器的scroll事件并进行防抖处理)

  • 防抖函数
/**
 * @param {Function} func
 * @param {number} wait
 * @param {boolean} immediate
 * @return {*}
 */
export function debounce(func, wait, immediate) {
  let timeout, args, context, timestamp, result

  const later = function() {
    // 据上一次触发时间间隔
    const last = +new Date() - timestamp

    // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait
    if (last < wait && last > 0) {
      timeout = setTimeout(later, wait - last)
    } else {
      timeout = null
      // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用
      if (!immediate) {
        result = func.apply(context, args)
        if (!timeout) context = args = null
      }
    }
  }

  return function(...args) {
    context = this
    timestamp = +new Date()
    const callNow = immediate && !timeout
    // 如果延时不存在,重新设定延时
    if (!timeout) timeout = setTimeout(later, wait)
    if (callNow) {
      result = func.apply(context, args)
      context = args = null
    }

    return result
  }
}
  • 滚动加载指令
  directives: {
    scrollLoad: {
      bind(el, binding, vnode) {
        let wrapDom = el.querySelector('.el-autocomplete-suggestion__wrap')
        let listDom = el.querySelector('.el-autocomplete-suggestion__wrap  .el-autocomplete-suggestion__list')
        // 滚动事件做防抖处理
        wrapDom.addEventListener(
          'scroll',
          debounce(e => {
            let condition = wrapDom.offsetHeight + wrapDom.scrollTop + 50 - listDom.offsetHeight
            if (condition > 0 && !vnode.context.loading) {
              binding.value()
            }
          }, 300),
          false
        )
      }
    }
  }

4. 分页加载

  • 请求前展示加载圈
  • 加载至最后一页时不再进行请求并提示暂无更多数据
  • 关闭loading加载圈
  • 把数据追加至已展示的数据列表中

4.0 获取数据,并进行格式化

第一种方式: 在组件上设置valueKey为你要展示的字段名称,默认值为value

<el-autocomplete valueKey="nickName"></el-autocomplete>

第二种方式:拿到数据后遍历数据为每一项增添value属性,值为自己组合想展示的方式

    // 获取用户列表
    async getList(queryString) {
      let result = await searchUserList({
        pageNum: this.pageNum,
        pageSize: this.pageSize,
        searchValue: decodeURI(queryString)
      })
      this.total = result.total
      // 调用 callback 返回建议列表的数据
      result.rows &&
        result.rows.forEach(element => {
          // 学生展示 姓名+班级
          if (element.classList[0] && element.roleId === 101) {
            element.value = element.nickName + '-' + element.classList[0].className
          } else {
            // 非学生或者学生没有主班级展示 姓名+身份
            element.value = element.nickName + '-' + (element.roleName || '暂无角色ID')
          }
        })
      return result.rows
    },

第三种方式:在组件对应的插槽slot中自定义展示内容

<el-autocomplete >
    <!--  输入框小图标插槽  -->
    <i class="el-icon-edit el-input__icon" slot="suffix"> </i>
    <!--  搜索列表每一项展示  -->
    <template slot-scope="{ item }">
        <div class="name">{{ item.nickName }} - {{item.className}}</div>
    </template>
</el-autocomplete>

4.1 关闭加载圈

    // 关闭加载圈
    closeLoading() {
      loadingInstance && loadingInstance.close && loadingInstance.close()
      loadingInstance = null
    },

4.2 分页加载事件

    // 滚动加载
    async load() {
      this.closeLoading()
      // 加载到最后一页停止加载
      if (this.pageNum * this.pageSize > this.total) {
        return
      }
      this.pageNum++
      loadingInstance = Loading.service({
        target: document.querySelector('.el-autocomplete-suggestion'),
        fullscreen: false,
        spinner: 'el-icon-loading',
        lock: true,
        text: '加载中...'
      })
      let results = await this.getList(this.searchStr)
      this.closeLoading()
      this.pageNum * this.pageSize >= this.total ? results.push({ value: '暂无更多数据' }) : ''
      // 将数据添加到下拉列表
      this.$refs['autocomplete'].$data.suggestions = this.$refs['autocomplete'].$data.suggestions.concat(results)
    },

4.3 清空输入框,重置上次记录的数据

    // 清空搜索项
    handleClear() {
      this.blurTxt = ''
      this.blurArr = []
      this.$refs['autocomplete'].$data.suggestions = []
    },

4.4 选中时记录相关数据

    // 选中用户跳转至对应的页面
    handleSelect(item) {
      this.$refs['autocomplete'].$data.suggestions = this.blurArr = [item]
      this.blurTxt = this.searchStr || ''
      this.pageNum = 1
      this.total = 0
      ...
      //下拉选中的值
      // console.log(item)
    }

数据展示不稳定问题

例如姓名模糊搜索过程中,也许我们会先输入姓为第一个关键词,接着在输入第二个关键词名字,只输入姓的时候肯定要比姓名要查询的数据多,当在大量数据中查询时会面临着第二个请求(搜索条件:输入姓名的)先返回数据,然后第一个请求(搜索条件:输入姓的)才会返回数据的情况,而此时筛选列表中展示的肯定是最后请求出来的结果(搜索框中展示的是完整姓名:张三,而展示列表中却展示出了:张一、张二、张三...),此时的解决方案是相同接口取消上一次的接口。

  • 请求拦截中限制重复请求某个接口
import axios from 'axios'
let pending = []; //声明一个数组用于存储每个ajax请求的取消函数和ajax标识
let cancelToken = axios.CancelToken;
let removePending = (ever) => {
    for (let p in pending) {
        if (pending[p].u === ever.url + '&' + ever.method) { //当当前请求在数组中存在时执行函数体
            pending[p].f(); //执行取消操作
            pending.splice(p, 1); //把这条记录从数组中移除
        }
    }
}
var errorFlag = false;
var erFlag = false;
// 创建axios实例
const service = axios.create({
  // axios中请求配置有baseURL选项,表示请求URL公共部分
  baseURL: process.env.VUE_APP_BASE_API,
  // 超时
  timeout: 90000
})
// request拦截器
service.interceptors.request.use(
  config => {
     // 如果你是在老项目中开发就加一个限制,避免影响到原有的功能
    // if(config.url.indexOf('system/user/newsearch_list')!==-1){

      config && removePending(config); //在一个ajax发送前执行一下取消操作
      config.cancelToken = new cancelToken((c) => {
          // 这里的ajax标识我是用请求地址&请求方式拼接的字符串,当然你可以选择其他的一些方式
          pending.push({
              u: config.url + '&' + config.method,
              f: c
          });
      });

    // }
    return config
  },
  error => {
    console.log(error)
    Promise.reject(error)
  }
)
  • 相应拦截中对取消请求这个操作单独处理,不展示错误消息提示弹窗
// 响应拦截器
service.interceptors.response.use(res => {
    const code = res.data.code
    if (code === 401) {
     ...
    } else if (code !== 200) {
      if(!errorFlag){
        ...
        return Promise.reject(res.data || {})
      }
    } else {
      return res.data
    }
  },
  error => {
    // 单独处理取消请求导致的错误
    if(error.__CANCEL__){
      return false
    }
    if(!erFlag){
      Message({
        message: error.message,
        type: 'error',
        duration: 3 * 1000
      })
      return Promise.reject(error)
    }
  }
)

完整的 scss 文件

.el-autocomplete-component {
  max-width: 230px;
  vertical-align: text-bottom;
  height: 50px;
  padding-top: 1px;
  cursor: pointer;
  /deep/ .el-input__inner {
    cursor: pointer;
    padding-left: 5px;
    padding-right: 8px;
    background: transparent;
    border: none;
    color: #fff;
    font-size: 14px;
    text-overflow: ellipsis;
    white-space: nowrap;
    overflow: hidden;
    &::placeholder {
      color: #bfbfbf;
      font-size: 12px;
    }
  }
}

.diy-autocomplete {
  .name {
    max-width: 180px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    height: 34px;
  }
}

完整的 js 文件

<script>
import { searchUserList } from '@/api/system/user'  // 请求用户列表的接口
import { debounce } from '@/utils/index' // 防抖函数
import { Loading } from 'element-ui' // 下拉加载时的过渡loading
let loadingInstance = null

export default {
  data() {
    return {
      showAutocomplete: false,
      searchStr: '', //输入关键词的值
      pageNum: 1,
      pageSize: 20,
      total: 0, //筛选数据的总值
      placeholder: '请输入用户名/手机号/QQ',
      blurTxt: '', //记录失焦时搜索框中的文字,避免聚焦时重新筛选数据
      blurArr: [] //记录失焦时已经搜索出来的列表
    }
  },
  methods: {
    // 失焦事件
    handleBlur() {
      this.blurTxt = this.searchStr || ''
      this.blurArr = this.$refs['autocomplete'].$data.suggestions
    },
    // 清空搜索项
    handleClear() {
      this.blurTxt = ''
      this.blurArr = []
      this.$refs['autocomplete'].$data.suggestions = []
    },
    // 关闭加载圈
    closeLoading() {
      loadingInstance && loadingInstance.close && loadingInstance.close()
      loadingInstance = null
    },
    // 条件查询
    async querySearchAsync(queryString, cb) {
      this.$refs['autocomplete'].$data.suggestions = []
      if (this.blurTxt === queryString || !queryString) {
        cb(this.blurArr)
        return
      }
      this.handleClear()
      let searchVal = queryString
      // 后面所拼接的班级名称和角色不参与筛选字段中
      queryString.indexOf('-') !== -1 ? (searchVal = queryString.split('-')[0]) : ''
      this.pageNum = 1
      this.blurTxt = searchVal
      let results = await this.getList(searchVal)
      cb(results || [])
    },
    // 获取用户列表
    async getList(queryString) {
      let result = await searchUserList({
        pageNum: this.pageNum,
        pageSize: this.pageSize,
        searchValue: decodeURI(queryString)
      })
      this.total = result.total
      // 调用 callback 返回建议列表的数据
      result.rows &&
        result.rows.forEach(element => {
          // 学生展示 姓名+班级
          if (element.classList[0] && element.roleId === 101) {
            element.value = element.nickName + '-' + element.classList[0].className
          } else {
            // 非学生或者学生没有主班级展示 姓名+身份
            element.value = element.nickName + '-' + (element.roleName || '暂无角色ID')
          }
        })
      return result.rows
    },
    // 滚动加载
    async load() {
      this.closeLoading()
      // 加载到最后一页停止加载
      if (this.pageNum * this.pageSize > this.total) {
        return
      }
      this.pageNum++
      loadingInstance = Loading.service({
        target: document.querySelector('.el-autocomplete-suggestion'),
        fullscreen: false,
        spinner: 'el-icon-loading',
        lock: true,
        text: '加载中...'
      })
      let results = await this.getList(this.searchStr)
      this.closeLoading()
      this.pageNum * this.pageSize >= this.total ? results.push({ value: '暂无更多数据' }) : ''
      // 将数据添加到下拉列表
      this.$refs['autocomplete'].$data.suggestions = this.$refs['autocomplete'].$data.suggestions.concat(results)
    },
    // 选中用户跳转至对应的页面
    handleSelect(item) {
      this.$refs['autocomplete'].$data.suggestions = this.blurArr = [item]
      this.blurTxt = this.searchStr || ''
      this.pageNum = 1
      this.total = 0
      let routeData = {}
      if (item.roleId === 101) {
        // 学生
        routeData = this.$router.resolve({ path: '/personInf/student', query: { userId: item.userId } })
      } else {
        // 非学生
        routeData = this.$router.resolve({
          path: '/userManagement/user',
          query: { userInfo: item.nickName ,roleId: item.roleId||''}
        })
      }
      window.open(routeData.href, '_blank')
      //下拉选中的值
      // console.log(item)
    }
  },
  directives: {
    scrollLoad: {
      bind(el, binding, vnode) {
        let wrapDom = el.querySelector('.el-autocomplete-suggestion__wrap')
        let listDom = el.querySelector('.el-autocomplete-suggestion__wrap  .el-autocomplete-suggestion__list')
        // 滚动事件做防抖处理
        wrapDom.addEventListener(
          'scroll',
          debounce(e => {
            let condition = wrapDom.offsetHeight + wrapDom.scrollTop + 50 - listDom.offsetHeight
            if (condition > 0 && !vnode.context.loading) {
              binding.value()
            }
          }, 300),
          false
        )
      }
    }
  }
}
</script>

总结

到此这篇关于vue中el-autocomplete支持分页上拉加载功能的文章就介绍到这了,更多相关el-autocomplete分页上拉加载内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解element-ui 组件el-autocomplete使用踩坑记录

    项目遇到一个比较麻烦的需求,保存用户填写的历史记录,项目使用的element-ui,自然就使用了el-autocomplete组件,然后就是各种踩坑,以下记录以下写代码过程中遇到的问题 createFilter(queryString, filed) { console.log("createFilter==" + queryString) return (item) => { switch (filed) { case 'cardNum': break case 'cardPa

  • 基于Vue el-autocomplete 实现类似百度搜索框功能

    效果图如下所示: 首先上代码 <template> <div class="assets-search height-all"> <div class="search-layout"> <div class="search-title">让数据触手可及</div> <div class="search-input-layout"> <!--<e

  • vue中el-autocomplete与el-select的异同

    目录 前言 异同 el-autocomplete el-select 总结 前言 最近项目里面需要使用到下拉框的远程搜索,我这边使用的是el-select,其实查看文档我们可以得知,还可以使用el-autocomplete来实现远程搜索. 那么它们具体有何异同呢?今天我们来看看. 异同 el-autocomplete el-autocomplete是使用fetch-suggestions方法实现,当输入的时候,会调用我们提供的方法,传入的参数是输入的value,以及callback. 我们要把请

  • vue对el-autocomplete二次封装增加下拉分页

    目录 1.自定义指令实现下拉加载更多. 2.增加props(getOptionFn.searchKey.value.placeholder)抽离业务.成为公共组件 3.可能需要解释的 项目中的联想输入框现在都是采用的el-autocomplete实现的,但是随着数据量越来越多,产品要求一次不要返回所有的联想数据,要做分页处理,所以需要添加一个分页的功能. 注:看懂下面的代码需要先对vue和element有一定的学习. 废话不多数,先上完整代码 <template> <el-autocom

  • element带输入建议el-autocomplete的使用

    目录 引用el-autocomplete 触发带输入建议的两种方式 转成输入建议回调的数据结构 增加回车触发事件 解决回车后建议输入框没消失的bug vue+element UI element UI的带输入建议官方文档:https://element.eleme.cn/#/zh-CN/component/input 建议先看官方文档,这里是官方文档的适当补充 引用el-autocomplete 1.在需要的地方引用 <el-autocomplete       class="inline

  • Vue el-autocomplete远程搜索下拉框并实现自动填充功能(推荐)

    官网的demo献上 在elementui Input输入框中可以找到远程搜索组件,获取服务端的数据 官网中的数据list都是写在loadAll()中的,而如果我们此时要用到mock的数据就要在此基础上对代码进行修改. -mock数据的获取- 我们要获取远程mock中所有学生的学号信息,根据输入的数据来远程查找目标,并且在选中该目标后能够自动填充对应的姓名.生日.手机等信息,实现快速获取信息的功能,再也不用手动一个个去输入所有的数据啦- 在template中添加el-autocomplete <e

  • el autocomplete支持分页上拉加载使用详解

    目录 el-autocomplete使用 template 实现需求分析 输入框为空时聚焦或失焦后又重新聚焦不会触发请求数据接口 缓存上一次已查询的数据&搜索条件:blurArr.blurTxt 滚动加载指令(监听容器的scroll事件并进行防抖处理) 分页加载 获取数据,并进行格式化 关闭加载圈 分页加载事件 清空输入框,重置上次记录的数据 选中时记录相关数据 数据展示不稳定问题 完整的 scss 文件 完整的 js 文件 el-autocomplete使用 效果图 template <t

  • vue中el-autocomplete支持分页上拉加载功能

    目录 el-autocomplete使用 template 实现需求分析 1. 输入框为空时聚焦或失焦后又重新聚焦不会触发请求数据接口 2. 缓存上一次已查询的数据&搜索条件:blurArr.blurTxt 3.滚动加载指令(监听容器的scroll事件并进行防抖处理) 4. 分页加载 4.0 获取数据,并进行格式化 4.1 关闭加载圈 4.2 分页加载事件 4.3 清空输入框,重置上次记录的数据 4.4 选中时记录相关数据 数据展示不稳定问题 完整的 scss 文件 完整的 js 文件 总结 e

  • vux-scroller实现移动端上拉加载功能过程解析

    本文将讲述vue-cli+vux-scroller实现移动端的上拉加载功能: 纠错声明:网上查阅资料看到很多人都将vux和vuex弄混,在这里我们先解释一下,vuex是vue框架自带的组件,是数据状态管理工具,vux是一款移动端的UI组件库: vux(官方文档:https://doc.vux.li/zh-CN/)是基于WeUi和vue(2.x)开发的移动端的UI组件库,主要服务于微信页面.基于webpack+vue-loader+vux可以快速开发移动端页面,配合vux-loader方便你在We

  • mui上拉加载功能实例详解

    最近在做移动端的项目,用到了mui的上拉加载,整理如下: 1.需要引入的css.js <link rel="stylesheet" href="common/mui/css/mui.min.css" rel="external nofollow" > <script src="js/jquery-3.2.0.min.js"></script> <script src="com

  • 微信小程序实现上拉加载功能示例【加载更多数据/触底加载/点击加载更多数据】

    本文实例讲述了微信小程序实现上拉加载功能.分享给大家供大家参考,具体如下: 开发需求 进入页面,加载初始数据,当向上拖动页面至底部,自动加载新的数据,即上拉加载更多数据. 演示 index.wxml <!-- 数据列表 --> <view wx:for="{{listdata}}" wx:key="listdata" class='listitem'> <view class='title'>{{item.title}}</

  • 基于vue2实现上拉加载功能

    本文实例为大家分享了vue2实现上拉加载展示的具体代码,供大家参考,具体内容如下 因为我们项目中,还用了swiper.很多都是滑动切换的,但是又得上拉加载,所以导致,很多UI框架,我们用了,都有不同的bug出现,没办法,最后写了一个.代码如下(这个因为很多地方会用,所以建议放在components/common下面): <template> <div class="loadmore"> <slot></slot> <slot nam

  • 微信小程序实现上拉加载功能

    本文实例为大家分享了微信小程序上拉加载的具体代码,供大家参考,具体内容如下 demo.wxml  文件 <view wx:for="{{listdata}}" wx:key="listdata" class='listitem'> <view class='title'>{{ item.store_name }}</view> <image src='{{item.logo}}'></image> <

  • Ionic如何实现下拉刷新与上拉加载功能

    IONIC 是目前最有潜力的一款 HTML5 手机应用开发框架.通过 SASS 构建应用程序,它提供了很多 UI 组件来帮助开发者开发强大的应用. 它使用 JavaScript MVVM 框架和 AngularJS 来增强应用.提供数据的双向绑定,使用它成为 Web 和移动开发者的共同选择.Ionic是一个专注于用WEB开发技术,基于HTML5创建类似于手机平台原生应用的一个开发框架.Ionic框架的目的是从web的角度开发手机应用,基于PhoneGap的编译平台,可以实现编译成各个平台的应用程

  • AnglarJs中的上拉加载实现代码

    简介 上拉加载,是目前手机网站加载数据的一种常用方式,本文主要讲解AnglarJs集成,上拉加载功能.通常与下拉刷新配置使用,下拉刷新请查阅. 实现 页面 <div class="search-box"> <b class="dw" ></b> <input type="search" class="search-input" placeholder="请输入搜索关键词&qu

  • Android RecyclerView 上拉加载更多及下拉刷新功能的实现方法

    RecyclerView 已经出来很久了,但是在项目中之前都使用的是ListView,最近新的项目上了都大量的使用了RecycleView.尤其是瀑布流的下拉刷新,网上吧啦吧啦没有合适的自己总结了一哈. 先贴图上来看看: 使用RecyclerView实现上拉加载更多和下拉刷新的功能我自己有两种方式: 1.使用系统自带的Android.support.v4.widget.SwipeRefreshLayout这个控价来实现. 2.自定义的里面带有RecyleView的控件. 使用RecycleVie

随机推荐