mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-03-17 04:13:17 +08:00
✨ 浮窗触底加载
This commit is contained in:
@@ -10,9 +10,8 @@
|
||||
</div>
|
||||
<div class="vo-of-actions">
|
||||
<v-btn class="vo-of-btn" @click="loadMore(true)" :loading="loading">刷新</v-btn>
|
||||
<v-btn class="vo-of-btn" @click="loadMore()" :loading="loading">加载更多</v-btn>
|
||||
</div>
|
||||
<div class="vp-of-list">
|
||||
<div class="vp-of-list" ref="listRef">
|
||||
<TPostcard
|
||||
class="vp-of-list-item"
|
||||
v-for="(item, index) in posts"
|
||||
@@ -27,18 +26,31 @@
|
||||
import TOverlay from "@comp/app/t-overlay.vue";
|
||||
import TPostcard from "@comp/app/t-postcard.vue";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import { useBoxReachBottom } from "@hooks/reachBottom.js";
|
||||
import painterReq from "@req/painterReq.js";
|
||||
import useUserStore from "@store/user.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { ref, shallowRef, watch } from "vue";
|
||||
import { ref, shallowRef, useTemplateRef, watch } from "vue";
|
||||
|
||||
const { cookie } = storeToRefs(useUserStore());
|
||||
|
||||
const listEl = useTemplateRef<HTMLElement>("listRef");
|
||||
const { isReachBottom } = useBoxReachBottom(listEl);
|
||||
|
||||
const visible = defineModel<boolean>();
|
||||
|
||||
const offset = ref<number>();
|
||||
const isLast = ref<boolean>(false);
|
||||
const loading = ref<boolean>(false);
|
||||
const posts = shallowRef<Array<TGApp.BBS.Post.FullData>>([]);
|
||||
|
||||
watch(
|
||||
() => isReachBottom.value,
|
||||
async () => {
|
||||
if (!isReachBottom.value) return;
|
||||
await loadMore();
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => visible.value,
|
||||
async () => {
|
||||
@@ -71,6 +83,9 @@ async function loadMore(refresh: boolean = false): Promise<void> {
|
||||
else posts.value = posts.value.concat(resp.list);
|
||||
loading.value = false;
|
||||
showSnackbar.success(`成功加载${resp.list.length}条数据`);
|
||||
if (refresh && listEl.value) {
|
||||
listEl.value.scrollTo({ top: 0, behavior: "smooth" });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -4,12 +4,9 @@
|
||||
<div class="tops-top">查找:{{ search }}</div>
|
||||
<div class="tops-act">
|
||||
<span>分区:{{ label }}</span>
|
||||
<v-btn :loading="load" size="small" class="tops-btn" @click="searchPosts()" rounded>
|
||||
加载更多({{ results.length }})
|
||||
</v-btn>
|
||||
</div>
|
||||
<div class="tops-divider" />
|
||||
<div class="tops-list">
|
||||
<div class="tops-list" ref="listRef">
|
||||
<TPostCard
|
||||
class="tops-item"
|
||||
:model-value="item"
|
||||
@@ -24,15 +21,19 @@
|
||||
import TOverlay from "@comp/app/t-overlay.vue";
|
||||
import TPostCard from "@comp/app/t-postcard.vue";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import { useBoxReachBottom } from "@hooks/reachBottom.js";
|
||||
import postReq from "@req/postReq.js";
|
||||
import useBBSStore from "@store/bbs.js";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, onMounted, ref, shallowRef, watch } from "vue";
|
||||
import { computed, onMounted, ref, shallowRef, useTemplateRef, watch } from "vue";
|
||||
|
||||
type ToPostSearchProps = { gid: string; keyword?: string };
|
||||
|
||||
const { gameList } = storeToRefs(useBBSStore());
|
||||
|
||||
const listEl = useTemplateRef<HTMLElement>("listRef");
|
||||
const { isReachBottom } = useBoxReachBottom(listEl);
|
||||
|
||||
const props = defineProps<ToPostSearchProps>();
|
||||
const visible = defineModel<boolean>();
|
||||
|
||||
@@ -54,6 +55,13 @@ onMounted(async () => {
|
||||
if (visible.value) await searchPosts();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => isReachBottom.value,
|
||||
async () => {
|
||||
if (!isReachBottom.value) return;
|
||||
await searchPosts();
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => visible.value,
|
||||
async () => {
|
||||
@@ -118,6 +126,7 @@ async function searchPosts(): Promise<void> {
|
||||
isLast.value = res.is_last;
|
||||
load.value = false;
|
||||
if (!visible.value) visible.value = true;
|
||||
showSnackbar.success(`成功加载${res.posts.length}条数据`);
|
||||
}
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
@@ -181,10 +190,4 @@ async function searchPosts(): Promise<void> {
|
||||
height: fit-content;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tops-btn {
|
||||
width: fit-content;
|
||||
background: var(--tgc-btn-1);
|
||||
color: var(--btn-text);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -21,16 +21,13 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="vp-ou-mid">
|
||||
<v-btn :loading="load" size="small" class="vp-ou-btn" @click="loadPosts()" rounded>
|
||||
加载更多({{ results.length }})
|
||||
</v-btn>
|
||||
<div class="vp-ouu-extra" v-if="userInfo">
|
||||
<span>ID:{{ userInfo.uid }}</span>
|
||||
<span>IP:{{ userInfo.ip_region }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="vp-ou-divider" />
|
||||
<div class="vp-ou-list">
|
||||
<div class="vp-ou-list" ref="listRef">
|
||||
<TPostCard
|
||||
class="vp-ou-item"
|
||||
:model-value="item"
|
||||
@@ -46,12 +43,16 @@ import TMiImg from "@comp/app/t-mi-img.vue";
|
||||
import TOverlay from "@comp/app/t-overlay.vue";
|
||||
import TPostCard from "@comp/app/t-postcard.vue";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import { useBoxReachBottom } from "@hooks/reachBottom.js";
|
||||
import bbsReq from "@req/bbsReq.js";
|
||||
import postReq from "@req/postReq.js";
|
||||
import { computed, ref, shallowRef, watch } from "vue";
|
||||
import { computed, ref, shallowRef, useTemplateRef, watch } from "vue";
|
||||
|
||||
type ToPostUserProps = { gid: number; uid: string; postId?: string };
|
||||
|
||||
const listEl = useTemplateRef<HTMLElement>("listRef");
|
||||
const { isReachBottom } = useBoxReachBottom(listEl);
|
||||
|
||||
const props = defineProps<ToPostUserProps>();
|
||||
const visible = defineModel<boolean>();
|
||||
const offset = ref<string>();
|
||||
@@ -69,6 +70,13 @@ const levelColor = computed<string>(() => {
|
||||
return "var(--tgc-od-white)";
|
||||
});
|
||||
|
||||
watch(
|
||||
() => isReachBottom.value,
|
||||
async () => {
|
||||
if (!isReachBottom.value) return;
|
||||
await loadPosts();
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => visible.value,
|
||||
async () => {
|
||||
@@ -117,6 +125,7 @@ async function loadPosts(): Promise<void> {
|
||||
isLast.value = resp.is_last;
|
||||
results.value = results.value.concat(resp.list);
|
||||
load.value = false;
|
||||
showSnackbar.success(`成功加载${resp.list.length}条数据`);
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@@ -260,12 +269,6 @@ async function loadPosts(): Promise<void> {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.vp-ou-btn {
|
||||
width: fit-content;
|
||||
background: var(--tgc-btn-1);
|
||||
color: var(--btn-text);
|
||||
}
|
||||
|
||||
.vp-ouu-extra {
|
||||
position: relative;
|
||||
display: flex;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @since Beta v0.7.7
|
||||
*/
|
||||
|
||||
import { onMounted, onUnmounted, ref, Ref } from "vue";
|
||||
import { onMounted, onUnmounted, ref, Ref, TemplateRef, watch } from "vue";
|
||||
|
||||
type ReachBottomReturn = {
|
||||
isReachBottom: Ref<boolean>;
|
||||
@@ -39,3 +39,39 @@ export function usePageReachBottom(): ReachBottomReturn {
|
||||
});
|
||||
return { isReachBottom };
|
||||
}
|
||||
|
||||
/**
|
||||
* @function useBoxReachBottom
|
||||
* @description 元素触底检测
|
||||
* @since Beta v0.7.7
|
||||
* @param {TemplateRef<HTMLElement>} boxRef - 需要检测的元素引用
|
||||
* @return ReachBottomReturn
|
||||
*/
|
||||
export function useBoxReachBottom(boxRef: TemplateRef<HTMLElement>): ReachBottomReturn {
|
||||
const isReachBottom = ref<boolean>(false);
|
||||
|
||||
const handleScroll = () => {
|
||||
if (!boxRef.value) return;
|
||||
const scrollTop = boxRef.value.scrollTop;
|
||||
const clientHeight = boxRef.value.clientHeight;
|
||||
const scrollHeight = boxRef.value.scrollHeight;
|
||||
|
||||
// 检测是否触底
|
||||
isReachBottom.value = scrollTop + clientHeight >= scrollHeight - 1; // 减1是为了避免浮点数误差
|
||||
};
|
||||
watch(
|
||||
() => boxRef.value,
|
||||
() => {
|
||||
if (!boxRef.value) return;
|
||||
boxRef.value.addEventListener("scroll", handleScroll);
|
||||
handleScroll();
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
onUnmounted(() => {
|
||||
boxRef.value?.removeEventListener("scroll", handleScroll);
|
||||
});
|
||||
|
||||
return { isReachBottom };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user