亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Vue二次封裝el-select實(shí)現(xiàn)下拉滾動(dòng)加載效果(el-select無(wú)限滾動(dòng))

 更新時(shí)間:2024年04月10日 09:54:46   作者:Tencent?IT  
el-select默認(rèn)是不支持虛擬滾動(dòng)的,需要使用第三方插件來(lái)實(shí)現(xiàn)虛擬滾動(dòng)功能,下面這篇文章主要給大家介紹了關(guān)于Vue二次封裝el-select實(shí)現(xiàn)下拉滾動(dòng)加載效果的相關(guān)資料,需要的朋友可以參考下

前言

平時(shí)我們做業(yè)務(wù)需求的時(shí)候,可能會(huì)遇到非常大量的數(shù)據(jù),有時(shí)候成百上千條,一般后端都會(huì)寫(xiě)一個(gè)分頁(yè)的接口,只要我們請(qǐng)求的時(shí)候加上頁(yè)碼參數(shù)即可。

但是在使用element-ui的el-select下拉菜單組件中,官方?jīng)]有提供相應(yīng)的方法進(jìn)行多頁(yè)加載。

這時(shí)候我們可以實(shí)現(xiàn)一個(gè)Vue的自定義指令,每當(dāng)使用el-select滾動(dòng)到列表底部的時(shí)候就請(qǐng)求下一頁(yè)數(shù)據(jù),來(lái)達(dá)到下拉滾動(dòng)加載更多的目的。

實(shí)現(xiàn)自定義指令

首先實(shí)現(xiàn)一個(gè)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 這里可能因?yàn)闉g覽器縮放存在小數(shù)點(diǎn)的情況,導(dǎo)致了滾動(dòng)到底部時(shí)
            // scrollHeight 減去滾動(dòng)到底部時(shí)的scrollTop ,依然大于clientHeight 導(dǎo)致無(wú)法請(qǐng)求更多數(shù)據(jù)
            // 這里將scrollTop向上取整 保證滾到底部時(shí),觸發(fā)調(diào)用
            const CONDITION = this.scrollHeight - Math.ceil(this.scrollTop) <= this.clientHeight;
            // el.scrollTop !== 0 當(dāng)輸入時(shí),如果搜索結(jié)果很少,以至于沒(méi)看到滾動(dòng)條,那么此時(shí)的CONDITION計(jì)算結(jié)果是true,會(huì)執(zhí)行bind.value(),此時(shí)不應(yīng)該執(zhí)行,否則搜索結(jié)果不匹配
            if (CONDITION && this.scrollTop !== 0) {
                binding.value();
            }
        });
    },
});

代碼說(shuō)明:

document.querySelector:querySelector() 方法僅僅返回匹配指定選擇器的第一個(gè)元素。

Element.scrollHeight:在不使用滾動(dòng)條的情況下為了適應(yīng)視口中所用內(nèi)容所需的最小高度(只讀)

警告: 在使用顯示比例縮放的系統(tǒng)上,scrollTop可能會(huì)提供一個(gè)小數(shù)。

Element.scrollTop:獲取或設(shè)置一個(gè)元素的內(nèi)容垂直滾動(dòng)的像素?cái)?shù)。

Element.clientHeight:讀取元素的可見(jiàn)高度(只讀)。

如果元素滾動(dòng)到底,下面等式返回true,沒(méi)有則返回false。

// scrollTop 這里可能因?yàn)闉g覽器縮放不等于100%時(shí),存在小數(shù)點(diǎn)的情況,導(dǎo)致了滾動(dòng)到底部時(shí)

// scrollHeight 減去滾動(dòng)到底部時(shí)的scrollTop ,依然大于clientHeight 導(dǎo)致沒(méi)有觸發(fā)加載事件

// 這里將scrollTop向上取整 保證滾到底部時(shí),觸發(fā)調(diào)用

// 此判斷不準(zhǔn)確: element.scrollHeight - element.scrollTop === element.clientHeight// 使用下面的判斷方式保證

任何縮放都能觸發(fā):

element.scrollHeight - Math.ceil(element.scrollTop) <= element.clientHeight

在項(xiàng)目中全局注冊(cè)v-loadmore指令:

// main.js

import directives from './directive.js'
Vue.use(directives)

最后在組件el-select中使用該指令:

<template>
    <el-select v-model="selected" v-loadmore="loadMore">
        <el-option
            v-for="option in options"
            :label="option.label"
            :value="option.value"
            :key="option.value"
        ></el-option>
    </el-select>
</template>

<script>
export default {
    data() {
        return {
            selected: "",
            options: [
                {
                    label: "1",
                    value: 1
                },
                // ... 此處省略多個(gè)選項(xiàng)
                {
                    label: "到達(dá)底部啦",
                    value: 9
                }
            ]
        };
    },
    methods: {
        loadMore() {
            console.log("more")
        }
    }
};

使用效果如下:

從效果圖可以看出,每當(dāng)菜單列表滾動(dòng)到底部時(shí),指令就會(huì)調(diào)用傳入的loadMore函數(shù),控制臺(tái)隨即打印出 “more”。
注意事項(xiàng):

傳入的數(shù)組個(gè)數(shù)必須大于或者等于8個(gè)選項(xiàng)時(shí)才能讓el-select組件出現(xiàn)下拉滾動(dòng)。
列表里不存在滾動(dòng)時(shí),無(wú)法觸發(fā)傳入指令的函數(shù)。

進(jìn)行二次封裝

滾動(dòng)到底部調(diào)用函數(shù)的指令已經(jīng)實(shí)現(xiàn)了,下面只要調(diào)用接口,把獲取到下一頁(yè)的數(shù)據(jù)拼接到當(dāng)前的數(shù)據(jù)中即可。

接下來(lái)把el-select進(jìn)行二次封裝,封裝成公用的組件之后,傳入必要的參數(shù)就可以在項(xiàng)目中調(diào)用。

首先新建一個(gè)文件load-select.vue:

<template>
    <el-select :value="value" v-loadmore="loadMore" @focus="focus" v-bind="$attrs" v-on="$listeners">
        <el-option
            v-for="option in data"
            :label="option[dictLabel]"
            :value="option[dictValue]"
            :key="option.value"
        ></el-option>
    </el-select>
</template>

<script>
export default {
    props: {
        value: {
            type: String,
            default: ""
        },
        // 列表數(shù)據(jù)
        data: {
            type: Array,
            default: () => []
        },
        dictLabel: {
            type: String,
            default: "label"
        },
        dictValue: {
            type: String,
            default: "value"
        },
        // 調(diào)用頁(yè)數(shù)的接口
        request: {
            type: Function,
            default: () => {}
        },
        page: {
            type: [Number, String],
            default: 1
        }
    },
    data() {
        return {};
    },
    methods: {
        // 請(qǐng)求下一頁(yè)的數(shù)據(jù)
        loadMore() {
            this.request({ page: this.page + 1 })
        },
        // 選中下拉框沒(méi)有數(shù)據(jù)時(shí),自動(dòng)請(qǐng)求第一頁(yè)的數(shù)據(jù)
        focus() {
            if (!this.data.length) {
                this.request({page: 1})
            }
        }
    }
};
</script>

在頁(yè)面組件中調(diào)用load-select.vue:

<!-- page.vue -->

<template>
    <div class="xxx-page">
        <load-select v-model="selected" :data="data" :page="page" :request="getData"></load-select>
    </div>
</template>

<script>
// 導(dǎo)入該組件
import loadSelect from "@/components/load-select/index";

export default {
    name: "app",
    components: {
        loadSelect
    },
    data() {
        return {
            selected: "",
            page: 1,
            more: true,
            data: []
        };
    },
    methods: {
        // 傳入給load-select組件的函數(shù)
        getData({ page = 1 } = {}) {
            // 輸出頁(yè)數(shù)
            console.log(page)
            // 訪(fǎng)問(wèn)后端接口A(yíng)PI
            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 = []
                // 假設(shè)總共的數(shù)據(jù)有50條
                let total = 50; 
                for (let index = 1; index <= size; index++) {
                    // serial:處于第幾個(gè)元素,就顯示多少序號(hào)
                    let serial = index + (page - 1) * size
                    if (serial <= 50) {
                        responseData.push({
                            label: serial,
                            value: serial
                        });
                    }
                }
                // 模擬異步請(qǐng)求,500ms之后返回接口的數(shù)據(jù)
                setTimeout(() => {
                    resolve({
                        total,
                        page,
                        size,
                        result: responseData
                    });
                }, 500);
            });
        }
    }
};
</script>

代碼解析:

首次點(diǎn)擊下拉框時(shí),會(huì)觸發(fā)focus事件請(qǐng)求第一頁(yè)的數(shù)據(jù),之后只要每次滾動(dòng)列表到底部,就會(huì)自動(dòng)請(qǐng)求下一頁(yè)的數(shù)據(jù)然后拼接到當(dāng)前的數(shù)組中。

我們來(lái)看看效果:

完美!但是在實(shí)際使用的過(guò)程中,可能會(huì)因?yàn)榻涌谶€來(lái)不及返回?cái)?shù)據(jù),然后列表又向下滾動(dòng)再次觸發(fā)了請(qǐng)求,結(jié)果就是返回了兩份相同的數(shù)據(jù)。

現(xiàn)在把接口的延遲調(diào)到2000ms重現(xiàn)這個(gè)場(chǎng)景:

在兩次快速滾動(dòng)到底部的時(shí)候,請(qǐng)求的參數(shù)頁(yè)數(shù)都是2,如何解決這個(gè)問(wèn)題?可以在加載函數(shù)中加入一個(gè)攔截操作,在接口沒(méi)有響應(yīng)之前,不調(diào)用加載函數(shù),不過(guò)這樣做要把getData轉(zhuǎn)換成異步函數(shù)的形式。

首先在load-select.vue中的loadMore()中加入一個(gè)攔截操作:

<!-- load-select.vue -->

<template>
    ...
</template>

<script>
    // 請(qǐng)求下一頁(yè)的數(shù)據(jù)
    methods: {
        loadMore() {
            // 如果 intercept 屬性為 true 則不請(qǐng)求數(shù)據(jù)
            if (this.loadMore.intercept) {
                return 
            }
            this.loadMore.intercept = true
            this.request({ page: this.page + 1 }).then(() => {
                // 接口響應(yīng)之后才把 intercept 設(shè)置為 false
                this.loadMore.intercept = false
            })
        }
    }
</script>

然后在page.vue中的getData()函數(shù)轉(zhuǎn)換成異步函數(shù)的形式:

<template>
    ...
</template>

<script>
    methods: {
        // 傳入給load-select組件的函數(shù)
        getData({ page = 1 } = {}) {
            // 返回 Promise 對(duì)象
            return new Promise( resolve => {
                // 訪(fǎng)問(wèn)后端接口A(yíng)PI
                this.requestAPI({ page }).then(res => {
                    this.data = [...this.data, ...res.result]
                    this.page = res.page
                    resolve()
                });
            })
        }, 
    
    }
</script>

現(xiàn)在問(wèn)題來(lái)了:

一般分頁(yè)的接口都支持關(guān)鍵字的搜索,load-select.vue組件能不能加入關(guān)鍵字搜索的功能呢?

關(guān)鍵字搜索功能

還好el-select組件支持遠(yuǎn)程搜索功能,只要傳入filterable和remote參數(shù),具體的可以查看element-ui的官方文檔。

接下來(lái)對(duì)load-select.vue進(jìn)行以下修改:

<!-- load-select.vue -->
<template>
    <el-select
        :value="value"
        v-loadmore="loadMore"
        @focus="focus"
        filterable
        remote
        :filter-method="handleSearch"
        :loading="loading"
        clearable
        v-bind="$attrs"
        v-on="$listeners"
    >
        <el-option
            v-for="option in data"
            :label="option[dictLabel]"
            :value="option[dictValue]"
            :key="option.value"
        ></el-option>
        <!-- 此處加載中的value可以隨便設(shè)置,只要不與其他數(shù)據(jù)重復(fù)即可 -->
        <el-option v-if="hasMore" disabled label="加載中..." value="-1"></el-option>
    </el-select>
</template>

<script>
export default {
    props: {
        value: {
            default: ""
        },
        // 列表數(shù)據(jù)
        data: {
            type: Array,
            default: () => []
        },
        dictLabel: {
            type: String,
            default: "label"
        },
        dictValue: {
            type: String,
            default: "value"
        },
        // 調(diào)用頁(yè)數(shù)的接口
        request: {
            type: Function,
            default: () => {}
        },
        // 傳入的頁(yè)碼
        page: {
            type: [Number, String],
            default: 1
        },
        // 是否還有更多數(shù)據(jù)
        hasMore: {
            type: Boolean,
            default: true
        }
    },
    data() {
        return {
            // 存儲(chǔ)關(guān)鍵字用
            keyword: "", 
            loading: false
        };
    },
    methods: {
        // 請(qǐng)求下一頁(yè)的數(shù)據(jù)
        loadMore() {
            // 如果沒(méi)有更多數(shù)據(jù),則不請(qǐng)求
            if (!this.hasMore) {
                return
            }
            // 如果intercept屬性為true則不請(qǐng)求數(shù)據(jù),
            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
            });
        },
        // 選中下拉框沒(méi)有數(shù)據(jù)時(shí),自動(dòng)請(qǐng)求第一頁(yè)的數(shù)據(jù)
        focus() {
            if (!this.data.length) {
                this.request({ page: 1 })
            }
        },
        // 關(guān)鍵字搜索
        handleSearch(keyword) {
            this.keyword = keyword
            this.loading = true
            this.request({ page: 1, keyword }).then(() => {
                this.loading = false
            });
        },
        // 刪除選中時(shí),如果請(qǐng)求了關(guān)鍵字,則清除關(guān)鍵字再請(qǐng)求第一頁(yè)的數(shù)據(jù)
        clear() {
            if (this.keyword) {
                this.keyword = ""
                this.request({ page: 1 })
            }
        }
    }
};
</script>

頁(yè)面調(diào)用時(shí),getData()請(qǐng)求函數(shù)需要接收keyword和more參數(shù)并進(jìn)行相應(yīng)的處理:

<!-- page.vue -->

<template>
    <div class="xxx-page">
        <load-select v-model="selected" :data="data" :page="page" :hasMore="more" :request="getData"></load-select>
    </div>
</template>

<script>
// 導(dǎo)入該組件
import loadSelect from "@/components/load-select/index";

export default {
    name: "app",
    components: {
        loadSelect
    },
    data() {
        return {
            selected: "",
            page: 1,
            more: true,
            data: []
        };
    },
    methods: {
        // 傳入給load-select組件的函數(shù)
        getData({ page = 1, more = false, keyword = "" } = {}) {
            return new Promise(resolve => {
                // 訪(fǎng)問(wèn)后端接口A(yíng)PI
                this.requestAPI({ page, keyword }).then(res => {
                    // 如果是加載更多,則合并之前的數(shù)據(jù)
                    if (more) {
                        this.data = [...this.data, ...res.result]
                    } else {
                        this.data = res.result
                    }

                    this.page = res.page;
                    let { total, page, size } = res
                    // 如果為最后一頁(yè),則設(shè)置more為false
                    this.more = page * size < total
                    this.page = page
                    resolve()
                });
            });
        },
        // 模擬后端接口的API
        requestAPI({ page = 1, size = 10, keyword = "" } = {}) {
            return new Promise(resolve => {
                // 如果有 keyword 參數(shù),則返回帶有 keyword 的數(shù)據(jù)
                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 = [];
                // 假設(shè)總共的數(shù)據(jù)有50條
                let total = 50; 
                for (let index = 1; index <= size; index++) {
                    // serial:處于第幾個(gè)元素,就顯示多少序號(hào)
                    let serial = index + (page - 1) * size
                    if (serial <= 50) {
                        responseData.push({
                            label: serial,
                            value: serial
                        });
                    }
                }
                setTimeout(() => {
                    resolve({
                        total,
                        page,
                        size,
                        result: responseData
                    })
                }, 500)
            })
        }
    }
};
</script>

接下來(lái)看看搜索關(guān)鍵字的效果:

搜索功能也完成啦!

總結(jié)

為了適用于大部分的請(qǐng)求接口,因此在設(shè)計(jì)這個(gè)組件的時(shí)候只能把請(qǐng)求與組件剝離開(kāi)來(lái),易用程度不算太高,不過(guò)我們可以適當(dāng)?shù)貍魅胍恍┖?jiǎn)單必要的參數(shù)去維持基本地使用。

當(dāng)然,在項(xiàng)目中遇到某些固定的加載請(qǐng)求時(shí),我們也可以對(duì)該組件進(jìn)行再次封裝,具體可以根據(jù)自身的業(yè)務(wù)需求進(jìn)行修改。

到此這篇關(guān)于Vue二次封裝el-select實(shí)現(xiàn)下拉滾動(dòng)加載效果(el-select無(wú)限滾動(dòng))的文章就介紹到這了,更多相關(guān)Vue el-select無(wú)限滾動(dòng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue如何自定義配置運(yùn)行run命令

    vue如何自定義配置運(yùn)行run命令

    這篇文章主要介紹了vue如何自定義配置運(yùn)行run命令,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-04-04
  • Vue.js條件渲染和列表渲染以及Vue中key值的內(nèi)部原理

    Vue.js條件渲染和列表渲染以及Vue中key值的內(nèi)部原理

    這篇文章主要介紹了Vue.js條件渲染和列表渲染,以及Vue中key值的內(nèi)部原理,文中有詳細(xì)的代碼示例,感興趣的同學(xué)可以參考閱讀
    2023-04-04
  • Vue數(shù)據(jù)更新但頁(yè)面沒(méi)有更新的多種情況問(wèn)題及解決

    Vue數(shù)據(jù)更新但頁(yè)面沒(méi)有更新的多種情況問(wèn)題及解決

    這篇文章主要介紹了Vue數(shù)據(jù)更新但頁(yè)面沒(méi)有更新的多種情況問(wèn)題及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-07-07
  • vue2.0實(shí)現(xiàn)檢測(cè)無(wú)用的代碼并刪除

    vue2.0實(shí)現(xiàn)檢測(cè)無(wú)用的代碼并刪除

    這篇文章主要介紹了vue2.0實(shí)現(xiàn)檢測(cè)無(wú)用的代碼并刪除方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • vue的傳參方式匯總和router使用技巧

    vue的傳參方式匯總和router使用技巧

    這篇文章主要介紹了vue的傳參方式和router使用技巧,本文給大家列舉了好幾種傳參方式,需要的朋友可以參考下
    2018-05-05
  • Vue3 框架Arco Design詳解

    Vue3 框架Arco Design詳解

    Arco Design 作為一款專(zhuān)為 Vue3 打造的企業(yè)級(jí) UI 組件庫(kù),以其豐富的特性和卓越的性能,成為現(xiàn)代 Web 應(yīng)用開(kāi)發(fā)者值得關(guān)注的選擇,這篇文章主要介紹了Vue3 框架Arco Design詳解,需要的朋友可以參考下
    2024-08-08
  • vue2中l(wèi)ess的安裝以及使用教程

    vue2中l(wèi)ess的安裝以及使用教程

    less是css預(yù)處理器,對(duì)原先css進(jìn)行了擴(kuò)展和補(bǔ)充,下面這篇文章主要給大家介紹了關(guān)于vue2中l(wèi)ess的安裝以及使用的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-12-12
  • Vue.js彈出模態(tài)框組件開(kāi)發(fā)的示例代碼

    Vue.js彈出模態(tài)框組件開(kāi)發(fā)的示例代碼

    本篇文章主要介紹了Vue.js彈出模態(tài)框組件開(kāi)發(fā)的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-07-07
  • Vue.js學(xué)習(xí)之計(jì)算屬性

    Vue.js學(xué)習(xí)之計(jì)算屬性

    Vue.js 的內(nèi)聯(lián)表達(dá)式非常方便,但它最合適的使用場(chǎng)景是簡(jiǎn)單的布爾操作或字符串拼接。如果涉及更復(fù)雜的邏輯,你應(yīng)該使用計(jì)算屬性。下面這篇文章主要給大家介紹了Vue.js中的計(jì)算屬性,需要的朋友可以參考借鑒,一起來(lái)看看吧。
    2017-01-01
  • vue如何動(dòng)態(tài)配置ip與端口

    vue如何動(dòng)態(tài)配置ip與端口

    這篇文章主要介紹了vue如何動(dòng)態(tài)配置ip與端口,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09

最新評(píng)論