From 957df1a539eefa428f90dc04653e18da42fd38d1 Mon Sep 17 00:00:00 2001 From: bbb-lsy07 Date: Sun, 21 Jun 2026 09:55:03 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AF=B9=E8=AF=9D=E6=B0=94=E6=B3=A1?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BC=95=E7=94=A8=E6=91=98=E8=A6=81=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=EF=BC=8C=E8=A7=A3=E5=86=B3=E5=A4=9A=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E6=B7=B7=E6=9D=82=E4=BA=A4=E8=B0=88=E4=B8=8A=E4=B8=8B=E6=96=87?= =?UTF-8?q?=E4=B8=8D=E6=B8=85=E6=99=B0=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端 ConversationMessage Record 新增 quoteOwner/quoteContent 字段 - 后端 getConversation 方法重写,构建 Reply 映射字典溯源引用关系 - 前端 ConversationMessage 类型定义新增 quoteOwner/quoteContent - 前端新增 truncateQuote 截断方法(复用 stripHtml,限30字符) - 前端对话气泡模板渲染灰色引用条(bg-black/5 + border-l-2) --- .../endpoint/CommentAiAutopilotEndpoint.java | 49 ++++++++++++++----- ui/src/views/LogsView.vue | 26 +++++++++- 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/src/main/java/top/nxxy335/commentaiautopilot/endpoint/CommentAiAutopilotEndpoint.java b/src/main/java/top/nxxy335/commentaiautopilot/endpoint/CommentAiAutopilotEndpoint.java index 4910205..135a523 100644 --- a/src/main/java/top/nxxy335/commentaiautopilot/endpoint/CommentAiAutopilotEndpoint.java +++ b/src/main/java/top/nxxy335/commentaiautopilot/endpoint/CommentAiAutopilotEndpoint.java @@ -291,26 +291,51 @@ public class CommentAiAutopilotEndpoint implements CustomEndpoint { var commentTime = String.valueOf(comment.getMetadata().getCreationTimestamp()); var isCommentAi = isAiOwner(comment.getSpec().getOwner()); + // 首条评论没有引用对象 var commentMsg = new ConversationMessage( - "comment", commentOwner, commentContent, commentTime, isCommentAi + "comment", commentOwner, commentContent, commentTime, isCommentAi, null, null ); return client.list(Reply.class, reply -> commentName.equals(reply.getSpec().getCommentName()), null) .sort(Comparator.comparing(r -> r.getMetadata().getCreationTimestamp())) - .map(reply -> { - var replyOwner = extractOwnerName(reply.getSpec().getOwner()); - var replyContent = extractContent(reply.getSpec().getRaw(), reply.getSpec().getContent()); - var replyTime = String.valueOf(reply.getMetadata().getCreationTimestamp()); - var isAi = isAiOwner(reply.getSpec().getOwner()); - return new ConversationMessage("reply", replyOwner, replyContent, replyTime, isAi); - }) - .collectList() + .collectList() // 收集为List以便统一处理引用映射 .map(replyList -> { List messages = new ArrayList<>(); messages.add(commentMsg); - messages.addAll(replyList); + + // 构建 Reply 的映射字典,方便查找引用关系 + Map replyMap = new HashMap<>(); + for (Reply r : replyList) { + replyMap.put(r.getMetadata().getName(), r); + } + + for (Reply reply : replyList) { + var replyOwner = extractOwnerName(reply.getSpec().getOwner()); + var replyContent = extractContent(reply.getSpec().getRaw(), reply.getSpec().getContent()); + var replyTime = String.valueOf(reply.getMetadata().getCreationTimestamp()); + var isAi = isAiOwner(reply.getSpec().getOwner()); + + String quoteOwner = null; + String quoteContent = null; + + // 获取引用的 Reply 名称 (Halo中如果为空,代表直接回复顶级 Comment) + String quoteReplyName = reply.getSpec().getQuoteReply(); + if (quoteReplyName != null && !quoteReplyName.isBlank()) { + Reply quotedReply = replyMap.get(quoteReplyName); + if (quotedReply != null) { + quoteOwner = extractOwnerName(quotedReply.getSpec().getOwner()); + quoteContent = extractContent(quotedReply.getSpec().getRaw(), quotedReply.getSpec().getContent()); + } + } else { + // 没有 quoteReply 表示直接回复首条评论 + quoteOwner = commentOwner; + quoteContent = commentContent; + } + + messages.add(new ConversationMessage("reply", replyOwner, replyContent, replyTime, isAi, quoteOwner, quoteContent)); + } return messages; }); }) @@ -712,7 +737,9 @@ public class CommentAiAutopilotEndpoint implements CustomEndpoint { String owner, String content, String time, - boolean isAi + boolean isAi, + String quoteOwner, + String quoteContent ) {} public record CommenterInfo( diff --git a/ui/src/views/LogsView.vue b/ui/src/views/LogsView.vue index ff337eb..b634006 100644 --- a/ui/src/views/LogsView.vue +++ b/ui/src/views/LogsView.vue @@ -299,8 +299,19 @@ :class="msg.isAi ? 'bg-blue-50 text-gray-800 rounded-tl-md' : 'bg-gray-100 text-gray-800 rounded-tr-md'" - v-html="renderContent(msg.content)" - > + > + +
+ @{{ msg.quoteOwner }}: + {{ truncateQuote(msg.quoteContent, 30) }} +
+ + +
+
{{ formatDate(msg.time) }} @@ -358,6 +369,8 @@ interface ConversationMessage { content: string time: string isAi: boolean + quoteOwner?: string + quoteContent?: string } const replies = ref([]) @@ -639,6 +652,15 @@ const stripHtml = (html: string) => { .trim() } +/** + * Truncate quote content for preview + */ +const truncateQuote = (content: string, length = 30) => { + if (!content) return "" + const plain = stripHtml(content) + return plain.length > length ? plain.substring(0, length) + "..." : plain +} + /** * Sanitize and render HTML content for conversation bubbles. * Only allows safe inline tags, strips dangerous elements.