mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-03-19 04:29:45 +08:00
🚸 优化回复浮窗处理 (#169)
* Initial plan * Fix secondary reply scroll position issue by adding scroll-strategy="close" to submenu Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com> * Add auto-load on scroll for reply and sub-reply lists Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com> * Fix sub-reply scroll issues with custom event-based solution Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com> * Fix sub-reply initialization to use embedded sub_replies data Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com> * 🎨 codeStyle * Fix duplicate sub-reply data by filtering existing reply IDs Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com> * Use persistent Set for existingIds to improve duplicate filtering efficiency Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: BTMuli <72692909+BTMuli@users.noreply.github.com> Co-authored-by: BTMuli <bt-muli@outlook.com>
This commit is contained in:
@@ -51,7 +51,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<v-list class="tpr-reply-list">
|
||||
<v-list class="tpr-reply-list" @scroll="handleListScroll">
|
||||
<VpReplyItem
|
||||
v-for="(item, index) in reply"
|
||||
:key="index"
|
||||
@@ -75,6 +75,7 @@
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import postReq from "@req/postReq.js";
|
||||
import useAppStore from "@store/app.js";
|
||||
import { emit } from "@tauri-apps/api/event";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed, ref, shallowRef, watch } from "vue";
|
||||
|
||||
@@ -117,6 +118,22 @@ watch(
|
||||
},
|
||||
);
|
||||
|
||||
async function handleListScroll(e: Event): Promise<void> {
|
||||
const target = <HTMLElement>e.target;
|
||||
if (!target) return;
|
||||
// Emit event to close sub-reply menus when parent scrolls
|
||||
await emit("closeReplySub");
|
||||
// Check if scrolled to bottom for auto-load
|
||||
const scrollTop = target.scrollTop;
|
||||
const clientHeight = target.clientHeight;
|
||||
const scrollHeight = target.scrollHeight;
|
||||
if (scrollTop + clientHeight >= scrollHeight - 1) {
|
||||
if (!loading.value && !isLast.value) {
|
||||
await loadReply();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function showReply(): Promise<void> {
|
||||
if (reply.value.length > 0) return;
|
||||
if (isLast.value) return;
|
||||
|
||||
@@ -52,7 +52,12 @@
|
||||
:close-on-content-click="false"
|
||||
v-model="showSub"
|
||||
>
|
||||
<v-list class="tpr-reply-sub" width="300px" max-height="400px">
|
||||
<v-list
|
||||
class="tpr-reply-sub"
|
||||
width="300px"
|
||||
max-height="400px"
|
||||
@scroll="handleSubScroll"
|
||||
>
|
||||
<VpReplyItem
|
||||
v-for="(reply, index) in subReplies"
|
||||
:key="index"
|
||||
@@ -121,6 +126,7 @@ type TprReplyProps =
|
||||
const props = defineProps<TprReplyProps>();
|
||||
const replyId = `reply_${props.modelValue.reply.post_id}_${props.modelValue.reply.floor_id}_${props.modelValue.reply.reply_id}`;
|
||||
let subListener: UnlistenFn | null = null;
|
||||
let closeSubListener: UnlistenFn | null = null;
|
||||
|
||||
console.log("TprReply", toRaw(props.modelValue));
|
||||
|
||||
@@ -129,6 +135,7 @@ const lastId = ref<string>();
|
||||
const isLast = ref<boolean>(false);
|
||||
const loading = ref<boolean>(false);
|
||||
const subReplies = shallowRef<Array<TGApp.BBS.Reply.ReplyFull>>([]);
|
||||
const existingIds = new Set<string>();
|
||||
const levelColor = computed<string>(() => {
|
||||
const level = props.modelValue.user.level_exp.level;
|
||||
if (level < 5) return "var(--tgc-od-green)";
|
||||
@@ -138,12 +145,21 @@ const levelColor = computed<string>(() => {
|
||||
return "var(--tgc-od-white)";
|
||||
});
|
||||
|
||||
onMounted(async () => (props.mode === "main" ? (subListener = await listenSub()) : null));
|
||||
onMounted(async () => {
|
||||
if (props.mode === "main") {
|
||||
subListener = await listenSub();
|
||||
closeSubListener = await listenCloseSub();
|
||||
}
|
||||
});
|
||||
onUnmounted(() => {
|
||||
if (subListener !== null) {
|
||||
subListener();
|
||||
subListener = null;
|
||||
}
|
||||
if (closeSubListener !== null) {
|
||||
closeSubListener();
|
||||
closeSubListener = null;
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
@@ -159,6 +175,26 @@ async function listenSub(): Promise<UnlistenFn> {
|
||||
});
|
||||
}
|
||||
|
||||
async function listenCloseSub(): Promise<UnlistenFn> {
|
||||
return await event.listen<void>("closeReplySub", async () => {
|
||||
if (showSub.value) showSub.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
async function handleSubScroll(e: globalThis.Event): Promise<void> {
|
||||
const target = <HTMLElement>e.target;
|
||||
if (!target) return;
|
||||
// Check if scrolled to bottom for auto-load
|
||||
const scrollTop = target.scrollTop;
|
||||
const clientHeight = target.clientHeight;
|
||||
const scrollHeight = target.scrollHeight;
|
||||
if (scrollTop + clientHeight >= scrollHeight - 1) {
|
||||
if (!loading.value && !isLast.value) {
|
||||
await loadSub();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function share(): Promise<void> {
|
||||
const replyDom = document.querySelector<HTMLElement>(`#${replyId}`);
|
||||
if (replyDom === null) return;
|
||||
@@ -166,6 +202,17 @@ async function share(): Promise<void> {
|
||||
}
|
||||
|
||||
async function showReply(): Promise<void> {
|
||||
if (subReplies.value.length === 0 && props.modelValue.sub_replies?.length > 0) {
|
||||
subReplies.value = [...props.modelValue.sub_replies];
|
||||
// Populate existingIds with embedded sub-replies
|
||||
props.modelValue.sub_replies.forEach((r) => existingIds.add(r.reply.reply_id));
|
||||
const lastReply = props.modelValue.sub_replies[props.modelValue.sub_replies.length - 1];
|
||||
if (lastReply?.reply?.reply_id) lastId.value = lastReply.reply.reply_id;
|
||||
if (props.modelValue.sub_replies.length >= props.modelValue.sub_reply_count) {
|
||||
isLast.value = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (subReplies.value.length > 0) return;
|
||||
if (isLast.value) return;
|
||||
await loadSub();
|
||||
@@ -186,7 +233,11 @@ async function loadSub(): Promise<void> {
|
||||
}
|
||||
isLast.value = resp.is_last;
|
||||
lastId.value = resp.last_id;
|
||||
subReplies.value = subReplies.value.concat(resp.list);
|
||||
// Filter out duplicates using persistent existingIds Set
|
||||
const newReplies = resp.list.filter((r) => !existingIds.has(r.reply.reply_id));
|
||||
// Add new reply IDs to the Set
|
||||
newReplies.forEach((r) => existingIds.add(r.reply.reply_id));
|
||||
subReplies.value = subReplies.value.concat(newReplies);
|
||||
loading.value = false;
|
||||
if (isLast.value) showSnackbar.warn("没有更多了");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user