diff --git a/src/components/viewPost/vp-btn-reply.vue b/src/components/viewPost/vp-btn-reply.vue index 7c0365b7..936367fb 100644 --- a/src/components/viewPost/vp-btn-reply.vue +++ b/src/components/viewPost/vp-btn-reply.vue @@ -51,7 +51,7 @@ - + { + const target = 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 { if (reply.value.length > 0) return; if (isLast.value) return; diff --git a/src/components/viewPost/vp-reply-item.vue b/src/components/viewPost/vp-reply-item.vue index 5046fabe..46a79ca3 100644 --- a/src/components/viewPost/vp-reply-item.vue +++ b/src/components/viewPost/vp-reply-item.vue @@ -52,7 +52,12 @@ :close-on-content-click="false" v-model="showSub" > - + (); 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(); const isLast = ref(false); const loading = ref(false); const subReplies = shallowRef>([]); +const existingIds = new Set(); const levelColor = computed(() => { const level = props.modelValue.user.level_exp.level; if (level < 5) return "var(--tgc-od-green)"; @@ -138,12 +145,21 @@ const levelColor = computed(() => { 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 { }); } +async function listenCloseSub(): Promise { + return await event.listen("closeReplySub", async () => { + if (showSub.value) showSub.value = false; + }); +} + +async function handleSubScroll(e: globalThis.Event): Promise { + const target = 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 { const replyDom = document.querySelector(`#${replyId}`); if (replyDom === null) return; @@ -166,6 +202,17 @@ async function share(): Promise { } async function showReply(): Promise { + 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 { } 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("没有更多了"); }