From 4627d34dbe29c127fdea37b955cb8a2d93432d91 Mon Sep 17 00:00:00 2001
From: Tim <135014430+nagisa77@users.noreply.github.com>
Date: Wed, 9 Jul 2025 18:17:35 +0800
Subject: [PATCH] Implement reaction panel with backend support
---
open-isle-cli/src/components/CommentItem.vue | 26 +--
.../src/components/ReactionsGroup.vue | 172 ++++++++++++++++++
open-isle-cli/src/views/PostPageView.vue | 36 ++--
.../controller/ReactionController.java | 6 +
.../java/com/openisle/model/Reaction.java | 4 +-
.../repository/ReactionRepository.java | 4 +-
.../com/openisle/service/ReactionService.java | 16 +-
7 files changed, 213 insertions(+), 51 deletions(-)
create mode 100644 open-isle-cli/src/components/ReactionsGroup.vue
diff --git a/open-isle-cli/src/components/CommentItem.vue b/open-isle-cli/src/components/CommentItem.vue
index e4dfcd4a0..0dad7f7e9 100644
--- a/open-isle-cli/src/components/CommentItem.vue
+++ b/open-isle-cli/src/components/CommentItem.vue
@@ -18,27 +18,14 @@
-
({
id: r.id,
userName: r.author.username,
time: new Date(r.createdAt).toLocaleDateString('zh-CN', { month: 'numeric', day: 'numeric' }),
avatar: r.avatar,
text: r.content,
+ reactions: r.reactions || [],
reply: [],
openReplies: false
})),
@@ -168,7 +158,7 @@ const CommentItem = {
return { showReplies, toggleReplies, showEditor, toggleEditor, submitReply, copyCommentLink, renderMarkdown, isWaitingForReply }
}
}
-CommentItem.components = { CommentItem, CommentEditor, BaseTimeline }
+CommentItem.components = { CommentItem, CommentEditor, BaseTimeline, ReactionsGroup }
export default CommentItem
diff --git a/open-isle-cli/src/components/ReactionsGroup.vue b/open-isle-cli/src/components/ReactionsGroup.vue
new file mode 100644
index 000000000..ac9be94cd
--- /dev/null
+++ b/open-isle-cli/src/components/ReactionsGroup.vue
@@ -0,0 +1,172 @@
+
+
+
+
+
+ {{ iconMap[r.type] }}
+
+
点击以表态
+
+
{{ totalCount }}
+
+
+
+
+ {{ likeCount }}
+
+
+
+
+
+ {{ iconMap[t] }}{{ counts[t] }}
+
+
+
+
+
+
+
+
diff --git a/open-isle-cli/src/views/PostPageView.vue b/open-isle-cli/src/views/PostPageView.vue
index 51063c9a8..6fc447ee9 100644
--- a/open-isle-cli/src/views/PostPageView.vue
+++ b/open-isle-cli/src/views/PostPageView.vue
@@ -27,31 +27,11 @@
-
-
-
-
- 🤣
-
-
- ❤️
-
-
- 👏
-
-
-
1882
+
+
+
-
-
-
+
@@ -98,6 +78,7 @@ import CommentItem from '../components/CommentItem.vue'
import CommentEditor from '../components/CommentEditor.vue'
import BaseTimeline from '../components/BaseTimeline.vue'
import ArticleTags from '../components/ArticleTags.vue'
+import ReactionsGroup from '../components/ReactionsGroup.vue'
import { renderMarkdown } from '../utils/markdown'
import { API_BASE_URL, toast } from '../main'
import { getToken } from '../utils/auth'
@@ -107,7 +88,7 @@ hatch.register()
export default {
name: 'PostPageView',
- components: { CommentItem, CommentEditor, BaseTimeline, ArticleTags },
+ components: { CommentItem, CommentEditor, BaseTimeline, ArticleTags, ReactionsGroup },
setup() {
const route = useRoute()
const postId = route.params.id
@@ -118,6 +99,7 @@ export default {
const postContent = ref('')
const category = ref('')
const tags = ref([])
+ const postReactions = ref([])
const comments = ref([])
const isWaitingFetchingPost = ref(false);
const isWaitingPostingComment = ref(false);
@@ -150,6 +132,7 @@ export default {
time: new Date(c.createdAt).toLocaleDateString('zh-CN', { month: 'numeric', day: 'numeric' }),
avatar: c.author.avatar,
text: c.content,
+ reactions: c.reactions || [],
reply: (c.replies || []).map(mapComment),
openReplies: false,
src: c.author.avatar,
@@ -195,6 +178,7 @@ export default {
title.value = data.title
category.value = data.category
tags.value = data.tags || []
+ postReactions.value = data.reactions || []
comments.value = (data.comments || []).map(mapComment)
postTime.value = new Date(data.createdAt).toLocaleDateString('zh-CN', { month: 'numeric', day: 'numeric' })
await nextTick()
@@ -322,6 +306,8 @@ export default {
mainContainer,
currentIndex,
totalPosts,
+ postReactions,
+ postId,
postComment,
onSliderInput,
onScroll: updateCurrentIndex,
diff --git a/src/main/java/com/openisle/controller/ReactionController.java b/src/main/java/com/openisle/controller/ReactionController.java
index bef570999..ef8ddd1fa 100644
--- a/src/main/java/com/openisle/controller/ReactionController.java
+++ b/src/main/java/com/openisle/controller/ReactionController.java
@@ -28,6 +28,9 @@ public class ReactionController {
@RequestBody ReactionRequest req,
Authentication auth) {
Reaction reaction = reactionService.reactToPost(auth.getName(), postId, req.getType());
+ if (reaction == null) {
+ return ResponseEntity.noContent().build();
+ }
return ResponseEntity.ok(toDto(reaction));
}
@@ -36,6 +39,9 @@ public class ReactionController {
@RequestBody ReactionRequest req,
Authentication auth) {
Reaction reaction = reactionService.reactToComment(auth.getName(), commentId, req.getType());
+ if (reaction == null) {
+ return ResponseEntity.noContent().build();
+ }
return ResponseEntity.ok(toDto(reaction));
}
diff --git a/src/main/java/com/openisle/model/Reaction.java b/src/main/java/com/openisle/model/Reaction.java
index 380d2ceb9..9fb71d8c1 100644
--- a/src/main/java/com/openisle/model/Reaction.java
+++ b/src/main/java/com/openisle/model/Reaction.java
@@ -14,8 +14,8 @@ import lombok.Setter;
@NoArgsConstructor
@Table(name = "reactions",
uniqueConstraints = {
- @UniqueConstraint(columnNames = {"user_id", "post_id"}),
- @UniqueConstraint(columnNames = {"user_id", "comment_id"})
+ @UniqueConstraint(columnNames = {"user_id", "post_id", "type"}),
+ @UniqueConstraint(columnNames = {"user_id", "comment_id", "type"})
})
public class Reaction {
@Id
diff --git a/src/main/java/com/openisle/repository/ReactionRepository.java b/src/main/java/com/openisle/repository/ReactionRepository.java
index a9ffa9c43..c5ba190ac 100644
--- a/src/main/java/com/openisle/repository/ReactionRepository.java
+++ b/src/main/java/com/openisle/repository/ReactionRepository.java
@@ -13,8 +13,8 @@ import java.util.List;
import java.util.Optional;
public interface ReactionRepository extends JpaRepository
{
- Optional findByUserAndPost(User user, Post post);
- Optional findByUserAndComment(User user, Comment comment);
+ Optional findByUserAndPostAndType(User user, Post post, com.openisle.model.ReactionType type);
+ Optional findByUserAndCommentAndType(User user, Comment comment, com.openisle.model.ReactionType type);
List findByPost(Post post);
List findByComment(Comment comment);
diff --git a/src/main/java/com/openisle/service/ReactionService.java b/src/main/java/com/openisle/service/ReactionService.java
index 57c39a28e..cfbea0c33 100644
--- a/src/main/java/com/openisle/service/ReactionService.java
+++ b/src/main/java/com/openisle/service/ReactionService.java
@@ -28,8 +28,12 @@ public class ReactionService {
.orElseThrow(() -> new IllegalArgumentException("User not found"));
Post post = postRepository.findById(postId)
.orElseThrow(() -> new IllegalArgumentException("Post not found"));
- Reaction reaction = reactionRepository.findByUserAndPost(user, post)
- .orElseGet(Reaction::new);
+ java.util.Optional existing = reactionRepository.findByUserAndPostAndType(user, post, type);
+ if (existing.isPresent()) {
+ reactionRepository.delete(existing.get());
+ return null;
+ }
+ Reaction reaction = new Reaction();
reaction.setUser(user);
reaction.setPost(post);
reaction.setType(type);
@@ -45,8 +49,12 @@ public class ReactionService {
.orElseThrow(() -> new IllegalArgumentException("User not found"));
Comment comment = commentRepository.findById(commentId)
.orElseThrow(() -> new IllegalArgumentException("Comment not found"));
- Reaction reaction = reactionRepository.findByUserAndComment(user, comment)
- .orElseGet(Reaction::new);
+ java.util.Optional existing = reactionRepository.findByUserAndCommentAndType(user, comment, type);
+ if (existing.isPresent()) {
+ reactionRepository.delete(existing.get());
+ return null;
+ }
+ Reaction reaction = new Reaction();
reaction.setUser(user);
reaction.setComment(comment);
reaction.setPost(null);