mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-06-09 03:27:32 +08:00
feat: 评论分页
This commit is contained in:
@@ -112,7 +112,9 @@ public class CommentController {
|
|||||||
)
|
)
|
||||||
public List<TimelineItemDto<?>> listComments(
|
public List<TimelineItemDto<?>> listComments(
|
||||||
@PathVariable Long postId,
|
@PathVariable Long postId,
|
||||||
@RequestParam(value = "sort", required = false, defaultValue = "OLDEST") CommentSort sort
|
@RequestParam(value = "sort", required = false, defaultValue = "OLDEST") CommentSort sort,
|
||||||
|
@RequestParam(value = "page", required = false, defaultValue = "0") int page,
|
||||||
|
@RequestParam(value = "pageSize", required = false, defaultValue = "20") int pageSize
|
||||||
) {
|
) {
|
||||||
log.debug("listComments called for post {} with sort {}", postId, sort);
|
log.debug("listComments called for post {} with sort {}", postId, sort);
|
||||||
List<CommentDto> commentDtoList = commentService
|
List<CommentDto> commentDtoList = commentService
|
||||||
@@ -183,8 +185,23 @@ public class CommentController {
|
|||||||
createdAtComparator = createdAtComparator.reversed();
|
createdAtComparator = createdAtComparator.reversed();
|
||||||
}
|
}
|
||||||
itemDtoList.sort(comparator.thenComparing(createdAtComparator));
|
itemDtoList.sort(comparator.thenComparing(createdAtComparator));
|
||||||
log.debug("listComments returning {} comments", itemDtoList.size());
|
|
||||||
return itemDtoList;
|
int safePage = Math.max(0, page);
|
||||||
|
int safePageSize = Math.max(1, pageSize);
|
||||||
|
int fromIndex = safePage * safePageSize;
|
||||||
|
int toIndex = Math.min(fromIndex + safePageSize, itemDtoList.size());
|
||||||
|
List<TimelineItemDto<?>> pagedItems =
|
||||||
|
fromIndex >= itemDtoList.size() ? List.of() : itemDtoList.subList(fromIndex, toIndex);
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
"listComments returning {} items for post {} page {} size {} (total {})",
|
||||||
|
pagedItems.size(),
|
||||||
|
postId,
|
||||||
|
safePage,
|
||||||
|
safePageSize,
|
||||||
|
itemDtoList.size()
|
||||||
|
);
|
||||||
|
return pagedItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/comments/{commentId}/context")
|
@GetMapping("/comments/{commentId}/context")
|
||||||
|
|||||||
@@ -181,6 +181,12 @@
|
|||||||
<PostChangeLogItem v-else :log="item" :title="title" />
|
<PostChangeLogItem v-else :log="item" :title="title" />
|
||||||
</template>
|
</template>
|
||||||
</BaseTimeline>
|
</BaseTimeline>
|
||||||
|
<InfiniteLoadMore
|
||||||
|
v-if="timelineItems.length > 0"
|
||||||
|
:key="commentSort"
|
||||||
|
:on-load="loadMoreTimeline"
|
||||||
|
:pause="isLoadingMoreComments || isFetchingComments"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -211,6 +217,7 @@ import CommentEditor from '~/components/CommentEditor.vue'
|
|||||||
import BaseTimeline from '~/components/BaseTimeline.vue'
|
import BaseTimeline from '~/components/BaseTimeline.vue'
|
||||||
import BasePlaceholder from '~/components/BasePlaceholder.vue'
|
import BasePlaceholder from '~/components/BasePlaceholder.vue'
|
||||||
import PostChangeLogItem from '~/components/PostChangeLogItem.vue'
|
import PostChangeLogItem from '~/components/PostChangeLogItem.vue'
|
||||||
|
import InfiniteLoadMore from '~/components/InfiniteLoadMore.vue'
|
||||||
import ArticleTags from '~/components/ArticleTags.vue'
|
import ArticleTags from '~/components/ArticleTags.vue'
|
||||||
import ArticleCategory from '~/components/ArticleCategory.vue'
|
import ArticleCategory from '~/components/ArticleCategory.vue'
|
||||||
import ReactionsGroup from '~/components/ReactionsGroup.vue'
|
import ReactionsGroup from '~/components/ReactionsGroup.vue'
|
||||||
@@ -276,6 +283,10 @@ const currentIndex = ref(1)
|
|||||||
const subscribed = ref(false)
|
const subscribed = ref(false)
|
||||||
const commentSort = ref('NEWEST')
|
const commentSort = ref('NEWEST')
|
||||||
const isFetchingComments = ref(false)
|
const isFetchingComments = ref(false)
|
||||||
|
const commentPage = ref(0)
|
||||||
|
const commentPageSize = 10
|
||||||
|
const hasMoreComments = ref(true)
|
||||||
|
const isLoadingMoreComments = ref(false)
|
||||||
const isMobile = useIsMobile()
|
const isMobile = useIsMobile()
|
||||||
const timelineItems = ref([])
|
const timelineItems = ref([])
|
||||||
|
|
||||||
@@ -869,17 +880,33 @@ const fetchCommentSorts = () => {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchCommentsAndChangeLog = async () => {
|
const fetchCommentsAndChangeLog = async ({ pageNo = 0, append = false } = {}) => {
|
||||||
isFetchingComments.value = true
|
if (isLoadingMoreComments.value) return false
|
||||||
console.info('Fetching comments and chang log', { postId, sort: commentSort.value })
|
if (!append) {
|
||||||
|
hasMoreComments.value = true
|
||||||
|
commentPage.value = 0
|
||||||
|
}
|
||||||
|
if (pageNo === 0) {
|
||||||
|
isFetchingComments.value = true
|
||||||
|
} else {
|
||||||
|
isLoadingMoreComments.value = true
|
||||||
|
}
|
||||||
|
console.info('Fetching comments and chang log', {
|
||||||
|
postId,
|
||||||
|
sort: commentSort.value,
|
||||||
|
page: pageNo,
|
||||||
|
pageSize: commentPageSize,
|
||||||
|
})
|
||||||
|
let done = false
|
||||||
try {
|
try {
|
||||||
const token = getToken()
|
const token = getToken()
|
||||||
const res = await fetch(
|
const url = new URL(`${API_BASE_URL}/api/posts/${postId}/comments`)
|
||||||
`${API_BASE_URL}/api/posts/${postId}/comments?sort=${commentSort.value}`,
|
url.searchParams.set('sort', commentSort.value)
|
||||||
{
|
url.searchParams.set('page', String(pageNo))
|
||||||
headers: { Authorization: token ? `Bearer ${token}` : '' },
|
url.searchParams.set('pageSize', String(commentPageSize))
|
||||||
},
|
const res = await fetch(url.toString(), {
|
||||||
)
|
headers: { Authorization: token ? `Bearer ${token}` : '' },
|
||||||
|
})
|
||||||
console.info('Fetch comments response status', res.status)
|
console.info('Fetch comments response status', res.status)
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json()
|
const data = await res.json()
|
||||||
@@ -902,23 +929,48 @@ const fetchCommentsAndChangeLog = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
comments.value = commentList
|
if (append) {
|
||||||
changeLogs.value = changeLogList
|
comments.value.push(...commentList)
|
||||||
timelineItems.value = newTimelineItemList
|
changeLogs.value.push(...changeLogList)
|
||||||
|
timelineItems.value.push(...newTimelineItemList)
|
||||||
|
commentPage.value = pageNo
|
||||||
|
} else {
|
||||||
|
comments.value = commentList
|
||||||
|
changeLogs.value = changeLogList
|
||||||
|
timelineItems.value = newTimelineItemList
|
||||||
|
commentPage.value = 0
|
||||||
|
}
|
||||||
|
|
||||||
isFetchingComments.value = false
|
done = data.length < commentPageSize
|
||||||
|
hasMoreComments.value = !done
|
||||||
await nextTick()
|
await nextTick()
|
||||||
gatherPostItems()
|
gatherPostItems()
|
||||||
|
return done
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.debug('Fetch comments error', e)
|
console.debug('Fetch comments error', e)
|
||||||
|
hasMoreComments.value = false
|
||||||
|
return true
|
||||||
} finally {
|
} finally {
|
||||||
isFetchingComments.value = false
|
isFetchingComments.value = false
|
||||||
|
isLoadingMoreComments.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchTimeline = async () => {
|
const fetchTimeline = async () => {
|
||||||
await fetchCommentsAndChangeLog()
|
hasMoreComments.value = true
|
||||||
|
commentPage.value = 0
|
||||||
|
comments.value = []
|
||||||
|
changeLogs.value = []
|
||||||
|
timelineItems.value = []
|
||||||
|
await fetchCommentsAndChangeLog({ pageNo: 0, append: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadMoreTimeline = async () => {
|
||||||
|
if (!hasMoreComments.value || isLoadingMoreComments.value) return true
|
||||||
|
const nextPage = commentPage.value + 1
|
||||||
|
const done = await fetchCommentsAndChangeLog({ pageNo: nextPage, append: true })
|
||||||
|
return done || !hasMoreComments.value
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(commentSort, async () => {
|
watch(commentSort, async () => {
|
||||||
@@ -929,8 +981,17 @@ const jumpToHashComment = async () => {
|
|||||||
const hash = location.hash
|
const hash = location.hash
|
||||||
if (hash.startsWith('#comment-')) {
|
if (hash.startsWith('#comment-')) {
|
||||||
const id = hash.substring('#comment-'.length)
|
const id = hash.substring('#comment-'.length)
|
||||||
await new Promise((resolve) => setTimeout(resolve, 500))
|
await new Promise((resolve) => setTimeout(resolve, 300))
|
||||||
const el = document.getElementById('comment-' + id)
|
let el = document.getElementById('comment-' + id)
|
||||||
|
|
||||||
|
// 若未加载到目标评论,尝试继续分页加载直到找到或无更多
|
||||||
|
while (!el && hasMoreComments.value) {
|
||||||
|
const done = await loadMoreTimeline()
|
||||||
|
await nextTick()
|
||||||
|
el = document.getElementById('comment-' + id)
|
||||||
|
if (done) break
|
||||||
|
}
|
||||||
|
|
||||||
if (el) {
|
if (el) {
|
||||||
const top = el.getBoundingClientRect().top + window.scrollY - headerHeight - 20 // 20 for beauty
|
const top = el.getBoundingClientRect().top + window.scrollY - headerHeight - 20 // 20 for beauty
|
||||||
window.scrollTo({ top, behavior: 'smooth' })
|
window.scrollTo({ top, behavior: 'smooth' })
|
||||||
|
|||||||
Reference in New Issue
Block a user