From ebf04a284f952ecd75b820d8f540818c68aaf7fe Mon Sep 17 00:00:00 2001 From: Tim <135014430+nagisa77@users.noreply.github.com> Date: Fri, 12 Sep 2025 17:16:59 +0800 Subject: [PATCH] feat: add pagination for tags --- .../openisle/controller/TagController.java | 14 +++- .../openisle/repository/TagRepository.java | 4 ++ .../java/com/openisle/service/TagService.java | 9 +++ frontend_nuxt/components/Dropdown.vue | 66 ++++++++++++++++--- frontend_nuxt/components/InfiniteLoadMore.vue | 11 +++- frontend_nuxt/components/MenuComponent.vue | 58 +++++++++++----- frontend_nuxt/components/TagSelect.vue | 21 ++++-- 7 files changed, 146 insertions(+), 37 deletions(-) diff --git a/backend/src/main/java/com/openisle/controller/TagController.java b/backend/src/main/java/com/openisle/controller/TagController.java index b5a388849..c88aa6dc9 100644 --- a/backend/src/main/java/com/openisle/controller/TagController.java +++ b/backend/src/main/java/com/openisle/controller/TagController.java @@ -13,6 +13,7 @@ import com.openisle.service.PostService; import com.openisle.service.TagService; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; +import org.springframework.data.domain.Page; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; @@ -80,7 +81,18 @@ public class TagController { @ApiResponse(responseCode = "200", description = "List of tags", content = @Content(array = @ArraySchema(schema = @Schema(implementation = TagDto.class)))) public List list(@RequestParam(value = "keyword", required = false) String keyword, - @RequestParam(value = "limit", required = false) Integer limit) { + @RequestParam(value = "limit", required = false) Integer limit, + @RequestParam(value = "page", required = false) Integer page, + @RequestParam(value = "pageSize", required = false) Integer pageSize) { + if (page != null && pageSize != null) { + Page tagPage = tagService.searchTags(keyword, page, pageSize); + List tags = tagPage.getContent(); + List tagIds = tags.stream().map(Tag::getId).toList(); + Map postCntByTagIds = postService.countPostsByTagIds(tagIds); + return tags.stream() + .map(t -> tagMapper.toDto(t, postCntByTagIds.getOrDefault(t.getId(), 0L))) + .collect(Collectors.toList()); + } List tags = tagService.searchTags(keyword); List tagIds = tags.stream().map(Tag::getId).toList(); Map postCntByTagIds = postService.countPostsByTagIds(tagIds); diff --git a/backend/src/main/java/com/openisle/repository/TagRepository.java b/backend/src/main/java/com/openisle/repository/TagRepository.java index 1e2868437..f1cd5b6c7 100644 --- a/backend/src/main/java/com/openisle/repository/TagRepository.java +++ b/backend/src/main/java/com/openisle/repository/TagRepository.java @@ -4,6 +4,7 @@ import com.openisle.model.Tag; import com.openisle.model.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Page; import java.util.List; import java.util.Optional; @@ -14,6 +15,9 @@ public interface TagRepository extends JpaRepository { List findByApprovedTrue(); List findByNameContainingIgnoreCaseAndApprovedTrue(String keyword); + Page findByApprovedTrue(Pageable pageable); + Page findByNameContainingIgnoreCaseAndApprovedTrue(String keyword, Pageable pageable); + List findByCreatorOrderByCreatedAtDesc(User creator, Pageable pageable); List findByCreator(User creator); diff --git a/backend/src/main/java/com/openisle/service/TagService.java b/backend/src/main/java/com/openisle/service/TagService.java index eee84121e..01373bc99 100644 --- a/backend/src/main/java/com/openisle/service/TagService.java +++ b/backend/src/main/java/com/openisle/service/TagService.java @@ -9,6 +9,7 @@ import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Page; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -108,6 +109,14 @@ public class TagService { return tagRepository.findByNameContainingIgnoreCaseAndApprovedTrue(keyword); } + public Page searchTags(String keyword, int page, int pageSize) { + Pageable pageable = PageRequest.of(page, pageSize); + if (keyword == null || keyword.isBlank()) { + return tagRepository.findByApprovedTrue(pageable); + } + return tagRepository.findByNameContainingIgnoreCaseAndApprovedTrue(keyword, pageable); + } + public List getRecentTagsByUser(String username, int limit) { User user = userRepository.findByUsername(username) .orElseThrow(() -> new com.openisle.exception.NotFoundException("User not found")); diff --git a/frontend_nuxt/components/Dropdown.vue b/frontend_nuxt/components/Dropdown.vue index 6cc680874..a3062b164 100644 --- a/frontend_nuxt/components/Dropdown.vue +++ b/frontend_nuxt/components/Dropdown.vue @@ -52,6 +52,7 @@ v-if="open && !isMobile && (loading || filteredOptions.length > 0 || showSearch)" :class="['dropdown-menu', menuClass]" v-click-outside="close" + ref="menu" > + @@ -88,7 +96,7 @@ {{ placeholder }} - @@ -126,9 +141,11 @@