页面触底加载

This commit is contained in:
BTMuli
2025-06-15 15:16:25 +08:00
parent 7fb13635f2
commit 848227f6b0
5 changed files with 68 additions and 36 deletions

41
src/hooks/reachBottom.ts Normal file
View File

@@ -0,0 +1,41 @@
/**
* @file hooks/reachBottom.ts
* @description 触底检测
* @since Beta v0.7.7
*/
import { onMounted, onUnmounted, ref, Ref } from "vue";
type ReachBottomReturn = {
isReachBottom: Ref<boolean>;
};
/**
* @function usePageReachBottom
* @description 页面触底检测
* @since Beta v0.7.7
* @return ReachBottomReturn
*/
export function usePageReachBottom(): ReachBottomReturn {
const isReachBottom = ref<boolean>(false);
const handleScroll = () => {
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
const clientHeight = document.documentElement.clientHeight || document.body.clientHeight;
const scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight;
// 检测是否触底
isReachBottom.value = scrollTop + clientHeight >= scrollHeight - 1; // 减1是为了避免浮点数误差
};
// 监听滚动事件
onMounted(() => {
window.addEventListener("scroll", handleScroll);
// 初始检测
handleScroll();
});
onUnmounted(() => {
window.removeEventListener("scroll", handleScroll);
});
return { isReachBottom };
}

View File

@@ -115,12 +115,6 @@
<TPostCard :model-value="post" :user-click="true" @onUserClick="handleUserClick" />
</div>
</div>
<div class="posts-load-more">
<v-btn class="post-forum-btn" :rounded="true" @click="loadMore()">
已加载{{ posts.length }}
{{ postRaw.isLast ? "已加载完毕" : "加载更多" }}
</v-btn>
</div>
<VpOverlaySearch :gid="curGid.toString()" v-model="showSearch" :keyword="search" />
<VpOverlayUser v-model="showUser" :gid="curGid" :uid="curUid" />
</template>
@@ -132,6 +126,7 @@ import showLoading from "@comp/func/loading.js";
import showSnackbar from "@comp/func/snackbar.js";
import VpOverlaySearch from "@comp/viewPost/vp-overlay-search.vue";
import VpOverlayUser from "@comp/viewPost/vp-overlay-user.vue";
import { usePageReachBottom } from "@hooks/reachBottom.js";
import ApiHubReq from "@req/apiHubReq.js";
import painterReq from "@req/painterReq.js";
import useBBSStore from "@store/bbs.js";
@@ -158,6 +153,8 @@ const curSortType = ref<number>(1);
const firstLoad = ref<boolean>(false);
const isReq = ref<boolean>(false);
const { isReachBottom } = usePageReachBottom();
const search = ref<string>("");
const showSearch = ref<boolean>(false);
const curUid = ref<string>("");
@@ -193,6 +190,13 @@ onMounted(async () => {
await freshPostData();
firstLoad.value = true;
});
watch(
() => isReachBottom.value,
async () => {
if (!isReachBottom.value) return;
await loadMore();
},
);
watch(
() => curGid.value,
() => {
@@ -403,15 +407,6 @@ function handleUserClick(user: TGApp.BBS.Post.User, gid: number): void {
grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
}
.posts-load-more {
display: flex;
align-items: center;
justify-content: center;
margin: 10px;
font-family: var(--font-title);
transition: all 0.3s linear;
}
.select-item {
position: relative;
display: flex;

View File

@@ -50,12 +50,6 @@
<TPostCard :model-value="item" />
</div>
</div>
<div class="load-news">
<v-btn class="post-news-btn" :rounded="true" :loading="loading" @click="loadMore(value)">
已加载{{ rawData[value].lastId }}
{{ rawData[value].isLast ? "已加载完毕" : "加载更多" }}
</v-btn>
</div>
</v-window-item>
</v-window>
<ToChannel v-model="showList" :gid="gid" />
@@ -67,13 +61,14 @@ import showLoading from "@comp/func/loading.js";
import showSnackbar from "@comp/func/snackbar.js";
import ToChannel from "@comp/pageNews/to-channel.vue";
import VpOverlaySearch from "@comp/viewPost/vp-overlay-search.vue";
import { usePageReachBottom } from "@hooks/reachBottom.js";
import painterReq from "@req/painterReq.js";
import useAppStore, { type NewsType } from "@store/app.js";
import useBBSStore from "@store/bbs.js";
import TGLogger from "@utils/TGLogger.js";
import { createPost } from "@utils/TGWindow.js";
import { storeToRefs } from "pinia";
import { computed, onMounted, reactive, Ref, ref, shallowRef } from "vue";
import { computed, onMounted, reactive, Ref, ref, shallowRef, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
type PostData = { [key in NewsType]: Ref<Array<TGApp.BBS.Post.FullData>> };
@@ -85,6 +80,8 @@ const { recentNewsType } = storeToRefs(useAppStore());
const { gameList } = storeToRefs(useBBSStore());
const { gid } = <{ gid: string }>useRoute().params;
const { isReachBottom } = usePageReachBottom();
const tabValues: Readonly<Array<NewsType>> = ["notice", "activity", "news"];
const tabMap: Readonly<Record<NewsType, string>> = { notice: "1", activity: "2", news: "3" };
const label = computed<string>(() => {
@@ -113,6 +110,14 @@ const tab = computed<NewsType>({
onMounted(async () => await firstLoad(tab.value));
watch(
() => isReachBottom.value,
async () => {
if (!isReachBottom.value) return;
await loadMore(tab.value);
},
);
async function firstLoad(key: NewsType, refresh: boolean = false): Promise<void> {
if (loading.value) return;
loading.value = true;
@@ -191,7 +196,7 @@ async function searchPost(): Promise<void> {
showSearch.value = false;
}
</script>
<style lang="css" scoped>
<style lang="scss" scoped>
.news-tab {
margin-bottom: 10px;
color: var(--common-text-title);
@@ -230,15 +235,4 @@ async function searchPost(): Promise<void> {
grid-gap: 8px;
grid-template-columns: repeat(auto-fill, minmax(360px, 1fr));
}
/* load more */
.load-news {
display: flex;
align-items: center;
justify-content: center;
margin: 10px;
font-family: var(--font-title);
transition: all 0.3s linear;
}
</style>

View File

@@ -23,14 +23,15 @@
"@styles/*": ["src/assets/styles/*"],
"@comp/*": ["src/components/*"],
"@enum/*": ["src/enum/*"],
"@hooks/*": ["src/hooks/*"],
"@Bili/*": ["src/plugins/Bili/*"],
"@Hutao/*": ["src/plugins/Hutao/*"],
"@Mys/*": ["src/plugins/Mys/*"],
"@Sql/*": ["src/plugins/Sqlite/*"],
"@Sqlm/*": ["src/plugins/Sqlite/modules/*"],
"@utils/*": ["src/utils/*"],
"@req/*": ["src/request/*"],
"@store/*": ["src/store/modules/*"],
"@req/*": ["src/request/*"]
"@utils/*": ["src/utils/*"]
}
},
"include": [

View File

@@ -22,6 +22,7 @@ export default defineConfig({
"@styles/": "/src/assets/styles/",
"@comp/": "/src/components/",
"@enum/": "/src/enum/",
"@hooks/": "/src/hooks/",
"@Bili/": "/src/plugins/Bili/",
"@Hutao/": "/src/plugins/Hutao/",
"@Mys/": "/src/plugins/Mys/",