Vue el 您所在的位置:网站首页 开心人物简笔画半身图片 Vue el

Vue el

#Vue el| 来源: 网络整理| 查看: 265

平时我们做业务需求的时候,可能会遇到非常大量的数据,有时候成百上千条,一般后端都会写一个分页的接口,只要我们请求的时候加上页码参数即可。 但是在使用element-ui的el-select下拉菜单组件中,官方没有提供相应的方法进行多页加载。 这时候我们可以实现一个Vue的自定义指令,每当使用el-select滚动到列表底部的时候就请求下一页数据,来达到下拉滚动加载更多的目的。

实现自定义指令

首先实现一个el-select下拉加载的自定义指令v-loadmore:

// directives.js import Vue from 'vue' Vue.directive("loadmore", { bind(el, binding, vnode) { const SELECTWRAP = el.querySelector( ".el-select-dropdown .el-select-dropdown__wrap" ); SELECTWRAP.addEventListener("scroll", function () { // scrollTop 这里可能因为浏览器缩放存在小数点的情况,导致了滚动到底部时 // scrollHeight 减去滚动到底部时的scrollTop ,依然大于clientHeight 导致无法请求更多数据 // 这里将scrollTop向上取整 保证滚到底部时,触发调用 const CONDITION = this.scrollHeight - Math.ceil(this.scrollTop) data() { return { selected: "", options: [ { label: "1", value: 1 }, // ... 此处省略多个选项 { label: "到达底部啦", value: 9 } ] }; }, methods: { loadMore() { console.log("more") } } };

使用效果如下:

从效果图可以看出,每当菜单列表滚动到底部时,指令就会调用传入的loadMore函数,控制台随即打印出 “more”。 注意事项:

传入的数组个数必须大于或者等于8个选项时才能让el-select组件出现下拉滚动。 列表里不存在滚动时,无法触发传入指令的函数。

进行二次封装

滚动到底部调用函数的指令已经实现了,下面只要调用接口,把获取到下一页的数据拼接到当前的数据中即可。 接下来把el-select进行二次封装,封装成公用的组件之后,传入必要的参数就可以在项目中调用。 首先新建一个文件load-select.vue: javascript复制代码

export default { props: { value: { type: String, default: "" }, // 列表数据 data: { type: Array, default: () => [] }, dictLabel: { type: String, default: "label" }, dictValue: { type: String, default: "value" }, // 调用页数的接口 request: { type: Function, default: () => {} }, page: { type: [Number, String], default: 1 } }, data() { return {}; }, methods: { // 请求下一页的数据 loadMore() { this.request({ page: this.page + 1 }) }, // 选中下拉框没有数据时,自动请求第一页的数据 focus() { if (!this.data.length) { this.request({page: 1}) } } } };

在页面组件中调用load-select.vue: javascript复制代码

// 导入该组件 import loadSelect from "@/components/load-select/index"; export default { name: "app", components: { loadSelect }, data() { return { selected: "", page: 1, more: true, data: [] }; }, methods: { // 传入给load-select组件的函数 getData({ page = 1 } = {}) { // 输出页数 console.log(page) // 访问后端接口API this.requestAPI({ page }).then(res => { this.data = [...this.data, ...res.result] this.page = res.page }); }, // 模拟后端接口的API requestAPI({ page = 1, size = 10 } = {}) { return new Promise(resolve => { let responseData = [] // 假设总共的数据有50条 let total = 50; for (let index = 1; index responseData.push({ label: serial, value: serial }); } } // 模拟异步请求,500ms之后返回接口的数据 setTimeout(() => { resolve({ total, page, size, result: responseData }); }, 500); }); } } };

代码解析: 首次点击下拉框时,会触发focus事件请求第一页的数据,之后只要每次滚动列表到底部,就会自动请求下一页的数据然后拼接到当前的数组中。 我们来看看效果:

完美!但是在实际使用的过程中,可能会因为接口还来不及返回数据,然后列表又向下滚动再次触发了请求,结果就是返回了两份相同的数据。 现在把接口的延迟调到2000ms重现这个场景:

在两次快速滚动到底部的时候,请求的参数页数都是2,如何解决这个问题?可以在加载函数中加入一个拦截操作,在接口没有响应之前,不调用加载函数,不过这样做要把getData转换成异步函数的形式。 首先在load-select.vue中的loadMore()中加入一个拦截操作: javascript复制代码

... // 请求下一页的数据 methods: { loadMore() { // 如果 intercept 属性为 true 则不请求数据 if (this.loadMore.intercept) { return } this.loadMore.intercept = true this.request({ page: this.page + 1 }).then(() => { // 接口响应之后才把 intercept 设置为 false this.loadMore.intercept = false }) } }

然后在page.vue中的getData()函数转换成异步函数的形式:

... methods: { // 传入给load-select组件的函数 getData({ page = 1 } = {}) { // 返回 Promise 对象 return new Promise( resolve => { // 访问后端接口API this.requestAPI({ page }).then(res => { this.data = [...this.data, ...res.result] this.page = res.page resolve() }); }) }, }

现在问题来了:

一般分页的接口都支持关键字的搜索,load-select.vue组件能不能加入关键字搜索的功能呢?

关键字搜索功能

还好el-select组件支持远程搜索功能,只要传入filterable和remote参数,具体的可以查看element-ui的官方文档。 接下来对load-select.vue进行以下修改:

export default { props: { value: { default: "" }, // 列表数据 data: { type: Array, default: () => [] }, dictLabel: { type: String, default: "label" }, dictValue: { type: String, default: "value" }, // 调用页数的接口 request: { type: Function, default: () => {} }, // 传入的页码 page: { type: [Number, String], default: 1 }, // 是否还有更多数据 hasMore: { type: Boolean, default: true } }, data() { return { // 存储关键字用 keyword: "", loading: false }; }, methods: { // 请求下一页的数据 loadMore() { // 如果没有更多数据,则不请求 if (!this.hasMore) { return } // 如果intercept属性为true则不请求数据, if (this.loadMore.intercept) { return } this.loadMore.intercept = true; this.request({ page: this.page + 1, more: true, keyword: this.keyword }).then(() => { this.loadMore.intercept = false }); }, // 选中下拉框没有数据时,自动请求第一页的数据 focus() { if (!this.data.length) { this.request({ page: 1 }) } }, // 关键字搜索 handleSearch(keyword) { this.keyword = keyword this.loading = true this.request({ page: 1, keyword }).then(() => { this.loading = false }); }, // 删除选中时,如果请求了关键字,则清除关键字再请求第一页的数据 clear() { if (this.keyword) { this.keyword = "" this.request({ page: 1 }) } } } };

页面调用时,getData()请求函数需要接收keyword和more参数并进行相应的处理:

// 导入该组件 import loadSelect from "@/components/load-select/index"; export default { name: "app", components: { loadSelect }, data() { return { selected: "", page: 1, more: true, data: [] }; }, methods: { // 传入给load-select组件的函数 getData({ page = 1, more = false, keyword = "" } = {}) { return new Promise(resolve => { // 访问后端接口API this.requestAPI({ page, keyword }).then(res => { // 如果是加载更多,则合并之前的数据 if (more) { this.data = [...this.data, ...res.result] } else { this.data = res.result } this.page = res.page; let { total, page, size } = res // 如果为最后一页,则设置more为false this.more = page * size }) { return new Promise(resolve => { // 如果有 keyword 参数,则返回带有 keyword 的数据 if (keyword) { setTimeout(() => { resolve({ total: 3, page: 1, size: 10, result: [ { label: keyword, value: 1 }, { label: keyword + 1, value: 2 }, { label: keyword + 2, value: 3 } ] }) }, 500) return } let responseData = []; // 假设总共的数据有50条 let total = 50; for (let index = 1; index responseData.push({ label: serial, value: serial }); } } setTimeout(() => { resolve({ total, page, size, result: responseData }) }, 500) }) } } };

接下来看看搜索关键字的效果:

搜索功能也完成啦! 总结 为了适用于大部分的请求接口,因此在设计这个组件的时候只能把请求与组件剥离开来,易用程度不算太高,不过我们可以适当地传入一些简单必要的参数去维持基本地使用。 当然,在项目中遇到某些固定的加载请求时,我们也可以对该组件进行再次封装,具体可以根据自身的业务需求进行修改。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有