feat: 更新无限加载组件
All checks were successful
/ build-and-deploy-to-vercel (push) Successful in 1m24s
/ playwright (push) Successful in 2m6s
/ depcheck (push) Successful in 1m38s

This commit is contained in:
严浩
2024-12-08 00:04:31 +08:00
parent 2574e38d9a
commit 5f98fe12ba
4 changed files with 55 additions and 127 deletions

View File

@ -17,15 +17,22 @@ function checkIsVisible(el: Element, root: Element | null = null) {
</script>
<script setup lang="ts">
const target = ref(null);
// defineSlots
const state = ref<'' | 'loading' | 'loaded' | 'complete' | 'error'>(''); // TODO: use ts-enum-util
const props = defineProps<{
asyncLoad: () => Promise<{ hasMore: boolean }>;
}>();
defineSlots<{
loading(): unknown;
complete(): unknown;
error(props: { retry: () => void }): unknown;
}>();
const load = async () => {
const target = ref(null);
const state = ref<'' | 'loading' | 'loaded' | 'complete' | 'error'>(''); // TODO: use ts-enum-util
const load = async (why?: string) => {
console.group('load');
console.debug(`why :>> `, why);
console.groupEnd();
if (state.value === 'loading') return;
state.value = 'loading';
@ -35,7 +42,7 @@ const load = async () => {
if (hasMore) {
await nextTick();
if (checkIsVisible(target.value!)) {
load();
load('visible after load');
}
}
@ -51,7 +58,7 @@ const { pause /* , resume, isSupported, isActive */ } = useIntersectionObserver(
target,
([entry]) => {
if (entry?.isIntersecting) {
load();
load('visible in observer');
}
},
{
@ -66,21 +73,45 @@ const { pause /* , resume, isSupported, isActive */ } = useIntersectionObserver(
<template>
<div class="infinite-loading" ref="target">
<slot v-if="state === 'complete'" name="complete">
<span>没有更多了</span>
</slot>
<div v-if="state === 'complete'" class="infinite-loading__complete">
<slot name="complete">
<span>没有更多了</span>
</slot>
</div>
<template v-else>
<div v-show="state == 'loading'">
<div v-show="state == 'loading'" class="infinite-loading__loading">
<slot name="loading">
<span> 加载中... </span>
</slot>
</div>
<slot v-if="state === 'error'" name="error">
<div>
<span> 加载失败 </span>
<button @click="load">重试</button>
</div>
</slot>
<div
v-if="state === 'error'"
class="infinite-loading__error"
@click="
() => {
!$slots.error && load('click error');
}
"
>
<slot name="error" :retry="load">
<span> 加载失败点击重试 </span>
</slot>
</div>
</template>
</div>
</template>
<style scoped>
.infinite-loading__loading,
.infinite-loading__complete,
.infinite-loading__error {
display: flex;
justify-content: center;
align-items: center;
min-height: 40px;
color: #666;
}
.infinite-loading__error {
cursor: pointer;
}
</style>