Compare commits

..

3 Commits

Author SHA1 Message Date
Tim
da3d2a6a71 Return and show last channel message 2025-08-23 02:03:00 +08:00
tim
15cba0c96e fix: 支持显示最后一条消息 2025-08-23 01:57:33 +08:00
Tim
98a79acad9 Merge pull request #702 from nagisa77/codex/add-multi-tab-support-to-message-box
feat: add channel support
2025-08-23 01:31:26 +08:00
6 changed files with 63 additions and 12 deletions

View File

@@ -18,14 +18,14 @@ public class ChannelInitializer implements CommandLineRunner {
chat.setChannel(true);
chat.setName("吹水群");
chat.setDescription("吹水聊天");
chat.setAvatar("/default-avatar.svg");
chat.setAvatar("https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/32647273e2334d14adfd4a6ce9db0643.jpeg");
conversationRepository.save(chat);
MessageConversation tech = new MessageConversation();
tech.setChannel(true);
tech.setName("技术讨论群");
tech.setDescription("讨论技术相关话题");
tech.setAvatar("/default-avatar.svg");
tech.setAvatar("https://openisle-1307107697.cos.accelerate.myqcloud.com/dynamic_assert/5edde9a5864e471caa32491dbcdaa8b2.png");
conversationRepository.save(tech);
}
}

View File

@@ -121,6 +121,7 @@ public class SecurityConfig {
.requestMatchers(HttpMethod.GET, "/api/reaction-types").permitAll()
.requestMatchers(HttpMethod.GET, "/api/activities/**").permitAll()
.requestMatchers(HttpMethod.GET, "/api/sitemap.xml").permitAll()
.requestMatchers(HttpMethod.GET, "/api/channels").permitAll()
.requestMatchers(HttpMethod.GET, "/api/rss").permitAll()
.requestMatchers(HttpMethod.GET, "/api/point-goods").permitAll()
.requestMatchers(HttpMethod.POST, "/api/point-goods").permitAll()
@@ -156,7 +157,7 @@ public class SecurityConfig {
uri.startsWith("/api/search") || uri.startsWith("/api/users") ||
uri.startsWith("/api/reaction-types") || uri.startsWith("/api/config") ||
uri.startsWith("/api/activities") || uri.startsWith("/api/push/public-key") ||
uri.startsWith("/api/point-goods") ||
uri.startsWith("/api/point-goods") || uri.startsWith("/api/channels") ||
uri.startsWith("/api/sitemap.xml") || uri.startsWith("/api/medals") ||
uri.startsWith("/api/rss"));

View File

@@ -10,6 +10,7 @@ public class ChannelDto {
private String name;
private String description;
private String avatar;
private MessageDto lastMessage;
private long memberCount;
private boolean joined;
private long unreadCount;

View File

@@ -1,6 +1,9 @@
package com.openisle.service;
import com.openisle.dto.ChannelDto;
import com.openisle.dto.MessageDto;
import com.openisle.dto.UserSummaryDto;
import com.openisle.model.Message;
import com.openisle.model.MessageConversation;
import com.openisle.model.MessageParticipant;
import com.openisle.model.User;
@@ -54,6 +57,9 @@ public class ChannelService {
dto.setName(channel.getName());
dto.setDescription(channel.getDescription());
dto.setAvatar(channel.getAvatar());
if (channel.getLastMessage() != null) {
dto.setLastMessage(toMessageDto(channel.getLastMessage()));
}
dto.setMemberCount(channel.getParticipants().size());
boolean joined = channel.getParticipants().stream()
.anyMatch(p -> p.getUser().getId().equals(userId));
@@ -73,4 +79,20 @@ public class ChannelService {
}
return dto;
}
private MessageDto toMessageDto(Message message) {
MessageDto dto = new MessageDto();
dto.setId(message.getId());
dto.setContent(message.getContent());
dto.setConversationId(message.getConversation().getId());
dto.setCreatedAt(message.getCreatedAt());
UserSummaryDto userDto = new UserSummaryDto();
userDto.setId(message.getSender().getId());
userDto.setUsername(message.getSender().getUsername());
userDto.setAvatar(message.getSender().getAvatar());
dto.setSender(userDto);
return dto;
}
}

View File

@@ -22,8 +22,13 @@
</div>
<BaseTimeline :items="messages">
<template #item="{ item }">
<div class="message-timestamp">
{{ TimeManager.format(item.createdAt) }}
<div class="message-header">
<div class="user-name">
{{ item.sender.username }}
</div>
<div class="message-timestamp">
{{ TimeManager.format(item.createdAt) }}
</div>
</div>
<div class="message-content">
<div class="info-content-text" v-html="renderMarkdown(item.content)"></div>
@@ -448,10 +453,22 @@ onUnmounted(() => {
.message-timestamp {
font-size: 11px;
color: var(--text-color-secondary);
margin-top: 5px;
opacity: 0.6;
}
.message-header {
display: flex;
flex-direction: row;
align-items: center;
gap: 10px;
}
.user-name {
font-size: 14px;
font-weight: 600;
color: var(--text-color);
}
.message-item.sent {
align-self: flex-end;
flex-direction: row-reverse;

View File

@@ -94,10 +94,17 @@
{{ ch.name }}
<span v-if="ch.unreadCount > 0" class="unread-dot"></span>
</div>
<div class="message-time">成员 {{ ch.memberCount }}</div>
<div class="message-time">
{{ formatTime(ch.lastMessage?.createdAt || ch.createdAt) }}
</div>
</div>
<div class="last-message-row">
<div class="last-message">{{ ch.description }}</div>
<div class="last-message">
{{
ch.lastMessage ? stripMarkdownLength(ch.lastMessage.content, 100) : ch.description
}}
</div>
<div class="member-count">成员 {{ ch.memberCount }}</div>
</div>
</div>
</div>
@@ -262,8 +269,6 @@ function goToConversation(id) {
<style scoped>
.messages-container {
margin: 0 auto;
padding: 20px;
}
.tabs {
@@ -291,6 +296,8 @@ function goToConversation(id) {
.search-container {
margin-bottom: 24px;
margin-left: 20px;
margin-right: 20px;
}
.messages-header {
@@ -330,6 +337,8 @@ function goToConversation(id) {
.conversation-item {
display: flex;
align-items: center;
margin-left: 20px;
margin-right: 20px;
padding: 8px 10px;
cursor: pointer;
transition: background-color 0.2s ease;
@@ -415,8 +424,9 @@ function goToConversation(id) {
/* 响应式设计 */
@media (max-width: 768px) {
.messages-container {
padding: 10px 10px;
.conversation-item {
margin-left: 10px;
margin-right: 10px;
}
.messages-title {