為 el-select 組件添加滾動(dòng)加載更多的功能

轉(zhuǎn)載請(qǐng)注明出處轿塔,點(diǎn)擊此處 查看更多精彩內(nèi)容

el-selectelement-ui 組件庫(kù)提供的下拉選擇菜單組件。

在項(xiàng)目中,我們展示到 el-select 的數(shù)據(jù)通常是從服務(wù)端獲取的票腰,如果服務(wù)端的查詢較慢或者數(shù)據(jù)量過(guò)大姜凄,就會(huì)導(dǎo)致在前端的顯示很慢找默,特別是在網(wǎng)絡(luò)不好的時(shí)候更是如此。

所以崩掘,分頁(yè)展示就是一種較好的交互體驗(yàn)了七嫌,可惜的是 el-select 組件并沒(méi)有提供分頁(yè)的功能。

本著不重復(fù)造輪子(懶)的原則苞慢,在網(wǎng)上逛了一圈诵原,發(fā)現(xiàn)現(xiàn)有實(shí)現(xiàn)方案基本都是基于 el-select 封裝了新的組件,這可能導(dǎo)致 el-select 組件的部分功能不可用挽放,并且不是很靈活绍赛。

算啦,動(dòng)手做一個(gè)吧辑畦。

實(shí)現(xiàn)效果

效果圖

實(shí)現(xiàn)思路

  • 自定義一個(gè)組件 ElSelectLoading.vue吗蚌,由用戶自行插入到 el-select 組件菜單的底部。
  • 使用 IntersectionObserver 監(jiān)聽(tīng)當(dāng)前組件是否出現(xiàn)在可見(jiàn)范圍纯出,可見(jiàn)時(shí)觸發(fā)加載數(shù)據(jù)的事件蚯妇。
  • 用戶監(jiān)聽(tīng)事件加載新數(shù)據(jù),對(duì) el-select 的功能沒(méi)有影響暂筝。

這個(gè)思路也適用于其他的列表監(jiān)聽(tīng)滾動(dòng)觸底加載更多數(shù)據(jù)箩言。

實(shí)現(xiàn)代碼

<!-- 監(jiān)聽(tīng) el-select 的滾動(dòng),并提供觸底加載數(shù)據(jù)的回調(diào) -->
<template>
  <el-option ref="el" class="el-select-loading" value="">
    <template v-if="hasMore">
      <el-icon class="el-select-loading__icon"><Loading /></el-icon>
      <span class="el-select-loading__tips">{{ loadingText || "正在加載" }}</span>
    </template>
    <template v-else>{{ noMoreText || "到底了~" }}</template>
  </el-option>
</template>

<script setup lang="ts">
import { onMounted, onUnmounted, ref } from "vue";
import { ElOption } from "element-plus";

interface Props {
  // 當(dāng)前頁(yè)碼
  page: number;
  // 是否加載中焕襟,用來(lái)過(guò)濾重復(fù)的加載
  loading: boolean;
  // 加載中的提示文案
  loadingText?: string;
  // 是否有更多數(shù)據(jù)可加載
  hasMore: boolean;
  // 沒(méi)有更多數(shù)據(jù)的提示文案
  noMoreText?: string;
}

const props = defineProps<Props>();

interface Emits {
  (event: "loadMore", data: number): any;
}

const emit = defineEmits<Emits>();

const el = ref<typeof ElOption>();
const observer = ref<IntersectionObserver>();

// 組件加載成功陨收,監(jiān)聽(tīng)滾動(dòng)
onMounted(() => {
  if (!el.value) {
    return;
  }
  const callback: IntersectionObserverCallback = (entries) => {
    if (props.loading || !props.hasMore || !entries[0].isIntersecting) {
      return;
    }
    emit("loadMore", props.page + 1);
  };
  const options: IntersectionObserverInit = {
    root: el.value.$el.parentElement?.parentElement,
    rootMargin: "0px 0px 0px 0px",
  };
  observer.value = new IntersectionObserver(callback, options);
  observer.value.observe(el.value.$el);
});

// 組件卸載成功,取消滾動(dòng)監(jiān)聽(tīng)
onUnmounted(() => {
  if (!el.value) {
    return;
  }
  observer.value?.unobserve(el.value.$el);
});
</script>

<style lang="scss" scoped>
.el-select-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: initial;
  pointer-events: none;
  color: var(--el-color-info);
  font-size: 12px;

  &__icon {
    font-size: 16px;
    animation: rotate 1.5s linear infinite;
  }

  &__tips {
    margin-left: 6px;
  }

  @keyframes rotate {
    from {
      transform: rotate(0deg);
    }
    to {
      transform: rotate(360deg);
    }
  }
}
</style>

為什么根組件使用 el-option 而不是 div 或其他標(biāo)簽鸵赖?
這是因?yàn)?el-select 在內(nèi)部沒(méi)有任何 el-option 的時(shí)候不會(huì)渲染菜單浮層务漩,如果使用 div拄衰,組件可能會(huì)沒(méi)有機(jī)會(huì)渲染。

Props:

參數(shù)名稱 說(shuō)明 類型 默認(rèn)值
page 當(dāng)前頁(yè)碼 number -
loading 是否加載中菲饼,用來(lái)過(guò)濾重復(fù)的加載 boolean -
loadingText 加載中的提示文案 string 正在加載
hasMore 是否有更多數(shù)據(jù)可加載 boolean -
noMoreText 沒(méi)有更多數(shù)據(jù)的提示文案 string 到底了~

Emits:

事件名稱 說(shuō)明 回調(diào)參數(shù)
loadMore 觸底可加載數(shù)據(jù)時(shí)觸發(fā) (newPage: number)

使用示例

<template>
  <el-select placeholder="請(qǐng)選擇" v-model="selectValue">
    <el-option
      v-for="item in selectOptions"
      :key="item.id"
      :label="item.name"
      :value="item.id"
    />
    <ElSelectLoading
      :page="page"
      :loading="loading"
      :hasMore="hasMore"
      @loadMore="handleLoadMore"
    />
  </el-select>
</template>

<script setup lang="ts">
import { ref } from "vue";
import ElSelectLoading from "@/components/ElSelectLoading.vue";

const page = ref(0);
const loading = ref(false);
const hasMore = ref(true);

const selectValue = ref<number>();
const selectOptions = ref<any[]>([]);

/**
 * 加載數(shù)據(jù)列表
 */
const loadDataList = async (newPage: number) => {
  try {
    loading.value = true;
    const res = await pageRequest();
    const list = res.data.list || [];
    if (newPage === 1) {
      selectOptions.value = [];
    }
    selectOptions.value.push(...list);
    hasMore.value = selectOptions.value.length < res.data.total;
    page.value = newPage;
  } catch (err) {
    console.error(err);
  } finally {
    loading.value = false;
  }
};

/**
 * 加載更多數(shù)據(jù)
 */
const handleLoadMore = async (newPage: number) => {
  await loadDataList(newPage);
};
</script>

<style lang="scss" scoped></style>

觀察代碼可以發(fā)現(xiàn)肾砂,在菜單底部插入了 ElSelectLoading 組件,并在加載數(shù)據(jù)時(shí)更新對(duì)應(yīng)的狀態(tài)宏悦。

注意: 每次 loadMore 事件回調(diào)的新頁(yè)碼參數(shù)都是由組件 props.page + 1 得到的镐确,因此,

  1. page 參數(shù)的值應(yīng)該由 0 開(kāi)始饼煞。
  2. page.value 的更新應(yīng)該放在數(shù)據(jù)加載成功后源葫,以防加載失敗后重新加載時(shí)頁(yè)碼錯(cuò)誤。

如果項(xiàng)目中有多個(gè)功能需要分頁(yè)加載砖瞧,也可以自行基于 el-selectElSelectLoading 做封裝息堂。

分頁(yè)時(shí)數(shù)據(jù)回顯問(wèn)題的解決方案

默認(rèn)情況下要回顯的數(shù)據(jù)在菜單里不存在時(shí) el-select 會(huì)把 value 展示出來(lái),在分頁(yè)加載中這種情況是很常見(jiàn)的块促,對(duì)用戶很不友好荣堰,需要處理一下。

以下方案都建立在回顯時(shí)已拿到選中項(xiàng)的 valuelabel 值的前提下竭翠。

方案一:模擬回顯

如果是單選的話振坚,我們可以用 absolute 定位元素覆蓋到 el-select 組件上模擬回顯 label 值,可以完美回顯斋扰。

多選的話模擬起來(lái)很麻煩渡八,要考慮高度、刪除等問(wèn)題传货,建議不要用 el-select 組件了屎鳍,或者看一下方案二吧。

方案二:手動(dòng)處理數(shù)據(jù)

根據(jù)要回顯的 valuelabel 組建一個(gè)列表并插入到第一頁(yè)问裕,后續(xù)分頁(yè)加載時(shí)從列表中刪除重復(fù)數(shù)據(jù)逮壁。

該方案的缺點(diǎn)也很明顯:

  1. 回顯時(shí)會(huì)把原本分散的選中數(shù)據(jù)集中到最前面,有點(diǎn)違反直覺(jué)粮宛,如果原列表有排序的話貌踏,還會(huì)導(dǎo)致順序混亂。
  2. 從后續(xù)分頁(yè)中刪除已回顯的重復(fù)數(shù)據(jù)后窟勃,本頁(yè)加載到的有效數(shù)據(jù)量會(huì)小于 pagesSize,甚至出現(xiàn)為 0 的情況逗堵。

回顯方案總結(jié)

綜上所述秉氧,在列表篩選項(xiàng)等不需要回顯的場(chǎng)景或者【單選+回顯】場(chǎng)景下使用分頁(yè)加載是比較合適的,【多選+回顯】的情況不建議使用 el-select 分頁(yè)加載蜒秤,可以考慮用 dialog + table 去做(需要和產(chǎn)品經(jīng)理 battle 一下)汁咏,或者去找一找有沒(méi)有完善的帶回顯功能的分頁(yè)下拉菜單組件亚斋。

如果大家有好的回顯方案,也可以到評(píng)論區(qū)分享一下攘滩。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末帅刊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子漂问,更是在濱河造成了極大的恐慌赖瞒,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蚤假,死亡現(xiàn)場(chǎng)離奇詭異栏饮,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)磷仰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門袍嬉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人灶平,你說(shuō)我怎么就攤上這事伺通。” “怎么了逢享?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵罐监,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我拼苍,道長(zhǎng)笑诅,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任疮鲫,我火速辦了婚禮吆你,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘俊犯。我一直安慰自己妇多,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布燕侠。 她就那樣靜靜地躺著者祖,像睡著了一般。 火紅的嫁衣襯著肌膚如雪绢彤。 梳的紋絲不亂的頭發(fā)上七问,一...
    開(kāi)封第一講書(shū)人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音茫舶,去河邊找鬼械巡。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的讥耗。 我是一名探鬼主播有勾,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼古程!你這毒婦竟也來(lái)了蔼卡?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤挣磨,失蹤者是張志新(化名)和其女友劉穎雇逞,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體趋急,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡喝峦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了呜达。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谣蠢。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖查近,靈堂內(nèi)的尸體忽然破棺而出眉踱,到底是詐尸還是另有隱情,我是刑警寧澤霜威,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布谈喳,位于F島的核電站,受9級(jí)特大地震影響戈泼,放射性物質(zhì)發(fā)生泄漏婿禽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一大猛、第九天 我趴在偏房一處隱蔽的房頂上張望扭倾。 院中可真熱鬧,春花似錦挽绩、人聲如沸膛壹。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)模聋。三九已至,卻和暖如春唠亚,著一層夾襖步出監(jiān)牢的瞬間链方,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工灶搜, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留侄柔,地道東北人共啃。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像暂题,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子究珊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容